open-consul/agent/consul/auth/login.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
}