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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ English: [CHANGELOG.md](CHANGELOG.md)

## [Unreleased]

## [0.4.2] — 2026-06-03

### Added
- **사용자의 역할·그룹 조회.** `GET /admin/api/v1/users/{id}/roles`,
`GET /admin/api/v1/users/{id}/groups` 가 사용자에게 부여된 역할/그룹 id 를 반환합니다.
이로써 admin 콘솔이 **사용자 화면에서** 그 사용자의 접근권한을 관리할 수 있습니다(부여/회수는
기존대로 역할·그룹 리소스에: `POST/DELETE /roles/{roleId}/users/{userId}`,
`/groups/{groupId}/members/{userId}`). 안정적인 `[{ "value": "<uuid>" }]` 배열로 반환.

## [0.4.1] — 2026-06-03

### Fixed
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ The library major aligns with the Spring Boot major: `4.x.y` targets Spring Boot

## [Unreleased]

## [0.4.2] — 2026-06-03

### Added
- **List a user's roles and groups.** `GET /admin/api/v1/users/{id}/roles` and
`GET /admin/api/v1/users/{id}/groups` return the role / group ids assigned to a user, so the
admin console can manage a user's access from the user side (assign/revoke continue to live
on the role and group resources: `POST/DELETE /roles/{roleId}/users/{userId}` and
`/groups/{groupId}/members/{userId}`). Returned as a stable `[{ "value": "<uuid>" }]` array.

## [0.4.1] — 2026-06-03

### Fixed
Expand Down
4 changes: 2 additions & 2 deletions README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
**Gradle (Kotlin DSL)**

```kotlin
implementation("kr.devslab:devslab-kit-spring-boot-starter:0.4.1")
implementation("kr.devslab:devslab-kit-spring-boot-starter:0.4.2")
```

**Maven**
Expand All @@ -72,7 +72,7 @@ implementation("kr.devslab:devslab-kit-spring-boot-starter:0.4.1")
<dependency>
<groupId>kr.devslab</groupId>
<artifactId>devslab-kit-spring-boot-starter</artifactId>
<version>0.4.1</version>
<version>0.4.2</version>
</dependency>
```

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ specific product's domain.
**Gradle (Kotlin DSL)**

```kotlin
implementation("kr.devslab:devslab-kit-spring-boot-starter:0.4.1")
implementation("kr.devslab:devslab-kit-spring-boot-starter:0.4.2")
```

**Maven**
Expand All @@ -74,7 +74,7 @@ implementation("kr.devslab:devslab-kit-spring-boot-starter:0.4.1")
<dependency>
<groupId>kr.devslab</groupId>
<artifactId>devslab-kit-spring-boot-starter</artifactId>
<version>0.4.1</version>
<version>0.4.2</version>
</dependency>
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
import jakarta.validation.Valid;
import java.util.List;
import java.util.UUID;
import kr.devslab.kit.access.core.service.GroupMembershipService;
import kr.devslab.kit.access.core.service.UserRoleService;
import kr.devslab.kit.admin.AdminApiPaths;
import kr.devslab.kit.core.id.GroupId;
import kr.devslab.kit.core.id.RoleId;
import kr.devslab.kit.core.id.TenantId;
import kr.devslab.kit.core.id.UserId;
import kr.devslab.kit.identity.UserAccountView;
Expand All @@ -26,13 +30,19 @@ public class UserAdminController {

private final PlatformUserAccountAdminService adminService;
private final PlatformUserAccountService readService;
private final UserRoleService userRoles;
private final GroupMembershipService groupMemberships;

public UserAdminController(
PlatformUserAccountAdminService adminService,
PlatformUserAccountService readService
PlatformUserAccountService readService,
UserRoleService userRoles,
GroupMembershipService groupMemberships
) {
this.adminService = adminService;
this.readService = readService;
this.userRoles = userRoles;
this.groupMemberships = groupMemberships;
}

@PostMapping
Expand All @@ -59,6 +69,20 @@ public List<UserAccountView> list(@RequestParam String tenantId) {
return adminService.listByTenant(TenantId.of(tenantId));
}

/** Roles assigned directly to this user. Assign/revoke live on the role resource
* ({@code POST/DELETE /roles/{roleId}/users/{userId}}). */
@GetMapping("/{id}/roles")
public List<RoleId> roles(@PathVariable UUID id) {
return userRoles.findRoleIdsForUser(UserId.of(id));
}

/** Groups this user belongs to. Membership is managed on the group resource
* ({@code POST/DELETE /groups/{groupId}/members/{userId}}). */
@GetMapping("/{id}/groups")
public List<GroupId> groups(@PathVariable UUID id) {
return groupMemberships.findGroupsForUser(UserId.of(id));
}

@PutMapping("/{id}/lock")
public ResponseEntity<Void> lock(@PathVariable UUID id) {
adminService.lock(UserId.of(id));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,32 @@ void authenticatedCallerWithoutThePermissionIsForbidden() throws Exception {
assertThat(response.statusCode()).isEqualTo(403);
}

@Test
void exposesAUsersRolesAndGroups() throws Exception {
String adminToken = login("admin", "admin");

// Resolve the seeded admin user's id from the user list.
HttpResponse<String> users = getWithToken(USERS, adminToken);
assertThat(users.statusCode()).isEqualTo(200);
String adminId = null;
for (var node : objectMapper.readTree(users.body())) {
if ("admin".equals(node.get("loginId").asText())) {
adminId = node.get("id").get("value").asText();
break;
}
}
assertThat(adminId).isNotNull();

// New endpoints: a user's roles and groups, returned as a stable [{value}] array.
HttpResponse<String> roles = getWithToken("/admin/api/v1/users/" + adminId + "/roles", adminToken);
assertThat(roles.statusCode()).isEqualTo(200);
assertThat(objectMapper.readTree(roles.body()).isArray()).isTrue();

HttpResponse<String> groups = getWithToken("/admin/api/v1/users/" + adminId + "/groups", adminToken);
assertThat(groups.statusCode()).isEqualTo(200);
assertThat(objectMapper.readTree(groups.body()).isArray()).isTrue();
}

private String login(String loginId, String password) throws Exception {
HttpResponse<String> response = post(LOGIN, loginBody(loginId, password));
assertThat(response.statusCode()).isEqualTo(200);
Expand Down
12 changes: 6 additions & 6 deletions docs/getting-started/installation.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@
=== "Gradle (Kotlin DSL)"

```kotlin
implementation("kr.devslab:devslab-kit-spring-boot-starter:0.4.1")
implementation("kr.devslab:devslab-kit-spring-boot-starter:0.4.2")
```

=== "Gradle (Groovy)"

```groovy
implementation 'kr.devslab:devslab-kit-spring-boot-starter:0.4.1'
implementation 'kr.devslab:devslab-kit-spring-boot-starter:0.4.2'
```

=== "Maven"
Expand All @@ -32,7 +32,7 @@
<dependency>
<groupId>kr.devslab</groupId>
<artifactId>devslab-kit-spring-boot-starter</artifactId>
<version>0.4.1</version>
<version>0.4.2</version>
</dependency>
```

