2021-04-08 20:54:32 -04:00
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
2023-02-02 17:15:17 -08:00
2021-04-08 20:54:32 -04:00
package authmethods
import (
"context"
"fmt"
"strings"
"github.com/hashicorp/boundary/internal/auth/password"
pwstore "github.com/hashicorp/boundary/internal/auth/password/store"
2022-05-09 12:45:10 -07:00
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
2021-04-08 20:54:32 -04:00
"github.com/hashicorp/boundary/internal/errors"
2023-10-30 18:23:23 -04:00
"github.com/hashicorp/boundary/internal/event"
2021-04-08 20:54:32 -04:00
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/types/action"
2021-08-26 10:44:41 -04:00
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/authmethods"
pba "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/authtokens"
2021-04-08 20:54:32 -04:00
"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
2023-06-06 17:09:08 -04:00
if pwMaskManager , err = handlers . NewMaskManager ( context . Background (), handlers . MaskDestination { & pwstore . AuthMethod {}}, handlers . MaskSource { & pb . AuthMethod {}, & pb . PasswordAuthMethodAttributes {}}); err != nil {
2021-04-08 20:54:32 -04:00
panic ( err )
}
2023-10-31 13:44:17 +00:00
IdActions [ password . Subtype ] = action . NewActionSet (
2021-04-23 09:13:37 -05:00
action . NoOp ,
2021-04-08 20:54:32 -04:00
action . Read ,
action . Update ,
action . Delete ,
action . Authenticate ,
2023-10-31 13:44:17 +00:00
)
2021-04-08 20:54:32 -04:00
}
// 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 ) {
2023-06-06 17:09:08 -04:00
u , err := toStoragePwAuthMethod ( ctx , scopeId , item )
2021-04-08 20:54:32 -04:00
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 ) {
2023-06-06 17:09:08 -04:00
u , err := toStoragePwAuthMethod ( ctx , scopeId , item )
2021-04-08 20:54:32 -04:00
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 ) {
2022-04-11 14:50:29 -04:00
reqAttrs := req . GetPasswordLoginAttributes ()
tok , err := s . authenticateWithPwRepo ( ctx , authResults . Scope . GetId (), req . GetAuthMethodId (), reqAttrs . LoginName , reqAttrs . Password )
2021-04-08 20:54:32 -04:00
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 ) {
2023-10-30 18:23:23 -04:00
const op = "authmethods.(Service).authenticateWithPwRepo"
2021-04-08 20:54:32 -04:00
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
}
2023-10-30 18:23:23 -04:00
if err := event . WriteObservation ( ctx , op , event . WithDetails ( "user_id" , u . GetPublicId (), "auth_token_start" ,
tok . GetCreateTime (), "auth_token_end" , tok . GetExpirationTime ())); err != nil {
return nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "Unable to write observation event for authenticate method" ))
}
2021-05-04 19:57:35 -05:00
return s . ConvertInternalAuthTokenToApiAuthToken (
2021-04-08 20:54:32 -04:00
ctx ,
tok ,
)
}
2024-08-29 17:14:42 -04:00
func validateAuthenticatePasswordRequest ( ctx context . Context , req * pbs . AuthenticateRequest ) error {
const op = "authmethods.(Service).validateAuthenticatePasswordRequest"
2021-04-08 20:54:32 -04:00
badFields := make ( map [ string ] string )
2024-08-29 17:14:42 -04:00
requestInfo , ok := auth . GetRequestInfo ( ctx )
if ! ok {
return errors . New ( ctx , errors . Internal , op , "no request info found" )
}
for _ , action := range requestInfo . Actions {
switch action {
case auth . CallbackAction :
badFields [ "request_path" ] = "callback is not a valid action for this auth method."
}
}
2022-04-11 14:50:29 -04:00
attrs := req . GetPasswordLoginAttributes ()
2022-06-19 10:55:37 -04:00
switch {
case attrs == nil :
badFields [ "attributes" ] = "This is a required field."
default :
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".`
}
2021-04-08 20:54:32 -04:00
}
if len ( badFields ) > 0 {
return handlers . InvalidArgumentErrorf ( "Invalid fields provided in request." , badFields )
}
return nil
}
2023-06-06 17:09:08 -04:00
func toStoragePwAuthMethod ( ctx context . Context , scopeId string , item * pb . AuthMethod ) ( * password . AuthMethod , error ) {
2021-04-08 20:54:32 -04:00
const op = "authmethod_service.toStoragePwAuthMethod"
if item == nil {
2023-06-06 17:09:08 -04:00
return nil , errors . New ( ctx , errors . InvalidParameter , op , "nil auth method." )
2021-04-08 20:54:32 -04:00
}
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 ()))
}
2023-06-06 17:09:08 -04:00
u , err := password . NewAuthMethod ( ctx , scopeId , opts ... )
2021-04-08 20:54:32 -04:00
if err != nil {
return nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to build auth method for creation: %v." , err )
}
2022-04-08 11:40:59 -04:00
pwAttrs := item . GetPasswordAuthMethodAttributes ()
2021-04-08 20:54:32 -04:00
if pwAttrs . GetMinLoginNameLength () != 0 {
u . MinLoginNameLength = pwAttrs . GetMinLoginNameLength ()
}
if pwAttrs . GetMinPasswordLength () != 0 {
u . MinPasswordLength = pwAttrs . GetMinPasswordLength ()
}
return u , nil
}