mirror of https://github.com/hashicorp/boundary
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.
1221 lines
41 KiB
1221 lines
41 KiB
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package authmethods_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
"github.com/hashicorp/boundary/globals"
|
|
am "github.com/hashicorp/boundary/internal/auth"
|
|
"github.com/hashicorp/boundary/internal/auth/ldap"
|
|
"github.com/hashicorp/boundary/internal/auth/oidc"
|
|
"github.com/hashicorp/boundary/internal/auth/password"
|
|
"github.com/hashicorp/boundary/internal/authtoken"
|
|
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
|
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
|
|
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/authmethods"
|
|
"github.com/hashicorp/boundary/internal/db"
|
|
"github.com/hashicorp/boundary/internal/errors"
|
|
"github.com/hashicorp/boundary/internal/event"
|
|
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/types/scope"
|
|
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/authmethods"
|
|
scopepb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
|
|
"github.com/hashicorp/eventlogger/formatter_filters/cloudevents"
|
|
"github.com/hashicorp/go-hclog"
|
|
"github.com/jimlambrt/gldap"
|
|
"github.com/jimlambrt/gldap/testdirectory"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"google.golang.org/genproto/protobuf/field_mask"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/protobuf/testing/protocmp"
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
|
"google.golang.org/protobuf/types/known/wrapperspb"
|
|
)
|
|
|
|
func Test_UpdateLdap(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.TODO()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
rw := db.New(conn)
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
iamRepoFn := func() (*iam.Repository, error) {
|
|
return iam.TestRepo(t, conn, wrapper), nil
|
|
}
|
|
oidcRepoFn := func() (*oidc.Repository, error) {
|
|
return oidc.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
ldapRepoFn := func() (*ldap.Repository, error) {
|
|
return ldap.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
pwRepoFn := func() (*password.Repository, error) {
|
|
return password.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
atRepoFn := func() (*authtoken.Repository, error) {
|
|
return authtoken.NewRepository(ctx, rw, rw, kms)
|
|
}
|
|
authMethodRepoFn := func() (*am.AuthMethodRepository, error) {
|
|
return am.NewAuthMethodRepository(ctx, rw, rw, kms)
|
|
}
|
|
iamRepo := iam.TestRepo(t, conn, wrapper)
|
|
|
|
o, _ := iam.TestScopes(t, iamRepo)
|
|
tested, err := authmethods.NewService(ctx, kms, pwRepoFn, oidcRepoFn, iamRepoFn, atRepoFn, ldapRepoFn, authMethodRepoFn, 1000)
|
|
require.NoError(t, err, "Error when getting new auth_method service.")
|
|
|
|
defaultScopeInfo := &scopepb.ScopeInfo{Id: o.GetPublicId(), Type: o.GetType(), ParentScopeId: scope.Global.String()}
|
|
|
|
defaultAttributes := &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
Urls: []string{"ldaps://ldap1"},
|
|
State: "active-private",
|
|
},
|
|
}
|
|
defaultReadAttributes := &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
Urls: []string{"ldaps://ldap1"},
|
|
State: "active-private",
|
|
},
|
|
}
|
|
|
|
freshAuthMethod := func(t *testing.T, attrs *pb.AuthMethod_LdapAuthMethodsAttributes) (*pb.AuthMethod, func()) {
|
|
if attrs == nil {
|
|
attrs = defaultAttributes
|
|
}
|
|
ctx := auth.DisabledAuthTestContext(iamRepoFn, o.GetPublicId())
|
|
am, err := tested.CreateAuthMethod(ctx, &pbs.CreateAuthMethodRequest{Item: &pb.AuthMethod{
|
|
ScopeId: o.GetPublicId(),
|
|
Name: wrapperspb.String("default"),
|
|
Description: wrapperspb.String("default"),
|
|
Type: ldap.Subtype.String(),
|
|
Attrs: attrs,
|
|
}})
|
|
require.NoError(t, err)
|
|
|
|
clean := func() {
|
|
_, err := tested.DeleteAuthMethod(auth.DisabledAuthTestContext(iamRepoFn, o.GetPublicId()),
|
|
&pbs.DeleteAuthMethodRequest{Id: am.GetItem().GetId()})
|
|
require.NoError(t, err)
|
|
}
|
|
return am.GetItem(), clean
|
|
}
|
|
|
|
_, testEncodedCert := ldap.TestGenerateCA(t, "localhost")
|
|
|
|
tests := []struct {
|
|
name string
|
|
newAttrsOverride *pb.AuthMethod_LdapAuthMethodsAttributes
|
|
req *pbs.UpdateAuthMethodRequest
|
|
res *pbs.UpdateAuthMethodResponse
|
|
err error
|
|
errContains string
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "update-an-existing-auth-method",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"name", "description"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
Name: &wrapperspb.StringValue{Value: "new"},
|
|
Description: &wrapperspb.StringValue{Value: "desc"},
|
|
Type: ldap.Subtype.String(),
|
|
},
|
|
},
|
|
res: &pbs.UpdateAuthMethodResponse{
|
|
Item: &pb.AuthMethod{
|
|
ScopeId: o.GetPublicId(),
|
|
Name: &wrapperspb.StringValue{Value: "new"},
|
|
Description: &wrapperspb.StringValue{Value: "desc"},
|
|
Version: 2,
|
|
Type: ldap.Subtype.String(),
|
|
Attrs: defaultReadAttributes,
|
|
Scope: defaultScopeInfo,
|
|
AuthorizedActions: ldapAuthorizedActions,
|
|
AuthorizedCollectionActions: authorizedCollectionActions,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "multi-paths-in-single-string",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"name,description,type"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
Name: &wrapperspb.StringValue{Value: "new"},
|
|
Description: &wrapperspb.StringValue{Value: "desc"},
|
|
Type: ldap.Subtype.String(),
|
|
},
|
|
},
|
|
res: &pbs.UpdateAuthMethodResponse{
|
|
Item: &pb.AuthMethod{
|
|
ScopeId: o.GetPublicId(),
|
|
Version: 2,
|
|
Name: &wrapperspb.StringValue{Value: "new"},
|
|
Description: &wrapperspb.StringValue{Value: "desc"},
|
|
Type: ldap.Subtype.String(),
|
|
Attrs: defaultReadAttributes,
|
|
Scope: defaultScopeInfo,
|
|
AuthorizedActions: ldapAuthorizedActions,
|
|
AuthorizedCollectionActions: authorizedCollectionActions,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "no-update-mask",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
Item: &pb.AuthMethod{
|
|
Name: &wrapperspb.StringValue{Value: "updated name"},
|
|
Description: &wrapperspb.StringValue{Value: "updated desc"},
|
|
},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
errContains: "UpdateMask not provided but is required to update this resource",
|
|
},
|
|
{
|
|
name: "missing-paths-in-mask",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{Paths: []string{}},
|
|
Item: &pb.AuthMethod{
|
|
Name: &wrapperspb.StringValue{Value: "updated name"},
|
|
Description: &wrapperspb.StringValue{Value: "updated desc"},
|
|
},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
errContains: "No valid fields included in the update mask",
|
|
},
|
|
{
|
|
name: "non-existent-paths-in-mask",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{Paths: []string{"nonexistent_field"}},
|
|
Item: &pb.AuthMethod{
|
|
Name: &wrapperspb.StringValue{Value: "updated name"},
|
|
Description: &wrapperspb.StringValue{Value: "updated desc"},
|
|
},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
errContains: "No valid fields included in the update mask",
|
|
},
|
|
{
|
|
name: "cannot-change-type",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{Paths: []string{"name", "type"}},
|
|
Item: &pb.AuthMethod{
|
|
Name: &wrapperspb.StringValue{Value: "updated name"},
|
|
Type: password.Subtype.String(),
|
|
},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
errContains: "Cannot modify the resource type",
|
|
},
|
|
{
|
|
name: "cannot-change-is-primary",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{Paths: []string{"is_primary"}},
|
|
Item: &pb.AuthMethod{
|
|
IsPrimary: true,
|
|
},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
errContains: "This field is read only",
|
|
},
|
|
{
|
|
name: "unset-name",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"name"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
Description: &wrapperspb.StringValue{Value: "ignored"},
|
|
},
|
|
},
|
|
res: &pbs.UpdateAuthMethodResponse{
|
|
Item: &pb.AuthMethod{
|
|
ScopeId: o.GetPublicId(),
|
|
Version: 2,
|
|
Description: &wrapperspb.StringValue{Value: "default"},
|
|
Type: ldap.Subtype.String(),
|
|
Attrs: defaultReadAttributes,
|
|
Scope: defaultScopeInfo,
|
|
AuthorizedActions: ldapAuthorizedActions,
|
|
AuthorizedCollectionActions: authorizedCollectionActions,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "unset-description",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"description"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
Name: &wrapperspb.StringValue{Value: "ignored"},
|
|
},
|
|
},
|
|
res: &pbs.UpdateAuthMethodResponse{
|
|
Item: &pb.AuthMethod{
|
|
ScopeId: o.GetPublicId(),
|
|
Version: 2,
|
|
Name: &wrapperspb.StringValue{Value: "default"},
|
|
Type: ldap.Subtype.String(),
|
|
Attrs: defaultReadAttributes,
|
|
Scope: defaultScopeInfo,
|
|
AuthorizedActions: ldapAuthorizedActions,
|
|
AuthorizedCollectionActions: authorizedCollectionActions,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "update-only-state",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"attributes.state"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
Attrs: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
State: "active-public",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
res: &pbs.UpdateAuthMethodResponse{
|
|
Item: &pb.AuthMethod{
|
|
ScopeId: o.GetPublicId(),
|
|
Version: 2,
|
|
Name: &wrapperspb.StringValue{Value: "default"},
|
|
Description: &wrapperspb.StringValue{Value: "default"},
|
|
Type: ldap.Subtype.String(),
|
|
Attrs: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
Urls: []string{"ldaps://ldap1"},
|
|
State: "active-public",
|
|
},
|
|
},
|
|
Scope: defaultScopeInfo,
|
|
AuthorizedActions: ldapAuthorizedActions,
|
|
AuthorizedCollectionActions: authorizedCollectionActions,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "update-only-name",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"name"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
Name: &wrapperspb.StringValue{Value: "updated"},
|
|
Description: &wrapperspb.StringValue{Value: "ignored"},
|
|
},
|
|
},
|
|
res: &pbs.UpdateAuthMethodResponse{
|
|
Item: &pb.AuthMethod{
|
|
ScopeId: o.GetPublicId(),
|
|
Version: 2,
|
|
Name: &wrapperspb.StringValue{Value: "updated"},
|
|
Description: &wrapperspb.StringValue{Value: "default"},
|
|
Type: ldap.Subtype.String(),
|
|
Attrs: defaultReadAttributes,
|
|
Scope: defaultScopeInfo,
|
|
AuthorizedActions: ldapAuthorizedActions,
|
|
AuthorizedCollectionActions: authorizedCollectionActions,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "update-only-description",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"description"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
Name: &wrapperspb.StringValue{Value: "ignored"},
|
|
Description: &wrapperspb.StringValue{Value: "notignored"},
|
|
},
|
|
},
|
|
res: &pbs.UpdateAuthMethodResponse{
|
|
Item: &pb.AuthMethod{
|
|
ScopeId: o.GetPublicId(),
|
|
Version: 2,
|
|
Name: &wrapperspb.StringValue{Value: "default"},
|
|
Description: &wrapperspb.StringValue{Value: "notignored"},
|
|
Type: ldap.Subtype.String(),
|
|
Attrs: defaultReadAttributes,
|
|
Scope: defaultScopeInfo,
|
|
AuthorizedActions: ldapAuthorizedActions,
|
|
AuthorizedCollectionActions: authorizedCollectionActions,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "update-only-bind-dn",
|
|
newAttrsOverride: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
Urls: []string{"ldaps://ldap1"},
|
|
State: "active-private",
|
|
BindDn: &wrapperspb.StringValue{Value: "bind-dn"},
|
|
BindPassword: &wrapperspb.StringValue{Value: "bind-password"},
|
|
},
|
|
},
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"attributes.bind_dn"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
Attrs: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
BindDn: &wrapperspb.StringValue{Value: "updated"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
res: &pbs.UpdateAuthMethodResponse{
|
|
Item: &pb.AuthMethod{
|
|
ScopeId: o.GetPublicId(),
|
|
Version: 2,
|
|
Name: &wrapperspb.StringValue{Value: "default"},
|
|
Description: &wrapperspb.StringValue{Value: "default"},
|
|
Type: ldap.Subtype.String(),
|
|
Attrs: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
Urls: []string{"ldaps://ldap1"},
|
|
State: "active-private",
|
|
BindDn: &wrapperspb.StringValue{Value: "updated"},
|
|
// note: BindPassword is never returned (an HMAC'd
|
|
// value is returned in a separate attribute)
|
|
},
|
|
},
|
|
Scope: defaultScopeInfo,
|
|
AuthorizedActions: ldapAuthorizedActions,
|
|
AuthorizedCollectionActions: authorizedCollectionActions,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "err-update-only-bind-dn-with-no-orig-bind-password",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"attributes.bind_dn"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
Attrs: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
BindDn: &wrapperspb.StringValue{Value: "updated"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
errContains: "missing password",
|
|
},
|
|
{
|
|
name: "update-only-bind-password",
|
|
newAttrsOverride: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
Urls: []string{"ldaps://ldap1"},
|
|
State: "active-private",
|
|
BindDn: &wrapperspb.StringValue{Value: "bind-dn"},
|
|
BindPassword: &wrapperspb.StringValue{Value: "bind-password"},
|
|
},
|
|
},
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"attributes.bind_password"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
Attrs: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
BindPassword: &wrapperspb.StringValue{Value: "updated"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
res: &pbs.UpdateAuthMethodResponse{
|
|
Item: &pb.AuthMethod{
|
|
ScopeId: o.GetPublicId(),
|
|
Version: 2,
|
|
Name: &wrapperspb.StringValue{Value: "default"},
|
|
Description: &wrapperspb.StringValue{Value: "default"},
|
|
Type: ldap.Subtype.String(),
|
|
Attrs: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
Urls: []string{"ldaps://ldap1"},
|
|
State: "active-private",
|
|
BindDn: &wrapperspb.StringValue{Value: "bind-dn"},
|
|
// note: BindPassword is never returned (an HMAC'd
|
|
// value is returned in a separate attribute)
|
|
},
|
|
},
|
|
Scope: defaultScopeInfo,
|
|
AuthorizedActions: ldapAuthorizedActions,
|
|
AuthorizedCollectionActions: authorizedCollectionActions,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "err-update-only-bind-password-with-no-orig-bind-dn",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"attributes.bind_password"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
Attrs: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
BindPassword: &wrapperspb.StringValue{Value: "updated"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
errContains: "missing dn",
|
|
},
|
|
{
|
|
name: "update-a-non-existent-auth-method",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
Id: globals.LdapAuthMethodPrefix + "_DoesNotExist",
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"description"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
Name: &wrapperspb.StringValue{Value: "new"},
|
|
Description: &wrapperspb.StringValue{Value: "desc"},
|
|
},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.NotFound),
|
|
errContains: "Resource not found",
|
|
},
|
|
{
|
|
name: "cannot-change-id",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"id"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
Id: globals.LdapAuthMethodPrefix + "_something",
|
|
Name: &wrapperspb.StringValue{Value: "new"},
|
|
Description: &wrapperspb.StringValue{Value: "new desc"},
|
|
},
|
|
},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
errContains: "This is a read only field and cannot be specified in an update request",
|
|
},
|
|
{
|
|
name: "cannot-specify-created-time",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"created_time"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
CreatedTime: timestamppb.Now(),
|
|
},
|
|
},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
errContains: "This is a read only field and cannot be specified in an update request",
|
|
},
|
|
{
|
|
name: "cannot-specify-updated-time",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"updated_time"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
UpdatedTime: timestamppb.Now(),
|
|
},
|
|
},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
errContains: "This is a read only field and cannot be specified in an update request",
|
|
},
|
|
{
|
|
name: "cannot-specify-type",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"type"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
Type: ldap.Subtype.String(),
|
|
},
|
|
},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
errContains: "No valid fields included in the update mask",
|
|
},
|
|
{
|
|
name: "cannot-change-scope",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"scope"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
ScopeId: "something-new",
|
|
Name: &wrapperspb.StringValue{Value: "new"},
|
|
Description: &wrapperspb.StringValue{Value: "new desc"},
|
|
},
|
|
},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
errContains: "No valid fields included in the update mask",
|
|
},
|
|
{
|
|
name: "invalid-certs",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"attributes.certificates"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
ScopeId: "something-new",
|
|
Attrs: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
Certificates: []string{ldap.TestInvalidPem},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
errContains: "invalid attributes.certificates",
|
|
},
|
|
{
|
|
name: "update-certs",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"attributes.certificates"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
Attrs: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
Certificates: []string{testEncodedCert},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
res: &pbs.UpdateAuthMethodResponse{
|
|
Item: &pb.AuthMethod{
|
|
ScopeId: o.GetPublicId(),
|
|
Version: 2,
|
|
Name: &wrapperspb.StringValue{Value: "default"},
|
|
Description: &wrapperspb.StringValue{Value: "default"},
|
|
Type: ldap.Subtype.String(),
|
|
Attrs: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
Urls: []string{"ldaps://ldap1"},
|
|
State: "active-private",
|
|
Certificates: []string{testEncodedCert},
|
|
},
|
|
},
|
|
Scope: defaultScopeInfo,
|
|
AuthorizedActions: ldapAuthorizedActions,
|
|
AuthorizedCollectionActions: authorizedCollectionActions,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "update-urls",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"attributes.urls"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
Attrs: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
Urls: []string{"ldaps://ldap2", "ldaps://ldap3"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
res: &pbs.UpdateAuthMethodResponse{
|
|
Item: &pb.AuthMethod{
|
|
ScopeId: o.GetPublicId(),
|
|
Version: 2,
|
|
Name: &wrapperspb.StringValue{Value: "default"},
|
|
Description: &wrapperspb.StringValue{Value: "default"},
|
|
Type: ldap.Subtype.String(),
|
|
Attrs: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
Urls: []string{"ldaps://ldap2", "ldaps://ldap3"},
|
|
State: "active-private",
|
|
},
|
|
},
|
|
Scope: defaultScopeInfo,
|
|
AuthorizedActions: ldapAuthorizedActions,
|
|
AuthorizedCollectionActions: authorizedCollectionActions,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "update-urls-err",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"attributes.urls"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
Attrs: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
Urls: []string{" ldaps://ldap2"}, // invalid url (space at start)
|
|
},
|
|
},
|
|
},
|
|
},
|
|
res: nil,
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
errContains: "is not a valid url",
|
|
},
|
|
{
|
|
name: "update-user-search-config",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"attributes.user_dn", "attributes.user_attr", "attributes.user_filter"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
Attrs: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
UserDn: wrapperspb.String("user-dn"),
|
|
UserAttr: wrapperspb.String("user-attr"),
|
|
UserFilter: wrapperspb.String("user-filter"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
res: &pbs.UpdateAuthMethodResponse{
|
|
Item: &pb.AuthMethod{
|
|
ScopeId: o.GetPublicId(),
|
|
Version: 2,
|
|
Name: &wrapperspb.StringValue{Value: "default"},
|
|
Description: &wrapperspb.StringValue{Value: "default"},
|
|
Type: ldap.Subtype.String(),
|
|
Attrs: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
Urls: []string{"ldaps://ldap1"},
|
|
State: "active-private",
|
|
UserDn: wrapperspb.String("user-dn"),
|
|
UserAttr: wrapperspb.String("user-attr"),
|
|
UserFilter: wrapperspb.String("user-filter"),
|
|
},
|
|
},
|
|
Scope: defaultScopeInfo,
|
|
AuthorizedActions: ldapAuthorizedActions,
|
|
AuthorizedCollectionActions: authorizedCollectionActions,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "use-token-groups",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"attributes.use_token_groups"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
ScopeId: "something-new",
|
|
Attrs: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
UseTokenGroups: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
res: &pbs.UpdateAuthMethodResponse{
|
|
Item: &pb.AuthMethod{
|
|
ScopeId: o.GetPublicId(),
|
|
Version: 2,
|
|
Name: &wrapperspb.StringValue{Value: "default"},
|
|
Description: &wrapperspb.StringValue{Value: "default"},
|
|
Type: ldap.Subtype.String(),
|
|
Attrs: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
Urls: []string{"ldaps://ldap1"},
|
|
State: "active-private",
|
|
UseTokenGroups: true,
|
|
},
|
|
},
|
|
Scope: defaultScopeInfo,
|
|
AuthorizedActions: ldapAuthorizedActions,
|
|
AuthorizedCollectionActions: authorizedCollectionActions,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "enable-groups-err",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"attributes.enable_groups"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
ScopeId: "something-new",
|
|
Attrs: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
EnableGroups: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
res: nil,
|
|
wantErr: true,
|
|
errContains: "have a configured group_dn when enable_groups = true and use_token_groups = false",
|
|
},
|
|
{
|
|
name: "update-group-search-config",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"attributes.group_dn", "attributes.group_attr", "attributes.group_filter"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
Attrs: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
GroupDn: wrapperspb.String("group-dn"),
|
|
GroupAttr: wrapperspb.String("group-attr"),
|
|
GroupFilter: wrapperspb.String("group-filter"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
res: &pbs.UpdateAuthMethodResponse{
|
|
Item: &pb.AuthMethod{
|
|
ScopeId: o.GetPublicId(),
|
|
Version: 2,
|
|
Name: &wrapperspb.StringValue{Value: "default"},
|
|
Description: &wrapperspb.StringValue{Value: "default"},
|
|
Type: ldap.Subtype.String(),
|
|
Attrs: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
Urls: []string{"ldaps://ldap1"},
|
|
State: "active-private",
|
|
GroupDn: wrapperspb.String("group-dn"),
|
|
GroupAttr: wrapperspb.String("group-attr"),
|
|
GroupFilter: wrapperspb.String("group-filter"),
|
|
},
|
|
},
|
|
Scope: defaultScopeInfo,
|
|
AuthorizedActions: ldapAuthorizedActions,
|
|
AuthorizedCollectionActions: authorizedCollectionActions,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "no-change",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"attributes.certificates"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
Name: &wrapperspb.StringValue{Value: "default"},
|
|
},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.NotFound),
|
|
errContains: "no changes were made to the existing AuthMethod",
|
|
},
|
|
{
|
|
name: "valid-port-number",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"attributes.urls"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
Attrs: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
Urls: []string{"ldaps://ldap2:8156"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
res: &pbs.UpdateAuthMethodResponse{
|
|
Item: &pb.AuthMethod{
|
|
ScopeId: o.GetPublicId(),
|
|
Version: 2,
|
|
Name: &wrapperspb.StringValue{Value: "default"},
|
|
Description: &wrapperspb.StringValue{Value: "default"},
|
|
Type: ldap.Subtype.String(),
|
|
Attrs: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
Urls: []string{"ldaps://ldap2:8156"},
|
|
State: "active-private",
|
|
},
|
|
},
|
|
Scope: defaultScopeInfo,
|
|
AuthorizedActions: ldapAuthorizedActions,
|
|
AuthorizedCollectionActions: authorizedCollectionActions,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "invalid-port-number",
|
|
req: &pbs.UpdateAuthMethodRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"attributes.urls"},
|
|
},
|
|
Item: &pb.AuthMethod{
|
|
Attrs: &pb.AuthMethod_LdapAuthMethodsAttributes{
|
|
LdapAuthMethodsAttributes: &pb.LdapAuthMethodAttributes{
|
|
Urls: []string{"ldaps://ldap2:9999999"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
errContains: "port 9999999 in url ldaps://ldap2:9999999 is not valid",
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
am, cleanup := freshAuthMethod(t, tc.newAttrsOverride)
|
|
defer cleanup()
|
|
|
|
tc.req.Item.Version = am.GetVersion()
|
|
|
|
if tc.req.GetId() == "" {
|
|
tc.req.Id = am.GetId()
|
|
}
|
|
|
|
if tc.res != nil && tc.res.Item != nil {
|
|
tc.res.Item.Id = am.GetId()
|
|
tc.res.Item.CreatedTime = am.GetCreatedTime()
|
|
}
|
|
got, gErr := tested.UpdateAuthMethod(auth.DisabledAuthTestContext(iamRepoFn, o.GetPublicId()), tc.req)
|
|
// TODO: When handlers move to domain errors remove wantErr and rely errors.Match here.
|
|
if tc.err != nil || tc.wantErr {
|
|
require.Error(gErr)
|
|
if tc.err != nil {
|
|
assert.True(errors.Is(gErr, tc.err), "UpdateAuthMethod(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
|
}
|
|
if tc.errContains != "" {
|
|
assert.Contains(gErr.Error(), tc.errContains)
|
|
}
|
|
return
|
|
}
|
|
require.NoError(gErr)
|
|
if tc.res == nil {
|
|
require.Nil(got)
|
|
}
|
|
cmpOptions := []cmp.Option{
|
|
protocmp.Transform(),
|
|
cmpopts.SortSlices(func(a, b string) bool {
|
|
return a < b
|
|
}),
|
|
cmpopts.SortSlices(func(a, b protocmp.Message) bool {
|
|
return a.String() < b.String()
|
|
}),
|
|
}
|
|
if got != nil {
|
|
assert.NotNilf(tc.res, "Expected UpdateAuthMethod response to be nil, but was %v", got)
|
|
gotUpdateTime := got.GetItem().GetUpdatedTime().AsTime()
|
|
created := am.GetCreatedTime().AsTime()
|
|
|
|
// Verify it is a auth_method updated after it was created
|
|
assert.True(gotUpdateTime.After(created), "Updated auth_method should have been updated after it's creation. Was updated %v, which is after %v", gotUpdateTime, created)
|
|
|
|
// Ignore all values which are hard to compare against.
|
|
cmpOptions = append(
|
|
cmpOptions,
|
|
protocmp.IgnoreFields(&pb.AuthMethod{}, "updated_time"),
|
|
protocmp.SortRepeatedFields(&pb.LdapAuthMethodAttributes{}, "account_attribute_maps", "urls", "certificates"),
|
|
protocmp.IgnoreFields(&pb.LdapAuthMethodAttributes{}, "bind_password_hmac", "client_certificate_key_hmac"),
|
|
)
|
|
assert.NotEqual("bind_password", got.Item.GetLdapAuthMethodsAttributes().GetBindPasswordHmac())
|
|
assert.NotEqual("client_certificate_key", got.Item.GetLdapAuthMethodsAttributes().GetClientCertificateKeyHmac())
|
|
}
|
|
assert.Empty(cmp.Diff(got, tc.res, cmpOptions...), "UpdateAuthMethod(%q) got response %q, wanted %q", tc.req, got, tc.res)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAuthenticate_Ldap(t *testing.T) {
|
|
t.Parallel()
|
|
testCtx := context.Background()
|
|
testConn, _ := db.TestSetup(t, "postgres")
|
|
testRw := db.New(testConn)
|
|
testRootWrapper := db.TestWrapper(t)
|
|
testKms := kms.TestKms(t, testConn, testRootWrapper)
|
|
o, _ := iam.TestScopes(t, iam.TestRepo(t, testConn, testRootWrapper))
|
|
opt := event.TestWithObservationSink(t)
|
|
c := event.TestEventerConfig(t, "Test_StartAuth_to_Callback", opt)
|
|
testLock := &sync.Mutex{}
|
|
testLogger := hclog.New(&hclog.LoggerOptions{
|
|
Mutex: testLock,
|
|
Name: "test",
|
|
})
|
|
c.EventerConfig.TelemetryEnabled = true
|
|
require.NoError(t, event.InitSysEventer(testLogger, testLock, "use-Test_Authenticate", event.WithEventerConfig(&c.EventerConfig)))
|
|
iamRepoFn := func() (*iam.Repository, error) {
|
|
return iam.TestRepo(t, testConn, testRootWrapper), nil
|
|
}
|
|
oidcRepoFn := func() (*oidc.Repository, error) {
|
|
return oidc.NewRepository(testCtx, testRw, testRw, testKms)
|
|
}
|
|
ldapRepoFn := func() (*ldap.Repository, error) {
|
|
return ldap.NewRepository(testCtx, testRw, testRw, testKms)
|
|
}
|
|
pwRepoFn := func() (*password.Repository, error) {
|
|
return password.NewRepository(testCtx, testRw, testRw, testKms)
|
|
}
|
|
atRepoFn := func() (*authtoken.Repository, error) {
|
|
return authtoken.NewRepository(testCtx, testRw, testRw, testKms)
|
|
}
|
|
authMethodRepoFn := func() (*am.AuthMethodRepository, error) {
|
|
return am.NewAuthMethodRepository(testCtx, testRw, testRw, testKms)
|
|
}
|
|
|
|
orgDbWrapper, err := testKms.GetWrapper(testCtx, o.GetPublicId(), kms.KeyPurposeDatabase)
|
|
require.NoError(t, err)
|
|
logger := hclog.New(&hclog.LoggerOptions{
|
|
Name: "test-logger",
|
|
Level: hclog.Error,
|
|
})
|
|
td := testdirectory.Start(t,
|
|
testdirectory.WithDefaults(t, &testdirectory.Defaults{AllowAnonymousBind: true}),
|
|
testdirectory.WithLogger(t, logger),
|
|
)
|
|
groups := []*gldap.Entry{
|
|
testdirectory.NewGroup(t, "admin", []string{"alice"}),
|
|
testdirectory.NewGroup(t, "users", []string{"alice"}),
|
|
}
|
|
users := testdirectory.NewUsers(t, []string{"alice"}, testdirectory.WithMembersOf(t, "admin", "users"))
|
|
users2 := testdirectory.NewUsers(t, []string{"bob"})
|
|
td.SetUsers(append(users, users2...)...)
|
|
td.SetGroups(groups...)
|
|
|
|
tdCerts, err := ldap.ParseCertificates(testCtx, td.Cert())
|
|
require.NoError(t, err)
|
|
|
|
testAm := ldap.TestAuthMethod(t, testConn, orgDbWrapper, o.PublicId,
|
|
[]string{fmt.Sprintf("ldaps://%s:%d", td.Host(), td.Port())},
|
|
ldap.WithCertificates(testCtx, tdCerts...),
|
|
ldap.WithDiscoverDn(testCtx),
|
|
ldap.WithEnableGroups(testCtx),
|
|
ldap.WithUserDn(testCtx, testdirectory.DefaultUserDN),
|
|
ldap.WithGroupDn(testCtx, testdirectory.DefaultGroupDN),
|
|
)
|
|
|
|
iam.TestSetPrimaryAuthMethod(t, iam.TestRepo(t, testConn, testRootWrapper), o, testAm.PublicId)
|
|
|
|
testManagedGrp := ldap.TestManagedGroup(t, testConn, testAm, []string{"cn=admin,ou=groups,dc=example,dc=org"})
|
|
|
|
const (
|
|
testLoginName = "alice"
|
|
testPassword = "password"
|
|
testLoginName2 = "bob"
|
|
)
|
|
|
|
testAcct := ldap.TestAccount(t, testConn, testAm, testLoginName)
|
|
|
|
tests := []struct {
|
|
name string
|
|
acctId string
|
|
request *pbs.AuthenticateRequest
|
|
actions []string
|
|
wantType string
|
|
wantGroups []string
|
|
wantErr error
|
|
wantErrContains string
|
|
}{
|
|
{
|
|
name: "basic-with-groups",
|
|
acctId: testAcct.PublicId,
|
|
request: &pbs.AuthenticateRequest{
|
|
AuthMethodId: testAm.GetPublicId(),
|
|
TokenType: "token",
|
|
Attrs: &pbs.AuthenticateRequest_LdapLoginAttributes{
|
|
LdapLoginAttributes: &pbs.LdapLoginAttributes{
|
|
LoginName: testLoginName,
|
|
Password: testPassword,
|
|
},
|
|
},
|
|
},
|
|
wantGroups: []string{testManagedGrp.PublicId},
|
|
wantType: "token",
|
|
},
|
|
{
|
|
name: "basic-without-groups",
|
|
request: &pbs.AuthenticateRequest{
|
|
AuthMethodId: testAm.GetPublicId(),
|
|
TokenType: "token",
|
|
Attrs: &pbs.AuthenticateRequest_LdapLoginAttributes{
|
|
LdapLoginAttributes: &pbs.LdapLoginAttributes{
|
|
LoginName: testLoginName2,
|
|
Password: testPassword,
|
|
},
|
|
},
|
|
},
|
|
wantType: "token",
|
|
},
|
|
{
|
|
name: "cookie-type",
|
|
acctId: testAcct.PublicId,
|
|
request: &pbs.AuthenticateRequest{
|
|
AuthMethodId: testAm.GetPublicId(),
|
|
TokenType: "cookie",
|
|
Attrs: &pbs.AuthenticateRequest_LdapLoginAttributes{
|
|
LdapLoginAttributes: &pbs.LdapLoginAttributes{
|
|
LoginName: testLoginName,
|
|
Password: testPassword,
|
|
},
|
|
},
|
|
},
|
|
wantType: "cookie",
|
|
},
|
|
{
|
|
name: "no-token-type",
|
|
acctId: testAcct.PublicId,
|
|
request: &pbs.AuthenticateRequest{
|
|
AuthMethodId: testAm.GetPublicId(),
|
|
Attrs: &pbs.AuthenticateRequest_LdapLoginAttributes{
|
|
LdapLoginAttributes: &pbs.LdapLoginAttributes{
|
|
LoginName: testLoginName,
|
|
Password: testPassword,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "bad-token-type",
|
|
request: &pbs.AuthenticateRequest{
|
|
AuthMethodId: testAm.GetPublicId(),
|
|
TokenType: "email",
|
|
Attrs: &pbs.AuthenticateRequest_LdapLoginAttributes{
|
|
LdapLoginAttributes: &pbs.LdapLoginAttributes{
|
|
LoginName: testLoginName,
|
|
Password: testPassword,
|
|
},
|
|
},
|
|
},
|
|
wantErr: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "no-authmethod",
|
|
request: &pbs.AuthenticateRequest{
|
|
Attrs: &pbs.AuthenticateRequest_LdapLoginAttributes{
|
|
LdapLoginAttributes: &pbs.LdapLoginAttributes{
|
|
LoginName: testLoginName,
|
|
Password: testPassword,
|
|
},
|
|
},
|
|
},
|
|
wantErr: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
},
|
|
{
|
|
name: "wrong-password",
|
|
request: &pbs.AuthenticateRequest{
|
|
AuthMethodId: testAm.GetPublicId(),
|
|
TokenType: "token",
|
|
Attrs: &pbs.AuthenticateRequest_LdapLoginAttributes{
|
|
LdapLoginAttributes: &pbs.LdapLoginAttributes{
|
|
LoginName: testLoginName,
|
|
Password: "wrong",
|
|
},
|
|
},
|
|
},
|
|
wantErr: handlers.ApiErrorWithCode(codes.Unauthenticated),
|
|
},
|
|
{
|
|
name: "wrong-login-name",
|
|
request: &pbs.AuthenticateRequest{
|
|
AuthMethodId: testAm.GetPublicId(),
|
|
TokenType: "token",
|
|
Attrs: &pbs.AuthenticateRequest_LdapLoginAttributes{
|
|
LdapLoginAttributes: &pbs.LdapLoginAttributes{
|
|
LoginName: "wrong",
|
|
Password: testPassword,
|
|
},
|
|
},
|
|
},
|
|
wantErr: handlers.ApiErrorWithCode(codes.Unauthenticated),
|
|
},
|
|
{
|
|
name: "no-attributes",
|
|
request: &pbs.AuthenticateRequest{
|
|
AuthMethodId: testAm.GetPublicId(),
|
|
TokenType: "token",
|
|
Attrs: &pbs.AuthenticateRequest_LdapLoginAttributes{},
|
|
},
|
|
wantErr: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
wantErrContains: `Details: {{name: "attributes", desc: "This is a required field."}}`,
|
|
},
|
|
{
|
|
name: "with-callback-action",
|
|
acctId: testAcct.PublicId,
|
|
actions: []string{"callback"},
|
|
request: &pbs.AuthenticateRequest{
|
|
AuthMethodId: testAm.GetPublicId(),
|
|
TokenType: "token",
|
|
Attrs: &pbs.AuthenticateRequest_LdapLoginAttributes{
|
|
LdapLoginAttributes: &pbs.LdapLoginAttributes{
|
|
LoginName: testLoginName,
|
|
Password: testPassword,
|
|
},
|
|
},
|
|
},
|
|
wantGroups: []string{testManagedGrp.PublicId},
|
|
wantErr: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
|
wantErrContains: `Details: {{name: "request_path", desc: "callback is not a valid action for this auth method."}}`,
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
s, err := authmethods.NewService(testCtx, testKms, pwRepoFn, oidcRepoFn, iamRepoFn, atRepoFn, ldapRepoFn, authMethodRepoFn, 1000)
|
|
require.NoError(err)
|
|
|
|
resp, err := s.Authenticate(auth.DisabledAuthTestContext(iamRepoFn, o.GetPublicId(), auth.WithActions(tc.actions)), tc.request)
|
|
if tc.wantErr != nil {
|
|
assert.Error(err)
|
|
assert.Truef(errors.Is(err, tc.wantErr), "Got %#v, wanted %#v", err, tc.wantErr)
|
|
if tc.wantErrContains != "" {
|
|
assert.Contains(err.Error(), tc.wantErrContains)
|
|
}
|
|
return
|
|
}
|
|
require.NoError(err)
|
|
|
|
aToken := resp.GetAuthTokenResponse()
|
|
assert.NotEmpty(aToken.GetId())
|
|
assert.NotEmpty(aToken.GetToken())
|
|
assert.True(strings.HasPrefix(aToken.GetToken(), aToken.GetId()))
|
|
assert.Equal(testAm.GetPublicId(), aToken.GetAuthMethodId())
|
|
assert.Equal(aToken.GetCreatedTime(), aToken.GetUpdatedTime())
|
|
assert.Equal(aToken.GetCreatedTime(), aToken.GetApproximateLastUsedTime())
|
|
assert.Equal(testAm.GetPublicId(), aToken.GetAuthMethodId())
|
|
assert.Equal(tc.wantType, resp.GetType())
|
|
sinkFileName := c.ObservationEvents.Name()
|
|
defer func() { _ = os.WriteFile(sinkFileName, nil, 0o666) }()
|
|
b, err := os.ReadFile(sinkFileName)
|
|
require.NoError(err)
|
|
gotRes := &cloudevents.Event{}
|
|
err = json.Unmarshal(b, gotRes)
|
|
require.NoErrorf(err, "json: %s", string(b))
|
|
details, ok := gotRes.Data.(map[string]any)["details"]
|
|
require.True(ok)
|
|
for _, key := range details.([]any) {
|
|
assert.Contains(key.(map[string]any)["payload"], "user_id")
|
|
assert.Contains(key.(map[string]any)["payload"], "auth_token_start")
|
|
assert.Contains(key.(map[string]any)["payload"], "auth_token_end")
|
|
}
|
|
// support testing for pre-provisioned accounts
|
|
if tc.acctId != "" {
|
|
assert.Equal(tc.acctId, aToken.GetAccountId())
|
|
}
|
|
|
|
names := ldap.TestGetAcctManagedGroups(t, testConn, aToken.GetAccountId())
|
|
if len(tc.wantGroups) > 0 {
|
|
assert.Equal(tc.wantGroups, names)
|
|
}
|
|
})
|
|
}
|
|
}
|