add user_grants to the userinfo query

This commit is contained in:
Tim Möhlmann
2023-11-15 14:49:20 +02:00
parent ec65673e41
commit 3584833021
13 changed files with 363 additions and 178 deletions

View File

@@ -90,6 +90,36 @@ func UserGrantsFromQuery(c *actions.FieldConfig, userGrants *query.UserGrants) g
return c.Runtime.ToValue(grantList)
}
func UserGrantsFromSlice(c *actions.FieldConfig, userGrants []query.UserGrant) goja.Value {
if userGrants == nil {
return c.Runtime.ToValue(nil)
}
grantList := &userGrantList{
Count: uint64(len(userGrants)),
Grants: make([]*userGrant, len(userGrants)),
}
for i, grant := range userGrants {
grantList.Grants[i] = &userGrant{
Id: grant.ID,
ProjectGrantId: grant.GrantID,
State: grant.State,
CreationDate: grant.CreationDate,
ChangeDate: grant.ChangeDate,
Sequence: grant.Sequence,
UserId: grant.UserID,
Roles: grant.Roles,
UserResourceOwner: grant.UserResourceOwner,
UserGrantResourceOwner: grant.ResourceOwner,
UserGrantResourceOwnerName: grant.OrgName,
ProjectId: grant.ProjectID,
ProjectName: grant.ProjectName,
}
}
return c.Runtime.ToValue(grantList)
}
func UserGrantsToDomain(userID string, actionUserGrants []UserGrant) []*domain.UserGrant {
if actionUserGrants == nil {
return nil

View File

@@ -795,7 +795,7 @@ func (o *OPStorage) assertRoles(ctx context.Context, userID, applicationID strin
if len(requestedRoles) > 0 {
for _, requestedRole := range requestedRoles {
for _, grant := range grants.UserGrants {
checkGrantedRoles(roles, grant, requestedRole, grant.ProjectID == projectID)
checkGrantedRoles(roles, *grant, requestedRole, grant.ProjectID == projectID)
}
}
return grants, roles, nil
@@ -838,7 +838,7 @@ func (o *OPStorage) assertUserResourceOwner(ctx context.Context, userID string)
}, nil
}
func checkGrantedRoles(roles *projectsRoles, grant *query.UserGrant, requestedRole string, isRequested bool) {
func checkGrantedRoles(roles *projectsRoles, grant query.UserGrant, requestedRole string, isRequested bool) {
for _, grantedRole := range grant.Roles {
if requestedRole == grantedRole {
roles.Add(grant.ProjectID, grantedRole, grant.ResourceOwner, grant.OrgPrimaryDomain, isRequested)
@@ -854,6 +854,26 @@ type projectsRoles struct {
requestProjectID string
}
func newProjectRoles(projectID string, grants []query.UserGrant, requestedRoles []string) *projectsRoles {
roles := new(projectsRoles)
// if specific roles where requested, check if they are granted and append them in the roles list
if len(requestedRoles) > 0 {
for _, requestedRole := range requestedRoles {
for _, grant := range grants {
checkGrantedRoles(roles, grant, requestedRole, grant.ProjectID == projectID)
}
}
return roles
}
// no specific roles were requested, so convert any grants into roles
for _, grant := range grants {
for _, role := range grant.Roles {
roles.Add(grant.ProjectID, role, grant.ResourceOwner, grant.OrgPrimaryDomain, grant.ProjectID == projectID)
}
}
return roles
}
func (p *projectsRoles) Add(projectID, roleKey, orgID, domain string, isRequested bool) {
if p.projects == nil {
p.projects = make(map[string]projectRoles, 1)

View File

@@ -87,7 +87,7 @@ func (s *Server) Introspect(ctx context.Context, r *op.Request[op.IntrospectionR
if err = validateIntrospectionAudience(token.audience, client.clientID, client.projectID); err != nil {
return nil, err
}
userInfo, err := s.getUserInfoWithRoles(ctx, token.userID, client.projectID, token.scope, []string{client.projectID})
userInfo, err := s.userInfo(ctx, token.userID, client.projectID, token.scope, []string{client.projectID})
if err != nil {
return nil, err
}

View File

@@ -16,141 +16,42 @@ import (
"github.com/zitadel/zitadel/internal/actions/object"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/query"
"github.com/zitadel/zitadel/internal/telemetry/tracing"
)
func (s *Server) getUserInfoWithRoles(ctx context.Context, userID, projectID string, scope, roleAudience []string) (_ *oidc.UserInfo, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
userInfoChan := make(chan *userInfoResult)
go s.getUserInfo(ctx, userID, userInfoChan)
rolesChan := make(chan *assertRolesResult)
go s.assertRoles(ctx, userID, projectID, scope, roleAudience, rolesChan)
var (
userInfoResult *userInfoResult
assertRolesResult *assertRolesResult
)
// make sure both channels are always read,
// and cancel the context on first error
for i := 0; i < 2; i++ {
var resErr error
select {
case userInfoResult = <-userInfoChan:
resErr = userInfoResult.err
case assertRolesResult = <-rolesChan:
resErr = assertRolesResult.err
func (s *Server) userInfo(ctx context.Context, userID, projectID string, scope, roleAudience []string) (_ *oidc.UserInfo, err error) {
roleAudience, requestedRoles := prepareRoles(ctx, projectID, scope, roleAudience)
qu, err := s.query.GetOIDCUserInfo(ctx, userID, roleAudience)
if err != nil {
return nil, err
}
if resErr == nil {
continue
}
cancel()
// we only care for the first error that occured,
// as the next error is most probably a context error.
if err == nil {
err = resErr
}
}
userInfo := userInfoToOIDC(userInfoResult.userInfo, scope, s.assetAPIPrefix(ctx))
setUserInfoRoleClaims(userInfo, assertRolesResult.projectsRoles)
return userInfo, s.userinfoFlows(ctx, userInfoResult.userInfo, assertRolesResult.userGrants, userInfo)
userInfo := userInfoToOIDC(projectID, qu, scope, requestedRoles, s.assetAPIPrefix(ctx))
return userInfo, s.userinfoFlows(ctx, qu, userInfo)
}
type userInfoResult struct {
userInfo *query.OIDCUserInfo
err error
}
func (s *Server) getUserInfo(ctx context.Context, userID string, rc chan<- *userInfoResult) {
userInfo, err := s.query.GetOIDCUserInfo(ctx, userID)
rc <- &userInfoResult{
userInfo: userInfo,
err: err,
}
}
type assertRolesResult struct {
userGrants *query.UserGrants
projectsRoles *projectsRoles
err error
}
func (s *Server) assertRoles(ctx context.Context, userID, projectID string, scope, roleAudience []string, rc chan<- *assertRolesResult) {
userGrands, projectsRoles, err := func() (*query.UserGrants, *projectsRoles, error) {
func prepareRoles(ctx context.Context, projectID string, scope, roleAudience []string) (ra, requestedRoles []string) {
// if all roles are requested take the audience for those from the scopes
if slices.Contains(scope, ScopeProjectsRoles) {
roleAudience = domain.AddAudScopeToAudience(ctx, roleAudience, scope)
}
requestedRoles := make([]string, 0, len(scope))
requestedRoles = make([]string, 0, len(scope))
for _, s := range scope {
if role, ok := strings.CutPrefix(s, ScopeProjectRolePrefix); ok {
requestedRoles = append(requestedRoles, role)
}
}
if len(requestedRoles) == 0 && len(roleAudience) == 0 {
return nil, nil, nil
return nil, nil
}
// ensure the projectID of the requesting is part of the roleAudience
if projectID != "" {
if !slices.Contains(roleAudience, projectID) {
roleAudience = append(roleAudience, projectID)
}
queries := make([]query.SearchQuery, 0, 2)
projectQuery, err := query.NewUserGrantProjectIDsSearchQuery(roleAudience)
if err != nil {
return nil, nil, err
}
queries = append(queries, projectQuery)
userIDQuery, err := query.NewUserGrantUserIDSearchQuery(userID)
if err != nil {
return nil, nil, err
}
queries = append(queries, userIDQuery)
grants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{
Queries: queries,
}, false, false) // triggers disabled
if err != nil {
return nil, nil, err
}
roles := new(projectsRoles)
// if specific roles where requested, check if they are granted and append them in the roles list
if len(requestedRoles) > 0 {
for _, requestedRole := range requestedRoles {
for _, grant := range grants.UserGrants {
checkGrantedRoles(roles, grant, requestedRole, grant.ProjectID == projectID)
}
}
return grants, roles, nil
}
// no specific roles were requested, so convert any grants into roles
for _, grant := range grants.UserGrants {
for _, role := range grant.Roles {
roles.Add(grant.ProjectID, role, grant.ResourceOwner, grant.OrgPrimaryDomain, grant.ProjectID == projectID)
}
}
return grants, roles, nil
}()
rc <- &assertRolesResult{
userGrants: userGrands,
projectsRoles: projectsRoles,
err: err,
}
return roleAudience, requestedRoles
}
func userInfoToOIDC(user *query.OIDCUserInfo, scope []string, assetPrefix string) *oidc.UserInfo {
func userInfoToOIDC(projectID string, user *query.OIDCUserInfo, scope, requestedRoles []string, assetPrefix string) *oidc.UserInfo {
out := new(oidc.UserInfo)
for _, s := range scope {
switch s {
@@ -178,7 +79,7 @@ func userInfoToOIDC(user *query.OIDCUserInfo, scope []string, assetPrefix string
}
}
}
setUserInfoRoleClaims(out, newProjectRoles(projectID, user.UserGrants, requestedRoles))
return out
}
@@ -259,8 +160,8 @@ func setUserInfoRoleClaims(userInfo *oidc.UserInfo, roles *projectsRoles) {
}
}
func (s *Server) userinfoFlows(ctx context.Context, user *query.OIDCUserInfo, userGrants *query.UserGrants, userInfo *oidc.UserInfo) error {
queriedActions, err := s.query.GetActiveActionsByFlowAndTriggerType(ctx, domain.FlowTypeCustomiseToken, domain.TriggerTypePreUserinfoCreation, user.User.ResourceOwner, false)
func (s *Server) userinfoFlows(ctx context.Context, qu *query.OIDCUserInfo, userInfo *oidc.UserInfo) error {
queriedActions, err := s.query.GetActiveActionsByFlowAndTriggerType(ctx, domain.FlowTypeCustomiseToken, domain.TriggerTypePreUserinfoCreation, qu.User.ResourceOwner, false)
if err != nil {
return err
}
@@ -270,17 +171,17 @@ func (s *Server) userinfoFlows(ctx context.Context, user *query.OIDCUserInfo, us
actions.SetFields("claims", userinfoClaims(userInfo)),
actions.SetFields("getUser", func(c *actions.FieldConfig) interface{} {
return func(call goja.FunctionCall) goja.Value {
return object.UserFromQuery(c, user.User)
return object.UserFromQuery(c, qu.User)
}
}),
actions.SetFields("user",
actions.SetFields("getMetadata", func(c *actions.FieldConfig) interface{} {
return func(goja.FunctionCall) goja.Value {
return object.UserMetadataListFromSlice(c, user.Metadata)
return object.UserMetadataListFromSlice(c, qu.Metadata)
}
}),
actions.SetFields("grants", func(c *actions.FieldConfig) interface{} {
return object.UserGrantsFromQuery(c, userGrants)
return object.UserGrantsFromSlice(c, qu.UserGrants)
}),
),
),
@@ -334,7 +235,7 @@ func (s *Server) userinfoFlows(ctx context.Context, user *query.OIDCUserInfo, us
Key: key,
Value: value,
}
if _, err = s.command.SetUserMetadata(ctx, metadata, userInfo.Subject, user.User.ResourceOwner); err != nil {
if _, err = s.command.SetUserMetadata(ctx, metadata, userInfo.Subject, qu.User.ResourceOwner); err != nil {
logging.WithError(err).Info("unable to set md in action")
panic(err)
}

View File

@@ -1,3 +1,6 @@
-- deallocate q;
-- prepare q (text, text, text[]) as
with usr as (
select id, creation_date, change_date, sequence, state, resource_owner, username
from projections.users9 u
@@ -20,6 +23,7 @@ machine as (
and instance_id = $2
) r
),
-- find the user's metadata
metadata as (
select json_agg(row_to_json(r)) as metadata from (
select creation_date, change_date, sequence, resource_owner, key, encode(value, 'base64') as value
@@ -28,14 +32,46 @@ metadata as (
and instance_id = $2
) r
),
org as (
-- get all user grants, needed for the orgs query
user_grants as (
select id, grant_id, state, creation_date, change_date, sequence, user_id, roles, resource_owner, project_id
from projections.user_grants3
where user_id = $1
and instance_id = $2
and project_id = any($3)
),
-- filter all orgs we are interested in.
orgs as (
select id, name, primary_domain
from projections.orgs1
where id in (
select resource_owner from user_grants
union
select resource_owner from usr
)
and instance_id = $2
),
-- find the user's org
user_org as (
select row_to_json(r) as organization from (
select name, primary_domain
from projections.orgs1 o
from orgs o
join usr u on o.id = u.resource_owner
where instance_id = $2
) r
),
-- join user grants to orgs, projects and user
grants as (
select json_agg(row_to_json(r)) as grants from (
select g.*,
o.name as org_name, o.primary_domain as org_primary_domain,
p.name as project_name, u.resource_owner as user_resource_owner
from user_grants g
left join orgs o on o.id = g.resource_owner
left join projections.projects3 p on p.id = g.project_id
left join usr u on u.id = g.user_id
) r
)
-- build the final result JSON
select json_build_object(
'user', (
select row_to_json(r) as usr from (
@@ -45,6 +81,9 @@ select json_build_object(
left join machine m on u.id = m.user_id
) r
),
'org', (select organization from org),
'metadata', (select metadata from metadata)
'org', (select organization from user_org),
'metadata', (select metadata from metadata),
'user_grants', (select grants from grants)
);
-- execute q('231965491734773762','230690539048009730', '{"236645808328409090","240762134579904514"}')

View File

@@ -41,5 +41,6 @@
"key": "foo",
"value": "YmFy"
}
]
],
"user_grants": null
}

View File

@@ -0,0 +1,86 @@
{
"user": {
"id": "231965491734773762",
"creation_date": "2023-09-15T06:10:07.434142+00:00",
"change_date": "2023-11-14T13:27:02.072318+00:00",
"sequence": 1148,
"state": 1,
"resource_owner": "231848297847848962",
"username": "tim+tesmail@zitadel.com",
"human": {
"first_name": "Tim",
"last_name": "Mohlmann",
"nick_name": "muhlemmer",
"display_name": "Tim Mohlmann",
"avatar_key": null,
"email": "tim+tesmail@zitadel.com",
"is_email_verified": true,
"phone": "+40123456789",
"is_phone_verified": false
},
"machine": null
},
"org": {
"name": "demo",
"primary_domain": "demo.localhost"
},
"metadata": [
{
"creation_date": "2023-11-14T13:26:03.553702+00:00",
"change_date": "2023-11-14T13:26:03.553702+00:00",
"sequence": 1147,
"resource_owner": "231848297847848962",
"key": "bar",
"value": "Zm9v"
},
{
"creation_date": "2023-11-14T13:25:57.171368+00:00",
"change_date": "2023-11-14T13:25:57.171368+00:00",
"sequence": 1146,
"resource_owner": "231848297847848962",
"key": "foo",
"value": "YmFy"
}
],
"user_grants": [
{
"id": "240749256523120642",
"grant_id": "",
"state": 1,
"creation_date": "2023-11-14T20:28:59.168208+00:00",
"change_date": "2023-11-14T20:50:58.822391+00:00",
"sequence": 2,
"user_id": "231965491734773762",
"roles": [
"role1",
"role2"
],
"resource_owner": "231848297847848962",
"project_id": "236645808328409090",
"org_name": "demo",
"org_primary_domain": "demo.localhost",
"project_name": "tests",
"user_resource_owner": "231848297847848962"
},
{
"id": "240762315572510722",
"grant_id": "",
"state": 1,
"creation_date": "2023-11-14T22:38:42.967317+00:00",
"change_date": "2023-11-14T22:38:42.967317+00:00",
"sequence": 1,
"user_id": "231965491734773762",
"roles": [
"role3",
"role4"
],
"resource_owner": "231848297847848962",
"project_id": "240762134579904514",
"org_name": "demo",
"org_primary_domain": "demo.localhost",
"project_name": "tests2",
"user_resource_owner": "231848297847848962"
}
]
}

View File

@@ -24,5 +24,6 @@
"name": "demo",
"primary_domain": "demo.localhost"
},
"metadata": null
"metadata": null,
"user_grants": null
}

View File

@@ -34,5 +34,6 @@
"key": "second",
"value": "QnllIFdvcmxkIQ=="
}
]
],
"user_grants": null
}

View File

@@ -1,5 +1,6 @@
{
"user": null,
"org": null,
"metadata": null
"metadata": null,
"user_grants": null
}

View File

@@ -22,32 +22,32 @@ import (
type UserGrant struct {
// ID represents the aggregate id (id of the user grant)
ID string
CreationDate time.Time
ChangeDate time.Time
Sequence uint64
Roles database.TextArray[string]
ID string `json:"id,omitempty"`
CreationDate time.Time `json:"creation_date,omitempty"`
ChangeDate time.Time `json:"change_date,omitempty"`
Sequence uint64 `json:"sequence,omitempty"`
Roles database.TextArray[string] `json:"roles,omitempty"`
// GrantID represents the project grant id
GrantID string
State domain.UserGrantState
GrantID string `json:"grant_id,omitempty"`
State domain.UserGrantState `json:"state,omitempty"`
UserID string
Username string
UserType domain.UserType
UserResourceOwner string
FirstName string
LastName string
Email string
DisplayName string
AvatarURL string
PreferredLoginName string
UserID string `json:"user_id,omitempty"`
Username string `json:"username,omitempty"`
UserType domain.UserType `json:"user_type,omitempty"`
UserResourceOwner string `json:"user_resource_owner,omitempty"`
FirstName string `json:"first_name,omitempty"`
LastName string `json:"last_name,omitempty"`
Email string `json:"email,omitempty"`
DisplayName string `json:"display_name,omitempty"`
AvatarURL string `json:"avatar_url,omitempty"`
PreferredLoginName string `json:"preferred_login_name,omitempty"`
ResourceOwner string
OrgName string
OrgPrimaryDomain string
ResourceOwner string `json:"resource_owner,omitempty"`
OrgName string `json:"org_name,omitempty"`
OrgPrimaryDomain string `json:"org_primary_domain,omitempty"`
ProjectID string
ProjectName string
ProjectID string `json:"project_id,omitempty"`
ProjectName string `json:"project_name,omitempty"`
}
type UserGrants struct {

View File

@@ -7,6 +7,7 @@ import (
"encoding/json"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/database"
"github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/telemetry/tracing"
)
@@ -14,14 +15,17 @@ import (
//go:embed embed/userinfo_by_id.sql
var oidcUserInfoQuery string
func (q *Queries) GetOIDCUserInfo(ctx context.Context, userID string) (_ *OIDCUserInfo, err error) {
func (q *Queries) GetOIDCUserInfo(ctx context.Context, userID string, roleAudience []string) (_ *OIDCUserInfo, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
var data []byte
err = q.client.QueryRowContext(ctx, func(row *sql.Row) error {
return row.Scan(&data)
}, oidcUserInfoQuery, userID, authz.GetInstance(ctx).InstanceID())
},
oidcUserInfoQuery,
userID, authz.GetInstance(ctx).InstanceID(), database.TextArray[string](roleAudience),
)
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-Oath6", "Errors.Internal")
}
@@ -41,6 +45,7 @@ type OIDCUserInfo struct {
User *User `json:"user,omitempty"`
Metadata []UserMetadata `json:"metadata,omitempty"`
Org *userInfoOrg `json:"org,omitempty"`
UserGrants []UserGrant `json:"user_grants,omitempty"`
}
type userInfoOrg struct {

View File

@@ -22,6 +22,8 @@ var (
testdataUserInfoHumanNoMD string
//go:embed testdata/userinfo_human.json
testdataUserInfoHuman string
//go:embed testdata/userinfo_human_grants.json
testdataUserInfoHumanGrants string
//go:embed testdata/userinfo_machine.json
testdataUserInfoMachine string
)
@@ -30,6 +32,7 @@ func TestQueries_GetOIDCUserInfo(t *testing.T) {
expQuery := regexp.QuoteMeta(oidcUserInfoQuery)
type args struct {
userID string
roleAudience []string
}
tests := []struct {
name string
@@ -43,7 +46,7 @@ func TestQueries_GetOIDCUserInfo(t *testing.T) {
args: args{
userID: "231965491734773762",
},
mock: mockQueryErr(expQuery, sql.ErrConnDone, "231965491734773762", "instanceID"),
mock: mockQueryErr(expQuery, sql.ErrConnDone, "231965491734773762", "instanceID", nil),
wantErr: sql.ErrConnDone,
},
{
@@ -51,7 +54,7 @@ func TestQueries_GetOIDCUserInfo(t *testing.T) {
args: args{
userID: "231965491734773762",
},
mock: mockQuery(expQuery, []string{"json_build_object"}, []driver.Value{`~~~`}, "231965491734773762", "instanceID"),
mock: mockQuery(expQuery, []string{"json_build_object"}, []driver.Value{`~~~`}, "231965491734773762", "instanceID", nil),
wantErr: errors.ThrowInternal(nil, "QUERY-Vohs6", "Errors.Internal"),
},
{
@@ -59,7 +62,7 @@ func TestQueries_GetOIDCUserInfo(t *testing.T) {
args: args{
userID: "231965491734773762",
},
mock: mockQuery(expQuery, []string{"json_build_object"}, []driver.Value{testdataUserInfoNotFound}, "231965491734773762", "instanceID"),
mock: mockQuery(expQuery, []string{"json_build_object"}, []driver.Value{testdataUserInfoNotFound}, "231965491734773762", "instanceID", nil),
wantErr: errors.ThrowNotFound(nil, "QUERY-ahs4S", "Errors.User.NotFound"),
},
{
@@ -67,7 +70,7 @@ func TestQueries_GetOIDCUserInfo(t *testing.T) {
args: args{
userID: "231965491734773762",
},
mock: mockQuery(expQuery, []string{"json_build_object"}, []driver.Value{testdataUserInfoHumanNoMD}, "231965491734773762", "instanceID"),
mock: mockQuery(expQuery, []string{"json_build_object"}, []driver.Value{testdataUserInfoHumanNoMD}, "231965491734773762", "instanceID", nil),
want: &OIDCUserInfo{
User: &User{
ID: "231965491734773762",
@@ -102,7 +105,7 @@ func TestQueries_GetOIDCUserInfo(t *testing.T) {
args: args{
userID: "231965491734773762",
},
mock: mockQuery(expQuery, []string{"json_build_object"}, []driver.Value{testdataUserInfoHuman}, "231965491734773762", "instanceID"),
mock: mockQuery(expQuery, []string{"json_build_object"}, []driver.Value{testdataUserInfoHuman}, "231965491734773762", "instanceID", nil),
want: &OIDCUserInfo{
User: &User{
ID: "231965491734773762",
@@ -149,12 +152,109 @@ func TestQueries_GetOIDCUserInfo(t *testing.T) {
},
},
},
{
name: "human with metadata and grants",
args: args{
userID: "231965491734773762",
roleAudience: []string{"236645808328409090", "240762134579904514"},
},
mock: mockQuery(expQuery,
[]string{"json_build_object"},
[]driver.Value{testdataUserInfoHumanGrants},
"231965491734773762", "instanceID", database.TextArray[string]{"236645808328409090", "240762134579904514"},
),
want: &OIDCUserInfo{
User: &User{
ID: "231965491734773762",
CreationDate: time.Date(2023, time.September, 15, 6, 10, 7, 434142000, time.FixedZone("", 0)),
ChangeDate: time.Date(2023, time.November, 14, 13, 27, 2, 72318000, time.FixedZone("", 0)),
Sequence: 1148,
State: 1,
ResourceOwner: "231848297847848962",
Username: "tim+tesmail@zitadel.com",
Human: &Human{
FirstName: "Tim",
LastName: "Mohlmann",
NickName: "muhlemmer",
DisplayName: "Tim Mohlmann",
AvatarKey: "",
Email: "tim+tesmail@zitadel.com",
IsEmailVerified: true,
Phone: "+40123456789",
IsPhoneVerified: false,
},
Machine: nil,
},
Org: &userInfoOrg{
Name: "demo",
PrimaryDomain: "demo.localhost",
},
Metadata: []UserMetadata{
{
CreationDate: time.Date(2023, time.November, 14, 13, 26, 3, 553702000, time.FixedZone("", 0)),
ChangeDate: time.Date(2023, time.November, 14, 13, 26, 3, 553702000, time.FixedZone("", 0)),
Sequence: 1147,
ResourceOwner: "231848297847848962",
Key: "bar",
Value: []byte("foo"),
},
{
CreationDate: time.Date(2023, time.November, 14, 13, 25, 57, 171368000, time.FixedZone("", 0)),
ChangeDate: time.Date(2023, time.November, 14, 13, 25, 57, 171368000, time.FixedZone("", 0)),
Sequence: 1146,
ResourceOwner: "231848297847848962",
Key: "foo",
Value: []byte("bar"),
},
},
UserGrants: []UserGrant{
{
ID: "240749256523120642",
GrantID: "",
State: 1,
CreationDate: time.Date(2023, time.November, 14, 20, 28, 59, 168208000, time.FixedZone("", 0)),
ChangeDate: time.Date(2023, time.November, 14, 20, 50, 58, 822391000, time.FixedZone("", 0)),
Sequence: 2,
UserID: "231965491734773762",
Roles: []string{
"role1",
"role2",
},
ResourceOwner: "231848297847848962",
ProjectID: "236645808328409090",
OrgName: "demo",
OrgPrimaryDomain: "demo.localhost",
ProjectName: "tests",
UserResourceOwner: "231848297847848962",
},
{
ID: "240762315572510722",
GrantID: "",
State: 1,
CreationDate: time.Date(2023, time.November, 14, 22, 38, 42, 967317000, time.FixedZone("", 0)),
ChangeDate: time.Date(2023, time.November, 14, 22, 38, 42, 967317000, time.FixedZone("", 0)),
Sequence: 1,
UserID: "231965491734773762",
Roles: []string{
"role3",
"role4",
},
ResourceOwner: "231848297847848962",
ProjectID: "240762134579904514",
OrgName: "demo",
OrgPrimaryDomain: "demo.localhost",
ProjectName: "tests2",
UserResourceOwner: "231848297847848962",
},
},
},
},
{
name: "machine with metadata",
args: args{
userID: "240707570677841922",
},
mock: mockQuery(expQuery, []string{"json_build_object"}, []driver.Value{testdataUserInfoMachine}, "240707570677841922", "instanceID"),
mock: mockQuery(expQuery, []string{"json_build_object"}, []driver.Value{testdataUserInfoMachine}, "240707570677841922", "instanceID", nil),
want: &OIDCUserInfo{
User: &User{
ID: "240707570677841922",
@@ -206,7 +306,7 @@ func TestQueries_GetOIDCUserInfo(t *testing.T) {
}
ctx := authz.NewMockContext("instanceID", "orgID", "loginClient")
got, err := q.GetOIDCUserInfo(ctx, tt.args.userID)
got, err := q.GetOIDCUserInfo(ctx, tt.args.userID, tt.args.roleAudience)
require.ErrorIs(t, err, tt.wantErr)
assert.Equal(t, tt.want, got)
})