findAll();
+ User deleteUser(Integer id);
+}
+```
+
+Next, we will refactor our `UserService` class to properly implement
+the method required by `UserDetailsService` interface.
+
+First, modify your `UserService` class to be an implementer of the
+interface `IUserService`, and add the `@Transactional` annotation as well.
+This annotation will make sure that each `UserService` method completes all
+of it's actions successfully or none of them will take effect.
+
+```java
+@Service
+@Transactional
+public class UserService implements IUserService {
+```
+
+Once we add this interface, we will get an error saying that we must provide an
+implementation of `loadUserByUsername` from the `UserDetailsService` interface.
+
+This method will take in a username string and it needs to return a
+`UserDetails` object. For us, that will mean returning a `User` instance from
+the `org.springframework.security.core.userdetails` package. The [documentation
+for this constructor](https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/core/userdetails/User.html#%3Cinit%3E(java.lang.String,java.lang.String,java.util.Collection))
+shows that we need the username, the password hash, and a
+collection of the granted authorities for this user.
+
+Add the following method to your `UserService` class. Notice the call to the
+`getAuthorities` method that is not yet implemented.
+
+```java
+ @Override
+ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+ User user = userRepository.findByUsername(username);
+ if (user == null) {
+ throw new UsernameNotFoundException("Invalid username or password");
+ }
+ return new org.springframework.security.core.userdetails.User(user.getUsername(),
+ user.getPwHash(),
+ getAuthorities());
+ }
+```
+
+For now, we are going to implement the `getAuthorities` method to return the
+same authority for all users which is `ROLE_USER`. In the next lesson, we will
+make sure that the granted authorities are correct based on the actual role of
+each user.
+
+This helper method will return the authorities in the datatype required by the
+`UserDetails` instance, which is `Collection extends GrantedAuthority>`. Add
+the `getAuthorities` method to the `UserService` class as well, preferably
+at the bottom as it is a `private` method.
+
+```java
+ private Collection extends GrantedAuthority> getAuthorities() {
+ return Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"));
+ }
+```
+
+Lastly, we can update our `getCurrentUser` method to use the Spring Security
+framework for retrieving the current user from the `SecurityContextHolder`
+mentioned in the Authentication documentation, which tracks the currently
+authenticated user in an `Authentication` object.
+
+Let's modify our `getCurrentUser` method to make use of the framework:
+
+```java
+ public User getCurrentUser() {
+ Authentication auth = SecurityContextHolder.getContext().getAuthentication();
+ if (auth == null || !auth.isAuthenticated()) {
+ return null;
+ }
+ return findByUsername(auth.getName());
+ }
+```
+
+### Authorization and Request Filtering with `WebSecurity`
+
+Our current implementation of request filtering has done a good job of teaching
+us about the basics of user authentication. We currently implement the
+`HandlerInterceptor` interface so that we can check if a user is stored in the
+`HttpSession` before a request is handled by our controllers. If there is not
+an authenticated user stored in session, we redirect them to the login page.
+
+Spring Security 6 framework has some built-in annotations and methods that will
+do the same logic but will handle the user authentication and session management
+internally.
+
+Take a look at the [Spring Security documentation](https://docs.spring.io/spring-security/reference/servlet/authorization/authorize-http-requests.html)
+that talks about how we will authorize each request. With the authentication
+that we set up in the previous section, the `HttpSecurity` instance can be used
+to setup request filtering.
+
+To begin, we can **remove** the `AuthenticationFilter` class entirely, as well
+as the `WebApplicationConfig` class.
+
+Next, create a new class `WebSecurity` in the `security` package. This class
+will set up the `SecurityFilterChain` described in the [Authorizing
+Requests](https://docs.spring.io/spring-security/reference/servlet/authorization/authorize-http-requests.html#authorize-requests)
+section of the documentation.
+
+This class should also include `UserService` and `PasswordEncoder` autowired
+fields.
+
+```java
+@Configuration
+@EnableWebSecurity
+@EnableMethodSecurity
+@EnableTransactionManagement
+public class WebSecurity {
+
+ @Autowired
+ private UserService userService;
+
+ @Autowired
+ private PasswordEncoder passwordEncoder;
+}
+```
+
+1. `@Configuration` annotation lets Spring know that this class will set up
+a `@Bean`
+1. `@EnableWebSecurity` annotation lets Spring know that this class will set
+up the `SecurityFilterChain` bean
+1. `@EnableMethodSecurity` annotation gives the ability to use some helpful
+annotations to secure specific controller methods
+1. `@EnableTransactionManagement` annotation allows our Spring project to use
+the `@Transactional` annotation elsewhere
+
+To set up the `SecurityFilterChain`, we will use method chaining to build an
+`http` object that contains filtering rules for requests, meaning which
+routes are permitted and which ones require authentication,
+as well as error routes. We build this filter chain using a design pattern known
+as the **builder pattern**. This [helpful article](https://www.springcloud.io/post/2023-03/spring-security-design-patterns/#gsc.tab=0)
+lists some of different design patterns used in the Spring framework, for more
+context.
+
+Add the following method to the `WebSecurity` class:
+
+```java
+ @Bean
+ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+ http
+ .authorizeHttpRequests(authorize -> authorize
+ .requestMatchers("/login",
+ "/register",
+ "/logout",
+ "/error",
+ "/css/**",
+ "/js/**",
+ "/img/**").permitAll()
+ .anyRequest().authenticated()
+ )
+ .csrf(csrf -> csrf.disable())
+ .logout(logout -> logout
+ .logoutUrl("/logout")
+ .invalidateHttpSession(true)
+ .clearAuthentication(true)
+ .logoutSuccessUrl("/login")
+ .permitAll()
+ )
+ .securityContext((securityContext) -> securityContext
+ .securityContextRepository(securityContextRepository())
+ .requireExplicitSave(true)
+ )
+ .exceptionHandling(exception -> exception
+ .authenticationEntryPoint(authenticationEntryPoint())
+ );
+ return http.build();
+ }
+```
+
+With each of the methods in this method chaining sequence, we are setting up
+a portion of the security filter. The `exceptionHandling` and `securityContext`
+methods require a custom `@Bean` method and class so that we can access the
+objects in another class.
+
+First, we'll add a `SecurityContextRepository` bean which will implement the
+storage of our `SecurityContext` across different requests, so that we can
+remember who is logged in. Think of this as storing the `SecurityContext` which
+contains our `Authentication` information in the session.
+
+Add the following method to the `WebSecurity` class:
+
+```java
+ @Bean
+ public SecurityContextRepository securityContextRepository() {
+ return new DelegatingSecurityContextRepository(
+ new RequestAttributeSecurityContextRepository(),
+ new HttpSessionSecurityContextRepository()
+ );
+ }
+```
+
+Next, add the `authenticationEntryPoint` method to the `WebSecurity` class:
+
+```java
+ @Bean
+ public AuthenticationEntryPoint authenticationEntryPoint() {
+ return new CustomAuthenticationEntryPoint();
+ }
+```
+
+After adding this method, we'll need to create the
+`CustomAuthenticationEntryPoint` which will be responsible for sending errors
+to either `/login` route for unauthenticated requests or `/error` route
+otherwise.
+
+Create this new class in the `security` package.
+
+```java
+@Component
+public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
+ @Override
+ public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
+ throws IOException, ServletException {
+ Authentication auth = SecurityContextHolder.getContext().getAuthentication();
+ if (auth == null || !auth.isAuthenticated()) {
+ response.sendRedirect(request.getContextPath() + "/login");
+ } else {
+ response.sendRedirect(request.getContextPath() + "/error/403");
+ }
+ }
+}
+```
+
+Our last step to enable authorization in Spring Security is to set up our
+custom `AuthenticationManager` which will use our `UserService` and
+`PasswordEncoder` instances (from the autowired fields) to get the
+`UserDetails` instance.
+
+Add this last method to the `WebSecurity` class:
+
+```java
+ @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
+ public AuthenticationManager authenticationManagerBean(HttpSecurity http) throws Exception {
+ AuthenticationManagerBuilder authenticationManagerBuilder =
+ http.getSharedObject(AuthenticationManagerBuilder.class);
+
+ authenticationManagerBuilder.userDetailsService(userService).passwordEncoder(passwordEncoder);
+
+ return authenticationManagerBuilder.build();
+ }
+```
+
+### Refactoring `AuthenticationController`
+
+Recall that we have plugged in to the Spring Security framework by using
+`UserDetailsService` interface and relying on `Authentication` objects
+from `SecurityContextHolder`.
+
+In the `AuthenticationController` we can now remove some previous methods
+we needed when we were managing users in session.
+
+First, add a field for the `AuthenticationManager` and the
+`SecurityContextRepository`.
+
+```java
+ @Autowired
+ private AuthenticationManager authManager;
+
+ @Autowired
+ private SecurityContextRepository securityContextRepository;
+```
+
+Next, we can **remove** the `getUserFromSession` and `setUserInSession` methods
+entirely.
+
+Lastly, we are going to update the `processLoginForm` to use the
+`SecurityContextHolder` and `Authentication` objects for user
+session management. The `processLoginForm` is going to completely change with
+this refactoring, so you can start by **deleting** the contents of the method.
+We will start from scratch (including our error check and the title of the page
+in case of errors). Also, add the parameter for `HttpServletResponse` to the
+method.
+
+```java{ hl_lines="4" }
+ @PostMapping("/login")
+ public String processLoginForm(@ModelAttribute @Valid LoginFormDTO loginFormDTO,
+ Errors errors, HttpServletRequest request,
+ HttpServletResponse response,
+ Model model) {
+ model.addAttribute("title", "Log In");
+ if (errors.hasErrors()) {
+ return "login";
+ }
+ }
+```
+
+The next portion of this method will be a `try/catch` block that protects from
+the possibility of an `AuthenticationException`.
+
+Take a look at the [AuthenticationManager documentation](https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/authentication/AuthenticationManager.html)
+which has a method `authenticate()`. We will create a Username/Password token
+that we send to the `authenticate()` method for username/password validation.
+We will also use the `securityContextRepository` field to save the new
+`SecurityContext` established by the user login. If we catch an
+`AuthenticationException` instance, we will let the user know that the
+username or password were incorrect.
+
+Add the following block to your `processLoginForm` method:
+
+```java
+ try {
+ UsernamePasswordAuthenticationToken token =
+ UsernamePasswordAuthenticationToken.unauthenticated(
+ loginFormDTO.getUsername(),
+ loginFormDTO.getPassword()
+ );
+ Authentication authentication =
+ authManager.authenticate(token);
+
+ SecurityContext context = SecurityContextHolder.createEmptyContext();
+ context.setAuthentication(authentication);
+ this.securityContextRepository.saveContext(context, request, response);
+
+ return "redirect:";
+ } catch (AuthenticationException ex) {
+ errors.rejectValue("username", "bad.credentials", "Invalid e-mail or password");
+ return "/login";
+ }
+```
+
+Last, weirdly enough, we can remove our request handler for logout, as we
+included the `/logout` route as part of our security filter setup in
+`WebSecurity`. Navigating to `/logout` will invalidate the security session
+automatically.
+
+### Updating Error Handling
+
+When a user tries to access a route that does not exist, or a resource id
+that they don't own, we need to send them to a predesigned error page. We
+can do that with exception handling and automatic routing to an error
+controller.
+
+#### Adding `ErrorController`
+
+In the `controllers` package, create a class named `ErrorController`. This
+class will handle `ResourceNotFoundException` and `BadRequestException`.
+
+```java
+@ControllerAdvice
+public class ErrorController {
+
+ @ExceptionHandler(ResourceNotFoundException.class)
+ @ResponseStatus(HttpStatus.NOT_FOUND)
+ public String exception(final ResourceNotFoundException ex, final Model model) {
+ model.addAttribute("message", ex.getMessage());
+ return "error/404";
+ }
+
+ @ExceptionHandler(BadRequestException.class)
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ public String exception(final BadRequestException ex, final Model model) {
+ model.addAttribute("message", ex.getMessage());
+ return "error/400";
+ }
+
+}
+```
+
+Next, we need to create the `BadRequestException` in the `exception` package:
+
+```java
+public class BadRequestException extends RuntimeException {
+ public BadRequestException() {
+
+ }
+
+ public BadRequestException(String message) {
+ super(message);
+ }
+
+ public BadRequestException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public BadRequestException(Throwable cause) {
+ super(cause);
+ }
+
+ public BadRequestException(String message, Throwable cause, boolean enableSuppression,
+ boolean writeableStackTrace) {
+ super(message, cause, enableSuppression, writeableStackTrace);
+ }
+}
+```
+
+The `ErrorController` will handle exceptions and the `/error` route, but we need
+to add some views for it to display.
+
+In the `resources/templates/error/` directory, create a Thymeleaf view named
+`400.html`:
+
+```html
+
+
+
+
+
+
+
+
+
Incorrect data provided
+
+
+
+
+```
+
+Create `403.html` that has the same content except a message that says:
+
+```html
+
+
You have no access to this site
+
+```
+
+Create `404.html` that has the following message:
+
+```html
+
+```
+
+That wraps up our Spring Security refactoring. Be sure to test and
+make sure that register and login work, as well as proper loading of
+and creation of user data.
+
+The next lesson will add some new roled-based features for users, limiting
+access to the roles that users are assigned.
+
+
diff --git a/content/authentication/next-steps/bonus-module/user-data/_index.md b/content/authentication/next-steps/bonus-module/user-data/_index.md
new file mode 100644
index 0000000..c81da1f
--- /dev/null
+++ b/content/authentication/next-steps/bonus-module/user-data/_index.md
@@ -0,0 +1,298 @@
+---
+title: "User Owned Data"
+date: 2023-11-14T09:28:27-05:00
+draft: false
+weight: 1
+originalAuthor: Ben Clark # to be set by page creator
+originalAuthorGitHub: brclark # to be set by page creator
+reviewer: # to be set by the page reviewer
+reviewerGitHub: # to be set by the page reviewer
+lastEditor: # update any time edits are made after review
+lastEditorGitHub: # update any time edits are made after review
+lastMod: # UPDATE ANY TIME CHANGES ARE MADE
+---
+
+With the authentication filter additions, our application now requires users
+to log in before they can access any features of the site. However, users
+will want the data that they create to be associated with their account. How
+can we ensure that the events and categories that a user creates are
+associated with their account?
+
+Users *own* their data when the entities that they create (events, categories,
+etc) have their `user_id` associated with each new entity as a foreign key. That
+would allow us to, say "get all events for a specific user".
+
+## Creating User Specific Data
+
+{{% notice blue Note "rocket" %}}
+The code for this section begins with the
+[auth-filter branch](https://github.com/LaunchCodeEducation/CodingEventsJava/tree/auth-filter)
+and ends with the
+[user-data branch](https://github.com/LaunchCodeEducation/CodingEventsJava/tree/user-data)
+of the `CodingEventsJava` repository.
+{{% /notice %}}
+
+### Updating the models with a `User` field
+
+We need to set up a **One-To-Many** relationship between the `User` model and
+the different data models (`Event`, `EventCategory`, `Tag`).
+
+Open the `Event` model and add a `User` field to the field definitions. Call it `creator`
+and give it a `@ManyToOne` annotation.
+
+```java
+ @ManyToOne
+ private User creator;
+```
+
+We'll also need to add a getter and setter for `creator`.
+
+```java
+ public User getCreator() {
+ return creator;
+ }
+
+ public void setCreator(User creator) {
+ this.creator = creator;
+ }
+```
+
+Repeat the above steps to add the `creator` field/getters/setters to
+`EventCategory`.
+
+{{% notice blue Note "rocket" %}}
+The `Tag` resource will be your task to update throughout this bonus module. We
+will leave hints that you should continue updating that resource but will not
+explicitly include instructions on keeping the tag feature updated.
+
+You should update the `Tag` class as well to track `User creator`.
+{{% /notice %}}
+
+### Saving the `User` when creating new data
+
+We can now store a `User` as the creator of an `Event`/`EventCategory`/ `Tag`.
+Next, we need to make sure that the currently logged-in user is set as the creator
+before saving new entries. Let's update the `EventController` to set
+the `creator` field in new events.
+
+To get the currently logged-in user in `EventController`, we need references to
+the `AuthenticationController` and the `HttpSession`.
+
+In `EventController`, add the following below your other autowired fields:
+
+```java
+ @Autowired
+ private AuthenticationController authController;
+```
+
+With that reference, we can now get the current logged-in user during the POST
+handler for create. We need to add a parameter for the incoming `HttpSession`
+so that we can get the currently logged in user from the `authController`. If
+there are no errors in the form, we set the creator of the `newEvent` to
+`currUser`.
+
+```java{hl_lines="3-4 10"}
+ @PostMapping("create")
+ public String processCreateEventForm(@ModelAttribute @Valid Event newEvent,
+ Errors errors, Model model, HttpSession session) {
+ User currUser = authController.getUserFromSession(session);
+ if(errors.hasErrors()) {
+ model.addAttribute("title", "Create Event");
+ return "events/create";
+ }
+
+ newEvent.setCreator(currUser);
+
+ eventRepository.save(newEvent);
+ return "redirect:/events";
+ }
+```
+
+We need to repeat the above steps for the `EventCategoryController` and `TagController`.
+
+### Retrieving user data from database
+
+In previous lessons when we created a `@ManyToOne` field,
+we have included a corresponding `@OneToMany` reference in
+the appropriate model --- for example, the way we define
+`@OneToMany List events` in the `EventCategory` model.
+
+Rather than including `@OneToMany` relationships in the `User` model for events, categories
+and tags, we will instead use our `CrudRepository` interfaces and define custom queries that
+will achieve what we need.
+
+{{% notice blue Note "rocket" %}}
+You can create custom queries to the database using the intuitive query builder.
+We have used the built-in methods before like `findById`, `findAll`, and `save`.
+We can create new queries by defining the methods in our repository interfaces.
+
+For example, defining an `EventRepository` method
+`List findAllByCategory(EventCategory category)` gives us the ability to retrieve
+all events for a given category from the database.
+
+More info on the query builder can be found in the [SpringDataJPA docs](https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories)
+{{% /notice %}}
+
+Let's add custom queries to the `EventRepository`, `EventCategoryRepository`,
+and `TagRepository` interfaces that will allow us to retrieve all entries for a given `User`.
+
+In `EventRepository` add:
+
+```java{hl_lines="4-5"}
+@Repository
+public interface EventRepository extends CrudRepository {
+
+ List findAllByCreator(User creator);
+ Optional findByIdAndCreator(Integer id, User creator);
+}
+```
+
+`findAllByCreator` will retrieve the list of events for the associated user.
+`findByIdAndCreator` will attempt to retrieve an event based on the event ID,
+BUT it will give a null response if the corresponding event is not associated with
+the `creator` argument.
+
+Repeat the above steps to add the corresponding methods
+to the `EventCategoryRepository` and `TagRepository`.
+
+### Passing user data to views
+
+When users look at the tables for "All Events" or "All Categories", they should see
+the data that they created. Similarly, when users are creating new events, they should
+only see category options that they created.
+
+Let's update the controllers to retrieve the appropriate user data. This will involve
+the `AuthenticationController`, `HttpSession`, and the repository methods we added.
+
+#### Updating `EventController`
+
+First, we'll update `displayEvents` method that handles `GET /events?categoryId=` requests. We will receive the `HttpSession` as a param, use it to get the current user with `authController`,
+and finally pass the user to the `eventRepository` methods that we created.
+
+```java{hl_lines="2-3 7 9"}
+ @GetMapping
+ public String displayEvents(@RequestParam(required = false) Integer categoryId, Model model, HttpSession session) {
+ User currUser = authController.getUserFromSession(session);
+
+ if (categoryId == null) {
+ model.addAttribute("title", "All Events");
+ model.addAttribute("events", eventRepository.findAllByCreator(currUser));
+ } else {
+ Optional result = eventCategoryRepository.findByIdAndCreator(categoryId, currUser);
+ if (result.isEmpty()) {
+ model.addAttribute("title", "Invalid Category ID: " + categoryId);
+ } else {
+ EventCategory category = result.get();
+ model.addAttribute("title", "Events in category: " + category.getName());
+ model.addAttribute("events", category.getEvents());
+ }
+ }
+
+ return "events/index";
+ }
+```
+
+In the `displayCreateEventForm` method, we need to pass in the user-created categories instead
+of passing in all categories. Update your function like below.
+
+```java{hl_lines="2-3 6"}
+ @GetMapping("create")
+ public String displayCreateEventForm(Model model, HttpSession session) {
+ User currUser = authController.getUserFromSession(session);
+ model.addAttribute("title", "Create Event");
+ model.addAttribute(new Event());
+ model.addAttribute("categories", eventCategoryRepository.findAllByCreator(currUser));
+ return "events/create";
+ }
+```
+
+Don't forget to update the same in the **error** case of the POST request handler. If
+there are form errors, we want to pass the user-created categories back to the form.
+
+```java{hl_lines="7"}
+ @PostMapping("create")
+ public String processCreateEventForm(@ModelAttribute @Valid Event newEvent,
+ Errors errors, Model model, HttpSession session) {
+ User currUser = authController.getUserFromSession(session);
+ if(errors.hasErrors()) {
+ model.addAttribute("title", "Create Event");
+ model.addAttribute("categories", eventCategoryRepository.findAllByCreator(currUser));
+ return "events/create";
+ }
+
+ newEvent.setCreator(currUser);
+
+ eventRepository.save(newEvent);
+ return "redirect:/events";
+ }
+```
+
+When we display the delete events form, we want to make sure it displays the user-created
+events. Let's repeat the same procress to retrieve events for the current user in the
+`displayDeleteEventsForm` method.
+
+```java{hl_lines="2-3 5"}
+ @GetMapping("delete")
+ public String displayDeleteEventForm(Model model, HttpSession session) {
+ User currUser = authController.getUserFromSession(session);
+ model.addAttribute("title", "Delete Events");
+ model.addAttribute("events", eventRepository.findAllByCreator(currUser));
+ return "events/delete";
+ }
+```
+
+Finally, we want to make sure that the event details page will show events owned by the
+currently logged in user and reject any event ID's owned by other users. Once again,
+we'll retrieve the current user and the event based on its ID and the current user.
+
+In the `displayEventDetails` method, let's add:
+
+```java{hl_lines="2-5"}
+ @GetMapping("detail")
+ public String displayEventDetails(@RequestParam Integer eventId, Model model, HttpSession session) {
+ User currUser = authController.getUserFromSession(session);
+
+ Optional result = eventRepository.findByIdAndCreator(eventId, currUser);
+
+ if (result.isEmpty()) {
+ model.addAttribute("title", "Invalid Event ID: " + eventId);
+ } else {
+ Event event = result.get();
+ model.addAttribute("title", event.getName() + " Details");
+ model.addAttribute("event", event);
+ }
+
+ return "events/detail";
+ }
+```
+
+The rest of the function can stay the same. If a valid event ID is provided but
+the current user does not own it, then the `Optional` will contain a null value.
+
+#### Updating `EventCategoryController`
+
+Let's update the `displayAllCategories` method that handles `GET
+/eventCategories` requests. We can add the same `HttpSession` reference and
+use `authController` to retrieve the current user. We'll pass the categories
+for the current user as the model attribute.
+
+```java{hl_lines="2-3 5"}
+ @GetMapping
+ public String displayAllCategories(Model model, HttpSession session) {
+ User currUser = authController.getUserFromSession(session);
+ model.addAttribute("title", "All Categories");
+ model.addAttribute("categories", eventCategoryRepository.findAllByCreator(currUser));
+ return "eventCategories/index";
+ }
+```
+
+Luckily, the views for the `Event` and `EventCategory` resources will not need
+to be updated.
+
+The last controller to update is the `TagController`, which we will leave for you
+to complete.
+
+In the next lesson, we will expand the use of **Data Transfer Objects** or DTOs
+to decouple our database models from our forms, and we will make use of
+**Service** classes as a layer between the **Controller** and **Repository**.
+
diff --git a/content/authentication/next-steps/bonus-module/user-roles-privileges/_index.md b/content/authentication/next-steps/bonus-module/user-roles-privileges/_index.md
new file mode 100644
index 0000000..e136131
--- /dev/null
+++ b/content/authentication/next-steps/bonus-module/user-roles-privileges/_index.md
@@ -0,0 +1,431 @@
+---
+title: "User Roles & Privileges"
+date: 2023-11-08T23:36:10-06:00
+draft: false
+weight: 3
+originalAuthor: Ben Clark # to be set by page creator
+originalAuthorGitHub: brclark # to be set by page creator
+reviewer: # to be set by the page reviewer
+reviewerGitHub: # to be set by the page reviewer
+lastEditor: # update any time edits are made after review
+lastEditorGitHub: # update any time edits are made after review
+lastMod: # UPDATE ANY TIME CHANGES ARE MADE
+---
+
+We are going to add some additional features to Coding Events in the next three
+lessons. We currently have ability for users to manage their own data. Our next
+feature is to bring roles and privileges to our users. We will be able to
+configure users as admin, event creator, or event attendee.
+
+With roles and privileges in place, we are able to restrict certain functions
+to specific users. This is a common feature of many applications. Consider how
+we might associate users to the roles they hold, and how we might associate
+privileges to roles.
+
+Also, consider that your future apps may not need both roles AND privileges,
+and may just require one or the other. There is flexibility in how you design
+and implement future projects.
+
+## Adding Roles & Privileges to Users
+
+{{% notice blue Note "rocket" %}}
+The code for this section begins with the
+[add-service-dto branch](https://github.com/LaunchCodeEducation/CodingEventsJava/tree/add-service-dto)
+and ends with the
+[user-roles-privileges branch](https://github.com/LaunchCodeEducation/CodingEventsJava/tree/user-roles-privileges)
+of the `CodingEventsJava` repository.
+{{% /notice %}}
+
+This lesson describes how to add new models for `Role` and `Privilege` and
+associate them with the `User` model. This lesson will *not* add role-based
+functionality. That will be added in another lesson.
+
+Some definitions:
+
+- **Privilege**: access to a function or feature, such as `READ_EVENTS` or
+`CREATE_EVENTS`
+- **Role**: can be assigned to a user and combines multiple privileges in a
+distinct role, such as `ROLE_ADMIN` or `ROLE_CREATOR`
+
+### `Role` & `Privilege` Models
+
+Our `Role` model will need a database relationship with the `Privilege` model. We
+can say that a role can have many privileges, and a privilege can belong to many
+roles, giving us a **Many-to-Many** relationship.
+
+#### Adding the `Privilege` Model
+
+Create a new `Privilege` in your `models` package and make this class
+inherit from `AbstractEntity` and require the `@Entity` annotation.
+
+This class will only require a `String name` field to track what the privilege
+is. Add the field, constructors (including the empty constructor), getter/setter,
+and `toString` method for this class.
+
+In addition to this Model, we need to create some predefined privilege types
+for our application, which we can do with an `enum`. We will define privileges
+for being able to CRUD events and users. Some users will only have privileges
+to *read events* while others will have privileges to *read and create events*.
+
+Create a new `enum` in `models` package named `PrivilegeType`.
+
+```java
+public enum PrivilegeType {
+ READ_EVENTS,
+ CREATE_EVENTS,
+ DELETE_EVENTS,
+ READ_USERS,
+ UPDATE_USERS,
+ DELETE_USERS
+}
+```
+
+#### Adding the `Role` Model
+
+Similar to our `Privilege` model, we need to create a `Role` model that
+inherits from `AbstractEntity` and defines a field `String name`, along
+with the constructors, getter/setter, and `toString` method.
+
+Create the `Role` class within your `models` package.
+
+Once you have created this base for the `Role` class, we have to define
+the relationship between a `Role` object and `Privilege` object. We are
+going to define a **Many-to-Many** relationship, but we are going to do
+it slightly differently than we did in the previous chapter.
+
+Add the following field to the `Role` class after the `name` field definition.
+
+```java
+ @ManyToMany(fetch = FetchType.EAGER)
+ @JoinTable(
+ name = "roles_privileges",
+ joinColumns = @JoinColumn(
+ name = "role_id", referencedColumnName = "id"),
+ inverseJoinColumns = @JoinColumn(
+ name = "privilege_id", referencedColumnName = "id"))
+ private Collection privileges;
+```
+
+Here, we are defining the `@ManyToMany` relationship between roles and
+privileges, and we are manually defining the *Join Table* that we want
+to be created. This requires us to specify which fields in the join
+table are foreign keys used to link roles to privileges. It is not
+required to define the join table in this way, but it gives us an example
+of how we can have more control over the database tables via our ORM
+definitions.
+
+Add a constructor to set the `name` and a no-argument constructor as well. Also,
+add a getter/setter for the `privileges` field as well.
+
+Similar to the `PrivilegeType` definition we added, we need a `RoleType`
+enum definition to specify the types of roles our app allows. For now,
+we will say that there can be *admin*, *event creator*, and *regular* users.
+
+Add a new `enum` definition `RoleType` to the `models` package.
+
+```java
+public enum RoleType {
+ ROLE_USER("User"),
+ ROLE_ORGANIZER("Organizer"),
+ ROLE_ADMIN("Admin");
+
+ private final String displayName;
+
+ RoleType(String displayName) {
+ this.displayName = displayName;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+}
+```
+
+Admin users will be able to manage user accounts. Event creators will be able
+to create new events for the event listing. Regular users will be able to read
+the event listing and RSVP to the events they want to attend.
+
+Lastly, we need a special getter in the `Role` class that allows us to retrieve
+the `RoleType` enum from a `Role` object. Add the following method to your `Role`
+class:
+
+```java
+ public RoleType getType() {
+ return RoleType.valueOf(name);
+ }
+```
+
+#### Associating Users with Roles
+
+Our `User` model needs the ability to be assigned certain roles. In this regard,
+we need to set up a **Many-to-Many** relationship between `User` and `Role`.
+
+We will make a few other additions to our `User` model as well, such as a
+constraint on unique usernames, and a field to store when the user account
+was created.
+
+First, add this `@Table` annotation above the `User` class (after `@Entity`):
+
+```java
+@Table(uniqueConstraints = @UniqueConstraint(columnNames = "username"))
+```
+
+Next, we will add two new fields:
+
+```java
+ private LocalDateTime createDate;
+
+ @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
+ @JoinTable(
+ name="users_roles",
+ joinColumns = @JoinColumn(
+ name = "user_id", referencedColumnName = "id"),
+ inverseJoinColumns = @JoinColumn(
+ name = "role_id", referencedColumnName = "id"))
+ private Collection roles;
+```
+
+The `createDate` field will store info about when the user account is created.
+And similar to our Many-to-Many relationship between `Role` and `Privilege`,
+here we have manually set up the join table for our M2M relationship between
+`User` and `Role`.
+
+Next, let's add a new constructor that would allow us to initialize a `User`
+object and specify their roles:
+
+```java
+ public User(String username, String pwHash, Collection roles) {
+ this.username = username;
+ this.pwHash = pwHash;
+ this.roles = roles;
+ }
+```
+
+Our `createDate` field will be populated by a special method that runs
+automatically. Add this method after the constructors:
+
+```java
+ @PrePersist
+ public void setUpCreateDate() {
+ createDate = LocalDateTime.now();
+ }
+```
+
+Notice the `@PrePersist` annotation, which will automatically call this method
+before a new `User` instance gets created in the database.
+
+Lastly, we need a getter/setter for our the `roles` field. Add your getter/
+setter below the other methods.
+
+### Adding `Role` & `Privilege` Repositories
+
+Now that we have models defined for our database schema, we need to define a
+repository interface for each class so that we can interact with their database
+entries. We will define and override some of the repository methods to give more
+customized control over the database.
+
+First, let's create the `PrivilegeRepository` interface in the `data` package:
+
+```java
+@Repository
+public interface PrivilegeRepository extends CrudRepository {
+
+ Privilege findByName(String name);
+}
+```
+
+The `findByName` method creates a custom database query that allows us to
+provide the name of a privilege and retrieve the `Privilege` object.
+
+Next, let's create the `RoleRepository` interface in the `data` package.
+
+```java
+@Repository
+public interface RoleRepository extends CrudRepository {
+
+ Role findByName(String name);
+}
+```
+
+### Preloading Initial Data
+
+Until this point, if we needed any data in our database, we had to directly
+update the database or use the forms in our app to create new data. Sometimes,
+we have relational data that we need preloaded into the database, and we
+want to be certain it is added before our application is served to users.
+
+In this case, we can set up a component that will preload the database with
+some specific entries *when the application boots*. This requires us to listen
+for an `ApplicationReadyEvent` and trigger some writes to the database when
+it occurs.
+
+Create a new class `InitialDataLoader` within the `data`
+package. The following code block introduces some new Spring annotations:
+
+```java
+@Component
+@Transactional
+public class InitialDataLoader implements ApplicationListener {
+
+ @Autowired
+ private RoleRepository roleRepository;
+
+ @Autowired
+ private PrivilegeRepository privilegeRepository;
+
+ @Autowired
+ private UserRepository userRepository;
+
+ @Autowired
+ private PasswordEncoder passwordEncoder;
+}
+```
+
+Setting up our `InitialDataLoader` class, it has `@Component` and
+`@Transactional` annotations. `@Component` tells Spring to manage this class
+instance similar to `@Controller`, and it is necessary so that the class
+can handle the `ApplicationListener` event. `@Transactional` is applied to the
+whole class and makes sure that our method for loading data either applies
+successfully or not at all.
+
+Next, we'll add a method for initializing data when the application is ready:
+
+```java
+ @Override
+ public void onApplicationEvent(final ApplicationReadyEvent event) {
+ Privilege createEvents = createPrivilegeIfNotFound(PrivilegeType.CREATE_EVENTS.toString());
+ Privilege readEvents = createPrivilegeIfNotFound(PrivilegeType.READ_EVENTS.toString());
+ Privilege deleteEvents = createPrivilegeIfNotFound(PrivilegeType.DELETE_EVENTS.toString());
+ Privilege readUsers = createPrivilegeIfNotFound(PrivilegeType.READ_USERS.toString());
+ Privilege updateUsers = createPrivilegeIfNotFound(PrivilegeType.UPDATE_USERS.toString());
+ Privilege deleteUsers = createPrivilegeIfNotFound(PrivilegeType.DELETE_USERS.toString());
+
+ Role adminRole = createRoleIfNotFound(RoleType.ROLE_ADMIN.toString(),
+ Arrays.asList(readUsers, updateUsers, deleteUsers));
+ Role organizerRole = createRoleIfNotFound(RoleType.ROLE_ORGANIZER.toString(),
+ Arrays.asList(createEvents, deleteEvents));
+ Role userRole = createRoleIfNotFound(RoleType.ROLE_USER.toString(),
+ Arrays.asList(readEvents));
+
+ User admin = new User("admin", passwordEncoder.encode("launchcode"),
+ Arrays.asList(adminRole, organizerRole, userRole));
+
+ createUserIfNotFound(admin);
+ }
+```
+
+We will load entries for the different `Privilege` types, `Role` types, and
+create a default admin user. `ROLE_ADMIN` is associated with user CRUD.
+`ROLE_ORGANIZER` is associated with event creation and deletion. `ROLE_USER`
+is associated with reading events.
+
+Finally, we need to add the methods that will create the entries if they don't
+already exist in the database. Notice how the relationships are set up when
+creating a `Role`, by setting the list of `Privilege` objects associated with
+that `Role`.
+
+```java
+ private User createUserIfNotFound(User user) {
+ if (userRepository.findByUsername(user.getUsername()) == null) {
+ userRepository.save(user);
+ }
+ return user;
+ }
+
+ private Privilege createPrivilegeIfNotFound(String name) {
+ Privilege privilege = privilegeRepository.findByName(name);
+ if (privilege == null) {
+ privilege = new Privilege(name);
+ privilegeRepository.save(privilege);
+ }
+ return privilege;
+ }
+
+ private Role createRoleIfNotFound(String name, Collection privileges) {
+ Role role = roleRepository.findByName(name);
+ if (role == null) {
+ role = new Role(name);
+ role.setPrivileges(privileges);
+ roleRepository.save(role);
+ }
+ return role;
+ }
+```
+
+Now, when you boot your application, these entries will be added to the database
+automatically the first time. Test it out.
+
+### Adding a `SecurityService`
+
+With roles and privileges added to our database, the last piece to add is
+a service that can check whether the current user has a specific role or
+privilege. We will not make use of this service in our application yet, but
+recognize that this could be used to determine if an action can be taken in the
+controller.
+
+Create a new package named `security` inside the `codingevents` package. Then,
+create a new class `SecurityService` inside the `security` package.
+
+```java
+@Service
+public class SecurityService {
+ @Autowired
+ private UserService userService;
+}
+```
+
+We will add two methods that allow us to check if a user has a certain privilege
+or a certain role. Add the following `hasPrivilege` method to this service.
+
+{{% notice blue Note "rocket" %}}
+The `hasPrivilege` method below uses Java streams and lambda expressions to
+simplify the code for searching for a matching privilege within a user's
+associated roles. Take a look at the [Java docs for Stream](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html)
+for more background on this syntax.
+{{% /notice %}}
+
+```java
+ public boolean hasPrivilege(String privilege) {
+ final User theUser = userService.getCurrentUser();
+ if (theUser == null) {
+ return false;
+ }
+ Boolean hasPrivilege = theUser.getRoles()
+ .stream()
+ .map(Role::getPrivileges)
+ .flatMap(coll -> coll.stream())
+ .map(Privilege::getName)
+ .anyMatch(p -> p.equals(privilege));
+ return hasPrivilege;
+ }
+```
+
+This method will take a privilege, such as `"READ_EVENTS"`, and check if the
+current user has a role with the associated privilege. For each role, we get
+the privileges and gather them in a large collection, where we then check to
+see if the given argument matches any in the collection.
+
+Similary, we will add a `hasRole` method to the service as well.
+
+```java
+ public boolean hasRole(String role) {
+ final User theUser = userService.getCurrentUser();
+ if (theUser != null) {
+ return false;
+ }
+ Boolean hasRole = theUser.getRoles()
+ .stream()
+ .map(Role::getName)
+ .anyMatch(r -> r.equals(role));
+ return hasRole;
+ }
+```
+
+This method is similar to `hasPrivilege` but simpler, as we only need to compare
+the argument to the user's associated roles.
+
+With this service in place, we could check in each controller request handler
+whether we want to allow the current user to take action. But, Spring provides
+a larger framework for us to use in order to handle security, authentication,
+and checking user roles & privileges. In the next lesson, we will set up that
+framework and plug our existing models into it.