Files
threshold/internal/servers/controller/handlers/authmethods/authmethod_service.go
T
Jeff Mitchell aa570f4e48 Honor child scope list permissions when recursing (#1016)
Current behavior is that you need list grant on the parent scope when
recursively listing, and this is sufficient. This is good for
administrative workflows but can be unintuitive for resources that only
exist at a project level. This change allows permissions on the child
scope to also be taken into account, so that you can start a recursive
list from a parent scope without direct list support on that scope but
still get back child scopes for which list access is granted.

This also fixes a bug where visibility of a scope for recursive listing
would be based on whether the user had list resources for roles within
that scope instead of for the resource type in question.
2021-03-24 15:33:04 -04:00

691 lines
23 KiB
Go

package authmethods
import (
"context"
stderrors "errors"
"fmt"
"strings"
"github.com/hashicorp/boundary/internal/auth"
"github.com/hashicorp/boundary/internal/auth/password"
"github.com/hashicorp/boundary/internal/auth/password/store"
"github.com/hashicorp/boundary/internal/authtoken"
"github.com/hashicorp/boundary/internal/errors"
pb "github.com/hashicorp/boundary/internal/gen/controller/api/resources/authmethods"
pba "github.com/hashicorp/boundary/internal/gen/controller/api/resources/authtokens"
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/scopes"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/perms"
"github.com/hashicorp/boundary/internal/servers/controller/common"
"github.com/hashicorp/boundary/internal/servers/controller/common/scopeids"
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
"github.com/hashicorp/boundary/internal/servers/controller/handlers/accounts"
"github.com/hashicorp/boundary/internal/servers/controller/handlers/authtokens"
"github.com/hashicorp/boundary/internal/types/action"
"github.com/hashicorp/boundary/internal/types/resource"
"github.com/hashicorp/boundary/internal/types/scope"
"github.com/hashicorp/boundary/sdk/strutil"
"google.golang.org/grpc/codes"
"google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/wrapperspb"
)
const (
loginNameKey = "login_name"
pwKey = "password"
)
var (
maskManager handlers.MaskManager
// IdActions contains the set of actions that can be performed on
// individual resources
IdActions = action.ActionSet{
action.Read,
action.Update,
action.Delete,
action.Authenticate,
}
// CollectionActions contains the set of actions that can be performed on
// this collection
CollectionActions = action.ActionSet{
action.Create,
action.List,
}
collectionTypeMap = map[resource.Type]action.ActionSet{
resource.Account: accounts.CollectionActions,
}
)
func init() {
var err error
if maskManager, err = handlers.NewMaskManager(&store.AuthMethod{}, &pb.AuthMethod{}, &pb.PasswordAuthMethodAttributes{}); err != nil {
panic(err)
}
}
func populateCollectionAuthorizedActions(ctx context.Context,
authResults auth.VerifyResults,
item *pb.AuthMethod) error {
res := &perms.Resource{
ScopeId: authResults.Scope.Id,
Pin: item.Id,
}
// Range over the defined collections and check permissions against those
// collections. We use the ID of this scope being returned, not its parent,
// hence passing in a resource here.
for k, v := range collectionTypeMap {
res.Type = k
acts := authResults.FetchActionSetForType(ctx, k, v, auth.WithResource(res)).Strings()
if len(acts) > 0 {
if item.AuthorizedCollectionActions == nil {
item.AuthorizedCollectionActions = make(map[string]*structpb.ListValue)
}
lv, err := structpb.NewList(strutil.StringListToInterfaceList(acts))
if err != nil {
return err
}
item.AuthorizedCollectionActions[k.String()+"s"] = lv
}
}
return nil
}
// Service handles request as described by the pbs.AuthMethodServiceServer interface.
type Service struct {
pbs.UnimplementedAuthMethodServiceServer
kms *kms.Kms
pwRepoFn common.PasswordAuthRepoFactory
iamRepoFn common.IamRepoFactory
atRepoFn common.AuthTokenRepoFactory
}
// NewService returns a auth method service which handles auth method related requests to boundary.
func NewService(kms *kms.Kms, pwRepoFn common.PasswordAuthRepoFactory, iamRepoFn common.IamRepoFactory, atRepoFn common.AuthTokenRepoFactory) (Service, error) {
if kms == nil {
return Service{}, stderrors.New("nil kms provided")
}
if pwRepoFn == nil {
return Service{}, fmt.Errorf("nil password repository provided")
}
if iamRepoFn == nil {
return Service{}, fmt.Errorf("nil iam repository provided")
}
return Service{kms: kms, pwRepoFn: pwRepoFn, iamRepoFn: iamRepoFn, atRepoFn: atRepoFn}, nil
}
var _ pbs.AuthMethodServiceServer = Service{}
// ListAuthMethods implements the interface pbs.AuthMethodServiceServer.
func (s Service) ListAuthMethods(ctx context.Context, req *pbs.ListAuthMethodsRequest) (*pbs.ListAuthMethodsResponse, error) {
if err := validateListRequest(req); err != nil {
return nil, err
}
authResults := s.authResult(ctx, req.GetScopeId(), action.List)
if authResults.Error != nil {
// If it's forbidden, and it's a recursive request, and they're
// successfully authenticated but just not authorized, keep going as we
// may have authorization on downstream scopes.
if authResults.Error == handlers.ForbiddenError() &&
req.GetRecursive() &&
authResults.Authenticated {
} else {
return nil, authResults.Error
}
}
scopeIds, scopeInfoMap, err := scopeids.GetListingScopeIds(
ctx, s.iamRepoFn, authResults, req.GetScopeId(), resource.AuthMethod, req.GetRecursive(), false)
if err != nil {
return nil, err
}
// If no scopes match, return an empty response
if len(scopeIds) == 0 {
return &pbs.ListAuthMethodsResponse{}, nil
}
ul, err := s.listFromRepo(ctx, scopeIds)
if err != nil {
return nil, err
}
filter, err := handlers.NewFilter(req.GetFilter())
if err != nil {
return nil, err
}
finalItems := make([]*pb.AuthMethod, 0, len(ul))
res := &perms.Resource{
Type: resource.AuthMethod,
}
for _, item := range ul {
item.Scope = scopeInfoMap[item.GetScopeId()]
res.ScopeId = item.Scope.Id
item.AuthorizedActions = authResults.FetchActionSetForId(ctx, item.Id, IdActions, auth.WithResource(res)).Strings()
if len(item.AuthorizedActions) == 0 {
continue
}
if filter.Match(item) {
finalItems = append(finalItems, item)
if err := populateCollectionAuthorizedActions(ctx, authResults, item); err != nil {
return nil, err
}
}
}
return &pbs.ListAuthMethodsResponse{Items: finalItems}, nil
}
// GetAuthMethod implements the interface pbs.AuthMethodServiceServer.
func (s Service) GetAuthMethod(ctx context.Context, req *pbs.GetAuthMethodRequest) (*pbs.GetAuthMethodResponse, error) {
if err := validateGetRequest(req); err != nil {
return nil, err
}
authResults := s.authResult(ctx, req.GetId(), action.Read)
if authResults.Error != nil {
return nil, authResults.Error
}
u, err := s.getFromRepo(ctx, req.GetId())
if err != nil {
return nil, err
}
u.Scope = authResults.Scope
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions).Strings()
if err := populateCollectionAuthorizedActions(ctx, authResults, u); err != nil {
return nil, err
}
return &pbs.GetAuthMethodResponse{Item: u}, nil
}
// CreateAuthMethod implements the interface pbs.AuthMethodServiceServer.
func (s Service) CreateAuthMethod(ctx context.Context, req *pbs.CreateAuthMethodRequest) (*pbs.CreateAuthMethodResponse, error) {
if err := validateCreateRequest(req); err != nil {
return nil, err
}
authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create)
if authResults.Error != nil {
return nil, authResults.Error
}
u, err := s.createInRepo(ctx, authResults.Scope.GetId(), req.GetItem())
if err != nil {
return nil, err
}
u.Scope = authResults.Scope
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions).Strings()
if err := populateCollectionAuthorizedActions(ctx, authResults, u); err != nil {
return nil, err
}
return &pbs.CreateAuthMethodResponse{Item: u, Uri: fmt.Sprintf("auth-methods/%s", u.GetId())}, nil
}
// UpdateAuthMethod implements the interface pbs.AuthMethodServiceServer.
func (s Service) UpdateAuthMethod(ctx context.Context, req *pbs.UpdateAuthMethodRequest) (*pbs.UpdateAuthMethodResponse, error) {
if err := validateUpdateRequest(req); err != nil {
return nil, err
}
authResults := s.authResult(ctx, req.GetId(), action.Update)
if authResults.Error != nil {
return nil, authResults.Error
}
u, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem())
if err != nil {
return nil, err
}
u.Scope = authResults.Scope
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions).Strings()
if err := populateCollectionAuthorizedActions(ctx, authResults, u); err != nil {
return nil, err
}
return &pbs.UpdateAuthMethodResponse{Item: u}, nil
}
// DeleteAuthMethod implements the interface pbs.AuthMethodServiceServer.
func (s Service) DeleteAuthMethod(ctx context.Context, req *pbs.DeleteAuthMethodRequest) (*pbs.DeleteAuthMethodResponse, error) {
if err := validateDeleteRequest(req); err != nil {
return nil, err
}
authResults := s.authResult(ctx, req.GetId(), action.Delete)
if authResults.Error != nil {
return nil, authResults.Error
}
_, err := s.deleteFromRepo(ctx, authResults.Scope.GetId(), req.GetId())
if err != nil {
return nil, err
}
return &pbs.DeleteAuthMethodResponse{}, nil
}
// Authenticate implements the interface pbs.AuthenticationServiceServer.
//
// Deprecated in favor of AuthenticateLogin
func (s Service) Authenticate(ctx context.Context, req *pbs.AuthenticateRequest) (*pbs.AuthenticateResponse, error) {
if err := validateAuthenticateRequest(req); err != nil {
return nil, err
}
authResults := s.authResult(ctx, req.GetAuthMethodId(), action.Authenticate)
if authResults.Error != nil {
return nil, authResults.Error
}
creds := req.GetCredentials().GetFields()
tok, err := s.authenticateWithRepo(ctx, authResults.Scope.GetId(), req.GetAuthMethodId(), creds[loginNameKey].GetStringValue(), creds[pwKey].GetStringValue())
if err != nil {
return nil, err
}
res := &perms.Resource{
ScopeId: authResults.Scope.Id,
Type: resource.AuthToken,
}
tok.AuthorizedActions = authResults.FetchActionSetForId(ctx, tok.Id, authtokens.IdActions, auth.WithResource(res)).Strings()
return &pbs.AuthenticateResponse{Item: tok, TokenType: req.GetTokenType()}, nil
}
// AuthenticateLogin implements the interface pbs.AuthenticationServiceServer.
func (s Service) AuthenticateLogin(ctx context.Context, req *pbs.AuthenticateLoginRequest) (*pbs.AuthenticateLoginResponse, error) {
if err := validateAuthenticateLoginRequest(req); err != nil {
return nil, err
}
authResults := s.authResult(ctx, req.GetAuthMethodId(), action.Authenticate)
if authResults.Error != nil {
return nil, authResults.Error
}
creds := req.GetCredentials().GetFields()
tok, err := s.authenticateWithRepo(ctx, authResults.Scope.GetId(), req.GetAuthMethodId(), creds[loginNameKey].GetStringValue(), creds[pwKey].GetStringValue())
if err != nil {
return nil, err
}
res := &perms.Resource{
ScopeId: authResults.Scope.Id,
Type: resource.AuthToken,
}
tok.AuthorizedActions = authResults.FetchActionSetForId(ctx, tok.Id, authtokens.IdActions, auth.WithResource(res)).Strings()
return &pbs.AuthenticateLoginResponse{Item: tok, TokenType: req.GetTokenType()}, nil
}
func (s Service) getFromRepo(ctx context.Context, id string) (*pb.AuthMethod, error) {
repo, err := s.pwRepoFn()
if err != nil {
return nil, err
}
u, err := repo.LookupAuthMethod(ctx, id)
if err != nil {
if errors.IsNotFoundError(err) {
return nil, handlers.NotFoundErrorf("AuthMethod %q doesn't exist.", id)
}
return nil, err
}
if u == nil {
return nil, handlers.NotFoundErrorf("AuthMethod %q doesn't exist.", id)
}
return toAuthMethodProto(u)
}
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*pb.AuthMethod, error) {
repo, err := s.pwRepoFn()
if err != nil {
return nil, err
}
ul, err := repo.ListAuthMethods(ctx, scopeIds)
if err != nil {
return nil, err
}
var outUl []*pb.AuthMethod
for _, u := range ul {
ou, err := toAuthMethodProto(u)
if err != nil {
return nil, err
}
outUl = append(outUl, ou)
}
return outUl, nil
}
func (s Service) createInRepo(ctx context.Context, scopeId string, item *pb.AuthMethod) (*pb.AuthMethod, error) {
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)
}
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)
}
if out == nil {
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create auth method but no error returned from repository.")
}
return toAuthMethodProto(out)
}
func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []string, item *pb.AuthMethod) (*pb.AuthMethod, error) {
var opts []password.Option
if desc := item.GetDescription(); desc != nil {
opts = append(opts, password.WithDescription(desc.GetValue()))
}
if name := item.GetName(); name != nil {
opts = append(opts, password.WithName(name.GetValue()))
}
u, err := password.NewAuthMethod(scopeId, opts...)
if err != nil {
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to build auth method for update: %v.", err)
}
pwAttrs := &pb.PasswordAuthMethodAttributes{}
if err := handlers.StructToProto(item.GetAttributes(), pwAttrs); err != nil {
return nil, handlers.InvalidArgumentErrorf("Error in provided request.",
map[string]string{"attributes": "Attribute fields do not match the expected format."})
}
if pwAttrs.GetMinLoginNameLength() != 0 {
u.MinLoginNameLength = pwAttrs.GetMinLoginNameLength()
}
if pwAttrs.GetMinPasswordLength() != 0 {
u.MinPasswordLength = pwAttrs.GetMinPasswordLength()
}
version := item.GetVersion()
u.PublicId = id
dbMask := maskManager.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 toAuthMethodProto(out)
}
func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool, error) {
repo, err := s.pwRepoFn()
if err != nil {
return false, err
}
rows, err := repo.DeleteAuthMethod(ctx, scopeId, id)
if err != nil {
if errors.IsNotFoundError(err) {
return false, nil
}
return false, fmt.Errorf("unable to delete auth method: %w", err)
}
return rows > 0, nil
}
func (s Service) authenticateWithRepo(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(), iam.WithAutoVivify(true))
if err != nil {
return nil, err
}
tok, err := atRepo.CreateAuthToken(ctx, u, acct.GetPublicId())
if err != nil {
return nil, err
}
token, err := authtoken.EncryptToken(ctx, s.kms, scopeId, tok.GetPublicId(), tok.GetToken())
if err != nil {
return nil, err
}
tok.Token = tok.GetPublicId() + "_" + token
prot := toAuthTokenProto(tok)
scp, err := iamRepo.LookupScope(ctx, u.GetScopeId())
if err != nil {
return nil, err
}
if scp == nil {
return nil, err
}
prot.Scope = &scopes.ScopeInfo{
Id: scp.GetPublicId(),
Type: scp.GetType(),
ParentScopeId: scp.GetParentId(),
}
return prot, nil
}
func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults {
res := auth.VerifyResults{}
var parentId string
opts := []auth.Option{auth.WithType(resource.AuthMethod), auth.WithAction(a)}
switch a {
case action.List, action.Create:
parentId = id
iamRepo, err := s.iamRepoFn()
if err != nil {
res.Error = err
return res
}
scp, err := iamRepo.LookupScope(ctx, parentId)
if err != nil {
res.Error = err
return res
}
if scp == nil {
res.Error = handlers.NotFoundError()
return res
}
default:
repo, err := s.pwRepoFn()
if err != nil {
res.Error = err
return res
}
authMeth, err := repo.LookupAuthMethod(ctx, id)
if err != nil {
res.Error = err
return res
}
if authMeth == nil {
res.Error = handlers.NotFoundError()
return res
}
parentId = authMeth.GetScopeId()
opts = append(opts, auth.WithId(id))
}
opts = append(opts, auth.WithScopeId(parentId))
return auth.Verify(ctx, opts...)
}
func toAuthMethodProto(in *password.AuthMethod) (*pb.AuthMethod, error) {
out := pb.AuthMethod{
Id: in.GetPublicId(),
ScopeId: in.GetScopeId(),
CreatedTime: in.GetCreateTime().GetTimestamp(),
UpdatedTime: in.GetUpdateTime().GetTimestamp(),
Version: in.GetVersion(),
Type: auth.PasswordSubtype.String(),
}
if in.GetDescription() != "" {
out.Description = wrapperspb.String(in.GetDescription())
}
if in.GetName() != "" {
out.Name = wrapperspb.String(in.GetName())
}
st, err := handlers.ProtoToStruct(&pb.PasswordAuthMethodAttributes{
MinLoginNameLength: in.GetMinLoginNameLength(),
MinPasswordLength: in.GetMinPasswordLength(),
})
if err != nil {
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "failed building password attribute struct: %v", err)
}
out.Attributes = st
return &out, nil
}
func toAuthTokenProto(t *authtoken.AuthToken) *pba.AuthToken {
return &pba.AuthToken{
Id: t.GetPublicId(),
Token: t.GetToken(),
UserId: t.GetIamUserId(),
AuthMethodId: t.GetAuthMethodId(),
AccountId: t.GetAuthAccountId(),
CreatedTime: t.GetCreateTime().GetTimestamp(),
UpdatedTime: t.GetUpdateTime().GetTimestamp(),
ApproximateLastUsedTime: t.GetApproximateLastAccessTime().GetTimestamp(),
ExpirationTime: t.GetExpirationTime().GetTimestamp(),
}
}
// A validateX method should exist for each method above. These methods do not make calls to any backing service but enforce
// requirements on the structure of the request. They verify that:
// * The path passed in is correctly formatted
// * All required parameters are set
// * There are no conflicting parameters provided
func validateGetRequest(req *pbs.GetAuthMethodRequest) error {
return handlers.ValidateGetRequest(password.AuthMethodPrefix, req, handlers.NoopValidatorFn)
}
func validateCreateRequest(req *pbs.CreateAuthMethodRequest) error {
return handlers.ValidateCreateRequest(req.GetItem(), func() map[string]string {
badFields := map[string]string{}
if !handlers.ValidId(scope.Org.Prefix(), req.GetItem().GetScopeId()) &&
scope.Global.String() != req.GetItem().GetScopeId() {
badFields["scope_id"] = "This field must be 'global' or a valid org scope id."
}
switch auth.SubtypeFromType(req.GetItem().GetType()) {
case auth.PasswordSubtype:
pwAttrs := &pb.PasswordAuthMethodAttributes{}
if err := handlers.StructToProto(req.GetItem().GetAttributes(), pwAttrs); err != nil {
badFields["attributes"] = "Attribute fields do not match the expected format."
}
default:
badFields["type"] = fmt.Sprintf("This is a required field and must be %q.", auth.PasswordSubtype.String())
}
return badFields
})
}
func validateUpdateRequest(req *pbs.UpdateAuthMethodRequest) error {
return handlers.ValidateUpdateRequest(password.AuthMethodPrefix, req, req.GetItem(), func() map[string]string {
badFields := map[string]string{}
switch auth.SubtypeFromId(req.GetId()) {
case auth.PasswordSubtype:
if req.GetItem().GetType() != "" && auth.SubtypeFromType(req.GetItem().GetType()) != auth.PasswordSubtype {
badFields["type"] = "Cannot modify the resource type."
}
pwAttrs := &pb.PasswordAuthMethodAttributes{}
if err := handlers.StructToProto(req.GetItem().GetAttributes(), pwAttrs); err != nil {
badFields["attributes"] = "Attribute fields do not match the expected format."
}
default:
badFields["id"] = "Incorrectly formatted identifier."
}
return badFields
})
}
func validateDeleteRequest(req *pbs.DeleteAuthMethodRequest) error {
return handlers.ValidateDeleteRequest(password.AuthMethodPrefix, req, handlers.NoopValidatorFn)
}
func validateListRequest(req *pbs.ListAuthMethodsRequest) error {
badFields := map[string]string{}
if !handlers.ValidId(scope.Org.Prefix(), req.GetScopeId()) &&
req.GetScopeId() != scope.Global.String() {
badFields["scope_id"] = "This field must be 'global' or a valid org scope id."
}
if _, err := handlers.NewFilter(req.GetFilter()); err != nil {
badFields["filter"] = fmt.Sprintf("This field could not be parsed. %v", err)
}
if len(badFields) > 0 {
return handlers.InvalidArgumentErrorf("Improperly formatted identifier.", badFields)
}
return nil
}
// Deprecated; remove when Authenticate is removedLogin
func validateAuthenticateRequest(req *pbs.AuthenticateRequest) error {
badFields := make(map[string]string)
if strings.TrimSpace(req.GetAuthMethodId()) == "" {
badFields["auth_method_id"] = "This is a required field."
} else if !handlers.ValidId(password.AuthMethodPrefix, req.GetAuthMethodId()) {
badFields["auth_method_id"] = "Invalid formatted identifier."
}
// TODO: Update this when we enable different auth method types.
if req.GetCredentials() == nil {
badFields["credentials"] = "This is a required field."
}
creds := req.GetCredentials().GetFields()
if _, ok := creds[loginNameKey]; !ok {
badFields["credentials.login_name"] = "This is a required field."
}
if _, ok := creds[pwKey]; !ok {
badFields["credentials.password"] = "This is a required field."
}
tType := strings.ToLower(strings.TrimSpace(req.GetTokenType()))
if tType != "" && tType != "token" && tType != "cookie" {
badFields["token_type"] = `The only accepted types are "token" and "cookie".`
}
if len(badFields) > 0 {
return handlers.InvalidArgumentErrorf("Invalid fields provided in request.", badFields)
}
return nil
}
func validateAuthenticateLoginRequest(req *pbs.AuthenticateLoginRequest) error {
badFields := make(map[string]string)
if strings.TrimSpace(req.GetAuthMethodId()) == "" {
badFields["auth_method_id"] = "This is a required field."
} else if !handlers.ValidId(password.AuthMethodPrefix, req.GetAuthMethodId()) {
badFields["auth_method_id"] = "Invalid formatted identifier."
}
if req.GetCredentials() == nil {
badFields["credentials"] = "This is a required field."
}
creds := req.GetCredentials().GetFields()
if _, ok := creds[loginNameKey]; !ok {
badFields["credentials.login_name"] = "This is a required field."
}
if _, ok := creds[pwKey]; !ok {
badFields["credentials.password"] = "This is a required field."
}
tType := strings.ToLower(strings.TrimSpace(req.GetTokenType()))
if tType != "" && tType != "token" && tType != "cookie" {
badFields["token_type"] = `The only accepted types are "token" and "cookie".`
}
if len(badFields) > 0 {
return handlers.InvalidArgumentErrorf("Invalid fields provided in request.", badFields)
}
return nil
}