mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-12 19:14:23 +00:00
ba9b807854
* get key by id and cache them
* userinfo from events for v2 tokens
* improve keyset caching
* concurrent token and client checks
* client and project in single query
* logging and otel
* drop owner_removed column on apps and authN tables
* userinfo and project roles in go routines
* get oidc user info from projections and add actions
* add avatar URL
* some cleanup
* pull oidc work branch
* remove storage from server
* add config flag for experimental introspection
* legacy introspection flag
* drop owner_removed column on user projections
* drop owner_removed column on useer_metadata
* query userinfo unit test
* query introspection client test
* add user_grants to the userinfo query
* handle PAT scopes
* bring triggers back
* test instance keys query
* add userinfo unit tests
* unit test keys
* go mod tidy
* solve some bugs
* fix missing preferred login name
* do not run triggers in go routines, they seem to deadlock
* initialize the trigger handlers late with a sync.OnceValue
* Revert "do not run triggers in go routines, they seem to deadlock"
This reverts commit 2a03da2127
.
* add missing translations
* chore: update go version for linting
* pin oidc version
* parse a global time location for query test
* fix linter complains
* upgrade go lint
* fix more linting issues
---------
Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com>
337 lines
11 KiB
Go
337 lines
11 KiB
Go
package query
|
|
|
|
import (
|
|
"database/sql"
|
|
"database/sql/driver"
|
|
_ "embed"
|
|
"encoding/json"
|
|
"regexp"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/zitadel/zitadel/internal/api/authz"
|
|
"github.com/zitadel/zitadel/internal/database"
|
|
"github.com/zitadel/zitadel/internal/errors"
|
|
)
|
|
|
|
var (
|
|
//go:embed testdata/userinfo_not_found.json
|
|
testdataUserInfoNotFound string
|
|
//go:embed testdata/userinfo_human_no_md.json
|
|
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
|
|
|
|
// timeLocation does a single parse of the testdata and extracts a time.Location,
|
|
// so that it may be used during test assertion.
|
|
// Because depending on the environment json.Unmarshal parses
|
|
// the 00:00 timezones differently.
|
|
// On my local system is parses to an empty timezone with 0 offset,
|
|
// but in github action is pares into UTC.
|
|
timeLocation = func() *time.Location {
|
|
referenceInfo := new(OIDCUserInfo)
|
|
err := json.Unmarshal([]byte(testdataUserInfoHumanNoMD), referenceInfo)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return referenceInfo.User.CreationDate.Location()
|
|
}()
|
|
)
|
|
|
|
func TestQueries_GetOIDCUserInfo(t *testing.T) {
|
|
expQuery := regexp.QuoteMeta(oidcUserInfoQuery)
|
|
type args struct {
|
|
userID string
|
|
roleAudience []string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
mock sqlExpectation
|
|
want *OIDCUserInfo
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "query error",
|
|
args: args{
|
|
userID: "231965491734773762",
|
|
},
|
|
mock: mockQueryErr(expQuery, sql.ErrConnDone, "231965491734773762", "instanceID", nil),
|
|
wantErr: sql.ErrConnDone,
|
|
},
|
|
{
|
|
name: "unmarshal error",
|
|
args: args{
|
|
userID: "231965491734773762",
|
|
},
|
|
mock: mockQuery(expQuery, []string{"json_build_object"}, []driver.Value{`~~~`}, "231965491734773762", "instanceID", nil),
|
|
wantErr: errors.ThrowInternal(nil, "QUERY-Vohs6", "Errors.Internal"),
|
|
},
|
|
{
|
|
name: "user not found",
|
|
args: args{
|
|
userID: "231965491734773762",
|
|
},
|
|
mock: mockQuery(expQuery, []string{"json_build_object"}, []driver.Value{testdataUserInfoNotFound}, "231965491734773762", "instanceID", nil),
|
|
wantErr: errors.ThrowNotFound(nil, "QUERY-ahs4S", "Errors.User.NotFound"),
|
|
},
|
|
{
|
|
name: "human without metadata",
|
|
args: args{
|
|
userID: "231965491734773762",
|
|
},
|
|
mock: mockQuery(expQuery, []string{"json_build_object"}, []driver.Value{testdataUserInfoHumanNoMD}, "231965491734773762", "instanceID", nil),
|
|
want: &OIDCUserInfo{
|
|
User: &User{
|
|
ID: "231965491734773762",
|
|
CreationDate: time.Date(2023, time.September, 15, 6, 10, 7, 434142000, timeLocation),
|
|
ChangeDate: time.Date(2023, time.November, 14, 13, 27, 2, 72318000, timeLocation),
|
|
Sequence: 1148,
|
|
State: 1,
|
|
ResourceOwner: "231848297847848962",
|
|
Username: "tim+tesmail@zitadel.com",
|
|
PreferredLoginName: "tim+tesmail@zitadel.com@demo.localhost",
|
|
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: nil,
|
|
},
|
|
},
|
|
{
|
|
name: "human with metadata",
|
|
args: args{
|
|
userID: "231965491734773762",
|
|
},
|
|
mock: mockQuery(expQuery, []string{"json_build_object"}, []driver.Value{testdataUserInfoHuman}, "231965491734773762", "instanceID", nil),
|
|
want: &OIDCUserInfo{
|
|
User: &User{
|
|
ID: "231965491734773762",
|
|
CreationDate: time.Date(2023, time.September, 15, 6, 10, 7, 434142000, timeLocation),
|
|
ChangeDate: time.Date(2023, time.November, 14, 13, 27, 2, 72318000, timeLocation),
|
|
Sequence: 1148,
|
|
State: 1,
|
|
ResourceOwner: "231848297847848962",
|
|
Username: "tim+tesmail@zitadel.com",
|
|
PreferredLoginName: "tim+tesmail@zitadel.com@demo.localhost",
|
|
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, timeLocation),
|
|
ChangeDate: time.Date(2023, time.November, 14, 13, 26, 3, 553702000, timeLocation),
|
|
Sequence: 1147,
|
|
ResourceOwner: "231848297847848962",
|
|
Key: "bar",
|
|
Value: []byte("foo"),
|
|
},
|
|
{
|
|
CreationDate: time.Date(2023, time.November, 14, 13, 25, 57, 171368000, timeLocation),
|
|
ChangeDate: time.Date(2023, time.November, 14, 13, 25, 57, 171368000, timeLocation),
|
|
Sequence: 1146,
|
|
ResourceOwner: "231848297847848962",
|
|
Key: "foo",
|
|
Value: []byte("bar"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
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, timeLocation),
|
|
ChangeDate: time.Date(2023, time.November, 14, 13, 27, 2, 72318000, timeLocation),
|
|
Sequence: 1148,
|
|
State: 1,
|
|
ResourceOwner: "231848297847848962",
|
|
Username: "tim+tesmail@zitadel.com",
|
|
PreferredLoginName: "tim+tesmail@zitadel.com@demo.localhost",
|
|
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, timeLocation),
|
|
ChangeDate: time.Date(2023, time.November, 14, 13, 26, 3, 553702000, timeLocation),
|
|
Sequence: 1147,
|
|
ResourceOwner: "231848297847848962",
|
|
Key: "bar",
|
|
Value: []byte("foo"),
|
|
},
|
|
{
|
|
CreationDate: time.Date(2023, time.November, 14, 13, 25, 57, 171368000, timeLocation),
|
|
ChangeDate: time.Date(2023, time.November, 14, 13, 25, 57, 171368000, timeLocation),
|
|
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, timeLocation),
|
|
ChangeDate: time.Date(2023, time.November, 14, 20, 50, 58, 822391000, timeLocation),
|
|
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, timeLocation),
|
|
ChangeDate: time.Date(2023, time.November, 14, 22, 38, 42, 967317000, timeLocation),
|
|
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", nil),
|
|
want: &OIDCUserInfo{
|
|
User: &User{
|
|
ID: "240707570677841922",
|
|
CreationDate: time.Date(2023, time.November, 14, 13, 34, 52, 473732000, timeLocation),
|
|
ChangeDate: time.Date(2023, time.November, 14, 13, 35, 2, 861342000, timeLocation),
|
|
Sequence: 2,
|
|
State: 1,
|
|
ResourceOwner: "231848297847848962",
|
|
Username: "tests",
|
|
PreferredLoginName: "tests@demo.localhost",
|
|
Human: nil,
|
|
Machine: &Machine{
|
|
Name: "tests",
|
|
Description: "My test service user",
|
|
},
|
|
},
|
|
Org: &UserInfoOrg{
|
|
Name: "demo",
|
|
PrimaryDomain: "demo.localhost",
|
|
},
|
|
Metadata: []UserMetadata{
|
|
{
|
|
CreationDate: time.Date(2023, time.November, 14, 13, 35, 30, 126849000, timeLocation),
|
|
ChangeDate: time.Date(2023, time.November, 14, 13, 35, 30, 126849000, timeLocation),
|
|
Sequence: 3,
|
|
ResourceOwner: "231848297847848962",
|
|
Key: "first",
|
|
Value: []byte("Hello World!"),
|
|
},
|
|
{
|
|
CreationDate: time.Date(2023, time.November, 14, 13, 35, 44, 28343000, timeLocation),
|
|
ChangeDate: time.Date(2023, time.November, 14, 13, 35, 44, 28343000, timeLocation),
|
|
Sequence: 4,
|
|
ResourceOwner: "231848297847848962",
|
|
Key: "second",
|
|
Value: []byte("Bye World!"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
execMock(t, tt.mock, func(db *sql.DB) {
|
|
q := &Queries{
|
|
client: &database.DB{
|
|
DB: db,
|
|
Database: &prepareDB{},
|
|
},
|
|
}
|
|
ctx := authz.NewMockContext("instanceID", "orgID", "loginClient")
|
|
|
|
got, err := q.GetOIDCUserInfo(ctx, tt.args.userID, tt.args.roleAudience)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.Equal(t, tt.want, got)
|
|
})
|
|
})
|
|
}
|
|
}
|