发布 0.1.0:通知渠道、告警规则、令牌版本与安全加固
This commit is contained in:
@@ -47,26 +47,82 @@ type IdentitySettingInfo struct {
|
||||
PrimaryEmailRequired bool `json:"primaryEmailRequired"`
|
||||
}
|
||||
|
||||
// domainURL 解析租户 Default Identity Domain 的 endpoint URL。
|
||||
func (c *RealClient) domainURL(ctx context.Context, cred Credentials, region string) (string, error) {
|
||||
// IdentityDomain 是租户下一个 ACTIVE 身份域的列表视图;
|
||||
// URL 仅供后端解析 SCIM 端点,不下发给前端(防面板被引导签名任意地址)。
|
||||
type IdentityDomain struct {
|
||||
ID string `json:"id"`
|
||||
DisplayName string `json:"displayName"`
|
||||
URL string `json:"-"`
|
||||
HomeRegion string `json:"homeRegion"`
|
||||
Type string `json:"type"`
|
||||
LicenseType string `json:"licenseType"`
|
||||
}
|
||||
|
||||
// ListIdentityDomains 实现 Client:列出租户全部 ACTIVE 身份域。
|
||||
// 无域老租户返回空列表(非错误),调用方据此回退经典 IAM 路径。
|
||||
func (c *RealClient) ListIdentityDomains(ctx context.Context, cred Credentials, region string) ([]IdentityDomain, error) {
|
||||
items, err := c.listDomainSummaries(ctx, cred, region)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]IdentityDomain, 0, len(items))
|
||||
for _, d := range items {
|
||||
if d.LifecycleState != identity.DomainLifecycleStateActive {
|
||||
continue
|
||||
}
|
||||
out = append(out, IdentityDomain{
|
||||
ID: deref(d.Id),
|
||||
DisplayName: deref(d.DisplayName),
|
||||
URL: deref(d.Url),
|
||||
HomeRegion: deref(d.HomeRegion),
|
||||
Type: string(d.Type),
|
||||
LicenseType: deref(d.LicenseType),
|
||||
})
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// listDomainSummaries 拉取租户全部域(数量极少,仍按游标翻全以防万一)。
|
||||
func (c *RealClient) listDomainSummaries(ctx context.Context, cred Credentials, region string) ([]identity.DomainSummary, error) {
|
||||
ic, err := c.identityClientAt(cred, region)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var items []identity.DomainSummary
|
||||
var page *string
|
||||
for {
|
||||
resp, err := ic.ListDomains(ctx, identity.ListDomainsRequest{CompartmentId: &cred.TenancyOCID, Page: page})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list identity domains: %w", err)
|
||||
}
|
||||
items = append(items, resp.Items...)
|
||||
if resp.OpcNextPage == nil {
|
||||
return items, nil
|
||||
}
|
||||
page = resp.OpcNextPage
|
||||
}
|
||||
}
|
||||
|
||||
// domainURL 解析身份域的 endpoint URL:domainID 非空按 OCID 精确匹配
|
||||
// (URL 只能来自租户自己的域列表,不信任外部输入),为空回退 Default 优先。
|
||||
func (c *RealClient) domainURL(ctx context.Context, cred Credentials, region, domainID string) (string, error) {
|
||||
items, err := c.listDomainSummaries(ctx, cred, region)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
resp, err := ic.ListDomains(ctx, identity.ListDomainsRequest{CompartmentId: &cred.TenancyOCID})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("list identity domains: %w", err)
|
||||
}
|
||||
url := pickDomainURL(resp.Items)
|
||||
url := pickDomainURL(items, domainID)
|
||||
if url == "" {
|
||||
if domainID != "" {
|
||||
return "", fmt.Errorf("identity domain %s not found or inactive", domainID)
|
||||
}
|
||||
return "", fmt.Errorf("no active identity domain found")
|
||||
}
|
||||
return url, nil
|
||||
}
|
||||
|
||||
// domainsClient 解析租户的 Default Identity Domain 并构造 SCIM 客户端。
|
||||
func (c *RealClient) domainsClient(ctx context.Context, cred Credentials, region string) (identitydomains.IdentityDomainsClient, error) {
|
||||
url, err := c.domainURL(ctx, cred, region)
|
||||
// domainsClient 解析身份域(domainID 为空取 Default)并构造 SCIM 客户端。
|
||||
func (c *RealClient) domainsClient(ctx context.Context, cred Credentials, region, domainID string) (identitydomains.IdentityDomainsClient, error) {
|
||||
url, err := c.domainURL(ctx, cred, region, domainID)
|
||||
if err != nil {
|
||||
return identitydomains.IdentityDomainsClient{}, err
|
||||
}
|
||||
@@ -80,13 +136,20 @@ func (c *RealClient) domainsClient(ctx context.Context, cred Credentials, region
|
||||
return dc, nil
|
||||
}
|
||||
|
||||
// pickDomainURL 优先返回名为 Default 的域,否则取第一个 ACTIVE 的域。
|
||||
func pickDomainURL(domains []identity.DomainSummary) string {
|
||||
// pickDomainURL 在 ACTIVE 域中选取:domainID 非空按 OCID 匹配;
|
||||
// 为空优先名为 Default 的域,否则取第一个 ACTIVE 的域。
|
||||
func pickDomainURL(domains []identity.DomainSummary, domainID string) string {
|
||||
fallback := ""
|
||||
for _, d := range domains {
|
||||
if d.LifecycleState != identity.DomainLifecycleStateActive {
|
||||
continue
|
||||
}
|
||||
if domainID != "" {
|
||||
if deref(d.Id) == domainID {
|
||||
return deref(d.Url)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if deref(d.DisplayName) == "Default" {
|
||||
return deref(d.Url)
|
||||
}
|
||||
@@ -98,8 +161,8 @@ func pickDomainURL(domains []identity.DomainSummary) string {
|
||||
}
|
||||
|
||||
// GetNotificationRecipients 实现 Client:查询域通知收件人设置。
|
||||
func (c *RealClient) GetNotificationRecipients(ctx context.Context, cred Credentials, region string) (NotificationRecipients, error) {
|
||||
dc, err := c.domainsClient(ctx, cred, region)
|
||||
func (c *RealClient) GetNotificationRecipients(ctx context.Context, cred Credentials, region, domainID string) (NotificationRecipients, error) {
|
||||
dc, err := c.domainsClient(ctx, cred, region, domainID)
|
||||
if err != nil {
|
||||
return NotificationRecipients{}, err
|
||||
}
|
||||
@@ -115,8 +178,8 @@ func (c *RealClient) GetNotificationRecipients(ctx context.Context, cred Credent
|
||||
|
||||
// UpdateNotificationRecipients 实现 Client:把域通知改为只发给指定收件人;
|
||||
// emails 为空时关闭 test mode 恢复默认发送。其余字段原样保留。
|
||||
func (c *RealClient) UpdateNotificationRecipients(ctx context.Context, cred Credentials, region string, emails []string) (NotificationRecipients, error) {
|
||||
dc, err := c.domainsClient(ctx, cred, region)
|
||||
func (c *RealClient) UpdateNotificationRecipients(ctx context.Context, cred Credentials, region, domainID string, emails []string) (NotificationRecipients, error) {
|
||||
dc, err := c.domainsClient(ctx, cred, region, domainID)
|
||||
if err != nil {
|
||||
return NotificationRecipients{}, err
|
||||
}
|
||||
@@ -153,8 +216,8 @@ func getNotificationSetting(ctx context.Context, dc identitydomains.IdentityDoma
|
||||
}
|
||||
|
||||
// ListPasswordPolicies 实现 Client:列出域内全部密码策略。
|
||||
func (c *RealClient) ListPasswordPolicies(ctx context.Context, cred Credentials, region string) ([]PasswordPolicyInfo, error) {
|
||||
dc, err := c.domainsClient(ctx, cred, region)
|
||||
func (c *RealClient) ListPasswordPolicies(ctx context.Context, cred Credentials, region, domainID string) ([]PasswordPolicyInfo, error) {
|
||||
dc, err := c.domainsClient(ctx, cred, region, domainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -171,12 +234,12 @@ func (c *RealClient) ListPasswordPolicies(ctx context.Context, cred Credentials,
|
||||
|
||||
// UpdatePasswordPolicy 实现 Client:以 SCIM Patch 修改密码策略的指定字段。
|
||||
// 仅 Custom 强度的策略可修改,Simple/Standard 内置策略 OCI 返回 403。
|
||||
func (c *RealClient) UpdatePasswordPolicy(ctx context.Context, cred Credentials, region, policyID string, in UpdatePasswordPolicyInput) (PasswordPolicyInfo, error) {
|
||||
func (c *RealClient) UpdatePasswordPolicy(ctx context.Context, cred Credentials, region, domainID, policyID string, in UpdatePasswordPolicyInput) (PasswordPolicyInfo, error) {
|
||||
ops := buildPolicyPatchOps(in)
|
||||
if len(ops) == 0 {
|
||||
return PasswordPolicyInfo{}, fmt.Errorf("update password policy: nothing to update")
|
||||
}
|
||||
dc, err := c.domainsClient(ctx, cred, region)
|
||||
dc, err := c.domainsClient(ctx, cred, region, domainID)
|
||||
if err != nil {
|
||||
return PasswordPolicyInfo{}, err
|
||||
}
|
||||
@@ -233,8 +296,8 @@ func removeOp(path string) identitydomains.Operations {
|
||||
}
|
||||
|
||||
// GetIdentitySetting 实现 Client:读取域身份设置(IdentitySettings 单例资源)。
|
||||
func (c *RealClient) GetIdentitySetting(ctx context.Context, cred Credentials, region string) (IdentitySettingInfo, error) {
|
||||
dc, err := c.domainsClient(ctx, cred, region)
|
||||
func (c *RealClient) GetIdentitySetting(ctx context.Context, cred Credentials, region, domainID string) (IdentitySettingInfo, error) {
|
||||
dc, err := c.domainsClient(ctx, cred, region, domainID)
|
||||
if err != nil {
|
||||
return IdentitySettingInfo{}, err
|
||||
}
|
||||
@@ -254,12 +317,12 @@ func (c *RealClient) GetIdentitySetting(ctx context.Context, cred Credentials, r
|
||||
}
|
||||
|
||||
// UpdateIdentitySetting 实现 Client:修改「用户需要提供主电子邮件地址」开关。
|
||||
func (c *RealClient) UpdateIdentitySetting(ctx context.Context, cred Credentials, region string, primaryEmailRequired bool) (IdentitySettingInfo, error) {
|
||||
current, err := c.GetIdentitySetting(ctx, cred, region)
|
||||
func (c *RealClient) UpdateIdentitySetting(ctx context.Context, cred Credentials, region, domainID string, primaryEmailRequired bool) (IdentitySettingInfo, error) {
|
||||
current, err := c.GetIdentitySetting(ctx, cred, region, domainID)
|
||||
if err != nil {
|
||||
return IdentitySettingInfo{}, err
|
||||
}
|
||||
dc, err := c.domainsClient(ctx, cred, region)
|
||||
dc, err := c.domainsClient(ctx, cred, region, domainID)
|
||||
if err != nil {
|
||||
return IdentitySettingInfo{}, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user