mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-11 00:57:00 +02:00
* Adding explicit MPL license for sub-package. This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Adding explicit MPL license for sub-package. This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Updating the license from MPL to Business Source License. Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at https://hashi.co/bsl-blog, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl. * add missing license headers * Update copyright file headers to BUS-1.1 * Fix test that expected exact offset on hcl file --------- Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com> Co-authored-by: Sarah Thompson <sthompson@hashicorp.com> Co-authored-by: Brian Kassouf <bkassouf@hashicorp.com>
145 lines
4.1 KiB
Go
145 lines
4.1 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package aws
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/url"
|
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
"github.com/aws/aws-sdk-go/service/iam"
|
|
"github.com/aws/aws-sdk-go/service/iam/iamiface"
|
|
"github.com/hashicorp/vault/sdk/logical"
|
|
)
|
|
|
|
// PolicyDocument represents an IAM policy document
|
|
type PolicyDocument struct {
|
|
Version string `json:"Version"`
|
|
Statements StatementEntries `json:"Statement"`
|
|
}
|
|
|
|
// StatementEntries is a slice of statements that make up a PolicyDocument
|
|
type StatementEntries []interface{}
|
|
|
|
// UnmarshalJSON is defined here for StatementEntries because the Statement
|
|
// portion of an IAM Policy can either be a list or a single element, so if it's
|
|
// a single element this wraps it in a []interface{} so that it's easy to
|
|
// combine with other policy statements:
|
|
// https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_statement.html
|
|
func (se *StatementEntries) UnmarshalJSON(b []byte) error {
|
|
var out StatementEntries
|
|
|
|
var data interface{}
|
|
if err := json.Unmarshal(b, &data); err != nil {
|
|
return err
|
|
}
|
|
|
|
switch t := data.(type) {
|
|
case []interface{}:
|
|
out = t
|
|
case interface{}:
|
|
out = []interface{}{t}
|
|
default:
|
|
return fmt.Errorf("unsupported data type %T for StatementEntries", t)
|
|
}
|
|
*se = out
|
|
return nil
|
|
}
|
|
|
|
// getGroupPolicies takes a list of IAM Group names and returns a list of their
|
|
// inline policy documents, and a list of the attached managed policy ARNs
|
|
func (b *backend) getGroupPolicies(ctx context.Context, s logical.Storage, iamGroups []string) ([]string, []string, error) {
|
|
var groupPolicies []string
|
|
var groupPolicyARNs []string
|
|
var err error
|
|
var agp *iam.ListAttachedGroupPoliciesOutput
|
|
var inlinePolicies *iam.ListGroupPoliciesOutput
|
|
var inlinePolicyDoc *iam.GetGroupPolicyOutput
|
|
var iamClient iamiface.IAMAPI
|
|
|
|
// Return early if there are no groups, to avoid creating an IAM client
|
|
// needlessly
|
|
if len(iamGroups) == 0 {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
iamClient, err = b.clientIAM(ctx, s)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
for _, g := range iamGroups {
|
|
// Collect managed policy ARNs from the IAM Group
|
|
agp, err = iamClient.ListAttachedGroupPoliciesWithContext(ctx, &iam.ListAttachedGroupPoliciesInput{
|
|
GroupName: aws.String(g),
|
|
})
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
for _, p := range agp.AttachedPolicies {
|
|
groupPolicyARNs = append(groupPolicyARNs, *p.PolicyArn)
|
|
}
|
|
|
|
// Collect inline policy names from the IAM Group
|
|
inlinePolicies, err = iamClient.ListGroupPoliciesWithContext(ctx, &iam.ListGroupPoliciesInput{
|
|
GroupName: aws.String(g),
|
|
})
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
for _, iP := range inlinePolicies.PolicyNames {
|
|
inlinePolicyDoc, err = iamClient.GetGroupPolicyWithContext(ctx, &iam.GetGroupPolicyInput{
|
|
GroupName: &g,
|
|
PolicyName: iP,
|
|
})
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
if inlinePolicyDoc != nil && inlinePolicyDoc.PolicyDocument != nil {
|
|
var policyStr string
|
|
if policyStr, err = url.QueryUnescape(*inlinePolicyDoc.PolicyDocument); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
groupPolicies = append(groupPolicies, policyStr)
|
|
}
|
|
}
|
|
}
|
|
return groupPolicies, groupPolicyARNs, nil
|
|
}
|
|
|
|
// combinePolicyDocuments takes policy strings as input, and combines them into
|
|
// a single policy document string
|
|
func combinePolicyDocuments(policies ...string) (string, error) {
|
|
var policy string
|
|
var err error
|
|
var policyBytes []byte
|
|
newPolicy := PolicyDocument{
|
|
// 2012-10-17 is the current version of the AWS policy language:
|
|
// https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_version.html
|
|
Version: "2012-10-17",
|
|
}
|
|
newPolicy.Statements = make(StatementEntries, 0, len(policies))
|
|
|
|
for _, p := range policies {
|
|
if len(p) == 0 {
|
|
continue
|
|
}
|
|
var tmpDoc PolicyDocument
|
|
err = json.Unmarshal([]byte(p), &tmpDoc)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
newPolicy.Statements = append(newPolicy.Statements, tmpDoc.Statements...)
|
|
}
|
|
|
|
policyBytes, err = json.Marshal(&newPolicy)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
policy = string(policyBytes)
|
|
return policy, nil
|
|
}
|