/
backend_test.go
220 lines (193 loc) · 6.96 KB
/
backend_test.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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
package okta
import (
"context"
"fmt"
"os"
"strings"
"testing"
"time"
log "github.com/hashicorp/go-hclog"
logicaltest "github.com/hashicorp/vault/helper/testhelpers/logical"
"github.com/hashicorp/vault/sdk/helper/logging"
"github.com/hashicorp/vault/sdk/helper/policyutil"
"github.com/hashicorp/vault/sdk/logical"
)
// To run this test, set the following env variables:
// VAULT_ACC=1
// OKTA_ORG=dev-219337
// OKTA_API_TOKEN=<find in 1password>
// OKTA_USERNAME=test2@example.com
// OKTA_PASSWORD=<find in 1password>
//
// You will need to install the Okta client app on your mobile device and
// setup MFA.
func TestBackend_Config(t *testing.T) {
defaultLeaseTTLVal := time.Hour * 12
maxLeaseTTLVal := time.Hour * 24
b, err := Factory(context.Background(), &logical.BackendConfig{
Logger: logging.NewVaultLogger(log.Trace),
System: &logical.StaticSystemView{
DefaultLeaseTTLVal: defaultLeaseTTLVal,
MaxLeaseTTLVal: maxLeaseTTLVal,
},
})
if err != nil {
t.Fatalf("Unable to create backend: %s", err)
}
username := os.Getenv("OKTA_USERNAME")
password := os.Getenv("OKTA_PASSWORD")
token := os.Getenv("OKTA_API_TOKEN")
configData := map[string]interface{}{
"org_name": os.Getenv("OKTA_ORG"),
"base_url": "oktapreview.com",
}
updatedDuration := time.Hour * 1
configDataToken := map[string]interface{}{
"api_token": token,
"token_ttl": "1h",
}
logicaltest.Test(t, logicaltest.TestCase{
AcceptanceTest: true,
PreCheck: func() { testAccPreCheck(t) },
CredentialBackend: b,
Steps: []logicaltest.TestStep{
testConfigCreate(t, configData),
// 2. Login with bad password, expect failure (E0000004=okta auth failure).
testLoginWrite(t, username, "wrong", "E0000004", 0, nil),
// 3. Make our user belong to two groups and have one user-specific policy.
testAccUserGroups(t, username, "local_grouP,lOcal_group2", []string{"user_policy"}),
// 4. Create the group local_group, assign it a single policy.
testAccGroups(t, "local_groUp", "loCal_group_policy"),
// 5. Login with good password, expect user to have their user-specific
// policy and the policy of the one valid group they belong to.
testLoginWrite(t, username, password, "", defaultLeaseTTLVal, []string{"local_group_policy", "user_policy"}),
// 6. Create the group everyone, assign it two policies. This is a
// magic group name in okta that always exists and which every
// user automatically belongs to.
testAccGroups(t, "everyoNe", "everyone_grouP_policy,eveRy_group_policy2"),
// 7. Login as before, expect same result
testLoginWrite(t, username, password, "", defaultLeaseTTLVal, []string{"local_group_policy", "user_policy"}),
// 8. Add API token so we can lookup groups
testConfigUpdate(t, configDataToken),
testConfigRead(t, token, configData),
// 10. Login should now lookup okta groups; since all okta users are
// in the "everyone" group, that should be returned; since we
// defined policies attached to the everyone group, we should now
// see those policies attached to returned vault token.
testLoginWrite(t, username, password, "", updatedDuration, []string{"everyone_group_policy", "every_group_policy2", "local_group_policy", "user_policy"}),
testAccGroups(t, "locAl_group2", "testgroup_group_policy"),
testLoginWrite(t, username, password, "", updatedDuration, []string{"everyone_group_policy", "every_group_policy2", "local_group_policy", "testgroup_group_policy", "user_policy"}),
},
})
}
func testLoginWrite(t *testing.T, username, password, reason string, expectedTTL time.Duration, policies []string) logicaltest.TestStep {
return logicaltest.TestStep{
Operation: logical.UpdateOperation,
Path: "login/" + username,
ErrorOk: true,
Data: map[string]interface{}{
"password": password,
},
Check: func(resp *logical.Response) error {
if resp.IsError() {
if reason == "" || !strings.Contains(resp.Error().Error(), reason) {
return resp.Error()
}
} else if reason != "" {
return fmt.Errorf("expected error containing %q, got no error", reason)
}
if resp.Auth != nil {
if !policyutil.EquivalentPolicies(resp.Auth.Policies, policies) {
return fmt.Errorf("policy mismatch expected %v but got %v", policies, resp.Auth.Policies)
}
actualTTL := resp.Auth.LeaseOptions.TTL
if actualTTL != expectedTTL {
return fmt.Errorf("TTL mismatch expected %v but got %v", expectedTTL, actualTTL)
}
}
return nil
},
}
}
func testConfigCreate(t *testing.T, d map[string]interface{}) logicaltest.TestStep {
return logicaltest.TestStep{
Operation: logical.CreateOperation,
Path: "config",
Data: d,
}
}
func testConfigUpdate(t *testing.T, d map[string]interface{}) logicaltest.TestStep {
return logicaltest.TestStep{
Operation: logical.UpdateOperation,
Path: "config",
Data: d,
}
}
func testConfigRead(t *testing.T, token string, d map[string]interface{}) logicaltest.TestStep {
return logicaltest.TestStep{
Operation: logical.ReadOperation,
Path: "config",
Check: func(resp *logical.Response) error {
if resp.IsError() {
return resp.Error()
}
if resp.Data["org_name"] != d["org_name"] {
return fmt.Errorf("org mismatch expected %s but got %s", d["organization"], resp.Data["Org"])
}
if resp.Data["base_url"] != d["base_url"] {
return fmt.Errorf("BaseURL mismatch expected %s but got %s", d["base_url"], resp.Data["BaseURL"])
}
for _, value := range resp.Data {
if value == token {
return fmt.Errorf("token should not be returned on a read request")
}
}
return nil
},
}
}
func testAccPreCheck(t *testing.T) {
if v := os.Getenv("OKTA_USERNAME"); v == "" {
t.Fatal("OKTA_USERNAME must be set for acceptance tests")
}
if v := os.Getenv("OKTA_PASSWORD"); v == "" {
t.Fatal("OKTA_PASSWORD must be set for acceptance tests")
}
if v := os.Getenv("OKTA_ORG"); v == "" {
t.Fatal("OKTA_ORG must be set for acceptance tests")
}
if v := os.Getenv("OKTA_API_TOKEN"); v == "" {
t.Fatal("OKTA_API_TOKEN must be set for acceptance tests")
}
}
func testAccUserGroups(t *testing.T, user string, groups interface{}, policies interface{}) logicaltest.TestStep {
return logicaltest.TestStep{
Operation: logical.UpdateOperation,
Path: "users/" + user,
Data: map[string]interface{}{
"groups": groups,
"policies": policies,
},
}
}
func testAccGroups(t *testing.T, group string, policies interface{}) logicaltest.TestStep {
t.Logf("[testAccGroups] - Registering group %s, policy %s", group, policies)
return logicaltest.TestStep{
Operation: logical.UpdateOperation,
Path: "groups/" + group,
Data: map[string]interface{}{
"policies": policies,
},
}
}
func testAccLogin(t *testing.T, user, password string, keys []string) logicaltest.TestStep {
return logicaltest.TestStep{
Operation: logical.UpdateOperation,
Path: "login/" + user,
Data: map[string]interface{}{
"password": password,
},
Unauthenticated: true,
Check: logicaltest.TestCheckAuth(keys),
}
}