diff --git a/README.md b/README.md index 4d53534..5b8dfa5 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ # BasicSpringBootAPI -Efficient REST API crafted using Core Java and Spring Boot, enabling smooth CRUD operations on an employee database. +Efficient REST API crafted using Core Java and Spring Boot, enabling smooth CRUD operations on an employee database, that includes the incorporation of a Spring Security layer, using bcrypt-hashed passwords, and executed through the adept utilization of JDBC technology. + +# Database Preview + +![DatabaseFinalPreview](https://github.com/manumiguezz/CompanySpringBootRESTAPI/assets/111899370/a97b153a-3a62-447e-99fb-258f90be4019) diff --git a/mysqlscripts/employee-directory.sql b/mysqlscripts/employee-directory.sql new file mode 100644 index 0000000..ee866ba --- /dev/null +++ b/mysqlscripts/employee-directory.sql @@ -0,0 +1,22 @@ +CREATE DATABASE IF NOT EXISTS `employee_directory`; +USE `employee_directory`; + + +DROP TABLE IF EXISTS `employee`; + +CREATE TABLE `employee` ( + `id` int NOT NULL AUTO_INCREMENT, + `first_name` varchar(45) DEFAULT NULL, + `last_name` varchar(45) DEFAULT NULL, + `email` varchar(45) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; + + +INSERT INTO `employee` VALUES + (1,'Manuel','Miguez','manum@mail.com'), + (2,'Luca','Vinelli','lucav@mail.com'), + (3,'Iñaki','Mariño','iñakim@mail.com'), + (4,'Manfiu','Puerto','manfiudp@mail.com'), + (5,'Juani','Pucheta','juanip@mail.com'); + diff --git a/mysqlscripts/spring-security-finished.sql b/mysqlscripts/spring-security-finished.sql new file mode 100644 index 0000000..959a855 --- /dev/null +++ b/mysqlscripts/spring-security-finished.sql @@ -0,0 +1,36 @@ +USE `employee_directory`; + +DROP TABLE IF EXISTS `roles`; +DROP TABLE IF EXISTS `members`; + + +CREATE TABLE `members` ( + `user` varchar(50) NOT NULL, + `psw` char(68) NOT NULL, + `active` tinyint NOT NULL, + PRIMARY KEY (`user`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +INSERT INTO `members` +VALUES +('ramiro','{bcrypt}$2a$10$S7GTZIc/KfegnP9H4GNaCOxHkvu0M32H7f9wg/lHl/c97VQtIoVvm',1), +('matias','{bcrypt}$2a$10$DWG0SGnfxqDIjx.LrywatevHjod2EbepV0W3t5d1aOoRLjSQuvszS',1), +('alejo','{bcrypt}$2a$10$0tqmRxOQufHIrIuQWNHa6.M4ei4LbE4NhN6gfUEOOnxpkbzLABGc6',1); + + +CREATE TABLE `roles` ( + `user` varchar(50) NOT NULL, + `role` varchar(50) NOT NULL, + UNIQUE KEY `authorities5_idx_1` (`user`,`role`), + CONSTRAINT `authorities5_ibfk_1` FOREIGN KEY (`user`) REFERENCES `members` (`user`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + + +INSERT INTO `roles` +VALUES +('ramiro','ROLE_EMPLOYEE'), +('matias','ROLE_EMPLOYEE'), +('matias','ROLE_MANAGER'), +('alejo','ROLE_EMPLOYEE'), +('alejo','ROLE_MANAGER'), +('alejo','ROLE_ADMIN'); diff --git a/mysqlscripts/spring-security-users-bcrypt.sql b/mysqlscripts/spring-security-users-bcrypt.sql new file mode 100644 index 0000000..e6002cd --- /dev/null +++ b/mysqlscripts/spring-security-users-bcrypt.sql @@ -0,0 +1,36 @@ +USE `employee_directory`; + +DROP TABLE IF EXISTS `authorities`; +DROP TABLE IF EXISTS `users`; + + +CREATE TABLE `users` ( + `username` varchar(50) NOT NULL, + `password` char(68) NOT NULL, + `enabled` tinyint NOT NULL, + PRIMARY KEY (`username`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +INSERT INTO `users` +VALUES +('ramiro','{bcrypt}$2a$10$S7GTZIc/KfegnP9H4GNaCOxHkvu0M32H7f9wg/lHl/c97VQtIoVvm',1), +('matias','{bcrypt}$2a$10$DWG0SGnfxqDIjx.LrywatevHjod2EbepV0W3t5d1aOoRLjSQuvszS',1), +('alejo','{bcrypt}$2a$10$0tqmRxOQufHIrIuQWNHa6.M4ei4LbE4NhN6gfUEOOnxpkbzLABGc6',1); + + +CREATE TABLE `authorities` ( + `username` varchar(50) NOT NULL, + `authority` varchar(50) NOT NULL, + UNIQUE KEY `authorities4_idx_1` (`username`,`authority`), + CONSTRAINT `authorities4_ibfk_1` FOREIGN KEY (`username`) REFERENCES `users` (`username`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + + +INSERT INTO `authorities` +VALUES +('ramiro','ROLE_EMPLOYEE'), +('matias','ROLE_EMPLOYEE'), +('matias','ROLE_MANAGER'), +('alejo','ROLE_EMPLOYEE'), +('alejo','ROLE_MANAGER'), +('alejo','ROLE_ADMIN'); \ No newline at end of file diff --git a/mysqlscripts/spring-security-users.sql b/mysqlscripts/spring-security-users.sql new file mode 100644 index 0000000..47363d0 --- /dev/null +++ b/mysqlscripts/spring-security-users.sql @@ -0,0 +1,39 @@ +USE `employee_directory`; + +DROP TABLE IF EXISTS `authorities`; +DROP TABLE IF EXISTS `users`; + + +CREATE TABLE `users` ( + `username` varchar(50) NOT NULL, + `password` varchar(50) NOT NULL, + `enabled` tinyint NOT NULL, + PRIMARY KEY (`username`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + + +INSERT INTO `users` +VALUES +('ramiro','{noop}examplepass',1), +('matias','{noop}examplepass',1), +('alejo','{noop}examplepass',1); + + +CREATE TABLE `authorities` ( + `username` varchar(50) NOT NULL, + `authority` varchar(50) NOT NULL, + UNIQUE KEY `authorities_idx_1` (`username`,`authority`), + CONSTRAINT `authorities_ibfk_1` FOREIGN KEY (`username`) REFERENCES `users` (`username`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + + +INSERT INTO `authorities` +VALUES +('ramiro','ROLE_EMPLOYEE'), +('matias','ROLE_EMPLOYEE'), +('matias','ROLE_MANAGER'), +('alejo','ROLE_EMPLOYEE'), +('alejo','ROLE_MANAGER'), +('alejo','ROLE_ADMIN'); + + diff --git a/pom.xml b/pom.xml index a4e1e3d..1baccf0 100644 --- a/pom.xml +++ b/pom.xml @@ -17,10 +17,22 @@ 17 + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.boot + spring-boot-starter-data-rest + + org.springframework.boot spring-boot-starter-data-jpa + org.springframework.boot spring-boot-starter-web @@ -32,16 +44,19 @@ runtime true + com.mysql mysql-connector-j runtime + org.springframework.boot spring-boot-starter-test test + diff --git a/src/main/assets/DatabaseFinalPreview.png b/src/main/assets/DatabaseFinalPreview.png new file mode 100644 index 0000000..043759d Binary files /dev/null and b/src/main/assets/DatabaseFinalPreview.png differ diff --git a/src/main/java/com/manumiguezz/crudapplication/dao/EmployeeDAO.java b/src/main/java/com/manumiguezz/crudapplication/dao/EmployeeDAO.java deleted file mode 100644 index e6f13fe..0000000 --- a/src/main/java/com/manumiguezz/crudapplication/dao/EmployeeDAO.java +++ /dev/null @@ -1,15 +0,0 @@ -//package com.manumiguezz.crudapplication.dao; -// -//import com.manumiguezz.crudapplication.entity.Employee; -// -//import java.util.List; -// -//public interface EmployeeDAO { -// List findAll(); -// -// Employee findById(int theId); -// -// Employee save(Employee theEmployee); -// -// void deleteByID(int theId); -//} diff --git a/src/main/java/com/manumiguezz/crudapplication/dao/EmployeeDAOJpaImpl.java b/src/main/java/com/manumiguezz/crudapplication/dao/EmployeeDAOJpaImpl.java deleted file mode 100644 index 6008827..0000000 --- a/src/main/java/com/manumiguezz/crudapplication/dao/EmployeeDAOJpaImpl.java +++ /dev/null @@ -1,50 +0,0 @@ -//package com.manumiguezz.crudapplication.dao; -// -//import com.manumiguezz.crudapplication.entity.Employee; -//import jakarta.persistence.EntityManager; -//import jakarta.persistence.TypedQuery; -//import org.springframework.beans.factory.annotation.Autowired; -//import org.springframework.stereotype.Repository; -// -//import java.util.List; -// -//@Repository -//public class EmployeeDAOJpaImpl implements EmployeeDAO { -// -// private EntityManager entityManager; -// -// @Autowired -// public EmployeeDAOJpaImpl(EntityManager theEntityManager) { -// entityManager = theEntityManager; -// } -// -// @Override -// public List findAll() { -// -// TypedQuery theQuery = entityManager.createQuery("from Employee", Employee.class); -// -// List employees = theQuery.getResultList(); -// -// return employees; -// } -// -// @Override -// public Employee findById(int theId) { -// Employee theEmployee = entityManager.find(Employee.class, theId); -// return theEmployee; -// } -// -// @Override -// public Employee save(Employee theEmployee) { -// // if ID of passed employee is equal to 0, it will insert a new one, else it will just update -// Employee dbEmployee = entityManager.merge(theEmployee); -// return dbEmployee; -// } -// -// @Override -// public void deleteByID(int theId) { -// Employee theEmployee = entityManager.find(Employee.class, theId); -// entityManager.remove(theEmployee); -// } -// -//} diff --git a/src/main/java/com/manumiguezz/crudapplication/rest/EmployeeRestController.java b/src/main/java/com/manumiguezz/crudapplication/rest/EmployeeRestController.java index 13a5acd..ad1fc54 100644 --- a/src/main/java/com/manumiguezz/crudapplication/rest/EmployeeRestController.java +++ b/src/main/java/com/manumiguezz/crudapplication/rest/EmployeeRestController.java @@ -1,9 +1,8 @@ package com.manumiguezz.crudapplication.rest; -import com.manumiguezz.crudapplication.dao.EmployeeDAO; import com.manumiguezz.crudapplication.entity.Employee; import com.manumiguezz.crudapplication.service.EmployeeService; -import com.manumiguezz.crudapplication.service.EmployeeServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -14,17 +13,20 @@ public class EmployeeRestController { private EmployeeService employeeService; - public EmployeeRestController (EmployeeService theEmployeeService) { + @Autowired + public EmployeeRestController(EmployeeService theEmployeeService) { employeeService = theEmployeeService; } @GetMapping("/employees") - public List findAll(){ + public List findAll() { return employeeService.findAll(); } + @GetMapping("/employees/{employeeId}") public Employee getEmployee(@PathVariable int employeeId) { + Employee theEmployee = employeeService.findById(employeeId); if (theEmployee == null) { @@ -34,35 +36,52 @@ public Employee getEmployee(@PathVariable int employeeId) { return theEmployee; } - @PostMapping ("/employees") + + @PostMapping("/employees") public Employee addEmployee(@RequestBody Employee theEmployee) { + theEmployee.setId(0); Employee dbEmployee = employeeService.save(theEmployee); + return dbEmployee; } @PutMapping("/employees") public Employee updateEmployee(@RequestBody Employee theEmployee) { + Employee dbEmployee = employeeService.save(theEmployee); + return dbEmployee; } - @DeleteMapping("/employees/{employeeId} ") + @DeleteMapping("/employees/{employeeId}") public String deleteEmployee(@PathVariable int employeeId) { + Employee tempEmployee = employeeService.findById(employeeId); if (tempEmployee == null) { - throw new RuntimeException("Employee id: " + employeeId + " not found"); + throw new RuntimeException("Employee id not found - " + employeeId); } - employeeService.deleteByID(employeeId); + employeeService.deleteById(employeeId); - return "Employee with id: " + employeeId + " was deleted"; + return "Deleted employee id - " + employeeId; } +} + + + + + + + + + + + -} diff --git a/src/main/java/com/manumiguezz/crudapplication/security/SecurityConfig.java b/src/main/java/com/manumiguezz/crudapplication/security/SecurityConfig.java new file mode 100644 index 0000000..cd5fe8b --- /dev/null +++ b/src/main/java/com/manumiguezz/crudapplication/security/SecurityConfig.java @@ -0,0 +1,49 @@ +package com.manumiguezz.crudapplication.security; + + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.provisioning.JdbcUserDetailsManager; +import org.springframework.security.provisioning.UserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; + +import javax.sql.DataSource; + +@Configuration +public class SecurityConfig { + + @Bean + public UserDetailsManager userDetailsManager (DataSource dataSource){ + JdbcUserDetailsManager jdbcUserDetailsManager = new JdbcUserDetailsManager(dataSource); + + jdbcUserDetailsManager + .setUsersByUsernameQuery("select user, psw, active from members where user=?"); + + jdbcUserDetailsManager + .setAuthoritiesByUsernameQuery("select user, role from roles where user=?"); + + return jdbcUserDetailsManager; + } + + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + + http.authorizeHttpRequests(configurer -> + configurer + .requestMatchers(HttpMethod.GET, "/api/employees").hasRole("EMPLOYEE") + .requestMatchers(HttpMethod.GET, "/api/employees/**").hasRole("EMPLOYEE") + .requestMatchers(HttpMethod.POST, "/api/employees").hasRole("TL") + .requestMatchers(HttpMethod.PUT, "/api/employees").hasRole("TL") + .requestMatchers(HttpMethod.DELETE, "/api/employees/**").hasRole("ADMIN") + ); + + http.httpBasic(Customizer.withDefaults()); + http.csrf(csrf -> csrf.disable()); + + return http.build(); + } +} diff --git a/src/main/java/com/manumiguezz/crudapplication/service/EmployeeService.java b/src/main/java/com/manumiguezz/crudapplication/service/EmployeeService.java index ff56fdb..ea2f973 100644 --- a/src/main/java/com/manumiguezz/crudapplication/service/EmployeeService.java +++ b/src/main/java/com/manumiguezz/crudapplication/service/EmployeeService.java @@ -1,16 +1,18 @@ package com.manumiguezz.crudapplication.service; + import com.manumiguezz.crudapplication.entity.Employee; import java.util.List; public interface EmployeeService { + List findAll(); Employee findById(int theId); Employee save(Employee theEmployee); - void deleteByID(int theId); + void deleteById(int theId); } diff --git a/src/main/java/com/manumiguezz/crudapplication/service/EmployeeServiceImpl.java b/src/main/java/com/manumiguezz/crudapplication/service/EmployeeServiceImpl.java index 6acf772..8964a1c 100644 --- a/src/main/java/com/manumiguezz/crudapplication/service/EmployeeServiceImpl.java +++ b/src/main/java/com/manumiguezz/crudapplication/service/EmployeeServiceImpl.java @@ -1,22 +1,21 @@ package com.manumiguezz.crudapplication.service; -import com.manumiguezz.crudapplication.dao.EmployeeDAO; + import com.manumiguezz.crudapplication.dao.EmployeeRepository; import com.manumiguezz.crudapplication.entity.Employee; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.Optional; @Service -public class EmployeeServiceImpl implements EmployeeService{ +public class EmployeeServiceImpl implements EmployeeService { private EmployeeRepository employeeRepository; @Autowired - public EmployeeServiceImpl (EmployeeRepository theEmployeeRepository) { + public EmployeeServiceImpl(EmployeeRepository theEmployeeRepository) { employeeRepository = theEmployeeRepository; } @@ -29,13 +28,13 @@ public List findAll() { public Employee findById(int theId) { Optional result = employeeRepository.findById(theId); - Employee theEmployee; + Employee theEmployee = null; if (result.isPresent()) { theEmployee = result.get(); } else { - throw new RuntimeException("Did not find employee id: " + theId); + throw new RuntimeException("Did not find employee id - " + theId); } return theEmployee; @@ -47,7 +46,13 @@ public Employee save(Employee theEmployee) { } @Override - public void deleteByID(int theId) { + public void deleteById(int theId) { employeeRepository.deleteById(theId); } } + + + + + + diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 4a4b39a..8e8ddad 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,4 +1,10 @@ spring.datasource.url=jdbc:mysql://localhost:3306/employee_directory spring.datasource.username=springstudent -spring.datasource.password=springstudent \ No newline at end of file +spring.datasource.password=springstudent + +spring.data.rest.base-path=/company-api +spring.data.rest.default-page-size=40 + +spring.security.user.name=example +spring.security.user.password=example \ No newline at end of file diff --git a/target/classes/application.properties b/target/classes/application.properties index 4a4b39a..8e8ddad 100644 --- a/target/classes/application.properties +++ b/target/classes/application.properties @@ -1,4 +1,10 @@ spring.datasource.url=jdbc:mysql://localhost:3306/employee_directory spring.datasource.username=springstudent -spring.datasource.password=springstudent \ No newline at end of file +spring.datasource.password=springstudent + +spring.data.rest.base-path=/company-api +spring.data.rest.default-page-size=40 + +spring.security.user.name=example +spring.security.user.password=example \ No newline at end of file diff --git a/target/classes/com/manumiguezz/crudapplication/dao/EmployeeRepository.class b/target/classes/com/manumiguezz/crudapplication/dao/EmployeeRepository.class new file mode 100644 index 0000000..1d2b904 Binary files /dev/null and b/target/classes/com/manumiguezz/crudapplication/dao/EmployeeRepository.class differ diff --git a/target/classes/com/manumiguezz/crudapplication/rest/EmployeeRestController.class b/target/classes/com/manumiguezz/crudapplication/rest/EmployeeRestController.class new file mode 100644 index 0000000..a82df70 Binary files /dev/null and b/target/classes/com/manumiguezz/crudapplication/rest/EmployeeRestController.class differ diff --git a/target/classes/com/manumiguezz/crudapplication/security/SecurityConfig.class b/target/classes/com/manumiguezz/crudapplication/security/SecurityConfig.class new file mode 100644 index 0000000..2b7ca08 Binary files /dev/null and b/target/classes/com/manumiguezz/crudapplication/security/SecurityConfig.class differ diff --git a/target/classes/com/manumiguezz/crudapplication/service/EmployeeService.class b/target/classes/com/manumiguezz/crudapplication/service/EmployeeService.class index fb6d74a..32b1e27 100644 Binary files a/target/classes/com/manumiguezz/crudapplication/service/EmployeeService.class and b/target/classes/com/manumiguezz/crudapplication/service/EmployeeService.class differ diff --git a/target/classes/com/manumiguezz/crudapplication/service/EmployeeServiceImpl.class b/target/classes/com/manumiguezz/crudapplication/service/EmployeeServiceImpl.class new file mode 100644 index 0000000..d74acc3 Binary files /dev/null and b/target/classes/com/manumiguezz/crudapplication/service/EmployeeServiceImpl.class differ