mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-18 05:52:51 +00:00
# Which Problems Are Solved Add the ability to update the timestamp when MFA initialization was last skipped. Get User By ID now also returns the timestamps when MFA setup was last skipped. # How the Problems Are Solved - Add a `HumanMFAInitSkipped` method to the `users/v2` API. - MFA skipped was already projected in the `auth.users3` table. In this PR the same column is added to the users projection. Event handling is kept the same as in the `UserView`: <details> https://github.com/zitadel/zitadel/blob/62804ca45f804322e54831aeb2c9be9128ae60ca/internal/user/repository/view/model/user.go#L243-L377 </details> # Additional Changes - none # Additional Context - Closes https://github.com/zitadel/zitadel/issues/9197
306 lines
7.5 KiB
Go
306 lines
7.5 KiB
Go
package query
|
|
|
|
import (
|
|
"database/sql"
|
|
"database/sql/driver"
|
|
"errors"
|
|
"fmt"
|
|
"regexp"
|
|
"testing"
|
|
|
|
"github.com/zitadel/zitadel/internal/database"
|
|
"github.com/zitadel/zitadel/internal/domain"
|
|
)
|
|
|
|
var (
|
|
orgMembersQuery = regexp.QuoteMeta("SELECT" +
|
|
" members.creation_date" +
|
|
", members.change_date" +
|
|
", members.sequence" +
|
|
", members.resource_owner" +
|
|
", members.user_resource_owner" +
|
|
", members.user_id" +
|
|
", members.roles" +
|
|
", projections.login_names3.login_name" +
|
|
", projections.users14_humans.email" +
|
|
", projections.users14_humans.first_name" +
|
|
", projections.users14_humans.last_name" +
|
|
", projections.users14_humans.display_name" +
|
|
", projections.users14_machines.name" +
|
|
", projections.users14_humans.avatar_key" +
|
|
", projections.users14.type" +
|
|
", COUNT(*) OVER () " +
|
|
"FROM projections.org_members4 AS members " +
|
|
"LEFT JOIN projections.users14_humans " +
|
|
"ON members.user_id = projections.users14_humans.user_id " +
|
|
"AND members.instance_id = projections.users14_humans.instance_id " +
|
|
"LEFT JOIN projections.users14_machines " +
|
|
"ON members.user_id = projections.users14_machines.user_id " +
|
|
"AND members.instance_id = projections.users14_machines.instance_id " +
|
|
"LEFT JOIN projections.users14 " +
|
|
"ON members.user_id = projections.users14.id " +
|
|
"AND members.instance_id = projections.users14.instance_id " +
|
|
"LEFT JOIN projections.login_names3 " +
|
|
"ON members.user_id = projections.login_names3.user_id " +
|
|
"AND members.instance_id = projections.login_names3.instance_id " +
|
|
"AS OF SYSTEM TIME '-1 ms' " +
|
|
"WHERE projections.login_names3.is_primary = $1")
|
|
orgMembersColumns = []string{
|
|
"creation_date",
|
|
"change_date",
|
|
"sequence",
|
|
"resource_owner",
|
|
"user_resource_owner",
|
|
"user_id",
|
|
"roles",
|
|
"login_name",
|
|
"email",
|
|
"first_name",
|
|
"last_name",
|
|
"display_name",
|
|
"name",
|
|
"avatar_key",
|
|
"type",
|
|
"count",
|
|
}
|
|
)
|
|
|
|
func Test_OrgMemberPrepares(t *testing.T) {
|
|
type want struct {
|
|
sqlExpectations sqlExpectation
|
|
err checkErr
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
prepare interface{}
|
|
want want
|
|
object interface{}
|
|
}{
|
|
{
|
|
name: "prepareOrgMembersQuery no result",
|
|
prepare: prepareOrgMembersQuery,
|
|
want: want{
|
|
sqlExpectations: mockQueries(
|
|
orgMembersQuery,
|
|
nil,
|
|
nil,
|
|
),
|
|
},
|
|
object: &Members{
|
|
Members: []*Member{},
|
|
},
|
|
},
|
|
{
|
|
name: "prepareOrgMembersQuery human found",
|
|
prepare: prepareOrgMembersQuery,
|
|
want: want{
|
|
sqlExpectations: mockQueries(
|
|
orgMembersQuery,
|
|
orgMembersColumns,
|
|
[][]driver.Value{
|
|
{
|
|
testNow,
|
|
testNow,
|
|
uint64(20211206),
|
|
"ro",
|
|
"uro",
|
|
"user-id",
|
|
database.TextArray[string]{"role-1", "role-2"},
|
|
"[email protected]",
|
|
"[email protected]",
|
|
"first-name",
|
|
"last-name",
|
|
"display name",
|
|
nil,
|
|
nil,
|
|
domain.UserTypeHuman,
|
|
},
|
|
},
|
|
),
|
|
},
|
|
object: &Members{
|
|
SearchResponse: SearchResponse{
|
|
Count: 1,
|
|
},
|
|
Members: []*Member{
|
|
{
|
|
CreationDate: testNow,
|
|
ChangeDate: testNow,
|
|
Sequence: 20211206,
|
|
ResourceOwner: "ro",
|
|
UserResourceOwner: "uro",
|
|
UserID: "user-id",
|
|
Roles: database.TextArray[string]{"role-1", "role-2"},
|
|
PreferredLoginName: "[email protected]",
|
|
Email: "[email protected]",
|
|
FirstName: "first-name",
|
|
LastName: "last-name",
|
|
DisplayName: "display name",
|
|
AvatarURL: "",
|
|
UserType: domain.UserTypeHuman,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "prepareOrgMembersQuery machine found",
|
|
prepare: prepareOrgMembersQuery,
|
|
want: want{
|
|
sqlExpectations: mockQueries(
|
|
orgMembersQuery,
|
|
orgMembersColumns,
|
|
[][]driver.Value{
|
|
{
|
|
testNow,
|
|
testNow,
|
|
uint64(20211206),
|
|
"ro",
|
|
"uro",
|
|
"user-id",
|
|
database.TextArray[string]{"role-1", "role-2"},
|
|
"[email protected]",
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
"machine-name",
|
|
nil,
|
|
domain.UserTypeMachine,
|
|
},
|
|
},
|
|
),
|
|
},
|
|
object: &Members{
|
|
SearchResponse: SearchResponse{
|
|
Count: 1,
|
|
},
|
|
Members: []*Member{
|
|
{
|
|
CreationDate: testNow,
|
|
ChangeDate: testNow,
|
|
Sequence: 20211206,
|
|
ResourceOwner: "ro",
|
|
UserResourceOwner: "uro",
|
|
UserID: "user-id",
|
|
Roles: database.TextArray[string]{"role-1", "role-2"},
|
|
PreferredLoginName: "[email protected]",
|
|
Email: "",
|
|
FirstName: "",
|
|
LastName: "",
|
|
DisplayName: "machine-name",
|
|
AvatarURL: "",
|
|
UserType: domain.UserTypeMachine,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "prepareOrgMembersQuery multiple users",
|
|
prepare: prepareOrgMembersQuery,
|
|
want: want{
|
|
sqlExpectations: mockQueries(
|
|
orgMembersQuery,
|
|
orgMembersColumns,
|
|
[][]driver.Value{
|
|
{
|
|
testNow,
|
|
testNow,
|
|
uint64(20211206),
|
|
"ro",
|
|
"uro",
|
|
"user-id-1",
|
|
database.TextArray[string]{"role-1", "role-2"},
|
|
"[email protected]",
|
|
"[email protected]",
|
|
"first-name",
|
|
"last-name",
|
|
"display name",
|
|
nil,
|
|
nil,
|
|
domain.UserTypeHuman,
|
|
},
|
|
{
|
|
testNow,
|
|
testNow,
|
|
uint64(20211206),
|
|
"ro",
|
|
"uro",
|
|
"user-id-2",
|
|
database.TextArray[string]{"role-1", "role-2"},
|
|
"[email protected]",
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
"machine-name",
|
|
nil,
|
|
domain.UserTypeMachine,
|
|
},
|
|
},
|
|
),
|
|
},
|
|
object: &Members{
|
|
SearchResponse: SearchResponse{
|
|
Count: 2,
|
|
},
|
|
Members: []*Member{
|
|
{
|
|
CreationDate: testNow,
|
|
ChangeDate: testNow,
|
|
Sequence: 20211206,
|
|
ResourceOwner: "ro",
|
|
UserResourceOwner: "uro",
|
|
UserID: "user-id-1",
|
|
Roles: database.TextArray[string]{"role-1", "role-2"},
|
|
PreferredLoginName: "[email protected]",
|
|
Email: "[email protected]",
|
|
FirstName: "first-name",
|
|
LastName: "last-name",
|
|
DisplayName: "display name",
|
|
AvatarURL: "",
|
|
UserType: domain.UserTypeHuman,
|
|
},
|
|
{
|
|
CreationDate: testNow,
|
|
ChangeDate: testNow,
|
|
Sequence: 20211206,
|
|
ResourceOwner: "ro",
|
|
UserResourceOwner: "uro",
|
|
UserID: "user-id-2",
|
|
Roles: database.TextArray[string]{"role-1", "role-2"},
|
|
PreferredLoginName: "[email protected]",
|
|
Email: "",
|
|
FirstName: "",
|
|
LastName: "",
|
|
DisplayName: "machine-name",
|
|
AvatarURL: "",
|
|
UserType: domain.UserTypeMachine,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "prepareOrgMembersQuery sql err",
|
|
prepare: prepareOrgMembersQuery,
|
|
want: want{
|
|
sqlExpectations: mockQueryErr(
|
|
orgMembersQuery,
|
|
sql.ErrConnDone,
|
|
),
|
|
err: func(err error) (error, bool) {
|
|
if !errors.Is(err, sql.ErrConnDone) {
|
|
return fmt.Errorf("err should be sql.ErrConnDone got: %w", err), false
|
|
}
|
|
return nil, true
|
|
},
|
|
},
|
|
object: (*OrgMembership)(nil),
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
assertPrepare(t, tt.prepare, tt.object, tt.want.sqlExpectations, tt.want.err, defaultPrepareArgs...)
|
|
})
|
|
}
|
|
}
|