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
105 changes: 76 additions & 29 deletions src/main/java/apu/saerok_admin/infra/CurrentAdminClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.slf4j.Logger;
Expand All @@ -22,12 +21,6 @@
public class CurrentAdminClient {

private static final Logger log = LoggerFactory.getLogger(CurrentAdminClient.class);
private static final Map<String, String> ROLE_DESCRIPTION_MAP = Map.of(
"ADMIN_VIEWER", "열람자",
"ADMIN_EDITOR", "운영자"
);
private static final String UNKNOWN_ROLE_DESCRIPTION = "알 수 없는 관리자 권한";

private final RestClient saerokRestClient;
private final List<String> missingPrefixSegments;
private final LoginSessionManager loginSessionManager;
Expand Down Expand Up @@ -57,14 +50,19 @@ public Optional<CurrentAdminProfile> fetchCurrentAdminProfile() {
return Optional.empty();
}

List<String> roles = response.roles() != null ? List.copyOf(response.roles()) : List.of();
List<String> backendRoles = response.roles() != null ? List.copyOf(response.roles()) : List.of();
AdminRoleInfo roleInfo = fetchAdminRoleInfo();
List<String> roleCodes = !roleInfo.roleCodes().isEmpty()
? roleInfo.roleCodes()
: normalizeRoleCodes(backendRoles);

return Optional.of(new CurrentAdminProfile(
response.nickname(),
response.email(),
response.profileImageUrl(),
toRoleDescriptions(roles),
normalizeRoleCodes(roles)
roleInfo.roleDisplayNames(),
roleCodes,
roleInfo.permissionKeys()
));
} catch (RestClientResponseException exception) {
log.warn(
Expand Down Expand Up @@ -96,30 +94,79 @@ private record BackendUserProfileResponse(
) {
}

private List<String> toRoleDescriptions(List<String> roles) {
if (roles == null || roles.isEmpty()) {
return List.of();
}

Set<String> descriptions = new LinkedHashSet<>();
for (String role : roles) {
if (!StringUtils.hasText(role)) {
continue;
}
private AdminRoleInfo fetchAdminRoleInfo() {
try {
AdminMyRoleResponse response = saerokRestClient.get()
.uri(uriBuilder -> buildUri(uriBuilder, "admin", "role", "me"))
.retrieve()
.body(AdminMyRoleResponse.class);

String normalized = role.toUpperCase(Locale.ROOT);
String description = ROLE_DESCRIPTION_MAP.get(normalized);
if (description != null) {
descriptions.add(description);
continue;
if (response == null) {
return AdminRoleInfo.empty();
}

if (normalized.startsWith("ADMIN_")) {
descriptions.add(UNKNOWN_ROLE_DESCRIPTION);
}
List<String> roleDisplayNames = response.roles() == null
? List.of()
: response.roles().stream()
.map(RoleSummaryResponse::displayName)
.filter(StringUtils::hasText)
.map(String::trim)
.toList();
List<String> roleCodes = response.roles() == null
? List.of()
: response.roles().stream()
.map(RoleSummaryResponse::code)
.filter(StringUtils::hasText)
.toList();
List<String> permissionKeys = response.permissions() == null
? List.of()
: response.permissions().stream()
.map(PermissionSummaryResponse::key)
.filter(StringUtils::hasText)
.toList();
return new AdminRoleInfo(roleDisplayNames, roleCodes, permissionKeys);
} catch (RestClientResponseException exception) {
log.warn(
"Failed to fetch current admin roles. status={}, body={}",
exception.getStatusCode(),
exception.getResponseBodyAsString(),
exception
);
} catch (RestClientException exception) {
log.warn("Failed to fetch current admin roles.", exception);
}
return AdminRoleInfo.empty();
}

private record AdminMyRoleResponse(
List<RoleSummaryResponse> roles,
List<PermissionSummaryResponse> permissions
) {
}

return descriptions.isEmpty() ? List.of() : List.copyOf(descriptions);
private record RoleSummaryResponse(
Long id,
String code,
String displayName,
String description,
Boolean builtin
) {
}

private record PermissionSummaryResponse(
String key,
String description
) {
}

private record AdminRoleInfo(
List<String> roleDisplayNames,
List<String> roleCodes,
List<String> permissionKeys
) {
private static AdminRoleInfo empty() {
return new AdminRoleInfo(List.of(), List.of(), List.of());
}
}

private List<String> normalizeRoleCodes(List<String> roles) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
public class BackendAuthClient {

private static final Logger log = LoggerFactory.getLogger(BackendAuthClient.class);
private static final String ADMIN_CHANNEL = "admin";

private final RestClient authRestClient;
private final List<String> missingPrefixSegments;
Expand All @@ -39,10 +38,10 @@ public BackendAuthClient(
}

public LoginSuccess kakaoLogin(String authorizationCode) {
KakaoLoginPayload payload = new KakaoLoginPayload(authorizationCode, ADMIN_CHANNEL);
KakaoLoginPayload payload = new KakaoLoginPayload(authorizationCode);
log.info("Requesting Kakao login from backend with authorization code length {}", authorizationCode == null ? 0 : authorizationCode.length());
ResponseEntity<BackendAccessTokenResponse> response = authRestClient.post()
.uri(uriBuilder -> buildUri(uriBuilder, "auth", "kakao", "login"))
.uri(uriBuilder -> buildUri(uriBuilder, "admin", "auth", "kakao", "login"))
.contentType(MediaType.APPLICATION_JSON)
.body(payload)
.retrieve()
Expand Down Expand Up @@ -113,6 +112,10 @@ private URI buildUri(UriBuilder builder, String... segments) {
}

private record KakaoLoginPayload(String authorizationCode, String channel) {

private KakaoLoginPayload(String authorizationCode) {
this(authorizationCode, "admin");
}
}

private record AppleLoginPayload(String authorizationCode) {
Expand Down
128 changes: 128 additions & 0 deletions src/main/java/apu/saerok_admin/infra/role/AdminRoleClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package apu.saerok_admin.infra.role;

import apu.saerok_admin.infra.SaerokApiProps;
import apu.saerok_admin.infra.role.dto.AdminMyRoleResponse;
import apu.saerok_admin.infra.role.dto.AdminRoleListResponse;
import apu.saerok_admin.infra.role.dto.AdminRoleUserListResponse;
import apu.saerok_admin.infra.role.dto.AdminUserRoleResponse;
import apu.saerok_admin.infra.role.dto.AssignRoleRequest;
import apu.saerok_admin.infra.role.dto.CreateRoleRequest;
import apu.saerok_admin.infra.role.dto.RoleDetailResponse;
import apu.saerok_admin.infra.role.dto.UpdateRolePermissionsRequest;
import java.net.URI;
import java.util.List;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;
import org.springframework.web.util.UriBuilder;

@Component
public class AdminRoleClient {

private static final String[] ADMIN_ROLE_SEGMENTS = {"admin", "role"};

private final RestClient saerokRestClient;
private final String[] missingPrefixSegments;

public AdminRoleClient(RestClient saerokRestClient, SaerokApiProps saerokApiProps) {
this.saerokRestClient = saerokRestClient;
List<String> missing = saerokApiProps.missingPrefixSegments();
this.missingPrefixSegments = missing.toArray(new String[0]);
}

public AdminMyRoleResponse getMyRoles() {
return get(AdminMyRoleResponse.class, "me");
}

public AdminRoleUserListResponse listAdminUsers() {
return get(AdminRoleUserListResponse.class, "users");
}

public AdminRoleListResponse listRoles() {
return get(AdminRoleListResponse.class);
}

public RoleDetailResponse createRole(CreateRoleRequest request) {
return post(RoleDetailResponse.class, request);
}

public RoleDetailResponse updateRolePermissions(String roleCode, UpdateRolePermissionsRequest request) {
return put(RoleDetailResponse.class, request, roleCode, "permissions");
}

public void deleteRole(String roleCode) {
deleteVoid(roleCode);
}

public AdminUserRoleResponse grantRole(Long userId, AssignRoleRequest request) {
return post(AdminUserRoleResponse.class, request, "users", userId.toString(), "roles");
}

public AdminUserRoleResponse revokeRole(Long userId, String roleCode) {
return delete(AdminUserRoleResponse.class, "users", userId.toString(), "roles", roleCode);
}

private <T> T get(Class<T> responseType, String... segments) {
T response = saerokRestClient.get()
.uri(uriBuilder -> buildUri(uriBuilder, segments))
.retrieve()
.body(responseType);
if (response == null) {
throw new IllegalStateException("Empty response from admin role API");
}
return response;
}

private <T> T post(Class<T> responseType, Object body, String... segments) {
T response = saerokRestClient.post()
.uri(uriBuilder -> buildUri(uriBuilder, segments))
.body(body)
.retrieve()
.body(responseType);
if (response == null) {
throw new IllegalStateException("Empty response from admin role API");
}
return response;
}

private <T> T put(Class<T> responseType, Object body, String... segments) {
T response = saerokRestClient.method(HttpMethod.PUT)
.uri(uriBuilder -> buildUri(uriBuilder, segments))
.body(body)
.retrieve()
.body(responseType);
if (response == null) {
throw new IllegalStateException("Empty response from admin role API");
}
return response;
}

private <T> T delete(Class<T> responseType, String... segments) {
T response = saerokRestClient.delete()
.uri(uriBuilder -> buildUri(uriBuilder, segments))
.retrieve()
.body(responseType);
if (response == null) {
throw new IllegalStateException("Empty response from admin role API");
}
return response;
}

private void deleteVoid(String... segments) {
saerokRestClient.delete()
.uri(uriBuilder -> buildUri(uriBuilder, segments))
.retrieve()
.toBodilessEntity();
}

private URI buildUri(UriBuilder builder, String... segments) {
if (missingPrefixSegments.length > 0) {
builder.pathSegment(missingPrefixSegments);
}
builder.pathSegment(ADMIN_ROLE_SEGMENTS);
if (segments.length > 0) {
builder.pathSegment(segments);
}
return builder.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package apu.saerok_admin.infra.role.dto;

import java.util.List;

public record AdminMyRoleResponse(
List<RoleSummaryResponse> roles,
List<PermissionSummaryResponse> permissions
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package apu.saerok_admin.infra.role.dto;

import java.util.List;

public record AdminRoleListResponse(
List<RoleDetailResponse> roles
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package apu.saerok_admin.infra.role.dto;

import java.util.List;

public record AdminRoleUserListResponse(
List<AdminUserRoleResponse> users
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package apu.saerok_admin.infra.role.dto;

import java.util.List;

public record AdminUserRoleResponse(
Long userId,
String nickname,
String email,
boolean superAdmin,
List<RoleSummaryResponse> roles,
List<PermissionSummaryResponse> permissions
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package apu.saerok_admin.infra.role.dto;

public record AssignRoleRequest(
String roleCode
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package apu.saerok_admin.infra.role.dto;

public record CreateRoleRequest(
String code,
String displayName,
String description
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package apu.saerok_admin.infra.role.dto;

public record PermissionSummaryResponse(
String key,
String description
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package apu.saerok_admin.infra.role.dto;

import java.util.List;

public record RoleDetailResponse(
Long id,
String code,
String displayName,
String description,
boolean builtin,
List<PermissionSummaryResponse> permissions
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package apu.saerok_admin.infra.role.dto;

public record RoleSummaryResponse(
Long id,
String code,
String displayName,
String description,
boolean builtin
) {
}
Loading