2020-08-10 17:15:02 -04:00
// Copyright (c) HashiCorp, Inc.
2020-12-23 11:00:00 -08:00
// SPDX-License-Identifier: BUSL-1.1
2023-02-02 17:15:17 -08:00
2021-04-20 20:33:56 -04:00
package iam_test
2020-06-04 20:05:08 -04:00
import (
"context"
"fmt"
2020-09-25 13:35:32 -04:00
"sort"
2020-06-04 20:05:08 -04:00
"testing"
"time"
2021-04-20 20:33:56 -04:00
"github.com/google/go-cmp/cmp"
2023-03-10 15:06:39 -05:00
"github.com/hashicorp/boundary/globals"
2021-04-20 20:33:56 -04:00
"github.com/hashicorp/boundary/internal/auth/oidc"
"github.com/hashicorp/boundary/internal/auth/password"
"github.com/hashicorp/boundary/internal/auth/store"
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/internal/db"
dbassert "github.com/hashicorp/boundary/internal/db/assert"
2020-11-05 16:54:03 -05:00
"github.com/hashicorp/boundary/internal/errors"
2021-04-20 20:33:56 -04:00
"github.com/hashicorp/boundary/internal/iam"
2020-09-25 13:35:32 -04:00
"github.com/hashicorp/boundary/internal/kms"
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/internal/oplog"
2021-07-14 17:37:40 -07:00
"github.com/hashicorp/go-secure-stdlib/strutil"
2021-04-20 20:33:56 -04:00
"github.com/hashicorp/go-uuid"
2020-06-04 20:05:08 -04:00
"github.com/stretchr/testify/assert"
2020-06-10 12:10:34 -04:00
"github.com/stretchr/testify/require"
2020-06-04 20:05:08 -04:00
"google.golang.org/protobuf/proto"
2021-04-20 20:33:56 -04:00
"google.golang.org/protobuf/testing/protocmp"
2020-06-04 20:05:08 -04:00
)
func TestRepository_CreateUser ( t * testing . T ) {
t . Parallel ()
2023-06-06 17:09:08 -04:00
ctx := context . Background ()
2020-07-06 10:15:01 -07:00
conn , _ := db . TestSetup ( t , "postgres" )
2020-06-04 20:05:08 -04:00
rw := db . New ( conn )
wrapper := db . TestWrapper ( t )
2021-04-20 20:33:56 -04:00
repo := iam . TestRepo ( t , conn , wrapper )
2020-06-10 12:10:34 -04:00
id := testId ( t )
2021-04-20 20:33:56 -04:00
org , _ := iam . TestScopes ( t , repo )
2020-06-04 20:05:08 -04:00
type args struct {
2021-04-20 20:33:56 -04:00
user * iam . User
opt [] iam . Option
2020-06-04 20:05:08 -04:00
}
tests := [] struct {
name string
args args
wantDup bool
wantErr bool
wantErrMsg string
}{
{
name : "valid" ,
args : args {
2021-04-20 20:33:56 -04:00
user : func () * iam . User {
2023-06-06 17:09:08 -04:00
u , err := iam . NewUser ( ctx , org . PublicId , iam . WithName ( "valid" + id ), iam . WithDescription ( id ))
2020-06-04 20:05:08 -04:00
assert . NoError ( t , err )
return u
}(),
},
wantErr : false ,
},
{
name : "bad-scope-id" ,
args : args {
2021-04-20 20:33:56 -04:00
user : func () * iam . User {
2023-06-06 17:09:08 -04:00
u , err := iam . NewUser ( ctx , id )
2020-06-04 20:05:08 -04:00
assert . NoError ( t , err )
return u
}(),
},
wantErr : true ,
2020-12-23 11:00:00 -08:00
wantErrMsg : "iam.(Repository).create: error getting metadata: iam.(Repository).stdMetadata: unable to get scope: iam.LookupScope: db.LookupWhere: record not found" ,
2020-06-04 20:05:08 -04:00
},
{
name : "dup-name" ,
args : args {
2021-04-20 20:33:56 -04:00
user : func () * iam . User {
2023-06-06 17:09:08 -04:00
u , err := iam . NewUser ( ctx , org . PublicId , iam . WithName ( "dup-name" + id ))
2020-06-04 20:05:08 -04:00
assert . NoError ( t , err )
return u
}(),
},
wantDup : true ,
wantErr : true ,
2020-12-23 11:00:00 -08:00
wantErrMsg : "iam.(Repository).CreateUser: user %s already exists in org %s: integrity violation: error #1002" ,
2020-06-04 20:05:08 -04:00
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
2020-06-10 12:10:34 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-06-04 20:05:08 -04:00
if tt . wantDup {
2023-06-06 17:09:08 -04:00
dup , err := repo . CreateUser ( ctx , tt . args . user , tt . args . opt ... )
2020-06-10 12:10:34 -04:00
require . NoError ( err )
require . NotNil ( dup )
2020-06-04 20:05:08 -04:00
}
2023-06-06 17:09:08 -04:00
u , err := repo . CreateUser ( ctx , tt . args . user , tt . args . opt ... )
2020-06-04 20:05:08 -04:00
if tt . wantErr {
2020-06-10 12:10:34 -04:00
require . Error ( err )
2020-06-04 20:05:08 -04:00
assert . Nil ( u )
switch tt . name {
case "dup-name" :
2020-09-23 08:10:27 -07:00
assert . Contains ( err . Error (), fmt . Sprintf ( tt . wantErrMsg , "dup-name" + id , org . PublicId ))
2020-06-04 20:05:08 -04:00
default :
2020-11-05 16:54:03 -05:00
assert . Contains ( err . Error (), tt . wantErrMsg )
2020-06-04 20:05:08 -04:00
}
return
}
2020-06-10 12:10:34 -04:00
require . NoError ( err )
2020-06-04 20:05:08 -04:00
assert . NotNil ( u . CreateTime )
assert . NotNil ( u . UpdateTime )
2023-06-06 17:09:08 -04:00
foundUser , _ , err := repo . LookupUser ( ctx , u . PublicId )
2020-06-10 12:10:34 -04:00
require . NoError ( err )
2020-06-04 20:05:08 -04:00
assert . True ( proto . Equal ( foundUser , u ))
err = db . TestVerifyOplog ( t , rw , u . PublicId , db . WithOperation ( oplog . OpType_OP_TYPE_CREATE ), db . WithCreateNotBefore ( 10 * time . Second ))
assert . NoError ( err )
})
}
}
2021-04-21 19:10:54 -04:00
// TestRepository_LookupUser_WithDifferentPrimaryAuthMethods ensures that the
// when different auth method types are primary, the correct primary account
// info is returned from Repository.LookupUser(...)
func TestRepository_LookupUser_WithDifferentPrimaryAuthMethods ( t * testing . T ) {
t . Parallel ()
ctx := context . Background ()
conn , _ := db . TestSetup ( t , "postgres" )
wrapper := db . TestWrapper ( t )
kmsCache := kms . TestKms ( t , conn , wrapper )
repo := iam . TestRepo ( t , conn , wrapper )
org , _ := iam . TestScopes ( t , repo )
2023-06-06 17:09:08 -04:00
databaseWrapper , err := kmsCache . GetWrapper ( ctx , org . PublicId , kms . KeyPurposeDatabase )
2021-04-21 19:10:54 -04:00
require . NoError ( t , err )
var accountIds [] string
oidcAm := oidc . TestAuthMethod ( t , conn , databaseWrapper , org . PublicId , oidc . ActivePrivateState , "alice-rp" , "fido" ,
oidc . WithIssuer ( oidc . TestConvertToUrls ( t , "https://alice.com" )[ 0 ]),
oidc . WithSigningAlgs ( oidc . RS256 ),
oidc . WithApiUrl ( oidc . TestConvertToUrls ( t , "http://localhost" )[ 0 ]))
aa := oidc . TestAccount ( t , conn , oidcAm , "alice" , oidc . WithFullName ( "alice eve smith" ), oidc . WithEmail ( "alice@example.com" ))
accountIds = append ( accountIds , aa . PublicId )
pwAms := password . TestAuthMethods ( t , conn , org . PublicId , 1 )
require . Equal ( t , 1 , len ( pwAms ))
pwAcct := password . TestAccount ( t , conn , pwAms [ 0 ]. PublicId , "want-login-name" )
accountIds = append ( accountIds , pwAcct . PublicId )
u := iam . TestUser ( t , repo , org . PublicId )
2023-06-06 17:09:08 -04:00
newAccts , err := repo . AddUserAccounts ( ctx , u . PublicId , u . Version , accountIds )
2021-04-21 19:10:54 -04:00
require . NoError ( t , err )
sort . Strings ( newAccts )
require . Equal ( t , accountIds , newAccts )
tests := [] struct {
name string
primaryAuthMethodId string
wantLoginName string
wantPrimaryAcctId string
wantFullName string
wantEmail string
}{
{
name : "oidc" ,
primaryAuthMethodId : oidcAm . PublicId ,
wantLoginName : "alice" ,
wantPrimaryAcctId : aa . PublicId ,
wantFullName : "alice eve smith" ,
wantEmail : "alice@example.com" ,
},
{
name : "password" ,
primaryAuthMethodId : pwAms [ 0 ]. PublicId ,
wantLoginName : "want-login-name" ,
wantPrimaryAcctId : pwAcct . PublicId ,
wantFullName : "" ,
wantEmail : "" ,
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
s , err := repo . LookupScope ( context . Background (), org . PublicId )
require . NoError ( err )
iam . TestSetPrimaryAuthMethod ( t , repo , s , tt . primaryAuthMethodId )
got , gotAccts , err := repo . LookupUser ( ctx , u . PublicId )
require . NoError ( err )
sort . Strings ( gotAccts )
assert . Equal ( accountIds , gotAccts )
assert . Equal ( tt . wantLoginName , got . LoginName )
assert . Equal ( tt . wantPrimaryAcctId , got . PrimaryAccountId )
assert . Equal ( tt . wantFullName , got . FullName )
assert . Equal ( tt . wantEmail , got . Email )
})
}
}
2021-04-23 13:19:28 -04:00
2020-06-04 20:05:08 -04:00
func TestRepository_UpdateUser ( t * testing . T ) {
t . Parallel ()
2021-09-30 09:17:27 -04:00
ctx := context . Background ()
2020-07-06 10:15:01 -07:00
conn , _ := db . TestSetup ( t , "postgres" )
2020-06-04 20:05:08 -04:00
rw := db . New ( conn )
wrapper := db . TestWrapper ( t )
2021-04-20 20:33:56 -04:00
kmsCache := kms . TestKms ( t , conn , wrapper )
repo := iam . TestRepo ( t , conn , wrapper )
2020-06-10 12:10:34 -04:00
id := testId ( t )
2021-04-20 20:33:56 -04:00
org , proj := iam . TestScopes ( t , repo )
databaseWrapper , err := kmsCache . GetWrapper ( context . Background (), org . PublicId , kms . KeyPurposeDatabase )
require . NoError ( t , err )
2020-06-04 20:05:08 -04:00
pubId := func ( s string ) * string { return & s }
type args struct {
name string
description string
fieldMaskPaths [] string
2021-04-20 20:33:56 -04:00
opt [] iam . Option
2020-06-04 20:05:08 -04:00
ScopeId string
PublicId * string
}
tests := [] struct {
name string
2021-04-20 20:33:56 -04:00
newUserOpts [] iam . Option
2020-06-04 20:05:08 -04:00
args args
wantRowsUpdate int
wantErr bool
wantErrMsg string
2020-12-21 10:07:34 -08:00
wantIsErr errors . Code
2020-06-04 20:05:08 -04:00
wantDup bool
}{
{
name : "valid" ,
args : args {
name : "valid" + id ,
fieldMaskPaths : [] string { "Name" },
ScopeId : org . PublicId ,
},
wantErr : false ,
wantRowsUpdate : 1 ,
},
{
name : "valid-no-op" ,
args : args {
name : "valid-no-op" + id ,
fieldMaskPaths : [] string { "Name" },
ScopeId : org . PublicId ,
},
2021-04-20 20:33:56 -04:00
newUserOpts : [] iam . Option { iam . WithName ( "valid-no-op" + id )},
2020-06-04 20:05:08 -04:00
wantErr : false ,
wantRowsUpdate : 1 ,
},
{
name : "not-found" ,
args : args {
name : "not-found" + id ,
fieldMaskPaths : [] string { "Name" },
ScopeId : org . PublicId ,
PublicId : func () * string { s := "1" ; return & s }(),
},
wantErr : true ,
wantRowsUpdate : 0 ,
2022-01-05 14:38:38 -05:00
wantErrMsg : "iam.(Repository).UpdateUser: db.Update: record not found, search issue: error #1100" ,
2020-12-21 10:07:34 -08:00
wantIsErr : errors . RecordNotFound ,
2020-06-04 20:05:08 -04:00
},
{
name : "null-name" ,
args : args {
name : "" ,
fieldMaskPaths : [] string { "Name" },
ScopeId : org . PublicId ,
},
2021-04-20 20:33:56 -04:00
newUserOpts : [] iam . Option { iam . WithName ( "null-name" + id )},
2020-06-04 20:05:08 -04:00
wantErr : false ,
wantRowsUpdate : 1 ,
},
{
name : "null-description" ,
args : args {
name : "" ,
fieldMaskPaths : [] string { "Description" },
ScopeId : org . PublicId ,
},
2021-04-20 20:33:56 -04:00
newUserOpts : [] iam . Option { iam . WithDescription ( "null-description" + id )},
2020-06-04 20:05:08 -04:00
wantErr : false ,
wantRowsUpdate : 1 ,
},
{
name : "empty-field-mask" ,
args : args {
name : "valid" + id ,
fieldMaskPaths : [] string {},
ScopeId : org . PublicId ,
},
wantErr : true ,
wantRowsUpdate : 0 ,
2020-12-23 11:00:00 -08:00
wantErrMsg : "iam.(Repository).UpdateUser: empty field mask, parameter violation: error #104" ,
2020-06-04 20:05:08 -04:00
},
{
name : "nil-fieldmask" ,
args : args {
name : "valid" + id ,
fieldMaskPaths : nil ,
ScopeId : org . PublicId ,
},
wantErr : true ,
wantRowsUpdate : 0 ,
2020-12-23 11:00:00 -08:00
wantErrMsg : "iam.(Repository).UpdateUser: empty field mask, parameter violation: error #104" ,
2020-06-04 20:05:08 -04:00
},
{
name : "read-only-fields" ,
args : args {
name : "valid" + id ,
fieldMaskPaths : [] string { "CreateTime" },
ScopeId : org . PublicId ,
},
wantErr : true ,
wantRowsUpdate : 0 ,
2020-12-23 11:00:00 -08:00
wantErrMsg : "iam.(Repository).UpdateUser: invalid field mask: CreateTime: parameter violation: error #103" ,
2020-06-04 20:05:08 -04:00
},
{
name : "unknown-fields" ,
args : args {
name : "valid" + id ,
fieldMaskPaths : [] string { "Alice" },
ScopeId : org . PublicId ,
},
wantErr : true ,
wantRowsUpdate : 0 ,
2020-12-23 11:00:00 -08:00
wantErrMsg : "iam.(Repository).UpdateUser: invalid field mask: Alice: parameter violation: error #103" ,
2020-06-04 20:05:08 -04:00
},
{
name : "no-public-id" ,
args : args {
name : "valid" + id ,
fieldMaskPaths : [] string { "Name" },
ScopeId : org . PublicId ,
PublicId : pubId ( "" ),
},
wantErr : true ,
2020-12-23 11:00:00 -08:00
wantErrMsg : "iam.(Repository).UpdateUser: missing public id: parameter violation: error #100" ,
2020-06-04 20:05:08 -04:00
wantRowsUpdate : 0 ,
},
{
name : "proj-scope-id" ,
args : args {
name : "proj-scope-id" + id ,
fieldMaskPaths : [] string { "ScopeId" },
ScopeId : proj . PublicId ,
},
wantErr : true ,
2020-12-23 11:00:00 -08:00
wantErrMsg : "iam.(Repository).UpdateUser: invalid field mask: ScopeId: parameter violation: error #103" ,
2020-06-04 20:05:08 -04:00
},
{
name : "empty-scope-id-with-name-mask" ,
args : args {
name : "empty-scope-id" + id ,
fieldMaskPaths : [] string { "Name" },
ScopeId : "" ,
},
wantErr : false ,
wantRowsUpdate : 1 ,
},
{
name : "dup-name" ,
args : args {
name : "dup-name" + id ,
fieldMaskPaths : [] string { "Name" },
ScopeId : org . PublicId ,
},
wantErr : true ,
wantDup : true ,
2020-12-23 11:00:00 -08:00
wantErrMsg : `iam.(Repository).UpdateUser: user %s already exists in org %s: integrity violation: error #1002` ,
2020-06-04 20:05:08 -04:00
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
2020-06-10 12:10:34 -04:00
assert , require := assert . New ( t ), require . New ( t )
2021-04-20 20:33:56 -04:00
// we need to clean out any auth methods
_ , err = rw . Exec ( context . Background (), "delete from auth_method" , nil )
require . NoError ( err )
2020-06-04 20:05:08 -04:00
if tt . wantDup {
2021-04-20 20:33:56 -04:00
u := iam . TestUser ( t , repo , org . PublicId , tt . newUserOpts ... )
2020-06-04 20:05:08 -04:00
u . Name = tt . args . name
2020-09-25 13:35:32 -04:00
_ , _ , _ , err := repo . UpdateUser ( context . Background (), u , 1 , tt . args . fieldMaskPaths , tt . args . opt ... )
2020-06-10 12:10:34 -04:00
require . NoError ( err )
2020-06-04 20:05:08 -04:00
}
2021-04-20 20:33:56 -04:00
u := iam . TestUser ( t , repo , org . PublicId , tt . newUserOpts ... )
acctCount := 10
2020-09-25 13:35:32 -04:00
accountIds := make ([] string , 0 , acctCount )
2021-04-20 20:33:56 -04:00
var wantEmail , wantFullName , wantPrimaryAccountId string
var authMethod * oidc . AuthMethod
2020-09-25 13:35:32 -04:00
for i := 0 ; i < acctCount ; i ++ {
2021-04-20 20:33:56 -04:00
switch i % 2 {
case 0 :
authMethod = oidc . TestAuthMethod ( t , conn , databaseWrapper , org . PublicId , oidc . ActivePrivateState , fmt . Sprintf ( "alice-rp-%d" , i ), "fido" ,
oidc . WithIssuer ( oidc . TestConvertToUrls ( t , "https://alice.com" )[ 0 ]),
oidc . WithSigningAlgs ( oidc . RS256 ),
oidc . WithApiUrl ( oidc . TestConvertToUrls ( t , "http://localhost" )[ 0 ]))
wantEmail , wantFullName = fmt . Sprintf ( "%s-%d@example.com" , tt . name , i ), fmt . Sprintf ( "%s-%d" , tt . name , i )
aa := oidc . TestAccount ( t , conn , authMethod , fmt . Sprintf ( tt . name , i ), oidc . WithFullName ( wantFullName ), oidc . WithEmail ( wantEmail ))
wantPrimaryAccountId = aa . PublicId
accountIds = append ( accountIds , aa . PublicId )
default :
pwAms := password . TestAuthMethods ( t , conn , org . PublicId , 1 )
require . Equal ( 1 , len ( pwAms ))
pwAcct := password . TestAccount ( t , conn , pwAms [ 0 ]. PublicId , "name1" )
accountIds = append ( accountIds , pwAcct . PublicId )
}
2020-09-25 13:35:32 -04:00
}
2021-04-20 20:33:56 -04:00
require . Equal ( acctCount , len ( accountIds ))
var s * iam . Scope
s , err = repo . LookupScope ( context . Background (), org . PublicId )
require . NoError ( err )
// we need a primary auth method, so let's just pick the last oidc
// one created.
iam . TestSetPrimaryAuthMethod ( t , repo , s , authMethod . PublicId )
2020-09-25 13:35:32 -04:00
sort . Strings ( accountIds )
2021-04-20 20:33:56 -04:00
if len ( accountIds ) > 0 {
newAccts , err := repo . AddUserAccounts ( context . Background (), u . PublicId , u . Version , accountIds )
require . NoError ( err )
sort . Strings ( newAccts )
require . Equal ( accountIds , newAccts )
u . Version ++
}
// we need to clean out any oplog entries added because we
// associated accounts to the test user
_ , err = rw . Exec ( context . Background (), "delete from oplog_entry" , nil )
require . NoError ( err )
2020-06-04 20:05:08 -04:00
2021-04-20 20:33:56 -04:00
updateUser := iam . AllocUser ()
2020-06-04 20:05:08 -04:00
updateUser . PublicId = u . PublicId
if tt . args . PublicId != nil {
updateUser . PublicId = * tt . args . PublicId
}
updateUser . ScopeId = tt . args . ScopeId
updateUser . Name = tt . args . name
updateUser . Description = tt . args . description
2021-04-20 20:33:56 -04:00
var userAfterUpdate * iam . User
2020-09-25 13:35:32 -04:00
var acctIdsAfterUpdate [] string
2020-07-08 17:02:01 -04:00
var updatedRows int
var err error
2021-04-20 20:33:56 -04:00
userAfterUpdate , acctIdsAfterUpdate , updatedRows , err = repo . UpdateUser ( context . Background (), & updateUser , u . Version , tt . args . fieldMaskPaths , tt . args . opt ... )
2020-06-04 20:05:08 -04:00
if tt . wantErr {
2020-06-10 12:10:34 -04:00
require . Error ( err )
2021-01-12 08:50:19 -08:00
assert . True ( errors . Match ( errors . T ( tt . wantIsErr ), err ))
2020-06-04 20:05:08 -04:00
assert . Nil ( userAfterUpdate )
assert . Equal ( 0 , updatedRows )
switch tt . name {
case "dup-name" :
assert . Equal ( fmt . Sprintf ( tt . wantErrMsg , "dup-name" + id , org . PublicId ), err . Error ())
default :
2020-07-10 19:01:23 -04:00
assert . Containsf ( err . Error (), tt . wantErrMsg , "unexpected error: %s" , err . Error ())
2020-06-04 20:05:08 -04:00
}
2021-04-20 20:33:56 -04:00
err = db . TestVerifyOplog ( t , rw , u . PublicId , db . WithOperation ( oplog . OpType_OP_TYPE_UPDATE ), db . WithCreateNotBefore ( 1 * time . Second ))
2020-06-10 12:10:34 -04:00
require . Error ( err )
2020-11-05 16:54:03 -05:00
assert . Contains ( err . Error (), "record not found" )
2020-06-04 20:05:08 -04:00
return
}
2020-06-10 12:10:34 -04:00
require . NoError ( err )
2020-06-04 20:05:08 -04:00
assert . Equal ( tt . wantRowsUpdate , updatedRows )
assert . NotEqual ( u . UpdateTime , userAfterUpdate . UpdateTime )
2020-09-25 13:35:32 -04:00
sort . Strings ( acctIdsAfterUpdate )
assert . Equal ( accountIds , acctIdsAfterUpdate )
2021-04-20 20:33:56 -04:00
assert . Equal ( wantFullName , userAfterUpdate . FullName )
assert . Equal ( wantEmail , userAfterUpdate . Email )
assert . Equal ( wantPrimaryAccountId , userAfterUpdate . PrimaryAccountId )
2020-09-25 13:35:32 -04:00
foundUser , foundAccountIds , err := repo . LookupUser ( context . Background (), u . PublicId )
2020-06-10 12:10:34 -04:00
require . NoError ( err )
2020-06-04 20:05:08 -04:00
assert . True ( proto . Equal ( userAfterUpdate , foundUser ))
2020-09-25 13:35:32 -04:00
sort . Strings ( foundAccountIds )
assert . Equal ( accountIds , foundAccountIds )
2021-09-30 09:17:27 -04:00
underlyingDB , err := conn . SqlDB ( ctx )
2021-09-30 08:47:34 -04:00
require . NoError ( err )
dbassert := dbassert . New ( t , underlyingDB )
2020-06-04 20:05:08 -04:00
if tt . args . name == "" {
dbassert . IsNull ( foundUser , "name" )
}
if tt . args . description == "" {
dbassert . IsNull ( foundUser , "description" )
}
err = db . TestVerifyOplog ( t , rw , u . PublicId , db . WithOperation ( oplog . OpType_OP_TYPE_UPDATE ), db . WithCreateNotBefore ( 10 * time . Second ))
assert . NoError ( err )
})
}
}
func TestRepository_DeleteUser ( t * testing . T ) {
t . Parallel ()
2023-06-06 17:09:08 -04:00
ctx := context . Background ()
2020-07-06 10:15:01 -07:00
conn , _ := db . TestSetup ( t , "postgres" )
2020-06-04 20:05:08 -04:00
rw := db . New ( conn )
wrapper := db . TestWrapper ( t )
2021-04-20 20:33:56 -04:00
repo := iam . TestRepo ( t , conn , wrapper )
org , _ := iam . TestScopes ( t , repo )
2020-06-04 20:05:08 -04:00
type args struct {
2021-04-20 20:33:56 -04:00
user * iam . User
opt [] iam . Option
2020-06-04 20:05:08 -04:00
}
tests := [] struct {
name string
args args
wantRowsDeleted int
wantErr bool
wantErrMsg string
}{
{
name : "valid" ,
args : args {
2021-04-20 20:33:56 -04:00
user : iam . TestUser ( t , repo , org . PublicId ),
2020-06-04 20:05:08 -04:00
},
wantRowsDeleted : 1 ,
wantErr : false ,
},
{
name : "no-public-id" ,
args : args {
2021-04-20 20:33:56 -04:00
user : func () * iam . User {
u := iam . AllocUser ()
2020-06-04 20:05:08 -04:00
return & u
}(),
},
wantRowsDeleted : 0 ,
wantErr : true ,
2020-12-23 11:00:00 -08:00
wantErrMsg : "iam.(Repository).DeleteUser: missing public id: parameter violation: error #100" ,
2020-06-04 20:05:08 -04:00
},
{
name : "not-found" ,
args : args {
2021-04-20 20:33:56 -04:00
user : func () * iam . User {
2023-06-06 17:09:08 -04:00
u , err := iam . NewUser ( ctx , org . PublicId )
2020-06-10 12:10:34 -04:00
require . NoError ( t , err )
2023-06-06 17:09:08 -04:00
id , err := db . NewPublicId ( ctx , globals . UserPrefix )
2020-06-10 12:10:34 -04:00
require . NoError ( t , err )
2020-06-04 20:05:08 -04:00
u . PublicId = id
return u
}(),
},
wantRowsDeleted : 1 ,
wantErr : true ,
2020-12-23 11:00:00 -08:00
wantErrMsg : "db.LookupById: record not found, search issue: error #1100" ,
2020-06-04 20:05:08 -04:00
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
2020-06-10 12:10:34 -04:00
assert , require := assert . New ( t ), require . New ( t )
2023-06-06 17:09:08 -04:00
deletedRows , err := repo . DeleteUser ( ctx , tt . args . user . PublicId , tt . args . opt ... )
2020-06-04 20:05:08 -04:00
if tt . wantErr {
2020-06-10 12:10:34 -04:00
require . Error ( err )
2020-06-04 20:05:08 -04:00
assert . Equal ( 0 , deletedRows )
2020-11-05 16:54:03 -05:00
assert . Contains ( err . Error (), tt . wantErrMsg )
2020-06-04 20:05:08 -04:00
err = db . TestVerifyOplog ( t , rw , tt . args . user . PublicId , db . WithOperation ( oplog . OpType_OP_TYPE_DELETE ), db . WithCreateNotBefore ( 10 * time . Second ))
2020-06-10 12:10:34 -04:00
require . Error ( err )
2020-11-05 16:54:03 -05:00
assert . Contains ( err . Error (), "record not found" )
2020-06-04 20:05:08 -04:00
return
}
2020-06-10 12:10:34 -04:00
require . NoError ( err )
2020-06-04 20:05:08 -04:00
assert . Equal ( tt . wantRowsDeleted , deletedRows )
2023-06-06 17:09:08 -04:00
foundUser , _ , err := repo . LookupUser ( ctx , tt . args . user . PublicId )
2020-09-04 14:48:11 -07:00
require . NoError ( err )
2020-06-04 20:05:08 -04:00
assert . Nil ( foundUser )
err = db . TestVerifyOplog ( t , rw , tt . args . user . PublicId , db . WithOperation ( oplog . OpType_OP_TYPE_DELETE ), db . WithCreateNotBefore ( 10 * time . Second ))
2020-06-10 12:10:34 -04:00
require . NoError ( err )
2020-06-04 20:05:08 -04:00
})
}
}
2020-06-16 18:59:30 -04:00
func TestRepository_ListUsers ( t * testing . T ) {
t . Parallel ()
2020-07-06 10:15:01 -07:00
conn , _ := db . TestSetup ( t , "postgres" )
2020-06-16 18:59:30 -04:00
const testLimit = 10
wrapper := db . TestWrapper ( t )
2021-04-20 20:33:56 -04:00
kmsCache := kms . TestKms ( t , conn , wrapper )
repo := iam . TestRepo ( t , conn , wrapper , iam . WithLimit ( testLimit ))
org , _ := iam . TestScopes ( t , repo )
databaseWrapper , err := kmsCache . GetWrapper ( context . Background (), org . PublicId , kms . KeyPurposeDatabase )
require . NoError ( t , err )
authMethod := oidc . TestAuthMethod ( t , conn , databaseWrapper , org . PublicId , oidc . ActivePrivateState , "alice-rp" , "fido" ,
oidc . WithIssuer ( oidc . TestConvertToUrls ( t , "https://alice.com" )[ 0 ]),
oidc . WithSigningAlgs ( oidc . RS256 ),
oidc . WithApiUrl ( oidc . TestConvertToUrls ( t , "http://localhost" )[ 0 ]))
iam . TestSetPrimaryAuthMethod ( t , repo , org , authMethod . PublicId )
2020-06-16 18:59:30 -04:00
type args struct {
2020-07-11 12:00:49 -04:00
withOrgId string
2021-04-20 20:33:56 -04:00
opt [] iam . Option
2020-06-16 18:59:30 -04:00
}
tests := [] struct {
name string
createCnt int
args args
wantCnt int
wantErr bool
}{
{
2023-12-20 16:51:57 -08:00
name : "negative-limit" ,
2021-04-20 20:33:56 -04:00
createCnt : testLimit + 1 ,
2020-06-16 18:59:30 -04:00
args : args {
2020-07-11 12:00:49 -04:00
withOrgId : org . PublicId ,
2021-04-20 20:33:56 -04:00
opt : [] iam . Option { iam . WithLimit ( - 1 )},
2020-06-16 18:59:30 -04:00
},
2023-12-20 16:51:57 -08:00
wantErr : true ,
2020-06-16 18:59:30 -04:00
},
{
name : "default-limit" ,
2021-04-20 20:33:56 -04:00
createCnt : testLimit + 1 ,
2020-06-16 18:59:30 -04:00
args : args {
2020-07-11 12:00:49 -04:00
withOrgId : org . PublicId ,
2020-06-16 18:59:30 -04:00
},
2021-04-20 20:33:56 -04:00
wantCnt : testLimit ,
2020-06-16 18:59:30 -04:00
wantErr : false ,
},
{
name : "custom-limit" ,
2021-04-20 20:33:56 -04:00
createCnt : testLimit + 1 ,
2020-06-16 18:59:30 -04:00
args : args {
2020-07-11 12:00:49 -04:00
withOrgId : org . PublicId ,
2021-04-20 20:33:56 -04:00
opt : [] iam . Option { iam . WithLimit ( 3 )},
2020-06-16 18:59:30 -04:00
},
wantCnt : 3 ,
wantErr : false ,
},
{
name : "bad-org" ,
createCnt : 1 ,
args : args {
2020-07-11 12:00:49 -04:00
withOrgId : "bad-id" ,
2020-06-16 18:59:30 -04:00
},
wantCnt : 0 ,
wantErr : false ,
},
}
2021-04-20 20:33:56 -04:00
type userInfo struct {
email string
fullName string
primaryAcctId string
}
2020-06-16 18:59:30 -04:00
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
2022-10-25 18:29:38 -05:00
db . TestDeleteWhere ( t , conn , func () any { u := iam . AllocUser (); return & u }(), "public_id != 'u_anon' and public_id != 'u_auth' and public_id != 'u_recovery'" )
2021-04-20 20:33:56 -04:00
testUsers := [] * iam . User {}
wantUserInfo := map [ string ] userInfo {}
2020-06-16 18:59:30 -04:00
for i := 0 ; i < tt . createCnt ; i ++ {
2021-04-20 20:33:56 -04:00
u := iam . TestUser ( t , repo , org . PublicId )
testUsers = append ( testUsers , u )
wantEmail , wantFullName := fmt . Sprintf ( "%s-%d@example.com" , tt . name , i ), fmt . Sprintf ( "%s-%d" , tt . name , i )
a := oidc . TestAccount ( t , conn , authMethod , fmt . Sprintf ( tt . name , i ), oidc . WithFullName ( wantFullName ), oidc . WithEmail ( wantEmail ))
wantUserInfo [ u . PublicId ] = userInfo { email : wantEmail , fullName : wantFullName , primaryAcctId : a . PublicId }
_ , err := repo . AddUserAccounts ( context . Background (), u . PublicId , u . Version , [] string { a . PublicId })
require . NoError ( err )
pwAms := password . TestAuthMethods ( t , conn , org . PublicId , 1 )
require . Equal ( 1 , len ( pwAms ))
pwAcct := password . TestAccount ( t , conn , pwAms [ 0 ]. PublicId , "name1" )
_ , err = repo . AddUserAccounts ( context . Background (), u . PublicId , u . Version + 1 , [] string { pwAcct . PublicId })
require . NoError ( err )
2020-06-16 18:59:30 -04:00
}
assert . Equal ( tt . createCnt , len ( testUsers ))
2023-12-20 16:51:57 -08:00
got , _ , err := repo . ListUsers ( context . Background (), [] string { tt . args . withOrgId }, tt . args . opt ... )
2020-06-16 18:59:30 -04:00
if tt . wantErr {
require . Error ( err )
return
}
require . NoError ( err )
assert . Equal ( tt . wantCnt , len ( got ))
2021-04-20 20:33:56 -04:00
for _ , u := range got {
assert . Equal ( wantUserInfo [ u . PublicId ], userInfo {
email : u . Email ,
fullName : u . FullName ,
primaryAcctId : u . PrimaryAccountId ,
})
}
2020-06-16 18:59:30 -04:00
})
}
}
2020-06-29 08:18:57 -04:00
2021-01-25 19:37:40 -05:00
func TestRepository_ListUsers_Multiple_Scopes ( t * testing . T ) {
t . Parallel ()
conn , _ := db . TestSetup ( t , "postgres" )
wrapper := db . TestWrapper ( t )
2021-04-20 20:33:56 -04:00
repo := iam . TestRepo ( t , conn , wrapper )
org , _ := iam . TestScopes ( t , repo )
2021-01-25 19:37:40 -05:00
2023-12-20 16:51:57 -08:00
t . Cleanup ( func () {
db . TestDeleteWhere ( t , conn , func () any { i := iam . AllocUser (); return & i }(), "public_id != 'u_anon' and public_id != 'u_auth' and public_id != 'u_recovery'" )
})
2021-01-25 19:37:40 -05:00
const numPerScope = 10
2024-01-23 14:33:09 -05:00
total := 3 // anon, auth, recovery
2021-01-25 19:37:40 -05:00
for i := 0 ; i < numPerScope ; i ++ {
2021-04-20 20:33:56 -04:00
iam . TestUser ( t , repo , "global" )
2021-01-25 19:37:40 -05:00
total ++
2021-04-20 20:33:56 -04:00
iam . TestUser ( t , repo , org . GetPublicId ())
2021-01-25 19:37:40 -05:00
total ++
}
2023-12-20 16:51:57 -08:00
got , ttime , err := repo . ListUsers ( context . Background (), [] string { "global" , org . GetPublicId ()})
2021-01-25 19:37:40 -05:00
require . NoError ( t , err )
assert . Equal ( t , total , len ( got ))
2023-12-20 16:51:57 -08:00
// Transaction timestamp should be within ~10 seconds of now
assert . True ( t , time . Now (). Before ( ttime . Add ( 10 * time . Second )))
assert . True ( t , time . Now (). After ( ttime . Add ( - 10 * time . Second )))
2021-01-25 19:37:40 -05:00
}
2020-06-29 08:18:57 -04:00
func TestRepository_LookupUserWithLogin ( t * testing . T ) {
t . Parallel ()
2021-09-30 09:17:27 -04:00
ctx := context . Background ()
2020-07-06 10:15:01 -07:00
conn , _ := db . TestSetup ( t , "postgres" )
2020-06-29 08:18:57 -04:00
rw := db . New ( conn )
wrapper := db . TestWrapper ( t )
2021-04-20 20:33:56 -04:00
kmsCache := kms . TestKms ( t , conn , wrapper )
repo := iam . TestRepo ( t , conn , wrapper )
2020-06-29 08:18:57 -04:00
id := testId ( t )
2021-04-20 20:33:56 -04:00
org , _ := iam . TestScopes ( t , repo )
databaseWrapper , err := kmsCache . GetWrapper ( context . Background (), org . PublicId , kms . KeyPurposeDatabase )
require . NoError ( t , err )
authMethod := oidc . TestAuthMethod ( t , conn , databaseWrapper , org . PublicId , oidc . ActivePrivateState , "alice-rp" , "fido" ,
oidc . WithIssuer ( oidc . TestConvertToUrls ( t , "https://alice.com" )[ 0 ]),
oidc . WithSigningAlgs ( oidc . RS256 ),
oidc . WithApiUrl ( oidc . TestConvertToUrls ( t , "http://localhost" )[ 0 ]))
iam . TestSetPrimaryAuthMethod ( t , repo , org , authMethod . PublicId )
// an account with no assoc user to test auto-vivify for the primary auth method.
newAuthAcct := oidc . TestAccount ( t , conn , authMethod , "acct-1" , oidc . WithFullName ( "acct-1" ), oidc . WithEmail ( "acct-1@example.com" ))
2021-04-08 20:54:32 -04:00
2021-04-20 20:33:56 -04:00
// a 2nd auth method which will NOT be the primary auth method
authMethod2 := oidc . TestAuthMethod ( t , conn , databaseWrapper , org . PublicId , oidc . ActivePrivateState , "alice-rp-2" , "fido" ,
oidc . WithIssuer ( oidc . TestConvertToUrls ( t , "https://alice.com" )[ 0 ]),
oidc . WithSigningAlgs ( oidc . RS256 ),
oidc . WithApiUrl ( oidc . TestConvertToUrls ( t , "http://localhost" )[ 0 ]))
2020-06-29 08:18:57 -04:00
2021-04-20 20:33:56 -04:00
// an account with no assoc user to test non-primary auth methods do not auto-vivify
newAuthAcctWithoutVivify := oidc . TestAccount ( t , conn , authMethod2 , "acct-2" , oidc . WithFullName ( "acct-2" ), oidc . WithEmail ( "acct-2@example.com" ))
2021-04-08 20:54:32 -04:00
2021-04-20 20:33:56 -04:00
user := iam . TestUser ( t , repo , org . PublicId , iam . WithName ( "existing-" + id ))
existingUserWithAcctWithVivify := oidc . TestAccount ( t , conn , authMethod , "acct-3" , oidc . WithFullName ( "acct-3" ), oidc . WithEmail ( "acct-3@example.com" ))
added , err := repo . AddUserAccounts ( context . Background (), user . PublicId , user . Version , [] string { existingUserWithAcctWithVivify . PublicId })
require . NoError ( t , err )
require . Equal ( t , 1 , len ( added ))
foundAcct := allocAuthAccount ()
foundAcct . PublicId = existingUserWithAcctWithVivify . PublicId
require . NoError ( t , rw . LookupById ( context . Background (), & foundAcct ))
require . Equal ( t , user . PublicId , foundAcct . IamUserId )
// we need to set these primary acct values on the user, so the test
// comparisons will work properly.
user . LoginName = "acct-3"
user . FullName = "acct-3"
user . Email = "acct-3@example.com"
existingUserWithAcctNoVivify := oidc . TestAccount ( t , conn , authMethod2 , "acct-4" , oidc . WithFullName ( "acct-4" ), oidc . WithEmail ( "acct-4@example.com" ))
user , _ , err = repo . LookupUser ( context . Background (), user . PublicId )
require . NoError ( t , err )
added , err = repo . AddUserAccounts ( context . Background (), user . PublicId , user . Version , [] string { existingUserWithAcctNoVivify . PublicId })
require . NoError ( t , err )
require . Equal ( t , 2 , len ( added ))
foundAcct . PublicId = existingUserWithAcctNoVivify . PublicId
require . NoError ( t , rw . LookupById ( context . Background (), & foundAcct , db . WithWhere ( "iam_user_id = ?" , user . PublicId )))
require . Equal ( t , user . PublicId , foundAcct . IamUserId )
2020-06-29 08:18:57 -04:00
type args struct {
2020-08-10 15:17:15 -04:00
withAccountId string
2021-04-20 20:33:56 -04:00
opt [] iam . Option
2020-06-29 08:18:57 -04:00
}
tests := [] struct {
name string
args args
wantName string
wantDescription string
wantErr bool
2020-12-21 10:07:34 -08:00
wantErrIs errors . Code
2021-04-20 20:33:56 -04:00
wantUser * iam . User
2020-06-29 08:18:57 -04:00
}{
{
name : "valid" ,
args : args {
2020-08-10 15:17:15 -04:00
withAccountId : newAuthAcct . PublicId ,
2021-04-20 20:33:56 -04:00
opt : [] iam . Option {
iam . WithName ( "valid-" + id ),
iam . WithDescription ( "valid-" + id ),
2020-06-29 08:18:57 -04:00
},
},
wantName : "valid-" + id ,
wantDescription : "valid-" + id ,
wantErr : false ,
},
{
name : "new-acct-without-vivify" ,
args : args {
2020-08-10 15:17:15 -04:00
withAccountId : newAuthAcctWithoutVivify . PublicId ,
2020-06-29 08:18:57 -04:00
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . RecordNotFound ,
2020-06-29 08:18:57 -04:00
},
{
name : "missing auth acct id" ,
args : args {
2020-08-10 15:17:15 -04:00
withAccountId : "" ,
2020-06-29 08:18:57 -04:00
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-29 08:18:57 -04:00
},
{
2021-04-08 20:54:32 -04:00
name : "existing-user-with-account-with-vivify" ,
2020-06-29 08:18:57 -04:00
args : args {
2021-04-08 20:54:32 -04:00
withAccountId : existingUserWithAcctWithVivify . PublicId ,
2020-06-29 08:18:57 -04:00
},
wantErr : false ,
wantName : "existing-" + id ,
wantUser : user ,
},
{
2021-04-08 20:54:32 -04:00
name : "existing-user-with-account-no-vivify" ,
2020-06-29 08:18:57 -04:00
args : args {
2021-04-08 20:54:32 -04:00
withAccountId : existingUserWithAcctNoVivify . PublicId ,
2021-04-20 20:33:56 -04:00
opt : [] iam . Option {},
2020-06-29 08:18:57 -04:00
},
wantErr : false ,
wantName : "existing-" + id ,
wantUser : user ,
},
{
name : "bad-auth-account-id" ,
args : args {
2020-08-10 15:17:15 -04:00
withAccountId : id ,
2020-06-29 08:18:57 -04:00
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . RecordNotFound ,
2020-06-29 08:18:57 -04:00
},
{
name : "bad-auth-account-id-with-vivify" ,
args : args {
2020-08-10 15:17:15 -04:00
withAccountId : id ,
2021-04-20 20:33:56 -04:00
opt : [] iam . Option {},
2020-06-29 08:18:57 -04:00
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . RecordNotFound ,
2020-06-29 08:18:57 -04:00
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
2021-09-30 09:17:27 -04:00
underlyingDB , err := conn . SqlDB ( ctx )
2021-09-30 08:47:34 -04:00
require . NoError ( err )
dbassert := dbassert . New ( t , underlyingDB )
2020-08-10 15:17:15 -04:00
got , err := repo . LookupUserWithLogin ( context . Background (), tt . args . withAccountId , tt . args . opt ... )
2020-06-29 08:18:57 -04:00
if tt . wantErr {
require . Error ( err )
assert . Nil ( got )
2021-01-12 08:50:19 -08:00
assert . Truef ( errors . Match ( errors . T ( tt . wantErrIs ), err ), "unexpected error %s" , err . Error ())
2020-08-10 15:17:15 -04:00
if tt . args . withAccountId != "" && tt . args . withAccountId != id {
2020-06-29 08:18:57 -04:00
// need to assert that userid in auth_account is still null
2021-04-20 20:33:56 -04:00
acct := allocAuthAccount ()
2020-08-10 15:17:15 -04:00
acct . PublicId = tt . args . withAccountId
2020-06-29 08:18:57 -04:00
dbassert . IsNull ( & acct , "IamUserId" )
}
return
}
require . NoError ( err )
if tt . wantName != "" {
assert . Equal ( tt . wantName , got . Name )
}
if tt . wantDescription != "" {
assert . Equal ( tt . wantDescription , got . Description )
}
require . NotEmpty ( got . PublicId )
if tt . wantUser != nil {
2021-04-20 20:33:56 -04:00
tt . wantUser . Version = got . User . Version
tt . wantUser . CreateTime = got . User . CreateTime
tt . wantUser . UpdateTime = got . User . UpdateTime
assert . Empty ( cmp . Diff ( got . User , tt . wantUser . User , protocmp . Transform ()), "got %q, wanted %q" , got . User , tt . wantUser . User )
2020-06-29 08:18:57 -04:00
}
2021-04-20 20:33:56 -04:00
acct := allocAuthAccount ()
2020-08-10 15:17:15 -04:00
acct . PublicId = tt . args . withAccountId
2020-06-29 08:18:57 -04:00
err = rw . LookupByPublicId ( context . Background (), & acct )
require . NoError ( err )
assert . Equal ( got . PublicId , acct . IamUserId )
})
}
}
2020-08-10 15:17:15 -04:00
2020-09-25 13:35:32 -04:00
func TestRepository_AssociateAccounts ( t * testing . T ) {
t . Parallel ()
conn , _ := db . TestSetup ( t , "postgres" )
rw := db . New ( conn )
wrapper := db . TestWrapper ( t )
2021-04-20 20:33:56 -04:00
kmsCache := kms . TestKms ( t , conn , wrapper )
repo := iam . TestRepo ( t , conn , wrapper )
org , _ := iam . TestScopes ( t , repo )
databaseWrapper , err := kmsCache . GetWrapper ( context . Background (), org . PublicId , kms . KeyPurposeDatabase )
require . NoError ( t , err )
user := iam . TestUser ( t , repo , org . PublicId )
2020-08-10 15:17:15 -04:00
2021-04-20 20:33:56 -04:00
createAccountsFn := func ( prefix string ) [] string {
2022-10-25 18:29:38 -05:00
db . TestDeleteWhere ( t , conn , func () any { i := allocAuthAccount (); return & i }(), "iam_user_id = ?" , user . PublicId )
2020-09-25 13:35:32 -04:00
results := [] string {}
for i := 0 ; i < 5 ; i ++ {
2021-04-20 20:33:56 -04:00
authMethod := oidc . TestAuthMethod ( t , conn , databaseWrapper , org . PublicId , oidc . ActivePrivateState , fmt . Sprintf ( "%s-alice-rp-%d" , prefix , i ), "fido" ,
oidc . WithIssuer ( oidc . TestConvertToUrls ( t , "https://alice.com" )[ 0 ]),
oidc . WithSigningAlgs ( oidc . RS256 ),
oidc . WithApiUrl ( oidc . TestConvertToUrls ( t , "http://localhost" )[ 0 ]))
a := oidc . TestAccount ( t , conn , authMethod , fmt . Sprintf ( "acct-%d" , i ))
2020-09-25 13:35:32 -04:00
results = append ( results , a . PublicId )
}
return results
}
type args struct {
accountIdsFn func () [] string
userId string
userVersionOverride * uint32
2021-04-20 20:33:56 -04:00
opt [] iam . Option
2020-09-25 13:35:32 -04:00
}
tests := [] struct {
2021-03-30 13:00:48 -07:00
name string
args args
wantErr bool
wantErrCode errors . Code
2020-09-25 13:35:32 -04:00
}{
{
name : "valid" ,
args : args {
userId : user . PublicId ,
2021-04-20 20:33:56 -04:00
accountIdsFn : func () [] string { return createAccountsFn ( "valid" ) },
2020-09-25 13:35:32 -04:00
},
wantErr : false ,
},
{
name : "already-associated" ,
args : args {
userId : user . PublicId ,
accountIdsFn : func () [] string {
2021-04-20 20:33:56 -04:00
ids := createAccountsFn ( "already-associated" )
authMethod := oidc . TestAuthMethod ( t , conn , databaseWrapper , org . PublicId , oidc . ActivePrivateState , "already-associated-alice-rp" , "fido" ,
oidc . WithIssuer ( oidc . TestConvertToUrls ( t , "https://alice.com" )[ 0 ]),
oidc . WithSigningAlgs ( oidc . RS256 ),
oidc . WithApiUrl ( oidc . TestConvertToUrls ( t , "http://localhost" )[ 0 ]))
a := oidc . TestAccount ( t , conn , authMethod , "already-associated" )
user , _ , err := repo . LookupUser ( context . Background (), user . PublicId )
require . NoError ( t , err )
added , err := repo . AddUserAccounts ( context . Background (), user . PublicId , user . Version , [] string { a . PublicId })
require . NoError ( t , err )
require . Contains ( t , added , a . PublicId )
2020-09-25 13:35:32 -04:00
ids = append ( ids , a . PublicId )
return ids
},
},
wantErr : false ,
},
{
name : "associated-with-diff-user" ,
args : args {
userId : user . PublicId ,
accountIdsFn : func () [] string {
2021-04-20 20:33:56 -04:00
ids := createAccountsFn ( "associated-with-diff-user" )
authMethod := oidc . TestAuthMethod ( t , conn , databaseWrapper , org . PublicId , oidc . ActivePrivateState , "associated-with-diff-user-alice-rp" , "fido" ,
oidc . WithIssuer ( oidc . TestConvertToUrls ( t , "https://alice.com" )[ 0 ]),
oidc . WithSigningAlgs ( oidc . RS256 ),
oidc . WithApiUrl ( oidc . TestConvertToUrls ( t , "http://localhost" )[ 0 ]))
u := iam . TestUser ( t , repo , org . PublicId )
a := oidc . TestAccount ( t , conn , authMethod , "already-associated" )
added , err := repo . AddUserAccounts ( context . Background (), u . PublicId , u . Version , [] string { a . PublicId })
require . NoError ( t , err )
require . Contains ( t , added , a . PublicId )
2020-09-25 13:35:32 -04:00
ids = append ( ids , a . PublicId )
return ids
},
},
2021-03-30 13:00:48 -07:00
wantErr : true ,
wantErrCode : errors . AccountAlreadyAssociated ,
2020-09-25 13:35:32 -04:00
},
{
name : "bad-version" ,
args : args {
userVersionOverride : func () * uint32 {
i := uint32 ( 22 )
return & i
}(),
userId : user . PublicId ,
2021-04-20 20:33:56 -04:00
accountIdsFn : func () [] string { return createAccountsFn ( "bad-version" ) },
2020-09-25 13:35:32 -04:00
},
2021-03-30 13:00:48 -07:00
wantErr : true ,
wantErrCode : errors . MultipleRecords ,
2020-09-25 13:35:32 -04:00
},
{
name : "zero-version" ,
args : args {
userVersionOverride : func () * uint32 {
i := uint32 ( 0 )
return & i
}(),
userId : user . PublicId ,
2021-04-20 20:33:56 -04:00
accountIdsFn : func () [] string { return createAccountsFn ( "zero-version" ) },
2020-09-25 13:35:32 -04:00
},
2021-03-30 13:00:48 -07:00
wantErr : true ,
wantErrCode : errors . InvalidParameter ,
2020-09-25 13:35:32 -04:00
},
{
name : "no-accounts" ,
args : args {
userId : user . PublicId ,
accountIdsFn : func () [] string { return nil },
},
2021-03-30 13:00:48 -07:00
wantErr : true ,
wantErrCode : errors . InvalidParameter ,
2020-09-25 13:35:32 -04:00
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
accountIds := tt . args . accountIdsFn ()
sort . Strings ( accountIds )
origUser , _ , err := repo . LookupUser ( context . Background (), user . PublicId )
require . NoError ( err )
version := origUser . Version
if tt . args . userVersionOverride != nil {
version = * tt . args . userVersionOverride
}
2020-09-28 15:39:10 -04:00
got , err := repo . AddUserAccounts ( context . Background (), tt . args . userId , version , accountIds , tt . args . opt ... )
2020-09-25 13:35:32 -04:00
if tt . wantErr {
require . Error ( err )
2021-03-30 13:00:48 -07:00
assert . Truef ( errors . Match ( errors . T ( tt . wantErrCode ), err ), "unexpected error %s" , err )
2020-09-25 13:35:32 -04:00
return
}
require . NoError ( err )
err = db . TestVerifyOplog ( t , rw , tt . args . userId , db . WithOperation ( oplog . OpType_OP_TYPE_UPDATE ), db . WithCreateNotBefore ( 10 * time . Second ))
assert . NoError ( err )
for _ , id := range got {
err = db . TestVerifyOplog ( t , rw , id , db . WithOperation ( oplog . OpType_OP_TYPE_UPDATE ), db . WithCreateNotBefore ( 10 * time . Second ))
assert . NoError ( err )
}
sort . Strings ( got )
assert . Equal ( accountIds , got )
2020-09-28 15:39:10 -04:00
foundIds , err := repo . ListUserAccounts ( context . Background (), tt . args . userId )
2020-09-25 13:35:32 -04:00
require . NoError ( err )
sort . Strings ( foundIds )
assert . Equal ( accountIds , foundIds )
u , _ , err := repo . LookupUser ( context . Background (), tt . args . userId )
require . NoError ( err )
assert . Equal ( version + 1 , u . Version )
})
}
}
func TestRepository_DisassociateAccounts ( t * testing . T ) {
t . Parallel ()
conn , _ := db . TestSetup ( t , "postgres" )
rw := db . New ( conn )
wrapper := db . TestWrapper ( t )
2021-04-20 20:33:56 -04:00
kmsCache := kms . TestKms ( t , conn , wrapper )
repo := iam . TestRepo ( t , conn , wrapper )
org , _ := iam . TestScopes ( t , repo )
databaseWrapper , err := kmsCache . GetWrapper ( context . Background (), org . PublicId , kms . KeyPurposeDatabase )
require . NoError ( t , err )
2020-09-25 13:35:32 -04:00
2021-04-20 20:33:56 -04:00
user := iam . TestUser ( t , repo , org . PublicId )
createAccountsFn := func ( prefix string ) [] string {
2022-10-25 18:29:38 -05:00
db . TestDeleteWhere ( t , conn , func () any { a := allocAuthAccount (); return & a }(), "iam_user_id = ?" , user . PublicId )
2020-09-25 13:35:32 -04:00
results := [] string {}
for i := 0 ; i < 1 ; i ++ {
2021-04-20 20:33:56 -04:00
authMethod := oidc . TestAuthMethod ( t , conn , databaseWrapper , org . PublicId , oidc . ActivePrivateState , fmt . Sprintf ( "%s-alice-rp-%d" , prefix , i ), "fido" ,
oidc . WithIssuer ( oidc . TestConvertToUrls ( t , "https://alice.com" )[ 0 ]),
oidc . WithSigningAlgs ( oidc . RS256 ),
oidc . WithApiUrl ( oidc . TestConvertToUrls ( t , "http://localhost" )[ 0 ]))
a := oidc . TestAccount ( t , conn , authMethod , fmt . Sprintf ( "acct-%d" , i ))
user , _ , err := repo . LookupUser ( context . Background (), user . PublicId )
require . NoError ( t , err )
added , err := repo . AddUserAccounts ( context . Background (), user . PublicId , user . Version , [] string { a . PublicId })
require . NoError ( t , err )
require . Contains ( t , added , a . PublicId )
2020-09-25 13:35:32 -04:00
results = append ( results , a . PublicId )
}
return results
}
type args struct {
accountIdsFn func () [] string
userId string
userVersionOverride * uint32
2021-04-20 20:33:56 -04:00
opt [] iam . Option
2020-09-25 13:35:32 -04:00
}
tests := [] struct {
2021-03-30 13:00:48 -07:00
name string
args args
wantErr bool
wantErrCode errors . Code
2020-09-25 13:35:32 -04:00
}{
{
name : "valid" ,
args : args {
userId : user . PublicId ,
2021-04-20 20:33:56 -04:00
accountIdsFn : func () [] string { return createAccountsFn ( "valid" ) },
2020-09-25 13:35:32 -04:00
},
wantErr : false ,
},
{
name : "associated-with-diff-user" ,
args : args {
userId : user . PublicId ,
accountIdsFn : func () [] string {
2021-04-20 20:33:56 -04:00
ids := createAccountsFn ( "associated-with-diff-user" )
authMethod := oidc . TestAuthMethod ( t , conn , databaseWrapper , org . PublicId , oidc . ActivePrivateState , "associated-with-diff-user-alice-rp" , "fido" ,
oidc . WithIssuer ( oidc . TestConvertToUrls ( t , "https://alice.com" )[ 0 ]),
oidc . WithSigningAlgs ( oidc . RS256 ),
oidc . WithApiUrl ( oidc . TestConvertToUrls ( t , "http://localhost" )[ 0 ]))
u := iam . TestUser ( t , repo , org . PublicId )
a := oidc . TestAccount ( t , conn , authMethod , "already-associated" )
added , err := repo . AddUserAccounts ( context . Background (), u . PublicId , u . Version , [] string { a . PublicId })
require . NoError ( t , err )
require . Contains ( t , added , a . PublicId )
2020-09-25 13:35:32 -04:00
ids = append ( ids , a . PublicId )
return ids
},
},
2021-03-30 13:00:48 -07:00
wantErr : true ,
wantErrCode : errors . AccountAlreadyAssociated ,
2020-09-25 13:35:32 -04:00
},
{
name : "bad-version" ,
args : args {
userVersionOverride : func () * uint32 {
i := uint32 ( 22 )
return & i
}(),
userId : user . PublicId ,
2021-04-20 20:33:56 -04:00
accountIdsFn : func () [] string { return createAccountsFn ( "bad-version" ) },
2020-09-25 13:35:32 -04:00
},
2021-03-30 13:00:48 -07:00
wantErr : true ,
wantErrCode : errors . MultipleRecords ,
2020-09-25 13:35:32 -04:00
},
{
name : "zero-version" ,
args : args {
userVersionOverride : func () * uint32 {
i := uint32 ( 0 )
return & i
}(),
userId : user . PublicId ,
2021-04-20 20:33:56 -04:00
accountIdsFn : func () [] string { return createAccountsFn ( "zero-version" ) },
2020-09-25 13:35:32 -04:00
},
2021-03-30 13:00:48 -07:00
wantErr : true ,
wantErrCode : errors . InvalidParameter ,
2020-09-25 13:35:32 -04:00
},
{
name : "no-accounts" ,
args : args {
userId : user . PublicId ,
accountIdsFn : func () [] string { return nil },
},
2021-03-30 13:00:48 -07:00
wantErr : true ,
wantErrCode : errors . InvalidParameter ,
2020-09-25 13:35:32 -04:00
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
accountIds := tt . args . accountIdsFn ()
origUser , _ , err := repo . LookupUser ( context . Background (), user . PublicId )
require . NoError ( err )
version := origUser . Version
if tt . args . userVersionOverride != nil {
version = * tt . args . userVersionOverride
}
2020-09-28 15:39:10 -04:00
got , err := repo . DeleteUserAccounts ( context . Background (), tt . args . userId , version , accountIds , tt . args . opt ... )
2020-09-25 13:35:32 -04:00
if tt . wantErr {
require . Error ( err )
2021-03-30 13:00:48 -07:00
assert . Truef ( errors . Match ( errors . T ( tt . wantErrCode ), err ), "unexpected error %s" , err )
2020-09-25 13:35:32 -04:00
return
}
require . NoError ( err )
err = db . TestVerifyOplog ( t , rw , tt . args . userId , db . WithOperation ( oplog . OpType_OP_TYPE_UPDATE ), db . WithCreateNotBefore ( 10 * time . Second ))
assert . NoError ( err )
for _ , id := range got {
err = db . TestVerifyOplog ( t , rw , id , db . WithOperation ( oplog . OpType_OP_TYPE_UPDATE ), db . WithCreateNotBefore ( 10 * time . Second ))
assert . NoError ( err )
}
2020-09-28 15:39:10 -04:00
foundIds , err := repo . ListUserAccounts ( context . Background (), tt . args . userId )
2020-09-25 13:35:32 -04:00
require . NoError ( err )
for _ , id := range accountIds {
assert . True (! strutil . StrListContains ( foundIds , id ))
}
u , _ , err := repo . LookupUser ( context . Background (), tt . args . userId )
require . NoError ( err )
assert . Equal ( version + 1 , u . Version )
})
}
}
func TestRepository_SetAssociatedAccounts ( t * testing . T ) {
t . Parallel ()
conn , _ := db . TestSetup ( t , "postgres" )
rw := db . New ( conn )
wrapper := db . TestWrapper ( t )
2021-04-20 20:33:56 -04:00
kmsCache := kms . TestKms ( t , conn , wrapper )
repo := iam . TestRepo ( t , conn , wrapper )
org , _ := iam . TestScopes ( t , repo )
databaseWrapper , err := kmsCache . GetWrapper ( context . Background (), org . PublicId , kms . KeyPurposeDatabase )
require . NoError ( t , err )
user := iam . TestUser ( t , repo , org . PublicId )
2020-09-25 13:35:32 -04:00
2021-04-20 20:33:56 -04:00
createAccountsFn := func ( prefix string ) [] string {
2022-10-25 18:29:38 -05:00
db . TestDeleteWhere ( t , conn , func () any { i := allocAuthAccount (); return & i }(), "iam_user_id = ?" , user . PublicId )
2020-09-25 13:35:32 -04:00
results := [] string {}
2021-04-20 20:33:56 -04:00
for i := 0 ; i < 1 ; i ++ {
authMethod := oidc . TestAuthMethod ( t , conn , databaseWrapper , org . PublicId , oidc . ActivePrivateState , fmt . Sprintf ( "%s-alice-rp-%d" , prefix , i ), "fido" ,
oidc . WithIssuer ( oidc . TestConvertToUrls ( t , "https://alice.com" )[ 0 ]),
oidc . WithSigningAlgs ( oidc . RS256 ),
oidc . WithApiUrl ( oidc . TestConvertToUrls ( t , "http://localhost" )[ 0 ]))
a := oidc . TestAccount ( t , conn , authMethod , fmt . Sprintf ( "acct-%d" , i ))
2020-09-25 13:35:32 -04:00
results = append ( results , a . PublicId )
}
return results
}
type args struct {
accountIdsFn func () ([] string , [] string )
userId string
userVersionOverride * uint32
2021-04-20 20:33:56 -04:00
opt [] iam . Option
2020-09-25 13:35:32 -04:00
}
tests := [] struct {
2021-03-30 13:00:48 -07:00
name string
args args
wantErr bool
wantErrCode errors . Code
2020-09-25 13:35:32 -04:00
}{
{
name : "valid" ,
args : args {
userId : user . PublicId ,
accountIdsFn : func () ([] string , [] string ) {
2021-04-20 20:33:56 -04:00
ids := createAccountsFn ( "valid" )
2020-09-25 13:35:32 -04:00
return ids , ids
},
},
wantErr : false ,
},
{
name : "one-already-associated" ,
args : args {
userId : user . PublicId ,
accountIdsFn : func () ([] string , [] string ) {
2021-04-20 20:33:56 -04:00
ids := createAccountsFn ( "one-already-associated" )
2020-09-25 13:35:32 -04:00
changes := append ([] string {}, ids ... )
2021-04-20 20:33:56 -04:00
authMethod := oidc . TestAuthMethod ( t , conn , databaseWrapper , org . PublicId , oidc . ActivePrivateState , "one-already-associated-alice-rp" , "fido" ,
oidc . WithIssuer ( oidc . TestConvertToUrls ( t , "https://alice.com" )[ 0 ]),
oidc . WithSigningAlgs ( oidc . RS256 ),
oidc . WithApiUrl ( oidc . TestConvertToUrls ( t , "http://localhost" )[ 0 ]))
a := oidc . TestAccount ( t , conn , authMethod , "acct-one-already-associated" )
user , _ , err := repo . LookupUser ( context . Background (), user . PublicId )
require . NoError ( t , err )
added , err := repo . AddUserAccounts ( context . Background (), user . PublicId , user . Version , [] string { a . PublicId })
require . NoError ( t , err )
require . Contains ( t , added , a . PublicId )
2020-09-25 13:35:32 -04:00
ids = append ( ids , a . PublicId )
return ids , changes
},
},
wantErr : false ,
},
{
name : "no-change" ,
args : args {
userId : user . PublicId ,
accountIdsFn : func () ([] string , [] string ) {
2021-04-20 20:33:56 -04:00
ids := createAccountsFn ( "no-change" )
user , _ , err := repo . LookupUser ( context . Background (), user . PublicId )
require . NoError ( t , err )
added , err := repo . AddUserAccounts ( context . Background (), user . PublicId , user . Version , ids )
require . NoError ( t , err )
require . Equal ( t , len ( ids ), len ( added ))
// ids := []string{}
// for i := 0; i < 10; i++ {
// a := testAccount(t, conn, org.PublicId, authMethodId, user.PublicId)
// ids = append(ids, a.PublicId)
// }
2020-09-25 13:35:32 -04:00
return ids , nil
},
},
wantErr : false ,
},
{
name : "remove-all" ,
args : args {
userId : user . PublicId ,
accountIdsFn : func () ([] string , [] string ) {
2021-04-20 20:33:56 -04:00
ids := createAccountsFn ( "remove-all" )
user , _ , err := repo . LookupUser ( context . Background (), user . PublicId )
require . NoError ( t , err )
added , err := repo . AddUserAccounts ( context . Background (), user . PublicId , user . Version , ids )
require . NoError ( t , err )
require . Equal ( t , len ( ids ), len ( added ))
2020-09-25 13:35:32 -04:00
return nil , ids
},
},
wantErr : false ,
},
{
name : "associated-with-diff-user" ,
args : args {
userId : user . PublicId ,
accountIdsFn : func () ([] string , [] string ) {
2021-04-20 20:33:56 -04:00
ids := createAccountsFn ( "associated-with-diff-user" )
authMethod := oidc . TestAuthMethod ( t , conn , databaseWrapper , org . PublicId , oidc . ActivePrivateState , "associated-with-diff-user-alice-rp" , "fido" ,
oidc . WithIssuer ( oidc . TestConvertToUrls ( t , "https://alice.com" )[ 0 ]),
oidc . WithSigningAlgs ( oidc . RS256 ),
oidc . WithApiUrl ( oidc . TestConvertToUrls ( t , "http://localhost" )[ 0 ]))
u := iam . TestUser ( t , repo , org . PublicId )
a := oidc . TestAccount ( t , conn , authMethod , "already-associated" )
added , err := repo . AddUserAccounts ( context . Background (), u . PublicId , u . Version , [] string { a . PublicId })
require . NoError ( t , err )
require . Contains ( t , added , a . PublicId )
2020-09-25 13:35:32 -04:00
ids = append ( ids , a . PublicId )
return ids , ids
},
},
2021-03-30 13:00:48 -07:00
wantErr : true ,
wantErrCode : errors . AccountAlreadyAssociated ,
2020-09-25 13:35:32 -04:00
},
{
name : "bad-version" ,
args : args {
userVersionOverride : func () * uint32 {
i := uint32 ( 22 )
return & i
}(),
userId : user . PublicId ,
accountIdsFn : func () ([] string , [] string ) {
2021-04-20 20:33:56 -04:00
ids := createAccountsFn ( "bad-version" )
2020-09-25 13:35:32 -04:00
return ids , ids
},
},
2021-03-30 13:00:48 -07:00
wantErr : true ,
wantErrCode : errors . MultipleRecords ,
2020-09-25 13:35:32 -04:00
},
{
name : "zero-version" ,
args : args {
userVersionOverride : func () * uint32 {
i := uint32 ( 0 )
return & i
}(),
userId : user . PublicId ,
accountIdsFn : func () ([] string , [] string ) {
2021-04-20 20:33:56 -04:00
ids := createAccountsFn ( "zero-version" )
2020-09-25 13:35:32 -04:00
return ids , ids
},
},
2021-03-30 13:00:48 -07:00
wantErr : true ,
wantErrCode : errors . InvalidParameter ,
2020-09-25 13:35:32 -04:00
},
{
name : "no-accounts-no-changes" ,
args : args {
userId : user . PublicId ,
accountIdsFn : func () ([] string , [] string ) { return nil , nil },
},
wantErr : false ,
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
2022-10-25 18:29:38 -05:00
db . TestDeleteWhere ( t , conn , func () any { a := allocAuthAccount (); return & a }(), "iam_user_id = ?" , user . PublicId )
2021-04-20 20:33:56 -04:00
2020-09-25 13:35:32 -04:00
accountIds , changes := tt . args . accountIdsFn ()
sort . Strings ( accountIds )
origUser , _ , err := repo . LookupUser ( context . Background (), user . PublicId )
require . NoError ( err )
version := origUser . Version
if tt . args . userVersionOverride != nil {
version = * tt . args . userVersionOverride
}
2020-09-28 15:39:10 -04:00
got , err := repo . SetUserAccounts ( context . Background (), tt . args . userId , version , accountIds , tt . args . opt ... )
2020-09-25 13:35:32 -04:00
if tt . wantErr {
require . Error ( err )
2021-03-30 13:00:48 -07:00
assert . Truef ( errors . Match ( errors . T ( tt . wantErrCode ), err ), "unexpected error %s" , err )
2020-09-25 13:35:32 -04:00
return
}
require . NoError ( err )
if len ( changes ) != 0 {
err = db . TestVerifyOplog ( t , rw , tt . args . userId , db . WithOperation ( oplog . OpType_OP_TYPE_UPDATE ), db . WithCreateNotBefore ( 10 * time . Second ))
assert . NoError ( err )
for _ , id := range changes {
err = db . TestVerifyOplog ( t , rw , id , db . WithOperation ( oplog . OpType_OP_TYPE_UPDATE ), db . WithCreateNotBefore ( 10 * time . Second ))
assert . NoErrorf ( err , "%s missing oplog entry" , id )
}
}
sort . Strings ( got )
assert . Equal ( accountIds , got )
2020-09-28 15:39:10 -04:00
foundIds , err := repo . ListUserAccounts ( context . Background (), tt . args . userId )
2020-09-25 13:35:32 -04:00
require . NoError ( err )
sort . Strings ( foundIds )
assert . Equal ( accountIds , foundIds )
u , _ , err := repo . LookupUser ( context . Background (), tt . args . userId )
require . NoError ( err )
switch tt . name {
2021-04-20 20:33:56 -04:00
case "no-accounts-no-changes" , "no-change" :
assert . Equalf ( version , u . Version , "expected version %d and got: %d" , version , u . Version )
2020-09-25 13:35:32 -04:00
default :
2021-04-20 20:33:56 -04:00
assert . Equalf ( version + 1 , u . Version , "expected version %d and got: %d" , version + 1 , u . Version )
2020-09-25 13:35:32 -04:00
}
2020-08-10 15:17:15 -04:00
})
}
}
2021-04-20 20:33:56 -04:00
func testId ( t * testing . T ) string {
t . Helper ()
id , err := uuid . GenerateUUID ()
require . NoError ( t , err )
return id
}
type testAuthAccount struct {
* store . Account
tableName string `gorm:"-"`
}
func allocAuthAccount () testAuthAccount {
return testAuthAccount {
Account : & store . Account {},
}
}
// TableName returns the tablename to override the default gorm table name.
func ( a * testAuthAccount ) TableName () string {
if a . tableName != "" {
return a . tableName
}
return "auth_account"
}