Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

32 changes: 17 additions & 15 deletions src/main/java/de/dhbw/tinf22b6/codespark/api/model/Account.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
package de.dhbw.tinf22b6.codespark.api.model;

import de.dhbw.tinf22b6.codespark.api.common.UserRoleType;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.*;

@Getter
@Setter
Expand All @@ -29,15 +26,26 @@ public class Account {
@Column(nullable = false)
private String password;

@Enumerated(EnumType.STRING)
private UserRoleType role;

@Column(length = 512)
private String profileImageUrl;

@Column(nullable = false)
private boolean verified;

@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "account_role",
joinColumns = @JoinColumn(name = "account_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Set<Role> roles = new HashSet<>();

@Column(nullable = false, updatable = false)
private LocalDateTime creationDate;

@Column(nullable = false)
private LocalDateTime lastLogin;

@OneToOne(mappedBy = "account", cascade = CascadeType.ALL, orphanRemoval = true)
private Conversation conversation;

Expand All @@ -47,19 +55,13 @@ public class Account {
@OneToOne(mappedBy = "account", cascade = CascadeType.ALL, orphanRemoval = true)
private ExamDate examDate;

@Column(nullable = false, updatable = false)
private LocalDateTime creationDate;

@Column(nullable = false)
private LocalDateTime lastLogin;

public Account(String username, String email, String password, UserRoleType role, boolean verified,
public Account(String username, String email, String password, boolean verified, Set<Role> roles,
LocalDateTime creationDate, LocalDateTime lastLogin) {
this.username = username;
this.email = email;
this.password = password;
this.role = role;
this.verified = verified;
this.roles = roles;
this.creationDate = creationDate;
this.lastLogin = lastLogin;
}
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/de/dhbw/tinf22b6/codespark/api/model/Role.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package de.dhbw.tinf22b6.codespark.api.model;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.UUID;

@Getter
@Setter
@NoArgsConstructor
@Entity
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;

@Column(unique = true, nullable = false)
private String name;

private String description;

public Role(String name, String description) {
this.name = name;
this.description = description;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package de.dhbw.tinf22b6.codespark.api.repository;

import de.dhbw.tinf22b6.codespark.api.model.Role;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;
import java.util.UUID;

public interface RoleRepository extends JpaRepository<Role, UUID> {
Optional<Role> findByName(String name);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Collections;
import java.util.List;

@Component
public class JwtFilter extends OncePerRequestFilter {
Expand Down Expand Up @@ -50,9 +50,12 @@ protected void doFilterInternal(@NonNull HttpServletRequest request,
Account account = accountRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("Username not found"));

List<SimpleGrantedAuthority> authorities = account.getRoles().stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.getName()))
.toList();

UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
// Prefix with 'ROLE_' so 'hasRole()' can be used instead of 'hasAuthority()'
account, null, Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + account.getRole().name()))
account, null, authorities
);

SecurityContext context = SecurityContextHolder.createEmptyContext();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.List;

@Component
public class WebSocketAuthInterceptor implements ChannelInterceptor {
Expand Down Expand Up @@ -48,8 +48,12 @@ public Message<?> preSend(@NonNull Message<?> message, @NonNull MessageChannel c
Account account = accountRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("Username not found"));

List<SimpleGrantedAuthority> authorities = account.getRoles().stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.getName()))
.toList();

UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
account.getUsername(), null, Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + account.getRole().name()))
account.getUsername(), null, authorities
);

