1
0
forked from baron/baron-sso

일반 사용자의 DevFront 접근 및 RP 관리자 권한 연동

This commit is contained in:
2026-04-20 14:16:24 +09:00
parent 51e46a4d00
commit e15de6d334
12 changed files with 570 additions and 182 deletions

View File

@@ -199,6 +199,7 @@ var allowedRelyingPartyOperatorRelations = map[string]struct{}{
"admins": {},
"creator": {},
"config_editor": {},
"secret_viewer": {},
"secret_rotator": {},
"jwks_viewer": {},
"jwks_operator": {},
@@ -215,7 +216,7 @@ func normalizeUserRole(role string) string {
func isDevConsoleRoleAllowed(role string) bool {
switch normalizeUserRole(role) {
case domain.RoleSuperAdmin, domain.RoleTenantAdmin, domain.RoleRPAdmin:
case domain.RoleSuperAdmin, domain.RoleTenantAdmin, domain.RoleRPAdmin, domain.RoleUser:
return true
default:
return false
@@ -372,6 +373,32 @@ func (h *DevHandler) canOperateClientByPermit(c *fiber.Ctx, profile *domain.User
return err == nil && allowed
}
func (h *DevHandler) canViewClientSecret(c *fiber.Ctx, profile *domain.UserProfileResponse, summary clientSummary) bool {
if canAccessClientByLegacyScope(profile, summary) {
return true
}
return h.canOperateClientByPermit(c, profile, summary, "view_secret")
}
func (h *DevHandler) canBypassPrivateClientRestriction(c *fiber.Ctx, profile *domain.UserProfileResponse, summary clientSummary, relation string) bool {
if h.canOperateClientByPermit(c, profile, summary, relation) {
return true
}
allowed, err := h.checkAppManagerPermission(c)
return err == nil && allowed
}
func (h *DevHandler) redactClientSecretUnlessAllowed(c *fiber.Ctx, profile *domain.UserProfileResponse, summary clientSummary) clientSummary {
if summary.ClientSecret == "" {
return summary
}
if h.canViewClientSecret(c, profile, summary) {
return summary
}
summary.ClientSecret = ""
return summary
}
func (h *DevHandler) canViewClientRelations(c *fiber.Ctx, profile *domain.UserProfileResponse, summary clientSummary) bool {
if h.canOperateClientByPermit(c, profile, summary, "view_relationships") {
return true
@@ -474,7 +501,11 @@ func resolveClientTenantID(summary clientSummary) string {
}
func isRPAdminClientAllowed(profile *domain.UserProfileResponse, clientID string) bool {
if normalizeUserRole(profileRole(profile)) != domain.RoleRPAdmin {
role := normalizeUserRole(profileRole(profile))
if role == domain.RoleUser {
return false
}
if role != domain.RoleRPAdmin {
return true
}
allowed := managedClientIDsFromProfile(profile)
@@ -665,7 +696,11 @@ func (h *DevHandler) SearchUsers(c *fiber.Ctx) error {
return errorJSON(c, fiber.StatusUnauthorized, "unauthorized: authentication required")
}
if !isDevConsoleRoleAllowed(normalizeUserRole(profile.Role)) {
return errorJSON(c, fiber.StatusForbidden, "forbidden")
clientID := strings.TrimSpace(c.Query("clientId"))
summary, err := h.loadClientSummary(c.Context(), clientID)
if clientID == "" || err != nil || !h.canManageClientRelations(c, profile, summary) {
return errorJSON(c, fiber.StatusForbidden, "forbidden")
}
}
if h.KratosAdmin == nil {
return errorJSON(c, fiber.StatusServiceUnavailable, "kratos admin unavailable")
@@ -993,7 +1028,7 @@ func (h *DevHandler) ListClients(c *fiber.Ctx) error {
return errorJSON(c, fiber.StatusUnauthorized, "unauthorized: authentication required")
}
role := normalizeUserRole(profile.Role)
if !isDevConsoleViewerRole(role) {
if !isDevConsoleRoleAllowed(role) {
return errorJSON(c, fiber.StatusForbidden, "forbidden")
}
@@ -1054,7 +1089,7 @@ func (h *DevHandler) ListClients(c *fiber.Ctx) error {
continue
}
items = append(items, summary)
items = append(items, h.redactClientSecretUnlessAllowed(c, profile, summary))
}
return c.JSON(clientListResponse{
@@ -1240,6 +1275,7 @@ func (h *DevHandler) GetClient(c *fiber.Ctx) error {
}
cacheState, _ := h.publicHeadlessJWKSCacheState(summary.ID)
summary = h.redactClientSecretUnlessAllowed(c, profile, summary)
return c.JSON(clientDetailResponse{
Client: summary,
@@ -1318,17 +1354,17 @@ func (h *DevHandler) UpdateClientStatus(c *fiber.Ctx) error {
return errorJSON(c, fiber.StatusUnauthorized, "unauthorized: authentication required")
}
role := normalizeUserRole(profile.Role)
if !isDevConsoleRoleAllowed(role) {
if !isDevConsoleViewerRole(role) {
return errorJSON(c, fiber.StatusForbidden, "forbidden")
}
if !canAccessClientByLegacyScope(profile, summary) && !h.canOperateClientByPermit(c, profile, summary, "change_status") {
canChangeStatusByPermit := h.canOperateClientByPermit(c, profile, summary, "change_status")
if !canAccessClientByLegacyScope(profile, summary) && !canChangeStatusByPermit {
return errorJSON(c, fiber.StatusForbidden, "forbidden: rp_admin scope does not include this client")
}
if summary.Type == "private" {
isAppManager, _ := h.checkAppManagerPermission(c)
if !isAppManager {
if summary.Type == "private" && !h.canBypassPrivateClientRestriction(c, profile, summary, "change_status") {
if !canChangeStatusByPermit {
return errorJSON(c, fiber.StatusForbidden, "forbidden: insufficient permissions for private client")
}
}
@@ -1352,6 +1388,7 @@ func (h *DevHandler) UpdateClientStatus(c *fiber.Ctx) error {
}
updatedSummary := h.mapClientSummary(*updated)
updatedSummary = h.redactClientSecretUnlessAllowed(c, profile, updatedSummary)
cacheState, _ := h.publicHeadlessJWKSCacheState(updatedSummary.ID)
return c.JSON(clientDetailResponse{
Client: updatedSummary,
@@ -1566,7 +1603,7 @@ func (h *DevHandler) UpdateClient(c *fiber.Ctx) error {
return errorJSON(c, fiber.StatusUnauthorized, "unauthorized: authentication required")
}
role := normalizeUserRole(profile.Role)
if !isDevConsoleRoleAllowed(role) {
if !isDevConsoleViewerRole(role) {
return errorJSON(c, fiber.StatusForbidden, "forbidden")
}
@@ -1584,11 +1621,7 @@ func (h *DevHandler) UpdateClient(c *fiber.Ctx) error {
// [Security] Check permission for private clients (both current and new type)
if currentSummary.Type == "private" || clientType == "private" {
isAppManager, err := h.checkAppManagerPermission(c)
if err != nil {
return errorJSON(c, fiber.StatusInternalServerError, "permission check error")
}
if !isAppManager {
if !h.canBypassPrivateClientRestriction(c, profile, currentSummary, "edit_config") {
return errorJSON(c, fiber.StatusForbidden, "forbidden: insufficient permissions for private client")
}
}
@@ -1736,7 +1769,7 @@ func (h *DevHandler) DeleteClient(c *fiber.Ctx) error {
return errorJSON(c, fiber.StatusUnauthorized, "unauthorized: authentication required")
}
role := normalizeUserRole(profile.Role)
if !isDevConsoleRoleAllowed(role) {
if !isDevConsoleViewerRole(role) {
return errorJSON(c, fiber.StatusForbidden, "forbidden")
}
@@ -1746,8 +1779,7 @@ func (h *DevHandler) DeleteClient(c *fiber.Ctx) error {
// [Security] Check permission for private clients
if summary.Type == "private" {
isAppManager, _ := h.checkAppManagerPermission(c)
if !isAppManager {
if !h.canBypassPrivateClientRestriction(c, profile, summary, "manage") {
return errorJSON(c, fiber.StatusForbidden, "forbidden: insufficient permissions for private client")
}
}
@@ -1986,7 +2018,7 @@ func (h *DevHandler) RotateClientSecret(c *fiber.Ctx) error {
return errorJSON(c, fiber.StatusUnauthorized, "unauthorized: authentication required")
}
role := normalizeUserRole(profile.Role)
if !isDevConsoleRoleAllowed(role) {
if !isDevConsoleViewerRole(role) {
return errorJSON(c, fiber.StatusForbidden, "forbidden")
}
@@ -1997,8 +2029,7 @@ func (h *DevHandler) RotateClientSecret(c *fiber.Ctx) error {
// [Security] Check permission for private clients
if summary.Type == "private" {
isAppManager, _ := h.checkAppManagerPermission(c)
if !isAppManager {
if !h.canBypassPrivateClientRestriction(c, profile, summary, "rotate_secret") {
return errorJSON(c, fiber.StatusForbidden, "forbidden: insufficient permissions for private client")
}
}
@@ -2076,7 +2107,7 @@ func (h *DevHandler) RefreshHeadlessJWKSCache(c *fiber.Ctx) error {
return errorJSON(c, fiber.StatusUnauthorized, "unauthorized: authentication required")
}
role := normalizeUserRole(profile.Role)
if !isDevConsoleRoleAllowed(role) {
if !isDevConsoleViewerRole(role) {
return errorJSON(c, fiber.StatusForbidden, "forbidden")
}
if !canAccessClientByLegacyScope(profile, summary) && !h.canOperateClientByPermit(c, profile, summary, "operate_jwks") {
@@ -2141,18 +2172,10 @@ func (h *DevHandler) RevokeHeadlessJWKSCache(c *fiber.Ctx) error {
return errorJSON(c, fiber.StatusUnauthorized, "unauthorized: authentication required")
}
role := normalizeUserRole(profile.Role)
if !isDevConsoleRoleAllowed(role) {
if !isDevConsoleViewerRole(role) {
return errorJSON(c, fiber.StatusForbidden, "forbidden")
}
isSuperAdmin := role == domain.RoleSuperAdmin
userTenantID := tenantIDFromProfile(profile)
if !isSuperAdmin {
clientTenantID := resolveClientTenantID(summary)
if clientTenantID != userTenantID {
return errorJSON(c, fiber.StatusForbidden, "forbidden: access denied to client in another tenant")
}
}
if !isRPAdminClientAllowed(profile, summary.ID) {
if !canAccessClientByLegacyScope(profile, summary) && !h.canOperateClientByPermit(c, profile, summary, "operate_jwks") {
return errorJSON(c, fiber.StatusForbidden, "forbidden: rp_admin scope does not include this client")
}