open-vault/vault/identity_store_entities_ext_test.go
Jeff Mitchell 5d44c54947
Changes the way policies are reported in audit logs (#4747)
* This changes the way policies are reported in audit logs.

Previously, only policies tied to tokens would be reported. This could
make it difficult to perform after-the-fact analysis based on both the
initial response entry and further requests. Now, the full set of
applicable policies from both the token and any derived policies from
Identity are reported.

To keep things consistent, token authentications now also return the
full set of policies in api.Secret.Auth responses, so this both makes it
easier for users to understand their actual full set, and it matches
what the audit logs now report.
2018-06-14 09:49:33 -04:00

380 lines
9.7 KiB
Go

package vault_test
import (
"strings"
"testing"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/builtin/credential/approle"
"github.com/hashicorp/vault/helper/strutil"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/vault"
)
func TestIdentityStore_EntityDisabled(t *testing.T) {
// Use a TestCluster and the approle backend to get a token and entity for testing
coreConfig := &vault.CoreConfig{
CredentialBackends: map[string]logical.Factory{
"approle": approle.Factory,
},
}
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
})
cluster.Start()
defer cluster.Cleanup()
core := cluster.Cores[0].Core
vault.TestWaitActive(t, core)
client := cluster.Cores[0].Client
// Mount the auth backend
err := client.Sys().EnableAuthWithOptions("approle", &api.EnableAuthOptions{
Type: "approle",
})
if err != nil {
t.Fatal(err)
}
// Tune the mount
err = client.Sys().TuneMount("auth/approle", api.MountConfigInput{
DefaultLeaseTTL: "5m",
MaxLeaseTTL: "5m",
})
if err != nil {
t.Fatal(err)
}
// Create role
resp, err := client.Logical().Write("auth/approle/role/role-period", map[string]interface{}{
"period": "5m",
})
if err != nil {
t.Fatal(err)
}
// Get role_id
resp, err = client.Logical().Read("auth/approle/role/role-period/role-id")
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("expected a response for fetching the role-id")
}
roleID := resp.Data["role_id"]
// Get secret_id
resp, err = client.Logical().Write("auth/approle/role/role-period/secret-id", map[string]interface{}{})
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("expected a response for fetching the secret-id")
}
secretID := resp.Data["secret_id"]
// Login
resp, err = client.Logical().Write("auth/approle/login", map[string]interface{}{
"role_id": roleID,
"secret_id": secretID,
})
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("expected a response for login")
}
if resp.Auth == nil {
t.Fatal("expected auth object from response")
}
if resp.Auth.ClientToken == "" {
t.Fatal("expected a client token")
}
roleToken := resp.Auth.ClientToken
client.SetToken(roleToken)
resp, err = client.Auth().Token().LookupSelf()
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("expected a response for token lookup")
}
entityIDRaw, ok := resp.Data["entity_id"]
if !ok {
t.Fatal("expected an entity ID")
}
entityID, ok := entityIDRaw.(string)
if !ok {
t.Fatal("entity_id not a string")
}
client.SetToken(cluster.RootToken)
resp, err = client.Logical().Write("identity/entity/id/"+entityID, map[string]interface{}{
"disabled": true,
})
if err != nil {
t.Fatal(err)
}
// This call should now fail
client.SetToken(roleToken)
resp, err = client.Auth().Token().LookupSelf()
if err == nil {
t.Fatalf("expected error, got %#v", *resp)
}
if !strings.Contains(err.Error(), logical.ErrPermissionDenied.Error()) {
t.Fatalf("expected to see entity disabled error, got %v", err)
}
// Attempting to get a new token should also now fail
client.SetToken("")
resp, err = client.Logical().Write("auth/approle/login", map[string]interface{}{
"role_id": roleID,
"secret_id": secretID,
})
if err == nil {
t.Fatalf("expected error, got %#v", *resp)
}
if !strings.Contains(err.Error(), logical.ErrPermissionDenied.Error()) {
t.Fatalf("expected to see entity disabled error, got %v", err)
}
client.SetToken(cluster.RootToken)
resp, err = client.Logical().Write("identity/entity/id/"+entityID, map[string]interface{}{
"disabled": false,
})
if err != nil {
t.Fatal(err)
}
client.SetToken(roleToken)
resp, err = client.Auth().Token().LookupSelf()
if err != nil {
t.Fatal(err)
}
// Getting a new token should now work again too
client.SetToken("")
resp, err = client.Logical().Write("auth/approle/login", map[string]interface{}{
"role_id": roleID,
"secret_id": secretID,
})
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("expected a response for login")
}
if resp.Auth == nil {
t.Fatal("expected auth object from response")
}
if resp.Auth.ClientToken == "" {
t.Fatal("expected a client token")
}
}
func TestIdentityStore_EntityPoliciesInInitialAuth(t *testing.T) {
// Use a TestCluster and the approle backend to get a token and entity for testing
coreConfig := &vault.CoreConfig{
CredentialBackends: map[string]logical.Factory{
"approle": approle.Factory,
},
}
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
})
cluster.Start()
defer cluster.Cleanup()
core := cluster.Cores[0].Core
vault.TestWaitActive(t, core)
client := cluster.Cores[0].Client
// Mount the auth backend
err := client.Sys().EnableAuthWithOptions("approle", &api.EnableAuthOptions{
Type: "approle",
})
if err != nil {
t.Fatal(err)
}
// Tune the mount
err = client.Sys().TuneMount("auth/approle", api.MountConfigInput{
DefaultLeaseTTL: "5m",
MaxLeaseTTL: "5m",
})
if err != nil {
t.Fatal(err)
}
// Create role
resp, err := client.Logical().Write("auth/approle/role/role-period", map[string]interface{}{
"period": "5m",
})
if err != nil {
t.Fatal(err)
}
// Get role_id
resp, err = client.Logical().Read("auth/approle/role/role-period/role-id")
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("expected a response for fetching the role-id")
}
roleID := resp.Data["role_id"]
// Get secret_id
resp, err = client.Logical().Write("auth/approle/role/role-period/secret-id", map[string]interface{}{})
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("expected a response for fetching the secret-id")
}
secretID := resp.Data["secret_id"]
// Login
resp, err = client.Logical().Write("auth/approle/login", map[string]interface{}{
"role_id": roleID,
"secret_id": secretID,
})
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("expected a response for login")
}
if resp.Auth == nil {
t.Fatal("expected auth object from response")
}
if resp.Auth.ClientToken == "" {
t.Fatal("expected a client token")
}
if !strutil.EquivalentSlices(resp.Auth.TokenPolicies, []string{"default"}) {
t.Fatalf("policy mismatch, got token policies: %v", resp.Auth.TokenPolicies)
}
if len(resp.Auth.IdentityPolicies) > 0 {
t.Fatalf("policy mismatch, got identity policies: %v", resp.Auth.IdentityPolicies)
}
if !strutil.EquivalentSlices(resp.Auth.Policies, []string{"default"}) {
t.Fatalf("policy mismatch, got policies: %v", resp.Auth.Policies)
}
// Check policies
client.SetToken(resp.Auth.ClientToken)
resp, err = client.Auth().Token().LookupSelf()
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("expected a response for token lookup")
}
entityIDRaw, ok := resp.Data["entity_id"]
if !ok {
t.Fatal("expected an entity ID")
}
entityID, ok := entityIDRaw.(string)
if !ok {
t.Fatal("entity_id not a string")
}
policiesRaw := resp.Data["policies"]
if policiesRaw == nil {
t.Fatal("expected policies, got nil")
}
var policies []string
for _, v := range policiesRaw.([]interface{}) {
policies = append(policies, v.(string))
}
policiesRaw = resp.Data["identity_policies"]
if policiesRaw != nil {
t.Fatalf("expected nil policies, got %#v", policiesRaw)
}
if !strutil.EquivalentSlices(policies, []string{"default"}) {
t.Fatalf("policy mismatch, got policies: %v", resp.Auth.Policies)
}
// Write more policies into the entity
client.SetToken(cluster.RootToken)
resp, err = client.Logical().Write("identity/entity/id/"+entityID, map[string]interface{}{
"policies": []string{"foo", "bar"},
})
if err != nil {
t.Fatal(err)
}
// Reauthenticate to get a token with updated policies
client.SetToken("")
resp, err = client.Logical().Write("auth/approle/login", map[string]interface{}{
"role_id": roleID,
"secret_id": secretID,
})
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("expected a response for login")
}
if resp.Auth == nil {
t.Fatal("expected auth object from response")
}
if resp.Auth.ClientToken == "" {
t.Fatal("expected a client token")
}
if !strutil.EquivalentSlices(resp.Auth.TokenPolicies, []string{"default"}) {
t.Fatalf("policy mismatch, got token policies: %v", resp.Auth.TokenPolicies)
}
if !strutil.EquivalentSlices(resp.Auth.IdentityPolicies, []string{"foo", "bar"}) {
t.Fatalf("policy mismatch, got identity policies: %v", resp.Auth.IdentityPolicies)
}
if !strutil.EquivalentSlices(resp.Auth.Policies, []string{"default", "foo", "bar"}) {
t.Fatalf("policy mismatch, got policies: %v", resp.Auth.Policies)
}
// Validate the policies on lookup again -- this ensures that the right
// policies were encoded on the token but all were looked up successfully
client.SetToken(resp.Auth.ClientToken)
resp, err = client.Auth().Token().LookupSelf()
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("expected a response for token lookup")
}
entityIDRaw, ok = resp.Data["entity_id"]
if !ok {
t.Fatal("expected an entity ID")
}
entityID, ok = entityIDRaw.(string)
if !ok {
t.Fatal("entity_id not a string")
}
policies = nil
policiesRaw = resp.Data["policies"]
if policiesRaw == nil {
t.Fatal("expected policies, got nil")
}
for _, v := range policiesRaw.([]interface{}) {
policies = append(policies, v.(string))
}
if !strutil.EquivalentSlices(policies, []string{"default"}) {
t.Fatalf("policy mismatch, got policies: %v", policies)
}
policies = nil
policiesRaw = resp.Data["identity_policies"]
if policiesRaw == nil {
t.Fatal("expected policies, got nil")
}
for _, v := range policiesRaw.([]interface{}) {
policies = append(policies, v.(string))
}
if !strutil.EquivalentSlices(policies, []string{"foo", "bar"}) {
t.Fatalf("policy mismatch, got policies: %v", policies)
}
}