You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
boundary/internal/daemon/controller/handlers/authmethods/password.go

195 lines
5.9 KiB

package authmethods
import (
"context"
"fmt"
"strings"
"github.com/hashicorp/boundary/internal/auth/password"
pwstore "github.com/hashicorp/boundary/internal/auth/password/store"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
"github.com/hashicorp/boundary/internal/errors"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/types/action"
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/authmethods"
pba "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/authtokens"
"google.golang.org/grpc/codes"
)
const (
// password field names
loginNameField = "login_name"
passwordField = "password"
loginCommand = "login"
)
var pwMaskManager handlers.MaskManager
func init() {
var err error
if pwMaskManager, err = handlers.NewMaskManager(handlers.MaskDestination{&pwstore.AuthMethod{}}, handlers.MaskSource{&pb.AuthMethod{}, &pb.PasswordAuthMethodAttributes{}}); err != nil {
panic(err)
}
IdActions[password.Subtype] = action.ActionSet{
action.NoOp,
action.Read,
action.Update,
action.Delete,
action.Authenticate,
}
}
// createPwInRepo creates a password auth method in a repo and returns the result.
// This method should never return a nil AuthMethod without returning an error.
func (s Service) createPwInRepo(ctx context.Context, scopeId string, item *pb.AuthMethod) (*password.AuthMethod, error) {
u, err := toStoragePwAuthMethod(scopeId, item)
if err != nil {
return nil, err
}
repo, err := s.pwRepoFn()
if err != nil {
return nil, err
}
out, err := repo.CreateAuthMethod(ctx, u)
if err != nil {
return nil, fmt.Errorf("unable to create auth method: %w", err)
}
return out, err
}
func (s Service) updatePwInRepo(ctx context.Context, scopeId, id string, mask []string, item *pb.AuthMethod) (*password.AuthMethod, error) {
u, err := toStoragePwAuthMethod(scopeId, item)
if err != nil {
return nil, err
}
version := item.GetVersion()
u.PublicId = id
dbMask := pwMaskManager.Translate(mask)
if len(dbMask) == 0 {
return nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."})
}
repo, err := s.pwRepoFn()
if err != nil {
return nil, err
}
out, rowsUpdated, err := repo.UpdateAuthMethod(ctx, u, version, dbMask)
if err != nil {
return nil, fmt.Errorf("unable to update auth method: %w", err)
}
if rowsUpdated == 0 {
return nil, handlers.NotFoundErrorf("AuthMethod %q doesn't exist or incorrect version provided.", id)
}
return out, nil
}
func (s Service) authenticatePassword(ctx context.Context, req *pbs.AuthenticateRequest, authResults *auth.VerifyResults) (*pbs.AuthenticateResponse, error) {
reqAttrs := req.GetPasswordLoginAttributes()
tok, err := s.authenticateWithPwRepo(ctx, authResults.Scope.GetId(), req.GetAuthMethodId(), reqAttrs.LoginName, reqAttrs.Password)
if err != nil {
return nil, err
}
return s.convertToAuthenticateResponse(ctx, req, authResults, tok)
}
func (s Service) authenticateWithPwRepo(ctx context.Context, scopeId, authMethodId, loginName, pw string) (*pba.AuthToken, error) {
iamRepo, err := s.iamRepoFn()
if err != nil {
return nil, err
}
atRepo, err := s.atRepoFn()
if err != nil {
return nil, err
}
pwRepo, err := s.pwRepoFn()
if err != nil {
return nil, err
}
acct, err := pwRepo.Authenticate(ctx, scopeId, authMethodId, loginName, pw)
if err != nil {
return nil, err
}
if acct == nil {
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Unauthenticated, "Unable to authenticate.")
}
u, err := iamRepo.LookupUserWithLogin(ctx, acct.GetPublicId())
if err != nil {
return nil, err
}
tok, err := atRepo.CreateAuthToken(ctx, u, acct.GetPublicId())
if err != nil {
return nil, err
}
return s.ConvertInternalAuthTokenToApiAuthToken(
ctx,
tok,
)
}
func validateAuthenticatePasswordRequest(req *pbs.AuthenticateRequest) error {
badFields := make(map[string]string)
attrs := req.GetPasswordLoginAttributes()
if attrs.LoginName == "" {
badFields["attributes.login_name"] = "This is a required field."
}
if attrs.Password == "" {
badFields["attributes.password"] = "This is a required field."
}
if req.GetCommand() == "" {
// TODO: Eventually, require a command. For now, fall back to "login" for backwards compat.
req.Command = loginCommand
}
if req.Command != loginCommand {
badFields[commandField] = "Invalid command for this auth method type."
}
tokenType := req.GetType()
if tokenType == "" {
// Fall back to deprecated field if type is not set
tokenType = req.GetTokenType()
}
tType := strings.ToLower(strings.TrimSpace(tokenType))
if tType != "" && tType != "token" && tType != "cookie" {
badFields[tokenTypeField] = `The only accepted types are "token" and "cookie".`
}
if len(badFields) > 0 {
return handlers.InvalidArgumentErrorf("Invalid fields provided in request.", badFields)
}
return nil
}
func toStoragePwAuthMethod(scopeId string, item *pb.AuthMethod) (*password.AuthMethod, error) {
const op = "authmethod_service.toStoragePwAuthMethod"
if item == nil {
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "nil auth method.")
}
var opts []password.Option
if item.GetName() != nil {
opts = append(opts, password.WithName(item.GetName().GetValue()))
}
if item.GetDescription() != nil {
opts = append(opts, password.WithDescription(item.GetDescription().GetValue()))
}
u, err := password.NewAuthMethod(scopeId, opts...)
if err != nil {
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to build auth method for creation: %v.", err)
}
pwAttrs := item.GetPasswordAuthMethodAttributes()
if pwAttrs.GetMinLoginNameLength() != 0 {
u.MinLoginNameLength = pwAttrs.GetMinLoginNameLength()
}
if pwAttrs.GetMinPasswordLength() != 0 {
u.MinPasswordLength = pwAttrs.GetMinPasswordLength()
}
return u, nil
}