package handler import ( "baron-sso-backend/internal/domain" "baron-sso-backend/internal/service" "log/slog" "github.com/gofiber/fiber/v2" ) type RelyingPartyHandler struct { Service service.RelyingPartyService UserSvc *service.KratosAdminService } func NewRelyingPartyHandler(s service.RelyingPartyService, userSvc *service.KratosAdminService) *RelyingPartyHandler { return &RelyingPartyHandler{Service: s, UserSvc: userSvc} } func (h *RelyingPartyHandler) Create(c *fiber.Ctx) error { tenantID := c.Params("tenantId") if tenantID == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "tenantId is required"}) } var req domain.HydraClient if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid request body"}) } rp, err := h.Service.Create(c.Context(), tenantID, req) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.Status(fiber.StatusCreated).JSON(rp) } func (h *RelyingPartyHandler) ListAll(c *fiber.Ctx) error { profile, ok := c.Locals("user_profile").(*domain.UserProfileResponse) if !ok { return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "unauthorized: user profile not found in context"}) } var rps []domain.RelyingParty var err error if profile.Role == domain.RoleSuperAdmin { rps, err = h.Service.ListAll(c.Context()) } else if profile.Role == domain.RoleTenantAdmin && profile.TenantID != nil { rps, err = h.Service.List(c.Context(), *profile.TenantID) } else { slog.Warn("Forbidden access to all applications", "userID", profile.ID, "role", profile.Role) return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "forbidden: insufficient role to list all applications"}) } if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(rps) } func (h *RelyingPartyHandler) List(c *fiber.Ctx) error { tenantID := c.Params("tenantId") if tenantID == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "tenantId is required"}) } rps, err := h.Service.List(c.Context(), tenantID) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(rps) } func (h *RelyingPartyHandler) Get(c *fiber.Ctx) error { id := c.Params("id") rp, hydraClient, err := h.Service.Get(c.Context(), id) if err != nil { return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "relying party not found"}) } return c.JSON(fiber.Map{ "relyingParty": rp, "oauth2Config": hydraClient, }) } func (h *RelyingPartyHandler) Update(c *fiber.Ctx) error { id := c.Params("id") var req domain.HydraClient if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid request body"}) } rp, err := h.Service.Update(c.Context(), id, req) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(rp) } func (h *RelyingPartyHandler) Delete(c *fiber.Ctx) error { id := c.Params("id") if err := h.Service.Delete(c.Context(), id); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.SendStatus(fiber.StatusNoContent) } func (h *RelyingPartyHandler) ListOwners(c *fiber.Ctx) error { clientID := c.Params("id") subjects, err := h.Service.ListOwners(c.Context(), clientID) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } type ownerInfo struct { Subject string `json:"subject"` Name string `json:"name,omitempty"` Email string `json:"email,omitempty"` Type string `json:"type"` // "user" or "group" } owners := make([]ownerInfo, 0, len(subjects)) for _, s := range subjects { info := ownerInfo{Subject: s, Type: "unknown"} if len(s) > 5 && s[:5] == "User:" { info.Type = "user" userID := s[5:] identity, err := h.UserSvc.GetIdentity(c.Context(), userID) if err == nil && identity != nil { info.Name, _ = identity.Traits["name"].(string) info.Email, _ = identity.Traits["email"].(string) } } else if len(s) > 10 && s[:10] == "UserGroup:" { info.Type = "group" // Group name enrichment could be added if we have a GroupService here } owners = append(owners, info) } return c.JSON(owners) } func (h *RelyingPartyHandler) AddOwner(c *fiber.Ctx) error { clientID := c.Params("id") subject := c.Params("subject") // e.g. "User:uuid" if err := h.Service.AddOwner(c.Context(), clientID, subject); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"message": "owner added"}) } func (h *RelyingPartyHandler) RemoveOwner(c *fiber.Ctx) error { clientID := c.Params("id") subject := c.Params("subject") if err := h.Service.RemoveOwner(c.Context(), clientID, subject); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"message": "owner removed"}) }