forked from baron/baron-sso
개발자 권한 부여 페이지 추가
This commit is contained in:
@@ -861,6 +861,9 @@ func main() {
|
||||
dev.Post("/developer-request/:id/approve", devHandler.ApproveDeveloperRequest)
|
||||
dev.Post("/developer-request/:id/reject", devHandler.RejectDeveloperRequest)
|
||||
dev.Post("/developer-request/:id/cancel-approval", devHandler.CancelDeveloperRequestApproval)
|
||||
dev.Get("/developer-grants", devHandler.ListDeveloperGrants)
|
||||
dev.Post("/developer-grants", devHandler.CreateDeveloperGrant)
|
||||
dev.Post("/developer-grants/:id/revoke", devHandler.RevokeDeveloperGrant)
|
||||
|
||||
// Webhook for Kratos courier (HTTP delivery)
|
||||
auth.Post("/webhooks/kratos-courier", authHandler.HandleKratosCourierRelay)
|
||||
|
||||
@@ -4049,7 +4049,7 @@ func (h *DevHandler) ListDeveloperRequests(c *fiber.Ctx) error {
|
||||
userID = ""
|
||||
}
|
||||
|
||||
requests, err := h.DeveloperSvc.ListRequests(c.Context(), userID, status)
|
||||
requests, err := h.DeveloperSvc.ListRequests(c.Context(), userID, status, "")
|
||||
if err != nil {
|
||||
return errorJSON(c, fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
@@ -4057,6 +4057,159 @@ func (h *DevHandler) ListDeveloperRequests(c *fiber.Ctx) error {
|
||||
return c.JSON(requests)
|
||||
}
|
||||
|
||||
func (h *DevHandler) ListDeveloperGrants(c *fiber.Ctx) error {
|
||||
profile := h.getCurrentProfile(c)
|
||||
if profile == nil {
|
||||
return errorJSON(c, fiber.StatusUnauthorized, "unauthorized")
|
||||
}
|
||||
if normalizeUserRole(profile.Role) != domain.RoleSuperAdmin {
|
||||
return errorJSON(c, fiber.StatusForbidden, "forbidden: super_admin only")
|
||||
}
|
||||
|
||||
tenantID := strings.TrimSpace(c.Query("tenantId"))
|
||||
grants, err := h.DeveloperSvc.ListRequests(c.Context(), "", domain.DeveloperRequestStatusApproved, tenantID)
|
||||
if err != nil {
|
||||
return errorJSON(c, fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return c.JSON(grants)
|
||||
}
|
||||
|
||||
func (h *DevHandler) CreateDeveloperGrant(c *fiber.Ctx) error {
|
||||
profile := h.getCurrentProfile(c)
|
||||
if profile == nil {
|
||||
return errorJSON(c, fiber.StatusUnauthorized, "unauthorized")
|
||||
}
|
||||
if normalizeUserRole(profile.Role) != domain.RoleSuperAdmin {
|
||||
return errorJSON(c, fiber.StatusForbidden, "forbidden: super_admin only")
|
||||
}
|
||||
|
||||
var reqBody struct {
|
||||
UserID string `json:"userId"`
|
||||
TenantID string `json:"tenantId"`
|
||||
Reason string `json:"reason"`
|
||||
AdminNotes string `json:"adminNotes"`
|
||||
}
|
||||
if err := c.BodyParser(&reqBody); err != nil {
|
||||
return errorJSON(c, fiber.StatusBadRequest, "invalid request body")
|
||||
}
|
||||
|
||||
userID := strings.TrimSpace(reqBody.UserID)
|
||||
tenantID := strings.TrimSpace(reqBody.TenantID)
|
||||
if userID == "" || tenantID == "" {
|
||||
return errorJSON(c, fiber.StatusBadRequest, "userId and tenantId are required")
|
||||
}
|
||||
if h.KratosAdmin == nil || h.TenantSvc == nil {
|
||||
return errorJSON(c, fiber.StatusServiceUnavailable, "required services are unavailable")
|
||||
}
|
||||
|
||||
identity, err := h.KratosAdmin.GetIdentity(c.Context(), userID)
|
||||
if err != nil || identity == nil {
|
||||
return errorJSON(c, fiber.StatusNotFound, "user not found")
|
||||
}
|
||||
tenant, err := h.TenantSvc.GetTenant(c.Context(), tenantID)
|
||||
if err != nil || tenant == nil {
|
||||
return errorJSON(c, fiber.StatusNotFound, "tenant not found")
|
||||
}
|
||||
|
||||
name := strings.TrimSpace(extractTraitString(identity.Traits, "name"))
|
||||
if name == "" {
|
||||
name = userID
|
||||
}
|
||||
organization := strings.TrimSpace(tenant.Name)
|
||||
if organization == "" {
|
||||
organization = tenantID
|
||||
}
|
||||
email := strings.TrimSpace(extractTraitString(identity.Traits, "email"))
|
||||
phone := strings.TrimSpace(extractTraitString(identity.Traits, "phone"))
|
||||
role := normalizeUserRole(extractTraitString(identity.Traits, "role"))
|
||||
if role == "" {
|
||||
role = domain.RoleUser
|
||||
}
|
||||
reason := strings.TrimSpace(reqBody.Reason)
|
||||
if reason == "" {
|
||||
reason = "직접 부여"
|
||||
}
|
||||
|
||||
existing, err := h.DeveloperSvc.GetRequestStatus(c.Context(), userID, tenantID)
|
||||
if err != nil {
|
||||
return errorJSON(c, fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if existing != nil {
|
||||
switch existing.Status {
|
||||
case domain.DeveloperRequestStatusApproved:
|
||||
h.ensureDeveloperGrantRelation(c, userID, tenantID)
|
||||
return c.JSON(existing)
|
||||
case domain.DeveloperRequestStatusPending:
|
||||
if err := h.DeveloperSvc.ApproveRequest(c.Context(), existing.ID, reqBody.AdminNotes); err != nil {
|
||||
return errorJSON(c, fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
h.ensureDeveloperGrantRelation(c, userID, tenantID)
|
||||
existing.Status = domain.DeveloperRequestStatusApproved
|
||||
existing.AdminNotes = reqBody.AdminNotes
|
||||
return c.JSON(existing)
|
||||
}
|
||||
}
|
||||
|
||||
grant := domain.DeveloperRequest{
|
||||
UserID: userID,
|
||||
TenantID: tenantID,
|
||||
Name: name,
|
||||
Organization: organization,
|
||||
Email: email,
|
||||
Phone: phone,
|
||||
Role: role,
|
||||
Reason: reason,
|
||||
Status: domain.DeveloperRequestStatusApproved,
|
||||
AdminNotes: strings.TrimSpace(reqBody.AdminNotes),
|
||||
}
|
||||
if err := h.DeveloperSvc.CreateGrant(c.Context(), grant); err != nil {
|
||||
return errorJSON(c, fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
h.ensureDeveloperGrantRelation(c, userID, tenantID)
|
||||
|
||||
return c.Status(fiber.StatusCreated).JSON(grant)
|
||||
}
|
||||
|
||||
func (h *DevHandler) RevokeDeveloperGrant(c *fiber.Ctx) error {
|
||||
profile := h.getCurrentProfile(c)
|
||||
if profile == nil {
|
||||
return errorJSON(c, fiber.StatusUnauthorized, "unauthorized")
|
||||
}
|
||||
if normalizeUserRole(profile.Role) != domain.RoleSuperAdmin {
|
||||
return errorJSON(c, fiber.StatusForbidden, "forbidden: super_admin only")
|
||||
}
|
||||
|
||||
idStr := c.Params("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
return errorJSON(c, fiber.StatusBadRequest, "invalid grant id")
|
||||
}
|
||||
|
||||
var reqBody struct {
|
||||
AdminNotes string `json:"adminNotes"`
|
||||
}
|
||||
if err := c.BodyParser(&reqBody); err != nil {
|
||||
return errorJSON(c, fiber.StatusBadRequest, "invalid request body")
|
||||
}
|
||||
|
||||
devReq, err := h.DeveloperSvc.GetRequestByID(c.Context(), uint(id))
|
||||
if err != nil {
|
||||
return errorJSON(c, fiber.StatusInternalServerError, "failed to fetch grant details")
|
||||
}
|
||||
if devReq.Status != domain.DeveloperRequestStatusApproved {
|
||||
return errorJSON(c, fiber.StatusBadRequest, "only approved grants can be revoked")
|
||||
}
|
||||
|
||||
if err := h.DeveloperSvc.CancelApprovedRequest(c.Context(), uint(id), reqBody.AdminNotes); err != nil {
|
||||
return errorJSON(c, fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
h.revokeDeveloperGrantRelation(c, devReq.UserID, devReq.TenantID)
|
||||
|
||||
return c.JSON(fiber.Map{"status": "ok"})
|
||||
}
|
||||
|
||||
func (h *DevHandler) ApproveDeveloperRequest(c *fiber.Ctx) error {
|
||||
profile := h.getCurrentProfile(c)
|
||||
if profile == nil {
|
||||
|
||||
@@ -30,6 +30,10 @@ func (s *DeveloperService) RequestAccess(ctx context.Context, req domain.Develop
|
||||
return s.db.WithContext(ctx).Create(&req).Error
|
||||
}
|
||||
|
||||
func (s *DeveloperService) CreateGrant(ctx context.Context, req domain.DeveloperRequest) error {
|
||||
return s.db.WithContext(ctx).Create(&req).Error
|
||||
}
|
||||
|
||||
func (s *DeveloperService) GetRequestStatus(ctx context.Context, userID, tenantID string) (*domain.DeveloperRequest, error) {
|
||||
var req domain.DeveloperRequest
|
||||
err := s.db.WithContext(ctx).Where("user_id = ? AND tenant_id = ?", userID, tenantID).Order("created_at DESC").First(&req).Error
|
||||
@@ -51,7 +55,7 @@ func (s *DeveloperService) GetRequestByID(ctx context.Context, id uint) (*domain
|
||||
return &req, nil
|
||||
}
|
||||
|
||||
func (s *DeveloperService) ListRequests(ctx context.Context, userID, status string) ([]domain.DeveloperRequest, error) {
|
||||
func (s *DeveloperService) ListRequests(ctx context.Context, userID, status, tenantID string) ([]domain.DeveloperRequest, error) {
|
||||
var requests []domain.DeveloperRequest
|
||||
query := s.db.WithContext(ctx)
|
||||
if userID != "" {
|
||||
@@ -60,6 +64,9 @@ func (s *DeveloperService) ListRequests(ctx context.Context, userID, status stri
|
||||
if status != "" {
|
||||
query = query.Where("status = ?", status)
|
||||
}
|
||||
if tenantID != "" {
|
||||
query = query.Where("tenant_id = ?", tenantID)
|
||||
}
|
||||
err := query.Order("created_at DESC").Find(&requests).Error
|
||||
return requests, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user