2017-01-27 00:08:52 +00:00
|
|
|
package okta
|
|
|
|
|
|
|
|
import (
|
2018-01-19 06:44:44 +00:00
|
|
|
"context"
|
2017-01-27 00:08:52 +00:00
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
2020-02-03 17:51:10 +00:00
|
|
|
"time"
|
2017-01-27 00:08:52 +00:00
|
|
|
|
2018-04-03 00:46:59 +00:00
|
|
|
log "github.com/hashicorp/go-hclog"
|
2020-02-03 17:51:10 +00:00
|
|
|
logicaltest "github.com/hashicorp/vault/helper/testhelpers/logical"
|
2019-04-12 21:54:35 +00:00
|
|
|
"github.com/hashicorp/vault/sdk/helper/logging"
|
2019-04-12 22:08:46 +00:00
|
|
|
"github.com/hashicorp/vault/sdk/helper/policyutil"
|
2019-04-13 07:44:06 +00:00
|
|
|
"github.com/hashicorp/vault/sdk/logical"
|
2017-01-27 00:08:52 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestBackend_Config(t *testing.T) {
|
2017-07-05 13:42:37 +00:00
|
|
|
defaultLeaseTTLVal := time.Hour * 12
|
|
|
|
maxLeaseTTLVal := time.Hour * 24
|
2018-01-19 06:44:44 +00:00
|
|
|
b, err := Factory(context.Background(), &logical.BackendConfig{
|
2018-04-03 00:46:59 +00:00
|
|
|
Logger: logging.NewVaultLogger(log.Trace),
|
2017-07-05 13:42:37 +00:00
|
|
|
System: &logical.StaticSystemView{
|
|
|
|
DefaultLeaseTTLVal: defaultLeaseTTLVal,
|
|
|
|
MaxLeaseTTLVal: maxLeaseTTLVal,
|
|
|
|
},
|
2017-01-27 00:08:52 +00:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Unable to create backend: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
username := os.Getenv("OKTA_USERNAME")
|
|
|
|
password := os.Getenv("OKTA_PASSWORD")
|
2017-07-05 13:42:37 +00:00
|
|
|
token := os.Getenv("OKTA_API_TOKEN")
|
2017-01-27 00:08:52 +00:00
|
|
|
|
|
|
|
configData := map[string]interface{}{
|
2020-02-03 17:51:10 +00:00
|
|
|
"org_name": os.Getenv("OKTA_ORG"),
|
|
|
|
"base_url": "oktapreview.com",
|
2017-01-27 00:08:52 +00:00
|
|
|
}
|
|
|
|
|
2017-07-05 13:42:37 +00:00
|
|
|
updatedDuration := time.Hour * 1
|
2017-01-27 00:08:52 +00:00
|
|
|
configDataToken := map[string]interface{}{
|
2020-02-03 17:51:10 +00:00
|
|
|
"api_token": token,
|
|
|
|
"token_ttl": "1h",
|
2017-01-27 00:08:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
logicaltest.Test(t, logicaltest.TestCase{
|
2020-02-03 17:51:10 +00:00
|
|
|
AcceptanceTest: true,
|
|
|
|
PreCheck: func() { testAccPreCheck(t) },
|
|
|
|
CredentialBackend: b,
|
2017-01-27 00:08:52 +00:00
|
|
|
Steps: []logicaltest.TestStep{
|
|
|
|
testConfigCreate(t, configData),
|
2020-02-03 17:51:10 +00:00
|
|
|
// 2. Login with bad password, expect failure (E0000004=okta auth failure).
|
2017-07-05 13:42:37 +00:00
|
|
|
testLoginWrite(t, username, "wrong", "E0000004", 0, nil),
|
2020-02-03 17:51:10 +00:00
|
|
|
// 3. Make our user belong to two groups and have one user-specific policy.
|
2018-01-16 23:20:19 +00:00
|
|
|
testAccUserGroups(t, username, "local_grouP,lOcal_group2", []string{"user_policy"}),
|
2020-02-03 17:51:10 +00:00
|
|
|
// 4. Create the group local_group, assign it a single policy.
|
2017-08-25 18:48:37 +00:00
|
|
|
testAccGroups(t, "local_groUp", "loCal_group_policy"),
|
2020-02-03 17:51:10 +00:00
|
|
|
// 5. Login with good password, expect user to have their user-specific
|
|
|
|
// policy and the policy of the one valid group they belong to.
|
2018-01-16 23:20:19 +00:00
|
|
|
testLoginWrite(t, username, password, "", defaultLeaseTTLVal, []string{"local_group_policy", "user_policy"}),
|
2020-02-03 17:51:10 +00:00
|
|
|
// 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.
|
2017-08-25 18:48:37 +00:00
|
|
|
testAccGroups(t, "everyoNe", "everyone_grouP_policy,eveRy_group_policy2"),
|
2020-02-03 17:51:10 +00:00
|
|
|
// 7. Login as before, expect same result
|
2018-01-16 23:20:19 +00:00
|
|
|
testLoginWrite(t, username, password, "", defaultLeaseTTLVal, []string{"local_group_policy", "user_policy"}),
|
2020-02-03 17:51:10 +00:00
|
|
|
// 8. Add API token so we can lookup groups
|
2017-01-27 00:08:52 +00:00
|
|
|
testConfigUpdate(t, configDataToken),
|
2017-07-05 13:42:37 +00:00
|
|
|
testConfigRead(t, token, configData),
|
2020-02-03 17:51:10 +00:00
|
|
|
// 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.
|
2018-01-16 23:20:19 +00:00
|
|
|
testLoginWrite(t, username, password, "", updatedDuration, []string{"everyone_group_policy", "every_group_policy2", "local_group_policy", "user_policy"}),
|
2017-08-25 18:48:37 +00:00
|
|
|
testAccGroups(t, "locAl_group2", "testgroup_group_policy"),
|
2018-01-16 23:20:19 +00:00
|
|
|
testLoginWrite(t, username, password, "", updatedDuration, []string{"everyone_group_policy", "every_group_policy2", "local_group_policy", "testgroup_group_policy", "user_policy"}),
|
2017-01-27 00:08:52 +00:00
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-07-05 13:42:37 +00:00
|
|
|
func testLoginWrite(t *testing.T, username, password, reason string, expectedTTL time.Duration, policies []string) logicaltest.TestStep {
|
2017-01-27 00:08:52 +00:00
|
|
|
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()
|
|
|
|
}
|
2020-02-03 17:51:10 +00:00
|
|
|
} else if reason != "" {
|
|
|
|
return fmt.Errorf("expected error containing %q, got no error", reason)
|
|
|
|
|
2017-01-27 00:08:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if resp.Auth != nil {
|
2017-02-14 21:28:16 +00:00
|
|
|
if !policyutil.EquivalentPolicies(resp.Auth.Policies, policies) {
|
|
|
|
return fmt.Errorf("policy mismatch expected %v but got %v", policies, resp.Auth.Policies)
|
2017-01-27 00:08:52 +00:00
|
|
|
}
|
2017-07-05 13:42:37 +00:00
|
|
|
|
|
|
|
actualTTL := resp.Auth.LeaseOptions.TTL
|
|
|
|
if actualTTL != expectedTTL {
|
|
|
|
return fmt.Errorf("TTL mismatch expected %v but got %v", expectedTTL, actualTTL)
|
|
|
|
}
|
2017-01-27 00:08:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-05 13:42:37 +00:00
|
|
|
func testConfigRead(t *testing.T, token string, d map[string]interface{}) logicaltest.TestStep {
|
2017-01-27 00:08:52 +00:00
|
|
|
return logicaltest.TestStep{
|
|
|
|
Operation: logical.ReadOperation,
|
|
|
|
Path: "config",
|
|
|
|
Check: func(resp *logical.Response) error {
|
|
|
|
if resp.IsError() {
|
|
|
|
return resp.Error()
|
|
|
|
}
|
|
|
|
|
2020-02-03 17:51:10 +00:00
|
|
|
if resp.Data["org_name"] != d["org_name"] {
|
2018-04-09 18:35:21 +00:00
|
|
|
return fmt.Errorf("org mismatch expected %s but got %s", d["organization"], resp.Data["Org"])
|
2017-01-27 00:08:52 +00:00
|
|
|
}
|
|
|
|
|
2017-07-05 13:42:37 +00:00
|
|
|
if resp.Data["base_url"] != d["base_url"] {
|
2017-01-27 00:08:52 +00:00
|
|
|
return fmt.Errorf("BaseURL mismatch expected %s but got %s", d["base_url"], resp.Data["BaseURL"])
|
|
|
|
}
|
|
|
|
|
2017-07-05 13:42:37 +00:00
|
|
|
for _, value := range resp.Data {
|
|
|
|
if value == token {
|
|
|
|
return fmt.Errorf("token should not be returned on a read request")
|
|
|
|
}
|
2017-01-27 00:08:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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")
|
|
|
|
}
|
2018-01-16 23:20:19 +00:00
|
|
|
|
|
|
|
if v := os.Getenv("OKTA_API_TOKEN"); v == "" {
|
|
|
|
t.Fatal("OKTA_API_TOKEN must be set for acceptance tests")
|
|
|
|
}
|
2017-01-27 00:08:52 +00:00
|
|
|
}
|
|
|
|
|
2018-01-16 23:20:19 +00:00
|
|
|
func testAccUserGroups(t *testing.T, user string, groups interface{}, policies interface{}) logicaltest.TestStep {
|
2017-01-27 00:08:52 +00:00
|
|
|
return logicaltest.TestStep{
|
|
|
|
Operation: logical.UpdateOperation,
|
|
|
|
Path: "users/" + user,
|
|
|
|
Data: map[string]interface{}{
|
2018-01-16 23:20:19 +00:00
|
|
|
"groups": groups,
|
|
|
|
"policies": policies,
|
2017-01-27 00:08:52 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-16 23:20:19 +00:00
|
|
|
func testAccGroups(t *testing.T, group string, policies interface{}) logicaltest.TestStep {
|
2017-01-27 00:08:52 +00:00
|
|
|
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),
|
|
|
|
}
|
|
|
|
}
|