1package approle
2
3import (
4	"context"
5	"testing"
6	"time"
7
8	"github.com/hashicorp/vault/sdk/logical"
9)
10
11func TestAppRole_BoundCIDRLogin(t *testing.T) {
12	var resp *logical.Response
13	var err error
14	b, s := createBackendWithStorage(t)
15
16	// Create a role with secret ID binding disabled and only bound cidr list
17	// enabled
18	resp, err = b.HandleRequest(context.Background(), &logical.Request{
19		Path:      "role/testrole",
20		Operation: logical.CreateOperation,
21		Data: map[string]interface{}{
22			"bind_secret_id":    false,
23			"bound_cidr_list":   []string{"127.0.0.1/8"},
24			"token_bound_cidrs": []string{"10.0.0.0/8"},
25		},
26		Storage: s,
27	})
28	if err != nil || (resp != nil && resp.IsError()) {
29		t.Fatalf("err:%v resp:%#v", err, resp)
30	}
31
32	// Read the role ID
33	resp, err = b.HandleRequest(context.Background(), &logical.Request{
34		Path:      "role/testrole/role-id",
35		Operation: logical.ReadOperation,
36		Storage:   s,
37	})
38	if err != nil || (resp != nil && resp.IsError()) {
39		t.Fatalf("err:%v resp:%#v", err, resp)
40	}
41
42	roleID := resp.Data["role_id"]
43
44	// Fill in the connection information and login with just the role ID
45	resp, err = b.HandleRequest(context.Background(), &logical.Request{
46		Path:      "login",
47		Operation: logical.UpdateOperation,
48		Data: map[string]interface{}{
49			"role_id": roleID,
50		},
51		Storage:    s,
52		Connection: &logical.Connection{RemoteAddr: "127.0.0.1"},
53	})
54	if err != nil || (resp != nil && resp.IsError()) {
55		t.Fatalf("err:%v resp:%#v", err, resp)
56	}
57	if resp.Auth == nil {
58		t.Fatal("expected login to succeed")
59	}
60	if len(resp.Auth.BoundCIDRs) != 1 {
61		t.Fatal("bad token bound cidrs")
62	}
63	if resp.Auth.BoundCIDRs[0].String() != "10.0.0.0/8" {
64		t.Fatalf("bad: %s", resp.Auth.BoundCIDRs[0].String())
65	}
66
67	// Override with a secret-id value, verify it doesn't pass
68	resp, err = b.HandleRequest(context.Background(), &logical.Request{
69		Path:      "role/testrole",
70		Operation: logical.UpdateOperation,
71		Data: map[string]interface{}{
72			"bind_secret_id": true,
73		},
74		Storage: s,
75	})
76	if err != nil || (resp != nil && resp.IsError()) {
77		t.Fatalf("err:%v resp:%#v", err, resp)
78	}
79
80	roleSecretIDReq := &logical.Request{
81		Operation: logical.UpdateOperation,
82		Path:      "role/testrole/secret-id",
83		Storage:   s,
84		Data: map[string]interface{}{
85			"token_bound_cidrs": []string{"11.0.0.0/24"},
86		},
87	}
88	resp, err = b.HandleRequest(context.Background(), roleSecretIDReq)
89	if err == nil {
90		t.Fatal("expected error due to mismatching subnet relationship")
91	}
92
93	roleSecretIDReq.Data["token_bound_cidrs"] = "10.0.0.0/24"
94	resp, err = b.HandleRequest(context.Background(), roleSecretIDReq)
95	if err != nil || (resp != nil && resp.IsError()) {
96		t.Fatalf("err:%v resp:%#v", err, resp)
97	}
98	secretID := resp.Data["secret_id"]
99
100	resp, err = b.HandleRequest(context.Background(), &logical.Request{
101		Path:      "login",
102		Operation: logical.UpdateOperation,
103		Data: map[string]interface{}{
104			"role_id":   roleID,
105			"secret_id": secretID,
106		},
107		Storage:    s,
108		Connection: &logical.Connection{RemoteAddr: "127.0.0.1"},
109	})
110	if err != nil || (resp != nil && resp.IsError()) {
111		t.Fatalf("err:%v resp:%#v", err, resp)
112	}
113	if resp.Auth == nil {
114		t.Fatal("expected login to succeed")
115	}
116	if len(resp.Auth.BoundCIDRs) != 1 {
117		t.Fatal("bad token bound cidrs")
118	}
119	if resp.Auth.BoundCIDRs[0].String() != "10.0.0.0/24" {
120		t.Fatalf("bad: %s", resp.Auth.BoundCIDRs[0].String())
121	}
122}
123
124func TestAppRole_RoleLogin(t *testing.T) {
125	var resp *logical.Response
126	var err error
127	b, storage := createBackendWithStorage(t)
128
129	createRole(t, b, storage, "role1", "a,b,c")
130	roleRoleIDReq := &logical.Request{
131		Operation: logical.ReadOperation,
132		Path:      "role/role1/role-id",
133		Storage:   storage,
134	}
135	resp, err = b.HandleRequest(context.Background(), roleRoleIDReq)
136	if err != nil || (resp != nil && resp.IsError()) {
137		t.Fatalf("err:%v resp:%#v", err, resp)
138	}
139	roleID := resp.Data["role_id"]
140
141	roleSecretIDReq := &logical.Request{
142		Operation: logical.UpdateOperation,
143		Path:      "role/role1/secret-id",
144		Storage:   storage,
145	}
146	resp, err = b.HandleRequest(context.Background(), roleSecretIDReq)
147	if err != nil || (resp != nil && resp.IsError()) {
148		t.Fatalf("err:%v resp:%#v", err, resp)
149	}
150	secretID := resp.Data["secret_id"]
151
152	loginData := map[string]interface{}{
153		"role_id":   roleID,
154		"secret_id": secretID,
155	}
156	loginReq := &logical.Request{
157		Operation: logical.UpdateOperation,
158		Path:      "login",
159		Storage:   storage,
160		Data:      loginData,
161		Connection: &logical.Connection{
162			RemoteAddr: "127.0.0.1",
163		},
164	}
165	loginResp, err := b.HandleRequest(context.Background(), loginReq)
166	if err != nil || (loginResp != nil && loginResp.IsError()) {
167		t.Fatalf("err:%v resp:%#v", err, loginResp)
168	}
169
170	if loginResp.Auth == nil {
171		t.Fatalf("expected a non-nil auth object in the response")
172	}
173
174	if loginResp.Auth.Metadata == nil {
175		t.Fatalf("expected a non-nil metadata object in the response")
176	}
177
178	if val := loginResp.Auth.Metadata["role_name"]; val != "role1" {
179		t.Fatalf("expected metadata.role_name to equal 'role1', got: %v", val)
180	}
181
182	if loginResp.Auth.Alias.Metadata == nil {
183		t.Fatalf("expected a non-nil alias metadata object in the response")
184	}
185
186	if val := loginResp.Auth.Alias.Metadata["role_name"]; val != "role1" {
187		t.Fatalf("expected metadata.alias.role_name to equal 'role1', got: %v", val)
188	}
189
190	// Test renewal
191	renewReq := generateRenewRequest(storage, loginResp.Auth)
192
193	resp, err = b.HandleRequest(context.Background(), renewReq)
194	if err != nil || (resp != nil && resp.IsError()) {
195		t.Fatalf("err:%v resp:%#v", err, resp)
196	}
197
198	if resp.Auth.TTL != 400*time.Second {
199		t.Fatalf("expected period value from response to be 400s, got: %s", resp.Auth.TTL)
200	}
201
202	///
203	// Test renewal with period
204	///
205
206	// Create role
207	period := 600 * time.Second
208	roleData := map[string]interface{}{
209		"policies": "a,b,c",
210		"period":   period.String(),
211	}
212	roleReq := &logical.Request{
213		Operation: logical.CreateOperation,
214		Path:      "role/" + "role-period",
215		Storage:   storage,
216		Data:      roleData,
217	}
218	resp, err = b.HandleRequest(context.Background(), roleReq)
219	if err != nil || (resp != nil && resp.IsError()) {
220		t.Fatalf("err:%v resp:%#v", err, resp)
221	}
222
223	roleRoleIDReq = &logical.Request{
224		Operation: logical.ReadOperation,
225		Path:      "role/role-period/role-id",
226		Storage:   storage,
227	}
228	resp, err = b.HandleRequest(context.Background(), roleRoleIDReq)
229	if err != nil || (resp != nil && resp.IsError()) {
230		t.Fatalf("err:%v resp:%#v", err, resp)
231	}
232	roleID = resp.Data["role_id"]
233
234	roleSecretIDReq = &logical.Request{
235		Operation: logical.UpdateOperation,
236		Path:      "role/role-period/secret-id",
237		Storage:   storage,
238	}
239	resp, err = b.HandleRequest(context.Background(), roleSecretIDReq)
240	if err != nil || (resp != nil && resp.IsError()) {
241		t.Fatalf("err:%v resp:%#v", err, resp)
242	}
243	secretID = resp.Data["secret_id"]
244
245	loginData["role_id"] = roleID
246	loginData["secret_id"] = secretID
247
248	loginResp, err = b.HandleRequest(context.Background(), loginReq)
249	if err != nil || (loginResp != nil && loginResp.IsError()) {
250		t.Fatalf("err:%v resp:%#v", err, loginResp)
251	}
252
253	if loginResp.Auth == nil {
254		t.Fatalf("expected a non-nil auth object in the response")
255	}
256
257	renewReq = generateRenewRequest(storage, loginResp.Auth)
258
259	resp, err = b.HandleRequest(context.Background(), renewReq)
260	if err != nil || (resp != nil && resp.IsError()) {
261		t.Fatalf("err:%v resp:%#v", err, resp)
262	}
263
264	if resp.Auth.Period != period {
265		t.Fatalf("expected period value of %d in the response, got: %s", period, resp.Auth.Period)
266	}
267}
268
269func generateRenewRequest(s logical.Storage, auth *logical.Auth) *logical.Request {
270	renewReq := &logical.Request{
271		Operation: logical.RenewOperation,
272		Storage:   s,
273		Auth:      &logical.Auth{},
274	}
275	renewReq.Auth.InternalData = auth.InternalData
276	renewReq.Auth.Metadata = auth.Metadata
277	renewReq.Auth.LeaseOptions = auth.LeaseOptions
278	renewReq.Auth.Policies = auth.Policies
279	renewReq.Auth.Period = auth.Period
280
281	return renewReq
282}
283