瀏覽代碼

code cleanups

Daniel Bohry 1 周之前
父節點
當前提交
b3aad8dc8b

+ 8 - 0
.github/workflows/buildAndRelease.yml

@@ -53,3 +53,11 @@ jobs:
         run: |
           docker buildx create --use
           docker buildx build --platform linux/amd64,linux/arm64 -t dbohry/auth-service:latest --push .
+
+  deploy:
+    name: Deploy
+    needs: dockerize
+    runs-on: ubuntu-latest
+    steps:
+      - name: Trigger Portainer Webhook
+        run: curl --location --request POST '${{ secrets.DEPLOY_WEBHOOK }}'

+ 7 - 15
src/main/java/com/danielbohry/authservice/api/AuthController.java

@@ -4,11 +4,9 @@ import com.danielbohry.authservice.api.dto.AuthenticationRequest;
 import com.danielbohry.authservice.api.dto.AuthenticationResponse;
 import com.danielbohry.authservice.domain.ApplicationUser;
 import com.danielbohry.authservice.service.auth.AuthService;
+import com.danielbohry.authservice.util.SecurityUtils;
 import lombok.AllArgsConstructor;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.context.SecurityContext;
-import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.web.bind.annotation.*;
 
 import static org.springframework.http.HttpStatus.CREATED;
