1package acl
2
3import (
4	"fmt"
5	"strings"
6
7	"github.com/hashicorp/consul/agent/structs"
8	"github.com/hashicorp/consul/api"
9)
10
11func GetTokenIDFromPartial(client *api.Client, partialID string) (string, error) {
12	if partialID == "anonymous" {
13		return structs.ACLTokenAnonymousID, nil
14	}
15
16	// the full UUID string was given
17	if len(partialID) == 36 {
18		return partialID, nil
19	}
20
21	tokens, _, err := client.ACL().TokenList(nil)
22	if err != nil {
23		return "", err
24	}
25
26	tokenID := ""
27	for _, token := range tokens {
28		if strings.HasPrefix(token.AccessorID, partialID) {
29			if tokenID != "" {
30				return "", fmt.Errorf("Partial token ID is not unique")
31			}
32			tokenID = token.AccessorID
33		}
34	}
35
36	if tokenID == "" {
37		return "", fmt.Errorf("No such token ID with prefix: %s", partialID)
38	}
39
40	return tokenID, nil
41}
42
43func GetPolicyIDFromPartial(client *api.Client, partialID string) (string, error) {
44	if partialID == "global-management" {
45		return structs.ACLPolicyGlobalManagementID, nil
46	}
47	// The full UUID string was given
48	if len(partialID) == 36 {
49		return partialID, nil
50	}
51
52	policies, _, err := client.ACL().PolicyList(nil)
53	if err != nil {
54		return "", err
55	}
56
57	policyID := ""
58	for _, policy := range policies {
59		if strings.HasPrefix(policy.ID, partialID) {
60			if policyID != "" {
61				return "", fmt.Errorf("Partial policy ID is not unique")
62			}
63			policyID = policy.ID
64		}
65	}
66
67	if policyID == "" {
68		return "", fmt.Errorf("No such policy ID with prefix: %s", partialID)
69	}
70
71	return policyID, nil
72}
73
74func GetPolicyIDByName(client *api.Client, name string) (string, error) {
75	if name == "" {
76		return "", fmt.Errorf("No name specified")
77	}
78
79	policies, _, err := client.ACL().PolicyList(nil)
80	if err != nil {
81		return "", err
82	}
83
84	for _, policy := range policies {
85		if policy.Name == name {
86			return policy.ID, nil
87		}
88	}
89
90	return "", fmt.Errorf("No such policy with name %s", name)
91}
92
93func GetRulesFromLegacyToken(client *api.Client, tokenID string, isSecret bool) (string, error) {
94	tokenID, err := GetTokenIDFromPartial(client, tokenID)
95	if err != nil {
96		return "", err
97	}
98
99	var token *api.ACLToken
100	if isSecret {
101		qopts := api.QueryOptions{
102			Token: tokenID,
103		}
104		token, _, err = client.ACL().TokenReadSelf(&qopts)
105	} else {
106		token, _, err = client.ACL().TokenRead(tokenID, nil)
107	}
108
109	if err != nil {
110		return "", fmt.Errorf("Error reading token: %v", err)
111	}
112
113	if token == nil {
114		return "", fmt.Errorf("Token not found for ID")
115	}
116
117	if token.Rules == "" {
118		return "", fmt.Errorf("Token is not a legacy token with rules")
119	}
120
121	return token.Rules, nil
122}
123
124func GetRoleIDFromPartial(client *api.Client, partialID string) (string, error) {
125	// the full UUID string was given
126	if len(partialID) == 36 {
127		return partialID, nil
128	}
129
130	roles, _, err := client.ACL().RoleList(nil)
131	if err != nil {
132		return "", err
133	}
134
135	roleID := ""
136	for _, role := range roles {
137		if strings.HasPrefix(role.ID, partialID) {
138			if roleID != "" {
139				return "", fmt.Errorf("Partial role ID is not unique")
140			}
141			roleID = role.ID
142		}
143	}
144
145	if roleID == "" {
146		return "", fmt.Errorf("No such role ID with prefix: %s", partialID)
147	}
148
149	return roleID, nil
150}
151
152func GetRoleIDByName(client *api.Client, name string) (string, error) {
153	if name == "" {
154		return "", fmt.Errorf("No name specified")
155	}
156
157	roles, _, err := client.ACL().RoleList(nil)
158	if err != nil {
159		return "", err
160	}
161
162	for _, role := range roles {
163		if role.Name == name {
164			return role.ID, nil
165		}
166	}
167
168	return "", fmt.Errorf("No such role with name %s", name)
169}
170
171func GetBindingRuleIDFromPartial(client *api.Client, partialID string) (string, error) {
172	// the full UUID string was given
173	if len(partialID) == 36 {
174		return partialID, nil
175	}
176
177	rules, _, err := client.ACL().BindingRuleList("", nil)
178	if err != nil {
179		return "", err
180	}
181
182	ruleID := ""
183	for _, rule := range rules {
184		if strings.HasPrefix(rule.ID, partialID) {
185			if ruleID != "" {
186				return "", fmt.Errorf("Partial rule ID is not unique")
187			}
188			ruleID = rule.ID
189		}
190	}
191
192	if ruleID == "" {
193		return "", fmt.Errorf("No such rule ID with prefix: %s", partialID)
194	}
195
196	return ruleID, nil
197}
198
199func ExtractServiceIdentities(serviceIdents []string) ([]*api.ACLServiceIdentity, error) {
200	var out []*api.ACLServiceIdentity
201	for _, svcidRaw := range serviceIdents {
202		parts := strings.Split(svcidRaw, ":")
203		switch len(parts) {
204		case 2:
205			out = append(out, &api.ACLServiceIdentity{
206				ServiceName: parts[0],
207				Datacenters: strings.Split(parts[1], ","),
208			})
209		case 1:
210			out = append(out, &api.ACLServiceIdentity{
211				ServiceName: parts[0],
212			})
213		default:
214			return nil, fmt.Errorf("Malformed -service-identity argument: %q", svcidRaw)
215		}
216	}
217	return out, nil
218}
219
220func ExtractNodeIdentities(nodeIdents []string) ([]*api.ACLNodeIdentity, error) {
221	var out []*api.ACLNodeIdentity
222	for _, nodeidRaw := range nodeIdents {
223		parts := strings.Split(nodeidRaw, ":")
224		switch len(parts) {
225		case 2:
226			out = append(out, &api.ACLNodeIdentity{
227				NodeName:   parts[0],
228				Datacenter: parts[1],
229			})
230		default:
231			return nil, fmt.Errorf("Malformed -node-identity argument: %q", nodeidRaw)
232		}
233	}
234	return out, nil
235}
236
237// TestKubernetesJWT_A is a valid service account jwt extracted from a minikube setup.
238//
239// {
240//   "iss": "kubernetes/serviceaccount",
241//   "kubernetes.io/serviceaccount/namespace": "default",
242//   "kubernetes.io/serviceaccount/secret.name": "admin-token-qlz42",
243//   "kubernetes.io/serviceaccount/service-account.name": "admin",
244//   "kubernetes.io/serviceaccount/service-account.uid": "738bc251-6532-11e9-b67f-48e6c8b8ecb5",
245//   "sub": "system:serviceaccount:default:admin"
246// }
247const TestKubernetesJWT_A = "eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImFkbWluLXRva2VuLXFsejQyIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiNzM4YmMyNTEtNjUzMi0xMWU5LWI2N2YtNDhlNmM4YjhlY2I1Iiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6YWRtaW4ifQ.ixMlnWrAG7NVuTTKu8cdcYfM7gweS3jlKaEsIBNGOVEjPE7rtXtgMkAwjQTdYR08_0QBjkgzy5fQC5ZNyglSwONJ-bPaXGvhoH1cTnRi1dz9H_63CfqOCvQP1sbdkMeRxNTGVAyWZT76rXoCUIfHP4LY2I8aab0KN9FTIcgZRF0XPTtT70UwGIrSmRpxW38zjiy2ymWL01cc5VWGhJqVysmWmYk3wNp0h5N57H_MOrz4apQR4pKaamzskzjLxO55gpbmZFC76qWuUdexAR7DT2fpbHLOw90atN_NlLMY-VrXyW3-Ei5EhYaVreMB9PSpKwkrA4jULITohV-sxpa1LA"
248
249// TestKubernetesJWT_B is a valid service account jwt extracted from a minikube setup.
250//
251// {
252// "iss": "kubernetes/serviceaccount",
253// "kubernetes.io/serviceaccount/namespace": "default",
254// "kubernetes.io/serviceaccount/secret.name": "demo-token-kmb9n",
255// "kubernetes.io/serviceaccount/service-account.name": "demo",
256// "kubernetes.io/serviceaccount/service-account.uid": "76091af4-4b56-11e9-ac4b-708b11801cbe",
257// "sub": "system:serviceaccount:default:demo"
258// }
259const TestKubernetesJWT_B = "eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlbW8tdG9rZW4ta21iOW4iLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVtbyIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6Ijc2MDkxYWY0LTRiNTYtMTFlOS1hYzRiLTcwOGIxMTgwMWNiZSIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlbW8ifQ.ZiAHjijBAOsKdum0Aix6lgtkLkGo9_Tu87dWQ5Zfwnn3r2FejEWDAnftTft1MqqnMzivZ9Wyyki5ZjQRmTAtnMPJuHC-iivqY4Wh4S6QWCJ1SivBv5tMZR79t5t8mE7R1-OHwst46spru1pps9wt9jsA04d3LpV0eeKYgdPTVaQKklxTm397kIMUugA6yINIBQ3Rh8eQqBgNwEmL4iqyYubzHLVkGkoP9MJikFI05vfRiHtYr-piXz6JFDzXMQj9rW6xtMmrBSn79ChbyvC5nz-Nj2rJPnHsb_0rDUbmXY5PpnMhBpdSH-CbZ4j8jsiib6DtaGJhVZeEQ1GjsFAZwQ"
260