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