package service import ( "context" "fmt" "strings" "oci-portal/internal/oci" ) // IdentityProviders 列出域内 SAML 身份提供者。 func (s *OciConfigService) IdentityProviders(ctx context.Context, id uint) ([]oci.IdentityProviderInfo, error) { cred, homeRegion, err := s.credentialsAndHomeRegion(ctx, id) if err != nil { return nil, err } return s.client.ListIdentityProviders(ctx, cred, homeRegion) } // CreateIdentityProvider 创建禁用态 SAML IdP,激活另走 activate。 // 映射与 JIT 字段缺省时填控制台默认值(normalizeIdpInput)。 func (s *OciConfigService) CreateIdentityProvider(ctx context.Context, id uint, in oci.CreateIdpInput) (oci.IdentityProviderInfo, error) { if strings.TrimSpace(in.Name) == "" { return oci.IdentityProviderInfo{}, fmt.Errorf("create identity provider: name is required") } if !strings.Contains(in.Metadata, "EntityDescriptor") { return oci.IdentityProviderInfo{}, fmt.Errorf("create identity provider: metadata must be a SAML metadata XML") } // IDCS 校验 iconUrl 必须是 http(s) URL,data URI 会被 400 拒绝 if in.IconURL != "" && !strings.HasPrefix(in.IconURL, "http://") && !strings.HasPrefix(in.IconURL, "https://") { return oci.IdentityProviderInfo{}, fmt.Errorf("create identity provider: iconUrl 需为 http(s) 外链图片 URL") } cred, homeRegion, err := s.credentialsAndHomeRegion(ctx, id) if err != nil { return oci.IdentityProviderInfo{}, err } return s.client.CreateSamlIdentityProvider(ctx, cred, homeRegion, normalizeIdpInput(in)) } // normalizeIdpInput 填充映射字段的控制台默认值:名称 ID 格式「无」、 // 目标域用户属性「用户名」(JIT 开关组合由 API 层的指针缺省决定)。 func normalizeIdpInput(in oci.CreateIdpInput) oci.CreateIdpInput { if in.NameIDFormat == "" { in.NameIDFormat = "saml-none" } if in.UserStoreAttribute == "" { in.UserStoreAttribute = "userName" } return in } // SetIdentityProviderEnabled 激活/停用 IdP 并同步登录页显示。 func (s *OciConfigService) SetIdentityProviderEnabled(ctx context.Context, id uint, idpID string, enabled bool) (oci.IdentityProviderInfo, error) { cred, homeRegion, err := s.credentialsAndHomeRegion(ctx, id) if err != nil { return oci.IdentityProviderInfo{}, err } return s.client.SetIdentityProviderEnabled(ctx, cred, homeRegion, idpID, enabled) } // DeleteIdentityProvider 级联删除 IdP:先删关联的免 MFA 规则,再从登录页 // 移除、停用、删除 IdP 本体。 func (s *OciConfigService) DeleteIdentityProvider(ctx context.Context, id uint, idpID string) error { cred, homeRegion, err := s.credentialsAndHomeRegion(ctx, id) if err != nil { return err } if err := s.deleteIdpExemptions(ctx, cred, homeRegion, idpID); err != nil { return err } return s.client.DeleteIdentityProvider(ctx, cred, homeRegion, idpID) } // deleteIdpExemptions 删除 sign-on 策略中引用目标 IdP 的免 MFA 规则。 func (s *OciConfigService) deleteIdpExemptions(ctx context.Context, cred oci.Credentials, region, idpID string) error { rules, err := s.client.ListConsoleSignOnRules(ctx, cred, region) if err != nil { return fmt.Errorf("list sign-on rules before delete idp: %w", err) } for _, r := range rules { if r.BuiltIn || !strings.Contains(r.ConditionValue, idpID) { continue } if err := s.client.DeleteMfaExemptionRule(ctx, cred, region, r.ID); err != nil { return fmt.Errorf("delete exemption rule %s: %w", r.Name, err) } } return nil } // DomainSamlMetadata 下载域的 SP SAML 元数据 XML(提供给 IdP 侧配置)。 func (s *OciConfigService) DomainSamlMetadata(ctx context.Context, id uint) ([]byte, error) { cred, homeRegion, err := s.credentialsAndHomeRegion(ctx, id) if err != nil { return nil, err } return s.client.DownloadDomainSamlMetadata(ctx, cred, homeRegion) } // ConsoleSignOnRules 按优先级列出 OCI Console sign-on 策略的规则。 func (s *OciConfigService) ConsoleSignOnRules(ctx context.Context, id uint) ([]oci.SignOnRuleInfo, error) { cred, homeRegion, err := s.credentialsAndHomeRegion(ctx, id) if err != nil { return nil, err } return s.client.ListConsoleSignOnRules(ctx, cred, homeRegion) } // CreateMfaExemption 为指定 IdP 创建免 MFA 规则并置为最高优先级。 func (s *OciConfigService) CreateMfaExemption(ctx context.Context, id uint, idpID string) (oci.SignOnRuleInfo, error) { if strings.TrimSpace(idpID) == "" { return oci.SignOnRuleInfo{}, fmt.Errorf("create mfa exemption: identityProviderId is required") } cred, homeRegion, err := s.credentialsAndHomeRegion(ctx, id) if err != nil { return oci.SignOnRuleInfo{}, err } name, err := s.exemptionRuleName(ctx, cred, homeRegion, idpID) if err != nil { return oci.SignOnRuleInfo{}, err } return s.client.CreateMfaExemptionRule(ctx, cred, homeRegion, idpID, name) } // exemptionRuleName 校验 IdP 存在并用其名称生成规则名。 func (s *OciConfigService) exemptionRuleName(ctx context.Context, cred oci.Credentials, region, idpID string) (string, error) { idps, err := s.client.ListIdentityProviders(ctx, cred, region) if err != nil { return "", err } for _, idp := range idps { if idp.ID == idpID { return "skip-mfa-" + idp.Name, nil } } return "", fmt.Errorf("create mfa exemption: identity provider %s not found", idpID) } // DeleteMfaExemption 删除免 MFA 规则并恢复其余规则优先级。 func (s *OciConfigService) DeleteMfaExemption(ctx context.Context, id uint, ruleID string) error { cred, homeRegion, err := s.credentialsAndHomeRegion(ctx, id) if err != nil { return err } return s.client.DeleteMfaExemptionRule(ctx, cred, homeRegion, ruleID) }