diff --git a/codenames-common/src/main/java/com/codenames/enums/DefaultUserAuthRole.java b/codenames-common/src/main/java/com/codenames/enums/DefaultUserAuthRole.java new file mode 100644 index 0000000..b837537 --- /dev/null +++ b/codenames-common/src/main/java/com/codenames/enums/DefaultUserAuthRole.java @@ -0,0 +1,14 @@ +package com.codenames.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum DefaultUserAuthRole { + GUEST("ROLE_GUEST"), + USER("ROLE_USER"), + ADMIN("ROLE_ADMIN"); + + private final String role; +} diff --git a/codenames-common/src/main/resources/application.yml b/codenames-common/src/main/resources/application.yml index fe830ae..83a0042 100644 --- a/codenames-common/src/main/resources/application.yml +++ b/codenames-common/src/main/resources/application.yml @@ -1,6 +1,9 @@ spring: + flyway: + out-of-order: true + datasource: url: "${MYSQL_URL:jdbc:mysql://localhost:3306/codenames}" username: "${MYSQL_USERNAME:root}" diff --git a/codenames-common/src/main/resources/db/migration/V1__InitDatabase.sql b/codenames-common/src/main/resources/db/migration/V1__InitDatabase.sql new file mode 100644 index 0000000..e7f3000 --- /dev/null +++ b/codenames-common/src/main/resources/db/migration/V1__InitDatabase.sql @@ -0,0 +1,17 @@ + + +CREATE TABLE IF NOT EXISTS users( + id INT NOT NULL AUTO_INCREMENT, + login VARCHAR(100) NOT NULL UNIQUE, + nickname VARCHAR(50) NOT NULL, + password VARCHAR(255) NOT NULL, + PRIMARY KEY (id) +); + +CREATE TABLE IF NOT EXISTS users_roles( + id INT NOT NULL AUTO_INCREMENT, + user_id INT NOT NULL, + role VARCHAR(50) NOT NULL DEFAULT "GUEST", + PRIMARY KEY (id), + FOREIGN KEY (user_id) REFERENCES users (id) +); \ No newline at end of file diff --git a/codenames-domain/src/main/java/com/codenames/config/WebSecurityConfig.java b/codenames-domain/src/main/java/com/codenames/config/WebSecurityConfig.java index 1ce6a31..2acd441 100644 --- a/codenames-domain/src/main/java/com/codenames/config/WebSecurityConfig.java +++ b/codenames-domain/src/main/java/com/codenames/config/WebSecurityConfig.java @@ -43,6 +43,7 @@ public SecurityFilterChain filterChain (HttpSecurity http) throws Exception { .and() .authorizeHttpRequests(auth -> auth .requestMatchers(HttpMethod.POST , "/login", "/register").permitAll() + .requestMatchers("/whoami").permitAll() .requestMatchers("/socket/**").permitAll() .anyRequest().authenticated() ); diff --git a/codenames-domain/src/main/java/com/codenames/dto/UserAuthRoleDto.java b/codenames-domain/src/main/java/com/codenames/dto/UserAuthRoleDto.java new file mode 100644 index 0000000..7e32baf --- /dev/null +++ b/codenames-domain/src/main/java/com/codenames/dto/UserAuthRoleDto.java @@ -0,0 +1,14 @@ +package com.codenames.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Getter @Setter +public class UserAuthRoleDto { + + private String role; +} diff --git a/codenames-domain/src/main/java/com/codenames/entity/UserAuthRoleEntity.java b/codenames-domain/src/main/java/com/codenames/entity/UserAuthRoleEntity.java new file mode 100644 index 0000000..4a03bee --- /dev/null +++ b/codenames-domain/src/main/java/com/codenames/entity/UserAuthRoleEntity.java @@ -0,0 +1,33 @@ +package com.codenames.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Entity +@Table(name = "users_roles") +public class UserAuthRoleEntity { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(name = "id") + private Long id; + + @ManyToOne + @JoinColumn(name = "user_id") + private UserEntity user; + + @Column(name = "role") + private String role; +} diff --git a/codenames-domain/src/main/java/com/codenames/entity/UserEntity.java b/codenames-domain/src/main/java/com/codenames/entity/UserEntity.java index bffc976..03e45c1 100644 --- a/codenames-domain/src/main/java/com/codenames/entity/UserEntity.java +++ b/codenames-domain/src/main/java/com/codenames/entity/UserEntity.java @@ -1,16 +1,20 @@ package com.codenames.entity; +import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; import jakarta.persistence.Table; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import java.util.List; + @Entity @Table(name = "users") @Getter @Setter @@ -28,4 +32,7 @@ public class UserEntity { @Column(name = "password") private String password; + + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) + private List roles; } diff --git a/codenames-domain/src/main/java/com/codenames/mapper/UserAuthRoleMapper.java b/codenames-domain/src/main/java/com/codenames/mapper/UserAuthRoleMapper.java new file mode 100644 index 0000000..7a853e8 --- /dev/null +++ b/codenames-domain/src/main/java/com/codenames/mapper/UserAuthRoleMapper.java @@ -0,0 +1,11 @@ +package com.codenames.mapper; + +import com.codenames.dto.UserAuthRoleDto; +import com.codenames.entity.UserAuthRoleEntity; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +public interface UserAuthRoleMapper { + + UserAuthRoleDto userAuthRoleEntityToUserAuthRoleDto(UserAuthRoleEntity authRoleEntity); +} diff --git a/codenames-domain/src/main/java/com/codenames/repository/UserAuthRoleRepository.java b/codenames-domain/src/main/java/com/codenames/repository/UserAuthRoleRepository.java new file mode 100644 index 0000000..d5b554a --- /dev/null +++ b/codenames-domain/src/main/java/com/codenames/repository/UserAuthRoleRepository.java @@ -0,0 +1,15 @@ +package com.codenames.repository; + +import com.codenames.entity.UserAuthRoleEntity; +import com.codenames.entity.UserEntity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface UserAuthRoleRepository extends JpaRepository { + + Optional> findUserAuthRoleEntitiesByUser(UserEntity userEntity); +} diff --git a/codenames-domain/src/main/java/com/codenames/services/UserService.java b/codenames-domain/src/main/java/com/codenames/services/UserService.java index f243619..b40b304 100644 --- a/codenames-domain/src/main/java/com/codenames/services/UserService.java +++ b/codenames-domain/src/main/java/com/codenames/services/UserService.java @@ -3,6 +3,11 @@ import com.codenames.dto.CredentialDto; import com.codenames.dto.SignUpDto; import com.codenames.entity.UserEntity; +import com.codenames.entity.UserAuthRoleEntity; +import com.codenames.exception.UserAlreadyExistsException; +import com.codenames.exception.UserNotFoundException; +import com.codenames.mapper.UserMapper; +import com.codenames.repository.UserAuthRoleRepository; import com.codenames.exception.UserAlreadyExistsException; import com.codenames.exception.UserNotFoundException; import com.codenames.mapper.UserMapper; @@ -10,10 +15,15 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Controller; import java.nio.CharBuffer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import java.util.Optional; @Controller @@ -26,6 +36,8 @@ public class UserService { private final UserMapper userMapper; + private final UserAuthRoleRepository userAuthRoleRepository; + public T findByNickname(Class rClass, String login){ return userRepository.findByLogin(rClass, login) .orElseThrow(() -> new UserNotFoundException("User not found", HttpStatus.NOT_FOUND)); @@ -69,4 +81,28 @@ public Optional getUserEntityFromAuthentication(Authentication authe return Optional.of((UserEntity) principal); } + + public Optional getUserAuthRole(Authentication authentication){ + + // TODO: 22.05.2023 Perhaps this method will need to be corrected, but this is after adding a full-fledged user role assignment system + + if (authentication.getAuthorities() != null){ + if (!(authentication.getPrincipal() instanceof UserEntity user)){ + return Optional.empty(); + } + + Collection authorities = authentication.getAuthorities(); + + List userAuthRoles = userAuthRoleRepository.findUserAuthRoleEntitiesByUser(user) + .orElse(new ArrayList<>()); + + for (UserAuthRoleEntity role : userAuthRoles){ + if (authorities.contains(new SimpleGrantedAuthority(role.getRole()))) { + return Optional.of(role); + } + } + } + + return Optional.empty(); + } } diff --git a/codenames-front-end/src/App.tsx b/codenames-front-end/src/App.tsx index bc92706..6167961 100644 --- a/codenames-front-end/src/App.tsx +++ b/codenames-front-end/src/App.tsx @@ -9,16 +9,22 @@ import {Flip, ToastContainer} from "react-toastify"; import {UserAuthRole} from "./models/CodeNames/UserAuthRole"; import {notify} from "./models"; + +type WhoamiResponse = { + role: UserAuthRole; +} + + function App() { const isAuthorized = useRef(!!getAuthToken()) - const [checkUserIsAuth, isLoading, error] = useFetching(async () => { + const [checkUserIsAuth] = useFetching(async () => { try { - const response: AxiosResponse = await authRequest("GET", RestConfig.paths.request.whoami, {}) + const response: AxiosResponse = await authRequest("GET", RestConfig.paths.request.whoami, {}) - if (response.data === UserAuthRole.USER || response.data === UserAuthRole.ADMIN) { + if (response.data.role === UserAuthRole.USER || response.data.role === UserAuthRole.ADMIN) { isAuthorized.current = true - } else if (response.data === UserAuthRole.GUEST) { + } else if (response.data.role === UserAuthRole.GUEST) { notify.info("You are logged in as a GUEST, which may limit your possibilities") } } catch (e) { @@ -69,4 +75,4 @@ function App() { ); } -export default App; +export default App; \ No newline at end of file diff --git a/codenames-front-end/src/models/CodeNames/UserAuthRole.ts b/codenames-front-end/src/models/CodeNames/UserAuthRole.ts index 4fdeba1..34085c9 100644 --- a/codenames-front-end/src/models/CodeNames/UserAuthRole.ts +++ b/codenames-front-end/src/models/CodeNames/UserAuthRole.ts @@ -1,7 +1,7 @@ export enum UserAuthRole { - GUEST = "GUEST", - USER = "USER", - ADMIN = "ADMIN" + GUEST = "ROLE_GUEST", + USER = "ROLE_USER", + ADMIN = "ROLE_ADMIN" } \ No newline at end of file diff --git a/codenames-web-services/src/main/java/com/codenames/controllers/UsersController.java b/codenames-web-services/src/main/java/com/codenames/controllers/UsersController.java index cd4241a..fcfdd17 100644 --- a/codenames-web-services/src/main/java/com/codenames/controllers/UsersController.java +++ b/codenames-web-services/src/main/java/com/codenames/controllers/UsersController.java @@ -3,6 +3,10 @@ import com.codenames.dto.CredentialDto; import com.codenames.dto.SignUpDto; import com.codenames.dto.UserAuthDto; +import com.codenames.dto.UserAuthRoleDto; +import com.codenames.entity.UserAuthRoleEntity; +import com.codenames.enums.DefaultUserAuthRole; +import com.codenames.mapper.UserAuthRoleMapper; import com.codenames.enums.UserAuthRole; import com.codenames.mapper.UserMapper; import com.codenames.provider.UserAuthProvider; @@ -20,7 +24,6 @@ import java.util.Collection; - @RestController @RequiredArgsConstructor public class UsersController { @@ -31,6 +34,8 @@ public class UsersController { private final UserMapper userMapper; + private final UserAuthRoleMapper userAuthRoleMapper; + @PostMapping("/login") public ResponseEntity loginUser(@Valid @RequestBody CredentialDto credentialDto){ UserAuthDto user = userMapper.userEntityToUserAuthDto(userService.login(credentialDto)); diff --git a/pom.xml b/pom.xml index 5042ad3..9a1a2c3 100644 --- a/pom.xml +++ b/pom.xml @@ -69,6 +69,16 @@ 8.0.33 + + org.flywaydb + flyway-core + + + + org.flywaydb + flyway-mysql + + org.springframework.boot spring-boot-starter-data-redis