forked from baron/baron-sso
admin management 기능 추가
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"net/url"
|
||||
|
||||
"github.com/descope/go-sdk/descope"
|
||||
"github.com/descope/go-sdk/descope/client"
|
||||
@@ -43,17 +44,8 @@ func boolPtr(b bool) *bool {
|
||||
return &b
|
||||
}
|
||||
|
||||
type CreateUserRequest struct {
|
||||
LoginID string `json:"loginId"`
|
||||
Email string `json:"email"`
|
||||
Phone string `json:"phone"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Roles []string `json:"roles"`
|
||||
Tenants map[string][]string `json:"tenants"` // tenantId -> roles
|
||||
}
|
||||
|
||||
func (h *AdminHandler) CreateUser(c *fiber.Ctx) error {
|
||||
// 1. Simple Password Check
|
||||
// checkAuth Helper
|
||||
func (h *AdminHandler) checkAuth(c *fiber.Ctx) error {
|
||||
adminPass := os.Getenv("ADMIN_PASSWORD")
|
||||
if adminPass == "" {
|
||||
adminPass = "admin" // Default fallback
|
||||
@@ -63,6 +55,165 @@ func (h *AdminHandler) CreateUser(c *fiber.Ctx) error {
|
||||
if reqPass != adminPass {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid Admin Password"})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type CreateUserRequest struct {
|
||||
LoginID string `json:"loginId"`
|
||||
Email string `json:"email"`
|
||||
Phone string `json:"phone"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Roles []string `json:"roles"`
|
||||
Tenants map[string][]string `json:"tenants"` // tenantId -> roles
|
||||
}
|
||||
|
||||
func (h *AdminHandler) CheckAuth(c *fiber.Ctx) error {
|
||||
if err := h.checkAuth(c); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Status(fiber.StatusOK).JSON(fiber.Map{"status": "ok"})
|
||||
}
|
||||
|
||||
// ListUsers - GET /api/v1/admin/users
|
||||
func (h *AdminHandler) ListUsers(c *fiber.Ctx) error {
|
||||
if err := h.checkAuth(c); err != nil { return err }
|
||||
|
||||
text := c.Query("text")
|
||||
// Limit is not directly supported in SearchAll options as a simple int in all SDK versions,
|
||||
// but let's check the options struct.
|
||||
// Based on previous inspection: SearchAll takes UserSearchOptions.
|
||||
|
||||
var users []*descope.UserResponse
|
||||
var err error
|
||||
|
||||
if text != "" {
|
||||
options := &descope.UserSearchOptions{ Text: text, Limit: 50 }
|
||||
users, _, err = h.DescopeClient.Management.User().SearchAll(context.Background(), options)
|
||||
} else {
|
||||
// Nil options means default search (usually returns all or default page)
|
||||
users, _, err = h.DescopeClient.Management.User().SearchAll(context.Background(), nil)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Printf("[Admin] ListUsers failed: %v", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
return c.JSON(fiber.Map{"users": users})
|
||||
}
|
||||
|
||||
// DeleteUser - DELETE /api/v1/admin/users/:loginId
|
||||
func (h *AdminHandler) DeleteUser(c *fiber.Ctx) error {
|
||||
if err := h.checkAuth(c); err != nil { return err }
|
||||
|
||||
loginID := c.Params("loginId")
|
||||
// Decode if necessary (Fiber usually decodes params, but let's be safe if it's double encoded)
|
||||
if decoded, err := url.QueryUnescape(loginID); err == nil {
|
||||
loginID = decoded
|
||||
}
|
||||
|
||||
log.Printf("[Admin] Deleting user: %s", loginID)
|
||||
if err := h.DescopeClient.Management.User().Delete(context.Background(), loginID); err != nil {
|
||||
log.Printf("[Admin] DeleteUser failed: %v", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
return c.JSON(fiber.Map{"message": "User deleted successfully"})
|
||||
}
|
||||
|
||||
// UpdateUserStatus - PATCH /api/v1/admin/users/:loginId/status
|
||||
func (h *AdminHandler) UpdateUserStatus(c *fiber.Ctx) error {
|
||||
if err := h.checkAuth(c); err != nil { return err }
|
||||
|
||||
loginID := c.Params("loginId")
|
||||
if decoded, err := url.QueryUnescape(loginID); err == nil {
|
||||
loginID = decoded
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Status string `json:"status"` // "enabled" or "disabled"
|
||||
}
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid body"})
|
||||
}
|
||||
|
||||
var user *descope.UserResponse
|
||||
var err error
|
||||
|
||||
log.Printf("[Admin] Updating status for %s to %s", loginID, req.Status)
|
||||
|
||||
if req.Status == "enabled" || req.Status == "active" {
|
||||
user, err = h.DescopeClient.Management.User().Activate(context.Background(), loginID)
|
||||
} else {
|
||||
user, err = h.DescopeClient.Management.User().Deactivate(context.Background(), loginID)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Printf("[Admin] Status update failed: %v", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
"message": "Status updated",
|
||||
"user": user,
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateUser - PATCH /api/v1/admin/users/:loginId
|
||||
func (h *AdminHandler) UpdateUser(c *fiber.Ctx) error {
|
||||
if err := h.checkAuth(c); err != nil { return err }
|
||||
|
||||
loginID := c.Params("loginId")
|
||||
if decoded, err := url.QueryUnescape(loginID); err == nil {
|
||||
loginID = decoded
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Email *string `json:"email"`
|
||||
Phone *string `json:"phone"`
|
||||
DisplayName *string `json:"displayName"`
|
||||
}
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid body"})
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
var err error
|
||||
|
||||
// Update Display Name
|
||||
if req.DisplayName != nil {
|
||||
_, err = h.DescopeClient.Management.User().UpdateDisplayName(ctx, loginID, *req.DisplayName)
|
||||
if err != nil {
|
||||
return c.Status(500).JSON(fiber.Map{"error": "Failed to update name: " + err.Error()})
|
||||
}
|
||||
}
|
||||
|
||||
// Update Email
|
||||
if req.Email != nil {
|
||||
_, err = h.DescopeClient.Management.User().UpdateEmail(ctx, loginID, *req.Email, true, false) // verified=true, addToLoginIDs=false
|
||||
if err != nil {
|
||||
return c.Status(500).JSON(fiber.Map{"error": "Failed to update email: " + err.Error()})
|
||||
}
|
||||
}
|
||||
|
||||
// Update Phone
|
||||
if req.Phone != nil {
|
||||
phone := *req.Phone
|
||||
// Normalize
|
||||
if strings.HasPrefix(phone, "010") {
|
||||
phone = "+82" + phone[1:]
|
||||
}
|
||||
_, err = h.DescopeClient.Management.User().UpdatePhone(ctx, loginID, phone, true, false) // verified=true, addToLoginIDs=false
|
||||
if err != nil {
|
||||
return c.Status(500).JSON(fiber.Map{"error": "Failed to update phone: " + err.Error()})
|
||||
}
|
||||
}
|
||||
|
||||
return c.JSON(fiber.Map{"message": "User updated successfully"})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) CreateUser(c *fiber.Ctx) error {
|
||||
if err := h.checkAuth(c); err != nil { return err }
|
||||
|
||||
if h.DescopeClient == nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Descope Client not configured"})
|
||||
@@ -127,18 +278,4 @@ func (h *AdminHandler) CreateUser(c *fiber.Ctx) error {
|
||||
"message": "User created successfully",
|
||||
"user": res,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) CheckAuth(c *fiber.Ctx) error {
|
||||
adminPass := os.Getenv("ADMIN_PASSWORD")
|
||||
if adminPass == "" {
|
||||
adminPass = "admin"
|
||||
}
|
||||
|
||||
reqPass := c.Get("X-Admin-Password")
|
||||
if reqPass != adminPass {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid Admin Password"})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(fiber.Map{"status": "ok"})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user