-
Notifications
You must be signed in to change notification settings - Fork 423
/
Copy patharn.go
110 lines (95 loc) · 3.18 KB
/
arn.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package arn
import (
"fmt"
"strings"
awsarn "github.com/aws/aws-sdk-go/aws/arn"
"github.com/aws/aws-sdk-go/aws/endpoints"
)
type PrincipalType int
const (
// Supported principals
NONE PrincipalType = iota
ROLE
USER
ROOT
FEDERATED_USER
ASSUMED_ROLE
)
// Canonicalize validates IAM resources are appropriate for the authenticator
// and converts STS assumed roles into the IAM role resource.
//
// Supported IAM resources are:
// - AWS root user: arn:aws:iam::123456789012:root
// - IAM user: arn:aws:iam::123456789012:user/Bob
// - IAM role: arn:aws:iam::123456789012:role/S3Access
// - IAM Assumed role: arn:aws:sts::123456789012:assumed-role/Accounting-Role/Mary (converted to IAM role)
// - Federated user: arn:aws:sts::123456789012:federated-user/Bob
func Canonicalize(arn string) (PrincipalType, string, error) {
parsed, err := awsarn.Parse(arn)
if err != nil {
return NONE, "", fmt.Errorf("arn '%s' is invalid: '%v'", arn, err)
}
if err := checkPartition(parsed.Partition); err != nil {
return NONE, "", fmt.Errorf("arn '%s' does not have a recognized partition", arn)
}
parts := strings.Split(parsed.Resource, "/")
resource := parts[0]
switch parsed.Service {
case "sts":
switch resource {
case "federated-user":
return FEDERATED_USER, arn, nil
case "assumed-role":
if len(parts) < 3 {
return NONE, "", fmt.Errorf("assumed-role arn '%s' does not have a role", arn)
}
// IAM ARNs can contain paths, part[0] is resource, parts[len(parts)] is the SessionName.
role := strings.Join(parts[1:len(parts)-1], "/")
return ASSUMED_ROLE, fmt.Sprintf("arn:%s:iam::%s:role/%s", parsed.Partition, parsed.AccountID, role), nil
default:
return NONE, "", fmt.Errorf("unrecognized resource %s for service sts", parsed.Resource)
}
case "iam":
switch resource {
case "role":
return ROLE, arn, nil
case "user":
return USER, arn, nil
case "root":
return ROOT, arn, nil
default:
return NONE, "", fmt.Errorf("unrecognized resource %s for service iam", parsed.Resource)
}
}
return NONE, "", fmt.Errorf("service %s in arn %s is not a valid service for identities", parsed.Service, arn)
}
// TODO: add strip path functionality Canonicalize after testing it in all mappers - this can be used to support role paths in the configmap
func StripPath(arn string) (string, error) {
parsed, err := awsarn.Parse(arn)
if err != nil {
return "", fmt.Errorf("arn '%s' is invalid: '%v'", arn, err)
}
if err := checkPartition(parsed.Partition); err != nil {
return "", fmt.Errorf("arn '%s' does not have a recognized partition", arn)
}
parts := strings.Split(parsed.Resource, "/")
resource := parts[0]
if resource != "role" {
return arn, nil
}
if len(parts) > 2 {
// Stripping off the path means we just need to keep the first and last part of the arn resource
// role/path/for/this-role/matt -> role/matt
role := parts[len(parts)-1]
return fmt.Sprintf("arn:%s:iam::%s:role/%s", parsed.Partition, parsed.AccountID, role), nil
}
return arn, nil
}
func checkPartition(partition string) error {
for _, p := range endpoints.DefaultPartitions() {
if partition == p.ID() {
return nil
}
}
return fmt.Errorf("partition %s is not recognized", partition)
}