78 lines
2.5 KiB
Go
78 lines
2.5 KiB
Go
package auth
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/consul/acl"
|
|
"github.com/hashicorp/consul/agent/consul/authmethod"
|
|
"github.com/hashicorp/consul/agent/consul/state"
|
|
"github.com/hashicorp/consul/agent/structs"
|
|
)
|
|
|
|
// Login wraps the process of creating an ACLToken from the identity verified
|
|
// by an auth method.
|
|
type Login struct {
|
|
binder *Binder
|
|
writer *TokenWriter
|
|
}
|
|
|
|
// NewLogin returns a new Login with the given binder and writer.
|
|
func NewLogin(binder *Binder, writer *TokenWriter) *Login {
|
|
return &Login{binder, writer}
|
|
}
|
|
|
|
// TokenForVerifiedIdentity creates an ACLToken for the given identity verified
|
|
// by an auth method.
|
|
func (l *Login) TokenForVerifiedIdentity(identity *authmethod.Identity, authMethod *structs.ACLAuthMethod, description string) (*structs.ACLToken, error) {
|
|
bindings, err := l.binder.Bind(authMethod, identity)
|
|
switch {
|
|
case err != nil:
|
|
return nil, err
|
|
case bindings.None():
|
|
// We try to prevent the creation of a useless token without taking a trip
|
|
// through Raft and the state store if we can.
|
|
return nil, acl.ErrPermissionDenied
|
|
}
|
|
|
|
token := &structs.ACLToken{
|
|
Description: description,
|
|
Local: authMethod.TokenLocality != "global", // TokenWriter prevents the creation of global tokens in secondary datacenters.
|
|
AuthMethod: authMethod.Name,
|
|
ExpirationTTL: authMethod.MaxTokenTTL,
|
|
ServiceIdentities: bindings.ServiceIdentities,
|
|
NodeIdentities: bindings.NodeIdentities,
|
|
Roles: bindings.Roles,
|
|
EnterpriseMeta: bindings.EnterpriseMeta,
|
|
}
|
|
token.ACLAuthMethodEnterpriseMeta.FillWithEnterpriseMeta(&authMethod.EnterpriseMeta)
|
|
|
|
updated, err := l.writer.Create(token, true)
|
|
switch {
|
|
case err != nil && strings.Contains(err.Error(), state.ErrTokenHasNoPrivileges.Error()):
|
|
// If we were in a slight race with a role delete operation then we may
|
|
// still end up failing to insert an unprivileged token in the state
|
|
// machine instead. Return the same error as earlier so it doesn't
|
|
// actually matter which one prevents the insertion.
|
|
return nil, acl.ErrPermissionDenied
|
|
case err != nil:
|
|
return nil, err
|
|
}
|
|
return updated, nil
|
|
}
|
|
|
|
// BuildTokenDescription builds a description for an ACLToken by encoding the
|
|
// given meta as JSON and applying the prefix.
|
|
func BuildTokenDescription(prefix string, meta map[string]string) (string, error) {
|
|
if len(meta) == 0 {
|
|
return prefix, nil
|
|
}
|
|
|
|
d, err := json.Marshal(meta)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return fmt.Sprintf("%s: %s", prefix, d), nil
|
|
}
|