Changeset 8bcd64c


Ignore:
Timestamp:
04/19/23 21:19:08 (19 months ago)
Author:
Gjoko Kostadinov <gjoko.kostadinov@…>
Branches:
master
Children:
950fa0d
Parents:
9050790
Message:

Add admin functionality and business admin functionality.

Location:
src/main
Files:
5 added
21 edited

Legend:

Unmodified
Added
Removed
  • src/main/java/edu/gjoko/schedlr/config/AppConfig.java

    r9050790 r8bcd64c  
    55import org.springframework.security.config.core.GrantedAuthorityDefaults;
    66import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
     7import org.springframework.security.web.authentication.AuthenticationFailureHandler;
    78import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
    89
     
    1516
    1617    @Bean
    17     public AuthenticationSuccessHandler myApplicationAuthenticationSuccessHandler() {
     18    public AuthenticationSuccessHandler appAuthenticationSuccessHandler() {
    1819        return new AppAuthenticationSuccessHandler();
     20    }
     21
     22    @Bean
     23    public AuthenticationFailureHandler appAuthenticationFailureHandler() {
     24        return new AppAuthenticationFailureHandler();
    1925    }
    2026
  • src/main/java/edu/gjoko/schedlr/config/AppFilter.java

    r9050790 r8bcd64c  
    11package edu.gjoko.schedlr.config;
    22
     3import edu.gjoko.schedlr.services.PostgresUserDetailsService;
     4import lombok.RequiredArgsConstructor;
    35import org.springframework.security.core.GrantedAuthority;
    46import org.springframework.security.core.context.SecurityContextImpl;
    57import org.springframework.security.core.userdetails.UserDetails;
    68import org.springframework.util.StringUtils;
     9import org.springframework.web.bind.annotation.RequestBody;
    710import org.springframework.web.filter.GenericFilterBean;
    811
     
    1215import javax.servlet.ServletResponse;
    1316import javax.servlet.http.HttpServletRequest;
     17import javax.servlet.http.HttpServletResponse;
    1418import javax.servlet.http.HttpSession;
    1519import java.io.IOException;
     
    1822import java.util.Map;
    1923
     24@RequiredArgsConstructor
    2025public class AppFilter extends GenericFilterBean {
     26
     27    private final PostgresUserDetailsService userDetailsService;
    2128    @Override
    2229    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    2330        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
    2431        HttpSession session = httpServletRequest.getSession(false);
     32        HttpServletResponse response = (HttpServletResponse) servletResponse;
    2533
    2634        if(httpServletRequest.getRequestURI().endsWith(".js")
    27                 || httpServletRequest.getRequestURI().endsWith(".css")
    28                 || httpServletRequest.getRequestURI().startsWith("/api")) {
     35                || httpServletRequest.getRequestURI().endsWith(".css")) {
    2936            filterChain.doFilter(servletRequest, servletResponse);
    3037            return;
    3138        }
     39
     40        if(httpServletRequest.getRequestURI().startsWith("/api")) {
     41            session = ((HttpServletRequest) servletRequest).getSession(true);
     42            SecurityContextImpl sci = (SecurityContextImpl) session.getAttribute("SPRING_SECURITY_CONTEXT");
     43
     44            if(sci != null && session.getAttribute("stakeholderId") == null) {
     45                UserDetails userDetails = (UserDetails) sci.getAuthentication().getPrincipal();
     46                Long stakeholderId = userDetailsService.loadStakeholderId(userDetails.getUsername());
     47                session.setAttribute("stakeholderId", stakeholderId);
     48            }
     49            filterChain.doFilter(servletRequest, servletResponse);
     50            return;
     51        }
     52
    3253        if(session != null) {
    33             Map<String, String> roleTargetUrlMap = new HashMap<>();
    34             roleTargetUrlMap.put("ADMIN", "/admin");
    35             roleTargetUrlMap.put("CUSTOMER", "/homepage");
    36             roleTargetUrlMap.put("BUSINESS_OWNER", "/business_homepage");
    3754            SecurityContextImpl sci = (SecurityContextImpl) session.getAttribute("SPRING_SECURITY_CONTEXT");
    3855            if(sci != null) {
    3956                UserDetails userDetails = (UserDetails) sci.getAuthentication().getPrincipal();
     57                Long stakeholderId = userDetailsService.loadStakeholderId(userDetails.getUsername());
     58                session.setAttribute("stakeholderId", stakeholderId);
     59
    4060                final Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
    4161                for (final GrantedAuthority grantedAuthority : authorities) {
     
    4767                            break;
    4868                        case "CUSTOMER":
     69                            page = "/homepage";
     70                            break;
    4971                        case "BUSINESS_OWNER":
    50                             page = "/homepage";
     72                            page = "/business_admin";
    5173                            break;
    5274                        default:
  • src/main/java/edu/gjoko/schedlr/config/AppSecurityConfig.java

    r9050790 r8bcd64c  
    22
    33import edu.gjoko.schedlr.services.PostgresUserDetailsService;
     4import lombok.AllArgsConstructor;
    45import org.springframework.context.annotation.Bean;
    56import org.springframework.context.annotation.Configuration;
     
    1112import org.springframework.security.config.core.GrantedAuthorityDefaults;
    1213import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
     14import org.springframework.security.web.access.AccessDeniedHandler;
     15import org.springframework.security.web.authentication.AuthenticationFailureHandler;
    1316import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
    1417import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
     
    1619@Configuration
    1720@EnableWebSecurity
     21@AllArgsConstructor
    1822public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
    1923
     
    2428    private final AuthenticationSuccessHandler authenticationSuccessHandler;
    2529
    26     public AppSecurityConfig(PostgresUserDetailsService userDetailsService, BCryptPasswordEncoder passwordEncoder,
    27                              AuthenticationSuccessHandler authenticationSuccessHandler) {
    28         this.userDetailsService = userDetailsService;
    29         this.passwordEncoder = passwordEncoder;
    30         this.authenticationSuccessHandler = authenticationSuccessHandler;
    31     }
     30    private final AuthenticationFailureHandler authenticationFailureHandler;
    3231
    3332    @Bean
     
    4847                .authenticationEntryPoint(new AppAuthenticationEntryPoint())
    4948                .and()
    50                 .addFilterBefore(new AppFilter(), BasicAuthenticationFilter.class)
     49                .addFilterAfter(new AppFilter(userDetailsService), BasicAuthenticationFilter.class)
    5150                .formLogin()
    5251                .loginPage("/login")
    5352                .loginProcessingUrl("/login")
    5453                .successHandler(authenticationSuccessHandler)
     54                .failureHandler(authenticationFailureHandler)
    5555                .defaultSuccessUrl("/homepage")
    5656                .and()
  • src/main/java/edu/gjoko/schedlr/config/MvcConfig.java

    r9050790 r8bcd64c  
    22
    33import org.springframework.context.annotation.Configuration;
    4 import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
    54import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
    65import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
     
    1615        registry.addViewController("/homepage").setViewName("homepage");
    1716        registry.addViewController("/admin").setViewName("admin");
    18     }
    19 
    20     @Override
    21     public void addResourceHandlers(ResourceHandlerRegistry registry) {
    22         registry.addResourceHandler("/resources/**")
    23                 .addResourceLocations("/resources/");
     17        registry.addViewController("/business_admin").setViewName("business_admin");
    2418    }
    2519}
  • src/main/java/edu/gjoko/schedlr/controllers/AdminController.java

    r9050790 r8bcd64c  
    11package edu.gjoko.schedlr.controllers;
    22
     3import org.springframework.security.core.Authentication;
     4import org.springframework.security.core.context.SecurityContextHolder;
    35import org.springframework.stereotype.Controller;
    46import org.springframework.ui.Model;
    57import org.springframework.web.bind.annotation.GetMapping;
     8
     9import javax.servlet.http.HttpServletRequest;
     10import java.security.Principal;
    611
    712@Controller
     
    914
    1015    @GetMapping(path = "/admin")
    11     public String getAdminPageTemplate(Model model) {
     16    public String getAdminPageTemplate(Model model, HttpServletRequest request) {
    1217        return "admin";
    1318    }
     19
     20    @GetMapping(path = "/business_admin")
     21    public String getBusinessAdminPageTemplate(Model model, HttpServletRequest request) {
     22        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
     23        String currentPrincipalName = authentication.getName();
     24
     25        return "business_admin";
     26    }
     27
     28
    1429}
  • src/main/java/edu/gjoko/schedlr/controllers/HomePageController.java

    r9050790 r8bcd64c  
    66import org.springframework.web.bind.annotation.PostMapping;
    77
    8 import java.security.Principal;
    9 
    108@Controller
    119public class HomePageController {
    1210
    1311    @GetMapping(path = "/homepage")
    14     public String getHomePageTemplate(Model model, Principal principal) {
    15         System.out.println(principal);
     12    public String getHomePageTemplate(Model model) {
    1613        return "homepage";
    1714    }
  • src/main/java/edu/gjoko/schedlr/controllers/LoginController.java

    r9050790 r8bcd64c  
    77import org.springframework.web.bind.annotation.ModelAttribute;
    88import org.springframework.web.bind.annotation.PostMapping;
     9import org.springframework.web.bind.annotation.RequestParam;
     10
     11import java.security.Principal;
    912
    1013@Controller
     
    1215
    1316    @GetMapping(path = "/login")
    14     public String getMapping(@ModelAttribute Stakeholder customer, Model model) {
     17    public String getMapping(@RequestParam(value = "error", required = false) String error,
     18                             @ModelAttribute Stakeholder customer, Model model) {
    1519        return "login";
    1620    }
  • src/main/java/edu/gjoko/schedlr/controllers/RegisterController.java

    r9050790 r8bcd64c  
    2828    @PostMapping(path = "/register_customer")
    2929    public String registerCustomer(@ModelAttribute Stakeholder customer, Model model) {
    30         Stakeholder user = stakeholderService.saveStakeholder(customer);
     30        stakeholderService.saveStakeholder(customer);
    3131        return "redirect:login";
    3232    }
  • src/main/java/edu/gjoko/schedlr/controllers/rest/BusinessController.java

    r9050790 r8bcd64c  
    44import edu.gjoko.schedlr.services.BusinessService;
    55import lombok.AllArgsConstructor;
     6import org.springframework.security.core.Authentication;
     7import org.springframework.security.core.context.SecurityContextHolder;
    68import org.springframework.web.bind.annotation.*;
    79
     10import javax.servlet.http.HttpServletRequest;
    811import java.util.List;
    912
     
    2932        businessService.updateBusinesses(businessList);
    3033    }
     34
     35    @GetMapping(path = "/me")
     36    public Business getPersonalInfo(HttpServletRequest request) {
     37        Long businessOwnerId = (long) request.getSession(true).getAttribute("stakeholderId");
     38        return businessService.findByOwner(businessOwnerId);
     39    }
    3140}
  • src/main/java/edu/gjoko/schedlr/entity/Business.java

    r9050790 r8bcd64c  
    11package edu.gjoko.schedlr.entity;
    22
     3import com.fasterxml.jackson.annotation.JsonIgnore;
    34import com.fasterxml.jackson.annotation.JsonManagedReference;
    45import com.fasterxml.jackson.annotation.JsonProperty;
     
    5152    @Column(name = "created")
    5253    @CreatedDate
     54    @JsonIgnore
    5355    private LocalDateTime created;
    5456
    5557    @Column(name = "modified")
    5658    @LastModifiedDate
     59    @JsonIgnore
    5760    private LocalDateTime modified;
    5861
  • src/main/java/edu/gjoko/schedlr/entity/Service.java

    r9050790 r8bcd64c  
    22
    33import com.fasterxml.jackson.annotation.JsonBackReference;
     4import com.fasterxml.jackson.annotation.JsonIgnore;
    45import lombok.AllArgsConstructor;
    56import lombok.Getter;
     
    2930    private Integer duration;
    3031
     32    @Column(name = "price")
     33    private Integer price;
     34
    3135    @OneToOne(cascade = CascadeType.MERGE)
    3236    @JoinColumn(name = "service_type_id", referencedColumnName = "id")
     
    4044    @Column(name = "created")
    4145    @CreatedDate
     46    @JsonIgnore
    4247    private LocalDateTime created;
    4348
    4449    @Column(name = "modified")
    4550    @LastModifiedDate
     51    @JsonIgnore
    4652    private LocalDateTime modified;
    4753}
  • src/main/java/edu/gjoko/schedlr/entity/Stakeholder.java

    r9050790 r8bcd64c  
    11package edu.gjoko.schedlr.entity;
    22
     3import com.fasterxml.jackson.annotation.JsonIgnore;
    34import lombok.AllArgsConstructor;
    45import lombok.Getter;
     
    4243
    4344    @Column(name = "password")
     45    @JsonIgnore
    4446    private String password;
    4547
    4648    @Column(name = "created")
    4749    @CreatedDate
     50    @JsonIgnore
    4851    private LocalDateTime created;
    4952
    5053    @Column(name = "modified")
    5154    @LastModifiedDate
     55    @JsonIgnore
    5256    private LocalDateTime modified;
    5357}
  • src/main/java/edu/gjoko/schedlr/repositories/BusinessRepository.java

    r9050790 r8bcd64c  
    33import edu.gjoko.schedlr.entity.Business;
    44import edu.gjoko.schedlr.entity.BusinessStatus;
     5import edu.gjoko.schedlr.entity.Stakeholder;
    56import org.springframework.data.jpa.repository.JpaRepository;
    67import org.springframework.stereotype.Repository;
     
    1213
    1314    List<Business> findBusinessesByBusinessStatus(BusinessStatus status);
     15
     16    Business findBusinessByOwner(Stakeholder owner);
    1417}
  • src/main/java/edu/gjoko/schedlr/services/BusinessService.java

    r9050790 r8bcd64c  
    33import edu.gjoko.schedlr.entity.Business;
    44import edu.gjoko.schedlr.entity.ServiceType;
     5import edu.gjoko.schedlr.entity.Stakeholder;
    56import edu.gjoko.schedlr.entity.StakeholderType;
    67import edu.gjoko.schedlr.repositories.BusinessRepository;
    78import edu.gjoko.schedlr.repositories.ServiceTypeRepository;
    89import lombok.AllArgsConstructor;
     10import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    911import org.springframework.stereotype.Service;
    1012
    1113import java.util.List;
    1214
    13 import static edu.gjoko.schedlr.entity.BusinessStatus.ACTIVE;
    1415import static edu.gjoko.schedlr.entity.BusinessStatus.NEW;
    1516
     
    2021    private final BusinessRepository businessRepository;
    2122    private final ServiceTypeRepository serviceTypeRepository;
     23    private BCryptPasswordEncoder bCryptPasswordEncoder;
    2224
    2325    public void saveBusiness(Business business) {
    2426        saveNewServiceTypes(business);
    2527        business.getOwner().setStakeholderType(StakeholderType.BUSINESS_OWNER);
     28        business.getOwner().setPassword(bCryptPasswordEncoder.encode(business.getOwner().getPassword()));
    2629        business.setBusinessStatus(NEW);
    2730        businessRepository.save(business);
     
    5255    }
    5356
     57    public Business findByOwner(Long ownerId) {
     58        var owner = new Stakeholder();
     59        owner.setId(ownerId);
     60        return businessRepository.findBusinessByOwner(owner);
     61    }
    5462}
  • src/main/java/edu/gjoko/schedlr/services/PostgresUserDetailsService.java

    r9050790 r8bcd64c  
    11package edu.gjoko.schedlr.services;
    22
     3import edu.gjoko.schedlr.entity.Business;
     4import edu.gjoko.schedlr.entity.BusinessStatus;
    35import edu.gjoko.schedlr.entity.Stakeholder;
     6import edu.gjoko.schedlr.entity.StakeholderType;
     7import edu.gjoko.schedlr.repositories.BusinessRepository;
    48import edu.gjoko.schedlr.repositories.StakeholderRepository;
     9import lombok.RequiredArgsConstructor;
     10import org.springframework.security.access.AccessDeniedException;
    511import org.springframework.security.core.GrantedAuthority;
    612import org.springframework.security.core.authority.SimpleGrantedAuthority;
     
    1622
    1723@Service
     24@RequiredArgsConstructor
    1825public class PostgresUserDetailsService implements UserDetailsService {
    1926
     
    2128    private final BCryptPasswordEncoder bCryptPasswordEncoder;
    2229
    23     public PostgresUserDetailsService(StakeholderRepository stakeholderRepository,
    24                                       BCryptPasswordEncoder bCryptPasswordEncoder) {
    25         this.stakeholderRepository = stakeholderRepository;
    26         this.bCryptPasswordEncoder = bCryptPasswordEncoder;
    27     }
     30    private final BusinessRepository businessRepository;
    2831
    2932    @Override
     
    3336            throw new UsernameNotFoundException("Non existing user");
    3437        }
     38
     39        if(user.getStakeholderType() == StakeholderType.BUSINESS_OWNER) {
     40            Business business = businessRepository.findBusinessByOwner(user);
     41            if (business.getBusinessStatus() != BusinessStatus.ACTIVE) {
     42                throw new SecurityException("User not approved by admin");
     43            }
     44        }
    3545        Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
    3646        grantedAuthorities.add(new SimpleGrantedAuthority(user.getStakeholderType().name()));
     47
    3748        return new User(user.getUsername(), user.getPassword(), grantedAuthorities);
    3849    }
     50
     51    public Long loadStakeholderId(String username) {
     52        return stakeholderRepository.findStakeholderByUsername(username).getId();
     53    }
    3954}
  • src/main/resources/static/js/register_business.js

    r9050790 r8bcd64c  
    2222                '<div class=\"form-outline mb-4\">' +
    2323                '    <div class="row">' +
    24                 '        <div class="col-md-8">\n' +
     24                '        <div class="col-md-6">\n' +
    2525                '            <input class="form-check-input" type="checkbox" value=\"' + obj.id + '\" id=\"' + obj.id + '\">\n' +
    2626                '            <label class="form-check-label" for=\"' + obj.id + '\">\n' +
     
    2929                '        </div>' +
    3030                '        <div class=\"form-outline col-md-2 d-grid\">' +
    31                 '            <input type=\"text\" id=\"' + obj.id + obj.name.replace(/\s/g, "") + '\" class=\"form-control\" />' +
     31                '            <input type=\"text\" id=\"' + obj.id + obj.name.replace(/\s/g, "") + "duration" + '\" class=\"form-control\" placeholder="time" />' +
     32                '        </div>' +
     33                '        <div class=\"form-outline col-md-2 d-grid\">' +
     34                '            <input type=\"text\" id=\"' + obj.id + obj.name.replace(/\s/g, "") + "price" + '\" class=\"form-control\" placeholder="price" />' +
    3235                '        </div>' +
    3336                '    </div>' +
     
    5356            '        </div>' +
    5457            '        <div class="form-outline col-md-2 d-grid">' +
    55             '            <input type="text" id="' + -1 + input_service.replace(/\s/g, "") + '" class="form-control" />' +
     58            '            <input type="text" id="' + -1 + input_service.replace(/\s/g, "") + "duration" + '" class="form-control" placeholder="time" />' +
     59            '        </div>' +
     60            '        <div class="form-outline col-md-2 d-grid">' +
     61            '            <input type="text" id="' + -1 + input_service.replace(/\s/g, "") + "price"+ '" class="form-control" placeholder="price" />' +
    5662            '        </div>' +
    5763            '    </div>' +
     
    8389            var text = $(label).text();
    8490            var time = $($($(label).parent()).siblings()[0]).children()[0].value;
     91            var price = $($($(label).parent()).siblings()[1]).children()[0].value;
    8592
    8693            var serviceType = {}
     
    9198            service['serviceType'] = serviceType;
    9299            service['duration'] = time;
     100            service['price'] = price;
    93101            servicesObj.push(service);
    94102        });
  • src/main/resources/templates/admin.html

    r9050790 r8bcd64c  
    22<html lang="en">
    33<head>
    4     <title>Schedlr</title>
     4    <title>Admin</title>
    55
    66    <meta charset="utf-8"/>
  • src/main/resources/templates/homepage.html

    r9050790 r8bcd64c  
    22<html>
    33<head>
    4     <title>Schedlr</title>
     4    <title>Homepage</title>
    55
    66    <meta charset="utf-8"/>
  • src/main/resources/templates/login.html

    r9050790 r8bcd64c  
    33<head>
    44    <meta charset="UTF-8">
    5     <title>Schedlr</title>
     5    <title>Login</title>
    66
    77    <!-- Font Awesome -->
     
    5454
    5555                            <!-- Submit button -->
    56                             <button type="submit" class="btn btn-primary btn-block mb-4">
     56                            <button id="login" type="submit" class="btn btn-primary btn-block mb-4">
    5757                                Login
    5858                            </button>
     59                            <div th:if="${param.error}">
     60                                <div th:if="${param.error.contains('notApproved')}" class="alert alert-danger">
     61                                    Account not approved yet.
     62                                </div>
     63
     64                                <div th:if="${param.error.contains('badCredentials')}" class="alert alert-danger">
     65                                    Invalid username or password.
     66                                </div>
     67                            </div>
    5968                        </form>
    6069
  • src/main/resources/templates/register_business.html

    r9050790 r8bcd64c  
    33<head>
    44    <meta charset="UTF-8">
    5     <title>Schedlr</title>
    6 
     5    <title>Register business</title>
    76    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet"
    87          integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
    9 
    108    <link rel="stylesheet" href="css/login.css">
    11 
    129</head>
    1310<body>
     
    3027                        <form>
    3128                            <div class="text-center">
    32                                 <p>Personal Info</p>
    33                             </div>
     29                            <p>Personal Info</p>
     30                        </div>
    3431
    3532                            <!-- 2 column grid layout with text inputs for the first and last names -->
  • src/main/resources/templates/register_customer.html

    r9050790 r8bcd64c  
    33<head>
    44    <meta charset="UTF-8">
    5     <title>Schedlr</title>
     5    <title>Register customer</title>
    66
    77    <meta charset="utf-8"/>
Note: See TracChangeset for help on using the changeset viewer.