Pre admin
This commit is contained in:
@@ -0,0 +1,9 @@
|
|||||||
|
package net.tokishu.note.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
@Target(ElementType.PARAMETER)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
public @interface CurrentUser {
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package net.tokishu.note.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
@Target(ElementType.PARAMETER)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
public @interface NullableCurrentUser {
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package net.tokishu.note.config;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import net.tokishu.note.resolver.CurrentUserArgumentResolver;
|
||||||
|
import net.tokishu.note.resolver.NullableCurrentUserArgumentResolver;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class WebConfig implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
private final CurrentUserArgumentResolver currentUserArgumentResolver;
|
||||||
|
private final NullableCurrentUserArgumentResolver nullableCurrentUserArgumentResolver;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
|
||||||
|
resolvers.add(currentUserArgumentResolver);
|
||||||
|
resolvers.add(nullableCurrentUserArgumentResolver);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,7 +26,6 @@ import java.util.Map;
|
|||||||
public class AuthController {
|
public class AuthController {
|
||||||
|
|
||||||
public final AuthService authService;
|
public final AuthService authService;
|
||||||
public final UserService userService;
|
|
||||||
|
|
||||||
@PostMapping("/register")
|
@PostMapping("/register")
|
||||||
public ResponseEntity<RegisterResponse> register(@RequestBody @Valid RegisterRequest request) {
|
public ResponseEntity<RegisterResponse> register(@RequestBody @Valid RegisterRequest request) {
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ package net.tokishu.note.controller;
|
|||||||
|
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import net.tokishu.note.annotation.CurrentUser;
|
||||||
|
import net.tokishu.note.annotation.NullableCurrentUser;
|
||||||
import net.tokishu.note.dto.request.NoteRequest;
|
import net.tokishu.note.dto.request.NoteRequest;
|
||||||
import net.tokishu.note.dto.response.NoteResponse;
|
import net.tokishu.note.dto.response.NoteResponse;
|
||||||
|
import net.tokishu.note.model.User;
|
||||||
import net.tokishu.note.service.NoteService;
|
import net.tokishu.note.service.NoteService;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
@@ -23,30 +26,30 @@ public class NotesController {
|
|||||||
private final NoteService noteService;
|
private final NoteService noteService;
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public ResponseEntity<List<NoteResponse>> getAll(){
|
public ResponseEntity<List<NoteResponse>> getAll(@CurrentUser User sender){
|
||||||
return ResponseEntity.ok(noteService.getAll());
|
return ResponseEntity.ok(noteService.getAll(sender));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{idOrCode}")
|
@GetMapping("/{idOrCode}")
|
||||||
public NoteResponse find(@PathVariable String idOrCode) {
|
public NoteResponse find(@PathVariable String idOrCode, @NullableCurrentUser User sender) {
|
||||||
return noteService.findByIdOrPublicLink(idOrCode);
|
return noteService.findByIdOrPublicLink(idOrCode, sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping()
|
@PostMapping()
|
||||||
public ResponseEntity<Map<String, String>> add(@RequestBody @Valid NoteRequest note){
|
public ResponseEntity<Map<String, String>> add(@RequestBody @Valid NoteRequest note, @CurrentUser User sender){
|
||||||
noteService.add(note);
|
noteService.add(note, sender);
|
||||||
return ResponseEntity.status(HttpStatus.CREATED).body(Map.of("message", "Note added"));
|
return ResponseEntity.status(HttpStatus.CREATED).body(Map.of("message", "Note added"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{uuid}")
|
@PutMapping("/{uuid}")
|
||||||
public ResponseEntity<?> update(@PathVariable("uuid") UUID uuid, @RequestBody @Valid NoteRequest note){
|
public ResponseEntity<?> update(@PathVariable("uuid") UUID uuid, @RequestBody @Valid NoteRequest note, @CurrentUser User sender){
|
||||||
noteService.update(uuid, note);
|
noteService.update(uuid, note, sender);
|
||||||
return ResponseEntity.ok(Map.of("message", "Note updated"));
|
return ResponseEntity.ok(Map.of("message", "Note updated"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{uuid}")
|
@DeleteMapping("/{uuid}")
|
||||||
public ResponseEntity<?> delete(@PathVariable UUID uuid){
|
public ResponseEntity<?> delete(@PathVariable UUID uuid, @CurrentUser User sender){
|
||||||
noteService.delete(uuid);
|
noteService.delete(uuid, sender);
|
||||||
return ResponseEntity.ok(Map.of("message", "Note deleted"));
|
return ResponseEntity.ok(Map.of("message", "Note deleted"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,79 @@
|
|||||||
package net.tokishu.note.controller;
|
package net.tokishu.note.controller;
|
||||||
|
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.Builder;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import net.tokishu.note.annotation.CurrentUser;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import net.tokishu.note.dto.request.ChangePasswordRequest;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import net.tokishu.note.dto.request.UpdateProfileRequest;
|
||||||
|
import net.tokishu.note.dto.request.UserByAdminRequest;
|
||||||
|
import net.tokishu.note.dto.response.ApiResponse;
|
||||||
|
import net.tokishu.note.dto.response.UserResponse;
|
||||||
|
import net.tokishu.note.model.User;
|
||||||
|
import net.tokishu.note.service.UserService;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/users")
|
@RequestMapping("/users")
|
||||||
public class UserController {
|
public class UserController {
|
||||||
|
|
||||||
|
private final UserService userService;
|
||||||
|
|
||||||
|
@GetMapping("/me")
|
||||||
|
public UserResponse me(@CurrentUser User sender){
|
||||||
|
return userService.getCurrentUserResponse(sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/me")
|
||||||
|
public UserResponse updateMyProfile(@RequestBody @Valid UpdateProfileRequest request, @CurrentUser User sender) {
|
||||||
|
return userService.updateCurrentUser(request, sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/me/password")
|
||||||
|
public ApiResponse changePassword(@RequestBody @Valid ChangePasswordRequest request, @CurrentUser User sender) {
|
||||||
|
// userService.changePassword(request);
|
||||||
|
return ApiResponse.builder().status(200).message("JoJ").build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
ADMIN ACTIONS
|
||||||
|
*/
|
||||||
|
|
||||||
|
// @PostMapping("/new")
|
||||||
|
// @PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
// public ResponseEntity<?> newUser(@RequestBody @Valid UserByAdminRequest request, @CurrentUser User sender){
|
||||||
|
// return userService.createUserByAdmin(request);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @GetMapping
|
||||||
|
// @PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
// public ResponseEntity<List<UserResponse>> getAllUsers(@CurrentUser User sender) {
|
||||||
|
// return userService.getAllUsers();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @GetMapping("/{username}")
|
||||||
|
// @PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
// public UserResponse getUserByUsername(@PathVariable String username, @CurrentUser User sender) {
|
||||||
|
// return userService.getUserByUsername(username);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// @PutMapping("/{username}")
|
||||||
|
// @PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
// public UserResponse editUser(@RequestBody @Valid UserByAdminRequest request, @PathVariable String username, @CurrentUser User sender){
|
||||||
|
// return userService.editUser(username, request);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @DeleteMapping("/{username}")
|
||||||
|
// @PreAuthorize("hasAuthority('ADMIN')")
|
||||||
|
// public ApiResponse removeUser(@PathVariable String username, @CurrentUser User sender){
|
||||||
|
// return userService.deleteUser(username);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package net.tokishu.note.dto.request;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ChangePasswordRequest {
|
||||||
|
@NotBlank
|
||||||
|
private String oldPassword;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
private String newPassword;
|
||||||
|
}
|
||||||
@@ -1,16 +1,19 @@
|
|||||||
package net.tokishu.note.dto.request;
|
package net.tokishu.note.dto.request;
|
||||||
|
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Pattern;
|
||||||
import jakarta.validation.constraints.Size;
|
import jakarta.validation.constraints.Size;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class RegisterRequest {
|
public class RegisterRequest {
|
||||||
@NotBlank
|
@NotBlank()
|
||||||
@Size(min=1, max=24)
|
@Size(min = 3, max = 24)
|
||||||
|
@Pattern(regexp = "^[a-zA-Z0-9_-]+$",
|
||||||
|
message = "The username can only contain letters, numbers, symbols and hyphens.")
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
@NotBlank
|
@NotBlank()
|
||||||
@Size(min=6, max=42)
|
@Size(min = 6, max = 42)
|
||||||
private String password;
|
private String password;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package net.tokishu.note.dto.request;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Pattern;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class UpdateProfileRequest {
|
||||||
|
@NotBlank()
|
||||||
|
@Size(min = 3, max = 24)
|
||||||
|
@Pattern(regexp = "^[a-zA-Z0-9_-]+$",
|
||||||
|
message = "The username can only contain letters, numbers, symbols and hyphens.")
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@NotBlank()
|
||||||
|
private String gravatarEmail;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package net.tokishu.note.dto.request;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Pattern;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import lombok.Data;
|
||||||
|
import net.tokishu.note.model.UserRole;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class UserByAdminRequest {
|
||||||
|
@NotBlank()
|
||||||
|
@Size(min = 3, max = 24)
|
||||||
|
@Pattern(regexp = "^[a-zA-Z0-9_-]+$",
|
||||||
|
message = "The username can only contain letters, numbers, symbols and hyphens.")
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@NotBlank()
|
||||||
|
@Size(min = 6, max = 42)
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
private UserRole role;
|
||||||
|
}
|
||||||
+1
-1
@@ -5,7 +5,7 @@ import lombok.Getter;
|
|||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Builder
|
@Builder
|
||||||
public class ApiErrorResponse {
|
public class ApiResponse {
|
||||||
private final int status;
|
private final int status;
|
||||||
private final String message;
|
private final String message;
|
||||||
}
|
}
|
||||||
@@ -2,11 +2,12 @@ package net.tokishu.note.dto.response;
|
|||||||
|
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import net.tokishu.note.model.UserRole;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@Builder
|
@Builder
|
||||||
public class LoginResponse {
|
public class LoginResponse {
|
||||||
private String token;
|
private String token;
|
||||||
private String username;
|
private String username;
|
||||||
private String role;
|
private UserRole role;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ package net.tokishu.note.dto.response;
|
|||||||
|
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import net.tokishu.note.model.UserRole;
|
||||||
|
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@Builder
|
@Builder
|
||||||
public class RegisterResponse {
|
public class RegisterResponse {
|
||||||
private String username;
|
private String username;
|
||||||
private String role;
|
private UserRole role;
|
||||||
private String message;
|
private String message;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ package net.tokishu.note.dto.response;
|
|||||||
|
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import net.tokishu.note.model.UserRole;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@Builder
|
@Builder
|
||||||
public class RootResponse {
|
public class RootResponse {
|
||||||
private String username;
|
private String username;
|
||||||
private String password;
|
private String password;
|
||||||
private String role;
|
private UserRole role;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ package net.tokishu.note.dto.response;
|
|||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
|
import net.tokishu.note.model.UserRole;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@Builder
|
@Builder
|
||||||
public class UserResponse {
|
public class UserResponse {
|
||||||
private String username;
|
private String username;
|
||||||
private String role;
|
private String gravatarUrl;
|
||||||
|
private UserRole role;
|
||||||
}
|
}
|
||||||
@@ -1,20 +1,17 @@
|
|||||||
package net.tokishu.note.exception;
|
package net.tokishu.note.exception;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import net.tokishu.note.dto.response.ApiResponse;
|
||||||
import net.tokishu.note.dto.response.ApiErrorResponse;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
|
||||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
import org.springframework.web.servlet.NoHandlerFoundException;
|
|
||||||
|
|
||||||
@ControllerAdvice
|
@ControllerAdvice
|
||||||
public class GlobalExceptionHandler {
|
public class GlobalExceptionHandler {
|
||||||
|
|
||||||
@ExceptionHandler(Exception.class)
|
@ExceptionHandler(Exception.class)
|
||||||
public ResponseEntity<ApiErrorResponse> handleAll(Exception ex) {
|
public ResponseEntity<ApiResponse> handleAll(Exception ex) {
|
||||||
if (ex instanceof MethodArgumentNotValidException validationEx) {
|
if (ex instanceof MethodArgumentNotValidException validationEx) {
|
||||||
return handleValidation(validationEx);
|
return handleValidation(validationEx);
|
||||||
}
|
}
|
||||||
@@ -49,7 +46,7 @@ public class GlobalExceptionHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||||
public ResponseEntity<ApiErrorResponse> handleValidation(MethodArgumentNotValidException ex) {
|
public ResponseEntity<ApiResponse> handleValidation(MethodArgumentNotValidException ex) {
|
||||||
String message = ex.getBindingResult().getFieldErrors().stream()
|
String message = ex.getBindingResult().getFieldErrors().stream()
|
||||||
.map(error -> error.getField() + ": " + error.getDefaultMessage())
|
.map(error -> error.getField() + ": " + error.getDefaultMessage())
|
||||||
.findFirst()
|
.findFirst()
|
||||||
@@ -59,10 +56,10 @@ public class GlobalExceptionHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private ResponseEntity<ApiErrorResponse> buildErrorResponse(int status, String message) {
|
private ResponseEntity<ApiResponse> buildErrorResponse(int status, String message) {
|
||||||
return ResponseEntity
|
return ResponseEntity
|
||||||
.status(status)
|
.status(status)
|
||||||
.body(ApiErrorResponse.builder()
|
.body(ApiResponse.builder()
|
||||||
.status(status)
|
.status(status)
|
||||||
.message(message != null ? message : "Unexpected error")
|
.message(message != null ? message : "Unexpected error")
|
||||||
.build());
|
.build());
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package net.tokishu.note.exception;
|
|||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import net.tokishu.note.dto.response.ApiErrorResponse;
|
import net.tokishu.note.dto.response.ApiResponse;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
@@ -20,7 +20,7 @@ public class SecurityExceptionHandler implements AuthenticationEntryPoint, Acces
|
|||||||
private void writeErrorResponse(HttpServletResponse response, int status, String message) throws IOException {
|
private void writeErrorResponse(HttpServletResponse response, int status, String message) throws IOException {
|
||||||
response.setStatus(status);
|
response.setStatus(status);
|
||||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||||
ApiErrorResponse body = ApiErrorResponse.builder()
|
ApiResponse body = ApiResponse.builder()
|
||||||
.status(status)
|
.status(status)
|
||||||
.message(message)
|
.message(message)
|
||||||
.build();
|
.build();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package net.tokishu.note.model;
|
package net.tokishu.note.model;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
|
import jakarta.validation.constraints.Email;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.hibernate.annotations.ColumnDefault;
|
import org.hibernate.annotations.ColumnDefault;
|
||||||
import org.hibernate.annotations.CreationTimestamp;
|
import org.hibernate.annotations.CreationTimestamp;
|
||||||
@@ -13,11 +14,19 @@ import java.util.UUID;
|
|||||||
@Table(name = "Users")
|
@Table(name = "Users")
|
||||||
public class User {
|
public class User {
|
||||||
@Id
|
@Id
|
||||||
|
@Column(length = 24)
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
@ColumnDefault("'USER'")
|
@ColumnDefault("'USER'")
|
||||||
private String role;
|
private UserRole role;
|
||||||
|
|
||||||
|
@Email
|
||||||
|
@Column(nullable = true)
|
||||||
|
private String gravatarEmail;
|
||||||
|
|
||||||
@CreationTimestamp
|
@CreationTimestamp
|
||||||
@Column(updatable = false)
|
@Column(updatable = false)
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package net.tokishu.note.model;
|
||||||
|
|
||||||
|
public enum UserRole {
|
||||||
|
ADMIN, USER
|
||||||
|
}
|
||||||
@@ -2,6 +2,9 @@ package net.tokishu.note.repo;
|
|||||||
|
|
||||||
import net.tokishu.note.model.Note;
|
import net.tokishu.note.model.Note;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Modifying;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -12,4 +15,8 @@ public interface NoteRepository extends JpaRepository<Note, UUID> {
|
|||||||
List<Note> findByAuthorUsernameOrderByCreatedAtDesc(String username);
|
List<Note> findByAuthorUsernameOrderByCreatedAtDesc(String username);
|
||||||
Optional<Note> findByPublicLinkAndIsPublicTrue(String publicLink);
|
Optional<Note> findByPublicLinkAndIsPublicTrue(String publicLink);
|
||||||
boolean existsByPublicLink(String publicLink);
|
boolean existsByPublicLink(String publicLink);
|
||||||
|
|
||||||
|
@Modifying
|
||||||
|
@Query("UPDATE Note n SET n.author = :newUsername WHERE n.author = :oldUsername")
|
||||||
|
void updateOwnerUsername(@Param("oldUsername") String oldUsername, @Param("newUsername") String newUsername);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,19 @@
|
|||||||
package net.tokishu.note.repo;
|
package net.tokishu.note.repo;
|
||||||
|
|
||||||
import net.tokishu.note.model.User;
|
import net.tokishu.note.model.User;
|
||||||
|
import net.tokishu.note.model.UserRole;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Modifying;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public interface UserRepository extends JpaRepository<User, String> {
|
public interface UserRepository extends JpaRepository<User, String> {
|
||||||
boolean existsByRole(String role);
|
boolean existsByRole(UserRole role);
|
||||||
|
|
||||||
|
@Modifying
|
||||||
|
@Query("UPDATE User u SET u.username = :newUsername WHERE u.username = :oldUsername")
|
||||||
|
void updateUsername(@Param("oldUsername") String oldUsername, @Param("newUsername") String newUsername);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package net.tokishu.note.resolver;
|
||||||
|
|
||||||
|
import net.tokishu.note.annotation.CurrentUser;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import net.tokishu.note.model.User;
|
||||||
|
import net.tokishu.note.service.AuthService;
|
||||||
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||||
|
import org.springframework.web.context.request.NativeWebRequest;
|
||||||
|
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||||
|
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver {
|
||||||
|
|
||||||
|
private final AuthService authService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsParameter(MethodParameter parameter) {
|
||||||
|
return parameter.hasParameterAnnotation(CurrentUser.class)
|
||||||
|
&& parameter.getParameterType().equals(User.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object resolveArgument(MethodParameter parameter,
|
||||||
|
ModelAndViewContainer mavContainer,
|
||||||
|
NativeWebRequest webRequest,
|
||||||
|
WebDataBinderFactory binderFactory) {
|
||||||
|
return authService.getCurrentUser();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package net.tokishu.note.resolver;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import net.tokishu.note.annotation.NullableCurrentUser;
|
||||||
|
import net.tokishu.note.model.User;
|
||||||
|
import net.tokishu.note.service.AuthService;
|
||||||
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||||
|
import org.springframework.web.context.request.NativeWebRequest;
|
||||||
|
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||||
|
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class NullableCurrentUserArgumentResolver implements HandlerMethodArgumentResolver {
|
||||||
|
|
||||||
|
private final AuthService authService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsParameter(MethodParameter parameter) {
|
||||||
|
return parameter.hasParameterAnnotation(NullableCurrentUser.class)
|
||||||
|
&& parameter.getParameterType().equals(User.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object resolveArgument(MethodParameter parameter,
|
||||||
|
ModelAndViewContainer mavContainer,
|
||||||
|
NativeWebRequest webRequest,
|
||||||
|
WebDataBinderFactory binderFactory) {
|
||||||
|
return authService.getCurrentUserOrNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,11 +6,17 @@ import net.tokishu.note.dto.request.RegisterRequest;
|
|||||||
import net.tokishu.note.dto.response.LoginResponse;
|
import net.tokishu.note.dto.response.LoginResponse;
|
||||||
import net.tokishu.note.dto.response.RegisterResponse;
|
import net.tokishu.note.dto.response.RegisterResponse;
|
||||||
import net.tokishu.note.dto.response.RootResponse;
|
import net.tokishu.note.dto.response.RootResponse;
|
||||||
|
import net.tokishu.note.dto.response.UserResponse;
|
||||||
import net.tokishu.note.model.User;
|
import net.tokishu.note.model.User;
|
||||||
|
import net.tokishu.note.model.UserRole;
|
||||||
import net.tokishu.note.repo.UserRepository;
|
import net.tokishu.note.repo.UserRepository;
|
||||||
import net.tokishu.note.security.JwtService;
|
import net.tokishu.note.security.JwtService;
|
||||||
|
import net.tokishu.note.util.GravatarUtil;
|
||||||
import net.tokishu.note.util.PasswordGenerator;
|
import net.tokishu.note.util.PasswordGenerator;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
@@ -19,24 +25,14 @@ import org.springframework.web.server.ResponseStatusException;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class AuthService {
|
public class AuthService {
|
||||||
|
|
||||||
public final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
private final PasswordEncoder passwordEncoder;
|
private final PasswordEncoder passwordEncoder;
|
||||||
private final JwtService jwtService;
|
private final JwtService jwtService;
|
||||||
|
private final UserService userService;
|
||||||
|
|
||||||
public RootResponse registerRoot() {
|
public RootResponse registerRoot() {
|
||||||
if (userRepository.existsByRole("ADMIN")) {
|
|
||||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Administrator already exists");
|
|
||||||
}
|
|
||||||
|
|
||||||
String rawPassword = PasswordGenerator.generate(16);
|
String rawPassword = PasswordGenerator.generate(16);
|
||||||
String hashedPassword = passwordEncoder.encode(rawPassword);
|
User rootUser = userService.createUser("root", rawPassword, UserRole.ADMIN, null);
|
||||||
|
|
||||||
User rootUser = new User();
|
|
||||||
rootUser.setUsername("root");
|
|
||||||
rootUser.setPassword(hashedPassword);
|
|
||||||
rootUser.setRole("ADMIN");
|
|
||||||
|
|
||||||
userRepository.save(rootUser);
|
|
||||||
|
|
||||||
return RootResponse.builder()
|
return RootResponse.builder()
|
||||||
.username(rootUser.getUsername())
|
.username(rootUser.getUsername())
|
||||||
@@ -45,18 +41,8 @@ public class AuthService {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public RegisterResponse registerUser(RegisterRequest request) {
|
public RegisterResponse registerUser(RegisterRequest request) {
|
||||||
if (userRepository.existsById(request.getUsername())) {
|
User user = userService.createRegularUser(request.getUsername(), request.getPassword());
|
||||||
throw new ResponseStatusException(HttpStatus.CONFLICT, "User already exists");
|
|
||||||
}
|
|
||||||
|
|
||||||
User user = new User();
|
|
||||||
user.setUsername(request.getUsername());
|
|
||||||
user.setPassword(passwordEncoder.encode(request.getPassword()));
|
|
||||||
user.setRole("USER");
|
|
||||||
|
|
||||||
userRepository.save(user);
|
|
||||||
|
|
||||||
return RegisterResponse.builder()
|
return RegisterResponse.builder()
|
||||||
.username(user.getUsername())
|
.username(user.getUsername())
|
||||||
@@ -77,7 +63,7 @@ public class AuthService {
|
|||||||
org.springframework.security.core.userdetails.User
|
org.springframework.security.core.userdetails.User
|
||||||
.withUsername(user.getUsername())
|
.withUsername(user.getUsername())
|
||||||
.password(user.getPassword())
|
.password(user.getPassword())
|
||||||
.roles(user.getRole())
|
.roles(user.getRole().name())
|
||||||
.build()
|
.build()
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -88,4 +74,46 @@ public class AuthService {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
public User getCurrentUser() {
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
|
||||||
|
if (authentication == null || !authentication.isAuthenticated()) {
|
||||||
|
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "User not authenticated");
|
||||||
|
}
|
||||||
|
|
||||||
|
Object principal = authentication.getPrincipal();
|
||||||
|
String username;
|
||||||
|
|
||||||
|
if (principal instanceof UserDetails userDetails) {
|
||||||
|
username = userDetails.getUsername();
|
||||||
|
} else if (principal instanceof String) {
|
||||||
|
username = (String) principal;
|
||||||
|
} else {
|
||||||
|
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Unable to identify user");
|
||||||
|
}
|
||||||
|
|
||||||
|
return userRepository.findById(username)
|
||||||
|
.orElseThrow(() -> new ResponseStatusException(HttpStatus.UNAUTHORIZED, "User not found"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getCurrentUserOrNull() {
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
|
||||||
|
if (authentication == null || !authentication.isAuthenticated()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object principal = authentication.getPrincipal();
|
||||||
|
String username;
|
||||||
|
|
||||||
|
if (principal instanceof UserDetails userDetails) {
|
||||||
|
username = userDetails.getUsername();
|
||||||
|
} else if (principal instanceof String) {
|
||||||
|
username = (String) principal;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return userRepository.findById(username).orElse(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,9 @@ import net.tokishu.note.dto.request.NoteRequest;
|
|||||||
import net.tokishu.note.dto.response.NoteResponse;
|
import net.tokishu.note.dto.response.NoteResponse;
|
||||||
import net.tokishu.note.model.Note;
|
import net.tokishu.note.model.Note;
|
||||||
import net.tokishu.note.model.User;
|
import net.tokishu.note.model.User;
|
||||||
|
import net.tokishu.note.model.UserRole;
|
||||||
import net.tokishu.note.repo.NoteRepository;
|
import net.tokishu.note.repo.NoteRepository;
|
||||||
|
import net.tokishu.note.util.CheckAuthUtil;
|
||||||
import net.tokishu.note.util.CodeGenerator;
|
import net.tokishu.note.util.CodeGenerator;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -22,11 +24,10 @@ public class NoteService {
|
|||||||
public final NoteRepository noteRepository;
|
public final NoteRepository noteRepository;
|
||||||
public final UserService userService;
|
public final UserService userService;
|
||||||
|
|
||||||
public List<NoteResponse> getAll() {
|
public List<NoteResponse> getAll(User currentUser) {
|
||||||
User currentUser = userService.getCurrentUser();
|
|
||||||
List<Note> notes;
|
List<Note> notes;
|
||||||
|
|
||||||
if ("ADMIN".equalsIgnoreCase(currentUser.getRole())) {
|
if (currentUser.getRole() == UserRole.ADMIN) {
|
||||||
notes = noteRepository.findAll();
|
notes = noteRepository.findAll();
|
||||||
} else {
|
} else {
|
||||||
notes = noteRepository.findByAuthorUsername(currentUser.getUsername());
|
notes = noteRepository.findByAuthorUsername(currentUser.getUsername());
|
||||||
@@ -37,18 +38,17 @@ public class NoteService {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public NoteResponse findByIdOrPublicLink(String idOrCode) {
|
public NoteResponse findByIdOrPublicLink(String idOrCode, User currentUser) {
|
||||||
if (!StringUtils.hasText(idOrCode)) {
|
if (!StringUtils.hasText(idOrCode)) {
|
||||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid identifier or public link");
|
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid identifier or public link");
|
||||||
}
|
}
|
||||||
|
|
||||||
Note note;
|
Note note;
|
||||||
if (isUuid(idOrCode)) {
|
if (isUuid(idOrCode)) {
|
||||||
|
CheckAuthUtil.check(currentUser);
|
||||||
UUID id = UUID.fromString(idOrCode);
|
UUID id = UUID.fromString(idOrCode);
|
||||||
note = noteRepository.findById(id)
|
note = noteRepository.findById(id)
|
||||||
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Note not found"));
|
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Note not found"));
|
||||||
|
|
||||||
User currentUser = userService.getCurrentUser();
|
|
||||||
checkOwnership(note, currentUser);
|
checkOwnership(note, currentUser);
|
||||||
} else {
|
} else {
|
||||||
note = noteRepository.findByPublicLinkAndIsPublicTrue(idOrCode)
|
note = noteRepository.findByPublicLinkAndIsPublicTrue(idOrCode)
|
||||||
@@ -58,10 +58,7 @@ public class NoteService {
|
|||||||
return toResponse(note);
|
return toResponse(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NoteResponse add(NoteRequest data, User author) {
|
||||||
public NoteResponse add(NoteRequest data) {
|
|
||||||
User author = userService.getCurrentUser();
|
|
||||||
|
|
||||||
Note note = new Note();
|
Note note = new Note();
|
||||||
note.setName(data.getName());
|
note.setName(data.getName());
|
||||||
note.setText(data.getText());
|
note.setText(data.getText());
|
||||||
@@ -86,12 +83,10 @@ public class NoteService {
|
|||||||
return toResponse(saved);
|
return toResponse(saved);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NoteResponse update(UUID uuid, NoteRequest data, User currentUser) {
|
||||||
public NoteResponse update(UUID uuid, NoteRequest data) {
|
|
||||||
Note existing = noteRepository.findById(uuid)
|
Note existing = noteRepository.findById(uuid)
|
||||||
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Note not found"));
|
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Note not found"));
|
||||||
|
|
||||||
User currentUser = userService.getCurrentUser();
|
|
||||||
checkOwnership(existing, currentUser);
|
checkOwnership(existing, currentUser);
|
||||||
|
|
||||||
existing.setName(data.getName());
|
existing.setName(data.getName());
|
||||||
@@ -101,17 +96,16 @@ public class NoteService {
|
|||||||
return toResponse(noteRepository.save(existing));
|
return toResponse(noteRepository.save(existing));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void delete(UUID uuid) {
|
public void delete(UUID uuid, User currentUser) {
|
||||||
Note existing = noteRepository.findById(uuid)
|
Note existing = noteRepository.findById(uuid)
|
||||||
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Note not found"));
|
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Note not found"));
|
||||||
|
|
||||||
User currentUser = userService.getCurrentUser();
|
|
||||||
checkOwnership(existing, currentUser);
|
checkOwnership(existing, currentUser);
|
||||||
noteRepository.delete(existing);
|
noteRepository.delete(existing);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkOwnership(Note note, User user) {
|
private void checkOwnership(Note note, User user) {
|
||||||
if ("ADMIN".equalsIgnoreCase(user.getRole())) { // TODO: use enum
|
if (user.getRole() == UserRole.ADMIN) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
package net.tokishu.note.service;
|
package net.tokishu.note.service;
|
||||||
|
|
||||||
|
import jakarta.transaction.Transactional;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import net.tokishu.note.dto.request.RegisterRequest;
|
import net.tokishu.note.dto.request.UpdateProfileRequest;
|
||||||
import net.tokishu.note.dto.response.UserResponse;
|
import net.tokishu.note.dto.response.UserResponse;
|
||||||
import net.tokishu.note.model.User;
|
import net.tokishu.note.model.User;
|
||||||
|
import net.tokishu.note.model.UserRole;
|
||||||
|
import net.tokishu.note.repo.NoteRepository;
|
||||||
import net.tokishu.note.repo.UserRepository;
|
import net.tokishu.note.repo.UserRepository;
|
||||||
|
import net.tokishu.note.util.CheckAuthUtil;
|
||||||
|
import net.tokishu.note.util.GravatarUtil;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
@@ -13,45 +18,71 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class UserService {
|
public class UserService {
|
||||||
public final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
private final NoteRepository noteRepository;
|
||||||
|
|
||||||
public List<User> getAll(){
|
public UserResponse getCurrentUserResponse(User user) {
|
||||||
return userRepository.findAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
public User getCurrentUser() {
|
|
||||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
|
||||||
|
|
||||||
if (authentication == null || !authentication.isAuthenticated()) {
|
|
||||||
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "User not authenticated");
|
|
||||||
}
|
|
||||||
|
|
||||||
Object principal = authentication.getPrincipal();
|
|
||||||
String username;
|
|
||||||
|
|
||||||
if (principal instanceof UserDetails userDetails) {
|
|
||||||
username = userDetails.getUsername();
|
|
||||||
} else if (principal instanceof String) {
|
|
||||||
username = (String) principal;
|
|
||||||
} else {
|
|
||||||
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Unable to identify user");
|
|
||||||
}
|
|
||||||
|
|
||||||
return userRepository.findById(username)
|
|
||||||
.orElseThrow(() -> new ResponseStatusException(HttpStatus.UNAUTHORIZED, "User not found"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public UserResponse getCurrentUserResponse() {
|
|
||||||
User user = getCurrentUser();
|
|
||||||
return UserResponse.builder()
|
return UserResponse.builder()
|
||||||
.username(user.getUsername())
|
.username(user.getUsername())
|
||||||
.role(user.getRole())
|
.role(user.getRole())
|
||||||
|
.gravatarUrl(GravatarUtil.generateGravatarUrl(user.getGravatarEmail()))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
public User createUser(String username, String password, UserRole role, User currentUser) {
|
||||||
|
if (userRepository.existsById(username)) {
|
||||||
|
throw new ResponseStatusException(HttpStatus.CONFLICT, "User already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean adminExists = userRepository.existsByRole(UserRole.ADMIN);
|
||||||
|
boolean isCreatingAdmin = UserRole.ADMIN.equals(role);
|
||||||
|
|
||||||
|
if (isCreatingAdmin && adminExists) {
|
||||||
|
if (currentUser == null || currentUser.getRole() != UserRole.ADMIN) {
|
||||||
|
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Only admin can create another admin");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
User user = new User();
|
||||||
|
user.setUsername(username);
|
||||||
|
user.setPassword(passwordEncoder.encode(password));
|
||||||
|
user.setRole(role);
|
||||||
|
|
||||||
|
return userRepository.save(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public User createRegularUser(String username, String password) {
|
||||||
|
return createUser(username, password, UserRole.USER, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public UserResponse updateCurrentUser(UpdateProfileRequest request, User currentUser) {
|
||||||
|
String oldUsername = currentUser.getUsername();
|
||||||
|
String newUsername = request.getUsername();
|
||||||
|
|
||||||
|
if (!oldUsername.equals(newUsername)) {
|
||||||
|
if (userRepository.existsById(newUsername)) {
|
||||||
|
throw new ResponseStatusException(HttpStatus.CONFLICT, "Username is already taken");
|
||||||
|
}
|
||||||
|
|
||||||
|
userRepository.updateUsername(oldUsername, newUsername);
|
||||||
|
noteRepository.updateOwnerUsername(oldUsername, newUsername);
|
||||||
|
|
||||||
|
currentUser.setUsername(newUsername);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentUser.setGravatarEmail(request.getGravatarEmail());
|
||||||
|
User saved = userRepository.save(currentUser);
|
||||||
|
|
||||||
|
return UserResponse.builder()
|
||||||
|
.username(saved.getUsername())
|
||||||
|
.role(currentUser.getRole())
|
||||||
|
.gravatarUrl(GravatarUtil.generateGravatarUrl(saved.getGravatarEmail()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package net.tokishu.note.util;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import net.tokishu.note.model.User;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
public class CheckAuthUtil {
|
||||||
|
public void check(User user) {
|
||||||
|
if (user == null) {
|
||||||
|
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "User not authenticated");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package net.tokishu.note.util;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
public class GravatarUtil {
|
||||||
|
|
||||||
|
public static String generateGravatarUrl(String email) {
|
||||||
|
if (email == null || email.isBlank()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||||
|
byte[] digest = md.digest(email.trim().toLowerCase().getBytes(StandardCharsets.UTF_8));
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
for (byte b : digest) {
|
||||||
|
sb.append(String.format("%02x", b & 0xff));
|
||||||
|
}
|
||||||
|
|
||||||
|
return "https://www.gravatar.com/avatar/" + sb.toString();
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new RuntimeException("MD5 not supported", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user