mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 10:49:25 +00:00
Merge remote-tracking branch 'origin/main' into next-rc
This commit is contained in:
@@ -16,6 +16,13 @@ import (
|
||||
)
|
||||
|
||||
func (s *Server) createUserTypeHuman(ctx context.Context, humanPb *user.CreateUserRequest_Human, orgId string, userName, userId *string) (*connect.Response[user.CreateUserResponse], error) {
|
||||
metadataEntries := make([]*user.SetMetadataEntry, len(humanPb.Metadata))
|
||||
for i, metadataEntry := range humanPb.Metadata {
|
||||
metadataEntries[i] = &user.SetMetadataEntry{
|
||||
Key: metadataEntry.GetKey(),
|
||||
Value: metadataEntry.GetValue(),
|
||||
}
|
||||
}
|
||||
addHumanPb := &user.AddHumanUserRequest{
|
||||
Username: userName,
|
||||
UserId: userId,
|
||||
@@ -27,6 +34,7 @@ func (s *Server) createUserTypeHuman(ctx context.Context, humanPb *user.CreateUs
|
||||
Phone: humanPb.Phone,
|
||||
IdpLinks: humanPb.IdpLinks,
|
||||
TotpSecret: humanPb.TotpSecret,
|
||||
Metadata: metadataEntries,
|
||||
}
|
||||
switch pwType := humanPb.GetPasswordType().(type) {
|
||||
case *user.CreateUserRequest_Human_HashedPassword:
|
||||
|
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/object/v2"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/user/v2"
|
||||
|
@@ -22,7 +22,7 @@ import (
|
||||
)
|
||||
|
||||
func TestServer_AddKey(t *testing.T) {
|
||||
resp := Instance.CreateUserTypeMachine(IamCTX)
|
||||
resp := Instance.CreateUserTypeMachine(IamCTX, Instance.DefaultOrg.Id)
|
||||
userId := resp.GetId()
|
||||
expirationDate := timestamppb.New(time.Now().Add(time.Hour * 24))
|
||||
type args struct {
|
||||
@@ -108,7 +108,7 @@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
|
||||
ExpirationDate: expirationDate,
|
||||
},
|
||||
func(request *user.AddKeyRequest) error {
|
||||
resp := Instance.CreateUserTypeHuman(IamCTX)
|
||||
resp := Instance.CreateUserTypeHuman(IamCTX, gofakeit.Email())
|
||||
request.UserId = resp.Id
|
||||
return nil
|
||||
},
|
||||
@@ -220,7 +220,7 @@ func TestServer_AddKey_Permission(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestServer_RemoveKey(t *testing.T) {
|
||||
resp := Instance.CreateUserTypeMachine(IamCTX)
|
||||
resp := Instance.CreateUserTypeMachine(IamCTX, Instance.DefaultOrg.Id)
|
||||
userId := resp.GetId()
|
||||
expirationDate := timestamppb.New(time.Now().Add(time.Hour * 24))
|
||||
type args struct {
|
||||
@@ -388,7 +388,7 @@ func TestServer_ListKeys(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
otherOrgUserId := otherOrgUser.GetId()
|
||||
otherUserId := Instance.CreateUserTypeMachine(SystemCTX).GetId()
|
||||
otherUserId := Instance.CreateUserTypeMachine(SystemCTX, Instance.DefaultOrg.Id).GetId()
|
||||
onlySinceTestStartFilter := &user.KeysSearchFilter{Filter: &user.KeysSearchFilter_CreatedDateFilter{CreatedDateFilter: &filter.TimestampFilter{
|
||||
Timestamp: timestamppb.Now(),
|
||||
Method: filter.TimestampFilterMethod_TIMESTAMP_FILTER_METHOD_AFTER_OR_EQUALS,
|
||||
|
403
internal/api/grpc/user/v2/integration_test/metadata_test.go
Normal file
403
internal/api/grpc/user/v2/integration_test/metadata_test.go
Normal file
@@ -0,0 +1,403 @@
|
||||
//go:build integration
|
||||
|
||||
package user_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/brianvoe/gofakeit/v6"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/filter/v2"
|
||||
metadata "github.com/zitadel/zitadel/pkg/grpc/metadata/v2"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/user/v2"
|
||||
)
|
||||
|
||||
func TestServer_SetUserMetadata(t *testing.T) {
|
||||
iamOwnerCTX := Instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
ctx context.Context
|
||||
dep func(request *user.SetUserMetadataRequest)
|
||||
req *user.SetUserMetadataRequest
|
||||
setDate bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "missing permission",
|
||||
ctx: Instance.WithAuthorization(context.Background(), integration.UserTypeNoPermission),
|
||||
dep: func(req *user.SetUserMetadataRequest) {
|
||||
req.UserId = Instance.CreateUserTypeHuman(CTX, gofakeit.Email()).GetId()
|
||||
},
|
||||
req: &user.SetUserMetadataRequest{
|
||||
Metadata: []*user.Metadata{{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))}},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "set user metadata",
|
||||
ctx: iamOwnerCTX,
|
||||
dep: func(req *user.SetUserMetadataRequest) {
|
||||
req.UserId = Instance.CreateUserTypeHuman(CTX, gofakeit.Email()).GetId()
|
||||
},
|
||||
req: &user.SetUserMetadataRequest{
|
||||
Metadata: []*user.Metadata{{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))}},
|
||||
},
|
||||
setDate: true,
|
||||
},
|
||||
{
|
||||
name: "set user metadata, multiple",
|
||||
ctx: iamOwnerCTX,
|
||||
dep: func(req *user.SetUserMetadataRequest) {
|
||||
req.UserId = Instance.CreateUserTypeHuman(CTX, gofakeit.Email()).GetId()
|
||||
},
|
||||
req: &user.SetUserMetadataRequest{
|
||||
Metadata: []*user.Metadata{
|
||||
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))},
|
||||
{Key: "key2", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value2")))},
|
||||
{Key: "key3", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value3")))},
|
||||
},
|
||||
},
|
||||
setDate: true,
|
||||
},
|
||||
{
|
||||
name: "set user metadata on non existent user",
|
||||
ctx: iamOwnerCTX,
|
||||
req: &user.SetUserMetadataRequest{
|
||||
UserId: "notexisting",
|
||||
Metadata: []*user.Metadata{{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))}},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "update user metadata",
|
||||
ctx: Instance.WithAuthorization(CTX, integration.UserTypeIAMOwner),
|
||||
dep: func(req *user.SetUserMetadataRequest) {
|
||||
req.UserId = Instance.CreateUserTypeHuman(iamOwnerCTX, gofakeit.Email()).GetId()
|
||||
Instance.SetUserMetadata(iamOwnerCTX, req.UserId, "key1", "value1")
|
||||
},
|
||||
req: &user.SetUserMetadataRequest{
|
||||
Metadata: []*user.Metadata{{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value2")))}},
|
||||
},
|
||||
setDate: true,
|
||||
},
|
||||
{
|
||||
name: "update user metadata with same value",
|
||||
ctx: Instance.WithAuthorization(CTX, integration.UserTypeIAMOwner),
|
||||
dep: func(req *user.SetUserMetadataRequest) {
|
||||
req.UserId = Instance.CreateUserTypeHuman(iamOwnerCTX, gofakeit.Email()).GetId()
|
||||
Instance.SetUserMetadata(iamOwnerCTX, req.UserId, "key1", "value1")
|
||||
},
|
||||
req: &user.SetUserMetadataRequest{
|
||||
Metadata: []*user.Metadata{{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))}},
|
||||
},
|
||||
setDate: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
creationDate := time.Now().UTC()
|
||||
if tt.dep != nil {
|
||||
tt.dep(tt.req)
|
||||
}
|
||||
got, err := Client.SetUserMetadata(tt.ctx, tt.req)
|
||||
changeDate := time.Now().UTC()
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
assertSetUserMetadataResponse(t, creationDate, changeDate, tt.setDate, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func assertSetUserMetadataResponse(t *testing.T, creationDate, changeDate time.Time, expectedSetDat bool, actualResp *user.SetUserMetadataResponse) {
|
||||
if expectedSetDat {
|
||||
if !changeDate.IsZero() {
|
||||
assert.WithinRange(t, actualResp.GetSetDate().AsTime(), creationDate, changeDate)
|
||||
} else {
|
||||
assert.WithinRange(t, actualResp.GetSetDate().AsTime(), creationDate, time.Now().UTC())
|
||||
}
|
||||
} else {
|
||||
assert.Nil(t, actualResp.SetDate)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_ListUserMetadata(t *testing.T) {
|
||||
iamOwnerCTX := Instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
dep func(context.Context, *user.ListUserMetadataRequest, *user.ListUserMetadataResponse)
|
||||
req *user.ListUserMetadataRequest
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *user.ListUserMetadataResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "missing permission",
|
||||
args: args{
|
||||
ctx: Instance.WithAuthorization(context.Background(), integration.UserTypeNoPermission),
|
||||
dep: func(ctx context.Context, request *user.ListUserMetadataRequest, response *user.ListUserMetadataResponse) {
|
||||
userID := Instance.CreateUserTypeHuman(iamOwnerCTX, gofakeit.Email()).GetId()
|
||||
request.UserId = userID
|
||||
Instance.SetUserMetadata(iamOwnerCTX, userID, "key1", "value1")
|
||||
},
|
||||
req: &user.ListUserMetadataRequest{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "list request",
|
||||
args: args{
|
||||
ctx: iamOwnerCTX,
|
||||
dep: func(ctx context.Context, request *user.ListUserMetadataRequest, response *user.ListUserMetadataResponse) {
|
||||
userID := Instance.CreateUserTypeHuman(iamOwnerCTX, gofakeit.Email()).GetId()
|
||||
request.UserId = userID
|
||||
metadataResp := Instance.SetUserMetadata(iamOwnerCTX, userID, "key1", "value1")
|
||||
|
||||
response.Metadata[0] = &metadata.Metadata{
|
||||
CreationDate: metadataResp.GetSetDate(),
|
||||
ChangeDate: metadataResp.GetSetDate(),
|
||||
Key: "key1",
|
||||
Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1"))),
|
||||
}
|
||||
},
|
||||
req: &user.ListUserMetadataRequest{},
|
||||
},
|
||||
want: &user.ListUserMetadataResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 1,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Metadata: []*metadata.Metadata{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list request single key",
|
||||
args: args{
|
||||
ctx: iamOwnerCTX,
|
||||
dep: func(ctx context.Context, request *user.ListUserMetadataRequest, response *user.ListUserMetadataResponse) {
|
||||
userID := Instance.CreateUserTypeHuman(iamOwnerCTX, gofakeit.Email()).GetId()
|
||||
request.UserId = userID
|
||||
key := "key1"
|
||||
response.Metadata[0] = setUserMetadata(iamOwnerCTX, userID, key, "value1")
|
||||
Instance.SetUserMetadata(iamOwnerCTX, userID, "key2", "value2")
|
||||
Instance.SetUserMetadata(iamOwnerCTX, userID, "key3", "value3")
|
||||
request.Filters[0] = &metadata.MetadataSearchFilter{
|
||||
Filter: &metadata.MetadataSearchFilter_KeyFilter{KeyFilter: &metadata.MetadataKeyFilter{Key: key}},
|
||||
}
|
||||
},
|
||||
req: &user.ListUserMetadataRequest{
|
||||
Filters: []*metadata.MetadataSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &user.ListUserMetadataResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 1,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Metadata: []*metadata.Metadata{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list multiple keys",
|
||||
args: args{
|
||||
ctx: iamOwnerCTX,
|
||||
dep: func(ctx context.Context, request *user.ListUserMetadataRequest, response *user.ListUserMetadataResponse) {
|
||||
userID := Instance.CreateUserTypeHuman(iamOwnerCTX, gofakeit.Email()).GetId()
|
||||
request.UserId = userID
|
||||
|
||||
response.Metadata[2] = setUserMetadata(iamOwnerCTX, userID, "key1", "value1")
|
||||
response.Metadata[1] = setUserMetadata(iamOwnerCTX, userID, "key2", "value2")
|
||||
response.Metadata[0] = setUserMetadata(iamOwnerCTX, userID, "key3", "value3")
|
||||
},
|
||||
req: &user.ListUserMetadataRequest{},
|
||||
},
|
||||
want: &user.ListUserMetadataResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 3,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Metadata: []*metadata.Metadata{
|
||||
{}, {}, {},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.args.dep != nil {
|
||||
tt.args.dep(tt.args.ctx, tt.args.req, tt.want)
|
||||
}
|
||||
|
||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(iamOwnerCTX, time.Minute)
|
||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||
got, listErr := Instance.Client.UserV2.ListUserMetadata(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(ttt, listErr)
|
||||
return
|
||||
}
|
||||
require.NoError(ttt, listErr)
|
||||
// always first check length, otherwise its failed anyway
|
||||
if assert.Len(ttt, got.Metadata, len(tt.want.Metadata)) {
|
||||
assert.EqualExportedValues(ttt, got.Metadata, tt.want.Metadata)
|
||||
}
|
||||
assertPaginationResponse(ttt, tt.want.Pagination, got.Pagination)
|
||||
}, retryDuration, tick, "timeout waiting for expected execution result")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func setUserMetadata(ctx context.Context, userID, key, value string) *metadata.Metadata {
|
||||
metadataResp := Instance.SetUserMetadata(ctx, userID, key, value)
|
||||
return &metadata.Metadata{
|
||||
CreationDate: metadataResp.GetSetDate(),
|
||||
ChangeDate: metadataResp.GetSetDate(),
|
||||
Key: key,
|
||||
Value: []byte(base64.StdEncoding.EncodeToString([]byte(value))),
|
||||
}
|
||||
}
|
||||
|
||||
func assertPaginationResponse(t *assert.CollectT, expected *filter.PaginationResponse, actual *filter.PaginationResponse) {
|
||||
assert.Equal(t, expected.AppliedLimit, actual.AppliedLimit)
|
||||
assert.Equal(t, expected.TotalResult, actual.TotalResult)
|
||||
}
|
||||
|
||||
func TestServer_DeleteUserMetadata(t *testing.T) {
|
||||
iamOwnerCTX := Instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
ctx context.Context
|
||||
prepare func(request *user.DeleteUserMetadataRequest) (time.Time, time.Time)
|
||||
req *user.DeleteUserMetadataRequest
|
||||
wantDeletionDate bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "empty id",
|
||||
ctx: iamOwnerCTX,
|
||||
req: &user.DeleteUserMetadataRequest{
|
||||
UserId: "",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "delete, user not existing",
|
||||
ctx: iamOwnerCTX,
|
||||
req: &user.DeleteUserMetadataRequest{
|
||||
UserId: "notexisting",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "delete",
|
||||
ctx: iamOwnerCTX,
|
||||
prepare: func(request *user.DeleteUserMetadataRequest) (time.Time, time.Time) {
|
||||
creationDate := time.Now().UTC()
|
||||
userID := Instance.CreateUserTypeHuman(iamOwnerCTX, gofakeit.Email()).GetId()
|
||||
request.UserId = userID
|
||||
key := "key1"
|
||||
Instance.SetUserMetadata(iamOwnerCTX, userID, key, "value1")
|
||||
request.Keys = []string{key}
|
||||
return creationDate, time.Time{}
|
||||
},
|
||||
req: &user.DeleteUserMetadataRequest{},
|
||||
wantDeletionDate: true,
|
||||
},
|
||||
{
|
||||
name: "delete, empty list",
|
||||
ctx: iamOwnerCTX,
|
||||
prepare: func(request *user.DeleteUserMetadataRequest) (time.Time, time.Time) {
|
||||
creationDate := time.Now().UTC()
|
||||
userID := Instance.CreateUserTypeHuman(iamOwnerCTX, gofakeit.Email()).GetId()
|
||||
request.UserId = userID
|
||||
key := "key1"
|
||||
Instance.SetUserMetadata(iamOwnerCTX, userID, key, "value1")
|
||||
Instance.DeleteUserMetadata(iamOwnerCTX, userID, key)
|
||||
return creationDate, time.Now().UTC()
|
||||
},
|
||||
req: &user.DeleteUserMetadataRequest{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "delete, already removed",
|
||||
ctx: iamOwnerCTX,
|
||||
prepare: func(request *user.DeleteUserMetadataRequest) (time.Time, time.Time) {
|
||||
creationDate := time.Now().UTC()
|
||||
userID := Instance.CreateUserTypeHuman(iamOwnerCTX, gofakeit.Email()).GetId()
|
||||
request.UserId = userID
|
||||
key := "key1"
|
||||
Instance.SetUserMetadata(iamOwnerCTX, userID, key, "value1")
|
||||
Instance.DeleteUserMetadata(iamOwnerCTX, userID, key)
|
||||
request.Keys = []string{key}
|
||||
return creationDate, time.Now().UTC()
|
||||
},
|
||||
req: &user.DeleteUserMetadataRequest{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "delete, multiple",
|
||||
ctx: iamOwnerCTX,
|
||||
prepare: func(request *user.DeleteUserMetadataRequest) (time.Time, time.Time) {
|
||||
creationDate := time.Now().UTC()
|
||||
userID := Instance.CreateUserTypeHuman(iamOwnerCTX, gofakeit.Email()).GetId()
|
||||
request.UserId = userID
|
||||
key1 := "key1"
|
||||
Instance.SetUserMetadata(iamOwnerCTX, userID, key1, "value1")
|
||||
key2 := "key2"
|
||||
Instance.SetUserMetadata(iamOwnerCTX, userID, key2, "value1")
|
||||
key3 := "key3"
|
||||
Instance.SetUserMetadata(iamOwnerCTX, userID, key3, "value1")
|
||||
request.Keys = []string{key1, key2, key3}
|
||||
return creationDate, time.Time{}
|
||||
},
|
||||
req: &user.DeleteUserMetadataRequest{},
|
||||
wantDeletionDate: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var creationDate, deletionDate time.Time
|
||||
if tt.prepare != nil {
|
||||
creationDate, deletionDate = tt.prepare(tt.req)
|
||||
}
|
||||
got, err := Instance.Client.UserV2.DeleteUserMetadata(tt.ctx, tt.req)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
assertDeleteProjectResponse(t, creationDate, deletionDate, tt.wantDeletionDate, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func assertDeleteProjectResponse(t *testing.T, creationDate, deletionDate time.Time, expectedDeletionDate bool, actualResp *user.DeleteUserMetadataResponse) {
|
||||
if expectedDeletionDate {
|
||||
if !deletionDate.IsZero() {
|
||||
assert.WithinRange(t, actualResp.GetDeletionDate().AsTime(), creationDate, deletionDate)
|
||||
} else {
|
||||
assert.WithinRange(t, actualResp.GetDeletionDate().AsTime(), creationDate, time.Now().UTC())
|
||||
}
|
||||
} else {
|
||||
assert.Nil(t, actualResp.DeletionDate)
|
||||
}
|
||||
}
|
@@ -22,7 +22,7 @@ import (
|
||||
)
|
||||
|
||||
func TestServer_AddPersonalAccessToken(t *testing.T) {
|
||||
resp := Instance.CreateUserTypeMachine(IamCTX)
|
||||
resp := Instance.CreateUserTypeMachine(IamCTX, Instance.DefaultOrg.Id)
|
||||
userId := resp.GetId()
|
||||
expirationDate := timestamppb.New(time.Now().Add(time.Hour * 24))
|
||||
type args struct {
|
||||
@@ -64,7 +64,7 @@ func TestServer_AddPersonalAccessToken(t *testing.T) {
|
||||
ExpirationDate: expirationDate,
|
||||
},
|
||||
func(request *user.AddPersonalAccessTokenRequest) error {
|
||||
resp := Instance.CreateUserTypeHuman(IamCTX)
|
||||
resp := Instance.CreateUserTypeHuman(IamCTX, gofakeit.Email())
|
||||
request.UserId = resp.Id
|
||||
return nil
|
||||
},
|
||||
@@ -172,7 +172,7 @@ func TestServer_AddPersonalAccessToken_Permission(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestServer_RemovePersonalAccessToken(t *testing.T) {
|
||||
resp := Instance.CreateUserTypeMachine(IamCTX)
|
||||
resp := Instance.CreateUserTypeMachine(IamCTX, Instance.DefaultOrg.Id)
|
||||
userId := resp.GetId()
|
||||
expirationDate := timestamppb.New(time.Now().Add(time.Hour * 24))
|
||||
type args struct {
|
||||
@@ -339,7 +339,7 @@ func TestServer_ListPersonalAccessTokens(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
otherOrgUserId := otherOrgUser.GetId()
|
||||
otherUserId := Instance.CreateUserTypeMachine(SystemCTX).GetId()
|
||||
otherUserId := Instance.CreateUserTypeMachine(SystemCTX, Instance.DefaultOrg.Id).GetId()
|
||||
onlySinceTestStartFilter := &user.PersonalAccessTokensSearchFilter{Filter: &user.PersonalAccessTokensSearchFilter_CreatedDateFilter{CreatedDateFilter: &filter.TimestampFilter{
|
||||
Timestamp: timestamppb.Now(),
|
||||
Method: filter.TimestampFilterMethod_TIMESTAMP_FILTER_METHOD_AFTER_OR_EQUALS,
|
||||
|
@@ -43,7 +43,7 @@ func TestServer_AddSecret(t *testing.T) {
|
||||
CTX,
|
||||
&user.AddSecretRequest{},
|
||||
func(request *user.AddSecretRequest) error {
|
||||
resp := Instance.CreateUserTypeMachine(CTX)
|
||||
resp := Instance.CreateUserTypeMachine(CTX, Instance.DefaultOrg.Id)
|
||||
request.UserId = resp.GetId()
|
||||
return nil
|
||||
},
|
||||
@@ -55,7 +55,7 @@ func TestServer_AddSecret(t *testing.T) {
|
||||
CTX,
|
||||
&user.AddSecretRequest{},
|
||||
func(request *user.AddSecretRequest) error {
|
||||
resp := Instance.CreateUserTypeMachine(CTX)
|
||||
resp := Instance.CreateUserTypeMachine(CTX, Instance.DefaultOrg.Id)
|
||||
request.UserId = resp.GetId()
|
||||
return nil
|
||||
},
|
||||
@@ -67,7 +67,7 @@ func TestServer_AddSecret(t *testing.T) {
|
||||
CTX,
|
||||
&user.AddSecretRequest{},
|
||||
func(request *user.AddSecretRequest) error {
|
||||
resp := Instance.CreateUserTypeMachine(CTX)
|
||||
resp := Instance.CreateUserTypeMachine(CTX, Instance.DefaultOrg.Id)
|
||||
request.UserId = resp.GetId()
|
||||
_, err := Client.AddSecret(CTX, &user.AddSecretRequest{
|
||||
UserId: resp.GetId(),
|
||||
@@ -202,7 +202,7 @@ func TestServer_RemoveSecret(t *testing.T) {
|
||||
CTX,
|
||||
&user.RemoveSecretRequest{},
|
||||
func(request *user.RemoveSecretRequest) error {
|
||||
resp := Instance.CreateUserTypeMachine(CTX)
|
||||
resp := Instance.CreateUserTypeMachine(CTX, Instance.DefaultOrg.Id)
|
||||
request.UserId = resp.GetId()
|
||||
return nil
|
||||
},
|
||||
@@ -215,7 +215,7 @@ func TestServer_RemoveSecret(t *testing.T) {
|
||||
CTX,
|
||||
&user.RemoveSecretRequest{},
|
||||
func(request *user.RemoveSecretRequest) error {
|
||||
resp := Instance.CreateUserTypeMachine(CTX)
|
||||
resp := Instance.CreateUserTypeMachine(CTX, Instance.DefaultOrg.Id)
|
||||
request.UserId = resp.GetId()
|
||||
_, err := Instance.Client.UserV2.AddSecret(CTX, &user.AddSecretRequest{
|
||||
UserId: resp.GetId(),
|
||||
|
@@ -4,6 +4,7 @@ package user_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -1818,7 +1819,7 @@ func TestServer_DeleteUser(t *testing.T) {
|
||||
request.UserId = resp.GetUserId()
|
||||
Instance.CreateProjectUserGrant(t, CTX, projectResp.GetId(), request.UserId)
|
||||
Instance.CreateProjectMembership(t, CTX, projectResp.GetId(), request.UserId)
|
||||
Instance.CreateOrgMembership(t, CTX, request.UserId)
|
||||
Instance.CreateOrgMembership(t, CTX, Instance.DefaultOrg.Id, request.UserId)
|
||||
return CTX
|
||||
},
|
||||
},
|
||||
@@ -3969,6 +3970,44 @@ func TestServer_CreateUser(t *testing.T) {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with metadata",
|
||||
testCase: func(runId string) testCase {
|
||||
username := fmt.Sprintf("donald.duck+%s", runId)
|
||||
email := username + "@example.com"
|
||||
return testCase{
|
||||
args: args{
|
||||
CTX,
|
||||
&user.CreateUserRequest{
|
||||
OrganizationId: Instance.DefaultOrg.Id,
|
||||
Username: &username,
|
||||
UserType: &user.CreateUserRequest_Human_{
|
||||
Human: &user.CreateUserRequest_Human{
|
||||
Profile: &user.SetHumanProfile{
|
||||
GivenName: "Donald",
|
||||
FamilyName: "Duck",
|
||||
},
|
||||
Email: &user.SetHumanEmail{
|
||||
Email: email,
|
||||
Verification: &user.SetHumanEmail_IsVerified{
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
Metadata: []*user.Metadata{
|
||||
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))},
|
||||
{Key: "key2", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value2")))},
|
||||
{Key: "key3", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value3")))},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &user.CreateUserResponse{
|
||||
Id: "is generated",
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with idp",
|
||||
testCase: func(runId string) testCase {
|
||||
@@ -4914,7 +4953,7 @@ func TestServer_UpdateUserTypeHuman(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
now := time.Now()
|
||||
runId := fmt.Sprint(now.UnixNano() + int64(i))
|
||||
userId := Instance.CreateUserTypeHuman(CTX).GetId()
|
||||
userId := Instance.CreateUserTypeHuman(CTX, gofakeit.Email()).GetId()
|
||||
test := tt.testCase(runId, userId)
|
||||
got, err := Client.UpdateUser(test.args.ctx, test.args.req)
|
||||
if test.wantErr {
|
||||
@@ -4996,7 +5035,7 @@ func TestServer_UpdateUserTypeMachine(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
now := time.Now()
|
||||
runId := fmt.Sprint(now.UnixNano() + int64(i))
|
||||
userId := Instance.CreateUserTypeMachine(CTX).GetId()
|
||||
userId := Instance.CreateUserTypeMachine(CTX, Instance.DefaultOrg.Id).GetId()
|
||||
test := tt.testCase(runId, userId)
|
||||
got, err := Client.UpdateUser(test.args.ctx, test.args.req)
|
||||
if test.wantErr {
|
||||
|
80
internal/api/grpc/user/v2/metadata.go
Normal file
80
internal/api/grpc/user/v2/metadata.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/filter/v2"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/metadata/v2"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/user/v2"
|
||||
)
|
||||
|
||||
func (s *Server) ListUserMetadata(ctx context.Context, req *connect.Request[user.ListUserMetadataRequest]) (*connect.Response[user.ListUserMetadataResponse], error) {
|
||||
metadataQueries, err := s.listUserMetadataRequestToModel(req.Msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := s.query.SearchUserMetadata(ctx, true, req.Msg.UserId, metadataQueries, s.checkPermission)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return connect.NewResponse(&user.ListUserMetadataResponse{
|
||||
Metadata: metadata.UserMetadataListToPb(res.Metadata),
|
||||
Pagination: filter.QueryToPaginationPb(metadataQueries.SearchRequest, res.SearchResponse),
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (s *Server) listUserMetadataRequestToModel(req *user.ListUserMetadataRequest) (*query.UserMetadataSearchQueries, error) {
|
||||
offset, limit, asc, err := filter.PaginationPbToQuery(s.systemDefaults, req.Pagination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queries, err := metadata.UserMetadataFiltersToQuery(req.Filters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &query.UserMetadataSearchQueries{
|
||||
SearchRequest: query.SearchRequest{
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
Asc: asc,
|
||||
SortingColumn: query.UserMetadataCreationDateCol,
|
||||
},
|
||||
Queries: queries,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) SetUserMetadata(ctx context.Context, req *connect.Request[user.SetUserMetadataRequest]) (*connect.Response[user.SetUserMetadataResponse], error) {
|
||||
result, err := s.command.BulkSetUserMetadata(ctx, req.Msg.UserId, "", setUserMetadataToDomain(req.Msg)...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return connect.NewResponse(&user.SetUserMetadataResponse{
|
||||
SetDate: timestamppb.New(result.EventDate),
|
||||
}), nil
|
||||
}
|
||||
|
||||
func setUserMetadataToDomain(req *user.SetUserMetadataRequest) []*domain.Metadata {
|
||||
metadata := make([]*domain.Metadata, len(req.Metadata))
|
||||
for i, data := range req.Metadata {
|
||||
metadata[i] = &domain.Metadata{
|
||||
Key: data.Key,
|
||||
Value: data.Value,
|
||||
}
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
|
||||
func (s *Server) DeleteUserMetadata(ctx context.Context, req *connect.Request[user.DeleteUserMetadataRequest]) (*connect.Response[user.DeleteUserMetadataResponse], error) {
|
||||
result, err := s.command.BulkRemoveUserMetadata(ctx, req.Msg.UserId, "", req.Msg.Keys...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return connect.NewResponse(&user.DeleteUserMetadataResponse{
|
||||
DeletionDate: timestamppb.New(result.EventDate),
|
||||
}), nil
|
||||
}
|
@@ -37,9 +37,9 @@ type Server struct {
|
||||
type Config struct{}
|
||||
|
||||
func CreateServer(
|
||||
systemDefaults systemdefaults.SystemDefaults,
|
||||
command *command.Commands,
|
||||
query *query.Queries,
|
||||
systemDefaults systemdefaults.SystemDefaults,
|
||||
userCodeAlg crypto.EncryptionAlgorithm,
|
||||
idpAlg crypto.EncryptionAlgorithm,
|
||||
idpCallback func(ctx context.Context) string,
|
||||
@@ -48,7 +48,6 @@ func CreateServer(
|
||||
checkPermission domain.PermissionCheck,
|
||||
) *Server {
|
||||
return &Server{
|
||||
systemDefaults: systemDefaults,
|
||||
command: command,
|
||||
query: query,
|
||||
userCodeAlg: userCodeAlg,
|
||||
@@ -57,6 +56,7 @@ func CreateServer(
|
||||
samlRootURL: samlRootURL,
|
||||
assetAPIPrefix: assetAPIPrefix,
|
||||
checkPermission: checkPermission,
|
||||
systemDefaults: systemDefaults,
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -204,7 +204,7 @@ func (s *Server) removeUserDependencies(ctx context.Context, userID string) ([]*
|
||||
}
|
||||
grants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{
|
||||
Queries: []query.SearchQuery{userGrantUserQuery},
|
||||
}, true)
|
||||
}, true, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@@ -1773,7 +1773,7 @@ func TestServer_DeleteUser(t *testing.T) {
|
||||
request.UserId = resp.GetUserId()
|
||||
Instance.CreateProjectUserGrant(t, CTX, projectResp.GetId(), request.UserId)
|
||||
Instance.CreateProjectMembership(t, CTX, projectResp.GetId(), request.UserId)
|
||||
Instance.CreateOrgMembership(t, CTX, request.UserId)
|
||||
Instance.CreateOrgMembership(t, CTX, Instance.DefaultOrg.Id, request.UserId)
|
||||
},
|
||||
},
|
||||
want: &user.DeleteUserResponse{
|
||||
@@ -2125,7 +2125,7 @@ func TestServer_StartIdentityProviderIntent(t *testing.T) {
|
||||
if tt.want.url != "" && !tt.want.postForm {
|
||||
authUrl, err := url.Parse(got.GetAuthUrl())
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
assert.Equal(t, tt.want.url, authUrl.Scheme+"://"+authUrl.Host+authUrl.Path)
|
||||
require.Len(t, authUrl.Query(), len(tt.want.parametersEqual)+len(tt.want.parametersExisting))
|
||||
|
||||
|
@@ -296,7 +296,7 @@ func (s *Server) removeUserDependencies(ctx context.Context, userID string) ([]*
|
||||
}
|
||||
grants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{
|
||||
Queries: []query.SearchQuery{userGrantUserQuery},
|
||||
}, true)
|
||||
}, true, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
Reference in New Issue
Block a user