package api import ( "net/http" "github.com/gin-gonic/gin" "oci-portal/internal/oci" ) // ---- Federation:SAML IdP 与 sign-on 免 MFA ---- // createIdpRequest 的映射 / JIT 字段全部可缺省:布尔用指针区分「未传」, // 缺省即控制台默认(JIT 开启建用户不更新、分配 Administrators 组、 // 名称 ID 格式无、NameID 映射用户名)。 type createIdpRequest struct { Name string `json:"name" binding:"required"` Metadata string `json:"metadata" binding:"required"` Description string `json:"description"` IconURL string `json:"iconUrl"` NameIDFormat string `json:"nameIdFormat"` AssertionAttribute string `json:"assertionAttribute"` UserStoreAttribute string `json:"userStoreAttribute"` JitEnabled *bool `json:"jitEnabled"` JitCreate *bool `json:"jitCreate"` JitUpdate *bool `json:"jitUpdate"` JitAssignAdminGroup *bool `json:"jitAssignAdminGroup"` JitMapEmail bool `json:"jitMapEmail"` } func boolOr(v *bool, def bool) bool { if v == nil { return def } return *v } func (h *ociConfigHandler) listIdentityProviders(c *gin.Context) { id, ok := pathID(c) if !ok { return } idps, err := h.svc.IdentityProviders(c.Request.Context(), id) if err != nil { respondError(c, err) return } c.JSON(http.StatusOK, idps) } func (h *ociConfigHandler) createIdentityProvider(c *gin.Context) { id, ok := pathID(c) if !ok { return } var req createIdpRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } idp, err := h.svc.CreateIdentityProvider(c.Request.Context(), id, oci.CreateIdpInput{ Name: req.Name, Metadata: req.Metadata, Description: req.Description, IconURL: req.IconURL, NameIDFormat: req.NameIDFormat, AssertionAttribute: req.AssertionAttribute, UserStoreAttribute: req.UserStoreAttribute, JitEnabled: boolOr(req.JitEnabled, true), JitCreate: boolOr(req.JitCreate, true), JitUpdate: boolOr(req.JitUpdate, false), JitAssignAdminGroup: boolOr(req.JitAssignAdminGroup, true), JitMapEmail: req.JitMapEmail, }) if err != nil { respondError(c, err) return } c.JSON(http.StatusCreated, idp) } func (h *ociConfigHandler) activateIdentityProvider(c *gin.Context) { id, ok := pathID(c) if !ok { return } var req struct { Enabled *bool `json:"enabled" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } idp, err := h.svc.SetIdentityProviderEnabled(c.Request.Context(), id, c.Param("idpId"), *req.Enabled) if err != nil { respondError(c, err) return } c.JSON(http.StatusOK, idp) } func (h *ociConfigHandler) deleteIdentityProvider(c *gin.Context) { id, ok := pathID(c) if !ok { return } if err := h.svc.DeleteIdentityProvider(c.Request.Context(), id, c.Param("idpId")); err != nil { respondError(c, err) return } c.Status(http.StatusNoContent) } func (h *ociConfigHandler) downloadSamlMetadata(c *gin.Context) { id, ok := pathID(c) if !ok { return } metadata, err := h.svc.DomainSamlMetadata(c.Request.Context(), id) if err != nil { respondError(c, err) return } c.Header("Content-Disposition", `attachment; filename="oci-domain-saml-metadata.xml"`) c.Data(http.StatusOK, "application/xml", metadata) } func (h *ociConfigHandler) listSignOnRules(c *gin.Context) { id, ok := pathID(c) if !ok { return } rules, err := h.svc.ConsoleSignOnRules(c.Request.Context(), id) if err != nil { respondError(c, err) return } c.JSON(http.StatusOK, rules) } func (h *ociConfigHandler) createMfaExemption(c *gin.Context) { id, ok := pathID(c) if !ok { return } var req struct { IdentityProviderID string `json:"identityProviderId" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } rule, err := h.svc.CreateMfaExemption(c.Request.Context(), id, req.IdentityProviderID) if err != nil { respondError(c, err) return } c.JSON(http.StatusCreated, rule) } func (h *ociConfigHandler) deleteMfaExemption(c *gin.Context) { id, ok := pathID(c) if !ok { return } if err := h.svc.DeleteMfaExemption(c.Request.Context(), id, c.Param("ruleId")); err != nil { respondError(c, err) return } c.Status(http.StatusNoContent) }