forked from baron/baron-sso
ory용 MCP 제작, devfront/adminfront 백엔드 연결
This commit is contained in:
241
backend/internal/handler/dev_handler.go
Normal file
241
backend/internal/handler/dev_handler.go
Normal file
@@ -0,0 +1,241 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"baron-sso-backend/internal/service"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type DevHandler struct {
|
||||
Hydra *service.HydraAdminService
|
||||
}
|
||||
|
||||
func NewDevHandler() *DevHandler {
|
||||
return &DevHandler{
|
||||
Hydra: service.NewHydraAdminService(),
|
||||
}
|
||||
}
|
||||
|
||||
type clientSummary struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
CreatedAt *time.Time `json:"createdAt,omitempty"`
|
||||
RedirectURIs []string `json:"redirectUris"`
|
||||
Scopes []string `json:"scopes"`
|
||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
type clientListResponse struct {
|
||||
Items []clientSummary `json:"items"`
|
||||
Limit int `json:"limit"`
|
||||
Offset int `json:"offset"`
|
||||
}
|
||||
|
||||
type clientDetailResponse struct {
|
||||
Client clientSummary `json:"client"`
|
||||
Endpoints clientEndpoints `json:"endpoints"`
|
||||
}
|
||||
|
||||
type clientEndpoints struct {
|
||||
Discovery string `json:"discovery"`
|
||||
Issuer string `json:"issuer"`
|
||||
Authorization string `json:"authorization"`
|
||||
Token string `json:"token"`
|
||||
UserInfo string `json:"userinfo"`
|
||||
}
|
||||
|
||||
type consentSummary struct {
|
||||
Subject string `json:"subject"`
|
||||
ClientID string `json:"clientId"`
|
||||
ClientName string `json:"clientName,omitempty"`
|
||||
GrantedScopes []string `json:"grantedScopes"`
|
||||
AuthenticatedAt string `json:"authenticatedAt,omitempty"`
|
||||
}
|
||||
|
||||
type consentListResponse struct {
|
||||
Items []consentSummary `json:"items"`
|
||||
}
|
||||
|
||||
func (h *DevHandler) ListClients(c *fiber.Ctx) error {
|
||||
limit := c.QueryInt("limit", 50)
|
||||
offset := c.QueryInt("offset", 0)
|
||||
if limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
if offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
|
||||
clients, err := h.Hydra.ListClients(c.Context(), limit, offset)
|
||||
if err != nil {
|
||||
if errors.Is(err, service.ErrHydraNotFound) {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "clients not found"})
|
||||
}
|
||||
errMsg := err.Error()
|
||||
if strings.Contains(errMsg, "connection refused") || strings.Contains(errMsg, "dial tcp") {
|
||||
return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{
|
||||
"error": "Hydra service is unavailable. Please check if Ory Hydra is running.",
|
||||
})
|
||||
}
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": errMsg})
|
||||
}
|
||||
|
||||
items := make([]clientSummary, 0, len(clients))
|
||||
for _, client := range clients {
|
||||
items = append(items, mapClientSummary(client))
|
||||
}
|
||||
|
||||
return c.JSON(clientListResponse{
|
||||
Items: items,
|
||||
Limit: limit,
|
||||
Offset: offset,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *DevHandler) GetClient(c *fiber.Ctx) error {
|
||||
clientID := c.Params("id")
|
||||
if clientID == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "client id is required"})
|
||||
}
|
||||
|
||||
client, err := h.Hydra.GetClient(c.Context(), clientID)
|
||||
if err != nil {
|
||||
if errors.Is(err, service.ErrHydraNotFound) {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "client not found"})
|
||||
}
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
summary := mapClientSummary(*client)
|
||||
return c.JSON(clientDetailResponse{
|
||||
Client: summary,
|
||||
Endpoints: clientEndpoints{
|
||||
Discovery: strings.TrimRight(h.Hydra.PublicURL, "/") + "/.well-known/openid-configuration",
|
||||
Issuer: h.Hydra.PublicURL,
|
||||
Authorization: strings.TrimRight(h.Hydra.PublicURL, "/") + "/oauth2/auth",
|
||||
Token: strings.TrimRight(h.Hydra.PublicURL, "/") + "/oauth2/token",
|
||||
UserInfo: strings.TrimRight(h.Hydra.PublicURL, "/") + "/userinfo",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (h *DevHandler) UpdateClientStatus(c *fiber.Ctx) error {
|
||||
clientID := c.Params("id")
|
||||
if clientID == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "client id is required"})
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Status string `json:"status"`
|
||||
}
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid request body"})
|
||||
}
|
||||
|
||||
status := strings.ToLower(strings.TrimSpace(req.Status))
|
||||
if status != "active" && status != "inactive" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "status must be active or inactive"})
|
||||
}
|
||||
|
||||
updated, err := h.Hydra.PatchClientStatus(c.Context(), clientID, status)
|
||||
if err != nil {
|
||||
if errors.Is(err, service.ErrHydraNotFound) {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "client not found"})
|
||||
}
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
summary := mapClientSummary(*updated)
|
||||
return c.JSON(clientDetailResponse{
|
||||
Client: summary,
|
||||
Endpoints: clientEndpoints{
|
||||
Discovery: strings.TrimRight(h.Hydra.PublicURL, "/") + "/.well-known/openid-configuration",
|
||||
Issuer: h.Hydra.PublicURL,
|
||||
Authorization: strings.TrimRight(h.Hydra.PublicURL, "/") + "/oauth2/auth",
|
||||
Token: strings.TrimRight(h.Hydra.PublicURL, "/") + "/oauth2/token",
|
||||
UserInfo: strings.TrimRight(h.Hydra.PublicURL, "/") + "/userinfo",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (h *DevHandler) ListConsents(c *fiber.Ctx) error {
|
||||
subject := strings.TrimSpace(c.Query("subject"))
|
||||
if subject == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "subject is required"})
|
||||
}
|
||||
clientID := strings.TrimSpace(c.Query("client_id"))
|
||||
|
||||
sessions, err := h.Hydra.ListConsentSessions(c.Context(), subject, clientID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
items := make([]consentSummary, 0, len(sessions))
|
||||
for _, session := range sessions {
|
||||
authAt := ""
|
||||
if session.AuthenticatedAt != nil {
|
||||
authAt = session.AuthenticatedAt.Format(time.RFC3339)
|
||||
} else if session.RequestedAt != nil {
|
||||
authAt = session.RequestedAt.Format(time.RFC3339)
|
||||
}
|
||||
items = append(items, consentSummary{
|
||||
Subject: session.Subject,
|
||||
ClientID: session.Client.ClientID,
|
||||
ClientName: session.Client.ClientName,
|
||||
GrantedScopes: session.GrantedScope,
|
||||
AuthenticatedAt: authAt,
|
||||
})
|
||||
}
|
||||
|
||||
return c.JSON(consentListResponse{Items: items})
|
||||
}
|
||||
|
||||
func (h *DevHandler) RevokeConsents(c *fiber.Ctx) error {
|
||||
subject := strings.TrimSpace(c.Query("subject"))
|
||||
if subject == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "subject is required"})
|
||||
}
|
||||
clientID := strings.TrimSpace(c.Query("client_id"))
|
||||
|
||||
if err := h.Hydra.RevokeConsentSessions(c.Context(), subject, clientID); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
return c.SendStatus(fiber.StatusNoContent)
|
||||
}
|
||||
|
||||
func mapClientSummary(client service.HydraClient) clientSummary {
|
||||
status := "active"
|
||||
if client.Metadata != nil {
|
||||
if value, ok := client.Metadata["status"].(string); ok && strings.ToLower(value) == "inactive" {
|
||||
status = "inactive"
|
||||
}
|
||||
}
|
||||
|
||||
clientType := "confidential"
|
||||
if strings.EqualFold(client.TokenEndpointAuthMethod, "none") {
|
||||
clientType = "public"
|
||||
}
|
||||
|
||||
name := strings.TrimSpace(client.ClientName)
|
||||
if name == "" {
|
||||
name = client.ClientID
|
||||
}
|
||||
|
||||
scopes := strings.Fields(client.Scope)
|
||||
|
||||
return clientSummary{
|
||||
ID: client.ClientID,
|
||||
Name: name,
|
||||
Type: clientType,
|
||||
Status: status,
|
||||
RedirectURIs: client.RedirectURIs,
|
||||
Scopes: scopes,
|
||||
Metadata: client.Metadata,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user