diff --git a/shoppingcart/pom.xml b/shoppingcart/pom.xml index 1a2d4e3a..2a4a47f1 100644 --- a/shoppingcart/pom.xml +++ b/shoppingcart/pom.xml @@ -15,7 +15,7 @@ Demo project for Spring Boot - 14 + 15 @@ -76,6 +76,25 @@ 2.9.2 + + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.security + spring-security-test + test + + + + org.springframework.security.oauth + spring-security-oauth2 + 2.3.6.RELEASE + + diff --git a/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/config/AuthorizationServerConfig.java b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/config/AuthorizationServerConfig.java new file mode 100644 index 00000000..1c956afb --- /dev/null +++ b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/config/AuthorizationServerConfig.java @@ -0,0 +1,133 @@ +package com.lambdaschool.shoppingcart.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; +import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; +import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; +import org.springframework.security.oauth2.provider.token.TokenStore; + +/** + * This class enables and configures the Authorization Server. The class is also responsible for granting authorization to the client. + * This class is responsible for generating and maintaining the access tokens. + */ +@Configuration +@EnableAuthorizationServer +public class AuthorizationServerConfig + extends AuthorizationServerConfigurerAdapter +{ + /** + * Client Id is the user name for the client application. It is read from the environment variable OAUTHCLIENTID + */ +// static final String CLIENT_ID = System.getenv("OAUTHCLIENTID"); + static final String CLIENT_ID = "lambda-client"; + + /** + * Client secret is the password for the client application. It is read from the environment variable OAUTHCLIENTSECRET + */ +// static final String CLIENT_SECRET = System.getenv("OAUTHCLIENTSECRET"); // read from environment variable + static final String CLIENT_SECRET = "lambda-secret"; + + /** + * We are using username and password to authenticate a user + */ + static final String GRANT_TYPE_PASSWORD = "password"; + + /** + * We are using the client id and client security combination to authorize the client. + * The client id and security can be base64 encoded into a single API key or code + */ + static final String AUTHORIZATION_CODE = "authorization_code"; + + /** + * Scopes are meant to limit what a user can do with the application as a whole. + * Here we allow the user to read from the application. + * Currently we are not implementing scope in our applications. We are just setting up the framework to do so. + */ + static final String SCOPE_READ = "read"; + + /** + * Scopes are meant to limit what a user can do with the application as a whole. + * Here we allow the user to write to the application. + * Currently we are not implementing scope in our applications. We are just setting up the framework to do so. + */ + static final String SCOPE_WRITE = "write"; + + /** + * Scopes are meant to limit what a user can do with the application as a whole. + * Here we say the user is trusted. + * Currently we are not implementing scope in our applications. We are just setting up the framework to do so. + */ + static final String SCOPE_TRUST = "trust"; + + /** + * Tells how long in seconds the access code should be kept valid. After this timeout, the user has to sign on again. + * set to -1 if you want the token to be valid forever. 1 * 60 * 60 would give us 1 hour. + */ + static final int ACCESS_TOKEN_VALIDITY_SECONDS = -1; + + /** + * The token store is configured in Security Config. However, the authorization server manages it + */ + @Autowired + private TokenStore tokenStore; + + /** + * The authentication server authenticates a user to that user user gets assigned an access token that is managed by the authorization server + */ + @Autowired + private AuthenticationManager authenticationManager; + + /** + * The authorization server must encrypt the client secret so needs to know what password encoder is in use. + */ + @Autowired + private PasswordEncoder encoder; + + /** + * Method to configure the Client Details Service for our application. This is created and managed by Spring. + * We just need to give it our custom configuration. + * + * @param configurer The ClientDetailsServiceConfigurer used in our application. Spring Boot Security created this for us. + * We just use it. + * @throws Exception if the configuration fails + */ + @Override + public void configure(ClientDetailsServiceConfigurer configurer) + throws + Exception + { + configurer.inMemory() + .withClient(CLIENT_ID) + .secret(encoder.encode(CLIENT_SECRET)) + .authorizedGrantTypes(GRANT_TYPE_PASSWORD, + AUTHORIZATION_CODE) + .scopes(SCOPE_READ, + SCOPE_WRITE, + SCOPE_TRUST) + .accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS); + } + + /** + * Connects are endpoints to our custom authentication server and token store. + * We can also rename the endpoints for certain oauth functions + * + * @param endpoints The Authorization Server Endpoints Configurer is created and managed by Spring Boot Security. + * We give the configurer some custom configuration and let it work! + * @throws Exception if the configuration fails + */ + @Override + public void configure(AuthorizationServerEndpointsConfigurer endpoints) + throws + Exception + { + endpoints.tokenStore(tokenStore) + .authenticationManager(authenticationManager); + // here instead of our clients requesting authentication at the endpoint /oauth/token, they request it at the endpoint /login + endpoints.pathMapping("/oauth/token", + "/login"); + } +} diff --git a/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/config/ResourceServerConfig.java b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/config/ResourceServerConfig.java new file mode 100644 index 00000000..e467f4bd --- /dev/null +++ b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/config/ResourceServerConfig.java @@ -0,0 +1,110 @@ +package com.lambdaschool.shoppingcart.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; +import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; +import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; +import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler; + +/** + * Once the client has gained authorization, users need to gain authentication. This class is response for handling that. + * It also configures which roles have access to which endpoints. So controls the users' access! + */ +@Configuration +@EnableResourceServer +public class ResourceServerConfig + extends ResourceServerConfigurerAdapter +{ + /** + * We can have multiple resource servers in place. This ties this resource server to this application + */ + private static final String RESOURCE_ID = "resource_id"; + + /** + * Tries this application to the resource server + * + * @param resources the resource server + */ + @Override + public void configure(ResourceServerSecurityConfigurer resources) + { + // stateless refers to only working with access tokens. Testing using a different schema + // so stateless must be false. + resources.resourceId(RESOURCE_ID) + .stateless(false); + } + + /** + * This method configures which roles can access which endpoints + * + * @param http Our HttpSecurity object that is maintains by Spring + * @throws Exception in case the configurations fails + */ + @Override + public void configure(HttpSecurity http) + throws + Exception + { + // our antMatchers control which roles of users have access to which endpoints + // we must order our antmatchers from most restrictive to least restrictive. + // So restrict at method level before restricting at endpoint level. + // permitAll = everyone and their brother + // authenticated = any authenticated, signed in, user + // hasAnyRole = must be authenticated and be assigned this role! + http.authorizeRequests() + .antMatchers("/", + "/h2-console/**", + "/swagger-resources/**", + "/swagger-resource/**", + "/swagger-ui.html", + "/v2/api-docs", + "/webjars/**", + "/createnewuser") + .permitAll() + .antMatchers(HttpMethod.POST, + "/users/**") + .hasAnyRole("ADMIN") + .antMatchers(HttpMethod.DELETE, + "/users/**") + .hasAnyRole("ADMIN") + .antMatchers(HttpMethod.PUT, + "/users/**") + .hasAnyRole("ADMIN") + .antMatchers("/users/**", + "/useremails/**", + "/oauth/revoke-token", + "/logout") + .authenticated() + .antMatchers("/roles/**") + .hasAnyRole("ADMIN") + .and() + .exceptionHandling() + .accessDeniedHandler(new OAuth2AccessDeniedHandler()); + + // http.requiresChannel().anyRequest().requiresSecure(); required for https + + // disable the creation and use of Cross Site Request Forgery Tokens. + // These tokens require coordination with the front end client that is beyond the scope of this class. + // See https://www.yawintutor.com/how-to-enable-and-disable-csrf/ for more information + http.csrf() + .disable(); + + // this disables all of the security response headers. This is necessary for access to the H2 Console. + // Normally, Spring Security would include headers such as + // Cache-Control: no-cache, no-store, max-age=0, must-revalidate + // Pragma: no-cache + // Expires: 0 + // X-Content-Type-Options: nosniff + // X-Frame-Options: DENY + // X-XSS-Protection: 1; mode=block + http.headers() + .frameOptions() + .disable(); + + // This application implements its own logout procedure so disable the one built into Spring Security + http.logout() + .disable(); + } +} diff --git a/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/config/SecurityConfig.java b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/config/SecurityConfig.java new file mode 100644 index 00000000..42ba00d9 --- /dev/null +++ b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/config/SecurityConfig.java @@ -0,0 +1,88 @@ +package com.lambdaschool.shoppingcart.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore; + +import javax.annotation.Resource; + +/** + * This class allows us to set standard security protocols to be used throughout the application, + * such as password encoding, location of token store, our implementation of users, among others + */ +@Configuration +@EnableWebSecurity +// This allows us to further restrict access to an endpoint inside of a controller. +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class SecurityConfig + extends WebSecurityConfigurerAdapter +{ + /** + * Allows us to customize the Authentication Manager. Normally, as we do here, we just take the defaults. + * + * @return The configured authentication manager + * @throws Exception In case our custom configurations do not work. + */ + @Override + @Bean + public AuthenticationManager authenticationManagerBean() + throws + Exception + { + return super.authenticationManagerBean(); + } + + /** + * Connects the user details used by Spring Security to our implementation of it + */ + @Resource(name = "securityUserService") + private UserDetailsService securityUserService; + + /** + * Ties our implementation of user details and password encoding to the Authentication Manager + * + * @param auth the connection to our authentication manager + * @throws Exception in case our custom configuration does not work + */ + @Autowired + public void globalUserDetails(AuthenticationManagerBuilder auth) + throws + Exception + { + auth.userDetailsService(securityUserService) + .passwordEncoder(encoder()); + } + + + /** + * We will store our token in memory. This way when the application exists all access tokens are invalidated. + * + * @return A token store configured to be used in memory + */ + @Bean + public TokenStore tokenStore() + { + return new InMemoryTokenStore(); + } + + /** + * Set our preferred encoder for our password + * + * @return A password will be encoded using the standard BCrypt method + */ + @Bean + public PasswordEncoder encoder() + { + return new BCryptPasswordEncoder(); + } +} diff --git a/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/config/SimpleCorsFilter.java b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/config/SimpleCorsFilter.java new file mode 100644 index 00000000..d5d855d8 --- /dev/null +++ b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/config/SimpleCorsFilter.java @@ -0,0 +1,68 @@ +package com.lambdaschool.shoppingcart.config; + +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Component; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Spring uses filters to manage web traffic. Here we manually add a CORS (Cross-Origin Resource Sharing) filter to the chain. + * Using the Order annotation, we tell Spring this is the most important filter. If this filter blocks a request, + * don't do anything else. Just block the request. + */ +@Component +@Order(Ordered.HIGHEST_PRECEDENCE) +public class SimpleCorsFilter + implements Filter +{ + @Override + public void doFilter( + ServletRequest servletRequest, + ServletResponse servletResponse, + FilterChain filterChain) + throws + IOException, + ServletException + { + // Convert our request and response to Http ones. If they are not Http ones, an exception would be thrown + // that would handled by our exception handler! + HttpServletResponse response = (HttpServletResponse) servletResponse; + HttpServletRequest request = (HttpServletRequest) servletRequest; + + // white list domains that can access this API. * says let everyone access it. To restrict access use something like + // response.setHeader("Access-Control-Allow-Origin", + // "https://lambdaschool.com/"); + response.setHeader("Access-Control-Allow-Origin", + "*"); + + // white list http methods that can be used with this API. * says lets them all work! To restrict access use something like + // response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE"); + response.setHeader("Access-Control-Allow-Methods", + "*"); + + // while list access headers that can be used with this API. * says lets them all work! To restrict access use something like + // response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization, content-type, access_token"); + response.setHeader("Access-Control-Allow-Headers", + "*"); + + // maximum seconds results can be cached + response.setHeader("Access-Control-Max-Age", + "3600"); + + if (HttpMethod.OPTIONS.name() + .equalsIgnoreCase(request.getMethod())) + { + response.setStatus(HttpServletResponse.SC_OK); + } else + { + filterChain.doFilter(servletRequest, + servletResponse); + } + } +} + diff --git a/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/controllers/LogoutController.java b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/controllers/LogoutController.java new file mode 100644 index 00000000..2d45de92 --- /dev/null +++ b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/controllers/LogoutController.java @@ -0,0 +1,31 @@ +package com.lambdaschool.shoppingcart.controllers; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +import javax.servlet.http.HttpServletRequest; + +@Controller +public class LogoutController +{ + @Autowired + private TokenStore tokenStore; + + @GetMapping(value = {"/oauth/revoke-token", "/logout"}, produces = "application/json") + public ResponseEntity logoutSelf(HttpServletRequest request) + { + String authHeader = request.getHeader("Authorization"); + if(authHeader != null) + { + String tokenValue = authHeader.replace("Bearer", "").trim(); + OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue); + tokenStore.removeAccessToken(accessToken); + } + return new ResponseEntity<>(HttpStatus.OK); + } +} diff --git a/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/controllers/UserController.java b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/controllers/UserController.java index 50737ff4..e688e981 100755 --- a/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/controllers/UserController.java +++ b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/controllers/UserController.java @@ -6,6 +6,8 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; @@ -34,6 +36,7 @@ public class UserController * @return JSON list of all users with a status of OK * @see UserService#findAll() UserService.findAll() */ + @PreAuthorize("hasAnyRole('ADMIN')") @GetMapping(value = "/users", produces = "application/json") public ResponseEntity listAllUsers() @@ -70,6 +73,7 @@ public ResponseEntity getUserById( * @return JSON object of the user you seek * @see UserService#findByName(String) UserService.findByName(String) */ + @PreAuthorize("hasAnyRole('ADMIN')") @GetMapping(value = "/user/name/{userName}", produces = "application/json") public ResponseEntity getUserByName( @@ -89,6 +93,7 @@ public ResponseEntity getUserByName( * @return A JSON list of users you seek * @see UserService#findByNameContaining(String) UserService.findByNameContaining(String) */ + @PreAuthorize("hasAnyRole('ADMIN')") @GetMapping(value = "/user/name/like/{userName}", produces = "application/json") public ResponseEntity getUserLikeName( @@ -111,6 +116,7 @@ public ResponseEntity getUserLikeName( * @throws URISyntaxException Exception if something does not work in creating the location header * @see UserService#save(User) UserService.save(User) */ + @PreAuthorize("hasAnyRole('ADMIN')") @PostMapping(value = "/user", consumes = "application/json") public ResponseEntity addNewUser( @@ -148,6 +154,7 @@ public ResponseEntity addNewUser( * @return status of OK * @see UserService#save(User) UserService.save(User) */ + @PreAuthorize("hasAnyRole('ADMIN')") @PutMapping(value = "/user/{userid}", consumes = "application/json") public ResponseEntity updateFullUser( @@ -194,6 +201,7 @@ public ResponseEntity updateUser( * @param id the primary key of the user you wish to delete * @return Status of OK */ + @PreAuthorize("hasAnyRole('ADMIN')") @DeleteMapping(value = "/user/{id}") public ResponseEntity deleteUserById( @PathVariable @@ -202,4 +210,14 @@ public ResponseEntity deleteUserById( userService.delete(id); return new ResponseEntity<>(HttpStatus.OK); } + + @GetMapping(value = "/myinfo", produces = "application/json") + public ResponseEntity getUserInfo() + { + User u = userService.findByName(SecurityContextHolder.getContext() + .getAuthentication() + .getName()); + + return new ResponseEntity<>(u, HttpStatus.OK); + } } \ No newline at end of file diff --git a/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/models/User.java b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/models/User.java index fcc02f61..04d4b98f 100644 --- a/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/models/User.java +++ b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/models/User.java @@ -1,11 +1,16 @@ package com.lambdaschool.shoppingcart.models; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import javax.persistence.*; import javax.validation.constraints.Email; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; /** @@ -168,11 +173,18 @@ public String getPassword() * * @param password the new password (String) for the user */ - public void setPassword(String password) + + public void setPasswordNoEncrypt(String password) { this.password = password; } + public void setPassword(String password) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + this.password = passwordEncoder.encode(password); + } + /** * Getter for user role combinations * @@ -212,4 +224,16 @@ public void setCarts(Set carts) { this.carts = carts; } + + @JsonIgnore + public List getAuthority() + { + List rtnList = new ArrayList<>(); + for (UserRoles r : this.roles) + { + String myRole = "ROLE_" + r.getRole().getName().toUpperCase(); + rtnList.add(new SimpleGrantedAuthority(myRole)); + } + return rtnList; + } } diff --git a/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/services/HelperFunctions.java b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/services/HelperFunctions.java index 164b08d0..6df3beb3 100644 --- a/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/services/HelperFunctions.java +++ b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/services/HelperFunctions.java @@ -18,4 +18,6 @@ public interface HelperFunctions */ List getConstraintViolation(Throwable cause); + boolean isAuthorizedToMakeChange(String username); + } diff --git a/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/services/HelperFunctionsImpl.java b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/services/HelperFunctionsImpl.java index d7b91930..0adda5a2 100644 --- a/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/services/HelperFunctionsImpl.java +++ b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/services/HelperFunctionsImpl.java @@ -1,7 +1,11 @@ package com.lambdaschool.shoppingcart.services; +import com.lambdaschool.shoppingcart.exceptions.ResourceNotFoundException; import com.lambdaschool.shoppingcart.models.ValidationError; import org.hibernate.exception.ConstraintViolationException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; @@ -67,4 +71,22 @@ public List getConstraintViolation(Throwable cause) } return listVE; } + + @Override + public boolean isAuthorizedToMakeChange(String username) + { + Authentication authentication = SecurityContextHolder.getContext() + .getAuthentication(); + if(username.equalsIgnoreCase(authentication.getName() + .toLowerCase()) || authentication.getAuthorities() + .contains(new SimpleGrantedAuthority("ROLE_ADMIN"))) + { + return true; + } + else + { + throw new ResourceNotFoundException(authentication.getName() + " not authorized to make change"); + } + } } + diff --git a/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/services/SecurityUserServiceImpl.java b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/services/SecurityUserServiceImpl.java new file mode 100644 index 00000000..151ea1a0 --- /dev/null +++ b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/services/SecurityUserServiceImpl.java @@ -0,0 +1,30 @@ +package com.lambdaschool.shoppingcart.services; + +import com.lambdaschool.shoppingcart.exceptions.ResourceFoundException; +import com.lambdaschool.shoppingcart.exceptions.ResourceNotFoundException; +import com.lambdaschool.shoppingcart.models.User; +import com.lambdaschool.shoppingcart.repository.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service(value = "securityUserServer") +public class SecurityUserServiceImpl implements UserDetailsService +{ + @Autowired + private UserRepository userRepository; + + @Transactional + @Override + public UserDetails loadUserByUsername(String s) throws ResourceFoundException + { + User user = userRepository.findByUsername(s.toLowerCase()); + if (user == null) + { + throw new ResourceNotFoundException("Invalid username or password"); + } + return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), user.getAuthority()); + } +} diff --git a/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/services/UserAuditing.java b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/services/UserAuditing.java index 725dcee1..533365e9 100644 --- a/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/services/UserAuditing.java +++ b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/services/UserAuditing.java @@ -1,6 +1,8 @@ package com.lambdaschool.shoppingcart.services; import org.springframework.data.domain.AuditorAware; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import java.util.Optional; @@ -22,7 +24,15 @@ public class UserAuditing public Optional getCurrentAuditor() { String uname; - uname = "SYSTEM"; + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication != null) + { + uname = authentication.getName(); + } else + { + uname = "SYSTEM"; + } + return Optional.of(uname); } } diff --git a/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/services/UserService.java b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/services/UserService.java index e95366f8..5de68e6c 100755 --- a/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/services/UserService.java +++ b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/services/UserService.java @@ -1,6 +1,7 @@ package com.lambdaschool.shoppingcart.services; import com.lambdaschool.shoppingcart.models.User; +import org.springframework.security.core.context.SecurityContext; import java.util.List; @@ -79,4 +80,5 @@ User update( * Deletes all record and their associated records from the database */ public void deleteAll(); -} \ No newline at end of file +} + diff --git a/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/services/UserServiceImpl.java b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/services/UserServiceImpl.java index f88ce29b..9f5e737f 100755 --- a/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/services/UserServiceImpl.java +++ b/shoppingcart/src/main/java/com/lambdaschool/shoppingcart/services/UserServiceImpl.java @@ -33,11 +33,20 @@ public class UserServiceImpl @Autowired private RoleService roleService; - public User findUserById(long id) throws - ResourceNotFoundException + @Autowired + private HelperFunctions helperFunctions; + + public User findUserById(long id) throws ResourceNotFoundException { - return userrepos.findById(id) - .orElseThrow(() -> new ResourceNotFoundException("User id " + id + " not found!")); + User currentUser = userrepos.findById(id).orElseThrow(()-> new ResourceNotFoundException("User id " + id + " not found")); + if (helperFunctions.isAuthorizedToMakeChange(currentUser.getUsername())) + { + return currentUser; + } + else + { + throw new ResourceNotFoundException("This user is not authorized to make changes"); + } } @Override