* Move the server daemons into their own package With this change the internal/server package now is dedicated to the server domain (workers and controllers) and not to the server daemons. The hope is that this will make it easier to keep the daemons decoupled from the representation of the servers in the database.
195 lines
5.9 KiB
Go
195 lines
5.9 KiB
Go
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
|
|
}
|