mirror of
https://github.com/versity/versitygw.git
synced 2026-09-28 18:55:54 +00:00
feat: add IAM OIDC provider tagging actions
Adds `TagOpenIDConnectProvider`, `UntagOpenIDConnectProvider` and `ListOpenIDConnectProviderTags` to the standalone IAM service, backed by both the internal and Vault storers. They follow the user and role tagging actions in most respects — the tag action merges into the provider's existing tags and rejects a repeated key, untag removal is idempotent, and the tag listing is sorted by key and paginated, with the per-request member count and the per-provider tag total enforced as separate quotas so replacing a tag on a provider already at the 50-tag cap still succeeds — but differ in the one respect IAM itself draws: OIDC provider tag keys are compared exactly, not case-insensitively. On a provider `env` and `ENV` are two independent tags, both may be supplied in a single request, only a byte-identical repeat is a duplicate (reported without the "Tag keys are case insensitive" note the user and role actions carry), and untagging `env` leaves `ENV` in place. That distinction is now carried by `iamutil.TagKeyCase`, which `ParseTags` uses for duplicate detection and which `mergeTags`, `removeTags` and the tag listing's marker lookup use for key matching. `CreateOpenIDConnectProvider` moves onto the exact comparison too, so a provider created with case-differing tag keys keeps both. All three actions are authorized against the target provider's ARN, so `aws:ResourceTag/<key>` reads the provider's own tags, and the tag and untag actions populate `aws:RequestTag/<key>` and `aws:TagKeys` respectively, so a tag-scoped policy Condition governs which tags a caller may set or remove. All three report a missing provider with the wording `DeleteOpenIDConnectProvider` uses rather than the one `GetOpenIDConnectProvider` uses, which is why the Vault provider read now takes the not-found error its calling action reports. The WebGUI gains a Tags section in the OIDC provider manage view, replacing the read-only tag row, and the shared tag editor gains a case-sensitive mode that changes its duplicate-key check, its diffing of an edited set into an untag and tag pair, and the wording of its guidance.
This commit is contained in:
@@ -349,7 +349,7 @@ func (s *InternalStore) UpdateUser(_ context.Context, input UpdateUserInput) (*t
|
||||
|
||||
func (s *InternalStore) TagUser(_ context.Context, userName string, tags []types.Tag) error {
|
||||
return s.updateUserTags(userName, func(user *types.User) error {
|
||||
merged, err := mergeTags(user.Tags, tags)
|
||||
merged, err := mergeTags(user.Tags, tags, iamutil.TagKeysFolded)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -360,7 +360,7 @@ func (s *InternalStore) TagUser(_ context.Context, userName string, tags []types
|
||||
|
||||
func (s *InternalStore) UntagUser(_ context.Context, userName string, tagKeys []string) error {
|
||||
return s.updateUserTags(userName, func(user *types.User) error {
|
||||
user.Tags = removeTags(user.Tags, tagKeys)
|
||||
user.Tags = removeTags(user.Tags, tagKeys, iamutil.TagKeysFolded)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -405,7 +405,7 @@ func (s *InternalStore) ListUserTags(_ context.Context, input ListUserTagsInput)
|
||||
return nil, iamerr.NoSuchEntityUser(input.UserName)
|
||||
}
|
||||
|
||||
return paginateTags(user.Tags, input.Marker, input.MaxItems), nil
|
||||
return paginateTags(user.Tags, input.Marker, input.MaxItems, iamutil.TagKeysFolded), nil
|
||||
}
|
||||
|
||||
func (s *InternalStore) CreateAccessKey(_ context.Context, input CreateAccessKeyInput) (*types.AccessKey, error) {
|
||||
@@ -959,7 +959,7 @@ func (s *InternalStore) UpdateAssumeRolePolicy(_ context.Context, input UpdateAs
|
||||
|
||||
func (s *InternalStore) TagRole(_ context.Context, roleName string, tags []types.Tag) error {
|
||||
return s.updateRoleTags(roleName, func(role *types.Role) error {
|
||||
merged, err := mergeTags(role.Tags, tags)
|
||||
merged, err := mergeTags(role.Tags, tags, iamutil.TagKeysFolded)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -970,7 +970,7 @@ func (s *InternalStore) TagRole(_ context.Context, roleName string, tags []types
|
||||
|
||||
func (s *InternalStore) UntagRole(_ context.Context, roleName string, tagKeys []string) error {
|
||||
return s.updateRoleTags(roleName, func(role *types.Role) error {
|
||||
role.Tags = removeTags(role.Tags, tagKeys)
|
||||
role.Tags = removeTags(role.Tags, tagKeys, iamutil.TagKeysFolded)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -1015,7 +1015,7 @@ func (s *InternalStore) ListRoleTags(_ context.Context, input ListRoleTagsInput)
|
||||
return nil, iamerr.NoSuchEntityRole(input.RoleName)
|
||||
}
|
||||
|
||||
return paginateTags(role.Tags, input.Marker, input.MaxItems), nil
|
||||
return paginateTags(role.Tags, input.Marker, input.MaxItems, iamutil.TagKeysFolded), nil
|
||||
}
|
||||
|
||||
func (s *InternalStore) PutRolePolicy(_ context.Context, input PutRolePolicyInput) error {
|
||||
@@ -1357,6 +1357,74 @@ func (s *InternalStore) UpdateOIDCProviderThumbprint(_ context.Context, arn stri
|
||||
return unwrapAPIError(err)
|
||||
}
|
||||
|
||||
func (s *InternalStore) TagOIDCProvider(_ context.Context, arn string, tags []types.Tag) error {
|
||||
return s.updateOIDCProviderTags(arn, func(provider *types.OIDCProvider) error {
|
||||
merged, err := mergeTags(provider.Tags, tags, iamutil.TagKeysExact)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
provider.Tags = merged
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (s *InternalStore) UntagOIDCProvider(_ context.Context, arn string, tagKeys []string) error {
|
||||
return s.updateOIDCProviderTags(arn, func(provider *types.OIDCProvider) error {
|
||||
provider.Tags = removeTags(provider.Tags, tagKeys, iamutil.TagKeysExact)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// updateOIDCProviderTags applies mutate to arn's stored record and writes
|
||||
// it back under the store lock.
|
||||
func (s *InternalStore) updateOIDCProviderTags(arn string, mutate func(*types.OIDCProvider) error) error {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
err := s.engine.StoreIAM(func(data []byte) ([]byte, error) {
|
||||
conf, err := s.engine.ParseIAM(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
url, err := iamutil.ParseOIDCProviderArn(arn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
provider, ok := conf.OIDCProviders[url]
|
||||
if !ok {
|
||||
return nil, iamerr.NoSuchEntityOIDCProviderDelete(arn)
|
||||
}
|
||||
if err := mutate(&provider); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conf.OIDCProviders[url] = provider
|
||||
return json.Marshal(conf)
|
||||
})
|
||||
return unwrapAPIError(err)
|
||||
}
|
||||
|
||||
func (s *InternalStore) ListOIDCProviderTags(_ context.Context, input ListOIDCProviderTagsInput) (*ListTagsOutput, error) {
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
|
||||
url, err := iamutil.ParseOIDCProviderArn(input.Arn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conf, err := s.engine.GetIAM()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
provider, ok := conf.OIDCProviders[url]
|
||||
if !ok {
|
||||
return nil, iamerr.NoSuchEntityOIDCProviderDelete(input.Arn)
|
||||
}
|
||||
|
||||
return paginateTags(provider.Tags, input.Marker, input.MaxItems, iamutil.TagKeysExact), nil
|
||||
}
|
||||
|
||||
func (s *InternalStore) CreateSession(_ context.Context, session types.Session) (*types.Session, error) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
Reference in New Issue
Block a user