2017-11-02 20:05:48 +00:00
|
|
|
package vault
|
|
|
|
|
|
|
|
import (
|
2018-01-08 18:31:38 +00:00
|
|
|
"context"
|
2017-11-02 20:05:48 +00:00
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/hashicorp/vault/helper/identity"
|
2018-09-18 03:03:00 +00:00
|
|
|
"github.com/hashicorp/vault/helper/namespace"
|
2019-04-12 21:54:35 +00:00
|
|
|
"github.com/hashicorp/vault/sdk/framework"
|
2019-04-13 07:44:06 +00:00
|
|
|
"github.com/hashicorp/vault/sdk/logical"
|
2017-11-02 20:05:48 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
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{
|
2018-01-08 18:31:38 +00:00
|
|
|
logical.UpdateOperation: i.pathGroupAliasRegister(),
|
2017-11-02 20:05:48 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
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{
|
2018-04-02 14:42:01 +00:00
|
|
|
logical.UpdateOperation: i.pathGroupAliasIDUpdate(),
|
2018-01-08 18:31:38 +00:00
|
|
|
logical.ReadOperation: i.pathGroupAliasIDRead(),
|
|
|
|
logical.DeleteOperation: i.pathGroupAliasIDDelete(),
|
2017-11-02 20:05:48 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
HelpSynopsis: strings.TrimSpace(groupAliasHelp["group-alias-by-id"][0]),
|
2018-04-17 17:54:04 +00:00
|
|
|
HelpDescription: strings.TrimSpace(groupAliasHelp["group-alias-by-id"][1]),
|
2017-11-02 20:05:48 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Pattern: "group-alias/id/?$",
|
|
|
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
2018-01-08 18:31:38 +00:00
|
|
|
logical.ListOperation: i.pathGroupAliasIDList(),
|
2017-11-02 20:05:48 +00:00
|
|
|
},
|
|
|
|
|
2018-04-17 17:54:04 +00:00
|
|
|
HelpSynopsis: strings.TrimSpace(groupAliasHelp["group-alias-id-list"][0]),
|
|
|
|
HelpDescription: strings.TrimSpace(groupAliasHelp["group-alias-id-list"][1]),
|
2017-11-02 20:05:48 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-08 18:31:38 +00:00
|
|
|
func (i *IdentityStore) pathGroupAliasRegister() framework.OperationFunc {
|
|
|
|
return func(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
|
|
|
_, ok := d.GetOk("id")
|
|
|
|
if ok {
|
|
|
|
return i.pathGroupAliasIDUpdate()(ctx, req, d)
|
|
|
|
}
|
2017-11-02 20:05:48 +00:00
|
|
|
|
2018-01-08 18:31:38 +00:00
|
|
|
i.groupLock.Lock()
|
|
|
|
defer i.groupLock.Unlock()
|
2017-11-02 20:05:48 +00:00
|
|
|
|
2018-09-18 03:03:00 +00:00
|
|
|
return i.handleGroupAliasUpdateCommon(ctx, req, d, nil)
|
2018-01-08 18:31:38 +00:00
|
|
|
}
|
2017-11-02 20:05:48 +00:00
|
|
|
}
|
|
|
|
|
2018-01-08 18:31:38 +00:00
|
|
|
func (i *IdentityStore) pathGroupAliasIDUpdate() framework.OperationFunc {
|
|
|
|
return func(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
|
|
|
groupAliasID := d.Get("id").(string)
|
|
|
|
if groupAliasID == "" {
|
|
|
|
return logical.ErrorResponse("empty group alias ID"), nil
|
|
|
|
}
|
2017-11-02 20:05:48 +00:00
|
|
|
|
2018-01-08 18:31:38 +00:00
|
|
|
i.groupLock.Lock()
|
|
|
|
defer i.groupLock.Unlock()
|
2017-11-02 20:05:48 +00:00
|
|
|
|
2018-01-08 18:31:38 +00:00
|
|
|
groupAlias, err := i.MemDBAliasByID(groupAliasID, true, true)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if groupAlias == nil {
|
|
|
|
return logical.ErrorResponse("invalid group alias ID"), nil
|
|
|
|
}
|
2017-11-02 20:05:48 +00:00
|
|
|
|
2018-09-18 03:03:00 +00:00
|
|
|
return i.handleGroupAliasUpdateCommon(ctx, req, d, groupAlias)
|
2018-01-08 18:31:38 +00:00
|
|
|
}
|
2017-11-02 20:05:48 +00:00
|
|
|
}
|
|
|
|
|
2018-09-18 03:03:00 +00:00
|
|
|
func (i *IdentityStore) handleGroupAliasUpdateCommon(ctx context.Context, req *logical.Request, d *framework.FieldData, groupAlias *identity.Alias) (*logical.Response, error) {
|
2017-11-02 20:05:48 +00:00
|
|
|
var newGroupAlias bool
|
|
|
|
var group *identity.Group
|
2018-09-18 03:03:00 +00:00
|
|
|
var err error
|
2017-11-02 20:05:48 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2019-06-14 16:53:00 +00:00
|
|
|
mountEntry := i.core.router.MatchingMountByAccessor(mountAccessor)
|
|
|
|
if mountEntry == nil {
|
2017-11-02 20:05:48 +00:00
|
|
|
return logical.ErrorResponse(fmt.Sprintf("invalid mount accessor %q", mountAccessor)), nil
|
|
|
|
}
|
|
|
|
|
2019-06-14 16:53:00 +00:00
|
|
|
if mountEntry.Local {
|
2018-04-23 17:46:14 +00:00
|
|
|
return logical.ErrorResponse(fmt.Sprintf("mount_accessor %q is of a local mount", mountAccessor)), nil
|
|
|
|
}
|
|
|
|
|
2019-06-14 16:53:00 +00:00
|
|
|
groupAliasByFactors, err := i.MemDBAliasByFactors(mountEntry.Accessor, groupAliasName, false, true)
|
2017-11-02 20:05:48 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2019-06-14 16:53:00 +00:00
|
|
|
// Check if the group we found belongs to a different namespace than the mount accessor
|
|
|
|
if group.NamespaceID != mountEntry.NamespaceID {
|
|
|
|
return logical.ErrorResponse("mount accessor and group are located in different namespaces"), nil
|
|
|
|
}
|
|
|
|
|
2017-11-02 20:05:48 +00:00
|
|
|
group.Alias.Name = groupAliasName
|
2019-06-14 16:53:00 +00:00
|
|
|
group.Alias.MountAccessor = mountEntry.Accessor
|
2018-05-25 18:34:24 +00:00
|
|
|
// Explicitly correct for previous versions that persisted this
|
|
|
|
group.Alias.MountType = ""
|
2017-11-02 20:05:48 +00:00
|
|
|
|
2018-09-18 03:03:00 +00:00
|
|
|
err = i.sanitizeAndUpsertGroup(ctx, group, nil)
|
2017-11-02 20:05:48 +00:00
|
|
|
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
|
2018-01-08 18:31:38 +00:00
|
|
|
func (i *IdentityStore) pathGroupAliasIDRead() framework.OperationFunc {
|
|
|
|
return func(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
|
|
|
groupAliasID := d.Get("id").(string)
|
|
|
|
if groupAliasID == "" {
|
|
|
|
return logical.ErrorResponse("empty group alias id"), nil
|
|
|
|
}
|
2017-11-02 20:05:48 +00:00
|
|
|
|
2018-01-08 18:31:38 +00:00
|
|
|
groupAlias, err := i.MemDBAliasByID(groupAliasID, false, true)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-11-02 20:05:48 +00:00
|
|
|
|
2018-09-18 03:03:00 +00:00
|
|
|
return i.handleAliasReadCommon(ctx, groupAlias)
|
2018-01-08 18:31:38 +00:00
|
|
|
}
|
2017-11-02 20:05:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// pathGroupAliasIDDelete deletes the group's alias for a given group alias ID
|
2018-01-08 18:31:38 +00:00
|
|
|
func (i *IdentityStore) pathGroupAliasIDDelete() framework.OperationFunc {
|
|
|
|
return func(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
|
|
|
groupAliasID := d.Get("id").(string)
|
|
|
|
if groupAliasID == "" {
|
|
|
|
return logical.ErrorResponse("missing group alias ID"), nil
|
|
|
|
}
|
2017-11-02 20:05:48 +00:00
|
|
|
|
2018-06-24 11:45:53 +00:00
|
|
|
i.groupLock.Lock()
|
|
|
|
defer i.groupLock.Unlock()
|
|
|
|
|
|
|
|
txn := i.db.Txn(true)
|
|
|
|
defer txn.Abort()
|
|
|
|
|
|
|
|
alias, err := i.MemDBAliasByIDInTxn(txn, groupAliasID, false, true)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if alias == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2018-09-18 03:03:00 +00:00
|
|
|
ns, err := namespace.FromContext(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if ns.ID != alias.NamespaceID {
|
|
|
|
return nil, logical.ErrUnsupportedOperation
|
|
|
|
}
|
|
|
|
|
2018-06-24 11:45:53 +00:00
|
|
|
group, err := i.MemDBGroupByAliasIDInTxn(txn, alias.ID, true)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there is no group tied to a valid alias, something is wrong
|
|
|
|
if group == nil {
|
|
|
|
return nil, fmt.Errorf("alias not associated to a group")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete group alias in memdb
|
|
|
|
err = i.MemDBDeleteAliasByIDInTxn(txn, group.Alias.ID, true)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete the alias
|
|
|
|
group.Alias = nil
|
|
|
|
|
2019-05-01 17:47:41 +00:00
|
|
|
err = i.UpsertGroupInTxn(ctx, txn, group, true)
|
2018-06-24 11:45:53 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
txn.Commit()
|
|
|
|
|
|
|
|
return nil, nil
|
2018-01-08 18:31:38 +00:00
|
|
|
}
|
2017-11-02 20:05:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// pathGroupAliasIDList lists the IDs of all the valid group aliases in the
|
|
|
|
// identity store
|
2018-01-08 18:31:38 +00:00
|
|
|
func (i *IdentityStore) pathGroupAliasIDList() framework.OperationFunc {
|
|
|
|
return func(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
2018-09-18 03:03:00 +00:00
|
|
|
return i.handleAliasListCommon(ctx, true)
|
2018-01-08 18:31:38 +00:00
|
|
|
}
|
2017-11-02 20:05:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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": {
|
2018-04-17 17:54:04 +00:00
|
|
|
"List all the group alias IDs.",
|
2017-11-02 20:05:48 +00:00
|
|
|
"",
|
|
|
|
},
|
|
|
|
}
|