diff --git a/common/common.parent/common.parent.service/pom.xml b/common/common.parent/common.parent.service/pom.xml
index 45b0cc8a6..c93012c8d 100644
--- a/common/common.parent/common.parent.service/pom.xml
+++ b/common/common.parent/common.parent.service/pom.xml
@@ -97,6 +97,12 @@
${project.version}
compile
+
+ org.eclipse.slm
+ common.utils.keycloak
+ ${project.version}
+ compile
+
diff --git a/platform_management/platform_management.common/platform_management.common.api/pom.xml b/platform_management/platform_management.common/platform_management.common.api/pom.xml
new file mode 100644
index 000000000..61128cfbb
--- /dev/null
+++ b/platform_management/platform_management.common/platform_management.common.api/pom.xml
@@ -0,0 +1,42 @@
+
+
+ 4.0.0
+
+ platform_management.common.api
+ ${revision}
+ jar
+
+
+ org.eclipse.slm
+ platform_management.common
+ ${revision}
+
+
+
+
+
+ io.swagger.core.v3
+ swagger-annotations
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.eclipse.slm
+ platform_management.features.credentials_management.api
+ ${project.version}
+ compile
+
+
+ org.eclipse.slm
+ platform_management.features.user_management.api
+ ${project.version}
+ compile
+
+
+
+
\ No newline at end of file
diff --git a/platform_management/platform_management.service/platform_management.service.api/src/main/java/org/eclipse/slm/platform_management/service/api/PlatformManagementApiConfig.java b/platform_management/platform_management.common/platform_management.common.api/src/main/java/org/eclipse/slm/platform_management/service/api/PlatformManagementApiConfig.java
similarity index 100%
rename from platform_management/platform_management.service/platform_management.service.api/src/main/java/org/eclipse/slm/platform_management/service/api/PlatformManagementApiConfig.java
rename to platform_management/platform_management.common/platform_management.common.api/src/main/java/org/eclipse/slm/platform_management/service/api/PlatformManagementApiConfig.java
diff --git a/platform_management/platform_management.common/pom.xml b/platform_management/platform_management.common/pom.xml
new file mode 100644
index 000000000..9adea5f83
--- /dev/null
+++ b/platform_management/platform_management.common/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+ platform_management.common
+ ${revision}
+ pom
+
+
+ org.eclipse.slm
+ platform_management
+ ${revision}
+
+
+
+ platform_management.common.api
+
+
+
+
diff --git a/platform_management/platform_management.service/platform_management.service.api/pom.xml b/platform_management/platform_management.features/platform_management.features.credentials_management/platform_management.features.credentials_management.api/pom.xml
similarity index 82%
rename from platform_management/platform_management.service/platform_management.service.api/pom.xml
rename to platform_management/platform_management.features/platform_management.features.credentials_management/platform_management.features.credentials_management.api/pom.xml
index f95257646..4a90bf8dd 100644
--- a/platform_management/platform_management.service/platform_management.service.api/pom.xml
+++ b/platform_management/platform_management.features/platform_management.features.credentials_management/platform_management.features.credentials_management.api/pom.xml
@@ -4,28 +4,25 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
- platform_management.service.api
+ platform_management.features.credentials_management.api
${revision}
jar
org.eclipse.slm
- platform_management.service
+ platform_management.features.credentials_management
${revision}
-
io.swagger.core.v3
swagger-annotations
-
org.springframework.boot
spring-boot-starter-web
-
org.eclipse.slm
common.credentials
@@ -34,4 +31,5 @@
-
\ No newline at end of file
+
+
diff --git a/platform_management/platform_management.service/platform_management.service.api/src/main/java/org/eclipse/slm/platform_management/service/api/credentials/CredentialCreateRequest.kt b/platform_management/platform_management.features/platform_management.features.credentials_management/platform_management.features.credentials_management.api/src/main/java/org/eclipse/slm/platform_management/features/credentials_management/api/CredentialCreateRequest.kt
similarity index 85%
rename from platform_management/platform_management.service/platform_management.service.api/src/main/java/org/eclipse/slm/platform_management/service/api/credentials/CredentialCreateRequest.kt
rename to platform_management/platform_management.features/platform_management.features.credentials_management/platform_management.features.credentials_management.api/src/main/java/org/eclipse/slm/platform_management/features/credentials_management/api/CredentialCreateRequest.kt
index 2a0acf6e4..ec83a7b9b 100644
--- a/platform_management/platform_management.service/platform_management.service.api/src/main/java/org/eclipse/slm/platform_management/service/api/credentials/CredentialCreateRequest.kt
+++ b/platform_management/platform_management.features/platform_management.features.credentials_management/platform_management.features.credentials_management.api/src/main/java/org/eclipse/slm/platform_management/features/credentials_management/api/CredentialCreateRequest.kt
@@ -1,4 +1,4 @@
-package org.eclipse.slm.platform_management.service.api.credentials
+package org.eclipse.slm.platform_management.features.credentials_management.api
import com.fasterxml.jackson.annotation.JsonProperty
import org.eclipse.slm.common.credentials.model.Credential
@@ -14,5 +14,6 @@ data class CredentialCreateRequest(
@param:JsonProperty("fullPathOwnerGroupId")
val fullPathOwnerGroupId: String
-) {
-}
\ No newline at end of file
+)
+
+
diff --git a/platform_management/platform_management.service/platform_management.service.api/src/main/java/org/eclipse/slm/platform_management/service/api/credentials/CredentialManagementRestApi.java b/platform_management/platform_management.features/platform_management.features.credentials_management/platform_management.features.credentials_management.api/src/main/java/org/eclipse/slm/platform_management/features/credentials_management/api/CredentialManagementRestApi.java
similarity index 67%
rename from platform_management/platform_management.service/platform_management.service.api/src/main/java/org/eclipse/slm/platform_management/service/api/credentials/CredentialManagementRestApi.java
rename to platform_management/platform_management.features/platform_management.features.credentials_management/platform_management.features.credentials_management.api/src/main/java/org/eclipse/slm/platform_management/features/credentials_management/api/CredentialManagementRestApi.java
index b8330361f..d08db01e9 100644
--- a/platform_management/platform_management.service/platform_management.service.api/src/main/java/org/eclipse/slm/platform_management/service/api/credentials/CredentialManagementRestApi.java
+++ b/platform_management/platform_management.features/platform_management.features.credentials_management/platform_management.features.credentials_management.api/src/main/java/org/eclipse/slm/platform_management/features/credentials_management/api/CredentialManagementRestApi.java
@@ -1,11 +1,16 @@
-package org.eclipse.slm.platform_management.service.api.credentials;
+package org.eclipse.slm.platform_management.features.credentials_management.api;
import io.swagger.v3.oas.annotations.Operation;
import org.eclipse.slm.common.credentials.model.CredentialData;
import org.eclipse.slm.common.credentials.model.CredentialEntityLinkCreateDTO;
import org.eclipse.slm.common.credentials.model.CredentialReadDTO;
import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
import java.util.UUID;
@@ -14,51 +19,56 @@ public interface CredentialManagementRestApi {
@RequestMapping(value = "/{credentialId}", method = RequestMethod.GET)
@Operation(summary = "Get credential by id")
- @ResponseBody ResponseEntity getCredentialById(
- @PathVariable(name = "credentialId") UUID credentialId);
+ @ResponseBody
+ ResponseEntity getCredentialById(@PathVariable(name = "credentialId") UUID credentialId);
@RequestMapping(value = "/{credentialId}/data", method = RequestMethod.GET)
@Operation(summary = "Get credential data by id")
- @ResponseBody ResponseEntity getCredentialDataById(
+ @ResponseBody
+ ResponseEntity getCredentialDataById(
@PathVariable(name = "credentialId") UUID credentialId,
- @RequestParam(name = "impersonatedGroupId") String impersonatedGroupId
- );
+ @RequestParam(name = "impersonatedGroupId") String impersonatedGroupId);
@RequestMapping(value = "/", method = RequestMethod.POST)
@Operation(summary = "Create credential")
- @ResponseBody ResponseEntity createCredential(
- @RequestBody CredentialCreateRequest credentialCreateRequest);
+ @ResponseBody
+ ResponseEntity createCredential(@RequestBody CredentialCreateRequest credentialCreateRequest);
@RequestMapping(value = "/{credentialId}", method = RequestMethod.PUT)
@Operation(summary = "Create or update credential")
- @ResponseBody ResponseEntity createOrUpdateCredential(
- @PathVariable(name = "credentialId") UUID credentialId,
+ @ResponseBody
+ ResponseEntity createOrUpdateCredential(
+ @PathVariable(name = "credentialId") UUID credentialId,
@RequestBody CredentialCreateRequest credentialCreateRequest);
@RequestMapping(value = "/{credentialId}", method = RequestMethod.DELETE)
@Operation(summary = "Delete credential by id")
- @ResponseBody ResponseEntity deleteCredential(
- @PathVariable(name = "credentialId") UUID credentialId);
+ @ResponseBody
+ ResponseEntity deleteCredential(@PathVariable(name = "credentialId") UUID credentialId);
@RequestMapping(value = "/entities/{entityId}", method = RequestMethod.GET)
@Operation(summary = "Get credentials of entity")
- @ResponseBody ResponseEntity> getCredentialsOfEntity(
+ @ResponseBody
+ ResponseEntity> getCredentialsOfEntity(
@PathVariable(name = "entityId") String entityId,
@RequestParam(value = "entityType") String entityType);
@RequestMapping(value = "", method = RequestMethod.GET)
@Operation(summary = "Get credentials of user")
- @ResponseBody ResponseEntity> getCredentialsOfUser();
+ @ResponseBody
+ ResponseEntity> getCredentialsOfUser();
@RequestMapping(value = "/{credentialId}/links", method = RequestMethod.POST)
@Operation(summary = "Link existing credential to entity")
- @ResponseBody ResponseEntity linkCredentialToEntity(
+ @ResponseBody
+ ResponseEntity linkCredentialToEntity(
@PathVariable(name = "credentialId") UUID credentialId,
@RequestBody CredentialEntityLinkCreateDTO entityLink);
@RequestMapping(value = "/{credentialId}/links", method = RequestMethod.DELETE)
@Operation(summary = "Delete credential entity link")
- @ResponseBody ResponseEntity deleteCredentialEntityLink(
+ @ResponseBody
+ ResponseEntity deleteCredentialEntityLink(
@PathVariable(name = "credentialId") UUID credentialId,
@RequestParam(value = "entityType") String entityType,
@RequestParam(value = "entityId") String entityId,
@@ -66,14 +76,17 @@ public interface CredentialManagementRestApi {
@RequestMapping(value = "/{credentialId}/scopes", method = RequestMethod.POST)
@Operation(summary = "Add scopes to credential")
- @ResponseBody ResponseEntity addCredentialScopes(
+ @ResponseBody
+ ResponseEntity addCredentialScopes(
@PathVariable(name = "credentialId") UUID credentialId,
@RequestBody List scopes);
@RequestMapping(value = "/{credentialId}/scopes", method = RequestMethod.DELETE)
@Operation(summary = "Remove scopes from credential")
- @ResponseBody ResponseEntity removeCredentialScopes(
+ @ResponseBody
+ ResponseEntity removeCredentialScopes(
@PathVariable(name = "credentialId") UUID credentialId,
@RequestBody List scopes);
-
}
+
+
diff --git a/platform_management/platform_management.features/platform_management.features.credentials_management/platform_management.features.credentials_management.api/src/main/java/org/eclipse/slm/platform_management/features/credentials_management/api/CredentialManagementRestApiConfig.java b/platform_management/platform_management.features/platform_management.features.credentials_management/platform_management.features.credentials_management.api/src/main/java/org/eclipse/slm/platform_management/features/credentials_management/api/CredentialManagementRestApiConfig.java
new file mode 100644
index 000000000..d1f3c7513
--- /dev/null
+++ b/platform_management/platform_management.features/platform_management.features.credentials_management/platform_management.features.credentials_management.api/src/main/java/org/eclipse/slm/platform_management/features/credentials_management/api/CredentialManagementRestApiConfig.java
@@ -0,0 +1,11 @@
+package org.eclipse.slm.platform_management.features.credentials_management.api;
+
+public class CredentialManagementRestApiConfig {
+
+ public static final String BASE_PATH = "/credentials";
+
+ public static final String TAG = "Credential Management";
+
+}
+
+
diff --git a/platform_management/platform_management.features/platform_management.features.credentials_management/platform_management.features.credentials_management.impl/pom.xml b/platform_management/platform_management.features/platform_management.features.credentials_management/platform_management.features.credentials_management.impl/pom.xml
new file mode 100644
index 000000000..ac78d8435
--- /dev/null
+++ b/platform_management/platform_management.features/platform_management.features.credentials_management/platform_management.features.credentials_management.impl/pom.xml
@@ -0,0 +1,47 @@
+
+
+ 4.0.0
+
+ platform_management.features.credentials_management.impl
+ ${revision}
+ jar
+
+
+ org.eclipse.slm
+ platform_management.features.credentials_management
+ ${revision}
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.security
+ spring-security-oauth2-resource-server
+
+
+ org.eclipse.slm
+ common.credentials
+ ${project.version}
+ compile
+
+
+ org.eclipse.slm
+ common.utils.keycloak
+ ${project.version}
+ compile
+
+
+ org.eclipse.slm
+ platform_management.features.credentials_management.api
+ ${project.version}
+ compile
+
+
+
+
+
diff --git a/platform_management/platform_management.service/platform_management.service.app/src/main/java/org/eclipse/slm/platform_management/service/app/credentials/CredentialManagementRestController.java b/platform_management/platform_management.features/platform_management.features.credentials_management/platform_management.features.credentials_management.impl/src/main/java/org/eclipse/slm/platform_management/features/credentials_management/impl/CredentialManagementRestController.java
similarity index 83%
rename from platform_management/platform_management.service/platform_management.service.app/src/main/java/org/eclipse/slm/platform_management/service/app/credentials/CredentialManagementRestController.java
rename to platform_management/platform_management.features/platform_management.features.credentials_management/platform_management.features.credentials_management.impl/src/main/java/org/eclipse/slm/platform_management/features/credentials_management/impl/CredentialManagementRestController.java
index 80bb55678..c74deecec 100644
--- a/platform_management/platform_management.service/platform_management.service.app/src/main/java/org/eclipse/slm/platform_management/service/app/credentials/CredentialManagementRestController.java
+++ b/platform_management/platform_management.features/platform_management.features.credentials_management/platform_management.features.credentials_management.impl/src/main/java/org/eclipse/slm/platform_management/features/credentials_management/impl/CredentialManagementRestController.java
@@ -1,21 +1,20 @@
-package org.eclipse.slm.platform_management.service.app.credentials;
+package org.eclipse.slm.platform_management.features.credentials_management.impl;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.eclipse.slm.common.credentials.CredentialsManager;
import org.eclipse.slm.common.credentials.model.CredentialData;
+import org.eclipse.slm.common.credentials.model.CredentialEntityLinkCreateDTO;
import org.eclipse.slm.common.credentials.model.CredentialReadDTO;
-import org.eclipse.slm.common.restserver.annotations.AuthorizedAsSlmUser;
import org.eclipse.slm.common.utils.keycloak.KeycloakTokenUtil;
-import org.eclipse.slm.platform_management.service.api.credentials.CredentialCreateRequest;
-import org.eclipse.slm.platform_management.service.api.credentials.CredentialManagementRestApi;
-import org.eclipse.slm.platform_management.service.api.credentials.CredentialManagementRestApiConfig;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.eclipse.slm.platform_management.features.credentials_management.api.CredentialCreateRequest;
+import org.eclipse.slm.platform_management.features.credentials_management.api.CredentialManagementRestApi;
+import org.eclipse.slm.platform_management.features.credentials_management.api.CredentialManagementRestApiConfig;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.UUID;
@@ -25,8 +24,6 @@
@Tag(name = CredentialManagementRestApiConfig.TAG)
public class CredentialManagementRestController implements CredentialManagementRestApi {
- private final static Logger LOG = LoggerFactory.getLogger(CredentialManagementRestController.class);
-
private final CredentialsManager credentialsManager;
public CredentialManagementRestController(CredentialsManager credentialsManager) {
@@ -44,14 +41,12 @@ public ResponseEntity getCredentialById(UUID credentialId) {
@Override
@PreAuthorize("authentication.tokenAttributes['client_id'] == 'resource_management'")
public ResponseEntity getCredentialDataById(UUID credentialId, String impersonatedGroupId) {
- var jwtAuthenticationToken = (JwtAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
var credentialData = this.credentialsManager.getCredentialDataById(credentialId, impersonatedGroupId);
return ResponseEntity.ok(credentialData);
}
@Override
public ResponseEntity createCredential(CredentialCreateRequest credentialCreateRequest) {
- var jwtAuthenticationToken = (JwtAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
credentialCreateRequest.getCredential().setId(UUID.randomUUID());
this.credentialsManager.createCredential(
credentialCreateRequest.getCredential(),
@@ -62,9 +57,9 @@ public ResponseEntity createCredential(CredentialCreateRequest credentialC
@Override
public ResponseEntity createOrUpdateCredential(UUID credentialId, CredentialCreateRequest credentialCreateRequest) {
- var jwtAuthenticationToken = (JwtAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
credentialCreateRequest.getCredential().setId(credentialId);
- this.credentialsManager.createCredential(credentialCreateRequest.getCredential(),
+ this.credentialsManager.createCredential(
+ credentialCreateRequest.getCredential(),
credentialCreateRequest.getEntityLinks(),
credentialCreateRequest.getFullPathOwnerGroupId());
return ResponseEntity.ok().build();
@@ -74,9 +69,7 @@ public ResponseEntity createOrUpdateCredential(UUID credentialId, Credenti
public ResponseEntity deleteCredential(UUID credentialId) {
var jwtAuthenticationToken = (JwtAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
var userAccessToken = KeycloakTokenUtil.getToken(jwtAuthenticationToken);
-
this.credentialsManager.deleteCredentialForUser(credentialId, userAccessToken);
-
return ResponseEntity.ok().build();
}
@@ -97,14 +90,13 @@ public ResponseEntity> getCredentialsOfUser() {
var userGroups = (List) groupsClaimList;
var credentials = this.credentialsManager.getAllCredentialsForUser(userGroups, userAccessToken);
return ResponseEntity.ok(credentials);
- } else {
- throw new IllegalStateException("User groups claim is missing or invalid in the JWT token");
}
+ throw new IllegalStateException("User groups claim is missing or invalid in the JWT token");
}
@Override
- public ResponseEntity linkCredentialToEntity(UUID credentialId, org.eclipse.slm.common.credentials.model.CredentialEntityLinkCreateDTO entityLink) {
+ public ResponseEntity linkCredentialToEntity(UUID credentialId, CredentialEntityLinkCreateDTO entityLink) {
var jwtAuthenticationToken = (JwtAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
var userAccessToken = KeycloakTokenUtil.getToken(jwtAuthenticationToken);
this.credentialsManager.getCredentialByIdForUser(credentialId, userAccessToken);
@@ -139,3 +131,5 @@ public ResponseEntity removeCredentialScopes(UUID credentialId, List
+
+ 4.0.0
+
+ platform_management.features.credentials_management
+ ${revision}
+ pom
+
+
+ org.eclipse.slm
+ platform_management.features
+ ${revision}
+
+
+
+ platform_management.features.credentials_management.api
+ platform_management.features.credentials_management.impl
+
+
+
+
diff --git a/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.api/pom.xml b/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.api/pom.xml
new file mode 100644
index 000000000..17e4a5bf7
--- /dev/null
+++ b/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.api/pom.xml
@@ -0,0 +1,38 @@
+
+
+ 4.0.0
+
+ platform_management.features.user_management.api
+ ${revision}
+ jar
+
+
+ org.eclipse.slm
+ platform_management.features.user_management
+ ${revision}
+
+
+
+
+ io.swagger.core.v3
+ swagger-annotations
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ jakarta.validation
+ jakarta.validation-api
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+ test
+
+
+
+
+
diff --git a/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.api/src/main/java/org/eclipse/slm/platform_management/features/user_management/api/UserCreateRequest.kt b/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.api/src/main/java/org/eclipse/slm/platform_management/features/user_management/api/UserCreateRequest.kt
new file mode 100644
index 000000000..cd28c4280
--- /dev/null
+++ b/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.api/src/main/java/org/eclipse/slm/platform_management/features/user_management/api/UserCreateRequest.kt
@@ -0,0 +1,48 @@
+package org.eclipse.slm.platform_management.features.user_management.api
+
+import com.fasterxml.jackson.annotation.JsonProperty
+import jakarta.validation.constraints.Email
+import jakarta.validation.constraints.NotBlank
+import jakarta.validation.constraints.Pattern
+
+data class UserCreateRequest(
+
+ @field:NotBlank(message = "Username must not be blank")
+ @field:Pattern(
+ regexp = "^[A-Za-z][A-Za-z0-9_-]*$",
+ message = "Username must start with a letter and contain only letters, numbers, '-' and '_'",
+ )
+ @param:JsonProperty("username")
+ val username: String,
+
+ @field:NotBlank(message = "First name must not be blank")
+ @field:Pattern(
+ regexp = "^[A-Za-z][A-Za-z .-]*$",
+ message = "First name must start with a letter and may only contain letters, spaces, '.' and '-'",
+ )
+ @param:JsonProperty("firstName")
+ val firstName: String,
+
+ @field:NotBlank(message = "Last name must not be blank")
+ @field:Pattern(
+ regexp = "^[A-Za-z][A-Za-z.-]*$",
+ message = "Last name must start with a letter and may only contain letters, '.' and '-'",
+ )
+ @param:JsonProperty("lastName")
+ val lastName: String,
+
+ @param:JsonProperty("password")
+ val password: String,
+
+ @param:JsonProperty("isPasswordTemporary")
+ val isPasswordTemporary: Boolean = false,
+
+ @field:NotBlank(message = "Email must not be blank")
+ @field:Email(message = "Email must be a valid email address")
+ @param:JsonProperty("email")
+ val email: String,
+
+ @param:JsonProperty("isAdmin")
+ val isAdmin: Boolean = false,
+)
+
diff --git a/platform_management/platform_management.service/platform_management.service.app/src/main/java/org/eclipse/slm/platform_management/service/app/users/UserManagementRestApi.java b/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.api/src/main/java/org/eclipse/slm/platform_management/features/user_management/api/UserManagementRestApi.java
similarity index 60%
rename from platform_management/platform_management.service/platform_management.service.app/src/main/java/org/eclipse/slm/platform_management/service/app/users/UserManagementRestApi.java
rename to platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.api/src/main/java/org/eclipse/slm/platform_management/features/user_management/api/UserManagementRestApi.java
index 60f8623a6..76c61b574 100644
--- a/platform_management/platform_management.service/platform_management.service.app/src/main/java/org/eclipse/slm/platform_management/service/app/users/UserManagementRestApi.java
+++ b/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.api/src/main/java/org/eclipse/slm/platform_management/features/user_management/api/UserManagementRestApi.java
@@ -1,4 +1,4 @@
-package org.eclipse.slm.platform_management.service.app.users;
+package org.eclipse.slm.platform_management.features.user_management.api;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.http.MediaType;
@@ -8,13 +8,25 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
+import java.util.List;
+import java.util.Map;
+
public interface UserManagementRestApi {
+ @RequestMapping(value = "", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
+ @Operation(summary = "Get users")
+ ResponseEntity>> getUsers();
+
@RequestMapping(value = "", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Add user")
ResponseEntity createUser(@RequestBody UserCreateRequest userCreateRequest);
+ @RequestMapping(value = "{username}/admin", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+ @Operation(summary = "Grant admin role to user by username")
+ ResponseEntity makeUserAdmin(@PathVariable(name = "username") String username);
+
@RequestMapping(value = "{username}", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Delete user by username")
ResponseEntity deleteUser(@PathVariable(name = "username") String username);
}
+
diff --git a/platform_management/platform_management.service/platform_management.service.app/src/main/java/org/eclipse/slm/platform_management/service/app/users/UserManagementRestApiConfig.java b/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.api/src/main/java/org/eclipse/slm/platform_management/features/user_management/api/UserManagementRestApiConfig.java
similarity index 68%
rename from platform_management/platform_management.service/platform_management.service.app/src/main/java/org/eclipse/slm/platform_management/service/app/users/UserManagementRestApiConfig.java
rename to platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.api/src/main/java/org/eclipse/slm/platform_management/features/user_management/api/UserManagementRestApiConfig.java
index 7a59f410b..8405781d6 100644
--- a/platform_management/platform_management.service/platform_management.service.app/src/main/java/org/eclipse/slm/platform_management/service/app/users/UserManagementRestApiConfig.java
+++ b/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.api/src/main/java/org/eclipse/slm/platform_management/features/user_management/api/UserManagementRestApiConfig.java
@@ -1,10 +1,9 @@
-package org.eclipse.slm.platform_management.service.app.users;
+package org.eclipse.slm.platform_management.features.user_management.api;
public class UserManagementRestApiConfig {
public static final String BASE_PATH = "/users";
public static final String TAG = "User Management";
-
}
diff --git a/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.api/src/test/java/org/eclipse/slm/platform_management/features/user_management/api/UserCreateRequestValidationTest.java b/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.api/src/test/java/org/eclipse/slm/platform_management/features/user_management/api/UserCreateRequestValidationTest.java
new file mode 100644
index 000000000..b4e4c9b44
--- /dev/null
+++ b/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.api/src/test/java/org/eclipse/slm/platform_management/features/user_management/api/UserCreateRequestValidationTest.java
@@ -0,0 +1,128 @@
+package org.eclipse.slm.platform_management.features.user_management.api;
+
+import jakarta.validation.Validation;
+import jakarta.validation.Validator;
+import jakarta.validation.ValidatorFactory;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class UserCreateRequestValidationTest {
+
+ private static ValidatorFactory validatorFactory;
+ private static Validator validator;
+
+ @BeforeAll
+ static void initValidator() {
+ validatorFactory = Validation.buildDefaultValidatorFactory();
+ validator = validatorFactory.getValidator();
+ }
+
+ @AfterAll
+ static void closeValidator() {
+ if (validatorFactory != null) {
+ validatorFactory.close();
+ }
+ }
+
+ private static UserCreateRequest validRequest() {
+ return new UserCreateRequest(
+ "Alice_Admin",
+ "Alice",
+ "Doe",
+ "secret",
+ false,
+ "alice@example.org",
+ false);
+ }
+
+ @Nested
+ class UsernameValidationTests {
+
+ @Test
+ void acceptsUsername_whenItStartsWithLetterAndContainsAllowedCharacters() {
+ var request = new UserCreateRequest("A1_user-name", "Alice", "Doe", "secret", false, "alice@example.org", false);
+
+ var violations = validator.validate(request);
+
+ assertTrue(violations.stream().noneMatch(v -> "username".equals(v.getPropertyPath().toString())));
+ }
+
+ @Test
+ void rejectsUsername_whenItDoesNotStartWithLetter() {
+ var request = new UserCreateRequest("1alice", "Alice", "Doe", "secret", false, "alice@example.org", false);
+
+ var violations = validator.validate(request);
+
+ assertTrue(violations.stream().anyMatch(v -> "username".equals(v.getPropertyPath().toString())));
+ }
+ }
+
+ @Nested
+ class FirstNameValidationTests {
+
+ @Test
+ void rejectsFirstName_whenItDoesNotStartWithLetter() {
+ var request = new UserCreateRequest("Alice", "-Alice", "Doe", "secret", false, "alice@example.org", false);
+
+ var violations = validator.validate(request);
+
+ assertTrue(violations.stream().anyMatch(v -> "firstName".equals(v.getPropertyPath().toString())));
+ }
+
+ @Test
+ void acceptsFirstName_whenItStartsWithLetterAndUsesAllowedCharacters() {
+ var request = new UserCreateRequest("Alice", "A li-ce.", "Doe", "secret", false, "alice@example.org", false);
+
+ var violations = validator.validate(request);
+
+ assertTrue(violations.stream().noneMatch(v -> "firstName".equals(v.getPropertyPath().toString())));
+ }
+ }
+
+ @Nested
+ class LastNameValidationTests {
+
+ @Test
+ void rejectsLastName_whenItDoesNotStartWithLetter() {
+ var request = new UserCreateRequest("Alice", "Alice", ".Doe", "secret", false, "alice@example.org", false);
+
+ var violations = validator.validate(request);
+
+ assertTrue(violations.stream().anyMatch(v -> "lastName".equals(v.getPropertyPath().toString())));
+ }
+
+ @Test
+ void acceptsLastName_whenItStartsWithLetterAndUsesAllowedCharacters() {
+ var request = new UserCreateRequest("Alice", "Alice", "D-oe.", "secret", false, "alice@example.org", false);
+
+ var violations = validator.validate(request);
+
+ assertTrue(violations.stream().noneMatch(v -> "lastName".equals(v.getPropertyPath().toString())));
+ }
+ }
+
+ @Nested
+ class EmailValidationTests {
+
+ @Test
+ void rejectsEmail_whenInvalid() {
+ var request = new UserCreateRequest("Alice", "Alice", "Doe", "secret", false, "not-an-email", false);
+
+ var violations = validator.validate(request);
+
+ assertTrue(violations.stream().anyMatch(v -> "email".equals(v.getPropertyPath().toString())));
+ }
+
+ @Test
+ void acceptsEmail_whenValid() {
+ var violations = validator.validate(validRequest());
+
+ assertTrue(violations.stream().noneMatch(v -> "email".equals(v.getPropertyPath().toString())));
+ }
+ }
+}
+
diff --git a/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.impl/pom.xml b/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.impl/pom.xml
new file mode 100644
index 000000000..85885b0eb
--- /dev/null
+++ b/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.impl/pom.xml
@@ -0,0 +1,59 @@
+
+
+ 4.0.0
+
+ platform_management.features.user_management.impl
+ ${revision}
+ jar
+
+
+ org.eclipse.slm
+ platform_management.features.user_management
+ ${revision}
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ org.eclipse.slm
+ common.consul.client
+ ${project.version}
+ compile
+
+
+ org.eclipse.slm
+ common.keycloak.config
+ ${project.version}
+ compile
+
+
+ org.eclipse.slm
+ common.vault.client
+ ${project.version}
+ compile
+
+
+ org.eclipse.slm
+ common.restserver
+ ${project.version}
+ compile
+
+
+ org.eclipse.slm
+ platform_management.features.user_management.api
+ ${project.version}
+ compile
+
+
+
+
+
diff --git a/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.impl/src/main/java/org/eclipse/slm/platform_management/features/user_management/impl/LastAdminDeletionNotAllowedException.kt b/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.impl/src/main/java/org/eclipse/slm/platform_management/features/user_management/impl/LastAdminDeletionNotAllowedException.kt
new file mode 100644
index 000000000..0433cce0b
--- /dev/null
+++ b/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.impl/src/main/java/org/eclipse/slm/platform_management/features/user_management/impl/LastAdminDeletionNotAllowedException.kt
@@ -0,0 +1,8 @@
+package org.eclipse.slm.platform_management.features.user_management.impl
+
+import org.springframework.http.HttpStatus
+import org.springframework.web.bind.annotation.ResponseStatus
+
+@ResponseStatus(HttpStatus.CONFLICT)
+class LastAdminDeletionNotAllowedException(message: String) : RuntimeException(message)
+
diff --git a/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.impl/src/main/java/org/eclipse/slm/platform_management/features/user_management/impl/UserManagementRestController.java b/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.impl/src/main/java/org/eclipse/slm/platform_management/features/user_management/impl/UserManagementRestController.java
new file mode 100644
index 000000000..7572922d5
--- /dev/null
+++ b/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.impl/src/main/java/org/eclipse/slm/platform_management/features/user_management/impl/UserManagementRestController.java
@@ -0,0 +1,55 @@
+package org.eclipse.slm.platform_management.features.user_management.impl;
+
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import org.eclipse.slm.common.restserver.annotations.AuthorizedAsSlmAdminOrApiKey;
+import org.eclipse.slm.platform_management.features.user_management.api.UserCreateRequest;
+import org.eclipse.slm.platform_management.features.user_management.api.UserManagementRestApi;
+import org.eclipse.slm.platform_management.features.user_management.api.UserManagementRestApiConfig;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@RequestMapping(UserManagementRestApiConfig.BASE_PATH)
+@Tag(name = UserManagementRestApiConfig.TAG)
+public class UserManagementRestController implements UserManagementRestApi {
+
+ private final UserManager userManager;
+
+ public UserManagementRestController(UserManager userManager) {
+ this.userManager = userManager;
+ }
+
+ @Override
+ @AuthorizedAsSlmAdminOrApiKey
+ public ResponseEntity>> getUsers() {
+ return ResponseEntity.ok(this.userManager.getUsers());
+ }
+
+ @Override
+ @AuthorizedAsSlmAdminOrApiKey
+ public ResponseEntity createUser(@Valid @RequestBody UserCreateRequest userCreateRequest) {
+ this.userManager.createUser(userCreateRequest);
+ return ResponseEntity.ok().build();
+ }
+
+ @Override
+ @AuthorizedAsSlmAdminOrApiKey
+ public ResponseEntity makeUserAdmin(String username) {
+ this.userManager.makeUserAdmin(username);
+ return ResponseEntity.ok().build();
+ }
+
+ @Override
+ @AuthorizedAsSlmAdminOrApiKey
+ public ResponseEntity deleteUser(String username) {
+ this.userManager.deleteUser(username);
+ return ResponseEntity.ok().build();
+ }
+}
+
diff --git a/platform_management/platform_management.service/platform_management.service.app/src/main/java/org/eclipse/slm/platform_management/service/app/users/UserManagementRuntimeException.kt b/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.impl/src/main/java/org/eclipse/slm/platform_management/features/user_management/impl/UserManagementRuntimeException.kt
similarity index 70%
rename from platform_management/platform_management.service/platform_management.service.app/src/main/java/org/eclipse/slm/platform_management/service/app/users/UserManagementRuntimeException.kt
rename to platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.impl/src/main/java/org/eclipse/slm/platform_management/features/user_management/impl/UserManagementRuntimeException.kt
index 12c928820..44fcc2766 100644
--- a/platform_management/platform_management.service/platform_management.service.app/src/main/java/org/eclipse/slm/platform_management/service/app/users/UserManagementRuntimeException.kt
+++ b/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.impl/src/main/java/org/eclipse/slm/platform_management/features/user_management/impl/UserManagementRuntimeException.kt
@@ -1,6 +1,7 @@
-package org.eclipse.slm.platform_management.service.app.users
+package org.eclipse.slm.platform_management.features.user_management.impl
class UserManagementRuntimeException : RuntimeException {
constructor(message: String) : super(message)
constructor(message: String, cause: Throwable) : super(message, cause)
-}
\ No newline at end of file
+}
+
diff --git a/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.impl/src/main/java/org/eclipse/slm/platform_management/features/user_management/impl/UserManager.java b/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.impl/src/main/java/org/eclipse/slm/platform_management/features/user_management/impl/UserManager.java
new file mode 100644
index 000000000..8a1d8fc27
--- /dev/null
+++ b/platform_management/platform_management.features/platform_management.features.user_management/platform_management.features.user_management.impl/src/main/java/org/eclipse/slm/platform_management/features/user_management/impl/UserManager.java
@@ -0,0 +1,18 @@
+package org.eclipse.slm.platform_management.features.user_management.impl;
+
+import org.eclipse.slm.platform_management.features.user_management.api.UserCreateRequest;
+
+import java.util.List;
+import java.util.Map;
+
+public interface UserManager {
+
+ void createUser(UserCreateRequest userCreateRequest);
+
+ List