From ec3777181b2ffea6cad519dfbe707d5cf16f2c32 Mon Sep 17 00:00:00 2001 From: Mysaa Java Date: Mon, 13 Jul 2026 15:16:40 +0200 Subject: [PATCH] Added oidc connection --- README.md | 22 +++++ build.gradle | 1 + compose.yaml | 2 + flake.nix | 2 +- .../bernard/misael/auth/AuthController.java | 6 +- .../misael/auth/CustomUserDetailsService.java | 17 +--- .../bernard/misael/auth/SpringSecurity.java | 76 +++++++++++----- .../bernard/misael/auth/model/Privilege.java | 12 +++ .../com/bernard/misael/auth/model/Role.java | 62 ------------- .../com/bernard/misael/auth/model/User.java | 26 +++--- .../auth/repository/RoleRepository.java | 10 -- .../auth/repository/UserRepository.java | 3 + .../misael/auth/service/AdminMaker.java | 32 +------ .../misael/auth/service/UserService.java | 7 ++ .../misael/auth/service/UserServiceImpl.java | 76 +++++++++++++++- .../misael/quizz/QuestionsController.java | 91 +++++-------------- .../db/migration/V7__user_update.sql | 8 ++ src/main/resources/templates/header.html | 1 - versions.properties | 2 + 19 files changed, 236 insertions(+), 220 deletions(-) delete mode 100644 src/main/java/com/bernard/misael/auth/model/Role.java delete mode 100644 src/main/java/com/bernard/misael/auth/repository/RoleRepository.java create mode 100644 src/main/resources/db/migration/V7__user_update.sql diff --git a/README.md b/README.md index 39f25c4..7431859 100644 --- a/README.md +++ b/README.md @@ -11,3 +11,25 @@ And finaly we run `./gradlew bootRun` The compose file is enough. `launcher.sh` checks that the migrations are applied and then runs the spring boot server on port 8080 + + +## OIDC + +In order to connect to oidc, you need to setup the following options, if `bcom` is your provider name: +```Yaml +spring: + security: + oauth2: + client: + activate: true + registration: + bcom: + provider: bcom + client-id: misael + client-secret: "myclientsecret" + authorization-grant-type: authorization_code + scope: openid,roles + provider: + bcom: + issuer-uri: "https://auth.example.com/realms/bcom" +``` \ No newline at end of file diff --git a/build.gradle b/build.gradle index 26e6f64..2bb491f 100644 --- a/build.gradle +++ b/build.gradle @@ -36,6 +36,7 @@ dependencies { implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6:_' implementation Spring.boot.web implementation 'org.springframework:spring-jdbc:_' + implementation Spring.boot.oauth2Client implementation 'org.flywaydb:flyway-core:_' implementation 'org.flywaydb:flyway-database-postgresql:_' implementation Spring.session.jdbc diff --git a/compose.yaml b/compose.yaml index f3b420e..2139c63 100644 --- a/compose.yaml +++ b/compose.yaml @@ -5,6 +5,8 @@ services: - 'POSTGRES_DB=misael' - 'POSTGRES_PASSWORD=misael-dev' - 'POSTGRES_USER=misael' + ports: + - 5432:5432 networks: - misanetwork diff --git a/flake.nix b/flake.nix index 8d26c04..6c0011c 100644 --- a/flake.nix +++ b/flake.nix @@ -44,7 +44,7 @@ ''; postgres-server = pkgs.writeShellScriptBin "postgres-server" '' mkdir -p .postgres - ${pkgs.podman}/bin/podman run -e POSTGRES_USER=misael -e POSTGRES_PASSWORD=misael-dev -e POSTGRES_DB=misael --volume ./.postgres:/var/lib/postgresql -p 127.0.0.1:5432:5432 postgres:18.4 & > .postgres/postgres.log & + ${pkgs.podman}/bin/podman run -e POSTGRES_USER=misael -e POSTGRES_PASSWORD=misael-dev -e POSTGRES_DB=misael --volume ./.postgres:/var/lib/postgresql -p 127.0.0.1:5432:5432 postgres:18.4 &> .postgres/postgres.log & ''; thecrew-images = pkgs.runCommand "thecrew-images" {} '' mkdir $out diff --git a/src/main/java/com/bernard/misael/auth/AuthController.java b/src/main/java/com/bernard/misael/auth/AuthController.java index 4d1d1a8..2ed40f4 100644 --- a/src/main/java/com/bernard/misael/auth/AuthController.java +++ b/src/main/java/com/bernard/misael/auth/AuthController.java @@ -16,7 +16,7 @@ import org.springframework.web.bind.annotation.RequestParam; import com.bernard.misael.auth.repository.UserRepository; import com.bernard.misael.auth.service.UserService; -import com.bernard.misael.auth.model.Role; +import com.bernard.misael.auth.model.Privilege; import com.bernard.misael.auth.model.User; import com.bernard.misael.auth.service.dto.UserDto; @@ -97,12 +97,14 @@ public class AuthController { @Getter public static class UserInfo implements Comparable { private long id; + private String oidcId; private String pseudo; private String roles; public UserInfo(User u){ this.id = u.getId(); + this.oidcId = u.getOidcId(); this.pseudo = u.getName(); - this.roles = u.getRoles().stream().map(Role::getName).collect(Collectors.joining(";")); + this.roles = u.getPrivileges().stream().map(Privilege::name).collect(Collectors.joining(";")); } @Override public int compareTo(UserInfo other) { diff --git a/src/main/java/com/bernard/misael/auth/CustomUserDetailsService.java b/src/main/java/com/bernard/misael/auth/CustomUserDetailsService.java index 818237f..9b8cc63 100644 --- a/src/main/java/com/bernard/misael/auth/CustomUserDetailsService.java +++ b/src/main/java/com/bernard/misael/auth/CustomUserDetailsService.java @@ -1,18 +1,12 @@ package com.bernard.misael.auth; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Stream; - import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; -import com.bernard.misael.auth.repository.UserRepository; -import com.bernard.misael.auth.model.Privilege; -import com.bernard.misael.auth.model.Role; import com.bernard.misael.auth.model.User; +import com.bernard.misael.auth.repository.UserRepository; @Service public class CustomUserDetailsService implements UserDetailsService { @@ -28,16 +22,9 @@ public class CustomUserDetailsService implements UserDetailsService { User user = userRepository.findByName(pseudo); if (user != null) { - Stream inducedPrivileges = user.getRoles().stream() - .map(Role::getPrivileges) - .map(Set::stream) - .flatMap(Function.identity()) - .sorted() - .distinct(); - Stream roles = user.getRoles().stream(); return new org.springframework.security.core.userdetails.User(user.getName(), user.getPassword(), - Stream.concat(inducedPrivileges, roles).toList() + user.getPrivileges() ); }else{ throw new UsernameNotFoundException("Invalid username or password."); diff --git a/src/main/java/com/bernard/misael/auth/SpringSecurity.java b/src/main/java/com/bernard/misael/auth/SpringSecurity.java index d55637f..76b8848 100644 --- a/src/main/java/com/bernard/misael/auth/SpringSecurity.java +++ b/src/main/java/com/bernard/misael/auth/SpringSecurity.java @@ -5,6 +5,8 @@ import java.util.logging.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -12,9 +14,18 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe 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.client.oidc.userinfo.OidcUserRequest; +import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService; +import org.springframework.security.oauth2.client.userinfo.OAuth2UserService; +import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser; +import org.springframework.security.oauth2.core.oidc.user.OidcUser; +import org.springframework.security.oauth2.core.user.DefaultOAuth2User; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import com.bernard.misael.auth.model.User; +import com.bernard.misael.auth.service.UserService; + @Configuration @EnableWebSecurity @@ -23,8 +34,10 @@ public class SpringSecurity { public static final Logger LOG = Logger.getLogger(SpringSecurity.class.getName()); - @Autowired - private UserDetailsService userDetailsService; + public static final boolean oidcActivated(Environment env) { + String activate = env.getProperty("spring.security.oauth2.client.activate"); + return (activate!=null) && activate.equalsIgnoreCase("true"); + } @Bean public static PasswordEncoder passwordEncoder(){ @@ -32,29 +45,50 @@ public class SpringSecurity { } @Bean - public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - http.csrf(csrf -> csrf.disable()) - .authorizeHttpRequests((authorize) -> - authorize - .requestMatchers("/**").permitAll() - ).formLogin( - form -> form - .loginPage("/login") - .loginProcessingUrl("/login") - .defaultSuccessUrl("/") - .permitAll() - ).logout( - logout -> logout - .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) - .permitAll() - ); + public SecurityFilterChain filterChain(HttpSecurity http, Environment env, final UserService us) throws Exception { + http.csrf(csrf -> csrf.disable()); + http.authorizeHttpRequests(authorize -> + authorize + .requestMatchers("/**").permitAll() + ); + if(oidcActivated(env)) { + http.oauth2Login(oauth2 -> oauth2. + userInfoEndpoint(userInfo -> userInfo. + oidcUserService(this.oidcUserService(us)) + ) + ); + } else { + http.formLogin( + form -> form + .loginPage("/login") + .loginProcessingUrl("/login") + .defaultSuccessUrl("/") + .permitAll() + ); + http.logout( + logout -> logout + .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) + .permitAll() + ); + } return http.build(); } @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + public void configureGlobal(AuthenticationManagerBuilder auth, UserDetailsService userDetailsService) throws Exception { auth - .userDetailsService(userDetailsService) - .passwordEncoder(passwordEncoder()); + .userDetailsService(userDetailsService) + .passwordEncoder(passwordEncoder()); } + + private OAuth2UserService oidcUserService(UserService us) { + final OidcUserService delegate = new OidcUserService(); + + return userRequest -> { + // We create the original user + OidcUser oidcUser = delegate.loadUser(userRequest); + User u = us.ensureUser(oidcUser); + return new DefaultOidcUser(u.getPrivileges(), oidcUser.getIdToken()); + }; + } } \ No newline at end of file diff --git a/src/main/java/com/bernard/misael/auth/model/Privilege.java b/src/main/java/com/bernard/misael/auth/model/Privilege.java index 79977d8..e8cd13b 100644 --- a/src/main/java/com/bernard/misael/auth/model/Privilege.java +++ b/src/main/java/com/bernard/misael/auth/model/Privilege.java @@ -1,14 +1,26 @@ package com.bernard.misael.auth.model; +import java.util.Map; + import org.springframework.security.core.GrantedAuthority; public enum Privilege implements GrantedAuthority { LIST_USERS,ADD_USERS,LIST_QUIZZ,CREATE_QUIZZ,VIEW_ALL_FORMS; + public static final Map oidcRoles = Map.of( + "misael-view-all-forms",VIEW_ALL_FORMS, + "misael-list-users",LIST_USERS, + "misael-create-quizz",CREATE_QUIZZ, + "misael-list-quizz", LIST_QUIZZ + ); @Override public String getAuthority() { return this.name(); } + public static final Privilege ofOidcString(String s) { + return oidcRoles.getOrDefault(s, null); + } + } diff --git a/src/main/java/com/bernard/misael/auth/model/Role.java b/src/main/java/com/bernard/misael/auth/model/Role.java deleted file mode 100644 index 719adc9..0000000 --- a/src/main/java/com/bernard/misael/auth/model/Role.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.bernard.misael.auth.model; - -import java.util.EnumSet; -import java.util.List; -import java.util.Set; - -import org.springframework.security.core.GrantedAuthority; - -import jakarta.persistence.CollectionTable; -import jakarta.persistence.Column; -import jakarta.persistence.ElementCollection; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToMany; -import jakarta.persistence.Table; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor -@Entity -@Table(name="roles") -public class Role implements GrantedAuthority -{ - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @Column(nullable=false, unique=true) - private String name; - - @ManyToMany(mappedBy="roles") - private List users; - - @ElementCollection(fetch = FetchType.EAGER) - @Enumerated(EnumType.STRING) - @CollectionTable(name = "role_privileges" - , joinColumns = @JoinColumn(name = "id")) - @Column(name = "privileges", nullable = false) - private Set privileges; - - public Role(String name){ - super(); - this.setName(name); - this.setPrivileges(EnumSet.noneOf(Privilege.class)); - } - - @Override - public String getAuthority() { - return "ROLE_"+name; - } -} diff --git a/src/main/java/com/bernard/misael/auth/model/User.java b/src/main/java/com/bernard/misael/auth/model/User.java index 30e6b7c..c40236e 100644 --- a/src/main/java/com/bernard/misael/auth/model/User.java +++ b/src/main/java/com/bernard/misael/auth/model/User.java @@ -1,21 +1,20 @@ package com.bernard.misael.auth.model; -import java.util.ArrayList; -import java.util.List; import java.util.Set; import com.bernard.misael.quizz.model.Quizz; -import jakarta.persistence.CascadeType; +import jakarta.persistence.CollectionTable; import jakarta.persistence.Column; +import jakarta.persistence.ElementCollection; import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; -import jakarta.persistence.JoinTable; -import jakarta.persistence.ManyToMany; import jakarta.persistence.OneToMany; import jakarta.persistence.Table; import lombok.AllArgsConstructor; @@ -40,15 +39,18 @@ public class User @Column(nullable=false,unique = true) private String name; - @Column(nullable=false) + @Column(nullable=true, unique = true) + private String oidcId; + + @Column(nullable=true) private String password; - @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 List roles = new ArrayList<>(); + @ElementCollection(fetch = FetchType.EAGER) + @Enumerated(EnumType.STRING) + @CollectionTable(name = "user_privileges", + joinColumns = @JoinColumn(name = "id")) + @Column(name = "privileges", nullable = false) + private Set privileges; @OneToMany(mappedBy="owner") private Set myQuizzs; diff --git a/src/main/java/com/bernard/misael/auth/repository/RoleRepository.java b/src/main/java/com/bernard/misael/auth/repository/RoleRepository.java deleted file mode 100644 index 6705ccb..0000000 --- a/src/main/java/com/bernard/misael/auth/repository/RoleRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.bernard.misael.auth.repository; - -import org.springframework.data.jpa.repository.JpaRepository; - -import com.bernard.misael.auth.model.Role; - -public interface RoleRepository extends JpaRepository { - - Role findByName(String name); -} \ No newline at end of file diff --git a/src/main/java/com/bernard/misael/auth/repository/UserRepository.java b/src/main/java/com/bernard/misael/auth/repository/UserRepository.java index 6cdabee..352c361 100644 --- a/src/main/java/com/bernard/misael/auth/repository/UserRepository.java +++ b/src/main/java/com/bernard/misael/auth/repository/UserRepository.java @@ -1,5 +1,7 @@ package com.bernard.misael.auth.repository; +import java.util.Optional; + import org.springframework.data.jpa.repository.JpaRepository; import com.bernard.misael.auth.model.User; @@ -7,5 +9,6 @@ import com.bernard.misael.auth.model.User; public interface UserRepository extends JpaRepository { User findByName(String pseudo); + Optional findByOidcId(String oidcId); } \ No newline at end of file diff --git a/src/main/java/com/bernard/misael/auth/service/AdminMaker.java b/src/main/java/com/bernard/misael/auth/service/AdminMaker.java index b7deafe..a212abf 100644 --- a/src/main/java/com/bernard/misael/auth/service/AdminMaker.java +++ b/src/main/java/com/bernard/misael/auth/service/AdminMaker.java @@ -1,7 +1,6 @@ package com.bernard.misael.auth.service; -import java.util.Arrays; -import java.util.EnumSet; +import java.util.Set; import java.util.logging.Logger; import org.springframework.beans.factory.annotation.Autowired; @@ -11,11 +10,9 @@ import org.springframework.lang.NonNull; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component; -import com.bernard.misael.auth.repository.RoleRepository; -import com.bernard.misael.auth.repository.UserRepository; import com.bernard.misael.auth.model.Privilege; -import com.bernard.misael.auth.model.Role; import com.bernard.misael.auth.model.User; +import com.bernard.misael.auth.repository.UserRepository; import jakarta.transaction.Transactional; @@ -28,9 +25,6 @@ public class AdminMaker implements @Autowired private UserRepository userRepository; - @Autowired - private RoleRepository roleRepository; - @Autowired private PasswordEncoder passwordEncoder; @@ -38,37 +32,21 @@ public class AdminMaker implements @Transactional public void onApplicationEvent(@NonNull ContextRefreshedEvent event) { - if (alreadySetup) - return; + if (alreadySetup) return; + Logger log = Logger.getLogger("AdminMaker"); log.info("Checking that privileges and mysaa user exist"); - Role adminRole = createRoleIfNotFound("ADMIN", EnumSet.allOf(Privilege.class)); - createRoleIfNotFound("USER", EnumSet.noneOf(Privilege.class)); - User mysaa = userRepository.findByName("mysaa"); if (mysaa == null) { User user = new User(); user.setName("mysaa"); user.setPassword(passwordEncoder.encode("super")); - user.setRoles(Arrays.asList(adminRole)); + user.setPrivileges(Set.of(Privilege.values())); userRepository.save(user); } alreadySetup = true; log.info("Everything needed has been created"); } - - @Transactional - private Role createRoleIfNotFound( - String name, EnumSet privileges) { - - Role role = roleRepository.findByName(name); - if (role == null) { - role = new Role(name); - role.setPrivileges(privileges); - roleRepository.save(role); - } - return role; - } } diff --git a/src/main/java/com/bernard/misael/auth/service/UserService.java b/src/main/java/com/bernard/misael/auth/service/UserService.java index 23dab13..905421e 100644 --- a/src/main/java/com/bernard/misael/auth/service/UserService.java +++ b/src/main/java/com/bernard/misael/auth/service/UserService.java @@ -1,13 +1,20 @@ package com.bernard.misael.auth.service; +import java.security.Principal; import java.util.List; +import org.springframework.security.oauth2.core.oidc.user.OidcUser; + import com.bernard.misael.auth.model.Privilege; import com.bernard.misael.auth.model.User; import com.bernard.misael.auth.service.dto.UserDto; public interface UserService { + User ofPrincipal(Principal p); + + User ensureUser(OidcUser user); + void saveUser(UserDto userDto); void changePassword(User user, String password); diff --git a/src/main/java/com/bernard/misael/auth/service/UserServiceImpl.java b/src/main/java/com/bernard/misael/auth/service/UserServiceImpl.java index 72eaac5..3eb977f 100644 --- a/src/main/java/com/bernard/misael/auth/service/UserServiceImpl.java +++ b/src/main/java/com/bernard/misael/auth/service/UserServiceImpl.java @@ -1,12 +1,22 @@ package com.bernard.misael.auth.service; +import java.security.Principal; import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; +import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.stereotype.Service; import com.bernard.misael.auth.repository.UserRepository; import com.bernard.misael.auth.service.dto.UserDto; + +import jakarta.validation.constraints.NotNull; + import com.bernard.misael.auth.model.Privilege; import com.bernard.misael.auth.model.User; @@ -22,6 +32,51 @@ public class UserServiceImpl implements UserService { this.passwordEncoder = passwordEncoder; } + + + @Override + @NotNull + public User ensureUser(OidcUser oidcUser) { + String username = oidcUser.getPreferredUsername(); + String id = oidcUser.getName(); + Set privileges; + try { + privileges = oidcUser + .>>>getAttribute("resource_access") + .getOrDefault("misael",Map.of()) + .getOrDefault("roles",List.of()) + .stream() + .map(Privilege::ofOidcString) + .filter(p -> p != null) + .collect(Collectors.toSet()); + } catch (NullPointerException e) { + throw new RuntimeException(e); + } + + Optional u = userRepository.findByOidcId(id); + if(u.isEmpty()) { + Optional uN = Optional.ofNullable(userRepository.findByName(username)); + if(uN.isEmpty()) { + User u0 = new User(); + u0.setOidcId(id); + u0.setName(username); + userRepository.save(u0); + u = Optional.of(u0); + } else { + User u0 = uN.get(); + u0.setOidcId(id); + userRepository.save(u0); + u = uN; + } + } + User theu = u.get(); + if(!theu.getPrivileges().equals(privileges)) { + theu.setPrivileges(privileges); + userRepository.save(theu); + } + return theu; + } + @Override public void saveUser(UserDto userDto) { User user = new User(); @@ -29,7 +84,7 @@ public class UserServiceImpl implements UserService { // encrypt the password using spring security user.setPassword(passwordEncoder.encode(userDto.getPassword())); - user.setRoles(List.of()); + user.setPrivileges(Set.of()); userRepository.save(user); } @Override @@ -58,7 +113,22 @@ public class UserServiceImpl implements UserService { @Override public boolean hasPrivilege(User u, Privilege p) { - //TODO faire une query sql propre avec ça - return u.getRoles().stream().anyMatch(r -> r.getPrivileges().contains(p)); + return u.getPrivileges().contains(p); + } + + + + @Override + public User ofPrincipal(Principal p) { + User u = null; + if(p instanceof OAuth2AuthenticationToken) { + OAuth2AuthenticationToken o = (OAuth2AuthenticationToken) p; + u = userRepository.findByOidcId(o.getName()).orElseThrow(() -> new RuntimeException("Oauth2 user was not in the database")); + } + if(p instanceof UsernamePasswordAuthenticationToken) { + UsernamePasswordAuthenticationToken o = (UsernamePasswordAuthenticationToken) p; + u = userRepository.findByName(o.getName()); + } + return u; } } \ No newline at end of file diff --git a/src/main/java/com/bernard/misael/quizz/QuestionsController.java b/src/main/java/com/bernard/misael/quizz/QuestionsController.java index 0d6d851..12a0d26 100644 --- a/src/main/java/com/bernard/misael/quizz/QuestionsController.java +++ b/src/main/java/com/bernard/misael/quizz/QuestionsController.java @@ -16,9 +16,9 @@ import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; -import com.bernard.misael.auth.repository.UserRepository; +import com.bernard.misael.auth.service.UserService; import com.bernard.misael.quizz.model.Quizz; -import com.bernard.misael.quizz.model.QuizzForm; +import com.bernard.misael.quizz.model.QuizzForm; import com.bernard.misael.auth.model.User; import com.bernard.misael.quizz.questions.QTypes; import com.bernard.misael.quizz.repository.QuizzFormRepository; @@ -32,15 +32,12 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; - - - @Controller @RequestMapping("/questions") public class QuestionsController { @Autowired - UserRepository ur; + UserService us; @Autowired QuizzManager qm; @@ -50,20 +47,17 @@ public class QuestionsController { @Autowired QuizzFormRepository qfrepo; - + /* * List all quizz */ @GetMapping("/quizz") public String getQuizz(Model model, Principal p) { - User u = null; - if (p!=null) - u = ur.findByName(p.getName()); + User u = us.ofPrincipal(p); if(u!=null) { model.addAttribute("answerableQuizz",qm.answerableQuizz(u)); model.addAttribute("editableQuizz",qm.editableQuizz(u)); } - return "quizz.html"; } @@ -72,9 +66,7 @@ public class QuestionsController { */ @GetMapping("/forms") public String getForms(Model model, Principal p) { - User u = null; - if (p!=null) - u = ur.findByName(p.getName()); + User u = us.ofPrincipal(p); if(u!=null) { model.addAttribute("finishedForms",qfrepo.findByUserAndDoneTrue(u)); model.addAttribute("openForms",qfrepo.findByUserAndDoneFalse(u)); @@ -88,9 +80,7 @@ public class QuestionsController { */ @GetMapping("/showform/{id}") public Object showForm(@PathVariable("id") long id, Model m, Principal p) { - User u = null; - if (p!=null) - u = ur.findByName(p.getName()); + User u = us.ofPrincipal(p); if(u==null) return "redirect:/login?restricted"; Optional oqf = qm.canViewQuizzForm(u, id); @@ -107,9 +97,7 @@ public class QuestionsController { */ @GetMapping("/showformsadvancements/{id}") public Object showFormsAdvancements(@PathVariable("id") long id, Model m, Principal p) { - User u = null; - if (p!=null) - u = ur.findByName(p.getName()); + User u = us.ofPrincipal(p); if(u==null) return "redirect:/login?restricted"; Optional oq = qm.canViewQuizzFormsOfQuizz(u, id); @@ -126,9 +114,7 @@ public class QuestionsController { */ @PostMapping("/getformdata/{id}") public Object showFormApi(@PathVariable("id") long id, Principal p) { - User u = null; - if (p!=null) - u = ur.findByName(p.getName()); + User u = us.ofPrincipal(p); if(u==null) return "redirect:/login?restricted"; JsonNode out = qm.getQuizzFormData(u, id); @@ -140,9 +126,7 @@ public class QuestionsController { */ @GetMapping("/getallformsdata/{id}") public Object getAllFormsData(@PathVariable("id") long id, Principal p) { - User u = null; - if (p!=null) - u = ur.findByName(p.getName()); + User u = us.ofPrincipal(p); if(u==null) return "redirect:/login?restricted"; JsonNode out = qm.getAllFormsData(u, id); @@ -154,9 +138,7 @@ public class QuestionsController { */ @PostMapping("/getformadvancements/{id}") public Object showFormAdvancements(@PathVariable("id") long id, Principal p) { - User u = null; - if (p!=null) - u = ur.findByName(p.getName()); + User u = us.ofPrincipal(p); if(u==null) return "redirect:/login?restricted"; JsonNode out = qm.getQuizzFormAdvancments(u, id); @@ -168,9 +150,7 @@ public class QuestionsController { */ @GetMapping("/duplicate-quizz/{id}") public Object duplicateQuizz(@PathVariable("id") long id, Principal p) { - User u = null; - if (p!=null) - u = ur.findByName(p.getName()); + User u = us.ofPrincipal(p); if(u==null) return "redirect:/login?restricted"; Quizz q = qm.duplicateQuizz(u, id); @@ -181,7 +161,8 @@ public class QuestionsController { @GetMapping("/form/{q}") public String formpage(@PathVariable("q") long quizzId, Principal p, Model m) { - if (p==null) + User u = us.ofPrincipal(p); + if (u==null) return "redirect:/login?restricted"; m.addAttribute("formid", quizzId); Quizz q = qrepo.getReferenceById(quizzId); @@ -192,9 +173,7 @@ public class QuestionsController { @GetMapping("/question/{q}") public ResponseEntity question(@PathVariable("q") long quizzId, Principal p) { - User u = null; - if (p!=null) - u = ur.findByName(p.getName()); + User u = us.ofPrincipal(p); if(u==null) return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED); JsonNode out = qm.next(u, quizzId); @@ -203,9 +182,7 @@ public class QuestionsController { @PostMapping("/answer/{q}") public ResponseEntity answer(@PathVariable("q") long quizzId, @RequestBody JsonNode data, Principal p) { - User u = null; - if (p!=null) - u = ur.findByName(p.getName()); + User u = us.ofPrincipal(p); JsonNode out = qm.answer(u, quizzId, data); return new ResponseEntity<>(out, HttpStatus.OK); } @@ -213,9 +190,7 @@ public class QuestionsController { @GetMapping("/new-quizz") @Secured("CREATE_QUIZZ") public Object newQuizz(Principal p, Model m) { - if (p==null) - return "redirect:/login?restricted"; - User u = ur.findByName(p.getName()); + User u = us.ofPrincipal(p); if (u==null) return new ResponseEntity<>(HttpStatus.FORBIDDEN); Quizz q = qm.newQuizz(u); @@ -226,9 +201,7 @@ public class QuestionsController { Logger logger = LoggerFactory.getLogger(QuestionsController.class); @GetMapping("/quizz-edit/{q}") public Object quizzEdit(@PathVariable("q") long quizzId, Principal p, Model m) { - if (p==null) - return "redirect:/login?restricted"; - User u = ur.findByName(p.getName()); + User u = us.ofPrincipal(p); if (u==null || !qm.canEditQuizz(u, quizzId)) return new ResponseEntity<>(HttpStatus.FORBIDDEN); m.addAttribute("quizzId", quizzId); @@ -238,9 +211,7 @@ public class QuestionsController { @PostMapping("/quizz-edit/{q}/get") public ResponseEntity quizzSetName(@PathVariable("q") long quizzId, Principal p) { - User u = null; - if (p!=null) - u = ur.findByName(p.getName()); + User u = us.ofPrincipal(p); if(u==null) return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED); JsonNode out = qm.getQuizzInfo(u, quizzId); @@ -249,9 +220,7 @@ public class QuestionsController { @PostMapping("/quizz-edit/{q}/set-name") public ResponseEntity quizzSetName(@PathVariable("q") long quizzId, @RequestBody String data, Principal p) { - User u = null; - if (p!=null) - u = ur.findByName(p.getName()); + User u = us.ofPrincipal(p); if(u==null) return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED); JsonNode out = qm.setQuizzName(u, quizzId, data); @@ -260,9 +229,7 @@ public class QuestionsController { @PostMapping("/quizz-edit/{q}/add-question") public ResponseEntity quizzAddQuestion(@PathVariable("q") long quizzId, Principal p) { - User u = null; - if (p!=null) - u = ur.findByName(p.getName()); + User u = us.ofPrincipal(p); if(u==null) return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED); JsonNode out = qm.addQuestion(u, quizzId); @@ -271,9 +238,7 @@ public class QuestionsController { @PostMapping("/quizz-edit/{q}/remove-question/{qi}") public ResponseEntity quizzSetName(@PathVariable("q") long quizzId, @PathVariable("qi") long questionId, Principal p) { - User u = null; - if (p!=null) - u = ur.findByName(p.getName()); + User u = us.ofPrincipal(p); if(u==null) return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED); JsonNode out = qm.removeQuestion(u, quizzId, questionId); @@ -282,9 +247,7 @@ public class QuestionsController { @PostMapping("/quizz-edit/{q}/reorder-questions") public ResponseEntity quizzReorderQuestions(@PathVariable("q") long quizzId, @RequestBody JsonNode data, Principal p) { - User u = null; - if (p!=null) - u = ur.findByName(p.getName()); + User u = us.ofPrincipal(p); if(u==null) return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED); if(!data.isArray()) @@ -306,9 +269,7 @@ public class QuestionsController { @PostMapping("/quizz-edit/{q}/edit-question/{qi}") public ResponseEntity quizzEditQuestion(@PathVariable("q") long quizzId, @PathVariable("qi") long questionId, @RequestBody JsonNode data, Principal p) { - User u = null; - if (p!=null) - u = ur.findByName(p.getName()); + User u = us.ofPrincipal(p); if(u==null) return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED); @@ -319,9 +280,7 @@ public class QuestionsController { @PostMapping("/quizz-edit/{q}/set-question-type/{qi}") public ResponseEntity quizzSetQuestionType(@PathVariable("q") long quizzId, @PathVariable("qi") long questionId, @RequestBody JsonNode data, Principal p) { - User u = null; - if (p!=null) - u = ur.findByName(p.getName()); + User u = us.ofPrincipal(p); if(u==null) return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED); if(!data.isTextual()) diff --git a/src/main/resources/db/migration/V7__user_update.sql b/src/main/resources/db/migration/V7__user_update.sql new file mode 100644 index 0000000..15db44f --- /dev/null +++ b/src/main/resources/db/migration/V7__user_update.sql @@ -0,0 +1,8 @@ +create table user_privileges (id bigint not null, privileges varchar(255) not null check (privileges in ('LIST_USERS','ADD_USERS','LIST_QUIZZ','CREATE_QUIZZ','VIEW_ALL_FORMS')), primary key (id, privileges)); +drop table role_privileges cascade; +drop table roles cascade; +alter table if exists users alter column password drop not null; +alter table if exists users add column oidc_id varchar(255); +alter table if exists users drop constraint if exists UKs3fyv3dn1pq639igamsrq5vhj; +alter table if exists users add constraint UKs3fyv3dn1pq639igamsrq5vhj unique (oidc_id); +alter table if exists user_privileges add constraint FKqltalsck5q21fw7vcpsriacst foreign key (id) references users; diff --git a/src/main/resources/templates/header.html b/src/main/resources/templates/header.html index cfa56e8..a4c49a4 100644 --- a/src/main/resources/templates/header.html +++ b/src/main/resources/templates/header.html @@ -1,7 +1,6 @@