@@ -36,29 +34,23 @@ public class AuthController {
 
     @PostMapping("authorize")
     public ResponseEntity<Object> authorize(@RequestParam(defaultValue = "USER", required = false) String authority) {
-        SecurityContext context = SecurityContextHolder.getContext();
-        Object principal = context.getAuthentication().getPrincipal();
-        if (principal instanceof ApplicationUser user) {
-            if (user.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList().contains(authority)) {
-                return ResponseEntity.ok().build();
-            }
+        if (SecurityUtils.currentUserHasAuthority(authority)) {
+            return ResponseEntity.ok().build();
         }
-
         return ResponseEntity.status(FORBIDDEN).build();
     }
 
     @PostMapping("refresh")
     public ResponseEntity<Object> refresh() {
-        SecurityContext context = SecurityContextHolder.getContext();
-        Object principal = context.getAuthentication().getPrincipal();
-        if (principal instanceof ApplicationUser user) {
+        try {
+            ApplicationUser user = SecurityUtils.getCurrentUser();
             AuthenticationResponse response = service.refresh(user);
             return response != null
                     ? ResponseEntity.ok(response)
                     : ResponseEntity.status(FORBIDDEN).build();
+        } catch (SecurityException e) {
+            return ResponseEntity.status(FORBIDDEN).build();
         }
-
-        return ResponseEntity.status(FORBIDDEN).build();
     }
 
     @PostMapping("forgot-password")

+ 21 - 31
src/main/java/com/danielbohry/authservice/api/UserController.java

@@ -2,15 +2,12 @@ package com.danielbohry.authservice.api;
 
 import com.danielbohry.authservice.api.dto.*;
 import com.danielbohry.authservice.domain.ApplicationUser;
-import com.danielbohry.authservice.domain.Role;
 import com.danielbohry.authservice.service.auth.AuthService;
 import com.danielbohry.authservice.service.user.UserService;
-import com.mongodb.internal.bulk.UpdateRequest;
+import com.danielbohry.authservice.util.SecurityUtils;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.core.context.SecurityContext;
-import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
@@ -32,69 +29,62 @@ public class UserController {
 
     @GetMapping
     public ResponseEntity<List<UserResponse>> getAll() {
-        SecurityContext context = SecurityContextHolder.getContext();
-        Object principal = context.getAuthentication().getPrincipal();
-        if (principal instanceof ApplicationUser user && user.getRoles().contains(ADMIN)) {
+        try {
+            SecurityUtils.getCurrentAdminUser();
             return ResponseEntity.ok(userService.findAll().stream()
                     .map(UserResponse::from)
                     .toList());
+        } catch (SecurityException e) {
+            return ResponseEntity.status(FORBIDDEN).build();
         }
-
-        return ResponseEntity.status(FORBIDDEN).build();
     }
 
     @PutMapping("{userId}")
     public ResponseEntity<UserResponse> update(@PathVariable String userId, @RequestBody UserUpdateRequest request) {
-        SecurityContext context = SecurityContextHolder.getContext();
-        Object principal = context.getAuthentication().getPrincipal();
-        if (principal instanceof ApplicationUser user && user.getRoles().contains(ADMIN)) {
+        try {
+            ApplicationUser user = SecurityUtils.getCurrentAdminUser();
             user.setActive(request.isActive());
             user.setRoles(request.roles());
             user.setEmail(request.email());
             user.setUpdatedAt(now());
             return ResponseEntity.ok(UserResponse.from(userService.update(userId, user)));
+        } catch (SecurityException e) {
+            return ResponseEntity.status(FORBIDDEN).build();
         }
-
-        return ResponseEntity.status(FORBIDDEN).build();
     }
 
     @GetMapping("current")
     public ResponseEntity<UserResponse> get() {
-        SecurityContext context = SecurityContextHolder.getContext();
-        Object principal = context.getAuthentication().getPrincipal();
-        if (principal instanceof ApplicationUser user) {
+        try {
+            ApplicationUser user = SecurityUtils.getCurrentUser();
             return ResponseEntity.ok(UserResponse.from(user));
+        } catch (SecurityException e) {
+            return ResponseEntity.status(FORBIDDEN).build();
         }
-
-        return ResponseEntity.status(FORBIDDEN).build();
     }
 
     @PostMapping("reset-password")
     public ResponseEntity<AuthenticationResponse> resetPassword(@RequestBody PasswordResetRequest request) {
-        SecurityContext context = SecurityContextHolder.getContext();
-        Object principal = context.getAuthentication().getPrincipal();
-
-        if (principal instanceof ApplicationUser user) {
+        try {
+            ApplicationUser user = SecurityUtils.getCurrentUser();
             log.info("Resetting password for user [{}]", user.getUsername());
             var response = authService.resetPassword(user.getId(), request.getNewPassword());
             return ResponseEntity.ok(response);
+        } catch (SecurityException e) {
+            return ResponseEntity.status(UNAUTHORIZED).build();
         }
-
-        return ResponseEntity.status(UNAUTHORIZED).build();
     }
 
     @PostMapping("update-profile")
     public ResponseEntity<AuthenticationResponse> updateProfile(@RequestBody ProfileUpdateRequest request) {
-        SecurityContext context = SecurityContextHolder.getContext();
-        Object principal = context.getAuthentication().getPrincipal();
-
-        if (principal instanceof ApplicationUser user) {
+        try {
+            ApplicationUser user = SecurityUtils.getCurrentUser();
             log.info("Updating profile for user [{}]", user.getUsername());
             var response = authService.updateProfile(user.getId(), request.getCurrentPassword(), request.getNewPassword(), request.getEmail());
             return ResponseEntity.ok(response);
+        } catch (SecurityException e) {
+            return ResponseEntity.status(UNAUTHORIZED).build();
         }
-
-        return ResponseEntity.status(UNAUTHORIZED).build();
     }
 
 }

+ 23 - 20
src/main/java/com/danielbohry/authservice/service/user/UserService.java

@@ -47,7 +47,6 @@ public class UserService {
         validateUsername(applicationUser);
 
         applicationUser.setId(UUID.randomUUID().toString());
-        applicationUser.setPassword(applicationUser.getPassword());
         applicationUser.setRoles(List.of(USER));
         applicationUser.setActive(true);
 
@@ -67,38 +66,27 @@ public class UserService {
     }
 
     public ApplicationUser changePassword(String userId, String currentPassword, String newPassword, PasswordEncoder passwordEncoder) {
-        ApplicationUser user = repository.findById(userId)
-            .orElseThrow(() -> new NotFoundException("User not found"));
-
-        if (!passwordEncoder.matches(currentPassword, user.getPassword())) {
-            throw new BadRequestException("Current password is incorrect");
-        }
-
-        user.setPassword(passwordEncoder.encode(newPassword));
+        ApplicationUser user = findUserByIdInternal(userId);
+        validateCurrentPassword(user, currentPassword, passwordEncoder);
+        updateUserPassword(user, newPassword, passwordEncoder);
         return repository.save(user);
     }
 
     public ApplicationUser resetPassword(String userId, String newPassword, PasswordEncoder passwordEncoder) {
-        ApplicationUser user = repository.findById(userId)
-                .orElseThrow(() -> new NotFoundException("User not found"));
-
-        user.setPassword(passwordEncoder.encode(newPassword));
+        ApplicationUser user = findUserByIdInternal(userId);
+        updateUserPassword(user, newPassword, passwordEncoder);
         return repository.save(user);
     }
 
     public ApplicationUser updateProfile(String userId, String currentPassword, String newPassword, String email, PasswordEncoder passwordEncoder) {
-        ApplicationUser user = repository.findById(userId)
-                .orElseThrow(() -> new NotFoundException("User not found"));
-
-        if (!passwordEncoder.matches(currentPassword, user.getPassword())) {
-            throw new BadRequestException("Current password is incorrect");
-        }
+        ApplicationUser user = findUserByIdInternal(userId);
+        validateCurrentPassword(user, currentPassword, passwordEncoder);
 
         boolean hasChanges = false;
 
         // Update password if provided
         if (newPassword != null && !newPassword.trim().isEmpty()) {
-            user.setPassword(passwordEncoder.encode(newPassword));
+            updateUserPassword(user, newPassword, passwordEncoder);
             hasChanges = true;
         }
 
@@ -123,4 +111,19 @@ public class UserService {
         }
     }
 
+    private ApplicationUser findUserByIdInternal(String userId) {
+        return repository.findById(userId)
+                .orElseThrow(() -> new NotFoundException("User not found"));
+    }
+
+    private void validateCurrentPassword(ApplicationUser user, String currentPassword, PasswordEncoder passwordEncoder) {
+        if (!passwordEncoder.matches(currentPassword, user.getPassword())) {
+            throw new BadRequestException("Current password is incorrect");
+        }
+    }
+
+    private void updateUserPassword(ApplicationUser user, String newPassword, PasswordEncoder passwordEncoder) {
+        user.setPassword(passwordEncoder.encode(newPassword));
+    }
+
 }

+ 97 - 0
src/main/java/com/danielbohry/authservice/util/SecurityUtils.java

@@ -0,0 +1,97 @@
+package com.danielbohry.authservice.util;
+
+import com.danielbohry.authservice.domain.ApplicationUser;
+import com.danielbohry.authservice.domain.Role;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.GrantedAuthority;
+
+/**
+ * Utility class for security-related operations to reduce code duplication
+ * across controllers when extracting the current authenticated user.
+ */
+public final class SecurityUtils {
+
+    private SecurityUtils() {
+        // Utility class - prevent instantiation
+    }
+
+    /**
+     * Gets the current authenticated user from the security context.
+     *
+     * @return the current ApplicationUser
+     * @throws SecurityException if no user is authenticated or user is not ApplicationUser instance
+     */
+    public static ApplicationUser getCurrentUser() {
+        var context = SecurityContextHolder.getContext();
+        var authentication = context.getAuthentication();
+
+        if (authentication == null || authentication.getPrincipal() == null) {
+            throw new SecurityException("No authenticated user found");
+        }
+
+        Object principal = authentication.getPrincipal();
+        if (principal instanceof ApplicationUser user) {
+            return user;
+        }
+
+        throw new SecurityException("Current principal is not an ApplicationUser");
+    }
+
+    /**
+     * Gets the current authenticated user if they have the specified role.
+     *
+     * @param role the required role
+     * @return the current ApplicationUser if they have the role
+     * @throws SecurityException if no user is authenticated, user lacks the role, or not ApplicationUser instance
+     */
+    public static ApplicationUser getCurrentUserWithRole(Role role) {
+        ApplicationUser user = getCurrentUser();
+
+        if (!user.getRoles().contains(role)) {
+            throw new SecurityException("Current user does not have required role: " + role);
+        }
+
+        return user;
+    }
+
+    /**
+     * Gets the current authenticated user if they have admin privileges.
+     *
+     * @return the current ApplicationUser if they are an admin
+     * @throws SecurityException if no user is authenticated, user is not admin, or not ApplicationUser instance
+     */
+    public static ApplicationUser getCurrentAdminUser() {
+        return getCurrentUserWithRole(Role.ADMIN);
+    }
+
+    /**
+     * Checks if the current user has the specified authority string.
+     *
+     * @param authority the authority string to check
+     * @return true if user has the authority, false otherwise
+     */
+    public static boolean currentUserHasAuthority(String authority) {
+        try {
+            ApplicationUser user = getCurrentUser();
+            return user.getAuthorities()
+                    .stream()
+                    .map(GrantedAuthority::getAuthority)
+                    .anyMatch(authority::equals);
+        } catch (SecurityException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Tries to get the current user, returns null if not authenticated.
+     *
+     * @return the current ApplicationUser or null if not authenticated
+     */
+    public static ApplicationUser getCurrentUserOrNull() {
+        try {
+            return getCurrentUser();
+        } catch (SecurityException e) {
+            return null;
+        }
+    }
+}