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 } // @Summary 身份提供商列表 // @Tags 租户 IAM // @Param id path int true "配置 ID" // @Param domainId query string false "身份域 OCID(缺省 Default 域)" // @Success 200 {array} oci.IdentityProviderInfo // @Security BearerAuth // @Router /api/v1/oci-configs/{id}/identity-providers [get] func (h *ociConfigHandler) listIdentityProviders(c *gin.Context) { id, ok := pathID(c) if !ok { return } idps, err := h.svc.IdentityProviders(c.Request.Context(), id, c.Query("domainId")) if err != nil { respondError(c, err) return } c.JSON(http.StatusOK, idps) } // @Summary 创建身份提供商(SAML) // @Tags 租户 IAM // @Param id path int true "配置 ID" // @Param domainId query string false "身份域 OCID(缺省 Default 域)" // @Param body body createIdpRequest true "请求体" // @Success 201 {object} oci.IdentityProviderInfo // @Security BearerAuth // @Router /api/v1/oci-configs/{id}/identity-providers [post] 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, c.Query("domainId"), 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) } // @Summary 激活身份提供商 // @Tags 租户 IAM // @Param id path int true "配置 ID" // @Param domainId query string false "身份域 OCID(缺省 Default 域)" // @Param idpId path string true "idpId" // @Param body body object true "请求体(见接口说明)" // @Success 200 {object} oci.IdentityProviderInfo // @Security BearerAuth // @Router /api/v1/oci-configs/{id}/identity-providers/{idpId}/activate [post] 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.Query("domainId"), c.Param("idpId"), *req.Enabled) if err != nil { respondError(c, err) return } c.JSON(http.StatusOK, idp) } // @Summary 删除身份提供商 // @Tags 租户 IAM // @Param id path int true "配置 ID" // @Param domainId query string false "身份域 OCID(缺省 Default 域)" // @Param idpId path string true "idpId" // @Success 204 "无内容" // @Security BearerAuth // @Router /api/v1/oci-configs/{id}/identity-providers/{idpId} [delete] func (h *ociConfigHandler) deleteIdentityProvider(c *gin.Context) { id, ok := pathID(c) if !ok { return } if err := h.svc.DeleteIdentityProvider(c.Request.Context(), id, c.Query("domainId"), c.Param("idpId")); err != nil { respondError(c, err) return } c.Status(http.StatusNoContent) } // @Summary 下载 SAML 元数据 // @Tags 租户 IAM // @Param id path int true "配置 ID" // @Param domainId query string false "身份域 OCID(缺省 Default 域)" // @Success 200 {file} file "SAML 元数据 XML(附件下载)" // @Security BearerAuth // @Router /api/v1/oci-configs/{id}/saml-metadata [get] func (h *ociConfigHandler) downloadSamlMetadata(c *gin.Context) { id, ok := pathID(c) if !ok { return } metadata, err := h.svc.DomainSamlMetadata(c.Request.Context(), id, c.Query("domainId")) 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) } // @Summary 单点登录规则列表 // @Tags 租户 IAM // @Param id path int true "配置 ID" // @Param domainId query string false "身份域 OCID(缺省 Default 域)" // @Success 200 {array} oci.SignOnRuleInfo // @Security BearerAuth // @Router /api/v1/oci-configs/{id}/sign-on-rules [get] func (h *ociConfigHandler) listSignOnRules(c *gin.Context) { id, ok := pathID(c) if !ok { return } rules, err := h.svc.ConsoleSignOnRules(c.Request.Context(), id, c.Query("domainId")) if err != nil { respondError(c, err) return } c.JSON(http.StatusOK, rules) } // @Summary 创建 MFA 豁免规则 // @Tags 租户 IAM // @Param id path int true "配置 ID" // @Param domainId query string false "身份域 OCID(缺省 Default 域)" // @Param body body object true "请求体(见接口说明)" // @Success 201 {object} oci.SignOnRuleInfo // @Security BearerAuth // @Router /api/v1/oci-configs/{id}/sign-on-exemptions [post] 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, c.Query("domainId"), req.IdentityProviderID) if err != nil { respondError(c, err) return } c.JSON(http.StatusCreated, rule) } // @Summary 删除 MFA 豁免规则 // @Tags 租户 IAM // @Param id path int true "配置 ID" // @Param domainId query string false "身份域 OCID(缺省 Default 域)" // @Param ruleId path string true "ruleId" // @Success 204 "无内容" // @Security BearerAuth // @Router /api/v1/oci-configs/{id}/sign-on-exemptions/{ruleId} [delete] func (h *ociConfigHandler) deleteMfaExemption(c *gin.Context) { id, ok := pathID(c) if !ok { return } if err := h.svc.DeleteMfaExemption(c.Request.Context(), id, c.Query("domainId"), c.Param("ruleId")); err != nil { respondError(c, err) return } c.Status(http.StatusNoContent) }