SecurityContextHolder.getContext().setAuthentication(authToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,20 @@

import com.cloudinary.Cloudinary;
import com.cloudinary.utils.ObjectUtils;
import de.dhbw.tinf22b6.codespark.api.common.UserRoleType;
import de.dhbw.tinf22b6.codespark.api.common.VerificationTokenType;
import de.dhbw.tinf22b6.codespark.api.exception.AccountAlreadyExistsException;
import de.dhbw.tinf22b6.codespark.api.exception.ExpiredVerificationTokenException;
import de.dhbw.tinf22b6.codespark.api.exception.ImageUploadException;
import de.dhbw.tinf22b6.codespark.api.exception.InvalidVerificationTokenException;
import de.dhbw.tinf22b6.codespark.api.exception.*;
import de.dhbw.tinf22b6.codespark.api.model.Account;
import de.dhbw.tinf22b6.codespark.api.model.Role;
import de.dhbw.tinf22b6.codespark.api.model.VerificationToken;
import de.dhbw.tinf22b6.codespark.api.payload.request.AccountCreateRequest;
import de.dhbw.tinf22b6.codespark.api.payload.request.PasswordResetRequest;
import de.dhbw.tinf22b6.codespark.api.payload.request.RequestPasswordResetRequest;
import de.dhbw.tinf22b6.codespark.api.payload.response.AccountDetailsResponse;
import de.dhbw.tinf22b6.codespark.api.payload.response.UploadImageResponse;
import de.dhbw.tinf22b6.codespark.api.repository.AccountRepository;
import de.dhbw.tinf22b6.codespark.api.repository.RoleRepository;
import de.dhbw.tinf22b6.codespark.api.repository.VerificationTokenRepository;
import de.dhbw.tinf22b6.codespark.api.service.common.PredefinedUserRole;
import de.dhbw.tinf22b6.codespark.api.service.interfaces.AccountService;
import de.dhbw.tinf22b6.codespark.api.service.interfaces.EmailService;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -29,27 +28,28 @@
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.*;

@Service
public class AccountServiceImpl implements AccountService {
private final AccountRepository accountRepository;
private final VerificationTokenRepository verificationTokenRepository;
private final RoleRepository roleRepository;
private final EmailService emailService;
private final PasswordEncoder passwordEncoder;
private final Cloudinary cloudinary;
private final Environment env;

public AccountServiceImpl(@Autowired AccountRepository accountRepository,
@Autowired VerificationTokenRepository verificationTokenRepository,
@Autowired RoleRepository roleRepository,
@Autowired EmailService emailService,
@Autowired PasswordEncoder passwordEncoder,
@Autowired Cloudinary cloudinary,
@Autowired Environment env) {
this.accountRepository = accountRepository;
this.verificationTokenRepository = verificationTokenRepository;
this.roleRepository = roleRepository;
this.emailService = emailService;
this.passwordEncoder = passwordEncoder;
this.cloudinary = cloudinary;
Expand Down Expand Up @@ -77,10 +77,13 @@ public void createAccount(AccountCreateRequest request) {
throw new AccountAlreadyExistsException("This username is already taken.");
}

Role userRole = roleRepository.findByName(PredefinedUserRole.USER.getName())
.orElseThrow(() -> new EntryNotFoundException("The requested role does not exists."));

String encodedPassword = passwordEncoder.encode(request.getPassword());
LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
Account account = new Account(request.getUsername(), request.getEmail(), encodedPassword,
UserRoleType.USER, false, now, now);
Account account = new Account(request.getUsername(), request.getEmail(), encodedPassword, false,
Set.of(userRole), now, now);
accountRepository.save(account);

String token = UUID.randomUUID().toString();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package de.dhbw.tinf22b6.codespark.api.service.common;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public enum PredefinedUserRole {
USER("USER"),
ADMIN("ADMIN");

private final String name;
}
5 changes: 0 additions & 5 deletions src/main/resources/application-prod.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,2 @@
spring:
jpa:
hibernate:
ddl-auto: none

app:
base-url: "https://codespark-api.up.railway.app"
3 changes: 3 additions & 0 deletions src/main/resources/application-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ spring:
path: /h2-console
liquibase:
enabled: false
jpa:
hibernate:
ddl-auto: create-drop

auth:
jwt:
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ spring:
password: ${DATABASE_PASSWORD}
jpa:
hibernate:
ddl-auto: update
ddl-auto: none
show-sql: false
open-in-view: false
liquibase:
Expand Down
100 changes: 100 additions & 0 deletions src/main/resources/db/changelog/changes/002_roles-refactor.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
databaseChangeLog:
- changeSet:
id: 2
author: denniskp
comment: "Remove old enum-based role column from account table"
changes:
- dropColumn:
tableName: account
columnName: role

- changeSet:
id: 2.1
author: denniskp
comment: "Create role table"
changes:
- createTable:
tableName: role
columns:
- column:
name: id
type: UUID
constraints:
primaryKey: true
- column:
name: name
type: VARCHAR(50)
constraints:
nullable: false
unique: true
- column:
name: description
type: VARCHAR(255)
constraints:
nullable: true

- changeSet:
id: 2.2
author: denniskp
comment: "Insert default USER and ADMIN roles"
changes:
- insert:
tableName: role
columns:
- column: { name: id, valueComputed: random_uuid() }
- column: { name: name, value: USER }
- column: { name: description, value: Default user role }
- insert:
tableName: role
columns:
- column: { name: id, valueComputed: random_uuid() }
- column: { name: name, value: ADMIN }
- column: { name: description, value: Administrator role }

- changeSet:
id: 2.3
author: denniskp
comment: "Create account_role join table"
changes:
- createTable:
tableName: account_role
columns:
- column:
name: account_id
type: UUID
constraints:
nullable: false
- column:
name: role_id
type: UUID
constraints:
nullable: false
- addForeignKeyConstraint:
baseTableName: account_role
baseColumnNames: account_id
referencedTableName: account
referencedColumnNames: id
onDelete: CASCADE
constraintName: fk_account_role_account
- addForeignKeyConstraint:
baseTableName: account_role
baseColumnNames: role_id
referencedTableName: role
referencedColumnNames: id
onDelete: CASCADE
constraintName: fk_account_role_role

- changeSet:
id: 2.4
author: denniskp
comment: "Assign USER role to all existing accounts"
changes:
- sql:
comment: "Insert USER role assignments into account_role"
splitStatements: false
sql: |
INSERT INTO account_role (account_id, role_id)
SELECT a.id, r.id
FROM account a
CROSS JOIN role r
WHERE r.name = 'USER';
2 changes: 2 additions & 0 deletions src/main/resources/db/changelog/db.changelog-master.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
databaseChangeLog:
- include:
file: db/changelog/changes/001_add-account-timestamps.yaml
- include:
file: db/changelog/changes/002_roles-refactor.yaml
Loading