Files
oci-portal/internal/service/federation.go
T
Wang Defa dbba1f4905
CI / test (push) Successful in 30s
Release / release (push) Successful in 49s
发布 0.1.0:通知渠道、告警规则、令牌版本与安全加固
2026-07-10 17:38:34 +08:00

147 lines
5.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package service
import (
"context"
"fmt"
"strings"
"oci-portal/internal/oci"
)
// IdentityProviders 列出域内 SAML 身份提供者。
func (s *OciConfigService) IdentityProviders(ctx context.Context, id uint, domainID string) ([]oci.IdentityProviderInfo, error) {
cred, homeRegion, err := s.credentialsAndHomeRegion(ctx, id)
if err != nil {
return nil, err
}
return s.client.ListIdentityProviders(ctx, cred, homeRegion, domainID)
}
// CreateIdentityProvider 创建禁用态 SAML IdP,激活另走 activate。
// 映射与 JIT 字段缺省时填控制台默认值(normalizeIdpInput)。
func (s *OciConfigService) CreateIdentityProvider(ctx context.Context, id uint, domainID string, 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) URLdata 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, domainID, 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, domainID, 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, domainID, idpID, enabled)
}
// DeleteIdentityProvider 级联删除 IdP:先删关联的免 MFA 规则,再从登录页
// 移除、停用、删除 IdP 本体。
func (s *OciConfigService) DeleteIdentityProvider(ctx context.Context, id uint, domainID, idpID string) error {
cred, homeRegion, err := s.credentialsAndHomeRegion(ctx, id)
if err != nil {
return err
}
if err := s.deleteIdpExemptions(ctx, cred, homeRegion, domainID, idpID); err != nil {
return err
}
return s.client.DeleteIdentityProvider(ctx, cred, homeRegion, domainID, idpID)
}
// deleteIdpExemptions 删除 sign-on 策略中引用目标 IdP 的免 MFA 规则。
func (s *OciConfigService) deleteIdpExemptions(ctx context.Context, cred oci.Credentials, region, domainID, idpID string) error {
rules, err := s.client.ListConsoleSignOnRules(ctx, cred, region, domainID)
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, domainID, 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, domainID string) ([]byte, error) {
cred, homeRegion, err := s.credentialsAndHomeRegion(ctx, id)
if err != nil {
return nil, err
}
return s.client.DownloadDomainSamlMetadata(ctx, cred, homeRegion, domainID)
}
// ConsoleSignOnRules 按优先级列出 OCI Console sign-on 策略的规则。
func (s *OciConfigService) ConsoleSignOnRules(ctx context.Context, id uint, domainID string) ([]oci.SignOnRuleInfo, error) {
cred, homeRegion, err := s.credentialsAndHomeRegion(ctx, id)
if err != nil {
return nil, err
}
return s.client.ListConsoleSignOnRules(ctx, cred, homeRegion, domainID)
}
// CreateMfaExemption 为指定 IdP 创建免 MFA 规则并置为最高优先级。
func (s *OciConfigService) CreateMfaExemption(ctx context.Context, id uint, domainID, 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, domainID, idpID)
if err != nil {
return oci.SignOnRuleInfo{}, err
}
return s.client.CreateMfaExemptionRule(ctx, cred, homeRegion, domainID, idpID, name)
}
// exemptionRuleName 校验 IdP 存在并用其名称生成规则名。
func (s *OciConfigService) exemptionRuleName(ctx context.Context, cred oci.Credentials, region, domainID, idpID string) (string, error) {
idps, err := s.client.ListIdentityProviders(ctx, cred, region, domainID)
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, domainID, ruleID string) error {
cred, homeRegion, err := s.credentialsAndHomeRegion(ctx, id)
if err != nil {
return err
}
return s.client.DeleteMfaExemptionRule(ctx, cred, homeRegion, domainID, ruleID)
}