External identity groups (#3447)
* external identity groups * add local LDAP groups as well to group aliases * add group aliases for okta credential backend * Fix panic in tests * fix build failure * remove duplicated struct tag * add test steps to test out removal of group member during renewals * Add comment for having a prefix check in router * fix tests * s/parent_id/canonical_id * s/parent/canonical in comments and errors
This commit is contained in:
parent
710243ab26
commit
7bae606662
|
@ -74,7 +74,7 @@ func (b *backend) pathLogin(
|
|||
return logical.ErrorResponse(fmt.Sprintf("error sanitizing TTLs: %s", err)), nil
|
||||
}
|
||||
|
||||
return &logical.Response{
|
||||
resp := &logical.Response{
|
||||
Auth: &logical.Auth{
|
||||
InternalData: map[string]interface{}{
|
||||
"token": token,
|
||||
|
@ -93,7 +93,15 @@ func (b *backend) pathLogin(
|
|||
Name: *verifyResp.User.Login,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
for _, teamName := range verifyResp.TeamNames {
|
||||
resp.Auth.GroupAliases = append(resp.Auth.GroupAliases, &logical.Alias{
|
||||
Name: teamName,
|
||||
})
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (b *backend) pathLoginRenew(
|
||||
|
@ -125,7 +133,22 @@ func (b *backend) pathLoginRenew(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return framework.LeaseExtend(config.TTL, config.MaxTTL, b.System())(req, d)
|
||||
|
||||
resp, err := framework.LeaseExtend(config.TTL, config.MaxTTL, b.System())(req, d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Remove old aliases
|
||||
resp.Auth.GroupAliases = nil
|
||||
|
||||
for _, teamName := range verifyResp.TeamNames {
|
||||
resp.Auth.GroupAliases = append(resp.Auth.GroupAliases, &logical.Alias{
|
||||
Name: teamName,
|
||||
})
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (b *backend) verifyCredentials(req *logical.Request, token string) (*verifyCredentialsResp, *logical.Response, error) {
|
||||
|
@ -233,14 +256,16 @@ func (b *backend) verifyCredentials(req *logical.Request, token string) (*verify
|
|||
}
|
||||
|
||||
return &verifyCredentialsResp{
|
||||
User: user,
|
||||
Org: org,
|
||||
Policies: append(groupPoliciesList, userPoliciesList...),
|
||||
User: user,
|
||||
Org: org,
|
||||
Policies: append(groupPoliciesList, userPoliciesList...),
|
||||
TeamNames: teamNames,
|
||||
}, nil, nil
|
||||
}
|
||||
|
||||
type verifyCredentialsResp struct {
|
||||
User *github.User
|
||||
Org *github.Organization
|
||||
Policies []string
|
||||
User *github.User
|
||||
Org *github.Organization
|
||||
Policies []string
|
||||
TeamNames []string
|
||||
}
|
||||
|
|
|
@ -88,22 +88,22 @@ func EscapeLDAPValue(input string) string {
|
|||
return input
|
||||
}
|
||||
|
||||
func (b *backend) Login(req *logical.Request, username string, password string) ([]string, *logical.Response, error) {
|
||||
func (b *backend) Login(req *logical.Request, username string, password string) ([]string, *logical.Response, []string, error) {
|
||||
|
||||
cfg, err := b.Config(req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
if cfg == nil {
|
||||
return nil, logical.ErrorResponse("ldap backend not configured"), nil
|
||||
return nil, logical.ErrorResponse("ldap backend not configured"), nil, nil
|
||||
}
|
||||
|
||||
c, err := cfg.DialLDAP()
|
||||
if err != nil {
|
||||
return nil, logical.ErrorResponse(err.Error()), nil
|
||||
return nil, logical.ErrorResponse(err.Error()), nil, nil
|
||||
}
|
||||
if c == nil {
|
||||
return nil, logical.ErrorResponse("invalid connection returned from LDAP dial"), nil
|
||||
return nil, logical.ErrorResponse("invalid connection returned from LDAP dial"), nil, nil
|
||||
}
|
||||
|
||||
// Clean connection
|
||||
|
@ -111,7 +111,7 @@ func (b *backend) Login(req *logical.Request, username string, password string)
|
|||
|
||||
userBindDN, err := b.getUserBindDN(cfg, c, username)
|
||||
if err != nil {
|
||||
return nil, logical.ErrorResponse(err.Error()), nil
|
||||
return nil, logical.ErrorResponse(err.Error()), nil, nil
|
||||
}
|
||||
|
||||
if b.Logger().IsDebug() {
|
||||
|
@ -119,7 +119,7 @@ func (b *backend) Login(req *logical.Request, username string, password string)
|
|||
}
|
||||
|
||||
if cfg.DenyNullBind && len(password) == 0 {
|
||||
return nil, logical.ErrorResponse("password cannot be of zero length when passwordless binds are being denied"), nil
|
||||
return nil, logical.ErrorResponse("password cannot be of zero length when passwordless binds are being denied"), nil, nil
|
||||
}
|
||||
|
||||
// Try to bind as the login user. This is where the actual authentication takes place.
|
||||
|
@ -129,14 +129,14 @@ func (b *backend) Login(req *logical.Request, username string, password string)
|
|||
err = c.UnauthenticatedBind(userBindDN)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, logical.ErrorResponse(fmt.Sprintf("LDAP bind failed: %v", err)), nil
|
||||
return nil, logical.ErrorResponse(fmt.Sprintf("LDAP bind failed: %v", err)), nil, nil
|
||||
}
|
||||
|
||||
// We re-bind to the BindDN if it's defined because we assume
|
||||
// the BindDN should be the one to search, not the user logging in.
|
||||
if cfg.BindDN != "" && cfg.BindPassword != "" {
|
||||
if err := c.Bind(cfg.BindDN, cfg.BindPassword); err != nil {
|
||||
return nil, logical.ErrorResponse(fmt.Sprintf("Encountered an error while attempting to re-bind with the BindDN User: %s", err.Error())), nil
|
||||
return nil, logical.ErrorResponse(fmt.Sprintf("Encountered an error while attempting to re-bind with the BindDN User: %s", err.Error())), nil, nil
|
||||
}
|
||||
if b.Logger().IsDebug() {
|
||||
b.Logger().Debug("auth/ldap: Re-Bound to original BindDN")
|
||||
|
@ -145,12 +145,12 @@ func (b *backend) Login(req *logical.Request, username string, password string)
|
|||
|
||||
userDN, err := b.getUserDN(cfg, c, userBindDN)
|
||||
if err != nil {
|
||||
return nil, logical.ErrorResponse(err.Error()), nil
|
||||
return nil, logical.ErrorResponse(err.Error()), nil, nil
|
||||
}
|
||||
|
||||
ldapGroups, err := b.getLdapGroups(cfg, c, userDN, username)
|
||||
if err != nil {
|
||||
return nil, logical.ErrorResponse(err.Error()), nil
|
||||
return nil, logical.ErrorResponse(err.Error()), nil, nil
|
||||
}
|
||||
if b.Logger().IsDebug() {
|
||||
b.Logger().Debug("auth/ldap: Groups fetched from server", "num_server_groups", len(ldapGroups), "server_groups", ldapGroups)
|
||||
|
@ -199,10 +199,10 @@ func (b *backend) Login(req *logical.Request, username string, password string)
|
|||
}
|
||||
|
||||
ldapResponse.Data["error"] = errStr
|
||||
return nil, ldapResponse, nil
|
||||
return nil, ldapResponse, nil, nil
|
||||
}
|
||||
|
||||
return policies, ldapResponse, nil
|
||||
return policies, ldapResponse, allGroups, nil
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -55,7 +55,7 @@ func (b *backend) pathLogin(
|
|||
username := d.Get("username").(string)
|
||||
password := d.Get("password").(string)
|
||||
|
||||
policies, resp, err := b.Login(req, username, password)
|
||||
policies, resp, groupNames, err := b.Login(req, username, password)
|
||||
// Handle an internal error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -87,6 +87,12 @@ func (b *backend) pathLogin(
|
|||
Name: username,
|
||||
},
|
||||
}
|
||||
|
||||
for _, groupName := range groupNames {
|
||||
resp.Auth.GroupAliases = append(resp.Auth.GroupAliases, &logical.Alias{
|
||||
Name: groupName,
|
||||
})
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
|
@ -96,7 +102,7 @@ func (b *backend) pathLoginRenew(
|
|||
username := req.Auth.Metadata["username"]
|
||||
password := req.Auth.InternalData["password"].(string)
|
||||
|
||||
loginPolicies, resp, err := b.Login(req, username, password)
|
||||
loginPolicies, resp, groupNames, err := b.Login(req, username, password)
|
||||
if len(loginPolicies) == 0 {
|
||||
return resp, err
|
||||
}
|
||||
|
@ -105,7 +111,21 @@ func (b *backend) pathLoginRenew(
|
|||
return nil, fmt.Errorf("policies have changed, not renewing")
|
||||
}
|
||||
|
||||
return framework.LeaseExtend(0, 0, b.System())(req, d)
|
||||
resp, err = framework.LeaseExtend(0, 0, b.System())(req, d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Remove old aliases
|
||||
resp.Auth.GroupAliases = nil
|
||||
|
||||
for _, groupName := range groupNames {
|
||||
resp.Auth.GroupAliases = append(resp.Auth.GroupAliases, &logical.Alias{
|
||||
Name: groupName,
|
||||
})
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
const pathLoginSyn = `
|
||||
|
|
|
@ -47,13 +47,13 @@ type backend struct {
|
|||
*framework.Backend
|
||||
}
|
||||
|
||||
func (b *backend) Login(req *logical.Request, username string, password string) ([]string, *logical.Response, error) {
|
||||
func (b *backend) Login(req *logical.Request, username string, password string) ([]string, *logical.Response, []string, error) {
|
||||
cfg, err := b.Config(req.Storage)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
if cfg == nil {
|
||||
return nil, logical.ErrorResponse("Okta backend not configured"), nil
|
||||
return nil, logical.ErrorResponse("Okta backend not configured"), nil, nil
|
||||
}
|
||||
|
||||
client := cfg.OktaClient()
|
||||
|
@ -71,16 +71,16 @@ func (b *backend) Login(req *logical.Request, username string, password string)
|
|||
"password": password,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
var result authResult
|
||||
rsp, err := client.Do(authReq, &result)
|
||||
if err != nil {
|
||||
return nil, logical.ErrorResponse(fmt.Sprintf("Okta auth failed: %v", err)), nil
|
||||
return nil, logical.ErrorResponse(fmt.Sprintf("Okta auth failed: %v", err)), nil, nil
|
||||
}
|
||||
if rsp == nil {
|
||||
return nil, logical.ErrorResponse("okta auth backend unexpected failure"), nil
|
||||
return nil, logical.ErrorResponse("okta auth backend unexpected failure"), nil, nil
|
||||
}
|
||||
|
||||
oktaResponse := &logical.Response{
|
||||
|
@ -92,7 +92,7 @@ func (b *backend) Login(req *logical.Request, username string, password string)
|
|||
if cfg.Token != "" {
|
||||
oktaGroups, err := b.getOktaGroups(client, &result.Embedded.User)
|
||||
if err != nil {
|
||||
return nil, logical.ErrorResponse(fmt.Sprintf("okta failure retrieving groups: %v", err)), nil
|
||||
return nil, logical.ErrorResponse(fmt.Sprintf("okta failure retrieving groups: %v", err)), nil, nil
|
||||
}
|
||||
if len(oktaGroups) == 0 {
|
||||
errString := fmt.Sprintf(
|
||||
|
@ -142,10 +142,10 @@ func (b *backend) Login(req *logical.Request, username string, password string)
|
|||
}
|
||||
|
||||
oktaResponse.Data["error"] = errStr
|
||||
return nil, oktaResponse, nil
|
||||
return nil, oktaResponse, nil, nil
|
||||
}
|
||||
|
||||
return policies, oktaResponse, nil
|
||||
return policies, oktaResponse, allGroups, nil
|
||||
}
|
||||
|
||||
func (b *backend) getOktaGroups(client *okta.Client, user *okta.User) ([]string, error) {
|
||||
|
|
|
@ -57,7 +57,7 @@ func (b *backend) pathLogin(
|
|||
username := d.Get("username").(string)
|
||||
password := d.Get("password").(string)
|
||||
|
||||
policies, resp, err := b.Login(req, username, password)
|
||||
policies, resp, groupNames, err := b.Login(req, username, password)
|
||||
// Handle an internal error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -96,6 +96,13 @@ func (b *backend) pathLogin(
|
|||
Name: username,
|
||||
},
|
||||
}
|
||||
|
||||
for _, groupName := range groupNames {
|
||||
resp.Auth.GroupAliases = append(resp.Auth.GroupAliases, &logical.Alias{
|
||||
Name: groupName,
|
||||
})
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
|
@ -105,7 +112,7 @@ func (b *backend) pathLoginRenew(
|
|||
username := req.Auth.Metadata["username"]
|
||||
password := req.Auth.InternalData["password"].(string)
|
||||
|
||||
loginPolicies, resp, err := b.Login(req, username, password)
|
||||
loginPolicies, resp, groupNames, err := b.Login(req, username, password)
|
||||
if len(loginPolicies) == 0 {
|
||||
return resp, err
|
||||
}
|
||||
|
@ -119,7 +126,22 @@ func (b *backend) pathLoginRenew(
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return framework.LeaseExtend(cfg.TTL, cfg.MaxTTL, b.System())(req, d)
|
||||
resp, err = framework.LeaseExtend(cfg.TTL, cfg.MaxTTL, b.System())(req, d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Remove old aliases
|
||||
resp.Auth.GroupAliases = nil
|
||||
|
||||
for _, groupName := range groupNames {
|
||||
resp.Auth.GroupAliases = append(resp.Auth.GroupAliases, &logical.Alias{
|
||||
Name: groupName,
|
||||
})
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
|
||||
}
|
||||
|
||||
func (b *backend) getConfig(req *logical.Request) (*ConfigEntry, error) {
|
||||
|
|
|
@ -0,0 +1,368 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/vault/api"
|
||||
"github.com/hashicorp/vault/builtin/credential/ldap"
|
||||
vaulthttp "github.com/hashicorp/vault/http"
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/vault"
|
||||
logxi "github.com/mgutz/logxi/v1"
|
||||
)
|
||||
|
||||
func TestIdentityStore_Integ_GroupAliases(t *testing.T) {
|
||||
var err error
|
||||
coreConfig := &vault.CoreConfig{
|
||||
DisableMlock: true,
|
||||
DisableCache: true,
|
||||
Logger: logxi.NullLog,
|
||||
CredentialBackends: map[string]logical.Factory{
|
||||
"ldap": ldap.Factory,
|
||||
},
|
||||
}
|
||||
|
||||
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
|
||||
HandlerFunc: vaulthttp.Handler,
|
||||
})
|
||||
|
||||
cluster.Start()
|
||||
defer cluster.Cleanup()
|
||||
|
||||
cores := cluster.Cores
|
||||
|
||||
vault.TestWaitActive(t, cores[0].Core)
|
||||
|
||||
client := cores[0].Client
|
||||
|
||||
err = client.Sys().EnableAuthWithOptions("ldap", &api.EnableAuthOptions{
|
||||
Type: "ldap",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
auth, err := client.Sys().ListAuth()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
accessor := auth["ldap/"].Accessor
|
||||
|
||||
secret, err := client.Logical().Write("identity/group", map[string]interface{}{
|
||||
"type": "external",
|
||||
"name": "ldap_Italians",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
italiansGroupID := secret.Data["id"].(string)
|
||||
|
||||
secret, err = client.Logical().Write("identity/group", map[string]interface{}{
|
||||
"type": "external",
|
||||
"name": "ldap_Scientists",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
scientistsGroupID := secret.Data["id"].(string)
|
||||
|
||||
secret, err = client.Logical().Write("identity/group", map[string]interface{}{
|
||||
"type": "external",
|
||||
"name": "ldap_devops",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
devopsGroupID := secret.Data["id"].(string)
|
||||
|
||||
secret, err = client.Logical().Write("identity/group-alias", map[string]interface{}{
|
||||
"name": "Italians",
|
||||
"canonical_id": italiansGroupID,
|
||||
"mount_accessor": accessor,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
secret, err = client.Logical().Write("identity/group-alias", map[string]interface{}{
|
||||
"name": "Scientists",
|
||||
"canonical_id": scientistsGroupID,
|
||||
"mount_accessor": accessor,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
secret, err = client.Logical().Write("identity/group-alias", map[string]interface{}{
|
||||
"name": "devops",
|
||||
"canonical_id": devopsGroupID,
|
||||
"mount_accessor": accessor,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
secret, err = client.Logical().Read("identity/group/id/" + italiansGroupID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
aliasMap := secret.Data["alias"].(map[string]interface{})
|
||||
if aliasMap["canonical_id"] != italiansGroupID ||
|
||||
aliasMap["name"] != "Italians" ||
|
||||
aliasMap["mount_accessor"] != accessor {
|
||||
t.Fatalf("bad: group alias: %#v\n", aliasMap)
|
||||
}
|
||||
|
||||
secret, err = client.Logical().Read("identity/group/id/" + scientistsGroupID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
aliasMap = secret.Data["alias"].(map[string]interface{})
|
||||
if aliasMap["canonical_id"] != scientistsGroupID ||
|
||||
aliasMap["name"] != "Scientists" ||
|
||||
aliasMap["mount_accessor"] != accessor {
|
||||
t.Fatalf("bad: group alias: %#v\n", aliasMap)
|
||||
}
|
||||
|
||||
// Configure LDAP auth backend
|
||||
secret, err = client.Logical().Write("auth/ldap/config", map[string]interface{}{
|
||||
"url": "ldap://ldap.forumsys.com",
|
||||
"userattr": "uid",
|
||||
"userdn": "dc=example,dc=com",
|
||||
"groupdn": "dc=example,dc=com",
|
||||
"binddn": "cn=read-only-admin,dc=example,dc=com",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create a local group in LDAP backend
|
||||
secret, err = client.Logical().Write("auth/ldap/groups/devops", map[string]interface{}{
|
||||
"policies": "default",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create a local group in LDAP backend
|
||||
secret, err = client.Logical().Write("auth/ldap/groups/engineers", map[string]interface{}{
|
||||
"policies": "default",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create a local user in LDAP
|
||||
secret, err = client.Logical().Write("auth/ldap/users/tesla", map[string]interface{}{
|
||||
"policies": "default",
|
||||
"groups": "engineers,devops",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Login with LDAP and create a token
|
||||
secret, err = client.Logical().Write("auth/ldap/login/tesla", map[string]interface{}{
|
||||
"password": "password",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
token := secret.Auth.ClientToken
|
||||
|
||||
// Lookup the token to get the entity ID
|
||||
secret, err = client.Auth().Token().Lookup(token)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
entityID := secret.Data["entity_id"].(string)
|
||||
|
||||
// Re-read the Scientists, Italians and devops group. This entity ID should have
|
||||
// been added to both of these groups by now.
|
||||
secret, err = client.Logical().Read("identity/group/id/" + italiansGroupID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
groupMap := secret.Data
|
||||
found := false
|
||||
for _, entityIDRaw := range groupMap["member_entity_ids"].([]interface{}) {
|
||||
if entityIDRaw.(string) == entityID {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("expected entity ID %q to be part of Italians group")
|
||||
}
|
||||
|
||||
secret, err = client.Logical().Read("identity/group/id/" + scientistsGroupID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
groupMap = secret.Data
|
||||
found = false
|
||||
for _, entityIDRaw := range groupMap["member_entity_ids"].([]interface{}) {
|
||||
if entityIDRaw.(string) == entityID {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("expected entity ID %q to be part of Scientists group")
|
||||
}
|
||||
|
||||
secret, err = client.Logical().Read("identity/group/id/" + devopsGroupID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
groupMap = secret.Data
|
||||
found = false
|
||||
for _, entityIDRaw := range groupMap["member_entity_ids"].([]interface{}) {
|
||||
if entityIDRaw.(string) == entityID {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("expected entity ID %q to be part of devops group")
|
||||
}
|
||||
|
||||
identityStore := cores[0].IdentityStore()
|
||||
|
||||
group, err := identityStore.MemDBGroupByID(italiansGroupID, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Remove its member entities
|
||||
group.MemberEntityIDs = nil
|
||||
|
||||
err = identityStore.UpsertGroup(group, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
group, err = identityStore.MemDBGroupByID(italiansGroupID, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if group.MemberEntityIDs != nil {
|
||||
t.Fatalf("failed to remove entity ID from the group")
|
||||
}
|
||||
|
||||
group, err = identityStore.MemDBGroupByID(scientistsGroupID, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Remove its member entities
|
||||
group.MemberEntityIDs = nil
|
||||
|
||||
err = identityStore.UpsertGroup(group, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
group, err = identityStore.MemDBGroupByID(scientistsGroupID, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if group.MemberEntityIDs != nil {
|
||||
t.Fatalf("failed to remove entity ID from the group")
|
||||
}
|
||||
|
||||
group, err = identityStore.MemDBGroupByID(devopsGroupID, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Remove its member entities
|
||||
group.MemberEntityIDs = nil
|
||||
|
||||
err = identityStore.UpsertGroup(group, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
group, err = identityStore.MemDBGroupByID(devopsGroupID, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if group.MemberEntityIDs != nil {
|
||||
t.Fatalf("failed to remove entity ID from the group")
|
||||
}
|
||||
|
||||
_, err = client.Auth().Token().Renew(token, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// EntityIDs should have been added to the groups again during renewal
|
||||
secret, err = client.Logical().Read("identity/group/id/" + italiansGroupID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
groupMap = secret.Data
|
||||
found = false
|
||||
for _, entityIDRaw := range groupMap["member_entity_ids"].([]interface{}) {
|
||||
if entityIDRaw.(string) == entityID {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("expected entity ID %q to be part of Italians group")
|
||||
}
|
||||
|
||||
secret, err = client.Logical().Read("identity/group/id/" + scientistsGroupID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
groupMap = secret.Data
|
||||
found = false
|
||||
for _, entityIDRaw := range groupMap["member_entity_ids"].([]interface{}) {
|
||||
if entityIDRaw.(string) == entityID {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("expected entity ID %q to be part of Italians group")
|
||||
}
|
||||
|
||||
secret, err = client.Logical().Read("identity/group/id/" + devopsGroupID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
groupMap = secret.Data
|
||||
found = false
|
||||
for _, entityIDRaw := range groupMap["member_entity_ids"].([]interface{}) {
|
||||
if entityIDRaw.(string) == entityID {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("expected entity ID %q to be part of devops group")
|
||||
}
|
||||
|
||||
// Remove user tesla from the devops group in LDAP backend
|
||||
secret, err = client.Logical().Write("auth/ldap/users/tesla", map[string]interface{}{
|
||||
"policies": "default",
|
||||
"groups": "engineers",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Renewing the token now should remove its entity ID from the devops
|
||||
// group
|
||||
_, err = client.Auth().Token().Renew(token, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
group, err = identityStore.MemDBGroupByID(devopsGroupID, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if group.MemberEntityIDs != nil {
|
||||
t.Fatalf("failed to remove entity ID from the group")
|
||||
}
|
||||
}
|
|
@ -50,7 +50,7 @@ func (p *Alias) SentinelGet(key string) (interface{}, error) {
|
|||
case "last_update_time":
|
||||
return ptypes.TimestampString(p.LastUpdateTime), nil
|
||||
case "merged_from_entity_ids":
|
||||
return p.MergedFromEntityIDs, nil
|
||||
return p.MergedFromCanonicalIDs, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
|
|
|
@ -59,6 +59,15 @@ type Group struct {
|
|||
// the groups belonging to a particular bucket during invalidation of the
|
||||
// storage key.
|
||||
BucketKeyHash string `sentinel:"" protobuf:"bytes,10,opt,name=bucket_key_hash,json=bucketKeyHash" json:"bucket_key_hash,omitempty"`
|
||||
// Alias is used to mark this group as an internal mapping of a group that
|
||||
// is external to the identity store. Alias can only be set if the 'type'
|
||||
// is set to 'external'.
|
||||
Alias *Alias `sentinel:"" protobuf:"bytes,11,opt,name=alias" json:"alias,omitempty"`
|
||||
// Type indicates if this group is an internal group or an external group.
|
||||
// Memberships of the internal groups can be managed over the API whereas
|
||||
// the memberships on the external group --for which a corresponding alias
|
||||
// will be set-- will be managed automatically.
|
||||
Type string `sentinel:"" protobuf:"bytes,12,opt,name=type" json:"type,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Group) Reset() { *m = Group{} }
|
||||
|
@ -136,6 +145,20 @@ func (m *Group) GetBucketKeyHash() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func (m *Group) GetAlias() *Alias {
|
||||
if m != nil {
|
||||
return m.Alias
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Group) GetType() string {
|
||||
if m != nil {
|
||||
return m.Type
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Entity represents an entity that gets persisted and indexed.
|
||||
// Entity is fundamentally composed of zero or many aliases.
|
||||
type Entity struct {
|
||||
|
@ -253,8 +276,8 @@ func (m *Entity) GetBucketKeyHash() string {
|
|||
type Alias struct {
|
||||
// ID is the unique identifier that represents this alias
|
||||
ID string `sentinel:"" protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
|
||||
// EntityID is the entity identifier to which this alias belongs to
|
||||
EntityID string `sentinel:"" protobuf:"bytes,2,opt,name=entity_id,json=entityId" json:"entity_id,omitempty"`
|
||||
// CanonicalID is the entity identifier to which this alias belongs to
|
||||
CanonicalID string `sentinel:"" protobuf:"bytes,2,opt,name=canonical_id,json=canonicalId" json:"canonical_id,omitempty"`
|
||||
// MountType is the backend mount's type to which this alias belongs to.
|
||||
// This enables categorically querying aliases of specific backend types.
|
||||
MountType string `sentinel:"" protobuf:"bytes,3,opt,name=mount_type,json=mountType" json:"mount_type,omitempty"`
|
||||
|
@ -270,8 +293,8 @@ type Alias struct {
|
|||
// against their metadata.
|
||||
Metadata map[string]string `sentinel:"" protobuf:"bytes,6,rep,name=metadata" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
||||
// Name is the identifier of this alias in its authentication source.
|
||||
// This does not uniquely identify a alias in Vault. This in conjunction
|
||||
// with MountAccessor form to be the factors that represent a alias in a
|
||||
// This does not uniquely identify an alias in Vault. This in conjunction
|
||||
// with MountAccessor form to be the factors that represent an alias in a
|
||||
// unique way. Aliases will be indexed based on this combined uniqueness
|
||||
// factor.
|
||||
Name string `sentinel:"" protobuf:"bytes,7,opt,name=name" json:"name,omitempty"`
|
||||
|
@ -281,10 +304,8 @@ type Alias struct {
|
|||
// alias got modified. This is helpful in filtering out aliases based
|
||||
// on its age and to take action on them, if desired.
|
||||
LastUpdateTime *google_protobuf.Timestamp `sentinel:"" protobuf:"bytes,9,opt,name=last_update_time,json=lastUpdateTime" json:"last_update_time,omitempty"`
|
||||
// MergedFromEntityIDs is the FIFO history of merging activity by entity IDs from
|
||||
// which this alias is transfered over to the entity to which it
|
||||
// currently belongs to.
|
||||
MergedFromEntityIDs []string `sentinel:"" protobuf:"bytes,10,rep,name=merged_from_entity_ids,json=mergedFromEntityIDs" json:"merged_from_entity_ids,omitempty"`
|
||||
// MergedFromCanonicalIDs is the FIFO history of merging activity
|
||||
MergedFromCanonicalIDs []string `sentinel:"" protobuf:"bytes,10,rep,name=merged_from_canonical_ids,json=mergedFromCanonicalIds" json:"merged_from_canonical_ids,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Alias) Reset() { *m = Alias{} }
|
||||
|
@ -299,9 +320,9 @@ func (m *Alias) GetID() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func (m *Alias) GetEntityID() string {
|
||||
func (m *Alias) GetCanonicalID() string {
|
||||
if m != nil {
|
||||
return m.EntityID
|
||||
return m.CanonicalID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
@ -355,9 +376,9 @@ func (m *Alias) GetLastUpdateTime() *google_protobuf.Timestamp {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *Alias) GetMergedFromEntityIDs() []string {
|
||||
func (m *Alias) GetMergedFromCanonicalIDs() []string {
|
||||
if m != nil {
|
||||
return m.MergedFromEntityIDs
|
||||
return m.MergedFromCanonicalIDs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -371,41 +392,43 @@ func init() {
|
|||
func init() { proto.RegisterFile("types.proto", fileDescriptor0) }
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 570 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0xcd, 0x6e, 0xd3, 0x40,
|
||||
0x10, 0xc7, 0xe5, 0x38, 0x1f, 0xf6, 0xa4, 0x4d, 0xcb, 0x82, 0x90, 0x15, 0x54, 0x08, 0x95, 0x40,
|
||||
0x86, 0x83, 0x2b, 0xb5, 0x17, 0x28, 0x07, 0x54, 0x89, 0x02, 0x15, 0x42, 0x42, 0x56, 0x39, 0x5b,
|
||||
0x9b, 0x78, 0x9a, 0xac, 0x1a, 0x7b, 0x2d, 0xef, 0x1a, 0xe1, 0x27, 0xe4, 0x39, 0x38, 0xf1, 0x1a,
|
||||
0xc8, 0xb3, 0x76, 0x62, 0x08, 0x5f, 0x15, 0xb9, 0xd9, 0xff, 0x99, 0x1d, 0xcf, 0xce, 0xff, 0x37,
|
||||
0x86, 0xa1, 0x2e, 0x33, 0x54, 0x41, 0x96, 0x4b, 0x2d, 0x99, 0x23, 0x62, 0x4c, 0xb5, 0xd0, 0xe5,
|
||||
0xf8, 0xc1, 0x5c, 0xca, 0xf9, 0x12, 0x8f, 0x48, 0x9f, 0x16, 0x57, 0x47, 0x5a, 0x24, 0xa8, 0x34,
|
||||
0x4f, 0x32, 0x93, 0x7a, 0xf8, 0xcd, 0x86, 0xde, 0x9b, 0x5c, 0x16, 0x19, 0x1b, 0x41, 0x47, 0xc4,
|
||||
0x9e, 0x35, 0xb1, 0x7c, 0x37, 0xec, 0x88, 0x98, 0x31, 0xe8, 0xa6, 0x3c, 0x41, 0xaf, 0x43, 0x0a,
|
||||
0x3d, 0xb3, 0x31, 0x38, 0x99, 0x5c, 0x8a, 0x99, 0x40, 0xe5, 0xd9, 0x13, 0xdb, 0x77, 0xc3, 0xd5,
|
||||
0x3b, 0xf3, 0x61, 0x3f, 0xe3, 0x39, 0xa6, 0x3a, 0x9a, 0x57, 0xf5, 0x22, 0x11, 0x2b, 0xaf, 0x4b,
|
||||
0x39, 0x23, 0xa3, 0xd3, 0x67, 0x2e, 0x62, 0xc5, 0x9e, 0xc2, 0xad, 0x04, 0x93, 0x29, 0xe6, 0x91,
|
||||
0xe9, 0x92, 0x52, 0x7b, 0x94, 0xba, 0x67, 0x02, 0xe7, 0xa4, 0x57, 0xb9, 0xcf, 0xc1, 0x49, 0x50,
|
||||
0xf3, 0x98, 0x6b, 0xee, 0xf5, 0x27, 0xb6, 0x3f, 0x3c, 0x3e, 0x08, 0x9a, 0xdb, 0x05, 0x54, 0x31,
|
||||
0x78, 0x5f, 0xc7, 0xcf, 0x53, 0x9d, 0x97, 0xe1, 0x2a, 0x9d, 0xbd, 0x84, 0xdd, 0x59, 0x8e, 0x5c,
|
||||
0x0b, 0x99, 0x46, 0xd5, 0xb5, 0xbd, 0xc1, 0xc4, 0xf2, 0x87, 0xc7, 0xe3, 0xc0, 0xcc, 0x24, 0x68,
|
||||
0x66, 0x12, 0x5c, 0x36, 0x33, 0x09, 0x77, 0x9a, 0x03, 0x95, 0xc4, 0x5e, 0xc1, 0xfe, 0x92, 0x2b,
|
||||
0x1d, 0x15, 0x59, 0xcc, 0x35, 0x9a, 0x1a, 0xce, 0x5f, 0x6b, 0x8c, 0xaa, 0x33, 0x1f, 0xe9, 0x08,
|
||||
0x55, 0x79, 0x08, 0x3b, 0x89, 0x8c, 0xc5, 0x55, 0x19, 0x89, 0x34, 0xc6, 0xcf, 0x9e, 0x3b, 0xb1,
|
||||
0xfc, 0x6e, 0x38, 0x34, 0xda, 0x45, 0x25, 0xb1, 0xc7, 0xb0, 0x37, 0x2d, 0x66, 0xd7, 0xa8, 0xa3,
|
||||
0x6b, 0x2c, 0xa3, 0x05, 0x57, 0x0b, 0x0f, 0x68, 0xea, 0xbb, 0x46, 0x7e, 0x87, 0xe5, 0x5b, 0xae,
|
||||
0x16, 0xe3, 0x17, 0xb0, 0xfb, 0xc3, 0x65, 0xd9, 0x3e, 0xd8, 0xd7, 0x58, 0xd6, 0xa6, 0x55, 0x8f,
|
||||
0xec, 0x0e, 0xf4, 0x3e, 0xf1, 0x65, 0xd1, 0xd8, 0x66, 0x5e, 0x4e, 0x3b, 0xcf, 0xac, 0xc3, 0x2f,
|
||||
0x36, 0xf4, 0xcd, 0x5c, 0xd9, 0x13, 0x18, 0xf0, 0xa5, 0xe0, 0x0a, 0x95, 0x67, 0xd1, 0x4c, 0xf7,
|
||||
0xd6, 0x33, 0x3d, 0xab, 0x02, 0x61, 0x13, 0xaf, 0xa9, 0xe8, 0x6c, 0x50, 0x61, 0xb7, 0xa8, 0x38,
|
||||
0x6d, 0x79, 0xd4, 0xa5, 0x7a, 0xf7, 0xd7, 0xf5, 0xcc, 0x27, 0xff, 0xdd, 0xa4, 0xde, 0x16, 0x4c,
|
||||
0xea, 0xdf, 0xd8, 0x24, 0x42, 0x32, 0x9f, 0x63, 0xdc, 0x46, 0x72, 0xd0, 0x20, 0x59, 0x05, 0xd6,
|
||||
0x48, 0xb6, 0x97, 0xc0, 0xf9, 0x69, 0x09, 0x7e, 0xe1, 0xa4, 0xbb, 0x75, 0x27, 0xbf, 0xda, 0xd0,
|
||||
0x23, 0x9b, 0x36, 0x76, 0xf6, 0x1e, 0xb8, 0xab, 0xfe, 0xeb, 0x73, 0x0e, 0xd6, 0x8d, 0xb3, 0x03,
|
||||
0x80, 0x44, 0x16, 0xa9, 0x8e, 0xaa, 0x5f, 0x45, 0x6d, 0xa0, 0x4b, 0xca, 0x65, 0x99, 0x21, 0x7b,
|
||||
0x04, 0x23, 0x13, 0xe6, 0xb3, 0x19, 0x2a, 0x25, 0x73, 0xaf, 0x6b, 0x3a, 0x27, 0xf5, 0xac, 0x16,
|
||||
0xd7, 0x55, 0x32, 0xae, 0x17, 0xe4, 0x56, 0x53, 0xe5, 0x03, 0xd7, 0x8b, 0x3f, 0xef, 0x2b, 0x35,
|
||||
0xfd, 0x5b, 0x14, 0x1a, 0xb4, 0x06, 0x2d, 0xb4, 0x36, 0xf0, 0x70, 0xb6, 0x80, 0x87, 0x7b, 0x63,
|
||||
0x3c, 0x4e, 0xe0, 0x6e, 0x8d, 0xc7, 0x55, 0x2e, 0x93, 0x36, 0x23, 0x40, 0x00, 0xdc, 0x36, 0xd1,
|
||||
0xd7, 0xb9, 0x4c, 0x56, 0x9c, 0xfc, 0x97, 0xc7, 0xd3, 0x3e, 0x75, 0x75, 0xf2, 0x3d, 0x00, 0x00,
|
||||
0xff, 0xff, 0x17, 0x1c, 0xfc, 0x89, 0xd8, 0x05, 0x00, 0x00,
|
||||
// 603 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x93, 0xdd, 0x6e, 0xd3, 0x30,
|
||||
0x14, 0xc7, 0xd5, 0xa6, 0x9f, 0x27, 0x5d, 0x37, 0x2c, 0x84, 0x4c, 0xa5, 0x41, 0x37, 0x69, 0x28,
|
||||
0x70, 0x91, 0x49, 0xe3, 0x86, 0x8d, 0x0b, 0x34, 0xc1, 0x80, 0x09, 0x21, 0xa1, 0x68, 0x5c, 0x47,
|
||||
0x6e, 0xe2, 0xb5, 0xd6, 0x92, 0x38, 0x8a, 0x1d, 0x44, 0x5e, 0x87, 0x97, 0xe1, 0x69, 0x78, 0x07,
|
||||
0xe4, 0xe3, 0xa6, 0x0d, 0x74, 0x7c, 0x4c, 0xdb, 0x9d, 0xf3, 0x3f, 0xc7, 0xc7, 0x27, 0xe7, 0xff,
|
||||
0x3b, 0xe0, 0xea, 0x2a, 0xe7, 0xca, 0xcf, 0x0b, 0xa9, 0x25, 0x19, 0x88, 0x98, 0x67, 0x5a, 0xe8,
|
||||
0x6a, 0xf2, 0x78, 0x2e, 0xe5, 0x3c, 0xe1, 0x87, 0xa8, 0xcf, 0xca, 0xcb, 0x43, 0x2d, 0x52, 0xae,
|
||||
0x34, 0x4b, 0x73, 0x9b, 0xba, 0xff, 0xad, 0x03, 0xdd, 0x77, 0x85, 0x2c, 0x73, 0x32, 0x86, 0xb6,
|
||||
0x88, 0x69, 0x6b, 0xda, 0xf2, 0x86, 0x41, 0x5b, 0xc4, 0x84, 0x40, 0x27, 0x63, 0x29, 0xa7, 0x6d,
|
||||
0x54, 0xf0, 0x4c, 0x26, 0x30, 0xc8, 0x65, 0x22, 0x22, 0xc1, 0x15, 0x75, 0xa6, 0x8e, 0x37, 0x0c,
|
||||
0x56, 0xdf, 0xc4, 0x83, 0x9d, 0x9c, 0x15, 0x3c, 0xd3, 0xe1, 0xdc, 0xd4, 0x0b, 0x45, 0xac, 0x68,
|
||||
0x07, 0x73, 0xc6, 0x56, 0xc7, 0x67, 0xce, 0x63, 0x45, 0x9e, 0xc1, 0xbd, 0x94, 0xa7, 0x33, 0x5e,
|
||||
0x84, 0xb6, 0x4b, 0x4c, 0xed, 0x62, 0xea, 0xb6, 0x0d, 0x9c, 0xa1, 0x6e, 0x72, 0x8f, 0x61, 0x90,
|
||||
0x72, 0xcd, 0x62, 0xa6, 0x19, 0xed, 0x4d, 0x1d, 0xcf, 0x3d, 0xda, 0xf5, 0xeb, 0xbf, 0xf3, 0xb1,
|
||||
0xa2, 0xff, 0x71, 0x19, 0x3f, 0xcb, 0x74, 0x51, 0x05, 0xab, 0x74, 0xf2, 0x0a, 0xb6, 0xa2, 0x82,
|
||||
0x33, 0x2d, 0x64, 0x16, 0x9a, 0xdf, 0xa6, 0xfd, 0x69, 0xcb, 0x73, 0x8f, 0x26, 0xbe, 0x9d, 0x89,
|
||||
0x5f, 0xcf, 0xc4, 0xbf, 0xa8, 0x67, 0x12, 0x8c, 0xea, 0x0b, 0x46, 0x22, 0x6f, 0x60, 0x27, 0x61,
|
||||
0x4a, 0x87, 0x65, 0x1e, 0x33, 0xcd, 0x6d, 0x8d, 0xc1, 0x3f, 0x6b, 0x8c, 0xcd, 0x9d, 0xcf, 0x78,
|
||||
0x05, 0xab, 0xec, 0xc1, 0x28, 0x95, 0xb1, 0xb8, 0xac, 0x42, 0x91, 0xc5, 0xfc, 0x2b, 0x1d, 0x4e,
|
||||
0x5b, 0x5e, 0x27, 0x70, 0xad, 0x76, 0x6e, 0x24, 0xf2, 0x04, 0xb6, 0x67, 0x65, 0x74, 0xc5, 0x75,
|
||||
0x78, 0xc5, 0xab, 0x70, 0xc1, 0xd4, 0x82, 0x02, 0x4e, 0x7d, 0xcb, 0xca, 0x1f, 0x78, 0xf5, 0x9e,
|
||||
0xa9, 0x05, 0x39, 0x80, 0x2e, 0x4b, 0x04, 0x53, 0xd4, 0xc5, 0x2e, 0xb6, 0xd7, 0x93, 0x38, 0x35,
|
||||
0x72, 0x60, 0xa3, 0xc6, 0x39, 0x43, 0x03, 0x1d, 0x59, 0xe7, 0xcc, 0x79, 0xf2, 0x12, 0xb6, 0x7e,
|
||||
0x99, 0x13, 0xd9, 0x01, 0xe7, 0x8a, 0x57, 0x4b, 0xbf, 0xcd, 0x91, 0xdc, 0x87, 0xee, 0x17, 0x96,
|
||||
0x94, 0xb5, 0xe3, 0xf6, 0xe3, 0xa4, 0xfd, 0xa2, 0xb5, 0xff, 0xdd, 0x81, 0x9e, 0xb5, 0x84, 0x3c,
|
||||
0x85, 0x3e, 0x3e, 0xc2, 0x15, 0x6d, 0xa1, 0x1d, 0x1b, 0x4d, 0xd4, 0xf1, 0x25, 0x50, 0xed, 0x0d,
|
||||
0xa0, 0x9c, 0x06, 0x50, 0x27, 0x0d, 0x7b, 0x3b, 0x58, 0xef, 0xd1, 0xba, 0x9e, 0x7d, 0xf2, 0xff,
|
||||
0xfd, 0xed, 0xde, 0x81, 0xbf, 0xbd, 0x1b, 0xfb, 0x8b, 0x34, 0x17, 0x73, 0x1e, 0x37, 0x69, 0xee,
|
||||
0xd7, 0x34, 0x9b, 0xc0, 0x9a, 0xe6, 0xe6, 0xfe, 0x0c, 0x7e, 0xdb, 0x9f, 0x6b, 0x20, 0x18, 0x5e,
|
||||
0x03, 0xc1, 0xed, 0x9c, 0xfc, 0xe1, 0x40, 0x17, 0x6d, 0xda, 0x58, 0xf7, 0x3d, 0x18, 0x45, 0x2c,
|
||||
0x93, 0x99, 0x88, 0x58, 0x12, 0xae, 0x7c, 0x73, 0x57, 0xda, 0x79, 0x4c, 0x76, 0x01, 0x52, 0x59,
|
||||
0x66, 0x3a, 0x44, 0xba, 0xac, 0x8d, 0x43, 0x54, 0x2e, 0xaa, 0x9c, 0x93, 0x03, 0x18, 0xdb, 0x30,
|
||||
0x8b, 0x22, 0xae, 0x94, 0x2c, 0x68, 0xc7, 0xf6, 0x8f, 0xea, 0xe9, 0x52, 0x5c, 0x57, 0xc9, 0x99,
|
||||
0x5e, 0xa0, 0x67, 0x75, 0x95, 0x4f, 0x4c, 0x2f, 0xfe, 0xbe, 0xf0, 0xd8, 0xfa, 0x1f, 0x81, 0xa8,
|
||||
0x01, 0xeb, 0x37, 0x00, 0xdb, 0x80, 0x64, 0x70, 0x07, 0x90, 0x0c, 0x6f, 0x0c, 0xc9, 0x31, 0x3c,
|
||||
0x5c, 0x42, 0x72, 0x59, 0xc8, 0x34, 0x6c, 0x4e, 0x5a, 0x51, 0x40, 0x12, 0x1e, 0xd8, 0x84, 0xb7,
|
||||
0x85, 0x4c, 0x5f, 0xaf, 0x87, 0xae, 0x6e, 0xe5, 0xf7, 0xac, 0x87, 0xbd, 0x3d, 0xff, 0x19, 0x00,
|
||||
0x00, 0xff, 0xff, 0x8e, 0x4a, 0xc5, 0xdb, 0x1f, 0x06, 0x00, 0x00,
|
||||
}
|
||||
|
|
|
@ -42,6 +42,17 @@ message Group {
|
|||
// the groups belonging to a particular bucket during invalidation of the
|
||||
// storage key.
|
||||
string bucket_key_hash = 10;
|
||||
|
||||
// Alias is used to mark this group as an internal mapping of a group that
|
||||
// is external to the identity store. Alias can only be set if the 'type'
|
||||
// is set to 'external'.
|
||||
Alias alias = 11;
|
||||
|
||||
// Type indicates if this group is an internal group or an external group.
|
||||
// Memberships of the internal groups can be managed over the API whereas
|
||||
// the memberships on the external group --for which a corresponding alias
|
||||
// will be set-- will be managed automatically.
|
||||
string type = 12;
|
||||
}
|
||||
|
||||
|
||||
|
@ -108,8 +119,8 @@ message Alias {
|
|||
// ID is the unique identifier that represents this alias
|
||||
string id = 1;
|
||||
|
||||
// EntityID is the entity identifier to which this alias belongs to
|
||||
string entity_id = 2;
|
||||
// CanonicalID is the entity identifier to which this alias belongs to
|
||||
string canonical_id = 2;
|
||||
|
||||
// MountType is the backend mount's type to which this alias belongs to.
|
||||
// This enables categorically querying aliases of specific backend types.
|
||||
|
@ -130,8 +141,8 @@ message Alias {
|
|||
map<string, string> metadata = 6;
|
||||
|
||||
// Name is the identifier of this alias in its authentication source.
|
||||
// This does not uniquely identify a alias in Vault. This in conjunction
|
||||
// with MountAccessor form to be the factors that represent a alias in a
|
||||
// This does not uniquely identify an alias in Vault. This in conjunction
|
||||
// with MountAccessor form to be the factors that represent an alias in a
|
||||
// unique way. Aliases will be indexed based on this combined uniqueness
|
||||
// factor.
|
||||
string name = 7;
|
||||
|
@ -144,8 +155,6 @@ message Alias {
|
|||
// on its age and to take action on them, if desired.
|
||||
google.protobuf.Timestamp last_update_time = 9;
|
||||
|
||||
// MergedFromEntityIDs is the FIFO history of merging activity by entity IDs from
|
||||
// which this alias is transfered over to the entity to which it
|
||||
// currently belongs to.
|
||||
repeated string merged_from_entity_ids = 10;
|
||||
// MergedFromCanonicalIDs is the FIFO history of merging activity
|
||||
repeated string merged_from_canonical_ids = 10;
|
||||
}
|
||||
|
|
|
@ -58,7 +58,13 @@ type Auth struct {
|
|||
|
||||
// Alias is the information about the authenticated client returned by
|
||||
// the auth backend
|
||||
Alias *Alias `json:"alias" structs:"alias" mapstructure:"alias"`
|
||||
Alias *Alias `json:"alias" mapstructure:"alias" structs:"alias"`
|
||||
|
||||
// GroupAliases are the informational mappings of external groups which an
|
||||
// authenticated user belongs to. This is used to check if there are
|
||||
// mappings groups for the group aliases in identity store. For all the
|
||||
// matching groups, the entity ID of the user will be added.
|
||||
GroupAliases []*Alias `json:"group_aliases" mapstructure:"group_aliases" structs:"group_aliases"`
|
||||
}
|
||||
|
||||
func (a *Auth) GoString() string {
|
||||
|
|
|
@ -695,7 +695,7 @@ func (c *Core) fetchACLTokenEntryAndEntity(clientToken string) (*ACL, *TokenEntr
|
|||
if te.EntityID != "" {
|
||||
//c.logger.Debug("core: entity set on the token", "entity_id", te.EntityID)
|
||||
// Fetch entity for the entity ID in the token entry
|
||||
entity, err = c.identityStore.memDBEntityByID(te.EntityID, false)
|
||||
entity, err = c.identityStore.MemDBEntityByID(te.EntityID, false)
|
||||
if err != nil {
|
||||
c.logger.Error("core: failed to lookup entity using its ID", "error", err)
|
||||
return nil, nil, nil, ErrInternalError
|
||||
|
@ -705,7 +705,7 @@ func (c *Core) fetchACLTokenEntryAndEntity(clientToken string) (*ACL, *TokenEntr
|
|||
// If there was no corresponding entity object found, it is
|
||||
// possible that the entity got merged into another entity. Try
|
||||
// finding entity based on the merged entity index.
|
||||
entity, err = c.identityStore.memDBEntityByMergedEntityID(te.EntityID, false)
|
||||
entity, err = c.identityStore.MemDBEntityByMergedEntityID(te.EntityID, false)
|
||||
if err != nil {
|
||||
c.logger.Error("core: failed to lookup entity in merged entity ID index", "error", err)
|
||||
return nil, nil, nil, ErrInternalError
|
||||
|
|
|
@ -15,13 +15,13 @@ func lookupPaths(i *IdentityStore) []*framework.Path {
|
|||
Fields: map[string]*framework.FieldSchema{
|
||||
"type": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Type of lookup. Current supported values are 'by_id' and 'by_name'",
|
||||
Description: "Type of lookup. Current supported values are 'id' and 'name'",
|
||||
},
|
||||
"group_name": {
|
||||
"name": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Name of the group.",
|
||||
},
|
||||
"group_id": {
|
||||
"id": {
|
||||
Type: framework.TypeString,
|
||||
Description: "ID of the group.",
|
||||
},
|
||||
|
@ -33,6 +33,68 @@ func lookupPaths(i *IdentityStore) []*framework.Path {
|
|||
HelpSynopsis: strings.TrimSpace(lookupHelp["lookup-group"][0]),
|
||||
HelpDescription: strings.TrimSpace(lookupHelp["lookup-group"][1]),
|
||||
},
|
||||
{
|
||||
Pattern: "lookup/entity-alias$",
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"type": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Type of lookup. Current supported values are 'id', 'canonical_id' and 'factors'.",
|
||||
},
|
||||
"id": {
|
||||
Type: framework.TypeString,
|
||||
Description: "ID of the entity.",
|
||||
},
|
||||
"canonical_id": {
|
||||
Type: framework.TypeString,
|
||||
Description: "ID of the entity to which the alias belongs to.",
|
||||
},
|
||||
"name": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Name of the alias.",
|
||||
},
|
||||
"mount_accessor": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Accessor of the mount to which the entity alias belongs to.",
|
||||
},
|
||||
},
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.UpdateOperation: i.pathLookupEntityAliasUpdate,
|
||||
},
|
||||
|
||||
HelpSynopsis: strings.TrimSpace(lookupHelp["lookup-entity-alias"][0]),
|
||||
HelpDescription: strings.TrimSpace(lookupHelp["lookup-entity-alias"][1]),
|
||||
},
|
||||
{
|
||||
Pattern: "lookup/group-alias$",
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"type": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Type of lookup. Current supported values are 'id', 'canonical_id' and 'factors'.",
|
||||
},
|
||||
"id": {
|
||||
Type: framework.TypeString,
|
||||
Description: "ID of the group.",
|
||||
},
|
||||
"canonical_id": {
|
||||
Type: framework.TypeString,
|
||||
Description: "ID of the group to which the alias belongs to.",
|
||||
},
|
||||
"name": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Name of the alias.",
|
||||
},
|
||||
"mount_accessor": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Accessor of the mount to which the group alias belongs to.",
|
||||
},
|
||||
},
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.UpdateOperation: i.pathLookupGroupAliasUpdate,
|
||||
},
|
||||
|
||||
HelpSynopsis: strings.TrimSpace(lookupHelp["lookup-group-alias"][0]),
|
||||
HelpDescription: strings.TrimSpace(lookupHelp["lookup-group-alias"][1]),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,22 +105,22 @@ func (i *IdentityStore) pathLookupGroupUpdate(req *logical.Request, d *framework
|
|||
}
|
||||
|
||||
switch lookupType {
|
||||
case "by_id":
|
||||
groupID := d.Get("group_id").(string)
|
||||
case "id":
|
||||
groupID := d.Get("id").(string)
|
||||
if groupID == "" {
|
||||
return logical.ErrorResponse("empty group_id"), nil
|
||||
return logical.ErrorResponse("empty ID"), nil
|
||||
}
|
||||
group, err := i.memDBGroupByID(groupID, false)
|
||||
group, err := i.MemDBGroupByID(groupID, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return i.handleGroupReadCommon(group)
|
||||
case "by_name":
|
||||
groupName := d.Get("group_name").(string)
|
||||
case "name":
|
||||
groupName := d.Get("name").(string)
|
||||
if groupName == "" {
|
||||
return logical.ErrorResponse("empty group_name"), nil
|
||||
return logical.ErrorResponse("empty name"), nil
|
||||
}
|
||||
group, err := i.memDBGroupByName(groupName, false)
|
||||
group, err := i.MemDBGroupByName(groupName, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -70,9 +132,99 @@ func (i *IdentityStore) pathLookupGroupUpdate(req *logical.Request, d *framework
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func (i *IdentityStore) pathLookupEntityAliasUpdate(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
return i.handleLookupAliasUpdateCommon(req, d, false)
|
||||
}
|
||||
|
||||
func (i *IdentityStore) pathLookupGroupAliasUpdate(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
return i.handleLookupAliasUpdateCommon(req, d, true)
|
||||
}
|
||||
|
||||
func (i *IdentityStore) handleLookupAliasUpdateCommon(req *logical.Request, d *framework.FieldData, groupAlias bool) (*logical.Response, error) {
|
||||
lookupType := d.Get("type").(string)
|
||||
if lookupType == "" {
|
||||
return logical.ErrorResponse("empty type"), nil
|
||||
}
|
||||
|
||||
switch lookupType {
|
||||
case "id":
|
||||
aliasID := d.Get("id").(string)
|
||||
if aliasID == "" {
|
||||
return logical.ErrorResponse("empty ID"), nil
|
||||
}
|
||||
|
||||
alias, err := i.MemDBAliasByID(aliasID, false, groupAlias)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return i.handleAliasReadCommon(alias)
|
||||
|
||||
case "canonical_id":
|
||||
canonicalID := d.Get("canonical_id").(string)
|
||||
if canonicalID == "" {
|
||||
return logical.ErrorResponse("empty canonical_id"), nil
|
||||
}
|
||||
|
||||
alias, err := i.MemDBAliasByCanonicalID(canonicalID, false, groupAlias)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return i.handleAliasReadCommon(alias)
|
||||
|
||||
case "factors":
|
||||
aliasName := d.Get("name").(string)
|
||||
if aliasName == "" {
|
||||
return logical.ErrorResponse("empty name"), nil
|
||||
}
|
||||
mountAccessor := d.Get("mount_accessor").(string)
|
||||
if mountAccessor == "" {
|
||||
return logical.ErrorResponse("empty 'mount_accessor'"), nil
|
||||
}
|
||||
|
||||
alias, err := i.MemDBAliasByFactors(mountAccessor, aliasName, false, groupAlias)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return i.handleAliasReadCommon(alias)
|
||||
|
||||
default:
|
||||
return logical.ErrorResponse(fmt.Sprintf("unrecognized type %q", lookupType)), nil
|
||||
}
|
||||
}
|
||||
|
||||
var lookupHelp = map[string][2]string{
|
||||
"lookup-group": {
|
||||
"Query groups based on factors.",
|
||||
"Currently this supports querying groups by its name or ID.",
|
||||
"Query groups based on types.",
|
||||
`Supported types:
|
||||
- 'id'
|
||||
To query the group by its ID. This requires 'id' parameter to be set.
|
||||
- 'name'
|
||||
To query the group by its name. This requires 'name' parameter to be set.
|
||||
`,
|
||||
},
|
||||
"lookup-group-alias": {
|
||||
"Query group alias based on types.",
|
||||
`Supported types:
|
||||
- 'id'
|
||||
To query the group alias by its ID. This requires 'id' parameter to be set.
|
||||
- 'canonical_id'
|
||||
To query the group alias by the ID of the group it belongs to. This requires the 'canonical_id' parameter to be set.
|
||||
- 'factors'
|
||||
To query the group alias using the factors that uniquely identifies a group alias; its name and the mount accessor. This requires the 'name' and 'mount_accessor' parameters to be set.
|
||||
`,
|
||||
},
|
||||
"lookup-entity-alias": {
|
||||
"Query entity alias based on types.",
|
||||
`Supported types:
|
||||
- 'id'
|
||||
To query the entity alias by its ID. This requires 'id' parameter to be set.
|
||||
- 'canonical_id'
|
||||
To query the entity alias by the ID of the entity it belongs to. This requires the 'canonical_id' parameter to be set.
|
||||
- 'factors'
|
||||
To query the entity alias using the factors that uniquely identifies an entity alias; its name and the mount accessor. This requires the 'name' and 'mount_accessor' parameters to be set.
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -0,0 +1,219 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
)
|
||||
|
||||
func TestIdentityStore_Lookup_EntityAlias(t *testing.T) {
|
||||
var err error
|
||||
var resp *logical.Response
|
||||
|
||||
i, accessor, _ := testIdentityStoreWithGithubAuth(t)
|
||||
|
||||
entityReq := &logical.Request{
|
||||
Path: "entity",
|
||||
Operation: logical.UpdateOperation,
|
||||
}
|
||||
|
||||
resp, err = i.HandleRequest(entityReq)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\n err: %#v\n", resp, err)
|
||||
}
|
||||
entityID := resp.Data["id"].(string)
|
||||
|
||||
entityAliasReq := &logical.Request{
|
||||
Path: "entity-alias",
|
||||
Operation: logical.UpdateOperation,
|
||||
Data: map[string]interface{}{
|
||||
"canonical_id": entityID,
|
||||
"name": "testentityaliasname",
|
||||
"mount_type": "ldap",
|
||||
"mount_accessor": accessor,
|
||||
},
|
||||
}
|
||||
|
||||
resp, err = i.HandleRequest(entityAliasReq)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\n err: %#v\n", resp, err)
|
||||
}
|
||||
entityAliasID := resp.Data["id"].(string)
|
||||
|
||||
lookupReq := &logical.Request{
|
||||
Path: "lookup/entity-alias",
|
||||
Operation: logical.UpdateOperation,
|
||||
Data: map[string]interface{}{
|
||||
"type": "id",
|
||||
"id": entityAliasID,
|
||||
},
|
||||
}
|
||||
resp, err = i.HandleRequest(lookupReq)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\n err: %#v\n", resp, err)
|
||||
}
|
||||
if resp.Data["id"].(string) != entityAliasID {
|
||||
t.Fatalf("bad: group alias: %#v\n", resp.Data)
|
||||
}
|
||||
|
||||
lookupReq.Data = map[string]interface{}{
|
||||
"type": "factors",
|
||||
"mount_accessor": accessor,
|
||||
"name": "testentityaliasname",
|
||||
}
|
||||
resp, err = i.HandleRequest(lookupReq)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\n err: %#v\n", resp, err)
|
||||
}
|
||||
if resp.Data["id"].(string) != entityAliasID {
|
||||
t.Fatalf("bad: entity alias: %#v\n", resp.Data)
|
||||
}
|
||||
|
||||
entityReq = &logical.Request{
|
||||
Path: "entity/id/" + entityID,
|
||||
Operation: logical.ReadOperation,
|
||||
}
|
||||
resp, err = i.HandleRequest(entityReq)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\n err: %#v\n", resp, err)
|
||||
}
|
||||
|
||||
lookupReq.Data = map[string]interface{}{
|
||||
"type": "canonical_id",
|
||||
"canonical_id": entityID,
|
||||
}
|
||||
resp, err = i.HandleRequest(lookupReq)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\n err: %#v\n", resp, err)
|
||||
}
|
||||
if resp.Data["id"].(string) != entityAliasID {
|
||||
t.Fatalf("bad: entity alias: %#v\n", resp.Data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIdentityStore_Lookup_GroupAlias(t *testing.T) {
|
||||
var err error
|
||||
var resp *logical.Response
|
||||
|
||||
i, accessor, _ := testIdentityStoreWithGithubAuth(t)
|
||||
|
||||
groupReq := &logical.Request{
|
||||
Path: "group",
|
||||
Operation: logical.UpdateOperation,
|
||||
Data: map[string]interface{}{
|
||||
"type": "external",
|
||||
},
|
||||
}
|
||||
|
||||
resp, err = i.HandleRequest(groupReq)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\n err: %#v\n", resp, err)
|
||||
}
|
||||
groupID := resp.Data["id"].(string)
|
||||
|
||||
groupAliasReq := &logical.Request{
|
||||
Path: "group-alias",
|
||||
Operation: logical.UpdateOperation,
|
||||
Data: map[string]interface{}{
|
||||
"canonical_id": groupID,
|
||||
"name": "testgroupaliasname",
|
||||
"mount_type": "ldap",
|
||||
"mount_accessor": accessor,
|
||||
},
|
||||
}
|
||||
|
||||
resp, err = i.HandleRequest(groupAliasReq)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\n err: %#v\n", resp, err)
|
||||
}
|
||||
groupAliasID := resp.Data["id"].(string)
|
||||
|
||||
lookupReq := &logical.Request{
|
||||
Path: "lookup/group-alias",
|
||||
Operation: logical.UpdateOperation,
|
||||
Data: map[string]interface{}{
|
||||
"type": "id",
|
||||
"id": groupAliasID,
|
||||
},
|
||||
}
|
||||
resp, err = i.HandleRequest(lookupReq)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\n err: %#v\n", resp, err)
|
||||
}
|
||||
if resp.Data["id"].(string) != groupAliasID {
|
||||
t.Fatalf("bad: group alias: %#v\n", resp.Data)
|
||||
}
|
||||
|
||||
lookupReq.Data = map[string]interface{}{
|
||||
"type": "factors",
|
||||
"mount_accessor": accessor,
|
||||
"name": "testgroupaliasname",
|
||||
}
|
||||
resp, err = i.HandleRequest(lookupReq)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\n err: %#v\n", resp, err)
|
||||
}
|
||||
if resp.Data["id"].(string) != groupAliasID {
|
||||
t.Fatalf("bad: group alias: %#v\n", resp.Data)
|
||||
}
|
||||
|
||||
lookupReq.Data = map[string]interface{}{
|
||||
"type": "canonical_id",
|
||||
"canonical_id": groupID,
|
||||
}
|
||||
resp, err = i.HandleRequest(lookupReq)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\n err: %#v\n", resp, err)
|
||||
}
|
||||
if resp.Data["id"].(string) != groupAliasID {
|
||||
t.Fatalf("bad: group alias: %#v\n", resp.Data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIdentityStore_Lookup_Group(t *testing.T) {
|
||||
var err error
|
||||
var resp *logical.Response
|
||||
|
||||
i, _, _ := testIdentityStoreWithGithubAuth(t)
|
||||
|
||||
groupReq := &logical.Request{
|
||||
Path: "group",
|
||||
Operation: logical.UpdateOperation,
|
||||
}
|
||||
resp, err = i.HandleRequest(groupReq)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\n err: %#v\n", resp, err)
|
||||
}
|
||||
groupID := resp.Data["id"].(string)
|
||||
groupName := resp.Data["name"].(string)
|
||||
|
||||
lookupGroupReq := &logical.Request{
|
||||
Path: "lookup/group",
|
||||
Operation: logical.UpdateOperation,
|
||||
Data: map[string]interface{}{
|
||||
"type": "id",
|
||||
"id": groupID,
|
||||
},
|
||||
}
|
||||
|
||||
resp, err = i.HandleRequest(lookupGroupReq)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\n err: %#v\n", resp, err)
|
||||
}
|
||||
if resp.Data["id"].(string) != groupID {
|
||||
t.Fatalf("failed to lookup group")
|
||||
}
|
||||
|
||||
lookupGroupReq.Data = map[string]interface{}{
|
||||
"type": "name",
|
||||
"name": groupName,
|
||||
}
|
||||
|
||||
resp, err = i.HandleRequest(lookupGroupReq)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\n err: %#v\n", resp, err)
|
||||
}
|
||||
if resp.Data["id"].(string) != groupID {
|
||||
t.Fatalf("failed to lookup group")
|
||||
}
|
||||
}
|
|
@ -17,6 +17,10 @@ const (
|
|||
groupBucketsPrefix = "packer/group/buckets/"
|
||||
)
|
||||
|
||||
func (c *Core) IdentityStore() *IdentityStore {
|
||||
return c.identityStore
|
||||
}
|
||||
|
||||
// NewIdentityStore creates a new identity store
|
||||
func NewIdentityStore(core *Core, config *logical.BackendConfig) (*IdentityStore, error) {
|
||||
var err error
|
||||
|
@ -50,6 +54,7 @@ func NewIdentityStore(core *Core, config *logical.BackendConfig) (*IdentityStore
|
|||
Paths: framework.PathAppend(
|
||||
entityPaths(iStore),
|
||||
aliasPaths(iStore),
|
||||
groupAliasPaths(iStore),
|
||||
groupPaths(iStore),
|
||||
lookupPaths(iStore),
|
||||
upgradePaths(iStore),
|
||||
|
@ -90,7 +95,7 @@ func (i *IdentityStore) Invalidate(key string) {
|
|||
// entry key of the entity bucket. Fetch all the entities that
|
||||
// belong to this bucket using the hash value. Remove these entities
|
||||
// from MemDB along with all the aliases of each entity.
|
||||
entitiesFetched, err := i.memDBEntitiesByBucketEntryKeyHashInTxn(txn, string(bucketKeyHash))
|
||||
entitiesFetched, err := i.MemDBEntitiesByBucketEntryKeyHashInTxn(txn, string(bucketKeyHash))
|
||||
if err != nil {
|
||||
i.logger.Error("failed to fetch entities using the bucket entry key hash", "bucket_entry_key_hash", bucketKeyHash)
|
||||
return
|
||||
|
@ -106,7 +111,7 @@ func (i *IdentityStore) Invalidate(key string) {
|
|||
}
|
||||
|
||||
// Delete the entity using the same transaction
|
||||
err = i.memDBDeleteEntityByIDInTxn(txn, entity.ID)
|
||||
err = i.MemDBDeleteEntityByIDInTxn(txn, entity.ID)
|
||||
if err != nil {
|
||||
i.logger.Error("failed to delete entity from MemDB", "entity_id", entity.ID, "error", err)
|
||||
return
|
||||
|
@ -160,7 +165,7 @@ func (i *IdentityStore) Invalidate(key string) {
|
|||
txn := i.db.Txn(true)
|
||||
defer txn.Abort()
|
||||
|
||||
groupsFetched, err := i.memDBGroupsByBucketEntryKeyHashInTxn(txn, string(bucketKeyHash))
|
||||
groupsFetched, err := i.MemDBGroupsByBucketEntryKeyHashInTxn(txn, string(bucketKeyHash))
|
||||
if err != nil {
|
||||
i.logger.Error("failed to fetch groups using the bucket entry key hash", "bucket_entry_key_hash", bucketKeyHash)
|
||||
return
|
||||
|
@ -168,7 +173,7 @@ func (i *IdentityStore) Invalidate(key string) {
|
|||
|
||||
for _, group := range groupsFetched {
|
||||
// Delete the group using the same transaction
|
||||
err = i.memDBDeleteGroupByIDInTxn(txn, group.ID)
|
||||
err = i.MemDBDeleteGroupByIDInTxn(txn, group.ID)
|
||||
if err != nil {
|
||||
i.logger.Error("failed to delete group from MemDB", "group_id", group.ID, "error", err)
|
||||
return
|
||||
|
@ -232,9 +237,9 @@ func (i *IdentityStore) parseGroupFromBucketItem(item *storagepacker.Item) (*ide
|
|||
return &group, nil
|
||||
}
|
||||
|
||||
// EntityByAliasFactors fetches the entity based on factors of alias, i.e mount
|
||||
// entityByAliasFactors fetches the entity based on factors of alias, i.e mount
|
||||
// accessor and the alias name.
|
||||
func (i *IdentityStore) EntityByAliasFactors(mountAccessor, aliasName string, clone bool) (*identity.Entity, error) {
|
||||
func (i *IdentityStore) entityByAliasFactors(mountAccessor, aliasName string, clone bool) (*identity.Entity, error) {
|
||||
if mountAccessor == "" {
|
||||
return nil, fmt.Errorf("missing mount accessor")
|
||||
}
|
||||
|
@ -243,7 +248,7 @@ func (i *IdentityStore) EntityByAliasFactors(mountAccessor, aliasName string, cl
|
|||
return nil, fmt.Errorf("missing alias name")
|
||||
}
|
||||
|
||||
alias, err := i.memDBAliasByFactors(mountAccessor, aliasName, false)
|
||||
alias, err := i.MemDBAliasByFactors(mountAccessor, aliasName, false, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -252,11 +257,11 @@ func (i *IdentityStore) EntityByAliasFactors(mountAccessor, aliasName string, cl
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
return i.memDBEntityByAliasID(alias.ID, clone)
|
||||
return i.MemDBEntityByAliasID(alias.ID, clone)
|
||||
}
|
||||
|
||||
// CreateEntity creates a new entity. This is used by core to
|
||||
// associate each login attempt by a alias to a unified entity in Vault.
|
||||
// associate each login attempt by an alias to a unified entity in Vault.
|
||||
func (i *IdentityStore) CreateEntity(alias *logical.Alias) (*identity.Entity, error) {
|
||||
var entity *identity.Entity
|
||||
var err error
|
||||
|
@ -279,7 +284,7 @@ func (i *IdentityStore) CreateEntity(alias *logical.Alias) (*identity.Entity, er
|
|||
}
|
||||
|
||||
// Check if an entity already exists for the given alais
|
||||
entity, err = i.EntityByAliasFactors(alias.MountAccessor, alias.Name, false)
|
||||
entity, err = i.entityByAliasFactors(alias.MountAccessor, alias.Name, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -296,7 +301,7 @@ func (i *IdentityStore) CreateEntity(alias *logical.Alias) (*identity.Entity, er
|
|||
|
||||
// Create a new alias
|
||||
newAlias := &identity.Alias{
|
||||
EntityID: entity.ID,
|
||||
CanonicalID: entity.ID,
|
||||
Name: alias.Name,
|
||||
MountAccessor: alias.MountAccessor,
|
||||
MountPath: mountValidationResp.MountPath,
|
||||
|
|
|
@ -13,10 +13,47 @@ import (
|
|||
|
||||
// aliasPaths returns the API endpoints to operate on aliases.
|
||||
// Following are the paths supported:
|
||||
// alias - To register/modify a alias
|
||||
// alias - To register/modify an alias
|
||||
// alias/id - To lookup, delete and list aliases based on ID
|
||||
func aliasPaths(i *IdentityStore) []*framework.Path {
|
||||
return []*framework.Path{
|
||||
{
|
||||
Pattern: "entity-alias$",
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"id": {
|
||||
Type: framework.TypeString,
|
||||
Description: "ID of the alias",
|
||||
},
|
||||
// entity_id is deprecated
|
||||
"entity_id": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Entity ID to which this alias belongs to",
|
||||
},
|
||||
"canonical_id": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Entity ID to which this alias belongs to",
|
||||
},
|
||||
"mount_accessor": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Mount accessor to which this alias belongs to",
|
||||
},
|
||||
"name": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Name of the alias",
|
||||
},
|
||||
"metadata": {
|
||||
Type: framework.TypeStringSlice,
|
||||
Description: "Metadata to be associated with the alias. Format should be a list of `key=value` pairs.",
|
||||
},
|
||||
},
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.UpdateOperation: i.pathAliasRegister,
|
||||
},
|
||||
|
||||
HelpSynopsis: strings.TrimSpace(aliasHelp["alias"][0]),
|
||||
HelpDescription: strings.TrimSpace(aliasHelp["alias"][1]),
|
||||
},
|
||||
// BC path for identity/entity-alias
|
||||
{
|
||||
Pattern: "alias$",
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
|
@ -24,10 +61,15 @@ func aliasPaths(i *IdentityStore) []*framework.Path {
|
|||
Type: framework.TypeString,
|
||||
Description: "ID of the alias",
|
||||
},
|
||||
// entity_id is deprecated
|
||||
"entity_id": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Entity ID to which this alias belongs to",
|
||||
},
|
||||
"canonical_id": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Entity ID to which this alias belongs to",
|
||||
},
|
||||
"mount_accessor": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Mount accessor to which this alias belongs to",
|
||||
|
@ -48,6 +90,45 @@ func aliasPaths(i *IdentityStore) []*framework.Path {
|
|||
HelpSynopsis: strings.TrimSpace(aliasHelp["alias"][0]),
|
||||
HelpDescription: strings.TrimSpace(aliasHelp["alias"][1]),
|
||||
},
|
||||
{
|
||||
Pattern: "entity-alias/id/" + framework.GenericNameRegex("id"),
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"id": {
|
||||
Type: framework.TypeString,
|
||||
Description: "ID of the alias",
|
||||
},
|
||||
// entity_id is deprecated
|
||||
"entity_id": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Entity ID to which this alias belongs to",
|
||||
},
|
||||
"canonical_id": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Entity ID to which this alias should be tied to",
|
||||
},
|
||||
"mount_accessor": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Mount accessor to which this alias belongs to",
|
||||
},
|
||||
"name": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Name of the alias",
|
||||
},
|
||||
"metadata": {
|
||||
Type: framework.TypeStringSlice,
|
||||
Description: "Metadata to be associated with the alias. Format should be a comma separated list of `key=value` pairs.",
|
||||
},
|
||||
},
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.UpdateOperation: i.pathAliasIDUpdate,
|
||||
logical.ReadOperation: i.pathAliasIDRead,
|
||||
logical.DeleteOperation: i.pathAliasIDDelete,
|
||||
},
|
||||
|
||||
HelpSynopsis: strings.TrimSpace(aliasHelp["alias-id"][0]),
|
||||
HelpDescription: strings.TrimSpace(aliasHelp["alias-id"][1]),
|
||||
},
|
||||
// BC path for identity/entity-alias/id/<id>
|
||||
{
|
||||
Pattern: "alias/id/" + framework.GenericNameRegex("id"),
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
|
@ -55,10 +136,15 @@ func aliasPaths(i *IdentityStore) []*framework.Path {
|
|||
Type: framework.TypeString,
|
||||
Description: "ID of the alias",
|
||||
},
|
||||
// entity_id is deprecated
|
||||
"entity_id": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Entity ID to which this alias should be tied to",
|
||||
},
|
||||
"canonical_id": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Entity ID to which this alias should be tied to",
|
||||
},
|
||||
"mount_accessor": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Mount accessor to which this alias belongs to",
|
||||
|
@ -81,6 +167,16 @@ func aliasPaths(i *IdentityStore) []*framework.Path {
|
|||
HelpSynopsis: strings.TrimSpace(aliasHelp["alias-id"][0]),
|
||||
HelpDescription: strings.TrimSpace(aliasHelp["alias-id"][1]),
|
||||
},
|
||||
{
|
||||
Pattern: "entity-alias/id/?$",
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.ListOperation: i.pathAliasIDList,
|
||||
},
|
||||
|
||||
HelpSynopsis: strings.TrimSpace(aliasHelp["alias-id-list"][0]),
|
||||
HelpDescription: strings.TrimSpace(aliasHelp["alias-id-list"][1]),
|
||||
},
|
||||
// BC path for identity/alias/id
|
||||
{
|
||||
Pattern: "alias/id/?$",
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
|
@ -103,17 +199,17 @@ func (i *IdentityStore) pathAliasRegister(req *logical.Request, d *framework.Fie
|
|||
return i.handleAliasUpdateCommon(req, d, nil)
|
||||
}
|
||||
|
||||
// pathAliasIDUpdate is used to update a alias based on the given
|
||||
// pathAliasIDUpdate is used to update an alias based on the given
|
||||
// alias ID
|
||||
func (i *IdentityStore) pathAliasIDUpdate(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
// Get alias id
|
||||
aliasID := d.Get("id").(string)
|
||||
|
||||
if aliasID == "" {
|
||||
return logical.ErrorResponse("missing alias ID"), nil
|
||||
return logical.ErrorResponse("empty alias ID"), nil
|
||||
}
|
||||
|
||||
alias, err := i.memDBAliasByID(aliasID, true)
|
||||
alias, err := i.MemDBAliasByID(aliasID, true, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -124,7 +220,7 @@ func (i *IdentityStore) pathAliasIDUpdate(req *logical.Request, d *framework.Fie
|
|||
return i.handleAliasUpdateCommon(req, d, alias)
|
||||
}
|
||||
|
||||
// handleAliasUpdateCommon is used to update a alias
|
||||
// handleAliasUpdateCommon is used to update an alias
|
||||
func (i *IdentityStore) handleAliasUpdateCommon(req *logical.Request, d *framework.FieldData, alias *identity.Alias) (*logical.Response, error) {
|
||||
var err error
|
||||
var newAlias bool
|
||||
|
@ -139,9 +235,13 @@ func (i *IdentityStore) handleAliasUpdateCommon(req *logical.Request, d *framewo
|
|||
}
|
||||
|
||||
// Get entity id
|
||||
entityID := d.Get("entity_id").(string)
|
||||
if entityID != "" {
|
||||
entity, err = i.memDBEntityByID(entityID, true)
|
||||
canonicalID := d.Get("entity_id").(string)
|
||||
if canonicalID == "" {
|
||||
canonicalID = d.Get("canonical_id").(string)
|
||||
}
|
||||
|
||||
if canonicalID != "" {
|
||||
entity, err = i.MemDBEntityByID(canonicalID, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -179,7 +279,7 @@ func (i *IdentityStore) handleAliasUpdateCommon(req *logical.Request, d *framewo
|
|||
}
|
||||
}
|
||||
|
||||
aliasByFactors, err := i.memDBAliasByFactors(mountValidationResp.MountAccessor, aliasName, false)
|
||||
aliasByFactors, err := i.MemDBAliasByFactors(mountValidationResp.MountAccessor, aliasName, false, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -191,7 +291,7 @@ func (i *IdentityStore) handleAliasUpdateCommon(req *logical.Request, d *framewo
|
|||
return logical.ErrorResponse("combination of mount and alias name is already in use"), nil
|
||||
}
|
||||
|
||||
// If this is a alias being tied to a non-existent entity, create
|
||||
// If this is an alias being tied to a non-existent entity, create
|
||||
// a new entity for it.
|
||||
if entity == nil {
|
||||
entity = &identity.Entity{
|
||||
|
@ -210,7 +310,7 @@ func (i *IdentityStore) handleAliasUpdateCommon(req *logical.Request, d *framewo
|
|||
}
|
||||
|
||||
// Fetch the entity to which the alias is tied to
|
||||
existingEntity, err := i.memDBEntityByAliasID(alias.ID, true)
|
||||
existingEntity, err := i.MemDBEntityByAliasID(alias.ID, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -253,9 +353,9 @@ func (i *IdentityStore) handleAliasUpdateCommon(req *logical.Request, d *framewo
|
|||
alias.MountAccessor = mountValidationResp.MountAccessor
|
||||
alias.MountPath = mountValidationResp.MountPath
|
||||
|
||||
// Set the entity ID in the alias index. This should be done after
|
||||
// Set the canonical ID in the alias index. This should be done after
|
||||
// sanitizing entity.
|
||||
alias.EntityID = entity.ID
|
||||
alias.CanonicalID = entity.ID
|
||||
|
||||
// ID creation and other validations
|
||||
err = i.sanitizeAlias(alias)
|
||||
|
@ -274,14 +374,14 @@ func (i *IdentityStore) handleAliasUpdateCommon(req *logical.Request, d *framewo
|
|||
|
||||
// Return ID of both alias and entity
|
||||
resp.Data = map[string]interface{}{
|
||||
"id": alias.ID,
|
||||
"entity_id": entity.ID,
|
||||
"id": alias.ID,
|
||||
"canonical_id": entity.ID,
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// pathAliasIDRead returns the properties of a alias for a given
|
||||
// pathAliasIDRead returns the properties of an alias for a given
|
||||
// alias ID
|
||||
func (i *IdentityStore) pathAliasIDRead(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
aliasID := d.Get("id").(string)
|
||||
|
@ -289,24 +389,28 @@ func (i *IdentityStore) pathAliasIDRead(req *logical.Request, d *framework.Field
|
|||
return logical.ErrorResponse("missing alias id"), nil
|
||||
}
|
||||
|
||||
alias, err := i.memDBAliasByID(aliasID, false)
|
||||
alias, err := i.MemDBAliasByID(aliasID, false, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return i.handleAliasReadCommon(alias)
|
||||
}
|
||||
|
||||
func (i *IdentityStore) handleAliasReadCommon(alias *identity.Alias) (*logical.Response, error) {
|
||||
if alias == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
respData := map[string]interface{}{}
|
||||
respData["id"] = alias.ID
|
||||
respData["entity_id"] = alias.EntityID
|
||||
respData["canonical_id"] = alias.CanonicalID
|
||||
respData["mount_type"] = alias.MountType
|
||||
respData["mount_accessor"] = alias.MountAccessor
|
||||
respData["mount_path"] = alias.MountPath
|
||||
respData["metadata"] = alias.Metadata
|
||||
respData["name"] = alias.Name
|
||||
respData["merged_from_entity_ids"] = alias.MergedFromEntityIDs
|
||||
respData["merged_from_canonical_ids"] = alias.MergedFromCanonicalIDs
|
||||
|
||||
// Convert protobuf timestamp into RFC3339 format
|
||||
respData["creation_time"] = ptypes.TimestampString(alias.CreationTime)
|
||||
|
@ -317,7 +421,7 @@ func (i *IdentityStore) pathAliasIDRead(req *logical.Request, d *framework.Field
|
|||
}, nil
|
||||
}
|
||||
|
||||
// pathAliasIDDelete deleted the alias for a given alias ID
|
||||
// pathAliasIDDelete deletes the alias for a given alias ID
|
||||
func (i *IdentityStore) pathAliasIDDelete(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
aliasID := d.Get("id").(string)
|
||||
if aliasID == "" {
|
||||
|
@ -331,7 +435,7 @@ func (i *IdentityStore) pathAliasIDDelete(req *logical.Request, d *framework.Fie
|
|||
// store
|
||||
func (i *IdentityStore) pathAliasIDList(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
ws := memdb.NewWatchSet()
|
||||
iter, err := i.memDBAliases(ws)
|
||||
iter, err := i.MemDBAliases(ws, false)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch iterator for aliases in memdb: %v", err)
|
||||
}
|
||||
|
@ -350,15 +454,15 @@ func (i *IdentityStore) pathAliasIDList(req *logical.Request, d *framework.Field
|
|||
|
||||
var aliasHelp = map[string][2]string{
|
||||
"alias": {
|
||||
"Create a new alias",
|
||||
"Create a new alias.",
|
||||
"",
|
||||
},
|
||||
"alias-id": {
|
||||
"Update, read or delete an entity using alias ID",
|
||||
"Update, read or delete an alias ID.",
|
||||
"",
|
||||
},
|
||||
"alias-id-list": {
|
||||
"List all the entity IDs",
|
||||
"List all the entity IDs.",
|
||||
"",
|
||||
},
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ func TestIdentityStore_ListAlias(t *testing.T) {
|
|||
}
|
||||
entityID := resp.Data["id"].(string)
|
||||
|
||||
// Create a alias
|
||||
// Create an alias
|
||||
aliasData := map[string]interface{}{
|
||||
"name": "testaliasname",
|
||||
"mount_accessor": githubAccessor,
|
||||
|
@ -82,7 +82,7 @@ func TestIdentityStore_AliasSameAliasNames(t *testing.T) {
|
|||
Data: aliasData,
|
||||
}
|
||||
|
||||
// Register a alias
|
||||
// Register an alias
|
||||
resp, err = is.HandleRequest(aliasReq)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("err:%v resp:%#v", err, resp)
|
||||
|
@ -118,13 +118,13 @@ func TestIdentityStore_MemDBAliasIndexes(t *testing.T) {
|
|||
|
||||
entity.BucketKeyHash = is.entityPacker.BucketKeyHashByItemID(entity.ID)
|
||||
|
||||
err = is.memDBUpsertEntity(entity)
|
||||
err = is.MemDBUpsertEntity(entity)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
alias := &identity.Alias{
|
||||
EntityID: entity.ID,
|
||||
CanonicalID: entity.ID,
|
||||
ID: "testaliasid",
|
||||
MountAccessor: githubAccessor,
|
||||
MountType: validateMountResp.MountType,
|
||||
|
@ -135,12 +135,12 @@ func TestIdentityStore_MemDBAliasIndexes(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
err = is.memDBUpsertAlias(alias)
|
||||
err = is.MemDBUpsertAlias(alias, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
aliasFetched, err := is.memDBAliasByID("testaliasid", false)
|
||||
aliasFetched, err := is.MemDBAliasByID("testaliasid", false, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ func TestIdentityStore_MemDBAliasIndexes(t *testing.T) {
|
|||
t.Fatalf("bad: mismatched aliases; expected: %#v\n actual: %#v\n", alias, aliasFetched)
|
||||
}
|
||||
|
||||
aliasFetched, err = is.memDBAliasByEntityID(entity.ID, false)
|
||||
aliasFetched, err = is.MemDBAliasByCanonicalID(entity.ID, false, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ func TestIdentityStore_MemDBAliasIndexes(t *testing.T) {
|
|||
t.Fatalf("bad: mismatched aliases; expected: %#v\n actual: %#v\n", alias, aliasFetched)
|
||||
}
|
||||
|
||||
aliasFetched, err = is.memDBAliasByFactors(validateMountResp.MountAccessor, "testaliasname", false)
|
||||
aliasFetched, err = is.MemDBAliasByFactors(validateMountResp.MountAccessor, "testaliasname", false, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -167,9 +167,9 @@ func TestIdentityStore_MemDBAliasIndexes(t *testing.T) {
|
|||
t.Fatalf("bad: mismatched aliases; expected: %#v\n actual: %#v\n", alias, aliasFetched)
|
||||
}
|
||||
|
||||
aliasesFetched, err := is.memDBAliasesByMetadata(map[string]string{
|
||||
aliasesFetched, err := is.MemDBAliasesByMetadata(map[string]string{
|
||||
"testkey1": "testmetadatavalue1",
|
||||
}, false)
|
||||
}, false, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -182,9 +182,9 @@ func TestIdentityStore_MemDBAliasIndexes(t *testing.T) {
|
|||
t.Fatalf("bad: mismatched aliases; expected: %#v\n actual: %#v\n", alias, aliasFetched)
|
||||
}
|
||||
|
||||
aliasesFetched, err = is.memDBAliasesByMetadata(map[string]string{
|
||||
aliasesFetched, err = is.MemDBAliasesByMetadata(map[string]string{
|
||||
"testkey2": "testmetadatavalue2",
|
||||
}, false)
|
||||
}, false, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -197,10 +197,10 @@ func TestIdentityStore_MemDBAliasIndexes(t *testing.T) {
|
|||
t.Fatalf("bad: mismatched aliases; expected: %#v\n actual: %#v\n", alias, aliasFetched)
|
||||
}
|
||||
|
||||
aliasesFetched, err = is.memDBAliasesByMetadata(map[string]string{
|
||||
aliasesFetched, err = is.MemDBAliasesByMetadata(map[string]string{
|
||||
"testkey1": "testmetadatavalue1",
|
||||
"testkey2": "testmetadatavalue2",
|
||||
}, false)
|
||||
}, false, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -214,7 +214,7 @@ func TestIdentityStore_MemDBAliasIndexes(t *testing.T) {
|
|||
}
|
||||
|
||||
alias2 := &identity.Alias{
|
||||
EntityID: entity.ID,
|
||||
CanonicalID: entity.ID,
|
||||
ID: "testaliasid2",
|
||||
MountAccessor: validateMountResp.MountAccessor,
|
||||
MountType: validateMountResp.MountType,
|
||||
|
@ -225,14 +225,14 @@ func TestIdentityStore_MemDBAliasIndexes(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
err = is.memDBUpsertAlias(alias2)
|
||||
err = is.MemDBUpsertAlias(alias2, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
aliasesFetched, err = is.memDBAliasesByMetadata(map[string]string{
|
||||
aliasesFetched, err = is.MemDBAliasesByMetadata(map[string]string{
|
||||
"testkey1": "testmetadatavalue1",
|
||||
}, false)
|
||||
}, false, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -241,9 +241,9 @@ func TestIdentityStore_MemDBAliasIndexes(t *testing.T) {
|
|||
t.Fatalf("bad: length of aliases; expected: 2, actual: %d", len(aliasesFetched))
|
||||
}
|
||||
|
||||
aliasesFetched, err = is.memDBAliasesByMetadata(map[string]string{
|
||||
aliasesFetched, err = is.MemDBAliasesByMetadata(map[string]string{
|
||||
"testkey3": "testmetadatavalue3",
|
||||
}, false)
|
||||
}, false, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -252,12 +252,12 @@ func TestIdentityStore_MemDBAliasIndexes(t *testing.T) {
|
|||
t.Fatalf("bad: length of aliases; expected: 1, actual: %d", len(aliasesFetched))
|
||||
}
|
||||
|
||||
err = is.memDBDeleteAliasByID("testaliasid")
|
||||
err = is.MemDBDeleteAliasByID("testaliasid", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
aliasFetched, err = is.memDBAliasByID("testaliasid", false)
|
||||
aliasFetched, err = is.MemDBAliasByID("testaliasid", false, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -305,7 +305,7 @@ func TestIdentityStore_AliasRegister(t *testing.T) {
|
|||
t.Fatalf("invalid alias id in alias register response")
|
||||
}
|
||||
|
||||
entityIDRaw, ok := resp.Data["entity_id"]
|
||||
entityIDRaw, ok := resp.Data["canonical_id"]
|
||||
if !ok {
|
||||
t.Fatalf("entity id not present in alias register response")
|
||||
}
|
||||
|
@ -333,7 +333,7 @@ func TestIdentityStore_AliasUpdate(t *testing.T) {
|
|||
Data: aliasData,
|
||||
}
|
||||
|
||||
// This will create a alias and a corresponding entity
|
||||
// This will create an alias and a corresponding entity
|
||||
resp, err = is.HandleRequest(aliasReq)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("err:%v resp:%#v", err, resp)
|
||||
|
@ -508,7 +508,7 @@ func TestIdentityStore_AliasReadDelete(t *testing.T) {
|
|||
}
|
||||
|
||||
if resp.Data["id"].(string) == "" ||
|
||||
resp.Data["entity_id"].(string) == "" ||
|
||||
resp.Data["canonical_id"].(string) == "" ||
|
||||
resp.Data["name"].(string) != registerData["name"] ||
|
||||
resp.Data["mount_type"].(string) != "github" {
|
||||
t.Fatalf("bad: alias read response; \nexpected: %#v \nactual: %#v\n", registerData, resp.Data)
|
||||
|
|
|
@ -125,7 +125,7 @@ func (i *IdentityStore) pathEntityMergeID(req *logical.Request, d *framework.Fie
|
|||
|
||||
force := d.Get("force").(bool)
|
||||
|
||||
toEntityForLocking, err := i.memDBEntityByID(toEntityID, false)
|
||||
toEntityForLocking, err := i.MemDBEntityByID(toEntityID, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -144,7 +144,7 @@ func (i *IdentityStore) pathEntityMergeID(req *logical.Request, d *framework.Fie
|
|||
defer txn.Abort()
|
||||
|
||||
// Re-read post lock acquisition
|
||||
toEntity, err := i.memDBEntityByID(toEntityID, true)
|
||||
toEntity, err := i.MemDBEntityByID(toEntityID, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -163,7 +163,7 @@ func (i *IdentityStore) pathEntityMergeID(req *logical.Request, d *framework.Fie
|
|||
return logical.ErrorResponse("to_entity_id should not be present in from_entity_ids"), nil
|
||||
}
|
||||
|
||||
lockFromEntity, err := i.memDBEntityByID(fromEntityID, false)
|
||||
lockFromEntity, err := i.MemDBEntityByID(fromEntityID, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -186,7 +186,7 @@ func (i *IdentityStore) pathEntityMergeID(req *logical.Request, d *framework.Fie
|
|||
}
|
||||
|
||||
// Re-read the entities post lock acquisition
|
||||
fromEntity, err := i.memDBEntityByID(fromEntityID, false)
|
||||
fromEntity, err := i.MemDBEntityByID(fromEntityID, false)
|
||||
if err != nil {
|
||||
if fromLockHeld {
|
||||
fromEntityLock.Unlock()
|
||||
|
@ -209,13 +209,12 @@ func (i *IdentityStore) pathEntityMergeID(req *logical.Request, d *framework.Fie
|
|||
}
|
||||
|
||||
for _, alias := range fromEntity.Aliases {
|
||||
// Set the desired entity id
|
||||
alias.EntityID = toEntity.ID
|
||||
// Set the desired canonical ID
|
||||
alias.CanonicalID = toEntity.ID
|
||||
|
||||
// Set the entity id of which this alias is now an alias to
|
||||
alias.MergedFromEntityIDs = append(alias.MergedFromEntityIDs, fromEntity.ID)
|
||||
alias.MergedFromCanonicalIDs = append(alias.MergedFromCanonicalIDs, fromEntity.ID)
|
||||
|
||||
err = i.memDBUpsertAliasInTxn(txn, alias)
|
||||
err = i.MemDBUpsertAliasInTxn(txn, alias, false)
|
||||
if err != nil {
|
||||
if fromLockHeld {
|
||||
fromEntityLock.Unlock()
|
||||
|
@ -237,7 +236,7 @@ func (i *IdentityStore) pathEntityMergeID(req *logical.Request, d *framework.Fie
|
|||
toEntity.MergedEntityIDs = append(toEntity.MergedEntityIDs, fromEntity.ID)
|
||||
|
||||
// Delete the entity which we are merging from in MemDB using the same transaction
|
||||
err = i.memDBDeleteEntityByIDInTxn(txn, fromEntity.ID)
|
||||
err = i.MemDBDeleteEntityByIDInTxn(txn, fromEntity.ID)
|
||||
if err != nil {
|
||||
if fromLockHeld {
|
||||
fromEntityLock.Unlock()
|
||||
|
@ -264,7 +263,7 @@ func (i *IdentityStore) pathEntityMergeID(req *logical.Request, d *framework.Fie
|
|||
}
|
||||
|
||||
// Update MemDB with changes to the entity we are merging to
|
||||
err = i.memDBUpsertEntityInTxn(txn, toEntity)
|
||||
err = i.MemDBUpsertEntityInTxn(txn, toEntity)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -310,7 +309,7 @@ func (i *IdentityStore) pathEntityIDUpdate(req *logical.Request, d *framework.Fi
|
|||
return logical.ErrorResponse("missing entity id"), nil
|
||||
}
|
||||
|
||||
entity, err := i.memDBEntityByID(entityID, true)
|
||||
entity, err := i.MemDBEntityByID(entityID, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -342,7 +341,7 @@ func (i *IdentityStore) handleEntityUpdateCommon(req *logical.Request, d *framew
|
|||
// Get the name
|
||||
entityName := d.Get("name").(string)
|
||||
if entityName != "" {
|
||||
entityByName, err := i.memDBEntityByName(entityName, false)
|
||||
entityByName, err := i.MemDBEntityByName(entityName, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -403,7 +402,7 @@ func (i *IdentityStore) pathEntityIDRead(req *logical.Request, d *framework.Fiel
|
|||
return logical.ErrorResponse("missing entity id"), nil
|
||||
}
|
||||
|
||||
entity, err := i.memDBEntityByID(entityID, false)
|
||||
entity, err := i.MemDBEntityByID(entityID, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -427,13 +426,13 @@ func (i *IdentityStore) pathEntityIDRead(req *logical.Request, d *framework.Fiel
|
|||
for aliasIdx, alias := range entity.Aliases {
|
||||
aliasMap := map[string]interface{}{}
|
||||
aliasMap["id"] = alias.ID
|
||||
aliasMap["entity_id"] = alias.EntityID
|
||||
aliasMap["canonical_id"] = alias.CanonicalID
|
||||
aliasMap["mount_type"] = alias.MountType
|
||||
aliasMap["mount_accessor"] = alias.MountAccessor
|
||||
aliasMap["mount_path"] = alias.MountPath
|
||||
aliasMap["metadata"] = alias.Metadata
|
||||
aliasMap["name"] = alias.Name
|
||||
aliasMap["merged_from_entity_ids"] = alias.MergedFromEntityIDs
|
||||
aliasMap["merged_from_canonical_ids"] = alias.MergedFromCanonicalIDs
|
||||
aliasMap["creation_time"] = ptypes.TimestampString(alias.CreationTime)
|
||||
aliasMap["last_update_time"] = ptypes.TimestampString(alias.LastUpdateTime)
|
||||
aliasesToReturn[aliasIdx] = aliasMap
|
||||
|
@ -464,7 +463,7 @@ func (i *IdentityStore) pathEntityIDDelete(req *logical.Request, d *framework.Fi
|
|||
// store
|
||||
func (i *IdentityStore) pathEntityIDList(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
ws := memdb.NewWatchSet()
|
||||
iter, err := i.memDBEntities(ws)
|
||||
iter, err := i.MemDBEntities(ws)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch iterator for entities in memdb: %v", err)
|
||||
}
|
||||
|
|
|
@ -70,9 +70,9 @@ func TestIdentityStore_EntityCreateUpdate(t *testing.T) {
|
|||
|
||||
func TestIdentityStore_CloneImmutability(t *testing.T) {
|
||||
alias := &identity.Alias{
|
||||
ID: "testaliasid",
|
||||
Name: "testaliasname",
|
||||
MergedFromEntityIDs: []string{"entityid1"},
|
||||
ID: "testaliasid",
|
||||
Name: "testaliasname",
|
||||
MergedFromCanonicalIDs: []string{"entityid1"},
|
||||
}
|
||||
|
||||
entity := &identity.Entity{
|
||||
|
@ -100,9 +100,9 @@ func TestIdentityStore_CloneImmutability(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
alias.MergedFromEntityIDs[0] = "invalidid"
|
||||
alias.MergedFromCanonicalIDs[0] = "invalidid"
|
||||
|
||||
if clonedAlias.MergedFromEntityIDs[0] == "invalidid" {
|
||||
if clonedAlias.MergedFromCanonicalIDs[0] == "invalidid" {
|
||||
t.Fatalf("cloned alias is mutated")
|
||||
}
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ func TestIdentityStore_MemDBImmutability(t *testing.T) {
|
|||
}
|
||||
|
||||
alias1 := &identity.Alias{
|
||||
EntityID: "testentityid",
|
||||
CanonicalID: "testentityid",
|
||||
ID: "testaliasid",
|
||||
MountAccessor: githubAccessor,
|
||||
MountType: validateMountResp.MountType,
|
||||
|
@ -141,12 +141,12 @@ func TestIdentityStore_MemDBImmutability(t *testing.T) {
|
|||
|
||||
entity.BucketKeyHash = is.entityPacker.BucketKeyHashByItemID(entity.ID)
|
||||
|
||||
err = is.memDBUpsertEntity(entity)
|
||||
err = is.MemDBUpsertEntity(entity)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
entityFetched, err := is.memDBEntityByID(entity.ID, true)
|
||||
entityFetched, err := is.MemDBEntityByID(entity.ID, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ func TestIdentityStore_MemDBImmutability(t *testing.T) {
|
|||
// Modify the fetched entity outside of a transaction
|
||||
entityFetched.Aliases[0].ID = "invalidaliasid"
|
||||
|
||||
entityFetched, err = is.memDBEntityByID(entity.ID, false)
|
||||
entityFetched, err = is.MemDBEntityByID(entity.ID, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -360,7 +360,7 @@ func TestIdentityStore_MemDBEntityIndexes(t *testing.T) {
|
|||
}
|
||||
|
||||
alias1 := &identity.Alias{
|
||||
EntityID: "testentityid",
|
||||
CanonicalID: "testentityid",
|
||||
ID: "testaliasid",
|
||||
MountAccessor: githubAccessor,
|
||||
MountType: validateMountResp.MountType,
|
||||
|
@ -372,7 +372,7 @@ func TestIdentityStore_MemDBEntityIndexes(t *testing.T) {
|
|||
}
|
||||
|
||||
alias2 := &identity.Alias{
|
||||
EntityID: "testentityid",
|
||||
CanonicalID: "testentityid",
|
||||
ID: "testaliasid2",
|
||||
MountAccessor: validateMountResp.MountAccessor,
|
||||
MountType: validateMountResp.MountType,
|
||||
|
@ -397,13 +397,13 @@ func TestIdentityStore_MemDBEntityIndexes(t *testing.T) {
|
|||
|
||||
entity.BucketKeyHash = is.entityPacker.BucketKeyHashByItemID(entity.ID)
|
||||
|
||||
err = is.memDBUpsertEntity(entity)
|
||||
err = is.MemDBUpsertEntity(entity)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Fetch the entity using its ID
|
||||
entityFetched, err := is.memDBEntityByID(entity.ID, false)
|
||||
entityFetched, err := is.MemDBEntityByID(entity.ID, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -413,7 +413,7 @@ func TestIdentityStore_MemDBEntityIndexes(t *testing.T) {
|
|||
}
|
||||
|
||||
// Fetch the entity using its name
|
||||
entityFetched, err = is.memDBEntityByName(entity.Name, false)
|
||||
entityFetched, err = is.MemDBEntityByName(entity.Name, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -423,7 +423,7 @@ func TestIdentityStore_MemDBEntityIndexes(t *testing.T) {
|
|||
}
|
||||
|
||||
// Fetch entities using the metadata
|
||||
entitiesFetched, err := is.memDBEntitiesByMetadata(map[string]string{
|
||||
entitiesFetched, err := is.MemDBEntitiesByMetadata(map[string]string{
|
||||
"someusefulkey": "someusefulvalue",
|
||||
}, false)
|
||||
if err != nil {
|
||||
|
@ -438,7 +438,7 @@ func TestIdentityStore_MemDBEntityIndexes(t *testing.T) {
|
|||
t.Fatalf("entity mismatch; entity: %#v\n entitiesFetched[0]: %#v\n", entity, entitiesFetched[0])
|
||||
}
|
||||
|
||||
entitiesFetched, err = is.memDBEntitiesByBucketEntryKeyHash(entity.BucketKeyHash)
|
||||
entitiesFetched, err = is.MemDBEntitiesByBucketEntryKeyHash(entity.BucketKeyHash)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -447,12 +447,12 @@ func TestIdentityStore_MemDBEntityIndexes(t *testing.T) {
|
|||
t.Fatalf("bad: length of entities; expected: 1, actual: %d", len(entitiesFetched))
|
||||
}
|
||||
|
||||
err = is.memDBDeleteEntityByID(entity.ID)
|
||||
err = is.MemDBDeleteEntityByID(entity.ID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
entityFetched, err = is.memDBEntityByID(entity.ID, false)
|
||||
entityFetched, err = is.MemDBEntityByID(entity.ID, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -461,7 +461,7 @@ func TestIdentityStore_MemDBEntityIndexes(t *testing.T) {
|
|||
t.Fatalf("bad: entity; expected: nil, actual: %#v\n", entityFetched)
|
||||
}
|
||||
|
||||
entityFetched, err = is.memDBEntityByName(entity.Name, false)
|
||||
entityFetched, err = is.MemDBEntityByName(entity.Name, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -678,7 +678,7 @@ func TestIdentityStore_MergeEntitiesByID(t *testing.T) {
|
|||
t.Fatalf("err:%v resp:%#v", err, resp)
|
||||
}
|
||||
|
||||
entity1, err := is.memDBEntityByID(entityID1, false)
|
||||
entity1, err := is.MemDBEntityByID(entityID1, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -720,7 +720,7 @@ func TestIdentityStore_MergeEntitiesByID(t *testing.T) {
|
|||
t.Fatalf("err:%v resp:%#v", err, resp)
|
||||
}
|
||||
|
||||
entity2, err := is.memDBEntityByID(entityID2, false)
|
||||
entity2, err := is.MemDBEntityByID(entityID2, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -772,7 +772,7 @@ func TestIdentityStore_MergeEntitiesByID(t *testing.T) {
|
|||
|
||||
for _, aliasRaw := range entity2Aliases {
|
||||
alias := aliasRaw.(map[string]interface{})
|
||||
aliasLookedUp, err := is.memDBAliasByID(alias["id"].(string), false)
|
||||
aliasLookedUp, err := is.MemDBAliasByID(alias["id"].(string), false, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,279 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
memdb "github.com/hashicorp/go-memdb"
|
||||
"github.com/hashicorp/vault/helper/identity"
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
)
|
||||
|
||||
func groupAliasPaths(i *IdentityStore) []*framework.Path {
|
||||
return []*framework.Path{
|
||||
{
|
||||
Pattern: "group-alias$",
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"id": {
|
||||
Type: framework.TypeString,
|
||||
Description: "ID of the group alias.",
|
||||
},
|
||||
"name": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Alias of the group.",
|
||||
},
|
||||
"mount_accessor": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Mount accessor to which this alias belongs to.",
|
||||
},
|
||||
"canonical_id": {
|
||||
Type: framework.TypeString,
|
||||
Description: "ID of the group to which this is an alias.",
|
||||
},
|
||||
},
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.UpdateOperation: i.pathGroupAliasRegister,
|
||||
},
|
||||
|
||||
HelpSynopsis: strings.TrimSpace(groupAliasHelp["group-alias"][0]),
|
||||
HelpDescription: strings.TrimSpace(groupAliasHelp["group-alias"][1]),
|
||||
},
|
||||
{
|
||||
Pattern: "group-alias/id/" + framework.GenericNameRegex("id"),
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"id": {
|
||||
Type: framework.TypeString,
|
||||
Description: "ID of the group alias.",
|
||||
},
|
||||
"name": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Alias of the group.",
|
||||
},
|
||||
"mount_accessor": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Mount accessor to which this alias belongs to.",
|
||||
},
|
||||
"canonical_id": {
|
||||
Type: framework.TypeString,
|
||||
Description: "ID of the group to which this is an alias.",
|
||||
},
|
||||
},
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.UpdateOperation: i.pathGroupAliasIDUpdate,
|
||||
logical.ReadOperation: i.pathGroupAliasIDRead,
|
||||
logical.DeleteOperation: i.pathGroupAliasIDDelete,
|
||||
},
|
||||
|
||||
HelpSynopsis: strings.TrimSpace(groupAliasHelp["group-alias-by-id"][0]),
|
||||
HelpDescription: strings.TrimSpace(groupHelp["group-alias-by-id"][1]),
|
||||
},
|
||||
{
|
||||
Pattern: "group-alias/id/?$",
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.ListOperation: i.pathGroupAliasIDList,
|
||||
},
|
||||
|
||||
HelpSynopsis: strings.TrimSpace(entityHelp["group-alias-id-list"][0]),
|
||||
HelpDescription: strings.TrimSpace(entityHelp["group-alias-id-list"][1]),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (i *IdentityStore) pathGroupAliasRegister(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
_, ok := d.GetOk("id")
|
||||
if ok {
|
||||
return i.pathGroupAliasIDUpdate(req, d)
|
||||
}
|
||||
|
||||
i.groupLock.Lock()
|
||||
defer i.groupLock.Unlock()
|
||||
|
||||
return i.handleGroupAliasUpdateCommon(req, d, nil)
|
||||
}
|
||||
|
||||
func (i *IdentityStore) pathGroupAliasIDUpdate(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
groupAliasID := d.Get("id").(string)
|
||||
if groupAliasID == "" {
|
||||
return logical.ErrorResponse("empty group alias ID"), nil
|
||||
}
|
||||
|
||||
i.groupLock.Lock()
|
||||
defer i.groupLock.Unlock()
|
||||
|
||||
groupAlias, err := i.MemDBAliasByID(groupAliasID, true, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if groupAlias == nil {
|
||||
return logical.ErrorResponse("invalid group alias ID"), nil
|
||||
}
|
||||
|
||||
return i.handleGroupAliasUpdateCommon(req, d, groupAlias)
|
||||
}
|
||||
|
||||
func (i *IdentityStore) handleGroupAliasUpdateCommon(req *logical.Request, d *framework.FieldData, groupAlias *identity.Alias) (*logical.Response, error) {
|
||||
var err error
|
||||
var newGroupAlias bool
|
||||
var group *identity.Group
|
||||
|
||||
if groupAlias == nil {
|
||||
groupAlias = &identity.Alias{}
|
||||
newGroupAlias = true
|
||||
}
|
||||
|
||||
groupID := d.Get("canonical_id").(string)
|
||||
if groupID != "" {
|
||||
group, err = i.MemDBGroupByID(groupID, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if group == nil {
|
||||
return logical.ErrorResponse("invalid group ID"), nil
|
||||
}
|
||||
if group.Type != groupTypeExternal {
|
||||
return logical.ErrorResponse("alias can't be set on an internal group"), nil
|
||||
}
|
||||
}
|
||||
|
||||
// Get group alias name
|
||||
groupAliasName := d.Get("name").(string)
|
||||
if groupAliasName == "" {
|
||||
return logical.ErrorResponse("missing alias name"), nil
|
||||
}
|
||||
|
||||
mountAccessor := d.Get("mount_accessor").(string)
|
||||
if mountAccessor == "" {
|
||||
return logical.ErrorResponse("missing mount_accessor"), nil
|
||||
}
|
||||
|
||||
mountValidationResp := i.validateMountAccessorFunc(mountAccessor)
|
||||
if mountValidationResp == nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf("invalid mount accessor %q", mountAccessor)), nil
|
||||
}
|
||||
|
||||
groupAliasByFactors, err := i.MemDBAliasByFactors(mountValidationResp.MountAccessor, groupAliasName, false, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &logical.Response{}
|
||||
|
||||
if newGroupAlias {
|
||||
if groupAliasByFactors != nil {
|
||||
return logical.ErrorResponse("combination of mount and group alias name is already in use"), nil
|
||||
}
|
||||
|
||||
// If this is an alias being tied to a non-existent group, create
|
||||
// a new group for it.
|
||||
if group == nil {
|
||||
group = &identity.Group{
|
||||
Type: groupTypeExternal,
|
||||
Alias: groupAlias,
|
||||
}
|
||||
} else {
|
||||
group.Alias = groupAlias
|
||||
}
|
||||
} else {
|
||||
// Verify that the combination of group alias name and mount is not
|
||||
// already tied to a different alias
|
||||
if groupAliasByFactors != nil && groupAliasByFactors.ID != groupAlias.ID {
|
||||
return logical.ErrorResponse("combination of mount and group alias name is already in use"), nil
|
||||
}
|
||||
|
||||
// Fetch the group to which the alias is tied to
|
||||
existingGroup, err := i.MemDBGroupByAliasID(groupAlias.ID, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if existingGroup == nil {
|
||||
return nil, fmt.Errorf("group alias is not associated with a group")
|
||||
}
|
||||
|
||||
if group != nil && group.ID != existingGroup.ID {
|
||||
return logical.ErrorResponse("alias is already tied to a different group"), nil
|
||||
}
|
||||
|
||||
group = existingGroup
|
||||
group.Alias = groupAlias
|
||||
}
|
||||
|
||||
group.Alias.Name = groupAliasName
|
||||
group.Alias.MountType = mountValidationResp.MountType
|
||||
group.Alias.MountAccessor = mountValidationResp.MountAccessor
|
||||
|
||||
err = i.sanitizeAndUpsertGroup(group, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp.Data = map[string]interface{}{
|
||||
"id": groupAlias.ID,
|
||||
"canonical_id": group.ID,
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// pathGroupAliasIDRead returns the properties of an alias for a given
|
||||
// alias ID
|
||||
func (i *IdentityStore) pathGroupAliasIDRead(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
groupAliasID := d.Get("id").(string)
|
||||
if groupAliasID == "" {
|
||||
return logical.ErrorResponse("empty group alias id"), nil
|
||||
}
|
||||
|
||||
groupAlias, err := i.MemDBAliasByID(groupAliasID, false, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return i.handleAliasReadCommon(groupAlias)
|
||||
}
|
||||
|
||||
// pathGroupAliasIDDelete deletes the group's alias for a given group alias ID
|
||||
func (i *IdentityStore) pathGroupAliasIDDelete(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
groupAliasID := d.Get("id").(string)
|
||||
if groupAliasID == "" {
|
||||
return logical.ErrorResponse("missing group alias ID"), nil
|
||||
}
|
||||
|
||||
return nil, i.deleteGroupAlias(groupAliasID)
|
||||
}
|
||||
|
||||
// pathGroupAliasIDList lists the IDs of all the valid group aliases in the
|
||||
// identity store
|
||||
func (i *IdentityStore) pathGroupAliasIDList(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
ws := memdb.NewWatchSet()
|
||||
iter, err := i.MemDBAliases(ws, true)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch iterator for group aliases in memdb: %v", err)
|
||||
}
|
||||
|
||||
var groupAliasIDs []string
|
||||
for {
|
||||
raw := iter.Next()
|
||||
if raw == nil {
|
||||
break
|
||||
}
|
||||
groupAliasIDs = append(groupAliasIDs, raw.(*identity.Alias).ID)
|
||||
}
|
||||
|
||||
return logical.ListResponse(groupAliasIDs), nil
|
||||
}
|
||||
|
||||
var groupAliasHelp = map[string][2]string{
|
||||
"group-alias": {
|
||||
"Creates a new group alias, or updates an existing one.",
|
||||
"",
|
||||
},
|
||||
"group-alias-id": {
|
||||
"Update, read or delete a group alias using ID.",
|
||||
"",
|
||||
},
|
||||
"group-alias-id-list": {
|
||||
"List all the entity IDs.",
|
||||
"",
|
||||
},
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/vault/helper/identity"
|
||||
"github.com/hashicorp/vault/logical"
|
||||
)
|
||||
|
||||
func TestIdentityStore_GroupAliases_CRUD(t *testing.T) {
|
||||
var resp *logical.Response
|
||||
var err error
|
||||
i, accessor, _ := testIdentityStoreWithGithubAuth(t)
|
||||
|
||||
groupReq := &logical.Request{
|
||||
Path: "group",
|
||||
Operation: logical.UpdateOperation,
|
||||
Data: map[string]interface{}{
|
||||
"type": "external",
|
||||
},
|
||||
}
|
||||
resp, err = i.HandleRequest(groupReq)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\nerr: %v\n", resp, err)
|
||||
}
|
||||
groupID := resp.Data["id"].(string)
|
||||
|
||||
groupAliasReq := &logical.Request{
|
||||
Path: "group-alias",
|
||||
Operation: logical.UpdateOperation,
|
||||
Data: map[string]interface{}{
|
||||
"name": "testgroupalias",
|
||||
"mount_accessor": accessor,
|
||||
"canonical_id": groupID,
|
||||
"mount_type": "ldap",
|
||||
},
|
||||
}
|
||||
resp, err = i.HandleRequest(groupAliasReq)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\nerr: %v\n", resp, err)
|
||||
}
|
||||
groupAliasID := resp.Data["id"].(string)
|
||||
|
||||
groupAliasReq.Path = "group-alias/id/" + groupAliasID
|
||||
groupAliasReq.Operation = logical.ReadOperation
|
||||
resp, err = i.HandleRequest(groupAliasReq)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\nerr: %v\n", resp, err)
|
||||
}
|
||||
|
||||
if resp.Data["id"].(string) != groupAliasID {
|
||||
t.Fatalf("bad: group alias: %#v\n", resp.Data)
|
||||
}
|
||||
|
||||
groupAliasReq.Operation = logical.DeleteOperation
|
||||
resp, err = i.HandleRequest(groupAliasReq)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\nerr: %v\n", resp, err)
|
||||
}
|
||||
|
||||
groupAliasReq.Operation = logical.ReadOperation
|
||||
resp, err = i.HandleRequest(groupAliasReq)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\nerr: %v\n", resp, err)
|
||||
}
|
||||
|
||||
if resp != nil {
|
||||
t.Fatalf("failed to delete group alias")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIdentityStore_GroupAliases_MemDBIndexes(t *testing.T) {
|
||||
var err error
|
||||
i, accessor, _ := testIdentityStoreWithGithubAuth(t)
|
||||
|
||||
group := &identity.Group{
|
||||
ID: "testgroupid",
|
||||
Name: "testgroupname",
|
||||
Metadata: map[string]string{
|
||||
"testmetadatakey1": "testmetadatavalue1",
|
||||
"testmetadatakey2": "testmetadatavalue2",
|
||||
},
|
||||
Alias: &identity.Alias{
|
||||
ID: "testgroupaliasid",
|
||||
Name: "testalias",
|
||||
MountAccessor: accessor,
|
||||
CanonicalID: "testgroupid",
|
||||
MountType: "ldap",
|
||||
},
|
||||
ParentGroupIDs: []string{"testparentgroupid1", "testparentgroupid2"},
|
||||
MemberEntityIDs: []string{"testentityid1", "testentityid2"},
|
||||
Policies: []string{"testpolicy1", "testpolicy2"},
|
||||
BucketKeyHash: i.groupPacker.BucketKeyHashByItemID("testgroupid"),
|
||||
}
|
||||
|
||||
err = i.MemDBUpsertAlias(group.Alias, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = i.MemDBUpsertGroup(group)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
alias, err := i.MemDBAliasByID("testgroupaliasid", false, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if alias.ID != "testgroupaliasid" {
|
||||
t.Fatalf("bad: group alias: %#v\n", alias)
|
||||
}
|
||||
|
||||
group, err = i.MemDBGroupByAliasID("testgroupaliasid", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if group.ID != "testgroupid" {
|
||||
t.Fatalf("bad: group: %#v\n", group)
|
||||
}
|
||||
|
||||
aliasByFactors, err := i.MemDBAliasByFactors(group.Alias.MountAccessor, group.Alias.Name, false, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if aliasByFactors.ID != "testgroupaliasid" {
|
||||
t.Fatalf("bad: group alias: %#v\n", aliasByFactors)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIdentityStore_GroupAliases_AliasOnInternalGroup(t *testing.T) {
|
||||
var err error
|
||||
var resp *logical.Response
|
||||
|
||||
i, accessor, _ := testIdentityStoreWithGithubAuth(t)
|
||||
|
||||
groupReq := &logical.Request{
|
||||
Path: "group",
|
||||
Operation: logical.UpdateOperation,
|
||||
}
|
||||
resp, err = i.HandleRequest(groupReq)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v; err: %v", resp, err)
|
||||
}
|
||||
groupID := resp.Data["id"].(string)
|
||||
|
||||
aliasReq := &logical.Request{
|
||||
Path: "group-alias",
|
||||
Operation: logical.UpdateOperation,
|
||||
Data: map[string]interface{}{
|
||||
"name": "testname",
|
||||
"mount_accessor": accessor,
|
||||
"canonical_id": groupID,
|
||||
},
|
||||
}
|
||||
resp, err = i.HandleRequest(aliasReq)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !resp.IsError() {
|
||||
t.Fatalf("expected an error")
|
||||
}
|
||||
}
|
|
@ -11,6 +11,11 @@ import (
|
|||
"github.com/hashicorp/vault/logical/framework"
|
||||
)
|
||||
|
||||
const (
|
||||
groupTypeInternal = "internal"
|
||||
groupTypeExternal = "external"
|
||||
)
|
||||
|
||||
func groupPaths(i *IdentityStore) []*framework.Path {
|
||||
return []*framework.Path{
|
||||
{
|
||||
|
@ -20,6 +25,10 @@ func groupPaths(i *IdentityStore) []*framework.Path {
|
|||
Type: framework.TypeString,
|
||||
Description: "ID of the group.",
|
||||
},
|
||||
"type": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Type of the group, 'internal' or 'external'. Defaults to 'internal'",
|
||||
},
|
||||
"name": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Name of the group.",
|
||||
|
@ -55,6 +64,11 @@ func groupPaths(i *IdentityStore) []*framework.Path {
|
|||
Type: framework.TypeString,
|
||||
Description: "ID of the group.",
|
||||
},
|
||||
"type": {
|
||||
Type: framework.TypeString,
|
||||
Default: groupTypeInternal,
|
||||
Description: "Type of the group, 'internal' or 'external'. Defaults to 'internal'",
|
||||
},
|
||||
"name": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Name of the group.",
|
||||
|
@ -118,7 +132,7 @@ func (i *IdentityStore) pathGroupIDUpdate(req *logical.Request, d *framework.Fie
|
|||
i.groupLock.Lock()
|
||||
defer i.groupLock.Unlock()
|
||||
|
||||
group, err := i.memDBGroupByID(groupID, true)
|
||||
group, err := i.MemDBGroupByID(groupID, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -143,11 +157,30 @@ func (i *IdentityStore) handleGroupUpdateCommon(req *logical.Request, d *framewo
|
|||
group.Policies = policiesRaw.([]string)
|
||||
}
|
||||
|
||||
groupTypeRaw, ok := d.GetOk("type")
|
||||
if ok {
|
||||
groupType := groupTypeRaw.(string)
|
||||
if group.Type != "" && groupType != group.Type {
|
||||
return logical.ErrorResponse(fmt.Sprintf("group type cannot be changed")), nil
|
||||
}
|
||||
|
||||
group.Type = groupType
|
||||
}
|
||||
|
||||
// If group type is not set, default to internal type
|
||||
if group.Type == "" {
|
||||
group.Type = groupTypeInternal
|
||||
}
|
||||
|
||||
if group.Type != groupTypeInternal && group.Type != groupTypeExternal {
|
||||
return logical.ErrorResponse(fmt.Sprintf("invalid group type %q", group.Type)), nil
|
||||
}
|
||||
|
||||
// Get the name
|
||||
groupName := d.Get("name").(string)
|
||||
if groupName != "" {
|
||||
// Check if there is a group already existing for the given name
|
||||
groupByName, err := i.memDBGroupByName(groupName, false)
|
||||
groupByName, err := i.MemDBGroupByName(groupName, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -173,6 +206,9 @@ func (i *IdentityStore) handleGroupUpdateCommon(req *logical.Request, d *framewo
|
|||
|
||||
memberEntityIDsRaw, ok := d.GetOk("member_entity_ids")
|
||||
if ok {
|
||||
if group.Type == groupTypeExternal {
|
||||
return logical.ErrorResponse("member entities can't be set manually for external groups"), nil
|
||||
}
|
||||
group.MemberEntityIDs = memberEntityIDsRaw.([]string)
|
||||
if len(group.MemberEntityIDs) > 512 {
|
||||
return logical.ErrorResponse("member entity IDs exceeding the limit of 512"), nil
|
||||
|
@ -182,6 +218,9 @@ func (i *IdentityStore) handleGroupUpdateCommon(req *logical.Request, d *framewo
|
|||
memberGroupIDsRaw, ok := d.GetOk("member_group_ids")
|
||||
var memberGroupIDs []string
|
||||
if ok {
|
||||
if group.Type == groupTypeExternal {
|
||||
return logical.ErrorResponse("member groups can't be set for external groups"), nil
|
||||
}
|
||||
memberGroupIDs = memberGroupIDsRaw.([]string)
|
||||
}
|
||||
|
||||
|
@ -205,20 +244,17 @@ func (i *IdentityStore) pathGroupIDRead(req *logical.Request, d *framework.Field
|
|||
return logical.ErrorResponse("empty group id"), nil
|
||||
}
|
||||
|
||||
group, err := i.memDBGroupByID(groupID, false)
|
||||
group, err := i.MemDBGroupByID(groupID, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if group == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return i.handleGroupReadCommon(group)
|
||||
}
|
||||
|
||||
func (i *IdentityStore) handleGroupReadCommon(group *identity.Group) (*logical.Response, error) {
|
||||
if group == nil {
|
||||
return nil, fmt.Errorf("nil group")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
respData := map[string]interface{}{}
|
||||
|
@ -230,6 +266,23 @@ func (i *IdentityStore) handleGroupReadCommon(group *identity.Group) (*logical.R
|
|||
respData["creation_time"] = ptypes.TimestampString(group.CreationTime)
|
||||
respData["last_update_time"] = ptypes.TimestampString(group.LastUpdateTime)
|
||||
respData["modify_index"] = group.ModifyIndex
|
||||
respData["type"] = group.Type
|
||||
|
||||
aliasMap := map[string]interface{}{}
|
||||
if group.Alias != nil {
|
||||
aliasMap["id"] = group.Alias.ID
|
||||
aliasMap["canonical_id"] = group.Alias.CanonicalID
|
||||
aliasMap["mount_type"] = group.Alias.MountType
|
||||
aliasMap["mount_accessor"] = group.Alias.MountAccessor
|
||||
aliasMap["mount_path"] = group.Alias.MountPath
|
||||
aliasMap["metadata"] = group.Alias.Metadata
|
||||
aliasMap["name"] = group.Alias.Name
|
||||
aliasMap["merged_from_canonical_ids"] = group.Alias.MergedFromCanonicalIDs
|
||||
aliasMap["creation_time"] = ptypes.TimestampString(group.Alias.CreationTime)
|
||||
aliasMap["last_update_time"] = ptypes.TimestampString(group.Alias.LastUpdateTime)
|
||||
}
|
||||
|
||||
respData["alias"] = aliasMap
|
||||
|
||||
memberGroupIDs, err := i.memberGroupIDsByID(group.ID)
|
||||
if err != nil {
|
||||
|
@ -253,7 +306,7 @@ func (i *IdentityStore) pathGroupIDDelete(req *logical.Request, d *framework.Fie
|
|||
// pathGroupIDList lists the IDs of all the groups in the identity store
|
||||
func (i *IdentityStore) pathGroupIDList(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
ws := memdb.NewWatchSet()
|
||||
iter, err := i.memDBGroupIterator(ws)
|
||||
iter, err := i.MemDBGroupIterator(ws)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch iterator for group in memdb: %v", err)
|
||||
}
|
||||
|
|
|
@ -9,6 +9,94 @@ import (
|
|||
"github.com/hashicorp/vault/logical"
|
||||
)
|
||||
|
||||
func TestIdentityStore_Groups_TypeMembershipAdditions(t *testing.T) {
|
||||
var err error
|
||||
var resp *logical.Response
|
||||
|
||||
i, _, _ := testIdentityStoreWithGithubAuth(t)
|
||||
groupReq := &logical.Request{
|
||||
Path: "group",
|
||||
Operation: logical.UpdateOperation,
|
||||
Data: map[string]interface{}{
|
||||
"type": "external",
|
||||
"member_entity_ids": "sampleentityid",
|
||||
},
|
||||
}
|
||||
|
||||
resp, err = i.HandleRequest(groupReq)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !resp.IsError() {
|
||||
t.Fatalf("expected an error")
|
||||
}
|
||||
|
||||
groupReq.Data = map[string]interface{}{
|
||||
"type": "external",
|
||||
"member_group_ids": "samplegroupid",
|
||||
}
|
||||
|
||||
resp, err = i.HandleRequest(groupReq)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !resp.IsError() {
|
||||
t.Fatalf("expected an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIdentityStore_Groups_TypeImmutability(t *testing.T) {
|
||||
var err error
|
||||
var resp *logical.Response
|
||||
|
||||
i, _, _ := testIdentityStoreWithGithubAuth(t)
|
||||
groupReq := &logical.Request{
|
||||
Path: "group",
|
||||
Operation: logical.UpdateOperation,
|
||||
}
|
||||
|
||||
resp, err = i.HandleRequest(groupReq)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v, err: %v", resp, err)
|
||||
}
|
||||
internalGroupID := resp.Data["id"].(string)
|
||||
|
||||
groupReq.Data = map[string]interface{}{
|
||||
"type": "external",
|
||||
}
|
||||
resp, err = i.HandleRequest(groupReq)
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v, err: %v", resp, err)
|
||||
}
|
||||
externalGroupID := resp.Data["id"].(string)
|
||||
|
||||
// Try to mark internal group as external
|
||||
groupReq.Data = map[string]interface{}{
|
||||
"type": "external",
|
||||
}
|
||||
groupReq.Path = "group/id/" + internalGroupID
|
||||
resp, err = i.HandleRequest(groupReq)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !resp.IsError() {
|
||||
t.Fatalf("expected an error")
|
||||
}
|
||||
|
||||
// Try to mark internal group as external
|
||||
groupReq.Data = map[string]interface{}{
|
||||
"type": "internal",
|
||||
}
|
||||
groupReq.Path = "group/id/" + externalGroupID
|
||||
resp, err = i.HandleRequest(groupReq)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !resp.IsError() {
|
||||
t.Fatalf("expected an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIdentityStore_MemDBGroupIndexes(t *testing.T) {
|
||||
var err error
|
||||
i, _, _ := testIdentityStoreWithGithubAuth(t)
|
||||
|
@ -28,7 +116,7 @@ func TestIdentityStore_MemDBGroupIndexes(t *testing.T) {
|
|||
}
|
||||
|
||||
// Insert it into memdb
|
||||
err = i.memDBUpsertGroup(group)
|
||||
err = i.MemDBUpsertGroup(group)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -48,7 +136,7 @@ func TestIdentityStore_MemDBGroupIndexes(t *testing.T) {
|
|||
}
|
||||
|
||||
// Insert it into memdb
|
||||
err = i.memDBUpsertGroup(group)
|
||||
err = i.MemDBUpsertGroup(group)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -56,7 +144,7 @@ func TestIdentityStore_MemDBGroupIndexes(t *testing.T) {
|
|||
var fetchedGroup *identity.Group
|
||||
|
||||
// Fetch group given the name
|
||||
fetchedGroup, err = i.memDBGroupByName("testgroupname", false)
|
||||
fetchedGroup, err = i.MemDBGroupByName("testgroupname", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -65,7 +153,7 @@ func TestIdentityStore_MemDBGroupIndexes(t *testing.T) {
|
|||
}
|
||||
|
||||
// Fetch group given the ID
|
||||
fetchedGroup, err = i.memDBGroupByID("testgroupid", false)
|
||||
fetchedGroup, err = i.MemDBGroupByID("testgroupid", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -75,7 +163,7 @@ func TestIdentityStore_MemDBGroupIndexes(t *testing.T) {
|
|||
|
||||
var fetchedGroups []*identity.Group
|
||||
// Fetch the subgroups of a given group ID
|
||||
fetchedGroups, err = i.memDBGroupsByParentGroupID("testparentgroupid1", false)
|
||||
fetchedGroups, err = i.MemDBGroupsByParentGroupID("testparentgroupid1", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -83,7 +171,7 @@ func TestIdentityStore_MemDBGroupIndexes(t *testing.T) {
|
|||
t.Fatalf("failed to fetch an indexed group")
|
||||
}
|
||||
|
||||
fetchedGroups, err = i.memDBGroupsByParentGroupID("testparentgroupid2", false)
|
||||
fetchedGroups, err = i.MemDBGroupsByParentGroupID("testparentgroupid2", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -92,7 +180,7 @@ func TestIdentityStore_MemDBGroupIndexes(t *testing.T) {
|
|||
}
|
||||
|
||||
// Fetch groups based on policy name
|
||||
fetchedGroups, err = i.memDBGroupsByPolicy("testpolicy1", false)
|
||||
fetchedGroups, err = i.MemDBGroupsByPolicy("testpolicy1", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -100,7 +188,7 @@ func TestIdentityStore_MemDBGroupIndexes(t *testing.T) {
|
|||
t.Fatalf("failed to fetch an indexed group")
|
||||
}
|
||||
|
||||
fetchedGroups, err = i.memDBGroupsByPolicy("testpolicy2", false)
|
||||
fetchedGroups, err = i.MemDBGroupsByPolicy("testpolicy2", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -109,7 +197,7 @@ func TestIdentityStore_MemDBGroupIndexes(t *testing.T) {
|
|||
}
|
||||
|
||||
// Fetch groups based on member entity ID
|
||||
fetchedGroups, err = i.memDBGroupsByMemberEntityID("testentityid1", false)
|
||||
fetchedGroups, err = i.MemDBGroupsByMemberEntityID("testentityid1", false, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -117,7 +205,7 @@ func TestIdentityStore_MemDBGroupIndexes(t *testing.T) {
|
|||
t.Fatalf("failed to fetch an indexed group")
|
||||
}
|
||||
|
||||
fetchedGroups, err = i.memDBGroupsByMemberEntityID("testentityid2", false)
|
||||
fetchedGroups, err = i.MemDBGroupsByMemberEntityID("testentityid2", false, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -203,12 +291,14 @@ func TestIdentityStore_GroupsCreateUpdate(t *testing.T) {
|
|||
},
|
||||
}
|
||||
expectedData["id"] = resp.Data["id"]
|
||||
expectedData["type"] = resp.Data["type"]
|
||||
expectedData["name"] = resp.Data["name"]
|
||||
expectedData["member_group_ids"] = resp.Data["member_group_ids"]
|
||||
expectedData["member_entity_ids"] = resp.Data["member_entity_ids"]
|
||||
expectedData["creation_time"] = resp.Data["creation_time"]
|
||||
expectedData["last_update_time"] = resp.Data["last_update_time"]
|
||||
expectedData["modify_index"] = resp.Data["modify_index"]
|
||||
expectedData["alias"] = resp.Data["alias"]
|
||||
|
||||
if !reflect.DeepEqual(expectedData, resp.Data) {
|
||||
t.Fatalf("bad: group data;\nexpected: %#v\n actual: %#v\n", expectedData, resp.Data)
|
||||
|
@ -321,12 +411,14 @@ func TestIdentityStore_GroupsCRUD_ByID(t *testing.T) {
|
|||
},
|
||||
}
|
||||
expectedData["id"] = resp.Data["id"]
|
||||
expectedData["type"] = resp.Data["type"]
|
||||
expectedData["name"] = resp.Data["name"]
|
||||
expectedData["member_group_ids"] = resp.Data["member_group_ids"]
|
||||
expectedData["member_entity_ids"] = resp.Data["member_entity_ids"]
|
||||
expectedData["creation_time"] = resp.Data["creation_time"]
|
||||
expectedData["last_update_time"] = resp.Data["last_update_time"]
|
||||
expectedData["modify_index"] = resp.Data["modify_index"]
|
||||
expectedData["alias"] = resp.Data["alias"]
|
||||
|
||||
if !reflect.DeepEqual(expectedData, resp.Data) {
|
||||
t.Fatalf("bad: group data;\nexpected: %#v\n actual: %#v\n", expectedData, resp.Data)
|
||||
|
@ -496,7 +588,7 @@ func TestIdentityStore_GroupHierarchyCases(t *testing.T) {
|
|||
|
||||
var memberGroupIDs []string
|
||||
// Fetch 'eng' group
|
||||
engGroup, err := is.memDBGroupByID(engGroupID, false)
|
||||
engGroup, err := is.MemDBGroupByID(engGroupID, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -510,7 +602,7 @@ func TestIdentityStore_GroupHierarchyCases(t *testing.T) {
|
|||
t.Fatalf("bad: group membership IDs; expected: %#v\n actual: %#v\n", engMemberGroupIDs, memberGroupIDs)
|
||||
}
|
||||
|
||||
vaultGroup, err := is.memDBGroupByID(vaultGroupID, false)
|
||||
vaultGroup, err := is.MemDBGroupByID(vaultGroupID, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -524,7 +616,7 @@ func TestIdentityStore_GroupHierarchyCases(t *testing.T) {
|
|||
t.Fatalf("bad: group membership IDs; expected: %#v\n actual: %#v\n", vaultMemberGroupIDs, memberGroupIDs)
|
||||
}
|
||||
|
||||
opsGroup, err := is.memDBGroupByID(opsGroupID, false)
|
||||
opsGroup, err := is.MemDBGroupByID(opsGroupID, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -6,15 +6,23 @@ import (
|
|||
memdb "github.com/hashicorp/go-memdb"
|
||||
)
|
||||
|
||||
const (
|
||||
entitiesTable = "entities"
|
||||
entityAliasesTable = "entity_aliases"
|
||||
groupsTable = "groups"
|
||||
groupAliasesTable = "group_aliases"
|
||||
)
|
||||
|
||||
func identityStoreSchema() *memdb.DBSchema {
|
||||
iStoreSchema := &memdb.DBSchema{
|
||||
Tables: make(map[string]*memdb.TableSchema),
|
||||
}
|
||||
|
||||
schemas := []func() *memdb.TableSchema{
|
||||
entityTableSchema,
|
||||
entitiesTableSchema,
|
||||
aliasesTableSchema,
|
||||
groupTableSchema,
|
||||
groupsTableSchema,
|
||||
groupAliasesTableSchema,
|
||||
}
|
||||
|
||||
for _, schemaFunc := range schemas {
|
||||
|
@ -30,7 +38,7 @@ func identityStoreSchema() *memdb.DBSchema {
|
|||
|
||||
func aliasesTableSchema() *memdb.TableSchema {
|
||||
return &memdb.TableSchema{
|
||||
Name: "aliases",
|
||||
Name: entityAliasesTable,
|
||||
Indexes: map[string]*memdb.IndexSchema{
|
||||
"id": &memdb.IndexSchema{
|
||||
Name: "id",
|
||||
|
@ -39,11 +47,11 @@ func aliasesTableSchema() *memdb.TableSchema {
|
|||
Field: "ID",
|
||||
},
|
||||
},
|
||||
"entity_id": &memdb.IndexSchema{
|
||||
Name: "entity_id",
|
||||
"canonical_id": &memdb.IndexSchema{
|
||||
Name: "canonical_id",
|
||||
Unique: false,
|
||||
Indexer: &memdb.StringFieldIndex{
|
||||
Field: "EntityID",
|
||||
Field: "CanonicalID",
|
||||
},
|
||||
},
|
||||
"mount_type": &memdb.IndexSchema{
|
||||
|
@ -79,9 +87,9 @@ func aliasesTableSchema() *memdb.TableSchema {
|
|||
}
|
||||
}
|
||||
|
||||
func entityTableSchema() *memdb.TableSchema {
|
||||
func entitiesTableSchema() *memdb.TableSchema {
|
||||
return &memdb.TableSchema{
|
||||
Name: "entities",
|
||||
Name: entitiesTable,
|
||||
Indexes: map[string]*memdb.IndexSchema{
|
||||
"id": &memdb.IndexSchema{
|
||||
Name: "id",
|
||||
|
@ -125,9 +133,9 @@ func entityTableSchema() *memdb.TableSchema {
|
|||
}
|
||||
}
|
||||
|
||||
func groupTableSchema() *memdb.TableSchema {
|
||||
func groupsTableSchema() *memdb.TableSchema {
|
||||
return &memdb.TableSchema{
|
||||
Name: "groups",
|
||||
Name: groupsTable,
|
||||
Indexes: map[string]*memdb.IndexSchema{
|
||||
"id": {
|
||||
Name: "id",
|
||||
|
@ -178,3 +186,46 @@ func groupTableSchema() *memdb.TableSchema {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
func groupAliasesTableSchema() *memdb.TableSchema {
|
||||
return &memdb.TableSchema{
|
||||
Name: groupAliasesTable,
|
||||
Indexes: map[string]*memdb.IndexSchema{
|
||||
"id": &memdb.IndexSchema{
|
||||
Name: "id",
|
||||
Unique: true,
|
||||
Indexer: &memdb.StringFieldIndex{
|
||||
Field: "ID",
|
||||
},
|
||||
},
|
||||
"canonical_id": &memdb.IndexSchema{
|
||||
Name: "canonical_id",
|
||||
Unique: false,
|
||||
Indexer: &memdb.StringFieldIndex{
|
||||
Field: "CanonicalID",
|
||||
},
|
||||
},
|
||||
"mount_type": &memdb.IndexSchema{
|
||||
Name: "mount_type",
|
||||
Unique: false,
|
||||
Indexer: &memdb.StringFieldIndex{
|
||||
Field: "MountType",
|
||||
},
|
||||
},
|
||||
"factors": &memdb.IndexSchema{
|
||||
Name: "factors",
|
||||
Unique: true,
|
||||
Indexer: &memdb.CompoundIndex{
|
||||
Indexes: []memdb.Indexer{
|
||||
&memdb.StringFieldIndex{
|
||||
Field: "MountAccessor",
|
||||
},
|
||||
&memdb.StringFieldIndex{
|
||||
Field: "Name",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"sync"
|
||||
|
||||
memdb "github.com/hashicorp/go-memdb"
|
||||
"github.com/hashicorp/vault/helper/identity"
|
||||
"github.com/hashicorp/vault/helper/locksutil"
|
||||
"github.com/hashicorp/vault/helper/storagepacker"
|
||||
"github.com/hashicorp/vault/logical"
|
||||
|
@ -73,3 +74,9 @@ type IdentityStore struct {
|
|||
// buckets
|
||||
groupPacker *storagepacker.StoragePacker
|
||||
}
|
||||
|
||||
type groupDiff struct {
|
||||
New []*identity.Group
|
||||
Deleted []*identity.Group
|
||||
Unmodified []*identity.Group
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ func TestIdentityStore_EntityByAliasFactors(t *testing.T) {
|
|||
t.Fatalf("expected a non-nil response")
|
||||
}
|
||||
|
||||
entity, err := is.EntityByAliasFactors(ghAccessor, "alias_name", false)
|
||||
entity, err := is.entityByAliasFactors(ghAccessor, "alias_name", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -284,6 +284,21 @@ func (c *Core) handleRequest(req *logical.Request) (retResp *logical.Response, r
|
|||
}
|
||||
}
|
||||
|
||||
// If the request was to renew a token, and if there are group aliases set
|
||||
// in the auth object, then the group memberships should be refreshed
|
||||
if strings.HasPrefix(req.Path, "auth/token/renew") &&
|
||||
resp != nil &&
|
||||
resp.Auth != nil &&
|
||||
resp.Auth.EntityID != "" &&
|
||||
resp.Auth.GroupAliases != nil {
|
||||
err := c.identityStore.refreshExternalGroupMembershipsByEntityID(resp.Auth.EntityID, resp.Auth.GroupAliases)
|
||||
if err != nil {
|
||||
c.logger.Error("core: failed to refresh external group memberships", "error", err)
|
||||
retErr = multierror.Append(retErr, ErrInternalError)
|
||||
return nil, auth, retErr
|
||||
}
|
||||
}
|
||||
|
||||
// Only the token store is allowed to return an auth block, for any
|
||||
// other request this is an internal error. We exclude renewal of a token,
|
||||
// since it does not need to be re-registered
|
||||
|
@ -322,6 +337,7 @@ func (c *Core) handleRequest(req *logical.Request) (retResp *logical.Response, r
|
|||
if routeErr != nil {
|
||||
retErr = multierror.Append(retErr, routeErr)
|
||||
}
|
||||
|
||||
return resp, auth, retErr
|
||||
}
|
||||
|
||||
|
@ -412,7 +428,7 @@ func (c *Core) handleLoginRequest(req *logical.Request) (retResp *logical.Respon
|
|||
var err error
|
||||
|
||||
// Check if an entity already exists for the given alias
|
||||
entity, err = c.identityStore.EntityByAliasFactors(auth.Alias.MountAccessor, auth.Alias.Name, false)
|
||||
entity, err = c.identityStore.entityByAliasFactors(auth.Alias.MountAccessor, auth.Alias.Name, false)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -430,6 +446,12 @@ func (c *Core) handleLoginRequest(req *logical.Request) (retResp *logical.Respon
|
|||
}
|
||||
|
||||
auth.EntityID = entity.ID
|
||||
if auth.GroupAliases != nil {
|
||||
err = c.identityStore.refreshExternalGroupMembershipsByEntityID(auth.EntityID, auth.GroupAliases)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if strutil.StrListSubset(auth.Policies, []string{"root"}) {
|
||||
|
|
|
@ -471,10 +471,26 @@ func (r *Router) routeCommon(req *logical.Request, existenceCheck bool) (*logica
|
|||
return nil, ok, exists, err
|
||||
} else {
|
||||
resp, err := re.backend.HandleRequest(req)
|
||||
// When a token gets renewed, the request hits this path and reaches
|
||||
// token store. Token store delegates the renewal to the expiration
|
||||
// manager. Expiration manager in-turn creates a different logical
|
||||
// request and forwards the request to the auth backend that had
|
||||
// initially authenticated the login request. The forwarding to auth
|
||||
// backend will make this code path hit for the second time for the
|
||||
// same renewal request. The accessors in the Alias structs should be
|
||||
// of the auth backend and not of the token store. Therefore, avoiding
|
||||
// the overwriting of accessors by having a check for path prefix
|
||||
// having "renew". This gets applied for "renew" and "renew-self"
|
||||
// requests.
|
||||
if resp != nil &&
|
||||
resp.Auth != nil &&
|
||||
resp.Auth.Alias != nil {
|
||||
resp.Auth.Alias.MountAccessor = re.mountEntry.Accessor
|
||||
!strings.HasPrefix(req.Path, "renew") {
|
||||
if resp.Auth.Alias != nil {
|
||||
resp.Auth.Alias.MountAccessor = re.mountEntry.Accessor
|
||||
}
|
||||
for _, alias := range resp.Auth.GroupAliases {
|
||||
alias.MountAccessor = re.mountEntry.Accessor
|
||||
}
|
||||
}
|
||||
return resp, false, false, err
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue