diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
new file mode 100644
index 0000000..ae9cea7
--- /dev/null
+++ b/.github/workflows/gradle.yml
@@ -0,0 +1,49 @@
+name: Java CI with Gradle
+
+on: [push, pull_request]
+
+permissions:
+ contents: read
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Set up JDK 21
+ uses: actions/setup-java@v4
+ with:
+ java-version: '21'
+ distribution: 'temurin'
+ cache: gradle
+
+ - name: Grant execute permission for gradlew
+ run: chmod +x gradlew
+
+ - name: Build with Gradle
+ run: ./gradlew build -x test
+
+ - name: Run tests
+ run: ./gradlew test
+
+ - name: Publish Test Results
+ uses: actions/upload-artifact@v4
+ if: always()
+ with:
+ name: test-results
+ path: |
+ build/reports/tests/test/
+ build/test-results/test/
+
+ - name: Update status check
+ if: success()
+ run: echo "Tests passed" > status.txt || echo "Tests failed" > status.txt
+
+ - name: Upload status check
+ uses: actions/upload-artifact@v4
+ with:
+ name: status-check
+ path: status.txt
diff --git a/README.md b/README.md
index bd9162d..374b86e 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
# Upwork-Clone
+![Build Status](https://github.com/AhmedMohamedAbdelaty/Upwork/actions/workflows/gradle.yml/badge.svg)
+
A platform connecting freelancers and clients for job postings, proposals, and real-time chat.
## Table of Contents
@@ -11,6 +13,7 @@ A platform connecting freelancers and clients for job postings, proposals, and r
- [User Management](#user-management)
- [Password Management](#password-management)
- [Token Management](#token-management)
+ - [Role Management](#role-management)
- [Test Endpoints](#test-endpoints)
- [Swagger UI](#swagger-ui)
- [To-Do](#to-do)
@@ -155,6 +158,7 @@ Flyway is used to manage database migrations. The SQL scripts are located in `sr
- `id`: The ID of the user to reactivate
- **Response:** A `ResponseDto` object containing the result of the operation.
+
Get user profile
@@ -162,15 +166,15 @@ Flyway is used to manage database migrations. The SQL scripts are located in `sr
- **Method:** `GET`
- **Description:** Retrieve the profile information for a specific user.
- **Path Parameters:**
- - `id` (required): The ID of the user whose profile is to be retrieved.
- - **Response:**
- - **Status Code:** `200 OK`
- - **Body:**
- ```json
- {
- "status": "OK",
- "success": true,
- "data": {
+ - `userId` (required): The ID of the user whose profile is to be retrieved.
+- **Response:**
+ - **Status Code:** `200 OK`
+ - **Body:**
+ ```json
+ {
+ "status": "OK",
+ "success": true,
+ "data": {
"id": 160,
"firstName": "Teddy",
"lastName": "Johnson",
@@ -178,11 +182,12 @@ Flyway is used to manage database migrations. The SQL scripts are located in `sr
"description": null,
"hourlyRate": null,
"location": null
- },
- "error": null
- }
- ```
+ },
+ "error": null
+ }
+ ```
+
Update user profile
@@ -191,28 +196,28 @@ Flyway is used to manage database migrations. The SQL scripts are located in `sr
- **Description:** Update the profile information for a specific user.
- **Path Parameters:**
- `id` (required): The ID of the user whose currently logged in.
- - **Request Body:**
- - **Content-Type:** `application/json`
- - **Body Example:**
- ```json
- {
- "id": 160,
- "firstName": "string",
- "lastName": "string",
- "title": "string",
- "description": "string",
- "hourlyRate": 0,
- "location": "string"
- }
- ```
+- **Request Body:**
+ - **Content-Type:** `application/json`
+ - **Body Example:**
+ ```json
+ {
+ "id": 160,
+ "firstName": "string",
+ "lastName": "string",
+ "title": "string",
+ "description": "string",
+ "hourlyRate": 0,
+ "location": "string"
+ }
+ ```
- **Response:**
- **Status Code:** `200 OK`
- **Body Example:**
```json
- {
- "status": "OK",
- "success": true,
- "data": {
+ {
+ "status": "OK",
+ "success": true,
+ "data": {
"id": 160,
"firstName": "string",
"lastName": "string",
@@ -220,10 +225,9 @@ Flyway is used to manage database migrations. The SQL scripts are located in `sr
"description": "string",
"hourlyRate": 0,
"location": "string"
- },
- "error": null
- }
-
+ },
+ "error": null
+ }
```
@@ -301,6 +305,128 @@ Flyway is used to manage database migrations. The SQL scripts are located in `sr
- **Response:** An object indicating the result of the operation.
+### Role Management
+
+
+ Add a new role
+
+- **URL:** `/api/roles/add`
+- **Method:** `POST`
+- **Description:** Add a new role, accessible only by admins.
+- **Request Body:**
+ ```json
+ {
+ "name": "string"
+ }
+ ```
+- **Response:**
+ ```json
+ {
+ "status": "CREATED",
+ "success": true,
+ "data": {
+ "id": 1,
+ "name": "string"
+ },
+ "error": null
+ }
+ ```
+
+
+
+ Remove a role
+
+- **URL:** `/api/roles/remove/{roleId}`
+- **Method:** `DELETE`
+- **Description:** Remove a role, accessible only by admins.
+- **Path Parameters:**
+ - `roleId` (required): The ID of the role to remove.
+- **Response:**
+ ```json
+ {
+ "status": "OK",
+ "success": true,
+ "data": "Role removed successfully.",
+ "error": null
+ }
+ ```
+
+
+
+ Update a role
+
+- **URL:** `/api/roles/update/{roleId}`
+- **Method:** `PUT`
+- **Description:** Update a role, accessible only by admins.
+- **Path Parameters:**
+ - `roleId` (required): The ID of the role to update.
+- **Request Body:**
+ ```json
+ {
+ "name": "string"
+ }
+ ```
+- **Response:**
+ ```json
+ {
+ "status": "OK",
+ "success": true,
+ "data": {
+ "id": 1,
+ "name": "string"
+ },
+ "error": null
+ }
+ ```
+
+
+
+ Get all roles
+
+- **URL:** `/api/roles/all`
+- **Method:** `GET`
+- **Description:** Retrieve a list of all roles, accessible only by admins.
+- **Response:**
+ ```json
+ {
+ "status": "OK",
+ "success": true,
+ "data": [
+ {
+ "id": 1,
+ "name": "string"
+ }
+ ],
+ "error": null
+ }
+ ```
+
+
+
+ Assign roles to users
+
+- **URL:** `/api/roles/{id}/assign-roles`
+- **Method:** `POST`
+- **Description:** Assign roles to users, accessible only by admins.
+- **Path Parameters:**
+ - `id` (required): The ID of the user to assign roles to.
+- **Request Body:**
+ ```json
+ {
+ "roles": ["string"]
+ }
+ ```
+- **Response:**
+ ```json
+ {
+ "status": "OK",
+ "success": true,
+ "data": "Roles assigned successfully.",
+ "error": null
+ }
+ ```
+
+
### Test Endpoints
These endpoints are likely for testing purposes and may be removed in production:
@@ -325,4 +451,4 @@ You can access the Swagger UI documentation for this API at: http://localhost:80
- [ ] Add unit and integration tests for all endpoints.
- [ ] Implement logging and monitoring solutions.
- [ ] Create a Dockerfile and build a Docker image for the application.
-- [ ] Set up Docker Compose and document Docker setup for the frontend team.
\ No newline at end of file
+- [ ] Set up Docker Compose and document Docker setup for the frontend team.
diff --git a/build.gradle b/build.gradle
index d79cf4c..2bfdcbb 100644
--- a/build.gradle
+++ b/build.gradle
@@ -51,6 +51,7 @@ dependencies {
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
+ testImplementation 'com.h2database:h2'
}
tasks.named('test') {
diff --git a/src/main/java/com/activecourses/upwork/config/security/SecurityConfig.java b/src/main/java/com/activecourses/upwork/config/security/SecurityConfig.java
index 7f0d091..312cad0 100644
--- a/src/main/java/com/activecourses/upwork/config/security/SecurityConfig.java
+++ b/src/main/java/com/activecourses/upwork/config/security/SecurityConfig.java
@@ -6,7 +6,6 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
-import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
@@ -55,8 +54,6 @@ public class SecurityConfig {
"/api/client/**"
};
- private final CustomUserDetailsService customUserDetailsService;
-
private final AuthEntryPointJwt unauthorizedHandler;
@Bean
@@ -64,14 +61,6 @@ public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
- @Bean
- public DaoAuthenticationProvider authenticationProvider() {
- DaoAuthenticationProvider auth = new DaoAuthenticationProvider();
- auth.setUserDetailsService(customUserDetailsService);
- auth.setPasswordEncoder(passwordEncoder());
- return auth;
- }
-
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
@@ -96,12 +85,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
);
http.httpBasic(Customizer.withDefaults());
- http.authenticationProvider(authenticationProvider());
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
-//
-//
-//
}
}
diff --git a/src/main/java/com/activecourses/upwork/controller/TestController.java b/src/main/java/com/activecourses/upwork/controller/TestController.java
index e366c7f..8009e80 100644
--- a/src/main/java/com/activecourses/upwork/controller/TestController.java
+++ b/src/main/java/com/activecourses/upwork/controller/TestController.java
@@ -1,6 +1,10 @@
package com.activecourses.upwork.controller;
-
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.security.SecurityRequirement;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
@@ -10,33 +14,67 @@
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping("/api/test")
+@Tag(name = "Test", description = "Test API")
public class TestController {
+
+ private static final Logger logger = LoggerFactory.getLogger(TestController.class);
+
+ @Operation(
+ summary = "Public access",
+ description = "Accessible by anyone",
+ security = @SecurityRequirement(name = "")
+ )
@GetMapping("/all")
public String allAccess() {
+ logger.info("Accessing public content");
return "Public Content.";
}
+ @Operation(
+ summary = "Access for all users",
+ description = "Accessible by CLIENT, FREELANCER, and ADMIN roles",
+ security = @SecurityRequirement(name = "bearerAuth")
+ )
@GetMapping("/user")
@PreAuthorize("hasRole('CLIENT') or hasRole('FREELANCER') or hasRole('ADMIN')")
public String allUsersAccess() {
+ logger.info("Accessing content for all users");
return "All Users Content.";
}
+ @Operation(
+ summary = "Client access",
+ description = "Accessible by CLIENT role",
+ security = @SecurityRequirement(name = "bearerAuth")
+ )
@GetMapping("/client")
@PreAuthorize("hasRole('CLIENT')")
public String clientAccess() {
+ logger.info("Accessing client content");
return "Client Board.";
}
+ @Operation(
+ summary = "Admin access",
+ description = "Accessible by ADMIN role",
+ security = @SecurityRequirement(name = "bearerAuth")
+ )
@GetMapping("/admin")
@PreAuthorize("hasRole('ADMIN')")
public String adminAccess() {
+ logger.info("Accessing admin content");
return "Admin Board.";
}
+ @Operation(
+ summary = "Freelancer access",
+ description = "Accessible by FREELANCER role",
+ security = @SecurityRequirement(name = "bearerAuth")
+ )
@GetMapping("/freelancer")
@PreAuthorize("hasRole('FREELANCER')")
public String freelancerAccess() {
+ logger.info("Accessing freelancer content");
return "Freelancer Board.";
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/activecourses/upwork/controller/auth/AuthController.java b/src/main/java/com/activecourses/upwork/controller/auth/AuthController.java
index 9de71dc..471d06c 100644
--- a/src/main/java/com/activecourses/upwork/controller/auth/AuthController.java
+++ b/src/main/java/com/activecourses/upwork/controller/auth/AuthController.java
@@ -10,6 +10,8 @@
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseCookie;
@@ -25,7 +27,7 @@
@RequestMapping("/api/auth/")
public class AuthController {
private final AuthService authService;
- private final JwtService jwtService;
+ private static final Logger logger = LoggerFactory.getLogger(AuthController.class);
@Operation(
summary = "Register a new user",
@@ -34,6 +36,7 @@ public class AuthController {
)
@PostMapping("/register")
public ResponseEntity registerUser(@Valid @RequestBody RegistrationRequestDto registrationRequestDto) {
+ logger.info("Registering user with email: {}", registrationRequestDto.getEmail());
return ResponseEntity
.status(HttpStatus.OK)
.body(ResponseDto
@@ -50,8 +53,9 @@ public ResponseEntity registerUser(@Valid @RequestBody Registration
description = "Login",
security = @SecurityRequirement(name = "")
)
- @PostMapping("login")
+ @PostMapping("login")
public ResponseEntity> login(@Valid @RequestBody LoginRequestDto loginRequestDto) {
+ logger.info("User login attempt with email: {}", loginRequestDto.getEmail());
ResponseDto responseDto = authService.login(loginRequestDto);
Map cookies = (Map) responseDto.getData();
@@ -62,13 +66,14 @@ public ResponseEntity> login(@Valid @RequestBody LoginRequestDto loginRequestD
}
@Operation(
- summary = "Deactivate user",
- description = "Deactivate user",
- security = @SecurityRequirement(name = "")
+ summary = "Deactivate user",
+ description = "Deactivate user",
+ security = @SecurityRequirement(name = "bearerAuth")
)
@PreAuthorize("hasRole('ADMIN')")
@PostMapping("/{id}/deactivate")
public ResponseDto deactivateUser(@PathVariable int id) {
+ logger.info("Deactivating user with id: {}", id);
boolean success = authService.deactivateUser(id);
if (success) {
return ResponseDto.builder()
@@ -88,11 +93,12 @@ public ResponseDto deactivateUser(@PathVariable int id) {
@Operation(
summary = "Reactivate user",
description = "Reactivate user",
- security = @SecurityRequirement(name = "")
+ security = @SecurityRequirement(name = "bearerAuth")
)
@PreAuthorize("hasRole('ADMIN')")
@PostMapping("/{id}/reactivate")
public ResponseDto reactivateUser(@PathVariable int id) {
+ logger.info("Reactivating user with id: {}", id);
boolean success = authService.reactivateUser(id);
if (success) {
return ResponseDto.builder()
@@ -116,6 +122,7 @@ public ResponseDto reactivateUser(@PathVariable int id) {
)
@PostMapping("logout")
public ResponseEntity> logout() {
+ logger.info("User logout attempt");
return authService.logout();
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/activecourses/upwork/controller/role/RoleController.java b/src/main/java/com/activecourses/upwork/controller/role/RoleController.java
new file mode 100644
index 0000000..ba6f6d5
--- /dev/null
+++ b/src/main/java/com/activecourses/upwork/controller/role/RoleController.java
@@ -0,0 +1,111 @@
+package com.activecourses.upwork.controller.role;
+
+import com.activecourses.upwork.dto.ResponseDto;
+import com.activecourses.upwork.model.Role;
+import com.activecourses.upwork.service.role.RoleService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.security.SecurityRequirement;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Map;
+
+@Tag(name = "Role", description = "Role Management API")
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/api/roles/")
+public class RoleController {
+ private final RoleService roleService;
+ private static final Logger logger = LoggerFactory.getLogger(RoleController.class);
+
+ @Operation(
+ summary = "Add a new role",
+ description = "Add a new role, accessible only by admins",
+ security = @SecurityRequirement(name = "bearerAuth")
+ )
+ @PreAuthorize("hasRole('ADMIN')")
+ @PostMapping("/add")
+ public ResponseEntity addRole(@RequestBody Role role) {
+ logger.info("Adding new role: {}", role.getName());
+ Role createdRole = roleService.addRole(role);
+ return buildResponseEntity(HttpStatus.CREATED, true, createdRole, null);
+ }
+
+ @Operation(
+ summary = "Remove a role",
+ description = "Remove a role, accessible only by admins",
+ security = @SecurityRequirement(name = "bearerAuth")
+ )
+ @PreAuthorize("hasRole('ADMIN')")
+ @DeleteMapping("/remove/{roleId}")
+ public ResponseEntity removeRole(@PathVariable int roleId) {
+ logger.info("Removing role with id: {}", roleId);
+ boolean success = roleService.removeRole(roleId);
+ return success
+ ? buildResponseEntity(HttpStatus.OK, true, "Role removed successfully.", null)
+ : buildResponseEntity(HttpStatus.NOT_FOUND, false, null, "Role not found.");
+ }
+
+ @Operation(
+ summary = "Update a role",
+ description = "Update a role, accessible only by admins",
+ security = @SecurityRequirement(name = "bearerAuth")
+ )
+ @PreAuthorize("hasRole('ADMIN')")
+ @PutMapping("/update/{roleId}")
+ public ResponseEntity updateRole(@PathVariable int roleId, @RequestBody Role role) {
+ logger.info("Updating role with id: {}", roleId);
+ Role updatedRole = roleService.updateRole(roleId, role);
+ return updatedRole != null
+ ? buildResponseEntity(HttpStatus.OK, true, updatedRole, null)
+ : buildResponseEntity(HttpStatus.NOT_FOUND, false, null, "Role not found.");
+ }
+
+ @Operation(
+ summary = "Get all roles",
+ description = "Retrieve a list of all roles, accessible only by admins",
+ security = @SecurityRequirement(name = "bearerAuth")
+ )
+ @PreAuthorize("hasRole('ADMIN')")
+ @GetMapping("/all")
+ public ResponseEntity getAllRoles() {
+ logger.info("Retrieving all roles");
+ List roles = roleService.getAllRoles();
+ return buildResponseEntity(HttpStatus.OK, true, roles, null);
+ }
+
+ @Operation(
+ summary = "Assign roles to users",
+ description = "Assign roles to users, accessible only by admins",
+ security = @SecurityRequirement(name = "bearerAuth")
+ )
+ @PreAuthorize("hasRole('ADMIN')")
+ @PostMapping("/{id}/assign-roles")
+ public ResponseEntity assignRolesToUser(@PathVariable int id, @RequestBody Map roles) {
+ logger.info("Assigning roles to user with id: {}", id);
+ boolean success = roleService.assignRolesToUser(id, roles);
+ return success
+ ? buildResponseEntity(HttpStatus.OK, true, "Roles assigned successfully.", null)
+ : buildResponseEntity(HttpStatus.NOT_FOUND, false, null, "User not found.");
+ }
+
+ private ResponseEntity buildResponseEntity(HttpStatus status, boolean success, Object data, Object error) {
+ return ResponseEntity
+ .status(status)
+ .body(ResponseDto
+ .builder()
+ .status(status)
+ .success(success)
+ .data(data)
+ .error(error)
+ .build()
+ );
+ }
+}
diff --git a/src/main/java/com/activecourses/upwork/controller/user/UserController.java b/src/main/java/com/activecourses/upwork/controller/user/UserController.java
index f96e565..f60767a 100644
--- a/src/main/java/com/activecourses/upwork/controller/user/UserController.java
+++ b/src/main/java/com/activecourses/upwork/controller/user/UserController.java
@@ -3,7 +3,7 @@
import com.activecourses.upwork.dto.ResponseDto;
import com.activecourses.upwork.dto.user.UserProfileDto;
import com.activecourses.upwork.dto.user.UserResponseDto;
-import com.activecourses.upwork.service.user.UserProfileService;
+import com.activecourses.upwork.service.user.profile.UserProfileService;
import com.activecourses.upwork.service.user.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -30,10 +30,10 @@ public class UserController {
@Operation(summary = "Get all users",
description = "Retrieve a paginated list of all users. Only accessible by users with the ROLE_ADMIN role.",
parameters = {
- @Parameter(name = "pageNo", in = ParameterIn.QUERY, description = "Page number", required = false, schema = @Schema(type = "integer", defaultValue = "0")),
- @Parameter(name = "pageSize", in = ParameterIn.QUERY, description = "Page size", required = false, schema = @Schema(type = "integer", defaultValue = "10")),
- @Parameter(name = "sortBy", in = ParameterIn.QUERY, description = "Sort by", required = false, schema = @Schema(type = "string", defaultValue = "id")),
- @Parameter(name = "sortDir", in = ParameterIn.QUERY, description = "Sort direction", required = false, schema = @Schema(type = "string", defaultValue = "asc"))
+ @Parameter(name = "pageNo", in = ParameterIn.QUERY, description = "Page number", schema = @Schema(type = "integer", defaultValue = "0")),
+ @Parameter(name = "pageSize", in = ParameterIn.QUERY, description = "Page size", schema = @Schema(type = "integer", defaultValue = "10")),
+ @Parameter(name = "sortBy", in = ParameterIn.QUERY, description = "Sort by", schema = @Schema(type = "string", defaultValue = "id")),
+ @Parameter(name = "sortDir", in = ParameterIn.QUERY, description = "Sort direction", schema = @Schema(type = "string", defaultValue = "asc"))
}
)
@PreAuthorize("hasRole('ROLE_ADMIN')")
@@ -77,7 +77,7 @@ public ResponseEntity> updateUserProfile(
@PathVariable int userId,
@RequestBody @Valid UserProfileDto updateRequest) {
try {
- return userProfileService.UpdateUserProfile(userId,updateRequest);
+ return userProfileService.UpdateUserProfile(userId, updateRequest);
} catch (Exception e) {
return ResponseEntity.badRequest().body(ResponseDto.builder()
.status(HttpStatus.BAD_REQUEST)
diff --git a/src/main/java/com/activecourses/upwork/dto/authentication/login/LoginRequestDto.java b/src/main/java/com/activecourses/upwork/dto/authentication/login/LoginRequestDto.java
index 1061ea7..98d33c7 100644
--- a/src/main/java/com/activecourses/upwork/dto/authentication/login/LoginRequestDto.java
+++ b/src/main/java/com/activecourses/upwork/dto/authentication/login/LoginRequestDto.java
@@ -19,4 +19,10 @@ public class LoginRequestDto {
@NotNull(message = "Password is required")
@Size(min = 6, message = "Password must be at least 6 characters long")
private String password;
+
+ public LoginRequestDto(@Email(message = "Email is not valid") @NotEmpty(message = "Email is required") @NotNull(message = "Email is required") String email,
+ @NotEmpty(message = "Password is required") @NotNull(message = "Password is required") @Size(min = 6, message = "Password must be at least 6 characters long") String password) {
+ this.email = email;
+ this.password = password;
+ }
}
diff --git a/src/main/java/com/activecourses/upwork/service/RefreshTokenService.java b/src/main/java/com/activecourses/upwork/service/RefreshTokenService.java
index 7485161..9fe742d 100644
--- a/src/main/java/com/activecourses/upwork/service/RefreshTokenService.java
+++ b/src/main/java/com/activecourses/upwork/service/RefreshTokenService.java
@@ -75,9 +75,11 @@ public ResponseEntity refreshToken(HttpServletRequest request) {
return buildBadRequestResponse("Refresh Token is empty!");
}
}
+
private String getRefreshTokenFromRequest(HttpServletRequest request) {
return jwtService.getJwtRefreshFromCookies(request);
}
+
private String getTokenFromRequest(HttpServletRequest request) {
return jwtService.getJwtFromCookies(request);
}
@@ -117,7 +119,7 @@ public RefreshToken createRefreshToken(int userId) {
public void verifyExpiration(RefreshToken token) {
if (token.getExpiryDate().compareTo(Instant.now()) < 0) {
refreshTokenRepository.delete(token);
- throw new TokenRefreshException(token.getToken(), "Refresh token was expired. Please make a new signin request");
+ throw new TokenRefreshException(token.getToken(), "Refresh token was expired. Please sign in again to obtain a new refresh token.");
}
}
diff --git a/src/main/java/com/activecourses/upwork/service/authentication/AuthServiceImpl.java b/src/main/java/com/activecourses/upwork/service/authentication/AuthServiceImpl.java
index 218e245..45207a0 100644
--- a/src/main/java/com/activecourses/upwork/service/authentication/AuthServiceImpl.java
+++ b/src/main/java/com/activecourses/upwork/service/authentication/AuthServiceImpl.java
@@ -10,6 +10,7 @@
import com.activecourses.upwork.model.User;
import com.activecourses.upwork.model.UserProfile;
import com.activecourses.upwork.repository.user.UserRepository;
+import com.activecourses.upwork.repository.role.RoleRepository;
import com.activecourses.upwork.config.security.jwt.JwtService;
import com.activecourses.upwork.service.RefreshTokenService;
@@ -18,6 +19,8 @@
import java.util.Map;
import java.util.Optional;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseCookie;
@@ -38,6 +41,7 @@
public class AuthServiceImpl implements AuthService {
private final UserRepository userRepository;
+ private final RoleRepository roleRepository;
private final PasswordEncoder passwordEncoder;
private final AuthenticationManager authenticationManager;
private final JwtService jwtService;
@@ -45,10 +49,11 @@ public class AuthServiceImpl implements AuthService {
private final Mapper userMapper;
private final CustomUserDetailsService customUserDetailsService;
private final RefreshTokenService refreshTokenService;
-
+ private static final Logger logger = LoggerFactory.getLogger(AuthServiceImpl.class);
@Override
public RegistrationResponseDto registerUser(RegistrationRequestDto registrationRequestDto) {
+ logger.info("Registering user with email: {}", registrationRequestDto.getEmail());
User user = userMapper.mapFrom(registrationRequestDto);
user.setPassword(passwordEncoder.encode(user.getPassword()));
user.setUserProfile(new UserProfile());
@@ -63,10 +68,11 @@ public RegistrationResponseDto registerUser(RegistrationRequestDto registrationR
@Transactional
@Override
public ResponseDto login(LoginRequestDto loginRequestDto) {
-
+ logger.info("User login attempt with email: {}", loginRequestDto.getEmail());
User user = findByEmail(loginRequestDto.getEmail());
if (!user.isAccountEnabled()) {
+ logger.warn("Account is disabled for user: {}", loginRequestDto.getEmail());
return ResponseDto
.builder()
.status(HttpStatus.FORBIDDEN)
@@ -74,8 +80,9 @@ public ResponseDto login(LoginRequestDto loginRequestDto) {
.error("Account is disabled.")
.build();
}
-
+
if (user.isAccountLocked()) {
+ logger.warn("Account is locked for user: {}", loginRequestDto.getEmail());
return ResponseDto
.builder()
.status(HttpStatus.LOCKED)
@@ -109,6 +116,7 @@ public ResponseDto login(LoginRequestDto loginRequestDto) {
@Override
public ResponseEntity logout() {
+ logger.info("User logout attempt");
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (!principal.toString().equals("anonymousUser")) {
int userId = ((User) principal).getId();
@@ -132,31 +140,32 @@ public ResponseEntity logout() {
@Override
public Optional refreshToken(String refreshToken) {
+ logger.info("Refreshing token");
String username = jwtService.getUserNameFromJwtToken(refreshToken);
return userRepository.findByEmail(username);
}
@Override
public boolean verifyUser(String token) {
+ logger.info("Verifying user with token: {}", token);
Optional user = userRepository.findByVerificationToken(token);
User unwrappedUser = unwrapUser(user);
- if (unwrappedUser != null) {
- unwrappedUser.setAccountEnabled(true);
- unwrappedUser.setVerificationToken(null); // Clear the token
- userRepository.save(unwrappedUser);
- return true;
- }
- return false;
+ unwrappedUser.setAccountEnabled(true);
+ unwrappedUser.setVerificationToken(null); // Clear the token
+ userRepository.save(unwrappedUser);
+ return true;
}
@Override
public User findByEmail(String email) {
+ logger.info("Finding user by email: {}", email);
Optional user = userRepository.findByEmail(email);
return unwrapUser(user);
}
@Override
public void sendVerificationEmail(User user) {
+ logger.info("Sending verification email to: {}", user.getEmail());
String verificationLink = "http://localhost:8080/api/users/verify?token=" + user.getVerificationToken();
SimpleMailMessage message = new SimpleMailMessage();
@@ -165,33 +174,29 @@ public void sendVerificationEmail(User user) {
message.setText("Click the link to verify your email: " + verificationLink);
mailSender.send(message);
}
-
+
@Override
public boolean deactivateUser(int userId) {
+ logger.info("Deactivating user with id: {}", userId);
Optional user = userRepository.findById(userId);
User unwrappedUser = unwrapUser(user);
- if (unwrappedUser != null) {
- unwrappedUser.setAccountEnabled(false);
- userRepository.save(unwrappedUser);
- return true;
- }
- return false;
+ unwrappedUser.setAccountEnabled(false);
+ userRepository.save(unwrappedUser);
+ return true;
}
@Override
public boolean reactivateUser(int userId) {
+ logger.info("Reactivating user with id: {}", userId);
Optional user = userRepository.findById(userId);
User unwrappedUser = unwrapUser(user);
- if (unwrappedUser != null) {
- unwrappedUser.setAccountEnabled(true);
- userRepository.save(unwrappedUser);
- return true;
- }
- return false;
+ unwrappedUser.setAccountEnabled(true);
+ userRepository.save(unwrappedUser);
+ return true;
}
static User unwrapUser(Optional entity) {
if (entity.isPresent()) return entity.get();
else throw new UnsupportedOperationException("Unimplemented method 'unwrapUser'");
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/activecourses/upwork/service/role/RoleService.java b/src/main/java/com/activecourses/upwork/service/role/RoleService.java
new file mode 100644
index 0000000..eb537ce
--- /dev/null
+++ b/src/main/java/com/activecourses/upwork/service/role/RoleService.java
@@ -0,0 +1,18 @@
+package com.activecourses.upwork.service.role;
+
+import com.activecourses.upwork.model.Role;
+
+import java.util.List;
+import java.util.Map;
+
+public interface RoleService {
+ Role addRole(Role role);
+
+ boolean removeRole(int roleId);
+
+ Role updateRole(int roleId, Role role);
+
+ List getAllRoles();
+
+ boolean assignRolesToUser(int userId, Map roles);
+}
diff --git a/src/main/java/com/activecourses/upwork/service/role/RoleServiceImpl.java b/src/main/java/com/activecourses/upwork/service/role/RoleServiceImpl.java
new file mode 100644
index 0000000..5951727
--- /dev/null
+++ b/src/main/java/com/activecourses/upwork/service/role/RoleServiceImpl.java
@@ -0,0 +1,69 @@
+package com.activecourses.upwork.service.role;
+
+import com.activecourses.upwork.model.Role;
+import com.activecourses.upwork.model.User;
+import com.activecourses.upwork.repository.role.RoleRepository;
+import com.activecourses.upwork.repository.user.UserRepository;
+import lombok.RequiredArgsConstructor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+@Service
+@RequiredArgsConstructor
+public class RoleServiceImpl implements RoleService {
+
+ private final RoleRepository roleRepository;
+ private final UserRepository userRepository;
+ private static final Logger logger = LoggerFactory.getLogger(RoleServiceImpl.class);
+
+ @Override
+ public Role addRole(Role role) {
+ return roleRepository.save(role);
+ }
+
+ @Override
+ public boolean removeRole(int roleId) {
+ Optional role = roleRepository.findById(roleId);
+ if (role.isPresent()) {
+ roleRepository.delete(role.get());
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public Role updateRole(int roleId, Role role) {
+ Optional existingRole = roleRepository.findById(roleId);
+ if (existingRole.isPresent()) {
+ Role updatedRole = existingRole.get();
+ updatedRole.setName(role.getName());
+ return roleRepository.save(updatedRole);
+ }
+ return null;
+ }
+
+ @Override
+ public List getAllRoles() {
+ return roleRepository.findAll();
+ }
+
+ @Override
+ public boolean assignRolesToUser(int userId, Map roles) {
+ logger.info("Assigning roles to user with id: {}", userId);
+ Optional user = userRepository.findById(userId);
+ User unwrappedUser = user.orElseThrow(() -> new RuntimeException("User not found: " + userId));
+ List roleList = roles.keySet().stream()
+ .map(roleName -> roleRepository.findByName(roleName)
+ .orElseThrow(() -> new RuntimeException("Role not found: " + roleName)))
+ .collect(Collectors.toList());
+ unwrappedUser.setRoles(roleList);
+ userRepository.save(unwrappedUser);
+ return true;
+ }
+}
diff --git a/src/main/java/com/activecourses/upwork/service/user/userServiceImpl.java b/src/main/java/com/activecourses/upwork/service/user/UserServiceImpl.java
similarity index 96%
rename from src/main/java/com/activecourses/upwork/service/user/userServiceImpl.java
rename to src/main/java/com/activecourses/upwork/service/user/UserServiceImpl.java
index 72afd00..6e54e68 100644
--- a/src/main/java/com/activecourses/upwork/service/user/userServiceImpl.java
+++ b/src/main/java/com/activecourses/upwork/service/user/UserServiceImpl.java
@@ -23,7 +23,6 @@ public class UserServiceImpl implements UserService {
private final UserDtoMapper userDtoMapper;
-
@Override
public UserResponseDto getAllUsers(int pageNo, int pageSize, String sortBy, String sortDir) {
// Set the sorting direction
@@ -54,11 +53,10 @@ public UserResponseDto getAllUsers(int pageNo, int pageSize, String sortBy, Stri
userResponseDto.setLast(pagedResult.isLast());
return userResponseDto;
}
+
@Override
public User findByEmail(String email) {
return userRepository.findByEmail(email).
- orElseThrow(()->new UsernameNotFoundException("User Not Found"));
+ orElseThrow(() -> new UsernameNotFoundException("User Not Found"));
}
-
-
}
diff --git a/src/main/java/com/activecourses/upwork/service/user/ImplUserProfileService.java b/src/main/java/com/activecourses/upwork/service/user/profile/ImplUserProfileService.java
similarity index 68%
rename from src/main/java/com/activecourses/upwork/service/user/ImplUserProfileService.java
rename to src/main/java/com/activecourses/upwork/service/user/profile/ImplUserProfileService.java
index 6c34eb4..fb50f5a 100644
--- a/src/main/java/com/activecourses/upwork/service/user/ImplUserProfileService.java
+++ b/src/main/java/com/activecourses/upwork/service/user/profile/ImplUserProfileService.java
@@ -1,4 +1,4 @@
-package com.activecourses.upwork.service.user;
+package com.activecourses.upwork.service.user.profile;
import com.activecourses.upwork.dto.ResponseDto;
import com.activecourses.upwork.dto.user.UserProfileDto;
@@ -14,9 +14,10 @@
@Service
@RequiredArgsConstructor
-public class ImplUserProfileService implements UserProfileService{
+public class ImplUserProfileService implements UserProfileService {
private final UserRepository userRepository;
private final Mapper userProfileMapper;
+
@Override
public ResponseEntity> getUserProfile(int id) {
User user = userRepository.findById(id).
@@ -31,7 +32,7 @@ public ResponseEntity> getUserProfile(int id) {
.build());
}
- public UserProfile createUserProfile(User user){
+ public UserProfile createUserProfile(User user) {
return UserProfile.builder()
.user(user).build();
}
@@ -40,34 +41,34 @@ public UserProfile createUserProfile(User user){
public ResponseEntity> UpdateUserProfile(int userId, UserProfileDto updateRequest) {
User user = userRepository.findById(updateRequest.getId()).
orElseThrow(() -> new UsernameNotFoundException("user Not Found"));
- // check if this user try to update another user profile
- if(userId!=user.getId()){
+ // Check if this user try to update another user profile
+ if (userId != user.getId()) {
throw new RuntimeException("this User can't update another user profile");
}
return ResponseEntity.ok()
.body(ResponseDto.builder()
.status(HttpStatus.OK)
.success(true)
- .data(doUpdate(updateRequest,user))
+ .data(doUpdate(updateRequest, user))
.build());
}
private UserProfileDto doUpdate(UserProfileDto updateRequest, User user) {
- //update fields that in user table
- user.setFirstName(updateRequest.getFirstName());
- user.setLastName(updateRequest.getLastName());
+ // Update fields that in user table
+ user.setFirstName(updateRequest.getFirstName());
+ user.setLastName(updateRequest.getLastName());
- //get userProfile and update it.
- UserProfile userProfile=user.getUserProfile();
- userProfile.setDescription(updateRequest.getDescription());
- userProfile.setLocation(updateRequest.getLocation());
- userProfile.setTitle(userProfile.getTitle());
- userProfile.setHourlyRate(updateRequest.getHourlyRate());
+ // Get userProfile and update it.
+ UserProfile userProfile = user.getUserProfile();
+ userProfile.setDescription(updateRequest.getDescription());
+ userProfile.setLocation(updateRequest.getLocation());
+ userProfile.setTitle(userProfile.getTitle());
+ userProfile.setHourlyRate(updateRequest.getHourlyRate());
- // update user in database
- user.setUserProfile(userProfile);
- userRepository.save(user);
- return updateRequest;
+ // Update user in database
+ user.setUserProfile(userProfile);
+ userRepository.save(user);
+ return updateRequest;
}
}
diff --git a/src/main/java/com/activecourses/upwork/service/user/UserProfileService.java b/src/main/java/com/activecourses/upwork/service/user/profile/UserProfileService.java
similarity index 88%
rename from src/main/java/com/activecourses/upwork/service/user/UserProfileService.java
rename to src/main/java/com/activecourses/upwork/service/user/profile/UserProfileService.java
index 0d50830..c8f1340 100644
--- a/src/main/java/com/activecourses/upwork/service/user/UserProfileService.java
+++ b/src/main/java/com/activecourses/upwork/service/user/profile/UserProfileService.java
@@ -1,4 +1,4 @@
-package com.activecourses.upwork.service.user;
+package com.activecourses.upwork.service.user.profile;
import com.activecourses.upwork.dto.user.UserProfileDto;
import com.activecourses.upwork.model.User;
@@ -7,6 +7,7 @@
public interface UserProfileService {
ResponseEntity> getUserProfile(int id);
+
UserProfile createUserProfile(User user);
ResponseEntity> UpdateUserProfile(int userId, UserProfileDto updateRequest);
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
index b63e64c..63e533e 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -22,9 +22,9 @@ application:
security:
jwt:
secret-key: ${JWT_SECRET_KEY}
- expiration: 60000 # 1 minute, for testing
+ expiration: 600000 # 10 minutes
refresh-token:
- expiration: 180000 # 3 minutes, for testing
+ expiration: 604800000 # 7 days
cookie:
jwt-cookie-name: ${JWT_COOKIE_NAME}
jwt-refresh-cookie-name: ${JWT_REFRESH_COOKIE_NAME}
diff --git a/src/main/resources/env.properties b/src/main/resources/env.properties
index 5cf956c..5cb5170 100644
--- a/src/main/resources/env.properties
+++ b/src/main/resources/env.properties
@@ -3,14 +3,16 @@ POSTGRES_USER=postgres
POSTGRES_PASSWORD=root
POSTGRES_DB=upwork
-JWT_SECRET_KEY=382b27314b5b3b637a2c766f41596c50636b25755b2b3f4e7721494f61
-JWT_COOKIE_NAME=jwt_token
-JWT_REFRESH_COOKIE_NAME=jwt_refresh_token
-JWT_COOKIE_MAX_AGE=86400
-
spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=test@gmail.com
spring.mail.password=test
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
+
+application.security.jwt.secret-key=382b27314b5b3b637a2c766f41596c50636b25755b2b3f4e7721494f61
+application.security.jwt.expiration=3600000
+application.security.jwt.refresh-token.expiration=7200000
+application.security.jwt.cookie.jwt-cookie-name=jwt_token
+application.security.jwt.cookie.jwt-refresh-cookie-name=jwt_refresh_token
+application.security.jwt.cookie.max-age=86400
\ No newline at end of file
diff --git a/src/test/java/com/activecourses/upwork/UpworkApplicationTests.java b/src/test/java/com/activecourses/upwork/UpworkApplicationTests.java
index 27d9540..c9214f7 100644
--- a/src/test/java/com/activecourses/upwork/UpworkApplicationTests.java
+++ b/src/test/java/com/activecourses/upwork/UpworkApplicationTests.java
@@ -2,12 +2,13 @@
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
@SpringBootTest
+@ActiveProfiles("test")
class UpworkApplicationTests {
- @Test
- void contextLoads() {
- }
-
-}
+ @Test
+ void contextLoads() {
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/activecourses/upwork/service/AuthServiceImplTest.java b/src/test/java/com/activecourses/upwork/service/AuthServiceImplTest.java
new file mode 100644
index 0000000..7975704
--- /dev/null
+++ b/src/test/java/com/activecourses/upwork/service/AuthServiceImplTest.java
@@ -0,0 +1,237 @@
+package com.activecourses.upwork.service;
+
+import com.activecourses.upwork.dto.ResponseDto;
+import com.activecourses.upwork.dto.authentication.login.LoginRequestDto;
+import com.activecourses.upwork.dto.authentication.registration.RegistrationRequestDto;
+import com.activecourses.upwork.mapper.Mapper;
+import com.activecourses.upwork.model.RefreshToken;
+import com.activecourses.upwork.model.User;
+import com.activecourses.upwork.repository.user.UserRepository;
+import com.activecourses.upwork.repository.role.RoleRepository;
+import com.activecourses.upwork.service.authentication.AuthServiceImpl;
+import com.activecourses.upwork.config.security.jwt.JwtService;
+import com.activecourses.upwork.config.security.CustomUserDetailsService;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.mail.SimpleMailMessage;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.http.ResponseCookie;
+
+import java.util.Objects;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
+class AuthServiceImplTest {
+
+ @Mock
+ private UserRepository userRepository;
+
+ @Mock
+ private RoleRepository roleRepository;
+
+ @Mock
+ private PasswordEncoder passwordEncoder;
+
+ @Mock
+ private Mapper userMapper;
+
+ @Mock
+ private AuthenticationManager authenticationManager;
+
+ @Mock
+ private JwtService jwtService;
+
+ @Mock
+ private JavaMailSender mailSender;
+
+ @Mock
+ private CustomUserDetailsService customUserDetailsService;
+
+ @Mock
+ private RefreshTokenService refreshTokenService;
+
+ @InjectMocks
+ private AuthServiceImpl authService;
+
+ @BeforeEach
+ void setUp() {
+ MockitoAnnotations.openMocks(this);
+ }
+
+ @Test
+ void registerUser() {
+ RegistrationRequestDto registrationRequestDto = new RegistrationRequestDto();
+ registrationRequestDto.setFirstName("Ahmed");
+ registrationRequestDto.setLastName("Muhammed");
+ registrationRequestDto.setEmail("am0103738@gmail.com");
+ registrationRequestDto.setPassword("password123");
+
+ User user = new User();
+ user.setFirstName("Ahmed");
+ user.setLastName("Muhammed");
+ user.setEmail("am0103738@gmail.com");
+ user.setPassword("encodedPassword");
+
+ when(userMapper.mapFrom(any(RegistrationRequestDto.class))).thenReturn(user);
+ when(passwordEncoder.encode(any(String.class))).thenReturn("encodedPassword");
+ when(userRepository.save(any(User.class))).thenReturn(user);
+ when(userRepository.findByEmail("am0103738@gmail.com")).thenReturn(Optional.of(user));
+
+ authService.registerUser(registrationRequestDto);
+
+ verify(userRepository, times(1)).save(user);
+ assertNotNull(userRepository.findByEmail("am0103738@gmail.com"));
+ assertEquals("encodedPassword", user.getPassword());
+ }
+
+ @Test
+ void login() {
+ LoginRequestDto loginRequestDto = new LoginRequestDto("am0103738@gmail.com", "password123");
+
+ User user = new User();
+ user.setId(1);
+ user.setEmail("am0103738@gmail.com");
+ user.setPassword("encodedPassword");
+ user.setAccountEnabled(true);
+ user.setAccountLocked(false);
+
+ UserDetails userDetails = mock(UserDetails.class);
+ when(userDetails.getUsername()).thenReturn(user.getEmail());
+ when(userDetails.getPassword()).thenReturn(user.getPassword());
+
+ when(userRepository.findByEmail("am0103738@gmail.com")).thenReturn(Optional.of(user));
+ when(authenticationManager.authenticate(any(Authentication.class))).thenReturn(mock(Authentication.class));
+ when(customUserDetailsService.loadUserByUsername("am0103738@gmail.com")).thenReturn(user);
+
+ ResponseCookie jwtCookie = mock(ResponseCookie.class);
+ when(jwtService.generateJwtCookie(any(UserDetails.class))).thenReturn(jwtCookie);
+
+ RefreshToken refreshToken = mock(RefreshToken.class);
+ when(refreshTokenService.createRefreshToken(anyInt())).thenReturn(refreshToken);
+ when(refreshToken.getToken()).thenReturn("refreshToken");
+
+ ResponseCookie refreshJwtCookie = mock(ResponseCookie.class);
+ when(jwtService.generateRefreshJwtCookie(anyString())).thenReturn(refreshJwtCookie);
+
+ ResponseDto responseDto = authService.login(loginRequestDto);
+
+ assertEquals(HttpStatus.OK, responseDto.getStatus());
+ assertTrue(responseDto.isSuccess());
+ }
+
+ @Test
+ void logout() {
+ User user = new User();
+ user.setId(1);
+
+ SecurityContextHolder.getContext().setAuthentication(mock(Authentication.class));
+ when(SecurityContextHolder.getContext().getAuthentication().getPrincipal()).thenReturn(user);
+ when(jwtService.getCleanJwtCookie()).thenReturn(mock(ResponseCookie.class));
+ when(jwtService.getCleanJwtRefreshCookie()).thenReturn(mock(ResponseCookie.class));
+
+ ResponseEntity responseEntity = authService.logout();
+
+ assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
+ assertTrue(Objects.requireNonNull(responseEntity.getBody()).isSuccess());
+ }
+
+ @Test
+ void refreshToken() {
+ String refreshToken = "refreshToken";
+ User user = new User();
+ user.setEmail("am0103738@gmail.com");
+
+ when(jwtService.getUserNameFromJwtToken(refreshToken)).thenReturn("am0103738@gmail.com");
+ when(userRepository.findByEmail("am0103738@gmail.com")).thenReturn(Optional.of(user));
+
+ Optional refreshedUser = authService.refreshToken(refreshToken);
+
+ assertTrue(refreshedUser.isPresent());
+ assertEquals("am0103738@gmail.com", refreshedUser.get().getEmail());
+ }
+
+ @Test
+ void verifyUser() {
+ String token = "verificationToken";
+ User user = new User();
+ user.setVerificationToken(token);
+
+ when(userRepository.findByVerificationToken(token)).thenReturn(Optional.of(user));
+
+ boolean isVerified = authService.verifyUser(token);
+
+ assertTrue(isVerified);
+ assertTrue(user.isAccountEnabled());
+ assertNull(user.getVerificationToken());
+ }
+
+ @Test
+ void findByEmail() {
+ String email = "am0103738@gmail.com";
+ User user = new User();
+ user.setEmail(email);
+
+ when(userRepository.findByEmail(email)).thenReturn(Optional.of(user));
+
+ User foundUser = authService.findByEmail(email);
+
+ assertNotNull(foundUser);
+ assertEquals(email, foundUser.getEmail());
+ }
+
+ @Test
+ void sendVerificationEmail() {
+ User user = new User();
+ user.setEmail("am0103738@gmail.com");
+ user.setVerificationToken("verificationToken");
+
+ doNothing().when(mailSender).send(any(SimpleMailMessage.class));
+
+ authService.sendVerificationEmail(user);
+
+ verify(mailSender, times(1)).send(any(SimpleMailMessage.class));
+ }
+
+ @Test
+ void deactivateUser() {
+ int userId = 1;
+ User user = new User();
+ user.setId(userId);
+ user.setAccountEnabled(true);
+
+ when(userRepository.findById(userId)).thenReturn(Optional.of(user));
+
+ boolean success = authService.deactivateUser(userId);
+
+ assertTrue(success);
+ assertFalse(user.isAccountEnabled());
+ }
+
+ @Test
+ void reactivateUser() {
+ int userId = 1;
+ User user = new User();
+ user.setId(userId);
+ user.setAccountEnabled(false);
+
+ when(userRepository.findById(userId)).thenReturn(Optional.of(user));
+
+ boolean success = authService.reactivateUser(userId);
+
+ assertTrue(success);
+ assertTrue(user.isAccountEnabled());
+ }
+}
diff --git a/src/test/java/com/activecourses/upwork/service/AuthServiceTest.java b/src/test/java/com/activecourses/upwork/service/AuthServiceTest.java
deleted file mode 100644
index c3d35c2..0000000
--- a/src/test/java/com/activecourses/upwork/service/AuthServiceTest.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package com.activecourses.upwork.service;
-
-import com.activecourses.upwork.dto.authentication.registration.RegistrationRequestDto;
-import com.activecourses.upwork.mapper.Mapper;
-import com.activecourses.upwork.model.User;
-import com.activecourses.upwork.repository.user.UserRepository;
-import com.activecourses.upwork.service.authentication.AuthServiceImpl;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.springframework.security.crypto.password.PasswordEncoder;
-
-import java.util.Optional;
-
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.*;
-
-class AuthServiceTest {
-
- @Mock
- private UserRepository userRepository;
-
- @Mock
- private PasswordEncoder passwordEncoder;
-
- @Mock
- private Mapper userMapper;
-
- @InjectMocks
- private AuthServiceImpl userService;
-
- @BeforeEach
- void setUp() {
- MockitoAnnotations.openMocks(this);
- }
-
- @Test
- void registerUser() {
- RegistrationRequestDto registrationRequestDto = new RegistrationRequestDto();
- registrationRequestDto.setFirstName("Ahmed");
- registrationRequestDto.setLastName("Muhammed");
- registrationRequestDto.setEmail("am0103738@gmail.com");
- registrationRequestDto.setPassword("password123");
-
- User user = new User();
- user.setFirstName("Ahmed");
- user.setLastName("Muhammed");
- user.setEmail("am0103738@gmail.com");
- user.setPassword("encodedPassword");
-
- when(userMapper.mapFrom(any(RegistrationRequestDto.class))).thenReturn(user);
- // Verify that the UserService correctly calls the passwordEncoder.encode method
- // Just simulate the behavior of the password encoder without actually performing the encoding
- when(passwordEncoder.encode(any(String.class))).thenReturn("encodedPassword");
- when(userRepository.save(any(User.class))).thenReturn(user);
- when(userRepository.findByEmail("am0103738@gmail.com")).thenReturn(Optional.of(user));
-
- userService.registerUser(registrationRequestDto);
-
- verify(userRepository, times(1)).save(user);
- assertNotNull(userRepository.findByEmail("am0103738@gmail.com"));
- assertEquals("encodedPassword", user.getPassword());
- }
-}
\ No newline at end of file
diff --git a/src/test/java/com/activecourses/upwork/service/RoleServiceImplTest.java b/src/test/java/com/activecourses/upwork/service/RoleServiceImplTest.java
new file mode 100644
index 0000000..560e0da
--- /dev/null
+++ b/src/test/java/com/activecourses/upwork/service/RoleServiceImplTest.java
@@ -0,0 +1,134 @@
+package com.activecourses.upwork.service;
+
+import com.activecourses.upwork.model.Role;
+import com.activecourses.upwork.model.User;
+import com.activecourses.upwork.repository.role.RoleRepository;
+import com.activecourses.upwork.repository.user.UserRepository;
+import com.activecourses.upwork.service.role.RoleServiceImpl;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.HashMap;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
+class RoleServiceImplTest {
+
+ @Mock
+ private RoleRepository roleRepository;
+
+ @Mock
+ private UserRepository userRepository;
+
+ @InjectMocks
+ private RoleServiceImpl roleService;
+
+ @BeforeEach
+ void setUp() {
+ MockitoAnnotations.openMocks(this);
+ }
+
+ @Test
+ void addRole() {
+ Role role = new Role();
+ role.setName("ROLE_TEST");
+
+ when(roleRepository.save(any(Role.class))).thenReturn(role);
+
+ Role createdRole = roleService.addRole(role);
+
+ assertNotNull(createdRole);
+ assertEquals("ROLE_TEST", createdRole.getName());
+ }
+
+ @Test
+ void removeRole() {
+ int roleId = 1;
+
+ Role role = new Role();
+ role.setId(roleId);
+
+ when(roleRepository.findById(roleId)).thenReturn(Optional.of(role));
+
+ boolean success = roleService.removeRole(roleId);
+
+ assertTrue(success);
+ verify(roleRepository, times(1)).delete(role);
+ }
+
+ @Test
+ void updateRole() {
+ int roleId = 1;
+
+ Role role = new Role();
+ role.setId(roleId);
+ role.setName("ROLE_OLD");
+
+ Role updatedRole = new Role();
+ updatedRole.setName("ROLE_NEW");
+
+ when(roleRepository.findById(roleId)).thenReturn(Optional.of(role));
+ when(roleRepository.save(any(Role.class))).thenReturn(updatedRole);
+
+ Role result = roleService.updateRole(roleId, updatedRole);
+
+ assertNotNull(result);
+ assertEquals("ROLE_NEW", result.getName());
+ }
+
+ @Test
+ void getAllRoles() {
+ Role role1 = new Role();
+ role1.setName("ROLE_1");
+
+ Role role2 = new Role();
+ role2.setName("ROLE_2");
+
+ List roles = List.of(role1, role2);
+
+ when(roleRepository.findAll()).thenReturn(roles);
+
+ List result = roleService.getAllRoles();
+
+ assertNotNull(result);
+ assertEquals(2, result.size());
+ assertTrue(result.contains(role1));
+ assertTrue(result.contains(role2));
+ }
+
+ @Test
+ void assignRolesToUser() {
+ int userId = 1;
+ Map roles = new HashMap<>();
+ roles.put("ROLE_ADMIN", null);
+ roles.put("ROLE_USER", null);
+
+ User user = new User();
+ user.setId(userId);
+
+ Role adminRole = new Role();
+ adminRole.setName("ROLE_ADMIN");
+
+ Role userRole = new Role();
+ userRole.setName("ROLE_USER");
+
+ when(userRepository.findById(userId)).thenReturn(Optional.of(user));
+ when(roleRepository.findByName("ROLE_ADMIN")).thenReturn(Optional.of(adminRole));
+ when(roleRepository.findByName("ROLE_USER")).thenReturn(Optional.of(userRole));
+
+ boolean success = roleService.assignRolesToUser(userId, roles);
+
+ assertTrue(success);
+ assertEquals(2, user.getRoles().size());
+ assertTrue(user.getRoles().contains(adminRole));
+ assertTrue(user.getRoles().contains(userRole));
+ }
+}
diff --git a/src/test/java/com/activecourses/upwork/service/UserServiceImplTest.java b/src/test/java/com/activecourses/upwork/service/UserServiceImplTest.java
new file mode 100644
index 0000000..637be10
--- /dev/null
+++ b/src/test/java/com/activecourses/upwork/service/UserServiceImplTest.java
@@ -0,0 +1,100 @@
+package com.activecourses.upwork.service;
+
+import com.activecourses.upwork.dto.user.UserDto;
+import com.activecourses.upwork.dto.user.UserResponseDto;
+import com.activecourses.upwork.mapper.user.UserDtoMapper;
+import com.activecourses.upwork.model.User;
+import com.activecourses.upwork.repository.user.UserRepository;
+import com.activecourses.upwork.service.user.UserServiceImpl;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+
+import java.util.List;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
+class UserServiceImplTest {
+
+ @Mock
+ private UserRepository userRepository;
+
+ @Mock
+ private UserDtoMapper userDtoMapper;
+
+ @InjectMocks
+ private UserServiceImpl userService;
+
+ @BeforeEach
+ void setUp() {
+ MockitoAnnotations.openMocks(this);
+ }
+
+ @Test
+ void getAllUsers() {
+ User user1 = new User();
+ user1.setId(1);
+ user1.setEmail("user1@example.com");
+
+ User user2 = new User();
+ user2.setId(2);
+ user2.setEmail("user2@example.com");
+
+ List users = List.of(user1, user2);
+ Page pagedResult = new PageImpl<>(users);
+
+ Pageable pageable = PageRequest.of(0, 10, Sort.by("id").ascending());
+
+ when(userRepository.findAll(any(Pageable.class))).thenReturn(pagedResult);
+
+ UserDto userDto1 = new UserDto();
+ userDto1.setId(1);
+
+ UserDto userDto2 = new UserDto();
+ userDto2.setId(2);
+
+ when(userDtoMapper.mapTo(user1)).thenReturn(userDto1);
+ when(userDtoMapper.mapTo(user2)).thenReturn(userDto2);
+
+ UserResponseDto userResponseDto = userService.getAllUsers(0, 10, "id", "asc");
+
+ assertNotNull(userResponseDto);
+ assertEquals(2, userResponseDto.getContent().size());
+ assertEquals(1, userResponseDto.getContent().get(0).getId());
+ assertEquals(2, userResponseDto.getContent().get(1).getId());
+ }
+
+ @Test
+ void findByEmail() {
+ String email = "user@example.com";
+ User user = new User();
+ user.setEmail(email);
+
+ when(userRepository.findByEmail(email)).thenReturn(Optional.of(user));
+
+ User foundUser = userService.findByEmail(email);
+
+ assertNotNull(foundUser);
+ assertEquals(email, foundUser.getEmail());
+ }
+
+ @Test
+ void findByEmail_UserNotFound() {
+ String email = "user@example.com";
+
+ when(userRepository.findByEmail(email)).thenReturn(Optional.empty());
+
+ assertThrows(UsernameNotFoundException.class, () -> userService.findByEmail(email));
+ }
+}
diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml
new file mode 100644
index 0000000..dc0d8bb
--- /dev/null
+++ b/src/test/resources/application-test.yml
@@ -0,0 +1,10 @@
+spring:
+ datasource:
+ url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
+ driver-class-name: org.h2.Driver
+ username: sa
+ password: password
+ jpa:
+ database-platform: org.hibernate.dialect.H2Dialect
+ hibernate:
+ ddl-auto: create-drop
\ No newline at end of file