Expand All @@ -43,10 +43,10 @@
물러납니다(`@ConditionalOnMissingBean`).

```kotlin
implementation("kr.devslab:devslab-kit-access-core:0.4.1") // RBAC + 그룹 + ABAC
implementation("kr.devslab:devslab-kit-cache-core:0.4.1") // 플러그형 캐시
implementation("kr.devslab:devslab-kit-access-core:0.4.2") // RBAC + 그룹 + ABAC
implementation("kr.devslab:devslab-kit-cache-core:0.4.2") // 플러그형 캐시
// …또는 계약만:
implementation("kr.devslab:devslab-kit-access-api:0.4.1")
implementation("kr.devslab:devslab-kit-access-api:0.4.2")
```

동작하는 앱을 부팅하려면 [빠른 시작](quick-start.md)을 참고하세요.
Expand Down
12 changes: 6 additions & 6 deletions docs/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ whole platform; depend on individual modules only if you want à la carte.
=== "Gradle (Kotlin DSL)"

```kotlin
implementation("kr.devslab:devslab-kit-spring-boot-starter:0.4.1")
implementation("kr.devslab:devslab-kit-spring-boot-starter:0.4.2")
```

=== "Gradle (Groovy)"

```groovy
implementation 'kr.devslab:devslab-kit-spring-boot-starter:0.4.1'
implementation 'kr.devslab:devslab-kit-spring-boot-starter:0.4.2'
```

=== "Maven"
Expand All @@ -32,7 +32,7 @@ whole platform; depend on individual modules only if you want à la carte.
<dependency>
<groupId>kr.devslab</groupId>
<artifactId>devslab-kit-spring-boot-starter</artifactId>
<version>0.4.1</version>
<version>0.4.2</version>
</dependency>
```

Expand All @@ -44,10 +44,10 @@ your own — the auto-configuration backs off (`@ConditionalOnMissingBean`) when
do.

```kotlin
implementation("kr.devslab:devslab-kit-access-core:0.4.1") // RBAC + groups + ABAC
implementation("kr.devslab:devslab-kit-cache-core:0.4.1") // pluggable cache
implementation("kr.devslab:devslab-kit-access-core:0.4.2") // RBAC + groups + ABAC
implementation("kr.devslab:devslab-kit-cache-core:0.4.2") // pluggable cache
// …or just the contract:
implementation("kr.devslab:devslab-kit-access-api:0.4.1")
implementation("kr.devslab:devslab-kit-access-api:0.4.2")
```

See [Quick Start](quick-start.md) to boot a working app.
Expand Down
4 changes: 2 additions & 2 deletions docs/reference/configuration.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@
=== "Gradle (Kotlin DSL)"

```kotlin
implementation("kr.devslab:devslab-kit-spring-boot-starter:0.4.1") {
implementation("kr.devslab:devslab-kit-spring-boot-starter:0.4.2") {
exclude(group = "org.springdoc")
}
```
Expand All @@ -132,7 +132,7 @@
<dependency>
<groupId>kr.devslab</groupId>
<artifactId>devslab-kit-spring-boot-starter</artifactId>
<version>0.4.1</version>
<version>0.4.2</version>
<exclusions>
<exclusion>
<groupId>org.springdoc</groupId>
Expand Down
4 changes: 2 additions & 2 deletions docs/reference/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ Two ways to turn it off:
=== "Gradle (Kotlin DSL)"

```kotlin
implementation("kr.devslab:devslab-kit-spring-boot-starter:0.4.1") {
implementation("kr.devslab:devslab-kit-spring-boot-starter:0.4.2") {
exclude(group = "org.springdoc")
}
```
Expand All @@ -139,7 +139,7 @@ Two ways to turn it off:
<dependency>
<groupId>kr.devslab</groupId>
<artifactId>devslab-kit-spring-boot-starter</artifactId>
<version>0.4.1</version>
<version>0.4.2</version>
<exclusions>
<exclusion>
<groupId>org.springdoc</groupId>
Expand Down
Loading