mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-18 13:52:51 +00:00
* fix: block Login V2 auth for users in deactivated organizations Enforce organization state on session creation, OIDC token issuance/refresh, and SAML session creation so deactivated org users cannot authenticate. Co-authored-by: Cursor <[email protected]> * fix: reject claim serving for users in deactivated organizations Require an active resource-owner org in OIDC userinfo and SAML attribute paths, and use distinct error IDs for inactive user vs inactive org at token issuance. Co-authored-by: Cursor <[email protected]> * fix: drop sessions and tokens when an organization is deactivated Mirror OrgRemoved cleanup for OrgDeactivated in the V2 session projection and V1 auth user_session, token, and refresh_token handlers. Also delete V2 sessions on OrgRemoved, which does not emit per-user removal events. Co-authored-by: Cursor <[email protected]> * fix: reject API tokens from deactivated organizations Check the caller's resource-owner org state in authz middleware via a cached OrgByID lookup, so already-issued tokens lose ZITADEL API access when their organization is deactivated. Return unauthenticated (401). Target org remains unrestricted so instance admins can still manage deactivated orgs. Co-authored-by: Cursor <[email protected]> * fix: invalidate OIDC refresh tokens after org deactivation Reject refresh exchange if the user's organization was deactivated after the refresh token was issued, so grants stay dead after reactivate. Co-authored-by: Cursor <[email protected]> * fix: invalidate V2 access tokens after org deactivation Treat OrgDeactivated after the token position as session termination in ActiveAccessTokenByToken, so issued ATs stay dead after reactivation. Co-authored-by: Cursor <[email protected]> --------- Co-authored-by: Cursor <[email protected]>
149 lines
3.4 KiB
Go
149 lines
3.4 KiB
Go
package command
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/text/language"
|
|
|
|
"github.com/zitadel/zitadel/internal/domain"
|
|
"github.com/zitadel/zitadel/internal/eventstore"
|
|
"github.com/zitadel/zitadel/internal/repository/org"
|
|
"github.com/zitadel/zitadel/internal/repository/user"
|
|
"github.com/zitadel/zitadel/internal/zerrors"
|
|
)
|
|
|
|
func TestCommands_userStateForAuthentication(t *testing.T) {
|
|
type args struct {
|
|
ctx context.Context
|
|
userID string
|
|
resourceOwner string
|
|
userErrorID string
|
|
orgErrorID string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
eventstore func(*testing.T) *eventstore.Eventstore
|
|
args args
|
|
wantErr func(error) bool
|
|
}{
|
|
{
|
|
name: "user not active",
|
|
eventstore: expectEventstore(
|
|
expectFilter(
|
|
user.NewHumanAddedEvent(
|
|
context.Background(),
|
|
&user.NewAggregate("userID", "org1").Aggregate,
|
|
"username",
|
|
"firstname",
|
|
"lastname",
|
|
"nickname",
|
|
"displayname",
|
|
language.English,
|
|
domain.GenderUnspecified,
|
|
"email",
|
|
false,
|
|
),
|
|
user.NewUserDeactivatedEvent(
|
|
context.Background(),
|
|
&user.NewAggregate("userID", "org1").Aggregate,
|
|
),
|
|
),
|
|
),
|
|
args: args{
|
|
ctx: context.Background(),
|
|
userID: "userID",
|
|
resourceOwner: "org1",
|
|
userErrorID: "TEST-1",
|
|
orgErrorID: "TEST-1-ORG",
|
|
},
|
|
wantErr: zerrors.IsPreconditionFailed,
|
|
},
|
|
{
|
|
name: "org not active",
|
|
eventstore: expectEventstore(
|
|
expectFilter(
|
|
user.NewHumanAddedEvent(
|
|
context.Background(),
|
|
&user.NewAggregate("userID", "org1").Aggregate,
|
|
"username",
|
|
"firstname",
|
|
"lastname",
|
|
"nickname",
|
|
"displayname",
|
|
language.English,
|
|
domain.GenderUnspecified,
|
|
"email",
|
|
false,
|
|
),
|
|
),
|
|
expectFilter(
|
|
org.NewOrgAddedEvent(context.Background(),
|
|
&org.NewAggregate("org1").Aggregate,
|
|
"org"),
|
|
org.NewOrgDeactivatedEvent(context.Background(),
|
|
&org.NewAggregate("org1").Aggregate),
|
|
),
|
|
),
|
|
args: args{
|
|
ctx: context.Background(),
|
|
userID: "userID",
|
|
resourceOwner: "org1",
|
|
userErrorID: "TEST-2",
|
|
orgErrorID: "TEST-2-ORG",
|
|
},
|
|
wantErr: zerrors.IsPreconditionFailed,
|
|
},
|
|
{
|
|
name: "success",
|
|
eventstore: expectEventstore(
|
|
expectFilter(
|
|
user.NewHumanAddedEvent(
|
|
context.Background(),
|
|
&user.NewAggregate("userID", "org1").Aggregate,
|
|
"username",
|
|
"firstname",
|
|
"lastname",
|
|
"nickname",
|
|
"displayname",
|
|
language.English,
|
|
domain.GenderUnspecified,
|
|
"email",
|
|
false,
|
|
),
|
|
),
|
|
expectFilter(
|
|
org.NewOrgAddedEvent(context.Background(),
|
|
&org.NewAggregate("org1").Aggregate,
|
|
"org"),
|
|
),
|
|
),
|
|
args: args{
|
|
ctx: context.Background(),
|
|
userID: "userID",
|
|
resourceOwner: "org1",
|
|
userErrorID: "TEST-3",
|
|
orgErrorID: "TEST-3-ORG",
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
c := &Commands{
|
|
eventstore: tt.eventstore(t),
|
|
}
|
|
got, err := c.userStateForAuthentication(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.userErrorID, tt.args.orgErrorID)
|
|
if tt.wantErr != nil {
|
|
require.Error(t, err)
|
|
require.True(t, tt.wantErr(err))
|
|
require.Nil(t, got)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
require.NotNil(t, got)
|
|
require.Equal(t, "userID", got.AggregateID)
|
|
})
|
|
}
|
|
}
|