mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 07:57:32 +00:00
feat(queries): user grants (#2838)
* refactor(domain): add user type * fix(projections): start with login names * fix(login_policy): correct handling of user domain claimed event * fix(projections): add members * refactor: simplify member projections * add migration for members * add metadata to member projections * refactor: login name projection * fix: set correct suffixes on login name projections * test(projections): login name reduces * fix: correct cols in reduce member * test(projections): org, iam, project members * member additional cols and conds as opt, add project grant members * fix(migration): members * fix(migration): correct database name * migration version * migs * better naming for member cond and col * split project and project grant members * prepare member columns * feat(queries): membership query * test(queries): membership prepare * fix(queries): multiple projections for latest sequence * fix(api): use query for membership queries in auth and management * feat: org member queries * fix(api): use query for iam member calls * fix(queries): org members * fix(queries): project members * fix(queries): project grant members * fix(query): member queries and user avatar column * member cols * fix(queries): membership stmt * fix user test * fix user test * fix(projections): add user grant projection * fix(user_grant): handle state changes * add state to migration * fix(management): use query for user grant requests * merge eventstore-naming into user-grant-projection * feat(queries): user grants * fix(migrations): version * fix(api): user query for user grants * fix(query): event mappers for usergrant aggregate * fix(projection): correct aggregate for user grants * fix(queries): user grant roles as list contains * cleanup reducers * fix avater_key to avatar_key * tests * cleanup * cleanup * add resourceowner query * fix: user grant project name search query Co-authored-by: Livio Amstutz <livio.a@gmail.com> Co-authored-by: fabi <fabienne.gerschwiler@gmail.com>
This commit is contained in:
@@ -25,7 +25,7 @@ var (
|
||||
", zitadel.projections.users_humans.last_name" +
|
||||
", zitadel.projections.users_humans.display_name" +
|
||||
", zitadel.projections.users_machines.name" +
|
||||
", zitadel.projections.users_humans.avater_key" +
|
||||
", zitadel.projections.users_humans.avatar_key" +
|
||||
", COUNT(*) OVER () " +
|
||||
"FROM zitadel.projections.iam_members as members " +
|
||||
"LEFT JOIN zitadel.projections.users_humans " +
|
||||
@@ -48,7 +48,7 @@ var (
|
||||
"last_name",
|
||||
"display_name",
|
||||
"name",
|
||||
"avater_key",
|
||||
"avatar_key",
|
||||
"count",
|
||||
}
|
||||
)
|
||||
|
@@ -25,7 +25,7 @@ var (
|
||||
", zitadel.projections.users_humans.last_name" +
|
||||
", zitadel.projections.users_humans.display_name" +
|
||||
", zitadel.projections.users_machines.name" +
|
||||
", zitadel.projections.users_humans.avater_key" +
|
||||
", zitadel.projections.users_humans.avatar_key" +
|
||||
", COUNT(*) OVER () " +
|
||||
"FROM zitadel.projections.org_members as members " +
|
||||
"LEFT JOIN zitadel.projections.users_humans " +
|
||||
@@ -48,7 +48,7 @@ var (
|
||||
"last_name",
|
||||
"display_name",
|
||||
"name",
|
||||
"avater_key",
|
||||
"avatar_key",
|
||||
"count",
|
||||
}
|
||||
)
|
||||
|
@@ -25,7 +25,7 @@ var (
|
||||
", zitadel.projections.users_humans.last_name" +
|
||||
", zitadel.projections.users_humans.display_name" +
|
||||
", zitadel.projections.users_machines.name" +
|
||||
", zitadel.projections.users_humans.avater_key" +
|
||||
", zitadel.projections.users_humans.avatar_key" +
|
||||
", COUNT(*) OVER () " +
|
||||
"FROM zitadel.projections.project_grant_members as members " +
|
||||
"LEFT JOIN zitadel.projections.users_humans " +
|
||||
@@ -48,7 +48,7 @@ var (
|
||||
"last_name",
|
||||
"display_name",
|
||||
"name",
|
||||
"avater_key",
|
||||
"avatar_key",
|
||||
"count",
|
||||
}
|
||||
)
|
||||
|
@@ -25,7 +25,7 @@ var (
|
||||
", zitadel.projections.users_humans.last_name" +
|
||||
", zitadel.projections.users_humans.display_name" +
|
||||
", zitadel.projections.users_machines.name" +
|
||||
", zitadel.projections.users_humans.avater_key" +
|
||||
", zitadel.projections.users_humans.avatar_key" +
|
||||
", COUNT(*) OVER () " +
|
||||
"FROM zitadel.projections.project_members as members " +
|
||||
"LEFT JOIN zitadel.projections.users_humans " +
|
||||
@@ -48,7 +48,7 @@ var (
|
||||
"last_name",
|
||||
"display_name",
|
||||
"name",
|
||||
"avater_key",
|
||||
"avatar_key",
|
||||
"count",
|
||||
}
|
||||
)
|
||||
|
@@ -40,6 +40,7 @@ const (
|
||||
UserStateCol = "state"
|
||||
UserSequenceCol = "sequence"
|
||||
UserUsernameCol = "username"
|
||||
UserTypeCol = "type"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -53,7 +54,7 @@ const (
|
||||
HumanDisplayNameCol = "display_name"
|
||||
HumanPreferredLanguageCol = "preferred_language"
|
||||
HumanGenderCol = "gender"
|
||||
HumanAvaterURLCol = "avater_key"
|
||||
HumanAvaterURLCol = "avatar_key"
|
||||
|
||||
// email
|
||||
HumanEmailCol = "email"
|
||||
@@ -203,6 +204,7 @@ func (p *UserProjection) reduceHumanAdded(event eventstore.Event) (*handler.Stat
|
||||
handler.NewCol(UserStateCol, domain.UserStateInitial),
|
||||
handler.NewCol(UserSequenceCol, e.Sequence()),
|
||||
handler.NewCol(UserUsernameCol, e.UserName),
|
||||
handler.NewCol(UserTypeCol, domain.UserTypeHuman),
|
||||
},
|
||||
),
|
||||
crdb.AddCreateStatement(
|
||||
@@ -239,6 +241,7 @@ func (p *UserProjection) reduceHumanRegistered(event eventstore.Event) (*handler
|
||||
handler.NewCol(UserStateCol, domain.UserStateInitial),
|
||||
handler.NewCol(UserSequenceCol, e.Sequence()),
|
||||
handler.NewCol(UserUsernameCol, e.UserName),
|
||||
handler.NewCol(UserTypeCol, domain.UserTypeHuman),
|
||||
},
|
||||
),
|
||||
crdb.AddCreateStatement(
|
||||
@@ -656,6 +659,7 @@ func (p *UserProjection) reduceMachineAdded(event eventstore.Event) (*handler.St
|
||||
handler.NewCol(UserStateCol, domain.UserStateInitial),
|
||||
handler.NewCol(UserSequenceCol, e.Sequence()),
|
||||
handler.NewCol(UserUsernameCol, e.UserName),
|
||||
handler.NewCol(UserTypeCol, domain.UserTypeMachine),
|
||||
},
|
||||
),
|
||||
crdb.AddCreateStatement(
|
||||
|
@@ -50,7 +50,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username) VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
@@ -59,6 +59,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
domain.UserStateInitial,
|
||||
uint64(15),
|
||||
"user-name",
|
||||
domain.UserTypeHuman,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -107,7 +108,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username) VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
@@ -116,6 +117,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
domain.UserStateInitial,
|
||||
uint64(15),
|
||||
"user-name",
|
||||
domain.UserTypeHuman,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -159,7 +161,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username) VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
@@ -168,6 +170,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
domain.UserStateInitial,
|
||||
uint64(15),
|
||||
"user-name",
|
||||
domain.UserTypeHuman,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -216,7 +219,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username) VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
@@ -225,6 +228,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
domain.UserStateInitial,
|
||||
uint64(15),
|
||||
"user-name",
|
||||
domain.UserTypeHuman,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -273,7 +277,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username) VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
@@ -282,6 +286,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
domain.UserStateInitial,
|
||||
uint64(15),
|
||||
"user-name",
|
||||
domain.UserTypeHuman,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -325,7 +330,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username) VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
@@ -334,6 +339,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
domain.UserStateInitial,
|
||||
uint64(15),
|
||||
"user-name",
|
||||
domain.UserTypeHuman,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1031,7 +1037,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE zitadel.projections.users_humans SET (avater_key) = ($1) WHERE (user_id = $2)",
|
||||
expectedStmt: "UPDATE zitadel.projections.users_humans SET (avatar_key) = ($1) WHERE (user_id = $2)",
|
||||
expectedArgs: []interface{}{
|
||||
"users/agg-id/avatar",
|
||||
"agg-id",
|
||||
@@ -1067,7 +1073,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE zitadel.projections.users_humans SET (avater_key) = ($1) WHERE (user_id = $2)",
|
||||
expectedStmt: "UPDATE zitadel.projections.users_humans SET (avatar_key) = ($1) WHERE (user_id = $2)",
|
||||
expectedArgs: []interface{}{
|
||||
nil,
|
||||
"agg-id",
|
||||
@@ -1098,7 +1104,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username) VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
@@ -1107,6 +1113,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
domain.UserStateInitial,
|
||||
uint64(15),
|
||||
"username",
|
||||
domain.UserTypeMachine,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1143,7 +1150,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username) VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
@@ -1152,6 +1159,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
domain.UserStateInitial,
|
||||
uint64(15),
|
||||
"username",
|
||||
domain.UserTypeMachine,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@@ -44,6 +44,7 @@ const sqlPlaceholder = "?"
|
||||
|
||||
type SearchQuery interface {
|
||||
toQuery(sq.SelectBuilder) sq.SelectBuilder
|
||||
comp() sq.Sqlizer
|
||||
}
|
||||
|
||||
type NotNullQuery struct {
|
||||
@@ -60,7 +61,34 @@ func NewNotNullQuery(col Column) (*NotNullQuery, error) {
|
||||
}
|
||||
|
||||
func (q *NotNullQuery) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||
return query.Where(sq.NotEq{q.Column.identifier(): nil})
|
||||
return query.Where(q.comp())
|
||||
}
|
||||
|
||||
func (q *NotNullQuery) comp() sq.Sqlizer {
|
||||
return sq.NotEq{q.Column.identifier(): nil}
|
||||
}
|
||||
|
||||
type orQuery struct {
|
||||
queries []SearchQuery
|
||||
}
|
||||
|
||||
func newOrQuery(queries ...SearchQuery) (*orQuery, error) {
|
||||
if len(queries) == 0 {
|
||||
return nil, ErrMissingColumn
|
||||
}
|
||||
return &orQuery{queries: queries}, nil
|
||||
}
|
||||
|
||||
func (q *orQuery) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||
return query.Where(q.comp())
|
||||
}
|
||||
|
||||
func (q *orQuery) comp() sq.Sqlizer {
|
||||
or := make(sq.Or, len(q.queries))
|
||||
for i, query := range q.queries {
|
||||
or[i] = query.comp()
|
||||
}
|
||||
return or
|
||||
}
|
||||
|
||||
type TextQuery struct {
|
||||
@@ -90,32 +118,31 @@ func NewTextQuery(col Column, value string, compare TextComparison) (*TextQuery,
|
||||
}
|
||||
|
||||
func (q *TextQuery) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||
where, args := q.comp()
|
||||
return query.Where(where, args...)
|
||||
return query.Where(q.comp())
|
||||
}
|
||||
|
||||
func (s *TextQuery) comp() (comparison interface{}, args []interface{}) {
|
||||
func (s *TextQuery) comp() sq.Sqlizer {
|
||||
switch s.Compare {
|
||||
case TextEquals:
|
||||
return sq.Eq{s.Column.identifier(): s.Text}, nil
|
||||
return sq.Eq{s.Column.identifier(): s.Text}
|
||||
case TextEqualsIgnoreCase:
|
||||
return sq.ILike{s.Column.identifier(): s.Text}, nil
|
||||
return sq.ILike{s.Column.identifier(): s.Text}
|
||||
case TextStartsWith:
|
||||
return sq.Like{s.Column.identifier(): s.Text + "%"}, nil
|
||||
return sq.Like{s.Column.identifier(): s.Text + "%"}
|
||||
case TextStartsWithIgnoreCase:
|
||||
return sq.ILike{s.Column.identifier(): s.Text + "%"}, nil
|
||||
return sq.ILike{s.Column.identifier(): s.Text + "%"}
|
||||
case TextEndsWith:
|
||||
return sq.Like{s.Column.identifier(): "%" + s.Text}, nil
|
||||
return sq.Like{s.Column.identifier(): "%" + s.Text}
|
||||
case TextEndsWithIgnoreCase:
|
||||
return sq.ILike{s.Column.identifier(): "%" + s.Text}, nil
|
||||
return sq.ILike{s.Column.identifier(): "%" + s.Text}
|
||||
case TextContains:
|
||||
return sq.Like{s.Column.identifier(): "%" + s.Text + "%"}, nil
|
||||
return sq.Like{s.Column.identifier(): "%" + s.Text + "%"}
|
||||
case TextContainsIgnoreCase:
|
||||
return sq.ILike{s.Column.identifier(): "%" + s.Text + "%"}, nil
|
||||
return sq.ILike{s.Column.identifier(): "%" + s.Text + "%"}
|
||||
case TextListContains:
|
||||
return s.Column.identifier() + " @> ? ", []interface{}{pq.StringArray{s.Text}}
|
||||
return &listContains{col: s.Column, args: []interface{}{pq.StringArray{s.Text}}}
|
||||
}
|
||||
return nil, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
type TextComparison int
|
||||
@@ -187,24 +214,23 @@ func NewNumberQuery(c Column, value interface{}, compare NumberComparison) (*Num
|
||||
}
|
||||
|
||||
func (q *NumberQuery) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||
where, args := q.comp()
|
||||
return query.Where(where, args...)
|
||||
return query.Where(q.comp())
|
||||
}
|
||||
|
||||
func (s *NumberQuery) comp() (comparison interface{}, args []interface{}) {
|
||||
func (s *NumberQuery) comp() sq.Sqlizer {
|
||||
switch s.Compare {
|
||||
case NumberEquals:
|
||||
return sq.Eq{s.Column.identifier(): s.Number}, nil
|
||||
return sq.Eq{s.Column.identifier(): s.Number}
|
||||
case NumberNotEquals:
|
||||
return sq.NotEq{s.Column.identifier(): s.Number}, nil
|
||||
return sq.NotEq{s.Column.identifier(): s.Number}
|
||||
case NumberLess:
|
||||
return sq.Lt{s.Column.identifier(): s.Number}, nil
|
||||
return sq.Lt{s.Column.identifier(): s.Number}
|
||||
case NumberGreater:
|
||||
return sq.Gt{s.Column.identifier(): s.Number}, nil
|
||||
return sq.Gt{s.Column.identifier(): s.Number}
|
||||
case NumberListContains:
|
||||
return s.Column.identifier() + " @> ? ", []interface{}{pq.Array(s.Number)}
|
||||
return &listContains{col: s.Column, args: []interface{}{pq.GenericArray{s.Number}}}
|
||||
}
|
||||
return nil, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
type NumberComparison int
|
||||
@@ -258,16 +284,15 @@ func NewListQuery(column Column, value []interface{}, compare ListComparison) (*
|
||||
}
|
||||
|
||||
func (q *ListQuery) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||
where, args := q.comp()
|
||||
return query.Where(where, args...)
|
||||
return query.Where(q.comp())
|
||||
}
|
||||
|
||||
func (s *ListQuery) comp() (interface{}, []interface{}) {
|
||||
func (s *ListQuery) comp() sq.Sqlizer {
|
||||
switch s.Compare {
|
||||
case ListIn:
|
||||
return sq.Eq{s.Column.identifier(): s.List}, nil
|
||||
return sq.Eq{s.Column.identifier(): s.List}
|
||||
}
|
||||
return nil, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
type ListComparison int
|
||||
@@ -300,12 +325,11 @@ func NewBoolQuery(c Column, value bool) (*BoolQuery, error) {
|
||||
}
|
||||
|
||||
func (q *BoolQuery) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||
where, args := q.comp()
|
||||
return query.Where(where, args...)
|
||||
return query.Where(q.comp())
|
||||
}
|
||||
|
||||
func (s *BoolQuery) comp() (comparison interface{}, args []interface{}) {
|
||||
return sq.Eq{s.Column.identifier(): s.Value}, nil
|
||||
func (s *BoolQuery) comp() sq.Sqlizer {
|
||||
return sq.Eq{s.Column.identifier(): s.Value}
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -367,3 +391,12 @@ func (c Column) isZero() bool {
|
||||
func join(join, from Column) string {
|
||||
return join.table.identifier() + " ON " + from.identifier() + " = " + join.identifier()
|
||||
}
|
||||
|
||||
type listContains struct {
|
||||
col Column
|
||||
args []interface{}
|
||||
}
|
||||
|
||||
func (q *listContains) ToSql() (string, []interface{}, error) {
|
||||
return q.col.identifier() + " @> ? ", q.args, nil
|
||||
}
|
||||
|
@@ -230,7 +230,6 @@ func TestTextQuery_comp(t *testing.T) {
|
||||
}
|
||||
type want struct {
|
||||
query interface{}
|
||||
args []interface{}
|
||||
isNil bool
|
||||
}
|
||||
tests := []struct {
|
||||
@@ -247,7 +246,6 @@ func TestTextQuery_comp(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
query: sq.Eq{"test_table.test_col": "Hurst"},
|
||||
args: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -259,7 +257,6 @@ func TestTextQuery_comp(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
query: sq.ILike{"test_table.test_col": "Hurst"},
|
||||
args: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -271,7 +268,6 @@ func TestTextQuery_comp(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
query: sq.Like{"test_table.test_col": "Hurst%"},
|
||||
args: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -283,7 +279,6 @@ func TestTextQuery_comp(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
query: sq.ILike{"test_table.test_col": "Hurst%"},
|
||||
args: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -295,7 +290,6 @@ func TestTextQuery_comp(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
query: sq.Like{"test_table.test_col": "%Hurst"},
|
||||
args: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -307,7 +301,6 @@ func TestTextQuery_comp(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
query: sq.ILike{"test_table.test_col": "%Hurst"},
|
||||
args: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -319,7 +312,6 @@ func TestTextQuery_comp(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
query: sq.Like{"test_table.test_col": "%Hurst%"},
|
||||
args: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -331,7 +323,6 @@ func TestTextQuery_comp(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
query: sq.ILike{"test_table.test_col": "%Hurst%"},
|
||||
args: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -342,8 +333,10 @@ func TestTextQuery_comp(t *testing.T) {
|
||||
Compare: TextListContains,
|
||||
},
|
||||
want: want{
|
||||
query: "test_table.test_col @> ? ",
|
||||
args: []interface{}{pq.StringArray{"Hurst"}},
|
||||
query: &listContains{
|
||||
col: testCol,
|
||||
args: []interface{}{pq.StringArray{"Hurst"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -376,7 +369,7 @@ func TestTextQuery_comp(t *testing.T) {
|
||||
Text: tt.fields.Text,
|
||||
Compare: tt.fields.Compare,
|
||||
}
|
||||
query, args := s.comp()
|
||||
query := s.comp()
|
||||
if query == nil && tt.want.isNil {
|
||||
return
|
||||
} else if tt.want.isNil && query != nil {
|
||||
@@ -386,10 +379,6 @@ func TestTextQuery_comp(t *testing.T) {
|
||||
if !reflect.DeepEqual(query, tt.want.query) {
|
||||
t.Errorf("wrong query: want: %v, (%T), got: %v, (%T)", tt.want.query, tt.want.query, query, query)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(args, tt.want.args) {
|
||||
t.Errorf("wrong args: want: %v, (%T), got: %v (%T)", tt.want.args, tt.want.args, args, args)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -589,7 +578,6 @@ func TestNumberQuery_comp(t *testing.T) {
|
||||
}
|
||||
type want struct {
|
||||
query interface{}
|
||||
args []interface{}
|
||||
isNil bool
|
||||
}
|
||||
tests := []struct {
|
||||
@@ -606,7 +594,6 @@ func TestNumberQuery_comp(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
query: sq.Eq{"test_table.test_col": 42},
|
||||
args: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -618,7 +605,6 @@ func TestNumberQuery_comp(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
query: sq.NotEq{"test_table.test_col": 42},
|
||||
args: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -630,7 +616,6 @@ func TestNumberQuery_comp(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
query: sq.Lt{"test_table.test_col": 42},
|
||||
args: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -642,7 +627,6 @@ func TestNumberQuery_comp(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
query: sq.Gt{"test_table.test_col": 42},
|
||||
args: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -653,8 +637,10 @@ func TestNumberQuery_comp(t *testing.T) {
|
||||
Compare: NumberListContains,
|
||||
},
|
||||
want: want{
|
||||
query: "test_table.test_col @> ? ",
|
||||
args: []interface{}{pq.Array(42)},
|
||||
query: &listContains{
|
||||
col: testCol,
|
||||
args: []interface{}{pq.GenericArray{42}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -687,7 +673,7 @@ func TestNumberQuery_comp(t *testing.T) {
|
||||
Number: tt.fields.Number,
|
||||
Compare: tt.fields.Compare,
|
||||
}
|
||||
query, args := s.comp()
|
||||
query := s.comp()
|
||||
if query == nil && tt.want.isNil {
|
||||
return
|
||||
} else if tt.want.isNil && query != nil {
|
||||
@@ -697,10 +683,6 @@ func TestNumberQuery_comp(t *testing.T) {
|
||||
if !reflect.DeepEqual(query, tt.want.query) {
|
||||
t.Errorf("wrong query: want: %v, (%T), got: %v, (%T)", tt.want.query, tt.want.query, query, query)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(args, tt.want.args) {
|
||||
t.Errorf("wrong args: want: %v, (%T), got: %v (%T)", tt.want.args, tt.want.args, args, args)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -39,6 +39,10 @@ var (
|
||||
name: projection.UserUsernameCol,
|
||||
table: userTable,
|
||||
}
|
||||
UserTypeCol = Column{
|
||||
name: projection.UserTypeCol,
|
||||
table: userTable,
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
|
449
internal/query/user_grant.go
Normal file
449
internal/query/user_grant.go
Normal file
@@ -0,0 +1,449 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
errs "errors"
|
||||
"time"
|
||||
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
"github.com/lib/pq"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/query/projection"
|
||||
)
|
||||
|
||||
type UserGrant struct {
|
||||
ID string
|
||||
CreationDate time.Time
|
||||
ChangeDate time.Time
|
||||
Sequence uint64
|
||||
Roles []string
|
||||
GrantID string
|
||||
State domain.UserGrantState
|
||||
|
||||
UserID string
|
||||
Username string
|
||||
UserType domain.UserType
|
||||
UserResourceOwner string
|
||||
FirstName string
|
||||
LastName string
|
||||
Email string
|
||||
DisplayName string
|
||||
AvatarURL string
|
||||
|
||||
ResourceOwner string
|
||||
OrgName string
|
||||
OrgPrimaryDomain string
|
||||
|
||||
ProjectID string
|
||||
ProjectName string
|
||||
}
|
||||
|
||||
type UserGrants struct {
|
||||
SearchResponse
|
||||
UserGrants []*UserGrant
|
||||
}
|
||||
|
||||
type UserGrantsQueries struct {
|
||||
SearchRequest
|
||||
Queries []SearchQuery
|
||||
}
|
||||
|
||||
func (q *UserGrantsQueries) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||
query = q.SearchRequest.toQuery(query)
|
||||
for _, q := range q.Queries {
|
||||
query = q.toQuery(query)
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
func NewUserGrantUserIDSearchQuery(id string) (SearchQuery, error) {
|
||||
return NewTextQuery(UserGrantUserID, id, TextEquals)
|
||||
}
|
||||
|
||||
func NewUserGrantProjectIDSearchQuery(id string) (SearchQuery, error) {
|
||||
return NewTextQuery(UserGrantProjectID, id, TextEquals)
|
||||
}
|
||||
|
||||
func NewUserGrantProjectOwnerSearchQuery(id string) (SearchQuery, error) {
|
||||
return NewTextQuery(ProjectColumnResourceOwner, id, TextEquals)
|
||||
}
|
||||
|
||||
func NewUserGrantResourceOwnerSearchQuery(id string) (SearchQuery, error) {
|
||||
return NewTextQuery(UserGrantResourceOwner, id, TextEquals)
|
||||
}
|
||||
|
||||
func NewUserGrantGrantIDSearchQuery(id string) (SearchQuery, error) {
|
||||
return NewTextQuery(UserGrantGrantID, id, TextEquals)
|
||||
}
|
||||
|
||||
func NewUserGrantUserTypeQuery(typ domain.UserType) (SearchQuery, error) {
|
||||
return NewNumberQuery(UserTypeCol, typ, NumberEquals)
|
||||
}
|
||||
|
||||
func NewUserGrantDisplayNameQuery(displayName string, method TextComparison) (SearchQuery, error) {
|
||||
return NewTextQuery(HumanDisplayNameCol, displayName, method)
|
||||
}
|
||||
|
||||
func NewUserGrantEmailQuery(email string, method TextComparison) (SearchQuery, error) {
|
||||
return NewTextQuery(HumanEmailCol, email, method)
|
||||
}
|
||||
|
||||
func NewUserGrantFirstNameQuery(value string, method TextComparison) (SearchQuery, error) {
|
||||
return NewTextQuery(HumanFirstNameCol, value, method)
|
||||
}
|
||||
|
||||
func NewUserGrantLastNameQuery(value string, method TextComparison) (SearchQuery, error) {
|
||||
return NewTextQuery(HumanLastNameCol, value, method)
|
||||
}
|
||||
|
||||
func NewUserGrantUsernameQuery(value string, method TextComparison) (SearchQuery, error) {
|
||||
return NewTextQuery(UserUsernameCol, value, method)
|
||||
}
|
||||
|
||||
func NewUserGrantDomainQuery(value string, method TextComparison) (SearchQuery, error) {
|
||||
return NewTextQuery(OrgColumnDomain, value, method)
|
||||
}
|
||||
|
||||
func NewUserGrantOrgNameQuery(value string, method TextComparison) (SearchQuery, error) {
|
||||
return NewTextQuery(OrgColumnName, value, method)
|
||||
}
|
||||
|
||||
func NewUserGrantProjectNameQuery(value string, method TextComparison) (SearchQuery, error) {
|
||||
return NewTextQuery(ProjectColumnName, value, method)
|
||||
}
|
||||
|
||||
func NewUserGrantRoleQuery(value string) (SearchQuery, error) {
|
||||
return NewTextQuery(UserGrantRoles, value, TextListContains)
|
||||
}
|
||||
|
||||
func NewUserGrantWithGrantedQuery(owner string) (SearchQuery, error) {
|
||||
orgQuery, err := NewUserGrantResourceOwnerSearchQuery(owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
projectQuery, err := NewUserGrantProjectOwnerSearchQuery(owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newOrQuery(orgQuery, projectQuery)
|
||||
}
|
||||
|
||||
func NewUserGrantContainsRolesSearchQuery(roles ...string) (SearchQuery, error) {
|
||||
r := make([]interface{}, len(roles))
|
||||
for i, role := range roles {
|
||||
r[i] = role
|
||||
}
|
||||
return NewListQuery(UserGrantRoles, r, ListIn)
|
||||
}
|
||||
|
||||
var (
|
||||
userGrantTable = table{
|
||||
name: projection.UserGrantProjectionTable,
|
||||
}
|
||||
UserGrantID = Column{
|
||||
name: projection.UserGrantID,
|
||||
table: userGrantTable,
|
||||
}
|
||||
UserGrantResourceOwner = Column{
|
||||
name: projection.UserGrantResourceOwner,
|
||||
table: userGrantTable,
|
||||
}
|
||||
UserGrantCreationDate = Column{
|
||||
name: projection.UserGrantCreationDate,
|
||||
table: userGrantTable,
|
||||
}
|
||||
UserGrantChangeDate = Column{
|
||||
name: projection.UserGrantChangeDate,
|
||||
table: userGrantTable,
|
||||
}
|
||||
UserGrantSequence = Column{
|
||||
name: projection.UserGrantSequence,
|
||||
table: userGrantTable,
|
||||
}
|
||||
UserGrantUserID = Column{
|
||||
name: projection.UserGrantUserID,
|
||||
table: userGrantTable,
|
||||
}
|
||||
UserGrantProjectID = Column{
|
||||
name: projection.UserGrantProjectID,
|
||||
table: userGrantTable,
|
||||
}
|
||||
UserGrantGrantID = Column{
|
||||
name: projection.UserGrantGrantID,
|
||||
table: userGrantTable,
|
||||
}
|
||||
UserGrantRoles = Column{
|
||||
name: projection.UserGrantRoles,
|
||||
table: userGrantTable,
|
||||
}
|
||||
UserGrantState = Column{
|
||||
name: projection.UserGrantState,
|
||||
table: userGrantTable,
|
||||
}
|
||||
)
|
||||
|
||||
func (q *Queries) UserGrantByID(ctx context.Context, id string, queries ...SearchQuery) (*UserGrant, error) {
|
||||
query, scan := prepareUserGrantQuery()
|
||||
for _, q := range queries {
|
||||
query = q.toQuery(query)
|
||||
}
|
||||
stmt, args, err := query.Where(sq.Eq{
|
||||
UserGrantID.identifier(): id,
|
||||
}).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Fa1KW", "Errors.Query.SQLStatement")
|
||||
}
|
||||
|
||||
row := q.client.QueryRowContext(ctx, stmt, args...)
|
||||
return scan(row)
|
||||
}
|
||||
|
||||
func (q *Queries) UserGrants(ctx context.Context, queries *UserGrantsQueries) (*UserGrants, error) {
|
||||
query, scan := prepareUserGrantsQuery()
|
||||
stmt, args, err := queries.toQuery(query).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-wXnQR", "Errors.Query.SQLStatement")
|
||||
}
|
||||
|
||||
latestSequence, err := q.latestSequence(ctx, userGrantTable)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows, err := q.client.QueryContext(ctx, stmt, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
grants, err := scan(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
grants.LatestSequence = latestSequence
|
||||
return grants, nil
|
||||
}
|
||||
|
||||
func prepareUserGrantQuery() (sq.SelectBuilder, func(*sql.Row) (*UserGrant, error)) {
|
||||
return sq.Select(
|
||||
UserGrantID.identifier(),
|
||||
UserGrantCreationDate.identifier(),
|
||||
UserGrantChangeDate.identifier(),
|
||||
UserGrantSequence.identifier(),
|
||||
UserGrantGrantID.identifier(),
|
||||
UserGrantRoles.identifier(),
|
||||
UserGrantState.identifier(),
|
||||
|
||||
UserGrantUserID.identifier(),
|
||||
UserUsernameCol.identifier(),
|
||||
UserTypeCol.identifier(),
|
||||
UserResourceOwnerCol.identifier(),
|
||||
HumanFirstNameCol.identifier(),
|
||||
HumanLastNameCol.identifier(),
|
||||
HumanEmailCol.identifier(),
|
||||
HumanDisplayNameCol.identifier(),
|
||||
HumanAvaterURLCol.identifier(),
|
||||
|
||||
UserGrantResourceOwner.identifier(),
|
||||
OrgColumnName.identifier(),
|
||||
OrgColumnDomain.identifier(),
|
||||
|
||||
UserGrantProjectID.identifier(),
|
||||
ProjectColumnName.identifier(),
|
||||
).
|
||||
From(userGrantTable.identifier()).
|
||||
LeftJoin(join(UserIDCol, UserGrantUserID)).
|
||||
LeftJoin(join(HumanUserIDCol, UserGrantUserID)).
|
||||
LeftJoin(join(OrgColumnID, UserGrantResourceOwner)).
|
||||
LeftJoin(join(ProjectColumnID, UserGrantProjectID)).
|
||||
PlaceholderFormat(sq.Dollar),
|
||||
func(row *sql.Row) (*UserGrant, error) {
|
||||
g := new(UserGrant)
|
||||
|
||||
var (
|
||||
roles = pq.StringArray{}
|
||||
username sql.NullString
|
||||
firstName sql.NullString
|
||||
userType sql.NullInt32
|
||||
userOwner sql.NullString
|
||||
lastName sql.NullString
|
||||
email sql.NullString
|
||||
displayName sql.NullString
|
||||
avatarURL sql.NullString
|
||||
|
||||
orgName sql.NullString
|
||||
orgDomain sql.NullString
|
||||
|
||||
projectName sql.NullString
|
||||
)
|
||||
|
||||
err := row.Scan(
|
||||
&g.ID,
|
||||
&g.CreationDate,
|
||||
&g.ChangeDate,
|
||||
&g.Sequence,
|
||||
&g.GrantID,
|
||||
&roles,
|
||||
&g.State,
|
||||
|
||||
&g.UserID,
|
||||
&username,
|
||||
&userType,
|
||||
&userOwner,
|
||||
&firstName,
|
||||
&lastName,
|
||||
&email,
|
||||
&displayName,
|
||||
&avatarURL,
|
||||
|
||||
&g.ResourceOwner,
|
||||
&orgName,
|
||||
&orgDomain,
|
||||
|
||||
&g.ProjectID,
|
||||
&projectName,
|
||||
)
|
||||
if err != nil {
|
||||
if errs.Is(err, sql.ErrNoRows) {
|
||||
return nil, errors.ThrowNotFound(err, "QUERY-wIPkA", "Errors.UserGrant.NotFound")
|
||||
}
|
||||
return nil, errors.ThrowInternal(err, "QUERY-oQPcP", "Errors.Internal")
|
||||
}
|
||||
|
||||
g.Roles = roles
|
||||
g.Username = username.String
|
||||
g.UserType = domain.UserType(userType.Int32)
|
||||
g.UserResourceOwner = userOwner.String
|
||||
g.FirstName = firstName.String
|
||||
g.LastName = lastName.String
|
||||
g.Email = email.String
|
||||
g.DisplayName = displayName.String
|
||||
g.AvatarURL = avatarURL.String
|
||||
g.OrgName = orgName.String
|
||||
g.OrgPrimaryDomain = orgDomain.String
|
||||
g.ProjectName = projectName.String
|
||||
|
||||
return g, nil
|
||||
}
|
||||
}
|
||||
|
||||
func prepareUserGrantsQuery() (sq.SelectBuilder, func(*sql.Rows) (*UserGrants, error)) {
|
||||
return sq.Select(
|
||||
UserGrantID.identifier(),
|
||||
UserGrantCreationDate.identifier(),
|
||||
UserGrantChangeDate.identifier(),
|
||||
UserGrantSequence.identifier(),
|
||||
UserGrantGrantID.identifier(),
|
||||
UserGrantRoles.identifier(),
|
||||
UserGrantState.identifier(),
|
||||
|
||||
UserGrantUserID.identifier(),
|
||||
UserUsernameCol.identifier(),
|
||||
UserTypeCol.identifier(),
|
||||
UserResourceOwnerCol.identifier(),
|
||||
HumanFirstNameCol.identifier(),
|
||||
HumanLastNameCol.identifier(),
|
||||
HumanEmailCol.identifier(),
|
||||
HumanDisplayNameCol.identifier(),
|
||||
HumanAvaterURLCol.identifier(),
|
||||
|
||||
UserGrantResourceOwner.identifier(),
|
||||
OrgColumnName.identifier(),
|
||||
OrgColumnDomain.identifier(),
|
||||
|
||||
UserGrantProjectID.identifier(),
|
||||
ProjectColumnName.identifier(),
|
||||
|
||||
countColumn.identifier(),
|
||||
).
|
||||
From(userGrantTable.identifier()).
|
||||
LeftJoin(join(UserIDCol, UserGrantUserID)).
|
||||
LeftJoin(join(HumanUserIDCol, UserGrantUserID)).
|
||||
LeftJoin(join(OrgColumnID, UserGrantResourceOwner)).
|
||||
LeftJoin(join(ProjectColumnID, UserGrantProjectID)).
|
||||
PlaceholderFormat(sq.Dollar),
|
||||
func(rows *sql.Rows) (*UserGrants, error) {
|
||||
userGrants := make([]*UserGrant, 0)
|
||||
var count uint64
|
||||
for rows.Next() {
|
||||
g := new(UserGrant)
|
||||
|
||||
var (
|
||||
roles = pq.StringArray{}
|
||||
username sql.NullString
|
||||
userType sql.NullInt32
|
||||
userOwner sql.NullString
|
||||
firstName sql.NullString
|
||||
lastName sql.NullString
|
||||
email sql.NullString
|
||||
displayName sql.NullString
|
||||
avatarURL sql.NullString
|
||||
|
||||
orgName sql.NullString
|
||||
orgDomain sql.NullString
|
||||
|
||||
projectName sql.NullString
|
||||
)
|
||||
|
||||
err := rows.Scan(
|
||||
&g.ID,
|
||||
&g.CreationDate,
|
||||
&g.ChangeDate,
|
||||
&g.Sequence,
|
||||
&g.GrantID,
|
||||
&roles,
|
||||
&g.State,
|
||||
|
||||
&g.UserID,
|
||||
&username,
|
||||
&userType,
|
||||
&userOwner,
|
||||
&firstName,
|
||||
&lastName,
|
||||
&email,
|
||||
&displayName,
|
||||
&avatarURL,
|
||||
|
||||
&g.ResourceOwner,
|
||||
&orgName,
|
||||
&orgDomain,
|
||||
|
||||
&g.ProjectID,
|
||||
&projectName,
|
||||
|
||||
&count,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g.Roles = roles
|
||||
g.Username = username.String
|
||||
g.UserType = domain.UserType(userType.Int32)
|
||||
g.UserResourceOwner = userOwner.String
|
||||
g.FirstName = firstName.String
|
||||
g.LastName = lastName.String
|
||||
g.Email = email.String
|
||||
g.DisplayName = displayName.String
|
||||
g.AvatarURL = avatarURL.String
|
||||
g.OrgName = orgName.String
|
||||
g.OrgPrimaryDomain = orgDomain.String
|
||||
g.ProjectName = projectName.String
|
||||
|
||||
userGrants = append(userGrants, g)
|
||||
}
|
||||
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-iGvmP", "Errors.Query.CloseRows")
|
||||
}
|
||||
|
||||
return &UserGrants{
|
||||
UserGrants: userGrants,
|
||||
SearchResponse: SearchResponse{
|
||||
Count: count,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
778
internal/query/user_grant_test.go
Normal file
778
internal/query/user_grant_test.go
Normal file
@@ -0,0 +1,778 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/lib/pq"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
errs "github.com/caos/zitadel/internal/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
userGrantStmt = regexp.QuoteMeta(
|
||||
"SELECT zitadel.projections.user_grants.id" +
|
||||
", zitadel.projections.user_grants.creation_date" +
|
||||
", zitadel.projections.user_grants.change_date" +
|
||||
", zitadel.projections.user_grants.sequence" +
|
||||
", zitadel.projections.user_grants.grant_id" +
|
||||
", zitadel.projections.user_grants.roles" +
|
||||
", zitadel.projections.user_grants.state" +
|
||||
", zitadel.projections.user_grants.user_id" +
|
||||
", zitadel.projections.users.username" +
|
||||
", zitadel.projections.users.type" +
|
||||
", zitadel.projections.users.resource_owner" +
|
||||
", zitadel.projections.users_humans.first_name" +
|
||||
", zitadel.projections.users_humans.last_name" +
|
||||
", zitadel.projections.users_humans.email" +
|
||||
", zitadel.projections.users_humans.display_name" +
|
||||
", zitadel.projections.users_humans.avatar_key" +
|
||||
", zitadel.projections.user_grants.resource_owner" +
|
||||
", zitadel.projections.orgs.name" +
|
||||
", zitadel.projections.orgs.primary_domain" +
|
||||
", zitadel.projections.user_grants.project_id" +
|
||||
", zitadel.projections.projects.name" +
|
||||
" FROM zitadel.projections.user_grants" +
|
||||
" LEFT JOIN zitadel.projections.users ON zitadel.projections.user_grants.user_id = zitadel.projections.users.id" +
|
||||
" LEFT JOIN zitadel.projections.users_humans ON zitadel.projections.user_grants.user_id = zitadel.projections.users_humans.user_id" +
|
||||
" LEFT JOIN zitadel.projections.orgs ON zitadel.projections.user_grants.resource_owner = zitadel.projections.orgs.id" +
|
||||
" LEFT JOIN zitadel.projections.projects ON zitadel.projections.user_grants.project_id = zitadel.projections.projects.id")
|
||||
userGrantCols = []string{
|
||||
"id",
|
||||
"creation_date",
|
||||
"change_date",
|
||||
"sequence",
|
||||
"grant_id",
|
||||
"roles",
|
||||
"state",
|
||||
"user_id",
|
||||
"username",
|
||||
"type",
|
||||
"resource_owner", //user resource owner
|
||||
"first_name",
|
||||
"last_name",
|
||||
"email",
|
||||
"display_name",
|
||||
"avatar_key",
|
||||
"resource_owner", //user_grant resource owner
|
||||
"name", //org name
|
||||
"primary_domain",
|
||||
"project_id",
|
||||
"name", //project name
|
||||
}
|
||||
userGrantsStmt = regexp.QuoteMeta(
|
||||
"SELECT zitadel.projections.user_grants.id" +
|
||||
", zitadel.projections.user_grants.creation_date" +
|
||||
", zitadel.projections.user_grants.change_date" +
|
||||
", zitadel.projections.user_grants.sequence" +
|
||||
", zitadel.projections.user_grants.grant_id" +
|
||||
", zitadel.projections.user_grants.roles" +
|
||||
", zitadel.projections.user_grants.state" +
|
||||
", zitadel.projections.user_grants.user_id" +
|
||||
", zitadel.projections.users.username" +
|
||||
", zitadel.projections.users.type" +
|
||||
", zitadel.projections.users.resource_owner" +
|
||||
", zitadel.projections.users_humans.first_name" +
|
||||
", zitadel.projections.users_humans.last_name" +
|
||||
", zitadel.projections.users_humans.email" +
|
||||
", zitadel.projections.users_humans.display_name" +
|
||||
", zitadel.projections.users_humans.avatar_key" +
|
||||
", zitadel.projections.user_grants.resource_owner" +
|
||||
", zitadel.projections.orgs.name" +
|
||||
", zitadel.projections.orgs.primary_domain" +
|
||||
", zitadel.projections.user_grants.project_id" +
|
||||
", zitadel.projections.projects.name" +
|
||||
", COUNT(*) OVER ()" +
|
||||
" FROM zitadel.projections.user_grants" +
|
||||
" LEFT JOIN zitadel.projections.users ON zitadel.projections.user_grants.user_id = zitadel.projections.users.id" +
|
||||
" LEFT JOIN zitadel.projections.users_humans ON zitadel.projections.user_grants.user_id = zitadel.projections.users_humans.user_id" +
|
||||
" LEFT JOIN zitadel.projections.orgs ON zitadel.projections.user_grants.resource_owner = zitadel.projections.orgs.id" +
|
||||
" LEFT JOIN zitadel.projections.projects ON zitadel.projections.user_grants.project_id = zitadel.projections.projects.id")
|
||||
userGrantsCols = append(
|
||||
userGrantCols,
|
||||
"count",
|
||||
)
|
||||
)
|
||||
|
||||
func Test_UserGrantPrepares(t *testing.T) {
|
||||
type want struct {
|
||||
sqlExpectations sqlExpectation
|
||||
err checkErr
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
prepare interface{}
|
||||
want want
|
||||
object interface{}
|
||||
}{
|
||||
{
|
||||
name: "prepareUserGrantQuery no result",
|
||||
prepare: prepareUserGrantQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQueries(
|
||||
userGrantStmt,
|
||||
nil,
|
||||
nil,
|
||||
),
|
||||
err: func(err error) (error, bool) {
|
||||
if !errs.IsNotFound(err) {
|
||||
return fmt.Errorf("err should be zitadel.NotFoundError got: %w", err), false
|
||||
}
|
||||
return nil, true
|
||||
},
|
||||
},
|
||||
object: (*UserGrant)(nil),
|
||||
},
|
||||
{
|
||||
name: "prepareUserGrantQuery found",
|
||||
prepare: prepareUserGrantQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQuery(
|
||||
userGrantStmt,
|
||||
userGrantCols,
|
||||
[]driver.Value{
|
||||
"id",
|
||||
testNow,
|
||||
testNow,
|
||||
20211111,
|
||||
"grant-id",
|
||||
pq.StringArray{"role-key"},
|
||||
domain.UserGrantStateActive,
|
||||
"user-id",
|
||||
"username",
|
||||
domain.UserTypeHuman,
|
||||
"resource-owner",
|
||||
"first-name",
|
||||
"last-name",
|
||||
"email",
|
||||
"display-name",
|
||||
"avatar-key",
|
||||
"ro",
|
||||
"org-name",
|
||||
"primary-domain",
|
||||
"project-id",
|
||||
"project-name",
|
||||
},
|
||||
),
|
||||
},
|
||||
object: &UserGrant{
|
||||
ID: "id",
|
||||
CreationDate: testNow,
|
||||
ChangeDate: testNow,
|
||||
Sequence: 20211111,
|
||||
Roles: []string{"role-key"},
|
||||
GrantID: "grant-id",
|
||||
State: domain.UserGrantStateActive,
|
||||
UserID: "user-id",
|
||||
Username: "username",
|
||||
UserType: domain.UserTypeHuman,
|
||||
UserResourceOwner: "resource-owner",
|
||||
FirstName: "first-name",
|
||||
LastName: "last-name",
|
||||
Email: "email",
|
||||
DisplayName: "display-name",
|
||||
AvatarURL: "avatar-key",
|
||||
ResourceOwner: "ro",
|
||||
OrgName: "org-name",
|
||||
OrgPrimaryDomain: "primary-domain",
|
||||
ProjectID: "project-id",
|
||||
ProjectName: "project-name",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "prepareUserGrantQuery machine user found",
|
||||
prepare: prepareUserGrantQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQuery(
|
||||
userGrantStmt,
|
||||
userGrantCols,
|
||||
[]driver.Value{
|
||||
"id",
|
||||
testNow,
|
||||
testNow,
|
||||
20211111,
|
||||
"grant-id",
|
||||
pq.StringArray{"role-key"},
|
||||
domain.UserGrantStateActive,
|
||||
"user-id",
|
||||
"username",
|
||||
domain.UserTypeMachine,
|
||||
"resource-owner",
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
"ro",
|
||||
"org-name",
|
||||
"primary-domain",
|
||||
"project-id",
|
||||
"project-name",
|
||||
},
|
||||
),
|
||||
},
|
||||
object: &UserGrant{
|
||||
ID: "id",
|
||||
CreationDate: testNow,
|
||||
ChangeDate: testNow,
|
||||
Sequence: 20211111,
|
||||
Roles: []string{"role-key"},
|
||||
GrantID: "grant-id",
|
||||
State: domain.UserGrantStateActive,
|
||||
UserID: "user-id",
|
||||
Username: "username",
|
||||
UserType: domain.UserTypeMachine,
|
||||
UserResourceOwner: "resource-owner",
|
||||
FirstName: "",
|
||||
LastName: "",
|
||||
Email: "",
|
||||
DisplayName: "",
|
||||
AvatarURL: "",
|
||||
ResourceOwner: "ro",
|
||||
OrgName: "org-name",
|
||||
OrgPrimaryDomain: "primary-domain",
|
||||
ProjectID: "project-id",
|
||||
ProjectName: "project-name",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "prepareUserGrantQuery (no org) found",
|
||||
prepare: prepareUserGrantQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQuery(
|
||||
userGrantStmt,
|
||||
userGrantCols,
|
||||
[]driver.Value{
|
||||
"id",
|
||||
testNow,
|
||||
testNow,
|
||||
20211111,
|
||||
"grant-id",
|
||||
pq.StringArray{"role-key"},
|
||||
domain.UserGrantStateActive,
|
||||
"user-id",
|
||||
"username",
|
||||
domain.UserTypeHuman,
|
||||
"resource-owner",
|
||||
"first-name",
|
||||
"last-name",
|
||||
"email",
|
||||
"display-name",
|
||||
"avatar-key",
|
||||
"ro",
|
||||
nil,
|
||||
nil,
|
||||
"project-id",
|
||||
"project-name",
|
||||
},
|
||||
),
|
||||
},
|
||||
object: &UserGrant{
|
||||
ID: "id",
|
||||
CreationDate: testNow,
|
||||
ChangeDate: testNow,
|
||||
Sequence: 20211111,
|
||||
Roles: []string{"role-key"},
|
||||
GrantID: "grant-id",
|
||||
State: domain.UserGrantStateActive,
|
||||
UserID: "user-id",
|
||||
Username: "username",
|
||||
UserType: domain.UserTypeHuman,
|
||||
UserResourceOwner: "resource-owner",
|
||||
FirstName: "first-name",
|
||||
LastName: "last-name",
|
||||
Email: "email",
|
||||
DisplayName: "display-name",
|
||||
AvatarURL: "avatar-key",
|
||||
ResourceOwner: "ro",
|
||||
OrgName: "",
|
||||
OrgPrimaryDomain: "",
|
||||
ProjectID: "project-id",
|
||||
ProjectName: "project-name",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "prepareUserGrantQuery (no project) found",
|
||||
prepare: prepareUserGrantQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQuery(
|
||||
userGrantStmt,
|
||||
userGrantCols,
|
||||
[]driver.Value{
|
||||
"id",
|
||||
testNow,
|
||||
testNow,
|
||||
20211111,
|
||||
"grant-id",
|
||||
pq.StringArray{"role-key"},
|
||||
domain.UserGrantStateActive,
|
||||
"user-id",
|
||||
"username",
|
||||
domain.UserTypeHuman,
|
||||
"resource-owner",
|
||||
"first-name",
|
||||
"last-name",
|
||||
"email",
|
||||
"display-name",
|
||||
"avatar-key",
|
||||
"ro",
|
||||
"org-name",
|
||||
"primary-domain",
|
||||
"project-id",
|
||||
nil,
|
||||
},
|
||||
),
|
||||
},
|
||||
object: &UserGrant{
|
||||
ID: "id",
|
||||
CreationDate: testNow,
|
||||
ChangeDate: testNow,
|
||||
Sequence: 20211111,
|
||||
Roles: []string{"role-key"},
|
||||
GrantID: "grant-id",
|
||||
State: domain.UserGrantStateActive,
|
||||
UserID: "user-id",
|
||||
Username: "username",
|
||||
UserType: domain.UserTypeHuman,
|
||||
UserResourceOwner: "resource-owner",
|
||||
FirstName: "first-name",
|
||||
LastName: "last-name",
|
||||
Email: "email",
|
||||
DisplayName: "display-name",
|
||||
AvatarURL: "avatar-key",
|
||||
ResourceOwner: "ro",
|
||||
OrgName: "org-name",
|
||||
OrgPrimaryDomain: "primary-domain",
|
||||
ProjectID: "project-id",
|
||||
ProjectName: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "prepareUserGrantQuery sql err",
|
||||
prepare: prepareUserGrantQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQueryErr(
|
||||
userGrantStmt,
|
||||
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: nil,
|
||||
},
|
||||
{
|
||||
name: "prepareUserGrantsQuery no result",
|
||||
prepare: prepareUserGrantsQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQueries(
|
||||
userGrantsStmt,
|
||||
nil,
|
||||
nil,
|
||||
),
|
||||
},
|
||||
object: &UserGrants{UserGrants: []*UserGrant{}},
|
||||
},
|
||||
{
|
||||
name: "prepareUserGrantsQuery one grant",
|
||||
prepare: prepareUserGrantsQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQueries(
|
||||
userGrantsStmt,
|
||||
userGrantsCols,
|
||||
[][]driver.Value{
|
||||
{
|
||||
"id",
|
||||
testNow,
|
||||
testNow,
|
||||
20211111,
|
||||
"grant-id",
|
||||
pq.StringArray{"role-key"},
|
||||
domain.UserGrantStateActive,
|
||||
"user-id",
|
||||
"username",
|
||||
domain.UserTypeHuman,
|
||||
"resource-owner",
|
||||
"first-name",
|
||||
"last-name",
|
||||
"email",
|
||||
"display-name",
|
||||
"avatar-key",
|
||||
"ro",
|
||||
"org-name",
|
||||
"primary-domain",
|
||||
"project-id",
|
||||
"project-name",
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
object: &UserGrants{
|
||||
SearchResponse: SearchResponse{
|
||||
Count: 1,
|
||||
},
|
||||
UserGrants: []*UserGrant{
|
||||
{
|
||||
ID: "id",
|
||||
CreationDate: testNow,
|
||||
ChangeDate: testNow,
|
||||
Sequence: 20211111,
|
||||
Roles: []string{"role-key"},
|
||||
GrantID: "grant-id",
|
||||
State: domain.UserGrantStateActive,
|
||||
UserID: "user-id",
|
||||
Username: "username",
|
||||
UserType: domain.UserTypeHuman,
|
||||
UserResourceOwner: "resource-owner",
|
||||
FirstName: "first-name",
|
||||
LastName: "last-name",
|
||||
Email: "email",
|
||||
DisplayName: "display-name",
|
||||
AvatarURL: "avatar-key",
|
||||
ResourceOwner: "ro",
|
||||
OrgName: "org-name",
|
||||
OrgPrimaryDomain: "primary-domain",
|
||||
ProjectID: "project-id",
|
||||
ProjectName: "project-name",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "prepareUserGrantsQuery one grant (machine user)",
|
||||
prepare: prepareUserGrantsQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQueries(
|
||||
userGrantsStmt,
|
||||
userGrantsCols,
|
||||
[][]driver.Value{
|
||||
{
|
||||
"id",
|
||||
testNow,
|
||||
testNow,
|
||||
20211111,
|
||||
"grant-id",
|
||||
pq.StringArray{"role-key"},
|
||||
domain.UserGrantStateActive,
|
||||
"user-id",
|
||||
"username",
|
||||
domain.UserTypeMachine,
|
||||
"resource-owner",
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
"ro",
|
||||
"org-name",
|
||||
"primary-domain",
|
||||
"project-id",
|
||||
"project-name",
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
object: &UserGrants{
|
||||
SearchResponse: SearchResponse{
|
||||
Count: 1,
|
||||
},
|
||||
UserGrants: []*UserGrant{
|
||||
{
|
||||
ID: "id",
|
||||
CreationDate: testNow,
|
||||
ChangeDate: testNow,
|
||||
Sequence: 20211111,
|
||||
Roles: []string{"role-key"},
|
||||
GrantID: "grant-id",
|
||||
State: domain.UserGrantStateActive,
|
||||
UserID: "user-id",
|
||||
Username: "username",
|
||||
UserType: domain.UserTypeMachine,
|
||||
UserResourceOwner: "resource-owner",
|
||||
FirstName: "",
|
||||
LastName: "",
|
||||
Email: "",
|
||||
DisplayName: "",
|
||||
AvatarURL: "",
|
||||
ResourceOwner: "ro",
|
||||
OrgName: "org-name",
|
||||
OrgPrimaryDomain: "primary-domain",
|
||||
ProjectID: "project-id",
|
||||
ProjectName: "project-name",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "prepareUserGrantsQuery one grant (no org)",
|
||||
prepare: prepareUserGrantsQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQueries(
|
||||
userGrantsStmt,
|
||||
userGrantsCols,
|
||||
[][]driver.Value{
|
||||
{
|
||||
"id",
|
||||
testNow,
|
||||
testNow,
|
||||
20211111,
|
||||
"grant-id",
|
||||
pq.StringArray{"role-key"},
|
||||
domain.UserGrantStateActive,
|
||||
"user-id",
|
||||
"username",
|
||||
domain.UserTypeMachine,
|
||||
"resource-owner",
|
||||
"first-name",
|
||||
"last-name",
|
||||
"email",
|
||||
"display-name",
|
||||
"avatar-key",
|
||||
"ro",
|
||||
nil,
|
||||
nil,
|
||||
"project-id",
|
||||
"project-name",
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
object: &UserGrants{
|
||||
SearchResponse: SearchResponse{
|
||||
Count: 1,
|
||||
},
|
||||
UserGrants: []*UserGrant{
|
||||
{
|
||||
ID: "id",
|
||||
CreationDate: testNow,
|
||||
ChangeDate: testNow,
|
||||
Sequence: 20211111,
|
||||
Roles: []string{"role-key"},
|
||||
GrantID: "grant-id",
|
||||
State: domain.UserGrantStateActive,
|
||||
UserID: "user-id",
|
||||
Username: "username",
|
||||
UserType: domain.UserTypeMachine,
|
||||
UserResourceOwner: "resource-owner",
|
||||
FirstName: "first-name",
|
||||
LastName: "last-name",
|
||||
Email: "email",
|
||||
DisplayName: "display-name",
|
||||
AvatarURL: "avatar-key",
|
||||
ResourceOwner: "ro",
|
||||
OrgName: "",
|
||||
OrgPrimaryDomain: "",
|
||||
ProjectID: "project-id",
|
||||
ProjectName: "project-name",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "prepareUserGrantsQuery one grant (no project)",
|
||||
prepare: prepareUserGrantsQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQueries(
|
||||
userGrantsStmt,
|
||||
userGrantsCols,
|
||||
[][]driver.Value{
|
||||
{
|
||||
"id",
|
||||
testNow,
|
||||
testNow,
|
||||
20211111,
|
||||
"grant-id",
|
||||
pq.StringArray{"role-key"},
|
||||
domain.UserGrantStateActive,
|
||||
"user-id",
|
||||
"username",
|
||||
domain.UserTypeHuman,
|
||||
"resource-owner",
|
||||
"first-name",
|
||||
"last-name",
|
||||
"email",
|
||||
"display-name",
|
||||
"avatar-key",
|
||||
"ro",
|
||||
"org-name",
|
||||
"primary-domain",
|
||||
"project-id",
|
||||
nil,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
object: &UserGrants{
|
||||
SearchResponse: SearchResponse{
|
||||
Count: 1,
|
||||
},
|
||||
UserGrants: []*UserGrant{
|
||||
{
|
||||
ID: "id",
|
||||
CreationDate: testNow,
|
||||
ChangeDate: testNow,
|
||||
Sequence: 20211111,
|
||||
Roles: []string{"role-key"},
|
||||
GrantID: "grant-id",
|
||||
State: domain.UserGrantStateActive,
|
||||
UserID: "user-id",
|
||||
Username: "username",
|
||||
UserType: domain.UserTypeHuman,
|
||||
UserResourceOwner: "resource-owner",
|
||||
FirstName: "first-name",
|
||||
LastName: "last-name",
|
||||
Email: "email",
|
||||
DisplayName: "display-name",
|
||||
AvatarURL: "avatar-key",
|
||||
ResourceOwner: "ro",
|
||||
OrgName: "org-name",
|
||||
OrgPrimaryDomain: "primary-domain",
|
||||
ProjectID: "project-id",
|
||||
ProjectName: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "prepareUserGrantsQuery multiple grants",
|
||||
prepare: prepareUserGrantsQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQueries(
|
||||
userGrantsStmt,
|
||||
userGrantsCols,
|
||||
[][]driver.Value{
|
||||
{
|
||||
"id",
|
||||
testNow,
|
||||
testNow,
|
||||
20211111,
|
||||
"grant-id",
|
||||
pq.StringArray{"role-key"},
|
||||
domain.UserGrantStateActive,
|
||||
"user-id",
|
||||
"username",
|
||||
domain.UserTypeHuman,
|
||||
"resource-owner",
|
||||
"first-name",
|
||||
"last-name",
|
||||
"email",
|
||||
"display-name",
|
||||
"avatar-key",
|
||||
"ro",
|
||||
"org-name",
|
||||
"primary-domain",
|
||||
"project-id",
|
||||
"project-name",
|
||||
},
|
||||
{
|
||||
"id",
|
||||
testNow,
|
||||
testNow,
|
||||
20211111,
|
||||
"grant-id",
|
||||
pq.StringArray{"role-key"},
|
||||
domain.UserGrantStateActive,
|
||||
"user-id",
|
||||
"username",
|
||||
domain.UserTypeHuman,
|
||||
"resource-owner",
|
||||
"first-name",
|
||||
"last-name",
|
||||
"email",
|
||||
"display-name",
|
||||
"avatar-key",
|
||||
"ro",
|
||||
"org-name",
|
||||
"primary-domain",
|
||||
"project-id",
|
||||
"project-name",
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
object: &UserGrants{
|
||||
SearchResponse: SearchResponse{
|
||||
Count: 2,
|
||||
},
|
||||
UserGrants: []*UserGrant{
|
||||
{
|
||||
ID: "id",
|
||||
CreationDate: testNow,
|
||||
ChangeDate: testNow,
|
||||
Sequence: 20211111,
|
||||
Roles: []string{"role-key"},
|
||||
GrantID: "grant-id",
|
||||
State: domain.UserGrantStateActive,
|
||||
UserID: "user-id",
|
||||
Username: "username",
|
||||
UserType: domain.UserTypeHuman,
|
||||
UserResourceOwner: "resource-owner",
|
||||
FirstName: "first-name",
|
||||
LastName: "last-name",
|
||||
Email: "email",
|
||||
DisplayName: "display-name",
|
||||
AvatarURL: "avatar-key",
|
||||
ResourceOwner: "ro",
|
||||
OrgName: "org-name",
|
||||
OrgPrimaryDomain: "primary-domain",
|
||||
ProjectID: "project-id",
|
||||
ProjectName: "project-name",
|
||||
},
|
||||
{
|
||||
ID: "id",
|
||||
CreationDate: testNow,
|
||||
ChangeDate: testNow,
|
||||
Sequence: 20211111,
|
||||
Roles: []string{"role-key"},
|
||||
GrantID: "grant-id",
|
||||
State: domain.UserGrantStateActive,
|
||||
UserID: "user-id",
|
||||
Username: "username",
|
||||
UserType: domain.UserTypeHuman,
|
||||
UserResourceOwner: "resource-owner",
|
||||
FirstName: "first-name",
|
||||
LastName: "last-name",
|
||||
Email: "email",
|
||||
DisplayName: "display-name",
|
||||
AvatarURL: "avatar-key",
|
||||
ResourceOwner: "ro",
|
||||
OrgName: "org-name",
|
||||
OrgPrimaryDomain: "primary-domain",
|
||||
ProjectID: "project-id",
|
||||
ProjectName: "project-name",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "prepareUserGrantsQuery sql err",
|
||||
prepare: prepareUserGrantsQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQueryErr(
|
||||
userGrantsStmt,
|
||||
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: 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)
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user