mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 03:37:34 +00:00
chore(tests): use a coverage server binary (#8407)
# Which Problems Are Solved Use a single server instance for API integration tests. This optimizes the time taken for the integration test pipeline, because it allows running tests on multiple packages in parallel. Also, it saves time by not start and stopping a zitadel server for every package. # How the Problems Are Solved - Build a binary with `go build -race -cover ....` - Integration tests only construct clients. The server remains running in the background. - The integration package and tested packages now fully utilize the API. No more direct database access trough `query` and `command` packages. - Use Makefile recipes to setup, start and stop the server in the background. - The binary has the race detector enabled - Init and setup jobs are configured to halt immediately on race condition - Because the server runs in the background, races are only logged. When the server is stopped and race logs exist, the Makefile recipe will throw an error and print the logs. - Makefile recipes include logic to print logs and convert coverage reports after the server is stopped. - Some tests need a downstream HTTP server to make requests, like quota and milestones. A new `integration/sink` package creates an HTTP server and uses websockets to forward HTTP request back to the test packages. The package API uses Go channels for abstraction and easy usage. # Additional Changes - Integration test files already used the `//go:build integration` directive. In order to properly split integration from unit tests, integration test files need to be in a `integration_test` subdirectory of their package. - `UseIsolatedInstance` used to overwrite the `Tester.Client` for each instance. Now a `Instance` object is returned with a gRPC client that is connected to the isolated instance's hostname. - The `Tester` type is now `Instance`. The object is created for the first instance, used by default in any test. Isolated instances are also `Instance` objects and therefore benefit from the same methods and values. The first instance and any other us capable of creating an isolated instance over the system API. - All test packages run in an Isolated instance by calling `NewInstance()` - Individual tests that use an isolated instance use `t.Parallel()` # Additional Context - Closes #6684 - https://go.dev/doc/articles/race_detector - https://go.dev/doc/build-cover --------- Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com>
This commit is contained in:
297
internal/api/grpc/user/v2beta/integration_test/email_test.go
Normal file
297
internal/api/grpc/user/v2beta/integration_test/email_test.go
Normal file
@@ -0,0 +1,297 @@
|
||||
//go:build integration
|
||||
|
||||
package user_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
object "github.com/zitadel/zitadel/pkg/grpc/object/v2beta"
|
||||
user "github.com/zitadel/zitadel/pkg/grpc/user/v2beta"
|
||||
)
|
||||
|
||||
func TestServer_SetEmail(t *testing.T) {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
req *user.SetEmailRequest
|
||||
want *user.SetEmailResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "user not existing",
|
||||
req: &user.SetEmailRequest{
|
||||
UserId: "xxx",
|
||||
Email: "default-verifier@mouse.com",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "default verfication",
|
||||
req: &user.SetEmailRequest{
|
||||
UserId: userID,
|
||||
Email: "default-verifier@mouse.com",
|
||||
},
|
||||
want: &user.SetEmailResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: 1,
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "custom url template",
|
||||
req: &user.SetEmailRequest{
|
||||
UserId: userID,
|
||||
Email: "custom-url@mouse.com",
|
||||
Verification: &user.SetEmailRequest_SendCode{
|
||||
SendCode: &user.SendEmailVerificationCode{
|
||||
UrlTemplate: gu.Ptr("https://example.com/email/verify?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}"),
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &user.SetEmailResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: 1,
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "template error",
|
||||
req: &user.SetEmailRequest{
|
||||
UserId: userID,
|
||||
Email: "custom-url@mouse.com",
|
||||
Verification: &user.SetEmailRequest_SendCode{
|
||||
SendCode: &user.SendEmailVerificationCode{
|
||||
UrlTemplate: gu.Ptr("{{"),
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "return code",
|
||||
req: &user.SetEmailRequest{
|
||||
UserId: userID,
|
||||
Email: "return-code@mouse.com",
|
||||
Verification: &user.SetEmailRequest_ReturnCode{
|
||||
ReturnCode: &user.ReturnEmailVerificationCode{},
|
||||
},
|
||||
},
|
||||
want: &user.SetEmailResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: 1,
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
VerificationCode: gu.Ptr("xxx"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "is verified true",
|
||||
req: &user.SetEmailRequest{
|
||||
UserId: userID,
|
||||
Email: "verified-true@mouse.com",
|
||||
Verification: &user.SetEmailRequest_IsVerified{
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
want: &user.SetEmailResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: 1,
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "is verified false",
|
||||
req: &user.SetEmailRequest{
|
||||
UserId: userID,
|
||||
Email: "verified-false@mouse.com",
|
||||
Verification: &user.SetEmailRequest_IsVerified{
|
||||
IsVerified: false,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.SetEmail(CTX, tt.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
if tt.want.GetVerificationCode() != "" {
|
||||
assert.NotEmpty(t, got.GetVerificationCode())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_ResendEmailCode(t *testing.T) {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
verifiedUserID := Instance.CreateHumanUserVerified(CTX, Instance.DefaultOrg.Id, fmt.Sprintf("%d@mouse.com", time.Now().UnixNano())).GetUserId()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
req *user.ResendEmailCodeRequest
|
||||
want *user.ResendEmailCodeResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "user not existing",
|
||||
req: &user.ResendEmailCodeRequest{
|
||||
UserId: "xxx",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "user no code",
|
||||
req: &user.ResendEmailCodeRequest{
|
||||
UserId: verifiedUserID,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "resend",
|
||||
req: &user.ResendEmailCodeRequest{
|
||||
UserId: userID,
|
||||
},
|
||||
want: &user.ResendEmailCodeResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: 1,
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "custom url template",
|
||||
req: &user.ResendEmailCodeRequest{
|
||||
UserId: userID,
|
||||
Verification: &user.ResendEmailCodeRequest_SendCode{
|
||||
SendCode: &user.SendEmailVerificationCode{
|
||||
UrlTemplate: gu.Ptr("https://example.com/email/verify?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}"),
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &user.ResendEmailCodeResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: 1,
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "template error",
|
||||
req: &user.ResendEmailCodeRequest{
|
||||
UserId: userID,
|
||||
Verification: &user.ResendEmailCodeRequest_SendCode{
|
||||
SendCode: &user.SendEmailVerificationCode{
|
||||
UrlTemplate: gu.Ptr("{{"),
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "return code",
|
||||
req: &user.ResendEmailCodeRequest{
|
||||
UserId: userID,
|
||||
Verification: &user.ResendEmailCodeRequest_ReturnCode{
|
||||
ReturnCode: &user.ReturnEmailVerificationCode{},
|
||||
},
|
||||
},
|
||||
want: &user.ResendEmailCodeResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: 1,
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
VerificationCode: gu.Ptr("xxx"),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.ResendEmailCode(CTX, tt.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
if tt.want.GetVerificationCode() != "" {
|
||||
assert.NotEmpty(t, got.GetVerificationCode())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_VerifyEmail(t *testing.T) {
|
||||
userResp := Instance.CreateHumanUser(CTX)
|
||||
tests := []struct {
|
||||
name string
|
||||
req *user.VerifyEmailRequest
|
||||
want *user.VerifyEmailResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "wrong code",
|
||||
req: &user.VerifyEmailRequest{
|
||||
UserId: userResp.GetUserId(),
|
||||
VerificationCode: "xxx",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "wrong user",
|
||||
req: &user.VerifyEmailRequest{
|
||||
UserId: "xxx",
|
||||
VerificationCode: userResp.GetEmailCode(),
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "verify user",
|
||||
req: &user.VerifyEmailRequest{
|
||||
UserId: userResp.GetUserId(),
|
||||
VerificationCode: userResp.GetEmailCode(),
|
||||
},
|
||||
want: &user.VerifyEmailResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: 1,
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.VerifyEmail(CTX, tt.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
362
internal/api/grpc/user/v2beta/integration_test/otp_test.go
Normal file
362
internal/api/grpc/user/v2beta/integration_test/otp_test.go
Normal file
@@ -0,0 +1,362 @@
|
||||
//go:build integration
|
||||
|
||||
package user_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
object "github.com/zitadel/zitadel/pkg/grpc/object/v2beta"
|
||||
user "github.com/zitadel/zitadel/pkg/grpc/user/v2beta"
|
||||
)
|
||||
|
||||
func TestServer_AddOTPSMS(t *testing.T) {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
Instance.RegisterUserPasskey(CTX, userID)
|
||||
_, sessionToken, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userID)
|
||||
|
||||
otherUser := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
Instance.RegisterUserPasskey(CTX, otherUser)
|
||||
_, sessionTokenOtherUser, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, otherUser)
|
||||
|
||||
userVerified := Instance.CreateHumanUser(CTX)
|
||||
_, err := Client.VerifyPhone(CTX, &user.VerifyPhoneRequest{
|
||||
UserId: userVerified.GetUserId(),
|
||||
VerificationCode: userVerified.GetPhoneCode(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
Instance.RegisterUserPasskey(CTX, userVerified.GetUserId())
|
||||
_, sessionTokenVerified, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userVerified.GetUserId())
|
||||
|
||||
userVerified2 := Instance.CreateHumanUser(CTX)
|
||||
_, err = Client.VerifyPhone(CTX, &user.VerifyPhoneRequest{
|
||||
UserId: userVerified2.GetUserId(),
|
||||
VerificationCode: userVerified2.GetPhoneCode(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *user.AddOTPSMSRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *user.AddOTPSMSResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "missing user id",
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
req: &user.AddOTPSMSRequest{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "user mismatch",
|
||||
args: args{
|
||||
ctx: integration.WithAuthorizationToken(context.Background(), sessionTokenOtherUser),
|
||||
req: &user.AddOTPSMSRequest{
|
||||
UserId: userID,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "phone not verified",
|
||||
args: args{
|
||||
ctx: integration.WithAuthorizationToken(context.Background(), sessionToken),
|
||||
req: &user.AddOTPSMSRequest{
|
||||
UserId: userID,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "add success",
|
||||
args: args{
|
||||
ctx: integration.WithAuthorizationToken(context.Background(), sessionTokenVerified),
|
||||
req: &user.AddOTPSMSRequest{
|
||||
UserId: userVerified.GetUserId(),
|
||||
},
|
||||
},
|
||||
want: &user.AddOTPSMSResponse{
|
||||
Details: &object.Details{
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add success, admin",
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
req: &user.AddOTPSMSRequest{
|
||||
UserId: userVerified2.GetUserId(),
|
||||
},
|
||||
},
|
||||
want: &user.AddOTPSMSResponse{
|
||||
Details: &object.Details{
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.AddOTPSMS(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_RemoveOTPSMS(t *testing.T) {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
Instance.RegisterUserPasskey(CTX, userID)
|
||||
_, sessionToken, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userID)
|
||||
|
||||
userVerified := Instance.CreateHumanUser(CTX)
|
||||
Instance.RegisterUserPasskey(CTX, userVerified.GetUserId())
|
||||
_, sessionTokenVerified, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userVerified.GetUserId())
|
||||
userVerifiedCtx := integration.WithAuthorizationToken(context.Background(), sessionTokenVerified)
|
||||
_, err := Client.VerifyPhone(userVerifiedCtx, &user.VerifyPhoneRequest{
|
||||
UserId: userVerified.GetUserId(),
|
||||
VerificationCode: userVerified.GetPhoneCode(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = Client.AddOTPSMS(userVerifiedCtx, &user.AddOTPSMSRequest{UserId: userVerified.GetUserId()})
|
||||
require.NoError(t, err)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *user.RemoveOTPSMSRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *user.RemoveOTPSMSResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "not added",
|
||||
args: args{
|
||||
ctx: integration.WithAuthorizationToken(context.Background(), sessionToken),
|
||||
req: &user.RemoveOTPSMSRequest{
|
||||
UserId: userID,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "success",
|
||||
args: args{
|
||||
ctx: userVerifiedCtx,
|
||||
req: &user.RemoveOTPSMSRequest{
|
||||
UserId: userVerified.GetUserId(),
|
||||
},
|
||||
},
|
||||
want: &user.RemoveOTPSMSResponse{
|
||||
Details: &object.Details{
|
||||
ResourceOwner: Instance.DefaultOrg.Details.ResourceOwner,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.RemoveOTPSMS(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_AddOTPEmail(t *testing.T) {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
Instance.RegisterUserPasskey(CTX, userID)
|
||||
_, sessionToken, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userID)
|
||||
|
||||
otherUser := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
Instance.RegisterUserPasskey(CTX, otherUser)
|
||||
_, sessionTokenOtherUser, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, otherUser)
|
||||
|
||||
userVerified := Instance.CreateHumanUser(CTX)
|
||||
_, err := Client.VerifyEmail(CTX, &user.VerifyEmailRequest{
|
||||
UserId: userVerified.GetUserId(),
|
||||
VerificationCode: userVerified.GetEmailCode(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
Instance.RegisterUserPasskey(CTX, userVerified.GetUserId())
|
||||
_, sessionTokenVerified, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userVerified.GetUserId())
|
||||
|
||||
userVerified2 := Instance.CreateHumanUser(CTX)
|
||||
_, err = Client.VerifyEmail(CTX, &user.VerifyEmailRequest{
|
||||
UserId: userVerified2.GetUserId(),
|
||||
VerificationCode: userVerified2.GetEmailCode(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *user.AddOTPEmailRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *user.AddOTPEmailResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "missing user id",
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
req: &user.AddOTPEmailRequest{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "user mismatch",
|
||||
args: args{
|
||||
ctx: integration.WithAuthorizationToken(context.Background(), sessionTokenOtherUser),
|
||||
req: &user.AddOTPEmailRequest{
|
||||
UserId: userID,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "email not verified",
|
||||
args: args{
|
||||
ctx: integration.WithAuthorizationToken(context.Background(), sessionToken),
|
||||
req: &user.AddOTPEmailRequest{
|
||||
UserId: userID,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "add success",
|
||||
args: args{
|
||||
ctx: integration.WithAuthorizationToken(context.Background(), sessionTokenVerified),
|
||||
req: &user.AddOTPEmailRequest{
|
||||
UserId: userVerified.GetUserId(),
|
||||
},
|
||||
},
|
||||
want: &user.AddOTPEmailResponse{
|
||||
Details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add success, admin",
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
req: &user.AddOTPEmailRequest{
|
||||
UserId: userVerified2.GetUserId(),
|
||||
},
|
||||
},
|
||||
want: &user.AddOTPEmailResponse{
|
||||
Details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.AddOTPEmail(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_RemoveOTPEmail(t *testing.T) {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
Instance.RegisterUserPasskey(CTX, userID)
|
||||
_, sessionToken, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userID)
|
||||
|
||||
userVerified := Instance.CreateHumanUser(CTX)
|
||||
Instance.RegisterUserPasskey(CTX, userVerified.GetUserId())
|
||||
_, sessionTokenVerified, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userVerified.GetUserId())
|
||||
userVerifiedCtx := integration.WithAuthorizationToken(context.Background(), sessionTokenVerified)
|
||||
_, err := Client.VerifyEmail(userVerifiedCtx, &user.VerifyEmailRequest{
|
||||
UserId: userVerified.GetUserId(),
|
||||
VerificationCode: userVerified.GetEmailCode(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = Client.AddOTPEmail(userVerifiedCtx, &user.AddOTPEmailRequest{UserId: userVerified.GetUserId()})
|
||||
require.NoError(t, err)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *user.RemoveOTPEmailRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *user.RemoveOTPEmailResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "not added",
|
||||
args: args{
|
||||
ctx: integration.WithAuthorizationToken(context.Background(), sessionToken),
|
||||
req: &user.RemoveOTPEmailRequest{
|
||||
UserId: userID,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "success",
|
||||
args: args{
|
||||
ctx: userVerifiedCtx,
|
||||
req: &user.RemoveOTPEmailRequest{
|
||||
UserId: userVerified.GetUserId(),
|
||||
},
|
||||
},
|
||||
want: &user.RemoveOTPEmailResponse{
|
||||
Details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Details.ResourceOwner,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.RemoveOTPEmail(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
319
internal/api/grpc/user/v2beta/integration_test/passkey_test.go
Normal file
319
internal/api/grpc/user/v2beta/integration_test/passkey_test.go
Normal file
@@ -0,0 +1,319 @@
|
||||
//go:build integration
|
||||
|
||||
package user_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
object "github.com/zitadel/zitadel/pkg/grpc/object/v2beta"
|
||||
user "github.com/zitadel/zitadel/pkg/grpc/user/v2beta"
|
||||
)
|
||||
|
||||
func TestServer_RegisterPasskey(t *testing.T) {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
reg, err := Client.CreatePasskeyRegistrationLink(CTX, &user.CreatePasskeyRegistrationLinkRequest{
|
||||
UserId: userID,
|
||||
Medium: &user.CreatePasskeyRegistrationLinkRequest_ReturnCode{},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// We also need a user session
|
||||
Instance.RegisterUserPasskey(CTX, userID)
|
||||
_, sessionToken, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userID)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *user.RegisterPasskeyRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *user.RegisterPasskeyResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "missing user id",
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
req: &user.RegisterPasskeyRequest{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "register code",
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
req: &user.RegisterPasskeyRequest{
|
||||
UserId: userID,
|
||||
Code: reg.GetCode(),
|
||||
Authenticator: user.PasskeyAuthenticator_PASSKEY_AUTHENTICATOR_PLATFORM,
|
||||
},
|
||||
},
|
||||
want: &user.RegisterPasskeyResponse{
|
||||
Details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "reuse code (not allowed)",
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
req: &user.RegisterPasskeyRequest{
|
||||
UserId: userID,
|
||||
Code: reg.GetCode(),
|
||||
Authenticator: user.PasskeyAuthenticator_PASSKEY_AUTHENTICATOR_PLATFORM,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "wrong code",
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
req: &user.RegisterPasskeyRequest{
|
||||
UserId: userID,
|
||||
Code: &user.PasskeyRegistrationCode{
|
||||
Id: reg.GetCode().GetId(),
|
||||
Code: "foobar",
|
||||
},
|
||||
Authenticator: user.PasskeyAuthenticator_PASSKEY_AUTHENTICATOR_CROSS_PLATFORM,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "user mismatch",
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
req: &user.RegisterPasskeyRequest{
|
||||
UserId: userID,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "user setting its own passkey",
|
||||
args: args{
|
||||
ctx: integration.WithAuthorizationToken(CTX, sessionToken),
|
||||
req: &user.RegisterPasskeyRequest{
|
||||
UserId: userID,
|
||||
},
|
||||
},
|
||||
want: &user.RegisterPasskeyResponse{
|
||||
Details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.RegisterPasskey(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
if tt.want != nil {
|
||||
assert.NotEmpty(t, got.GetPasskeyId())
|
||||
assert.NotEmpty(t, got.GetPublicKeyCredentialCreationOptions())
|
||||
_, err = Instance.WebAuthN.CreateAttestationResponse(got.GetPublicKeyCredentialCreationOptions())
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_VerifyPasskeyRegistration(t *testing.T) {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
reg, err := Client.CreatePasskeyRegistrationLink(CTX, &user.CreatePasskeyRegistrationLinkRequest{
|
||||
UserId: userID,
|
||||
Medium: &user.CreatePasskeyRegistrationLinkRequest_ReturnCode{},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
pkr, err := Client.RegisterPasskey(CTX, &user.RegisterPasskeyRequest{
|
||||
UserId: userID,
|
||||
Code: reg.GetCode(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, pkr.GetPasskeyId())
|
||||
require.NotEmpty(t, pkr.GetPublicKeyCredentialCreationOptions())
|
||||
|
||||
attestationResponse, err := Instance.WebAuthN.CreateAttestationResponse(pkr.GetPublicKeyCredentialCreationOptions())
|
||||
require.NoError(t, err)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *user.VerifyPasskeyRegistrationRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *user.VerifyPasskeyRegistrationResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "missing user id",
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
req: &user.VerifyPasskeyRegistrationRequest{
|
||||
PasskeyId: pkr.GetPasskeyId(),
|
||||
PublicKeyCredential: attestationResponse,
|
||||
PasskeyName: "nice name",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "success",
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
req: &user.VerifyPasskeyRegistrationRequest{
|
||||
UserId: userID,
|
||||
PasskeyId: pkr.GetPasskeyId(),
|
||||
PublicKeyCredential: attestationResponse,
|
||||
PasskeyName: "nice name",
|
||||
},
|
||||
},
|
||||
want: &user.VerifyPasskeyRegistrationResponse{
|
||||
Details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "wrong credential",
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
req: &user.VerifyPasskeyRegistrationRequest{
|
||||
UserId: userID,
|
||||
PasskeyId: pkr.GetPasskeyId(),
|
||||
PublicKeyCredential: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{"foo": {Kind: &structpb.Value_StringValue{StringValue: "bar"}}},
|
||||
},
|
||||
PasskeyName: "nice name",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.VerifyPasskeyRegistration(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_CreatePasskeyRegistrationLink(t *testing.T) {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *user.CreatePasskeyRegistrationLinkRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *user.CreatePasskeyRegistrationLinkResponse
|
||||
wantCode bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "missing user id",
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
req: &user.CreatePasskeyRegistrationLinkRequest{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "send default mail",
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
req: &user.CreatePasskeyRegistrationLinkRequest{
|
||||
UserId: userID,
|
||||
},
|
||||
},
|
||||
want: &user.CreatePasskeyRegistrationLinkResponse{
|
||||
Details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "send custom url",
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
req: &user.CreatePasskeyRegistrationLinkRequest{
|
||||
UserId: userID,
|
||||
Medium: &user.CreatePasskeyRegistrationLinkRequest_SendLink{
|
||||
SendLink: &user.SendPasskeyRegistrationLink{
|
||||
UrlTemplate: gu.Ptr("https://example.com/passkey/register?userID={{.UserID}}&orgID={{.OrgID}}&codeID={{.CodeID}}&code={{.Code}}"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &user.CreatePasskeyRegistrationLinkResponse{
|
||||
Details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "return code",
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
req: &user.CreatePasskeyRegistrationLinkRequest{
|
||||
UserId: userID,
|
||||
Medium: &user.CreatePasskeyRegistrationLinkRequest_ReturnCode{},
|
||||
},
|
||||
},
|
||||
want: &user.CreatePasskeyRegistrationLinkResponse{
|
||||
Details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
wantCode: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.CreatePasskeyRegistrationLink(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
if tt.wantCode {
|
||||
assert.NotEmpty(t, got.GetCode().GetId())
|
||||
assert.NotEmpty(t, got.GetCode().GetId())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
232
internal/api/grpc/user/v2beta/integration_test/password_test.go
Normal file
232
internal/api/grpc/user/v2beta/integration_test/password_test.go
Normal file
@@ -0,0 +1,232 @@
|
||||
//go:build integration
|
||||
|
||||
package user_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
object "github.com/zitadel/zitadel/pkg/grpc/object/v2beta"
|
||||
user "github.com/zitadel/zitadel/pkg/grpc/user/v2beta"
|
||||
)
|
||||
|
||||
func TestServer_RequestPasswordReset(t *testing.T) {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
req *user.PasswordResetRequest
|
||||
want *user.PasswordResetResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "default medium",
|
||||
req: &user.PasswordResetRequest{
|
||||
UserId: userID,
|
||||
},
|
||||
want: &user.PasswordResetResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: 1,
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "custom url template",
|
||||
req: &user.PasswordResetRequest{
|
||||
UserId: userID,
|
||||
Medium: &user.PasswordResetRequest_SendLink{
|
||||
SendLink: &user.SendPasswordResetLink{
|
||||
NotificationType: user.NotificationType_NOTIFICATION_TYPE_Email,
|
||||
UrlTemplate: gu.Ptr("https://example.com/password/change?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}"),
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &user.PasswordResetResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: 1,
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "template error",
|
||||
req: &user.PasswordResetRequest{
|
||||
UserId: userID,
|
||||
Medium: &user.PasswordResetRequest_SendLink{
|
||||
SendLink: &user.SendPasswordResetLink{
|
||||
UrlTemplate: gu.Ptr("{{"),
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "return code",
|
||||
req: &user.PasswordResetRequest{
|
||||
UserId: userID,
|
||||
Medium: &user.PasswordResetRequest_ReturnCode{
|
||||
ReturnCode: &user.ReturnPasswordResetCode{},
|
||||
},
|
||||
},
|
||||
want: &user.PasswordResetResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: 1,
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
VerificationCode: gu.Ptr("xxx"),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.PasswordReset(CTX, tt.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
if tt.want.GetVerificationCode() != "" {
|
||||
assert.NotEmpty(t, got.GetVerificationCode())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_SetPassword(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *user.SetPasswordRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
prepare func(request *user.SetPasswordRequest) error
|
||||
args args
|
||||
want *user.SetPasswordResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "missing user id",
|
||||
prepare: func(request *user.SetPasswordRequest) error {
|
||||
return nil
|
||||
},
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
req: &user.SetPasswordRequest{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "set successful",
|
||||
prepare: func(request *user.SetPasswordRequest) error {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
request.UserId = userID
|
||||
return nil
|
||||
},
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
req: &user.SetPasswordRequest{
|
||||
NewPassword: &user.Password{
|
||||
Password: "Secr3tP4ssw0rd!",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &user.SetPasswordResponse{
|
||||
Details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "change successful",
|
||||
prepare: func(request *user.SetPasswordRequest) error {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
request.UserId = userID
|
||||
_, err := Client.SetPassword(CTX, &user.SetPasswordRequest{
|
||||
UserId: userID,
|
||||
NewPassword: &user.Password{
|
||||
Password: "InitialPassw0rd!",
|
||||
},
|
||||
})
|
||||
return err
|
||||
},
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
req: &user.SetPasswordRequest{
|
||||
NewPassword: &user.Password{
|
||||
Password: "Secr3tP4ssw0rd!",
|
||||
},
|
||||
Verification: &user.SetPasswordRequest_CurrentPassword{
|
||||
CurrentPassword: "InitialPassw0rd!",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &user.SetPasswordResponse{
|
||||
Details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "set with code successful",
|
||||
prepare: func(request *user.SetPasswordRequest) error {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
request.UserId = userID
|
||||
resp, err := Client.PasswordReset(CTX, &user.PasswordResetRequest{
|
||||
UserId: userID,
|
||||
Medium: &user.PasswordResetRequest_ReturnCode{
|
||||
ReturnCode: &user.ReturnPasswordResetCode{},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
request.Verification = &user.SetPasswordRequest_VerificationCode{
|
||||
VerificationCode: resp.GetVerificationCode(),
|
||||
}
|
||||
return nil
|
||||
},
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
req: &user.SetPasswordRequest{
|
||||
NewPassword: &user.Password{
|
||||
Password: "Secr3tP4ssw0rd!",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &user.SetPasswordResponse{
|
||||
Details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.prepare(tt.args.req)
|
||||
require.NoError(t, err)
|
||||
|
||||
got, err := Client.SetPassword(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
344
internal/api/grpc/user/v2beta/integration_test/phone_test.go
Normal file
344
internal/api/grpc/user/v2beta/integration_test/phone_test.go
Normal file
@@ -0,0 +1,344 @@
|
||||
//go:build integration
|
||||
|
||||
package user_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
object "github.com/zitadel/zitadel/pkg/grpc/object/v2beta"
|
||||
user "github.com/zitadel/zitadel/pkg/grpc/user/v2beta"
|
||||
)
|
||||
|
||||
func TestServer_SetPhone(t *testing.T) {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
req *user.SetPhoneRequest
|
||||
want *user.SetPhoneResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "default verification",
|
||||
req: &user.SetPhoneRequest{
|
||||
UserId: userID,
|
||||
Phone: "+41791234568",
|
||||
},
|
||||
want: &user.SetPhoneResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: 1,
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "send verification",
|
||||
req: &user.SetPhoneRequest{
|
||||
UserId: userID,
|
||||
Phone: "+41791234569",
|
||||
Verification: &user.SetPhoneRequest_SendCode{
|
||||
SendCode: &user.SendPhoneVerificationCode{},
|
||||
},
|
||||
},
|
||||
want: &user.SetPhoneResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: 1,
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "return code",
|
||||
req: &user.SetPhoneRequest{
|
||||
UserId: userID,
|
||||
Phone: "+41791234566",
|
||||
Verification: &user.SetPhoneRequest_ReturnCode{
|
||||
ReturnCode: &user.ReturnPhoneVerificationCode{},
|
||||
},
|
||||
},
|
||||
want: &user.SetPhoneResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: 1,
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
VerificationCode: gu.Ptr("xxx"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "is verified true",
|
||||
req: &user.SetPhoneRequest{
|
||||
UserId: userID,
|
||||
Phone: "+41791234565",
|
||||
Verification: &user.SetPhoneRequest_IsVerified{
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
want: &user.SetPhoneResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: 1,
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "is verified false",
|
||||
req: &user.SetPhoneRequest{
|
||||
UserId: userID,
|
||||
Phone: "+41791234564",
|
||||
Verification: &user.SetPhoneRequest_IsVerified{
|
||||
IsVerified: false,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.SetPhone(CTX, tt.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
if tt.want.GetVerificationCode() != "" {
|
||||
assert.NotEmpty(t, got.GetVerificationCode())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_ResendPhoneCode(t *testing.T) {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
verifiedUserID := Instance.CreateHumanUserVerified(CTX, Instance.DefaultOrg.Id, fmt.Sprintf("%d@mouse.com", time.Now().UnixNano())).GetUserId()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
req *user.ResendPhoneCodeRequest
|
||||
want *user.ResendPhoneCodeResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "user not existing",
|
||||
req: &user.ResendPhoneCodeRequest{
|
||||
UserId: "xxx",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "user not existing",
|
||||
req: &user.ResendPhoneCodeRequest{
|
||||
UserId: verifiedUserID,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "resend code",
|
||||
req: &user.ResendPhoneCodeRequest{
|
||||
UserId: userID,
|
||||
Verification: &user.ResendPhoneCodeRequest_SendCode{
|
||||
SendCode: &user.SendPhoneVerificationCode{},
|
||||
},
|
||||
},
|
||||
want: &user.ResendPhoneCodeResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: 1,
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "return code",
|
||||
req: &user.ResendPhoneCodeRequest{
|
||||
UserId: userID,
|
||||
Verification: &user.ResendPhoneCodeRequest_ReturnCode{
|
||||
ReturnCode: &user.ReturnPhoneVerificationCode{},
|
||||
},
|
||||
},
|
||||
want: &user.ResendPhoneCodeResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: 1,
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
VerificationCode: gu.Ptr("xxx"),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.ResendPhoneCode(CTX, tt.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
if tt.want.GetVerificationCode() != "" {
|
||||
assert.NotEmpty(t, got.GetVerificationCode())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_VerifyPhone(t *testing.T) {
|
||||
userResp := Instance.CreateHumanUser(CTX)
|
||||
tests := []struct {
|
||||
name string
|
||||
req *user.VerifyPhoneRequest
|
||||
want *user.VerifyPhoneResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "wrong code",
|
||||
req: &user.VerifyPhoneRequest{
|
||||
UserId: userResp.GetUserId(),
|
||||
VerificationCode: "xxx",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "wrong user",
|
||||
req: &user.VerifyPhoneRequest{
|
||||
UserId: "xxx",
|
||||
VerificationCode: userResp.GetPhoneCode(),
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "verify user",
|
||||
req: &user.VerifyPhoneRequest{
|
||||
UserId: userResp.GetUserId(),
|
||||
VerificationCode: userResp.GetPhoneCode(),
|
||||
},
|
||||
want: &user.VerifyPhoneResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: 1,
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.VerifyPhone(CTX, tt.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_RemovePhone(t *testing.T) {
|
||||
userResp := Instance.CreateHumanUser(CTX)
|
||||
failResp := Instance.CreateHumanUserNoPhone(CTX)
|
||||
otherUser := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
doubleRemoveUser := Instance.CreateHumanUser(CTX)
|
||||
|
||||
Instance.RegisterUserPasskey(CTX, otherUser)
|
||||
_, sessionTokenOtherUser, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, otherUser)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
ctx context.Context
|
||||
req *user.RemovePhoneRequest
|
||||
want *user.RemovePhoneResponse
|
||||
wantErr bool
|
||||
dep func(ctx context.Context, userID string) (*user.RemovePhoneResponse, error)
|
||||
}{
|
||||
{
|
||||
name: "remove phone",
|
||||
ctx: CTX,
|
||||
req: &user.RemovePhoneRequest{
|
||||
UserId: userResp.GetUserId(),
|
||||
},
|
||||
want: &user.RemovePhoneResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: 1,
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
dep: func(ctx context.Context, userID string) (*user.RemovePhoneResponse, error) {
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "user without phone",
|
||||
ctx: CTX,
|
||||
req: &user.RemovePhoneRequest{
|
||||
UserId: failResp.GetUserId(),
|
||||
},
|
||||
wantErr: true,
|
||||
dep: func(ctx context.Context, userID string) (*user.RemovePhoneResponse, error) {
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "remove previously deleted phone",
|
||||
ctx: CTX,
|
||||
req: &user.RemovePhoneRequest{
|
||||
UserId: doubleRemoveUser.GetUserId(),
|
||||
},
|
||||
wantErr: true,
|
||||
dep: func(ctx context.Context, userID string) (*user.RemovePhoneResponse, error) {
|
||||
return Client.RemovePhone(ctx, &user.RemovePhoneRequest{
|
||||
UserId: doubleRemoveUser.GetUserId(),
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no user id",
|
||||
ctx: CTX,
|
||||
req: &user.RemovePhoneRequest{},
|
||||
wantErr: true,
|
||||
dep: func(ctx context.Context, userID string) (*user.RemovePhoneResponse, error) {
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "other user, no permission",
|
||||
ctx: integration.WithAuthorizationToken(CTX, sessionTokenOtherUser),
|
||||
req: &user.RemovePhoneRequest{
|
||||
UserId: userResp.GetUserId(),
|
||||
},
|
||||
wantErr: true,
|
||||
dep: func(ctx context.Context, userID string) (*user.RemovePhoneResponse, error) {
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, depErr := tt.dep(tt.ctx, tt.req.UserId)
|
||||
require.NoError(t, depErr)
|
||||
|
||||
got, err := Client.RemovePhone(tt.ctx, tt.req)
|
||||
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
986
internal/api/grpc/user/v2beta/integration_test/query_test.go
Normal file
986
internal/api/grpc/user/v2beta/integration_test/query_test.go
Normal file
@@ -0,0 +1,986 @@
|
||||
//go:build integration
|
||||
|
||||
package user_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/muhlemmer/gu"
|
||||
"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"
|
||||
object_v2beta "github.com/zitadel/zitadel/pkg/grpc/object/v2beta"
|
||||
user "github.com/zitadel/zitadel/pkg/grpc/user/v2beta"
|
||||
)
|
||||
|
||||
func detailsV2ToV2beta(obj *object.Details) *object_v2beta.Details {
|
||||
return &object_v2beta.Details{
|
||||
Sequence: obj.GetSequence(),
|
||||
ChangeDate: obj.GetChangeDate(),
|
||||
ResourceOwner: obj.GetResourceOwner(),
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_GetUserByID(t *testing.T) {
|
||||
orgResp := Instance.CreateOrganization(IamCTX, fmt.Sprintf("GetUserByIDOrg%d", time.Now().UnixNano()), fmt.Sprintf("%d@mouse.com", time.Now().UnixNano()))
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *user.GetUserByIDRequest
|
||||
dep func(ctx context.Context, username string, request *user.GetUserByIDRequest) (*userAttr, error)
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *user.GetUserByIDResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "user by ID, no id provided",
|
||||
args: args{
|
||||
IamCTX,
|
||||
&user.GetUserByIDRequest{
|
||||
UserId: "",
|
||||
},
|
||||
func(ctx context.Context, username string, request *user.GetUserByIDRequest) (*userAttr, error) {
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "user by ID, not found",
|
||||
args: args{
|
||||
IamCTX,
|
||||
&user.GetUserByIDRequest{
|
||||
UserId: "unknown",
|
||||
},
|
||||
func(ctx context.Context, username string, request *user.GetUserByIDRequest) (*userAttr, error) {
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "user by ID, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
&user.GetUserByIDRequest{},
|
||||
func(ctx context.Context, username string, request *user.GetUserByIDRequest) (*userAttr, error) {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
request.UserId = resp.GetUserId()
|
||||
return &userAttr{resp.GetUserId(), username, nil, resp.GetDetails()}, nil
|
||||
},
|
||||
},
|
||||
want: &user.GetUserByIDResponse{
|
||||
User: &user.User{
|
||||
State: user.UserState_USER_STATE_ACTIVE,
|
||||
Username: "",
|
||||
LoginNames: nil,
|
||||
PreferredLoginName: "",
|
||||
Type: &user.User_Human{
|
||||
Human: &user.HumanUser{
|
||||
Profile: &user.HumanProfile{
|
||||
GivenName: "Mickey",
|
||||
FamilyName: "Mouse",
|
||||
NickName: gu.Ptr("Mickey"),
|
||||
DisplayName: gu.Ptr("Mickey Mouse"),
|
||||
PreferredLanguage: gu.Ptr("nl"),
|
||||
Gender: user.Gender_GENDER_MALE.Enum(),
|
||||
AvatarUrl: "",
|
||||
},
|
||||
Email: &user.HumanEmail{
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Details: &object_v2beta.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: orgResp.OrganizationId,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "user by ID, passwordChangeRequired, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
&user.GetUserByIDRequest{},
|
||||
func(ctx context.Context, username string, request *user.GetUserByIDRequest) (*userAttr, error) {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
request.UserId = resp.GetUserId()
|
||||
details := Instance.SetUserPassword(ctx, resp.GetUserId(), integration.UserPassword, true)
|
||||
return &userAttr{resp.GetUserId(), username, details.GetChangeDate(), resp.GetDetails()}, nil
|
||||
},
|
||||
},
|
||||
want: &user.GetUserByIDResponse{
|
||||
User: &user.User{
|
||||
State: user.UserState_USER_STATE_ACTIVE,
|
||||
Username: "",
|
||||
LoginNames: nil,
|
||||
PreferredLoginName: "",
|
||||
Type: &user.User_Human{
|
||||
Human: &user.HumanUser{
|
||||
Profile: &user.HumanProfile{
|
||||
GivenName: "Mickey",
|
||||
FamilyName: "Mouse",
|
||||
NickName: gu.Ptr("Mickey"),
|
||||
DisplayName: gu.Ptr("Mickey Mouse"),
|
||||
PreferredLanguage: gu.Ptr("nl"),
|
||||
Gender: user.Gender_GENDER_MALE.Enum(),
|
||||
AvatarUrl: "",
|
||||
},
|
||||
Email: &user.HumanEmail{
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
PasswordChangeRequired: true,
|
||||
PasswordChanged: timestamppb.Now(),
|
||||
},
|
||||
},
|
||||
},
|
||||
Details: &object_v2beta.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: orgResp.OrganizationId,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
username := fmt.Sprintf("%d@mouse.com", time.Now().UnixNano())
|
||||
userAttr, err := tt.args.dep(tt.args.ctx, username, tt.args.req)
|
||||
require.NoError(t, err)
|
||||
retryDuration := time.Minute
|
||||
if ctxDeadline, ok := CTX.Deadline(); ok {
|
||||
retryDuration = time.Until(ctxDeadline)
|
||||
}
|
||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||
got, getErr := Client.GetUserByID(tt.args.ctx, tt.args.req)
|
||||
assertErr := assert.NoError
|
||||
if tt.wantErr {
|
||||
assertErr = assert.Error
|
||||
}
|
||||
assertErr(ttt, getErr)
|
||||
if getErr != nil {
|
||||
return
|
||||
}
|
||||
tt.want.User.Details = detailsV2ToV2beta(userAttr.Details)
|
||||
tt.want.User.UserId = userAttr.UserID
|
||||
tt.want.User.Username = userAttr.Username
|
||||
tt.want.User.PreferredLoginName = userAttr.Username
|
||||
tt.want.User.LoginNames = []string{userAttr.Username}
|
||||
if human := tt.want.User.GetHuman(); human != nil {
|
||||
human.Email.Email = userAttr.Username
|
||||
if tt.want.User.GetHuman().GetPasswordChanged() != nil {
|
||||
human.PasswordChanged = userAttr.Changed
|
||||
}
|
||||
}
|
||||
assert.Equal(ttt, tt.want.User, got.User)
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
}, retryDuration, time.Second)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_GetUserByID_Permission(t *testing.T) {
|
||||
timeNow := time.Now().UTC()
|
||||
newOrgOwnerEmail := fmt.Sprintf("%d@permission.get.com", timeNow.UnixNano())
|
||||
newOrg := Instance.CreateOrganization(IamCTX, fmt.Sprintf("GetHuman%d", time.Now().UnixNano()), newOrgOwnerEmail)
|
||||
newUserID := newOrg.CreatedAdmins[0].GetUserId()
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *user.GetUserByIDRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *user.GetUserByIDResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "System, ok",
|
||||
args: args{
|
||||
SystemCTX,
|
||||
&user.GetUserByIDRequest{
|
||||
UserId: newUserID,
|
||||
},
|
||||
},
|
||||
want: &user.GetUserByIDResponse{
|
||||
User: &user.User{
|
||||
State: user.UserState_USER_STATE_ACTIVE,
|
||||
Username: "",
|
||||
LoginNames: nil,
|
||||
PreferredLoginName: "",
|
||||
Type: &user.User_Human{
|
||||
Human: &user.HumanUser{
|
||||
Profile: &user.HumanProfile{
|
||||
GivenName: "firstname",
|
||||
FamilyName: "lastname",
|
||||
NickName: gu.Ptr(""),
|
||||
DisplayName: gu.Ptr("firstname lastname"),
|
||||
PreferredLanguage: gu.Ptr("und"),
|
||||
Gender: user.Gender_GENDER_UNSPECIFIED.Enum(),
|
||||
AvatarUrl: "",
|
||||
},
|
||||
Email: &user.HumanEmail{
|
||||
Email: newOrgOwnerEmail,
|
||||
},
|
||||
Phone: &user.HumanPhone{},
|
||||
},
|
||||
},
|
||||
},
|
||||
Details: &object_v2beta.Details{
|
||||
ChangeDate: timestamppb.New(timeNow),
|
||||
ResourceOwner: newOrg.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Instance, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
&user.GetUserByIDRequest{
|
||||
UserId: newUserID,
|
||||
},
|
||||
},
|
||||
want: &user.GetUserByIDResponse{
|
||||
User: &user.User{
|
||||
State: user.UserState_USER_STATE_ACTIVE,
|
||||
Username: "",
|
||||
LoginNames: nil,
|
||||
PreferredLoginName: "",
|
||||
Type: &user.User_Human{
|
||||
Human: &user.HumanUser{
|
||||
Profile: &user.HumanProfile{
|
||||
GivenName: "firstname",
|
||||
FamilyName: "lastname",
|
||||
NickName: gu.Ptr(""),
|
||||
DisplayName: gu.Ptr("firstname lastname"),
|
||||
PreferredLanguage: gu.Ptr("und"),
|
||||
Gender: user.Gender_GENDER_UNSPECIFIED.Enum(),
|
||||
AvatarUrl: "",
|
||||
},
|
||||
Email: &user.HumanEmail{
|
||||
Email: newOrgOwnerEmail,
|
||||
},
|
||||
Phone: &user.HumanPhone{},
|
||||
},
|
||||
},
|
||||
},
|
||||
Details: &object_v2beta.Details{
|
||||
ChangeDate: timestamppb.New(timeNow),
|
||||
ResourceOwner: newOrg.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Org, error",
|
||||
args: args{
|
||||
CTX,
|
||||
&user.GetUserByIDRequest{
|
||||
UserId: newUserID,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "User, error",
|
||||
args: args{
|
||||
UserCTX,
|
||||
&user.GetUserByIDRequest{
|
||||
UserId: newUserID,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.GetUserByID(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
tt.want.User.UserId = tt.args.req.GetUserId()
|
||||
tt.want.User.Username = newOrgOwnerEmail
|
||||
tt.want.User.PreferredLoginName = newOrgOwnerEmail
|
||||
tt.want.User.LoginNames = []string{newOrgOwnerEmail}
|
||||
if human := tt.want.User.GetHuman(); human != nil {
|
||||
human.Email.Email = newOrgOwnerEmail
|
||||
}
|
||||
// details tested in GetUserByID
|
||||
tt.want.User.Details = got.User.GetDetails()
|
||||
|
||||
assert.Equal(t, tt.want.User, got.User)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type userAttr struct {
|
||||
UserID string
|
||||
Username string
|
||||
Changed *timestamppb.Timestamp
|
||||
Details *object.Details
|
||||
}
|
||||
|
||||
func TestServer_ListUsers(t *testing.T) {
|
||||
orgResp := Instance.CreateOrganization(IamCTX, fmt.Sprintf("ListUsersOrg%d", time.Now().UnixNano()), fmt.Sprintf("%d@mouse.com", time.Now().UnixNano()))
|
||||
userResp := Instance.CreateHumanUserVerified(IamCTX, orgResp.OrganizationId, fmt.Sprintf("%d@listusers.com", time.Now().UnixNano()))
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
count int
|
||||
req *user.ListUsersRequest
|
||||
dep func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error)
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *user.ListUsersResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "list user by id, no permission",
|
||||
args: args{
|
||||
UserCTX,
|
||||
0,
|
||||
&user.ListUsersRequest{},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
request.Queries = append(request.Queries, InUserIDsQuery([]string{userResp.UserId}))
|
||||
return []userAttr{}, nil
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
Details: &object_v2beta.ListDetails{
|
||||
TotalResult: 0,
|
||||
Timestamp: timestamppb.Now(),
|
||||
},
|
||||
SortingColumn: 0,
|
||||
Result: []*user.User{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list user by id, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
1,
|
||||
&user.ListUsersRequest{
|
||||
Queries: []*user.SearchQuery{
|
||||
OrganizationIdQuery(orgResp.OrganizationId),
|
||||
},
|
||||
},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
infos := make([]userAttr, len(usernames))
|
||||
userIDs := make([]string, len(usernames))
|
||||
for i, username := range usernames {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
userIDs[i] = resp.GetUserId()
|
||||
infos[i] = userAttr{resp.GetUserId(), username, nil, resp.GetDetails()}
|
||||
}
|
||||
request.Queries = append(request.Queries, InUserIDsQuery(userIDs))
|
||||
return infos, nil
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
Details: &object_v2beta.ListDetails{
|
||||
TotalResult: 1,
|
||||
Timestamp: timestamppb.Now(),
|
||||
},
|
||||
SortingColumn: 0,
|
||||
Result: []*user.User{
|
||||
{
|
||||
State: user.UserState_USER_STATE_ACTIVE,
|
||||
Type: &user.User_Human{
|
||||
Human: &user.HumanUser{
|
||||
Profile: &user.HumanProfile{
|
||||
GivenName: "Mickey",
|
||||
FamilyName: "Mouse",
|
||||
NickName: gu.Ptr("Mickey"),
|
||||
DisplayName: gu.Ptr("Mickey Mouse"),
|
||||
PreferredLanguage: gu.Ptr("nl"),
|
||||
Gender: user.Gender_GENDER_MALE.Enum(),
|
||||
},
|
||||
Email: &user.HumanEmail{
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list user by id, passwordChangeRequired, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
1,
|
||||
&user.ListUsersRequest{
|
||||
Queries: []*user.SearchQuery{
|
||||
OrganizationIdQuery(orgResp.OrganizationId),
|
||||
},
|
||||
},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
infos := make([]userAttr, len(usernames))
|
||||
userIDs := make([]string, len(usernames))
|
||||
for i, username := range usernames {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
userIDs[i] = resp.GetUserId()
|
||||
details := Instance.SetUserPassword(ctx, resp.GetUserId(), integration.UserPassword, true)
|
||||
infos[i] = userAttr{resp.GetUserId(), username, details.GetChangeDate(), resp.GetDetails()}
|
||||
}
|
||||
request.Queries = append(request.Queries, InUserIDsQuery(userIDs))
|
||||
return infos, nil
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
Details: &object_v2beta.ListDetails{
|
||||
TotalResult: 1,
|
||||
Timestamp: timestamppb.Now(),
|
||||
},
|
||||
SortingColumn: 0,
|
||||
Result: []*user.User{
|
||||
{
|
||||
State: user.UserState_USER_STATE_ACTIVE,
|
||||
Type: &user.User_Human{
|
||||
Human: &user.HumanUser{
|
||||
Profile: &user.HumanProfile{
|
||||
GivenName: "Mickey",
|
||||
FamilyName: "Mouse",
|
||||
NickName: gu.Ptr("Mickey"),
|
||||
DisplayName: gu.Ptr("Mickey Mouse"),
|
||||
PreferredLanguage: gu.Ptr("nl"),
|
||||
Gender: user.Gender_GENDER_MALE.Enum(),
|
||||
},
|
||||
Email: &user.HumanEmail{
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
PasswordChangeRequired: true,
|
||||
PasswordChanged: timestamppb.Now(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list user by id multiple, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
3,
|
||||
&user.ListUsersRequest{
|
||||
Queries: []*user.SearchQuery{
|
||||
OrganizationIdQuery(orgResp.OrganizationId),
|
||||
},
|
||||
},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
infos := make([]userAttr, len(usernames))
|
||||
userIDs := make([]string, len(usernames))
|
||||
for i, username := range usernames {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
userIDs[i] = resp.GetUserId()
|
||||
infos[i] = userAttr{resp.GetUserId(), username, nil, resp.GetDetails()}
|
||||
}
|
||||
request.Queries = append(request.Queries, InUserIDsQuery(userIDs))
|
||||
return infos, nil
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
Details: &object_v2beta.ListDetails{
|
||||
TotalResult: 3,
|
||||
Timestamp: timestamppb.Now(),
|
||||
},
|
||||
SortingColumn: 0,
|
||||
Result: []*user.User{
|
||||
{
|
||||
State: user.UserState_USER_STATE_ACTIVE,
|
||||
Type: &user.User_Human{
|
||||
Human: &user.HumanUser{
|
||||
Profile: &user.HumanProfile{
|
||||
GivenName: "Mickey",
|
||||
FamilyName: "Mouse",
|
||||
NickName: gu.Ptr("Mickey"),
|
||||
DisplayName: gu.Ptr("Mickey Mouse"),
|
||||
PreferredLanguage: gu.Ptr("nl"),
|
||||
Gender: user.Gender_GENDER_MALE.Enum(),
|
||||
},
|
||||
Email: &user.HumanEmail{
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
State: user.UserState_USER_STATE_ACTIVE,
|
||||
Type: &user.User_Human{
|
||||
Human: &user.HumanUser{
|
||||
Profile: &user.HumanProfile{
|
||||
GivenName: "Mickey",
|
||||
FamilyName: "Mouse",
|
||||
NickName: gu.Ptr("Mickey"),
|
||||
DisplayName: gu.Ptr("Mickey Mouse"),
|
||||
PreferredLanguage: gu.Ptr("nl"),
|
||||
Gender: user.Gender_GENDER_MALE.Enum(),
|
||||
},
|
||||
Email: &user.HumanEmail{
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
State: user.UserState_USER_STATE_ACTIVE,
|
||||
Type: &user.User_Human{
|
||||
Human: &user.HumanUser{
|
||||
Profile: &user.HumanProfile{
|
||||
GivenName: "Mickey",
|
||||
FamilyName: "Mouse",
|
||||
NickName: gu.Ptr("Mickey"),
|
||||
DisplayName: gu.Ptr("Mickey Mouse"),
|
||||
PreferredLanguage: gu.Ptr("nl"),
|
||||
Gender: user.Gender_GENDER_MALE.Enum(),
|
||||
},
|
||||
Email: &user.HumanEmail{
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list user by username, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
1,
|
||||
&user.ListUsersRequest{
|
||||
Queries: []*user.SearchQuery{
|
||||
OrganizationIdQuery(orgResp.OrganizationId),
|
||||
},
|
||||
},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
infos := make([]userAttr, len(usernames))
|
||||
userIDs := make([]string, len(usernames))
|
||||
for i, username := range usernames {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
userIDs[i] = resp.GetUserId()
|
||||
infos[i] = userAttr{resp.GetUserId(), username, nil, resp.GetDetails()}
|
||||
request.Queries = append(request.Queries, UsernameQuery(username))
|
||||
}
|
||||
return infos, nil
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
Details: &object_v2beta.ListDetails{
|
||||
TotalResult: 1,
|
||||
Timestamp: timestamppb.Now(),
|
||||
},
|
||||
SortingColumn: 0,
|
||||
Result: []*user.User{
|
||||
{
|
||||
State: user.UserState_USER_STATE_ACTIVE,
|
||||
Type: &user.User_Human{
|
||||
Human: &user.HumanUser{
|
||||
Profile: &user.HumanProfile{
|
||||
GivenName: "Mickey",
|
||||
FamilyName: "Mouse",
|
||||
NickName: gu.Ptr("Mickey"),
|
||||
DisplayName: gu.Ptr("Mickey Mouse"),
|
||||
PreferredLanguage: gu.Ptr("nl"),
|
||||
Gender: user.Gender_GENDER_MALE.Enum(),
|
||||
},
|
||||
Email: &user.HumanEmail{
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list user in emails, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
1,
|
||||
&user.ListUsersRequest{
|
||||
Queries: []*user.SearchQuery{
|
||||
OrganizationIdQuery(orgResp.OrganizationId),
|
||||
},
|
||||
},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
infos := make([]userAttr, len(usernames))
|
||||
for i, username := range usernames {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
infos[i] = userAttr{resp.GetUserId(), username, nil, resp.GetDetails()}
|
||||
}
|
||||
request.Queries = append(request.Queries, InUserEmailsQuery(usernames))
|
||||
return infos, nil
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
Details: &object_v2beta.ListDetails{
|
||||
TotalResult: 1,
|
||||
Timestamp: timestamppb.Now(),
|
||||
},
|
||||
SortingColumn: 0,
|
||||
Result: []*user.User{
|
||||
{
|
||||
State: user.UserState_USER_STATE_ACTIVE,
|
||||
Type: &user.User_Human{
|
||||
Human: &user.HumanUser{
|
||||
Profile: &user.HumanProfile{
|
||||
GivenName: "Mickey",
|
||||
FamilyName: "Mouse",
|
||||
NickName: gu.Ptr("Mickey"),
|
||||
DisplayName: gu.Ptr("Mickey Mouse"),
|
||||
PreferredLanguage: gu.Ptr("nl"),
|
||||
Gender: user.Gender_GENDER_MALE.Enum(),
|
||||
},
|
||||
Email: &user.HumanEmail{
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list user in emails multiple, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
3,
|
||||
&user.ListUsersRequest{
|
||||
Queries: []*user.SearchQuery{
|
||||
OrganizationIdQuery(orgResp.OrganizationId),
|
||||
},
|
||||
},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
infos := make([]userAttr, len(usernames))
|
||||
for i, username := range usernames {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
infos[i] = userAttr{resp.GetUserId(), username, nil, resp.GetDetails()}
|
||||
}
|
||||
request.Queries = append(request.Queries, InUserEmailsQuery(usernames))
|
||||
return infos, nil
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
Details: &object_v2beta.ListDetails{
|
||||
TotalResult: 3,
|
||||
Timestamp: timestamppb.Now(),
|
||||
},
|
||||
SortingColumn: 0,
|
||||
Result: []*user.User{
|
||||
{
|
||||
State: user.UserState_USER_STATE_ACTIVE,
|
||||
Type: &user.User_Human{
|
||||
Human: &user.HumanUser{
|
||||
Profile: &user.HumanProfile{
|
||||
GivenName: "Mickey",
|
||||
FamilyName: "Mouse",
|
||||
NickName: gu.Ptr("Mickey"),
|
||||
DisplayName: gu.Ptr("Mickey Mouse"),
|
||||
PreferredLanguage: gu.Ptr("nl"),
|
||||
Gender: user.Gender_GENDER_MALE.Enum(),
|
||||
},
|
||||
Email: &user.HumanEmail{
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
State: user.UserState_USER_STATE_ACTIVE,
|
||||
Type: &user.User_Human{
|
||||
Human: &user.HumanUser{
|
||||
Profile: &user.HumanProfile{
|
||||
GivenName: "Mickey",
|
||||
FamilyName: "Mouse",
|
||||
NickName: gu.Ptr("Mickey"),
|
||||
DisplayName: gu.Ptr("Mickey Mouse"),
|
||||
PreferredLanguage: gu.Ptr("nl"),
|
||||
Gender: user.Gender_GENDER_MALE.Enum(),
|
||||
},
|
||||
Email: &user.HumanEmail{
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
State: user.UserState_USER_STATE_ACTIVE,
|
||||
Type: &user.User_Human{
|
||||
Human: &user.HumanUser{
|
||||
Profile: &user.HumanProfile{
|
||||
GivenName: "Mickey",
|
||||
FamilyName: "Mouse",
|
||||
NickName: gu.Ptr("Mickey"),
|
||||
DisplayName: gu.Ptr("Mickey Mouse"),
|
||||
PreferredLanguage: gu.Ptr("nl"),
|
||||
Gender: user.Gender_GENDER_MALE.Enum(),
|
||||
},
|
||||
Email: &user.HumanEmail{
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list user in emails no found, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
3,
|
||||
&user.ListUsersRequest{Queries: []*user.SearchQuery{
|
||||
OrganizationIdQuery(orgResp.OrganizationId),
|
||||
InUserEmailsQuery([]string{"notfound"}),
|
||||
},
|
||||
},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
return []userAttr{}, nil
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
Details: &object_v2beta.ListDetails{
|
||||
TotalResult: 0,
|
||||
Timestamp: timestamppb.Now(),
|
||||
},
|
||||
SortingColumn: 0,
|
||||
Result: []*user.User{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list user resourceowner multiple, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
3,
|
||||
&user.ListUsersRequest{},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
orgResp := Instance.CreateOrganization(ctx, fmt.Sprintf("ListUsersResourceowner%d", time.Now().UnixNano()), fmt.Sprintf("%d@mouse.com", time.Now().UnixNano()))
|
||||
|
||||
infos := make([]userAttr, len(usernames))
|
||||
for i, username := range usernames {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
infos[i] = userAttr{resp.GetUserId(), username, nil, resp.GetDetails()}
|
||||
}
|
||||
request.Queries = append(request.Queries, OrganizationIdQuery(orgResp.OrganizationId))
|
||||
request.Queries = append(request.Queries, InUserEmailsQuery(usernames))
|
||||
return infos, nil
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
Details: &object_v2beta.ListDetails{
|
||||
TotalResult: 3,
|
||||
Timestamp: timestamppb.Now(),
|
||||
},
|
||||
SortingColumn: 0,
|
||||
Result: []*user.User{
|
||||
{
|
||||
State: user.UserState_USER_STATE_ACTIVE,
|
||||
Type: &user.User_Human{
|
||||
Human: &user.HumanUser{
|
||||
Profile: &user.HumanProfile{
|
||||
GivenName: "Mickey",
|
||||
FamilyName: "Mouse",
|
||||
NickName: gu.Ptr("Mickey"),
|
||||
DisplayName: gu.Ptr("Mickey Mouse"),
|
||||
PreferredLanguage: gu.Ptr("nl"),
|
||||
Gender: user.Gender_GENDER_MALE.Enum(),
|
||||
},
|
||||
Email: &user.HumanEmail{
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
State: user.UserState_USER_STATE_ACTIVE,
|
||||
Type: &user.User_Human{
|
||||
Human: &user.HumanUser{
|
||||
Profile: &user.HumanProfile{
|
||||
GivenName: "Mickey",
|
||||
FamilyName: "Mouse",
|
||||
NickName: gu.Ptr("Mickey"),
|
||||
DisplayName: gu.Ptr("Mickey Mouse"),
|
||||
PreferredLanguage: gu.Ptr("nl"),
|
||||
Gender: user.Gender_GENDER_MALE.Enum(),
|
||||
},
|
||||
Email: &user.HumanEmail{
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
State: user.UserState_USER_STATE_ACTIVE,
|
||||
Type: &user.User_Human{
|
||||
Human: &user.HumanUser{
|
||||
Profile: &user.HumanProfile{
|
||||
GivenName: "Mickey",
|
||||
FamilyName: "Mouse",
|
||||
NickName: gu.Ptr("Mickey"),
|
||||
DisplayName: gu.Ptr("Mickey Mouse"),
|
||||
PreferredLanguage: gu.Ptr("nl"),
|
||||
Gender: user.Gender_GENDER_MALE.Enum(),
|
||||
},
|
||||
Email: &user.HumanEmail{
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
usernames := make([]string, tt.args.count)
|
||||
for i := 0; i < tt.args.count; i++ {
|
||||
usernames[i] = fmt.Sprintf("%d%d@mouse.com", time.Now().UnixNano(), i)
|
||||
}
|
||||
infos, err := tt.args.dep(tt.args.ctx, usernames, tt.args.req)
|
||||
require.NoError(t, err)
|
||||
retryDuration := time.Minute
|
||||
if ctxDeadline, ok := CTX.Deadline(); ok {
|
||||
retryDuration = time.Until(ctxDeadline)
|
||||
}
|
||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||
got, listErr := Client.ListUsers(tt.args.ctx, tt.args.req)
|
||||
assertErr := assert.NoError
|
||||
if tt.wantErr {
|
||||
assertErr = assert.Error
|
||||
}
|
||||
assertErr(ttt, listErr)
|
||||
if listErr != nil {
|
||||
return
|
||||
}
|
||||
// always only give back dependency infos which are required for the response
|
||||
assert.Len(ttt, tt.want.Result, len(infos))
|
||||
// always first check length, otherwise its failed anyway
|
||||
assert.Len(ttt, got.Result, len(tt.want.Result))
|
||||
// fill in userid and username as it is generated
|
||||
|
||||
// totalResult is unrelated to the tests here so gets carried over, can vary from the count of results due to permissions
|
||||
tt.want.Details.TotalResult = got.Details.TotalResult
|
||||
|
||||
for i := range infos {
|
||||
tt.want.Result[i].UserId = infos[i].UserID
|
||||
tt.want.Result[i].Username = infos[i].Username
|
||||
tt.want.Result[i].PreferredLoginName = infos[i].Username
|
||||
tt.want.Result[i].LoginNames = []string{infos[i].Username}
|
||||
if human := tt.want.Result[i].GetHuman(); human != nil {
|
||||
human.Email.Email = infos[i].Username
|
||||
if tt.want.Result[i].GetHuman().GetPasswordChanged() != nil {
|
||||
human.PasswordChanged = infos[i].Changed
|
||||
}
|
||||
}
|
||||
tt.want.Result[i].Details = detailsV2ToV2beta(infos[i].Details)
|
||||
}
|
||||
for i := range tt.want.Result {
|
||||
assert.Contains(ttt, got.Result, tt.want.Result[i])
|
||||
}
|
||||
integration.AssertListDetails(t, tt.want, got)
|
||||
}, retryDuration, time.Millisecond*100, "timeout waiting for expected user result")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func InUserIDsQuery(ids []string) *user.SearchQuery {
|
||||
return &user.SearchQuery{Query: &user.SearchQuery_InUserIdsQuery{
|
||||
InUserIdsQuery: &user.InUserIDQuery{
|
||||
UserIds: ids,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func InUserEmailsQuery(emails []string) *user.SearchQuery {
|
||||
return &user.SearchQuery{Query: &user.SearchQuery_InUserEmailsQuery{
|
||||
InUserEmailsQuery: &user.InUserEmailsQuery{
|
||||
UserEmails: emails,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func UsernameQuery(username string) *user.SearchQuery {
|
||||
return &user.SearchQuery{Query: &user.SearchQuery_UserNameQuery{
|
||||
UserNameQuery: &user.UserNameQuery{
|
||||
UserName: username,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func OrganizationIdQuery(resourceowner string) *user.SearchQuery {
|
||||
return &user.SearchQuery{Query: &user.SearchQuery_OrganizationIdQuery{
|
||||
OrganizationIdQuery: &user.OrganizationIdQuery{
|
||||
OrganizationId: resourceowner,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
289
internal/api/grpc/user/v2beta/integration_test/totp_test.go
Normal file
289
internal/api/grpc/user/v2beta/integration_test/totp_test.go
Normal file
@@ -0,0 +1,289 @@
|
||||
//go:build integration
|
||||
|
||||
package user_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pquerna/otp/totp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
object "github.com/zitadel/zitadel/pkg/grpc/object/v2beta"
|
||||
user "github.com/zitadel/zitadel/pkg/grpc/user/v2beta"
|
||||
)
|
||||
|
||||
func TestServer_RegisterTOTP(t *testing.T) {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
Instance.RegisterUserPasskey(CTX, userID)
|
||||
_, sessionToken, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userID)
|
||||
ctx := integration.WithAuthorizationToken(CTX, sessionToken)
|
||||
|
||||
otherUser := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
Instance.RegisterUserPasskey(CTX, otherUser)
|
||||
_, sessionTokenOtherUser, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, otherUser)
|
||||
ctxOtherUser := integration.WithAuthorizationToken(CTX, sessionTokenOtherUser)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *user.RegisterTOTPRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *user.RegisterTOTPResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "missing user id",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
req: &user.RegisterTOTPRequest{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "user mismatch",
|
||||
args: args{
|
||||
ctx: ctxOtherUser,
|
||||
req: &user.RegisterTOTPRequest{
|
||||
UserId: userID,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "admin",
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
req: &user.RegisterTOTPRequest{
|
||||
UserId: userID,
|
||||
},
|
||||
},
|
||||
want: &user.RegisterTOTPResponse{
|
||||
Details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
req: &user.RegisterTOTPRequest{
|
||||
UserId: userID,
|
||||
},
|
||||
},
|
||||
want: &user.RegisterTOTPResponse{
|
||||
Details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.RegisterTOTP(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
assert.NotEmpty(t, got.GetUri())
|
||||
assert.NotEmpty(t, got.GetSecret())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_VerifyTOTPRegistration(t *testing.T) {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
Instance.RegisterUserPasskey(CTX, userID)
|
||||
_, sessionToken, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userID)
|
||||
ctx := integration.WithAuthorizationToken(CTX, sessionToken)
|
||||
|
||||
var reg *user.RegisterTOTPResponse
|
||||
assert.EventuallyWithT(t, func(ct *assert.CollectT) {
|
||||
var err error
|
||||
reg, err = Client.RegisterTOTP(ctx, &user.RegisterTOTPRequest{
|
||||
UserId: userID,
|
||||
})
|
||||
assert.NoError(ct, err)
|
||||
}, time.Minute, time.Second/10)
|
||||
|
||||
code, err := totp.GenerateCode(reg.Secret, time.Now())
|
||||
require.NoError(t, err)
|
||||
|
||||
otherUser := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
Instance.RegisterUserPasskey(CTX, otherUser)
|
||||
_, sessionTokenOtherUser, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, otherUser)
|
||||
ctxOtherUser := integration.WithAuthorizationToken(CTX, sessionTokenOtherUser)
|
||||
|
||||
regOtherUser, err := Client.RegisterTOTP(CTX, &user.RegisterTOTPRequest{
|
||||
UserId: otherUser,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
codeOtherUser, err := totp.GenerateCode(regOtherUser.Secret, time.Now())
|
||||
require.NoError(t, err)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *user.VerifyTOTPRegistrationRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *user.VerifyTOTPRegistrationResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "user mismatch",
|
||||
args: args{
|
||||
ctx: ctxOtherUser,
|
||||
req: &user.VerifyTOTPRegistrationRequest{
|
||||
UserId: userID,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "wrong code",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
req: &user.VerifyTOTPRegistrationRequest{
|
||||
UserId: userID,
|
||||
Code: "123",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "success",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
req: &user.VerifyTOTPRegistrationRequest{
|
||||
UserId: userID,
|
||||
Code: code,
|
||||
},
|
||||
},
|
||||
want: &user.VerifyTOTPRegistrationResponse{
|
||||
Details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Details.ResourceOwner,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success, admin",
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
req: &user.VerifyTOTPRegistrationRequest{
|
||||
UserId: otherUser,
|
||||
Code: codeOtherUser,
|
||||
},
|
||||
},
|
||||
want: &user.VerifyTOTPRegistrationResponse{
|
||||
Details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Details.ResourceOwner,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.VerifyTOTPRegistration(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_RemoveTOTP(t *testing.T) {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
Instance.RegisterUserPasskey(CTX, userID)
|
||||
_, sessionToken, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userID)
|
||||
|
||||
userVerified := Instance.CreateHumanUser(CTX)
|
||||
Instance.RegisterUserPasskey(CTX, userVerified.GetUserId())
|
||||
_, sessionTokenVerified, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userVerified.GetUserId())
|
||||
userVerifiedCtx := integration.WithAuthorizationToken(context.Background(), sessionTokenVerified)
|
||||
_, err := Client.VerifyPhone(userVerifiedCtx, &user.VerifyPhoneRequest{
|
||||
UserId: userVerified.GetUserId(),
|
||||
VerificationCode: userVerified.GetPhoneCode(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
regOtherUser, err := Client.RegisterTOTP(CTX, &user.RegisterTOTPRequest{
|
||||
UserId: userVerified.GetUserId(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
codeOtherUser, err := totp.GenerateCode(regOtherUser.Secret, time.Now())
|
||||
require.NoError(t, err)
|
||||
_, err = Client.VerifyTOTPRegistration(userVerifiedCtx, &user.VerifyTOTPRegistrationRequest{
|
||||
UserId: userVerified.GetUserId(),
|
||||
Code: codeOtherUser,
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *user.RemoveTOTPRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *user.RemoveTOTPResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "not added",
|
||||
args: args{
|
||||
ctx: integration.WithAuthorizationToken(context.Background(), sessionToken),
|
||||
req: &user.RemoveTOTPRequest{
|
||||
UserId: userID,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "success",
|
||||
args: args{
|
||||
ctx: userVerifiedCtx,
|
||||
req: &user.RemoveTOTPRequest{
|
||||
UserId: userVerified.GetUserId(),
|
||||
},
|
||||
},
|
||||
want: &user.RemoveTOTPResponse{
|
||||
Details: &object.Details{
|
||||
ResourceOwner: Instance.DefaultOrg.Details.ResourceOwner,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.RemoveTOTP(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
190
internal/api/grpc/user/v2beta/integration_test/u2f_test.go
Normal file
190
internal/api/grpc/user/v2beta/integration_test/u2f_test.go
Normal file
@@ -0,0 +1,190 @@
|
||||
//go:build integration
|
||||
|
||||
package user_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
object "github.com/zitadel/zitadel/pkg/grpc/object/v2beta"
|
||||
user "github.com/zitadel/zitadel/pkg/grpc/user/v2beta"
|
||||
)
|
||||
|
||||
func TestServer_RegisterU2F(t *testing.T) {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
otherUser := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
|
||||
// We also need a user session
|
||||
Instance.RegisterUserPasskey(CTX, userID)
|
||||
_, sessionToken, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userID)
|
||||
Instance.RegisterUserPasskey(CTX, otherUser)
|
||||
_, sessionTokenOtherUser, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, otherUser)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *user.RegisterU2FRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *user.RegisterU2FResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "missing user id",
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
req: &user.RegisterU2FRequest{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "admin user",
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
req: &user.RegisterU2FRequest{
|
||||
UserId: userID,
|
||||
},
|
||||
},
|
||||
want: &user.RegisterU2FResponse{
|
||||
Details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "other user, no permission",
|
||||
args: args{
|
||||
ctx: integration.WithAuthorizationToken(CTX, sessionTokenOtherUser),
|
||||
req: &user.RegisterU2FRequest{
|
||||
UserId: userID,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "user setting its own passkey",
|
||||
args: args{
|
||||
ctx: integration.WithAuthorizationToken(CTX, sessionToken),
|
||||
req: &user.RegisterU2FRequest{
|
||||
UserId: userID,
|
||||
},
|
||||
},
|
||||
want: &user.RegisterU2FResponse{
|
||||
Details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.RegisterU2F(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
if tt.want != nil {
|
||||
assert.NotEmpty(t, got.GetU2FId())
|
||||
assert.NotEmpty(t, got.GetPublicKeyCredentialCreationOptions())
|
||||
_, err = Instance.WebAuthN.CreateAttestationResponse(got.GetPublicKeyCredentialCreationOptions())
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_VerifyU2FRegistration(t *testing.T) {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
Instance.RegisterUserPasskey(CTX, userID)
|
||||
_, sessionToken, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userID)
|
||||
ctx := integration.WithAuthorizationToken(CTX, sessionToken)
|
||||
|
||||
pkr, err := Client.RegisterU2F(ctx, &user.RegisterU2FRequest{
|
||||
UserId: userID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, pkr.GetPublicKeyCredentialCreationOptions())
|
||||
|
||||
attestationResponse, err := Instance.WebAuthN.CreateAttestationResponse(pkr.GetPublicKeyCredentialCreationOptions())
|
||||
require.NoError(t, err)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *user.VerifyU2FRegistrationRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *user.VerifyU2FRegistrationResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "missing user id",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
req: &user.VerifyU2FRegistrationRequest{
|
||||
U2FId: "123",
|
||||
TokenName: "nice name",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "success",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
req: &user.VerifyU2FRegistrationRequest{
|
||||
UserId: userID,
|
||||
U2FId: pkr.GetU2FId(),
|
||||
PublicKeyCredential: attestationResponse,
|
||||
TokenName: "nice name",
|
||||
},
|
||||
},
|
||||
want: &user.VerifyU2FRegistrationResponse{
|
||||
Details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "wrong credential",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
req: &user.VerifyU2FRegistrationRequest{
|
||||
UserId: userID,
|
||||
U2FId: "123",
|
||||
PublicKeyCredential: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{"foo": {Kind: &structpb.Value_StringValue{StringValue: "bar"}}},
|
||||
},
|
||||
TokenName: "nice name",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.VerifyU2FRegistration(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
2519
internal/api/grpc/user/v2beta/integration_test/user_test.go
Normal file
2519
internal/api/grpc/user/v2beta/integration_test/user_test.go
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user