Files
versitygw/iamapi/policy/document.go
T
niksis02 e6573e11a9 feat: add IAM Role CRUD
Adds `CreateRole`, `GetRole`, `ListRoles`, `DeleteRole`, and `UpdateAssumeRolePolicy` to the standalone IAM service, following the same controller/storage patterns established for users. Both the internal filesystem/S3-backed store and the Vault-backed store implement the new `Storer` methods, with role-specific indexing and lookup helpers mirroring the existing user ones.

Role creation requires a trust policy, passed as `AssumeRolePolicyDocument`. A trust policy is a distinct kind of IAM policy document that governs who (or what) is allowed to assume a role, rather than what actions the role itself is permitted to perform. Its grammar is effectively the inverse of an identity policy: `Principal` is required, `Action`/`NotAction` values must carry the `sts:` prefix, and `Resource`/`NotResource` are forbidden. This is implemented in `iamapi/policy/trust.go` as a new validation path alongside the existing identity-policy validation, and is reused by `UpdateAssumeRolePolicy` when replacing a role's trust policy.

Also fixes user name uniqueness enforcement to be case-insensitive, matching AWS IAM behavior, and applies the same case-insensitive handling to role names. The internal store now maintains lowercase name indexes for both users and roles, and the Vault store resolves the canonical stored key via a case-insensitive list-and-compare fallback since Vault's KV paths are case-sensitive.
2026-07-14 22:24:01 +04:00

108 lines
3.0 KiB
Go

// Copyright 2026 Versity Software
// This file is licensed under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package policy
import (
"bytes"
"encoding/json"
)
// Recognized values for a policy document's Version element.
const (
Version2008 = "2008-10-17"
Version2012 = "2012-10-17"
)
// Document is a parsed AWS IAM policy document.
type Document struct {
Version string
Statement []Statement
}
// Statement is a single element of a policy document's Statement list.
type Statement struct {
Sid string
Effect string
Action StringOrSlice
NotAction StringOrSlice
Resource StringOrSlice
NotResource StringOrSlice
Principal json.RawMessage
NotPrincipal json.RawMessage
// Condition is never structurally validated (neither the identity- nor
// trust-policy path models its grammar) — it is only checked for
// presence, by the trust-policy Cognito-provider rule.
Condition json.RawMessage
}
// UnmarshalJSON accepts Statement as either a single JSON object or an
// array of objects, matching the AWS IAM policy grammar. A missing or
// JSON-null Statement leaves Document.Statement nil rather than erroring
// here — Validate reports that as a grammar error so all "empty document"
// shapes produce the same message.
func (d *Document) UnmarshalJSON(data []byte) error {
var raw struct {
Version string
Statement json.RawMessage
}
if err := json.Unmarshal(data, &raw); err != nil {
return err
}
d.Version = raw.Version
if len(raw.Statement) == 0 || string(bytes.TrimSpace(raw.Statement)) == "null" {
return nil
}
var stmts []Statement
if err := json.Unmarshal(raw.Statement, &stmts); err == nil {
d.Statement = stmts
return nil
}
var single Statement
if err := json.Unmarshal(raw.Statement, &single); err != nil {
return err
}
d.Statement = []Statement{single}
return nil
}
// StringOrSlice decodes a JSON value that may be either a single string or
// an array of strings, matching the AWS IAM policy grammar for Action,
// NotAction, Resource, and NotResource. A JSON-null value decodes to a nil
// StringOrSlice, identical to the key being absent.
type StringOrSlice []string
func (s *StringOrSlice) UnmarshalJSON(data []byte) error {
if string(bytes.TrimSpace(data)) == "null" {
*s = nil
return nil
}
var single string
if err := json.Unmarshal(data, &single); err == nil {
*s = StringOrSlice{single}
return nil
}
var multi []string
if err := json.Unmarshal(data, &multi); err != nil {
return err
}
*s = StringOrSlice(multi)
return nil
}