fix(login): changed permission check for sending invite code on log in (#10197)

# Which Problems Are Solved

Fixes issue when users would get an error message when attempting to
resend invitation code when logging in

# How the Problems Are Solved

Changing the permission check for looking for `org.write` to
`ommand.checkPermissionUpdateUser()`

# Additional Context

- Closes https://github.com/zitadel/zitadel/issues/10100
- backport to 3.x
This commit is contained in:
Iraq
2025-07-14 09:19:50 +02:00
committed by GitHub
parent 1b01fc6c40
commit 23d6d24bc8
2 changed files with 135 additions and 2 deletions

View File

@@ -6,6 +6,7 @@ import (
"github.com/zitadel/logging" "github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore"
@@ -50,9 +51,11 @@ func (c *Commands) sendInviteCode(ctx context.Context, invite *CreateUserInvite,
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
if wm.AggregateID != authz.GetCtxData(ctx).UserID {
if err := c.checkPermission(ctx, domain.PermissionUserWrite, wm.ResourceOwner, wm.AggregateID); err != nil { if err := c.checkPermission(ctx, domain.PermissionUserWrite, wm.ResourceOwner, wm.AggregateID); err != nil {
return nil, nil, err return nil, nil, err
} }
}
if !wm.UserState.Exists() { if !wm.UserState.Exists() {
return nil, nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-Wgvn4", "Errors.User.NotFound") return nil, nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-Wgvn4", "Errors.User.NotFound")
} }

View File

@@ -11,6 +11,7 @@ import (
"go.uber.org/mock/gomock" "go.uber.org/mock/gomock"
"golang.org/x/text/language" "golang.org/x/text/language"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore"
@@ -205,6 +206,64 @@ func TestCommands_CreateInviteCode(t *testing.T) {
returnCode: gu.Ptr("code"), returnCode: gu.Ptr("code"),
}, },
}, },
{
"return ok, with same user requests code",
fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("userID", "org1").Aggregate,
"username", "firstName",
"lastName",
"nickName",
"displayName",
language.Afrikaans,
domain.GenderUnspecified,
"email",
false,
),
),
),
expectPush(
eventFromEventPusher(
user.NewHumanInviteCodeAddedEvent(authz.SetCtxData(context.Background(), authz.CtxData{UserID: "userID"}),
&user.NewAggregate("userID", "org1").Aggregate,
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "enc",
KeyID: "id",
Crypted: []byte("code"),
},
time.Hour,
"",
true,
"",
"",
),
),
),
),
// we do not run checkPermission() because the same user is requesting the code as the user to which the code is intended for
checkPermission: nil,
newEncryptedCodeWithDefault: mockEncryptedCodeWithDefault("code", time.Hour),
defaultSecretGenerators: &SecretGenerators{},
},
args{
ctx: authz.SetCtxData(context.Background(), authz.CtxData{UserID: "userID"}),
invite: &CreateUserInvite{
UserID: "userID",
ReturnCode: true,
},
},
want{
details: &domain.ObjectDetails{
ResourceOwner: "org1",
ID: "userID",
},
returnCode: gu.Ptr("code"),
},
},
{ {
"with template and application name ok", "with template and application name ok",
fields{ fields{
@@ -510,6 +569,77 @@ func TestCommands_ResendInviteCode(t *testing.T) {
}, },
}, },
}, },
{
"return ok, with same user requests code",
fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("userID", "org1").Aggregate,
"username", "firstName",
"lastName",
"nickName",
"displayName",
language.Afrikaans,
domain.GenderUnspecified,
"email",
false,
),
),
eventFromEventPusher(
user.NewHumanInviteCodeAddedEvent(context.Background(),
&user.NewAggregate("userID", "org1").Aggregate,
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "enc",
KeyID: "id",
Crypted: []byte("code"),
},
time.Hour,
"",
false,
"",
"authRequestID",
),
),
),
expectPush(
eventFromEventPusher(
user.NewHumanInviteCodeAddedEvent(authz.SetCtxData(context.Background(), authz.CtxData{UserID: "userID"}),
&user.NewAggregate("userID", "org1").Aggregate,
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "enc",
KeyID: "id",
Crypted: []byte("code"),
},
time.Hour,
"",
false,
"",
"authRequestID",
),
),
),
),
// we do not run checkPermission() because the same user is requesting the code as the user to which the code is intended for
checkPermission: nil,
newEncryptedCodeWithDefault: mockEncryptedCodeWithDefault("code", time.Hour),
defaultSecretGenerators: &SecretGenerators{},
},
args{
// ctx: context.Background(),
ctx: authz.SetCtxData(context.Background(), authz.CtxData{UserID: "userID"}),
userID: "userID",
},
want{
details: &domain.ObjectDetails{
ResourceOwner: "org1",
ID: "userID",
},
},
},
{ {
"resend with new auth requestID ok", "resend with new auth requestID ok",
fields{ fields{