feat: allow session deletion without session token (#6889)

* fix: add resource owner of user and change the one of session to instance

* use user resource owner from session projection

* fix session permission check

* integration tests and fixes

* update api docs
This commit is contained in:
Livio Spring 2023-11-16 08:35:50 +02:00 committed by GitHub
parent 0948a0b9ae
commit 2e8c3b5a53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 448 additions and 301 deletions

View File

@ -953,6 +953,7 @@ InternalAuthZ:
- "project.grant.member.delete" - "project.grant.member.delete"
- "events.read" - "events.read"
- "milestones.read" - "milestones.read"
- "session.delete"
- Role: "IAM_OWNER_VIEWER" - Role: "IAM_OWNER_VIEWER"
Permissions: Permissions:
- "iam.read" - "iam.read"
@ -1030,6 +1031,7 @@ InternalAuthZ:
- "project.grant.member.read" - "project.grant.member.read"
- "project.grant.member.write" - "project.grant.member.write"
- "project.grant.member.delete" - "project.grant.member.delete"
- "session.delete"
- Role: "IAM_USER_MANAGER" - Role: "IAM_USER_MANAGER"
Permissions: Permissions:
- "org.read" - "org.read"
@ -1053,6 +1055,7 @@ InternalAuthZ:
- "project.grant.write" - "project.grant.write"
- "project.grant.delete" - "project.grant.delete"
- "project.grant.member.read" - "project.grant.member.read"
- "session.delete"
- Role: "ORG_OWNER" - Role: "ORG_OWNER"
Permissions: Permissions:
- "org.read" - "org.read"
@ -1102,6 +1105,7 @@ InternalAuthZ:
- "project.grant.member.read" - "project.grant.member.read"
- "project.grant.member.write" - "project.grant.member.write"
- "project.grant.member.delete" - "project.grant.member.delete"
- "session.delete"
- Role: "ORG_USER_MANAGER" - Role: "ORG_USER_MANAGER"
Permissions: Permissions:
- "org.read" - "org.read"
@ -1116,6 +1120,7 @@ InternalAuthZ:
- "policy.read" - "policy.read"
- "project.read" - "project.read"
- "project.role.read" - "project.role.read"
- "session.delete"
- Role: "ORG_OWNER_VIEWER" - Role: "ORG_OWNER_VIEWER"
Permissions: Permissions:
- "org.read" - "org.read"

View File

@ -355,7 +355,7 @@ func (s *Server) checksToCommand(ctx context.Context, checks *session.Checks) ([
// trigger activity log for session for user // trigger activity log for session for user
activity.Trigger(ctx, user.ResourceOwner, user.ID, activity.SessionAPI) activity.Trigger(ctx, user.ResourceOwner, user.ID, activity.SessionAPI)
sessionChecks = append(sessionChecks, command.CheckUser(user.ID)) sessionChecks = append(sessionChecks, command.CheckUser(user.ID, user.ResourceOwner))
} }
if password := checks.GetPassword(); password != nil { if password := checks.GetPassword(); password != nil {
sessionChecks = append(sessionChecks, command.CheckPassword(password.GetPassword())) sessionChecks = append(sessionChecks, command.CheckPassword(password.GetPassword()))

View File

@ -158,7 +158,7 @@ func TestServer_CreateSession(t *testing.T) {
}, },
want: &session.CreateSessionResponse{ want: &session.CreateSessionResponse{
Details: &object.Details{ Details: &object.Details{
ResourceOwner: Tester.Organisation.ID, ResourceOwner: Tester.Instance.InstanceID(),
}, },
}, },
}, },
@ -177,7 +177,7 @@ func TestServer_CreateSession(t *testing.T) {
}, },
want: &session.CreateSessionResponse{ want: &session.CreateSessionResponse{
Details: &object.Details{ Details: &object.Details{
ResourceOwner: Tester.Organisation.ID, ResourceOwner: Tester.Instance.InstanceID(),
}, },
}, },
wantUserAgent: &session.UserAgent{ wantUserAgent: &session.UserAgent{
@ -205,7 +205,7 @@ func TestServer_CreateSession(t *testing.T) {
}, },
want: &session.CreateSessionResponse{ want: &session.CreateSessionResponse{
Details: &object.Details{ Details: &object.Details{
ResourceOwner: Tester.Organisation.ID, ResourceOwner: Tester.Instance.InstanceID(),
}, },
}, },
wantExpirationWindow: 5 * time.Minute, wantExpirationWindow: 5 * time.Minute,
@ -224,7 +224,7 @@ func TestServer_CreateSession(t *testing.T) {
}, },
want: &session.CreateSessionResponse{ want: &session.CreateSessionResponse{
Details: &object.Details{ Details: &object.Details{
ResourceOwner: Tester.Organisation.ID, ResourceOwner: Tester.Instance.InstanceID(),
}, },
}, },
wantFactors: []wantFactor{wantUserFactor}, wantFactors: []wantFactor{wantUserFactor},
@ -660,6 +660,77 @@ func TestServer_SetSession_expired(t *testing.T) {
require.Error(t, err) require.Error(t, err)
} }
func TestServer_DeleteSession_token(t *testing.T) {
createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{})
require.NoError(t, err)
_, err = Client.DeleteSession(CTX, &session.DeleteSessionRequest{
SessionId: createResp.GetSessionId(),
SessionToken: gu.Ptr("invalid"),
})
require.Error(t, err)
_, err = Client.DeleteSession(CTX, &session.DeleteSessionRequest{
SessionId: createResp.GetSessionId(),
SessionToken: gu.Ptr(createResp.GetSessionToken()),
})
require.NoError(t, err)
}
func TestServer_DeleteSession_own_session(t *testing.T) {
// create two users for the test and a session each to get tokens for authorization
user1 := Tester.CreateHumanUser(CTX)
Tester.SetUserPassword(CTX, user1.GetUserId(), integration.UserPassword)
_, token1, _, _ := Tester.CreatePasswordSession(t, CTX, user1.GetUserId(), integration.UserPassword)
user2 := Tester.CreateHumanUser(CTX)
Tester.SetUserPassword(CTX, user2.GetUserId(), integration.UserPassword)
_, token2, _, _ := Tester.CreatePasswordSession(t, CTX, user2.GetUserId(), integration.UserPassword)
// create a new session for the first user
createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{
Checks: &session.Checks{
User: &session.CheckUser{
Search: &session.CheckUser_UserId{
UserId: user1.GetUserId(),
},
},
},
})
require.NoError(t, err)
// delete the new (user1) session must not be possible with user (has no permission)
_, err = Client.DeleteSession(Tester.WithAuthorizationToken(context.Background(), token2), &session.DeleteSessionRequest{
SessionId: createResp.GetSessionId(),
})
require.Error(t, err)
// delete the new (user1) session by himself
_, err = Client.DeleteSession(Tester.WithAuthorizationToken(context.Background(), token1), &session.DeleteSessionRequest{
SessionId: createResp.GetSessionId(),
})
require.NoError(t, err)
}
func TestServer_DeleteSession_with_permission(t *testing.T) {
createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{
Checks: &session.Checks{
User: &session.CheckUser{
Search: &session.CheckUser_UserId{
UserId: User.GetUserId(),
},
},
},
})
require.NoError(t, err)
// delete the new session by ORG_OWNER
_, err = Client.DeleteSession(Tester.WithAuthorization(context.Background(), integration.OrgOwner), &session.DeleteSessionRequest{
SessionId: createResp.GetSessionId(),
})
require.NoError(t, err)
}
func Test_ZITADEL_API_missing_authentication(t *testing.T) { func Test_ZITADEL_API_missing_authentication(t *testing.T) {
// create new, empty session // create new, empty session
createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{}) createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{})

View File

@ -90,7 +90,7 @@ func (c *Commands) LinkSessionToAuthRequest(ctx context.Context, id, sessionID,
if checkLoginClient && authz.GetCtxData(ctx).UserID != writeModel.LoginClient { if checkLoginClient && authz.GetCtxData(ctx).UserID != writeModel.LoginClient {
return nil, nil, errors.ThrowPermissionDenied(nil, "COMMAND-rai9Y", "Errors.AuthRequest.WrongLoginClient") return nil, nil, errors.ThrowPermissionDenied(nil, "COMMAND-rai9Y", "Errors.AuthRequest.WrongLoginClient")
} }
sessionWriteModel := NewSessionWriteModel(sessionID, authz.GetCtxData(ctx).OrgID) sessionWriteModel := NewSessionWriteModel(sessionID, authz.GetInstance(ctx).InstanceID())
err = c.eventstore.FilterToQueryReducer(ctx, sessionWriteModel) err = c.eventstore.FilterToQueryReducer(ctx, sessionWriteModel)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -98,7 +98,7 @@ func (c *Commands) LinkSessionToAuthRequest(ctx context.Context, id, sessionID,
if err = sessionWriteModel.CheckIsActive(); err != nil { if err = sessionWriteModel.CheckIsActive(); err != nil {
return nil, nil, err return nil, nil, err
} }
if err := c.sessionPermission(ctx, sessionWriteModel, sessionToken, domain.PermissionSessionWrite); err != nil { if err := c.sessionTokenVerifier(ctx, sessionToken, sessionWriteModel.AggregateID, sessionWriteModel.TokenID); err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@ -164,9 +164,8 @@ func TestCommands_AddAuthRequest(t *testing.T) {
func TestCommands_LinkSessionToAuthRequest(t *testing.T) { func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
mockCtx := authz.NewMockContext("instanceID", "orgID", "loginClient") mockCtx := authz.NewMockContext("instanceID", "orgID", "loginClient")
type fields struct { type fields struct {
eventstore *eventstore.Eventstore eventstore *eventstore.Eventstore
tokenVerifier func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) tokenVerifier func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error)
checkPermission domain.PermissionCheck
} }
type args struct { type args struct {
ctx context.Context ctx context.Context
@ -192,10 +191,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
eventstore: eventstoreExpect(t, eventstore: eventstoreExpect(t,
expectFilter(), expectFilter(),
), ),
tokenVerifier: func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) { tokenVerifier: newMockTokenVerifierValid(),
return nil
},
checkPermission: newMockPermissionCheckNotAllowed(),
}, },
args{ args{
ctx: mockCtx, ctx: mockCtx,
@ -235,10 +231,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
), ),
), ),
), ),
tokenVerifier: func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) { tokenVerifier: newMockTokenVerifierValid(),
return nil
},
checkPermission: newMockPermissionCheckAllowed(),
}, },
args{ args{
ctx: mockCtx, ctx: mockCtx,
@ -274,10 +267,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
), ),
), ),
), ),
tokenVerifier: func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) { tokenVerifier: newMockTokenVerifierValid(),
return nil
},
checkPermission: newMockPermissionCheckAllowed(),
}, },
args{ args{
ctx: authz.NewMockContext("instanceID", "orgID", "wrongLoginClient"), ctx: authz.NewMockContext("instanceID", "orgID", "wrongLoginClient"),
@ -316,10 +306,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
), ),
expectFilter(), expectFilter(),
), ),
tokenVerifier: func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) { tokenVerifier: newMockTokenVerifierValid(),
return nil
},
checkPermission: newMockPermissionCheckNotAllowed(),
}, },
args{ args{
ctx: mockCtx, ctx: mockCtx,
@ -357,7 +344,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
expectFilter( expectFilter(
eventFromEventPusher( eventFromEventPusher(
session.NewAddedEvent(mockCtx, session.NewAddedEvent(mockCtx,
&session.NewAggregate("sessionID", "org1").Aggregate, &session.NewAggregate("sessionID", "instance1").Aggregate,
&domain.UserAgent{ &domain.UserAgent{
FingerprintID: gu.Ptr("fp1"), FingerprintID: gu.Ptr("fp1"),
IP: net.ParseIP("1.2.3.4"), IP: net.ParseIP("1.2.3.4"),
@ -366,15 +353,15 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
}, },
)), )),
eventFromEventPusher( eventFromEventPusher(
session.NewUserCheckedEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate, session.NewUserCheckedEvent(mockCtx, &session.NewAggregate("sessionID", "instance1").Aggregate,
"userID", testNow.Add(-5*time.Minute)), "userID", "org1", testNow.Add(-5*time.Minute)),
), ),
eventFromEventPusher( eventFromEventPusher(
session.NewPasswordCheckedEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate, session.NewPasswordCheckedEvent(mockCtx, &session.NewAggregate("sessionID", "instance1").Aggregate,
testNow.Add(-5*time.Minute)), testNow.Add(-5*time.Minute)),
), ),
eventFromEventPusher( eventFromEventPusher(
session.NewLifetimeSetEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate, session.NewLifetimeSetEvent(mockCtx, &session.NewAggregate("sessionID", "instance1").Aggregate,
2*time.Minute), 2*time.Minute),
), ),
), ),
@ -390,57 +377,6 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
wantErr: caos_errs.ThrowPreconditionFailed(nil, "COMMAND-Hkl3d", "Errors.Session.Expired"), wantErr: caos_errs.ThrowPreconditionFailed(nil, "COMMAND-Hkl3d", "Errors.Session.Expired"),
}, },
}, },
{
"missing permission",
fields{
eventstore: eventstoreExpect(t,
expectFilter(
eventFromEventPusher(
authrequest.NewAddedEvent(mockCtx, &authrequest.NewAggregate("V2_id", "instanceID").Aggregate,
"loginClient",
"clientID",
"redirectURI",
"state",
"nonce",
[]string{"openid"},
[]string{"audience"},
domain.OIDCResponseTypeCode,
nil,
nil,
nil,
nil,
nil,
nil,
),
),
),
expectFilter(
eventFromEventPusher(
session.NewAddedEvent(mockCtx,
&session.NewAggregate("sessionID", "org1").Aggregate,
&domain.UserAgent{
FingerprintID: gu.Ptr("fp1"),
IP: net.ParseIP("1.2.3.4"),
Description: gu.Ptr("firefox"),
Header: http.Header{"foo": []string{"bar"}},
},
)),
),
),
tokenVerifier: func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) {
return nil
},
checkPermission: newMockPermissionCheckNotAllowed(),
},
args{
ctx: mockCtx,
id: "V2_id",
sessionID: "sessionID",
},
res{
wantErr: caos_errs.ThrowPermissionDenied(nil, "AUTHZ-HKJD33", "Errors.PermissionDenied"),
},
},
{ {
"invalid session token", "invalid session token",
fields{ fields{
@ -468,7 +404,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
expectFilter( expectFilter(
eventFromEventPusher( eventFromEventPusher(
session.NewAddedEvent(mockCtx, session.NewAddedEvent(mockCtx,
&session.NewAggregate("sessionID", "org1").Aggregate, &session.NewAggregate("sessionID", "instance1").Aggregate,
&domain.UserAgent{ &domain.UserAgent{
FingerprintID: gu.Ptr("fp1"), FingerprintID: gu.Ptr("fp1"),
IP: net.ParseIP("1.2.3.4"), IP: net.ParseIP("1.2.3.4"),
@ -478,9 +414,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
)), )),
), ),
), ),
tokenVerifier: func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) { tokenVerifier: newMockTokenVerifierInvalid(),
return caos_errs.ThrowPermissionDenied(nil, "COMMAND-sGr42", "Errors.Session.Token.Invalid")
},
}, },
args{ args{
ctx: mockCtx, ctx: mockCtx,
@ -519,7 +453,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
expectFilter( expectFilter(
eventFromEventPusher( eventFromEventPusher(
session.NewAddedEvent(mockCtx, session.NewAddedEvent(mockCtx,
&session.NewAggregate("sessionID", "org1").Aggregate, &session.NewAggregate("sessionID", "instance1").Aggregate,
&domain.UserAgent{ &domain.UserAgent{
FingerprintID: gu.Ptr("fp1"), FingerprintID: gu.Ptr("fp1"),
IP: net.ParseIP("1.2.3.4"), IP: net.ParseIP("1.2.3.4"),
@ -528,15 +462,15 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
}, },
)), )),
eventFromEventPusher( eventFromEventPusher(
session.NewUserCheckedEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate, session.NewUserCheckedEvent(mockCtx, &session.NewAggregate("sessionID", "instance1").Aggregate,
"userID", testNow), "userID", "org1", testNow),
), ),
eventFromEventPusher( eventFromEventPusher(
session.NewPasswordCheckedEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate, session.NewPasswordCheckedEvent(mockCtx, &session.NewAggregate("sessionID", "instance1").Aggregate,
testNow), testNow),
), ),
eventFromEventPusherWithCreationDateNow( eventFromEventPusherWithCreationDateNow(
session.NewLifetimeSetEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate, session.NewLifetimeSetEvent(mockCtx, &session.NewAggregate("sessionID", "instance1").Aggregate,
2*time.Minute), 2*time.Minute),
), ),
), ),
@ -549,10 +483,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
), ),
), ),
), ),
tokenVerifier: func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) { tokenVerifier: newMockTokenVerifierValid(),
return nil
},
checkPermission: newMockPermissionCheckAllowed(),
}, },
args{ args{
ctx: mockCtx, ctx: mockCtx,
@ -607,7 +538,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
expectFilter( expectFilter(
eventFromEventPusher( eventFromEventPusher(
session.NewAddedEvent(mockCtx, session.NewAddedEvent(mockCtx,
&session.NewAggregate("sessionID", "org1").Aggregate, &session.NewAggregate("sessionID", "instance1").Aggregate,
&domain.UserAgent{ &domain.UserAgent{
FingerprintID: gu.Ptr("fp1"), FingerprintID: gu.Ptr("fp1"),
IP: net.ParseIP("1.2.3.4"), IP: net.ParseIP("1.2.3.4"),
@ -616,15 +547,15 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
}, },
)), )),
eventFromEventPusher( eventFromEventPusher(
session.NewUserCheckedEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate, session.NewUserCheckedEvent(mockCtx, &session.NewAggregate("sessionID", "instance1").Aggregate,
"userID", testNow), "userID", "org1", testNow),
), ),
eventFromEventPusher( eventFromEventPusher(
session.NewPasswordCheckedEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate, session.NewPasswordCheckedEvent(mockCtx, &session.NewAggregate("sessionID", "instance1").Aggregate,
testNow), testNow),
), ),
eventFromEventPusherWithCreationDateNow( eventFromEventPusherWithCreationDateNow(
session.NewLifetimeSetEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate, session.NewLifetimeSetEvent(mockCtx, &session.NewAggregate("sessionID", "instance1").Aggregate,
2*time.Minute), 2*time.Minute),
), ),
), ),
@ -637,10 +568,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
), ),
), ),
), ),
tokenVerifier: func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) { tokenVerifier: newMockTokenVerifierValid(),
return nil
},
checkPermission: newMockPermissionCheckAllowed(),
}, },
args{ args{
ctx: authz.NewMockContext("instanceID", "orgID", "loginClient"), ctx: authz.NewMockContext("instanceID", "orgID", "loginClient"),
@ -675,7 +603,6 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
c := &Commands{ c := &Commands{
eventstore: tt.fields.eventstore, eventstore: tt.fields.eventstore,
sessionTokenVerifier: tt.fields.tokenVerifier, sessionTokenVerifier: tt.fields.tokenVerifier,
checkPermission: tt.fields.checkPermission,
} }
details, got, err := c.LinkSessionToAuthRequest(tt.args.ctx, tt.args.id, tt.args.sessionID, tt.args.sessionToken, tt.args.checkLoginClient) details, got, err := c.LinkSessionToAuthRequest(tt.args.ctx, tt.args.id, tt.args.sessionID, tt.args.sessionToken, tt.args.checkLoginClient)
require.ErrorIs(t, err, tt.res.wantErr) require.ErrorIs(t, err, tt.res.wantErr)

View File

@ -241,6 +241,17 @@ func newMockPermissionCheckNotAllowed() domain.PermissionCheck {
} }
} }
func newMockTokenVerifierValid() func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) {
return func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) {
return nil
}
}
func newMockTokenVerifierInvalid() func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) {
return func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) {
return errors.ThrowPermissionDenied(nil, "COMMAND-sGr42", "Errors.Session.Token.Invalid")
}
}
type plainHasher struct { type plainHasher struct {
x string // arbitrary info that triggers update when different from encoding x string // arbitrary info that triggers update when different from encoding
} }

View File

@ -153,7 +153,7 @@ func (c *Commands) newOIDCSessionAddEvents(ctx context.Context, authRequestID st
if err = authRequestWriteModel.CheckAuthenticated(); err != nil { if err = authRequestWriteModel.CheckAuthenticated(); err != nil {
return nil, err return nil, err
} }
sessionWriteModel := NewSessionWriteModel(authRequestWriteModel.SessionID, authz.GetCtxData(ctx).OrgID) sessionWriteModel := NewSessionWriteModel(authRequestWriteModel.SessionID, authz.GetInstance(ctx).InstanceID())
err = c.eventstore.FilterToQueryReducer(ctx, sessionWriteModel) err = c.eventstore.FilterToQueryReducer(ctx, sessionWriteModel)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -166,7 +166,7 @@ func TestCommands_AddOIDCSessionAccessToken(t *testing.T) {
expectFilter( expectFilter(
eventFromEventPusher( eventFromEventPusher(
session.NewAddedEvent(context.Background(), session.NewAddedEvent(context.Background(),
&session.NewAggregate("sessionID", "org1").Aggregate, &session.NewAggregate("sessionID", "instance1").Aggregate,
&domain.UserAgent{ &domain.UserAgent{
FingerprintID: gu.Ptr("fp1"), FingerprintID: gu.Ptr("fp1"),
IP: net.ParseIP("1.2.3.4"), IP: net.ParseIP("1.2.3.4"),
@ -177,7 +177,7 @@ func TestCommands_AddOIDCSessionAccessToken(t *testing.T) {
), ),
eventFromEventPusher( eventFromEventPusher(
session.NewUserCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate, session.NewUserCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate,
"userID", testNow), "userID", "org1", testNow),
), ),
eventFromEventPusher( eventFromEventPusher(
session.NewPasswordCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate, session.NewPasswordCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate,
@ -367,7 +367,7 @@ func TestCommands_AddOIDCSessionRefreshAndAccessToken(t *testing.T) {
expectFilter( expectFilter(
eventFromEventPusher( eventFromEventPusher(
session.NewAddedEvent(context.Background(), session.NewAddedEvent(context.Background(),
&session.NewAggregate("sessionID", "org1").Aggregate, &session.NewAggregate("sessionID", "instance1").Aggregate,
&domain.UserAgent{ &domain.UserAgent{
FingerprintID: gu.Ptr("fp1"), FingerprintID: gu.Ptr("fp1"),
IP: net.ParseIP("1.2.3.4"), IP: net.ParseIP("1.2.3.4"),
@ -378,7 +378,7 @@ func TestCommands_AddOIDCSessionRefreshAndAccessToken(t *testing.T) {
), ),
eventFromEventPusher( eventFromEventPusher(
session.NewUserCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate, session.NewUserCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate,
"userID", testNow), "userID", "org1", testNow),
), ),
eventFromEventPusher( eventFromEventPusher(
session.NewPasswordCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate, session.NewPasswordCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate,

View File

@ -55,12 +55,12 @@ func (c *Commands) NewSessionCommands(cmds []SessionCommand, session *SessionWri
} }
// CheckUser defines a user check to be executed for a session update // CheckUser defines a user check to be executed for a session update
func CheckUser(id string) SessionCommand { func CheckUser(id string, resourceOwner string) SessionCommand {
return func(ctx context.Context, cmd *SessionCommands) error { return func(ctx context.Context, cmd *SessionCommands) error {
if cmd.sessionWriteModel.UserID != "" && id != "" && cmd.sessionWriteModel.UserID != id { if cmd.sessionWriteModel.UserID != "" && id != "" && cmd.sessionWriteModel.UserID != id {
return caos_errs.ThrowInvalidArgument(nil, "", "user change not possible") return caos_errs.ThrowInvalidArgument(nil, "", "user change not possible")
} }
return cmd.UserChecked(ctx, id, cmd.now()) return cmd.UserChecked(ctx, id, resourceOwner, cmd.now())
} }
} }
@ -170,10 +170,11 @@ func (s *SessionCommands) Start(ctx context.Context, userAgent *domain.UserAgent
s.eventCommands = append(s.eventCommands, session.NewAddedEvent(ctx, s.sessionWriteModel.aggregate, userAgent)) s.eventCommands = append(s.eventCommands, session.NewAddedEvent(ctx, s.sessionWriteModel.aggregate, userAgent))
} }
func (s *SessionCommands) UserChecked(ctx context.Context, userID string, checkedAt time.Time) error { func (s *SessionCommands) UserChecked(ctx context.Context, userID, resourceOwner string, checkedAt time.Time) error {
s.eventCommands = append(s.eventCommands, session.NewUserCheckedEvent(ctx, s.sessionWriteModel.aggregate, userID, checkedAt)) s.eventCommands = append(s.eventCommands, session.NewUserCheckedEvent(ctx, s.sessionWriteModel.aggregate, userID, resourceOwner, checkedAt))
// set the userID so other checks can use it // set the userID so other checks can use it
s.sessionWriteModel.UserID = userID s.sessionWriteModel.UserID = userID
s.sessionWriteModel.UserResourceOwner = resourceOwner
return nil return nil
} }
@ -267,7 +268,7 @@ func (s *SessionCommands) gethumanWriteModel(ctx context.Context) (*HumanWriteMo
if s.sessionWriteModel.UserID == "" { if s.sessionWriteModel.UserID == "" {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-eeR2e", "Errors.User.UserIDMissing") return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-eeR2e", "Errors.User.UserIDMissing")
} }
humanWriteModel := NewHumanWriteModel(s.sessionWriteModel.UserID, "") humanWriteModel := NewHumanWriteModel(s.sessionWriteModel.UserID, s.sessionWriteModel.UserResourceOwner)
err := s.eventstore.FilterToQueryReducer(ctx, humanWriteModel) err := s.eventstore.FilterToQueryReducer(ctx, humanWriteModel)
if err != nil { if err != nil {
return nil, err return nil, err
@ -296,7 +297,7 @@ func (c *Commands) CreateSession(ctx context.Context, cmds []SessionCommand, met
if err != nil { if err != nil {
return nil, err return nil, err
} }
sessionWriteModel := NewSessionWriteModel(sessionID, authz.GetCtxData(ctx).OrgID) sessionWriteModel := NewSessionWriteModel(sessionID, authz.GetInstance(ctx).InstanceID())
err = c.eventstore.FilterToQueryReducer(ctx, sessionWriteModel) err = c.eventstore.FilterToQueryReducer(ctx, sessionWriteModel)
if err != nil { if err != nil {
return nil, err return nil, err
@ -307,12 +308,12 @@ func (c *Commands) CreateSession(ctx context.Context, cmds []SessionCommand, met
} }
func (c *Commands) UpdateSession(ctx context.Context, sessionID, sessionToken string, cmds []SessionCommand, metadata map[string][]byte, lifetime time.Duration) (set *SessionChanged, err error) { func (c *Commands) UpdateSession(ctx context.Context, sessionID, sessionToken string, cmds []SessionCommand, metadata map[string][]byte, lifetime time.Duration) (set *SessionChanged, err error) {
sessionWriteModel := NewSessionWriteModel(sessionID, authz.GetCtxData(ctx).OrgID) sessionWriteModel := NewSessionWriteModel(sessionID, authz.GetInstance(ctx).InstanceID())
err = c.eventstore.FilterToQueryReducer(ctx, sessionWriteModel) err = c.eventstore.FilterToQueryReducer(ctx, sessionWriteModel)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := c.sessionPermission(ctx, sessionWriteModel, sessionToken, domain.PermissionSessionWrite); err != nil { if err := c.sessionTokenVerifier(ctx, sessionToken, sessionWriteModel.AggregateID, sessionWriteModel.TokenID); err != nil {
return nil, err return nil, err
} }
cmd := c.NewSessionCommands(cmds, sessionWriteModel) cmd := c.NewSessionCommands(cmds, sessionWriteModel)
@ -328,12 +329,12 @@ func (c *Commands) TerminateSessionWithoutTokenCheck(ctx context.Context, sessio
} }
func (c *Commands) terminateSession(ctx context.Context, sessionID, sessionToken string, mustCheckToken bool) (*domain.ObjectDetails, error) { func (c *Commands) terminateSession(ctx context.Context, sessionID, sessionToken string, mustCheckToken bool) (*domain.ObjectDetails, error) {
sessionWriteModel := NewSessionWriteModel(sessionID, "") sessionWriteModel := NewSessionWriteModel(sessionID, authz.GetInstance(ctx).InstanceID())
if err := c.eventstore.FilterToQueryReducer(ctx, sessionWriteModel); err != nil { if err := c.eventstore.FilterToQueryReducer(ctx, sessionWriteModel); err != nil {
return nil, err return nil, err
} }
if mustCheckToken { if mustCheckToken {
if err := c.sessionPermission(ctx, sessionWriteModel, sessionToken, domain.PermissionSessionDelete); err != nil { if err := c.checkSessionTerminationPermission(ctx, sessionWriteModel, sessionToken); err != nil {
return nil, err return nil, err
} }
} }
@ -386,13 +387,17 @@ func (c *Commands) updateSession(ctx context.Context, checks *SessionCommands, m
return changed, nil return changed, nil
} }
// sessionPermission will check that the provided sessionToken is correct or // checkSessionTerminationPermission will check that the provided sessionToken is correct or
// if empty, check that the caller is granted the necessary permission // if empty, check that the caller is either terminating the own session or
func (c *Commands) sessionPermission(ctx context.Context, sessionWriteModel *SessionWriteModel, sessionToken, permission string) (err error) { // is granted the "session.delete" permission on the resource owner of the authenticated user.
if sessionToken == "" { func (c *Commands) checkSessionTerminationPermission(ctx context.Context, model *SessionWriteModel, token string) error {
return c.checkPermission(ctx, permission, authz.GetCtxData(ctx).OrgID, sessionWriteModel.AggregateID) if token != "" {
return c.sessionTokenVerifier(ctx, token, model.AggregateID, model.TokenID)
} }
return c.sessionTokenVerifier(ctx, sessionToken, sessionWriteModel.AggregateID, sessionWriteModel.TokenID) if model.UserID != "" && model.UserID == authz.GetCtxData(ctx).UserID {
return nil
}
return c.checkPermission(ctx, domain.PermissionSessionDelete, model.UserResourceOwner, model.UserID)
} }
func sessionTokenCreator(idGenerator id.Generator, sessionAlg crypto.EncryptionAlgorithm) func(sessionID string) (id string, token string, err error) { func sessionTokenCreator(idGenerator id.Generator, sessionAlg crypto.EncryptionAlgorithm) func(sessionID string) (id string, token string, err error) {

View File

@ -39,6 +39,7 @@ type SessionWriteModel struct {
TokenID string TokenID string
UserID string UserID string
UserResourceOwner string
UserCheckedAt time.Time UserCheckedAt time.Time
PasswordCheckedAt time.Time PasswordCheckedAt time.Time
IntentCheckedAt time.Time IntentCheckedAt time.Time
@ -58,14 +59,14 @@ type SessionWriteModel struct {
aggregate *eventstore.Aggregate aggregate *eventstore.Aggregate
} }
func NewSessionWriteModel(sessionID string, resourceOwner string) *SessionWriteModel { func NewSessionWriteModel(sessionID string, instanceID string) *SessionWriteModel {
return &SessionWriteModel{ return &SessionWriteModel{
WriteModel: eventstore.WriteModel{ WriteModel: eventstore.WriteModel{
AggregateID: sessionID, AggregateID: sessionID,
ResourceOwner: resourceOwner, ResourceOwner: instanceID,
}, },
Metadata: make(map[string][]byte), Metadata: make(map[string][]byte),
aggregate: &session.NewAggregate(sessionID, resourceOwner).Aggregate, aggregate: &session.NewAggregate(sessionID, instanceID).Aggregate,
} }
} }
@ -141,6 +142,7 @@ func (wm *SessionWriteModel) reduceAdded(e *session.AddedEvent) {
func (wm *SessionWriteModel) reduceUserChecked(e *session.UserCheckedEvent) { func (wm *SessionWriteModel) reduceUserChecked(e *session.UserCheckedEvent) {
wm.UserID = e.UserID wm.UserID = e.UserID
wm.UserResourceOwner = e.UserResourceOwner
wm.UserCheckedAt = e.CheckedAt wm.UserCheckedAt = e.CheckedAt
} }

View File

@ -204,7 +204,7 @@ func TestCommands_CreateSession(t *testing.T) {
}, },
}, },
args{ args{
ctx: authz.NewMockContext("", "org1", ""), ctx: authz.NewMockContext("instance1", "", ""),
userAgent: &domain.UserAgent{ userAgent: &domain.UserAgent{
FingerprintID: gu.Ptr("fp1"), FingerprintID: gu.Ptr("fp1"),
IP: net.ParseIP("1.2.3.4"), IP: net.ParseIP("1.2.3.4"),
@ -231,7 +231,7 @@ func TestCommands_CreateSession(t *testing.T) {
}, },
}, },
args{ args{
ctx: authz.NewMockContext("", "org1", ""), ctx: authz.NewMockContext("instance1", "", ""),
userAgent: &domain.UserAgent{ userAgent: &domain.UserAgent{
FingerprintID: gu.Ptr("fp1"), FingerprintID: gu.Ptr("fp1"),
IP: net.ParseIP("1.2.3.4"), IP: net.ParseIP("1.2.3.4"),
@ -244,7 +244,7 @@ func TestCommands_CreateSession(t *testing.T) {
expectFilter(), expectFilter(),
expectPush( expectPush(
session.NewAddedEvent(context.Background(), session.NewAddedEvent(context.Background(),
&session.NewAggregate("sessionID", "org1").Aggregate, &session.NewAggregate("sessionID", "instance1").Aggregate,
&domain.UserAgent{ &domain.UserAgent{
FingerprintID: gu.Ptr("fp1"), FingerprintID: gu.Ptr("fp1"),
IP: net.ParseIP("1.2.3.4"), IP: net.ParseIP("1.2.3.4"),
@ -252,15 +252,15 @@ func TestCommands_CreateSession(t *testing.T) {
Header: http.Header{"foo": []string{"bar"}}, Header: http.Header{"foo": []string{"bar"}},
}, },
), ),
session.NewLifetimeSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, 10*time.Minute), session.NewLifetimeSetEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate, 10*time.Minute),
session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
"tokenID", "tokenID",
), ),
), ),
}, },
res{ res{
want: &SessionChanged{ want: &SessionChanged{
ObjectDetails: &domain.ObjectDetails{ResourceOwner: "org1"}, ObjectDetails: &domain.ObjectDetails{ResourceOwner: "instance1"},
ID: "sessionID", ID: "sessionID",
NewToken: "token", NewToken: "token",
}, },
@ -326,7 +326,7 @@ func TestCommands_UpdateSession(t *testing.T) {
expectFilter( expectFilter(
eventFromEventPusher( eventFromEventPusher(
session.NewAddedEvent(context.Background(), session.NewAddedEvent(context.Background(),
&session.NewAggregate("sessionID", "org1").Aggregate, &session.NewAggregate("sessionID", "instance1").Aggregate,
&domain.UserAgent{ &domain.UserAgent{
FingerprintID: gu.Ptr("fp1"), FingerprintID: gu.Ptr("fp1"),
IP: net.ParseIP("1.2.3.4"), IP: net.ParseIP("1.2.3.4"),
@ -335,13 +335,11 @@ func TestCommands_UpdateSession(t *testing.T) {
}, },
)), )),
eventFromEventPusher( eventFromEventPusher(
session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
"tokenID")), "tokenID")),
), ),
), ),
tokenVerifier: func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) { tokenVerifier: newMockTokenVerifierInvalid(),
return caos_errs.ThrowPermissionDenied(nil, "COMMAND-sGr42", "Errors.Session.Token.Invalid")
},
}, },
args{ args{
ctx: context.Background(), ctx: context.Background(),
@ -359,7 +357,7 @@ func TestCommands_UpdateSession(t *testing.T) {
expectFilter( expectFilter(
eventFromEventPusher( eventFromEventPusher(
session.NewAddedEvent(context.Background(), session.NewAddedEvent(context.Background(),
&session.NewAggregate("sessionID", "org1").Aggregate, &session.NewAggregate("sessionID", "instance1").Aggregate,
&domain.UserAgent{ &domain.UserAgent{
FingerprintID: gu.Ptr("fp1"), FingerprintID: gu.Ptr("fp1"),
IP: net.ParseIP("1.2.3.4"), IP: net.ParseIP("1.2.3.4"),
@ -368,7 +366,7 @@ func TestCommands_UpdateSession(t *testing.T) {
}, },
)), )),
eventFromEventPusher( eventFromEventPusher(
session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
"tokenID")), "tokenID")),
), ),
), ),
@ -384,7 +382,7 @@ func TestCommands_UpdateSession(t *testing.T) {
res{ res{
want: &SessionChanged{ want: &SessionChanged{
ObjectDetails: &domain.ObjectDetails{ ObjectDetails: &domain.ObjectDetails{
ResourceOwner: "org1", ResourceOwner: "instance1",
}, },
ID: "sessionID", ID: "sessionID",
NewToken: "", NewToken: "",
@ -463,7 +461,7 @@ func TestCommands_updateSession(t *testing.T) {
args{ args{
ctx: context.Background(), ctx: context.Background(),
checks: &SessionCommands{ checks: &SessionCommands{
sessionWriteModel: NewSessionWriteModel("sessionID", "org1"), sessionWriteModel: NewSessionWriteModel("sessionID", "instance1"),
sessionCommands: []SessionCommand{ sessionCommands: []SessionCommand{
func(ctx context.Context, cmd *SessionCommands) error { func(ctx context.Context, cmd *SessionCommands) error {
return caos_errs.ThrowInternal(nil, "id", "check failed") return caos_errs.ThrowInternal(nil, "id", "check failed")
@ -481,16 +479,16 @@ func TestCommands_updateSession(t *testing.T) {
eventstore: eventstoreExpect(t), eventstore: eventstoreExpect(t),
}, },
args{ args{
ctx: context.Background(), ctx: authz.NewMockContext("instance1", "", ""),
checks: &SessionCommands{ checks: &SessionCommands{
sessionWriteModel: NewSessionWriteModel("sessionID", "org1"), sessionWriteModel: NewSessionWriteModel("sessionID", "instance1"),
sessionCommands: []SessionCommand{}, sessionCommands: []SessionCommand{},
}, },
}, },
res{ res{
want: &SessionChanged{ want: &SessionChanged{
ObjectDetails: &domain.ObjectDetails{ ObjectDetails: &domain.ObjectDetails{
ResourceOwner: "org1", ResourceOwner: "instance1",
}, },
ID: "sessionID", ID: "sessionID",
NewToken: "", NewToken: "",
@ -503,9 +501,9 @@ func TestCommands_updateSession(t *testing.T) {
eventstore: eventstoreExpect(t), eventstore: eventstoreExpect(t),
}, },
args{ args{
ctx: context.Background(), ctx: authz.NewMockContext("instance1", "", ""),
checks: &SessionCommands{ checks: &SessionCommands{
sessionWriteModel: NewSessionWriteModel("sessionID", "org1"), sessionWriteModel: NewSessionWriteModel("sessionID", "instance1"),
sessionCommands: []SessionCommand{}, sessionCommands: []SessionCommand{},
eventstore: eventstoreExpect(t), eventstore: eventstoreExpect(t),
createToken: func(sessionID string) (string, string, error) { createToken: func(sessionID string) (string, string, error) {
@ -528,19 +526,19 @@ func TestCommands_updateSession(t *testing.T) {
fields{ fields{
eventstore: eventstoreExpect(t, eventstore: eventstoreExpect(t,
expectPush( expectPush(
session.NewLifetimeSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, session.NewLifetimeSetEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
10*time.Minute, 10*time.Minute,
), ),
session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
"tokenID", "tokenID",
), ),
), ),
), ),
}, },
args{ args{
ctx: context.Background(), ctx: authz.NewMockContext("instance1", "", ""),
checks: &SessionCommands{ checks: &SessionCommands{
sessionWriteModel: NewSessionWriteModel("sessionID", "org1"), sessionWriteModel: NewSessionWriteModel("sessionID", "instance1"),
sessionCommands: []SessionCommand{}, sessionCommands: []SessionCommand{},
eventstore: eventstoreExpect(t), eventstore: eventstoreExpect(t),
createToken: func(sessionID string) (string, string, error) { createToken: func(sessionID string) (string, string, error) {
@ -557,7 +555,7 @@ func TestCommands_updateSession(t *testing.T) {
res{ res{
want: &SessionChanged{ want: &SessionChanged{
ObjectDetails: &domain.ObjectDetails{ ObjectDetails: &domain.ObjectDetails{
ResourceOwner: "org1", ResourceOwner: "instance1",
}, },
ID: "sessionID", ID: "sessionID",
NewToken: "token", NewToken: "token",
@ -569,27 +567,27 @@ func TestCommands_updateSession(t *testing.T) {
fields{ fields{
eventstore: eventstoreExpect(t, eventstore: eventstoreExpect(t,
expectPush( expectPush(
session.NewUserCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, session.NewUserCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
"userID", testNow, "userID", "org1", testNow,
), ),
session.NewPasswordCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, session.NewPasswordCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
testNow, testNow,
), ),
session.NewMetadataSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, session.NewMetadataSetEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
map[string][]byte{"key": []byte("value")}, map[string][]byte{"key": []byte("value")},
), ),
session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
"tokenID", "tokenID",
), ),
), ),
), ),
}, },
args{ args{
ctx: context.Background(), ctx: authz.NewMockContext("instance1", "", ""),
checks: &SessionCommands{ checks: &SessionCommands{
sessionWriteModel: NewSessionWriteModel("sessionID", "org1"), sessionWriteModel: NewSessionWriteModel("sessionID", "instance1"),
sessionCommands: []SessionCommand{ sessionCommands: []SessionCommand{
CheckUser("userID"), CheckUser("userID", "org1"),
CheckPassword("password"), CheckPassword("password"),
}, },
eventstore: eventstoreExpect(t, eventstore: eventstoreExpect(t,
@ -621,7 +619,7 @@ func TestCommands_updateSession(t *testing.T) {
res{ res{
want: &SessionChanged{ want: &SessionChanged{
ObjectDetails: &domain.ObjectDetails{ ObjectDetails: &domain.ObjectDetails{
ResourceOwner: "org1", ResourceOwner: "instance1",
}, },
ID: "sessionID", ID: "sessionID",
NewToken: "token", NewToken: "token",
@ -634,11 +632,11 @@ func TestCommands_updateSession(t *testing.T) {
eventstore: eventstoreExpect(t), eventstore: eventstoreExpect(t),
}, },
args{ args{
ctx: context.Background(), ctx: authz.NewMockContext("instance1", "", ""),
checks: &SessionCommands{ checks: &SessionCommands{
sessionWriteModel: NewSessionWriteModel("sessionID", "org1"), sessionWriteModel: NewSessionWriteModel("sessionID", "instance1"),
sessionCommands: []SessionCommand{ sessionCommands: []SessionCommand{
CheckUser("userID"), CheckUser("userID", "org1"),
CheckIntent("intent", "aW50ZW50"), CheckIntent("intent", "aW50ZW50"),
}, },
eventstore: eventstoreExpect(t, eventstore: eventstoreExpect(t,
@ -673,11 +671,11 @@ func TestCommands_updateSession(t *testing.T) {
eventstore: eventstoreExpect(t), eventstore: eventstoreExpect(t),
}, },
args{ args{
ctx: context.Background(), ctx: authz.NewMockContext("instance1", "", ""),
checks: &SessionCommands{ checks: &SessionCommands{
sessionWriteModel: NewSessionWriteModel("sessionID", "org1"), sessionWriteModel: NewSessionWriteModel("sessionID", "instance1"),
sessionCommands: []SessionCommand{ sessionCommands: []SessionCommand{
CheckUser("userID"), CheckUser("userID", "org1"),
CheckIntent("intent", "aW50ZW50"), CheckIntent("intent", "aW50ZW50"),
}, },
eventstore: eventstoreExpect(t, eventstore: eventstoreExpect(t,
@ -722,11 +720,11 @@ func TestCommands_updateSession(t *testing.T) {
eventstore: eventstoreExpect(t), eventstore: eventstoreExpect(t),
}, },
args{ args{
ctx: context.Background(), ctx: authz.NewMockContext("instance1", "", ""),
checks: &SessionCommands{ checks: &SessionCommands{
sessionWriteModel: NewSessionWriteModel("sessionID", "org1"), sessionWriteModel: NewSessionWriteModel("sessionID", "instance1"),
sessionCommands: []SessionCommand{ sessionCommands: []SessionCommand{
CheckUser("userID"), CheckUser("userID", "org1"),
CheckIntent("intent2", "aW50ZW50"), CheckIntent("intent2", "aW50ZW50"),
}, },
eventstore: eventstoreExpect(t), eventstore: eventstoreExpect(t),
@ -753,23 +751,23 @@ func TestCommands_updateSession(t *testing.T) {
fields{ fields{
eventstore: eventstoreExpect(t, eventstore: eventstoreExpect(t,
expectPush( expectPush(
session.NewUserCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, session.NewUserCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
"userID", testNow), "userID", "org1", testNow),
session.NewIntentCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, session.NewIntentCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
testNow), testNow),
session.NewMetadataSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, session.NewMetadataSetEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
map[string][]byte{"key": []byte("value")}), map[string][]byte{"key": []byte("value")}),
session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
"tokenID"), "tokenID"),
), ),
), ),
}, },
args{ args{
ctx: context.Background(), ctx: authz.NewMockContext("instance1", "", ""),
checks: &SessionCommands{ checks: &SessionCommands{
sessionWriteModel: NewSessionWriteModel("sessionID", "org1"), sessionWriteModel: NewSessionWriteModel("sessionID", "instance1"),
sessionCommands: []SessionCommand{ sessionCommands: []SessionCommand{
CheckUser("userID"), CheckUser("userID", "org1"),
CheckIntent("intent", "aW50ZW50"), CheckIntent("intent", "aW50ZW50"),
}, },
eventstore: eventstoreExpect(t, eventstore: eventstoreExpect(t,
@ -807,7 +805,7 @@ func TestCommands_updateSession(t *testing.T) {
res{ res{
want: &SessionChanged{ want: &SessionChanged{
ObjectDetails: &domain.ObjectDetails{ ObjectDetails: &domain.ObjectDetails{
ResourceOwner: "org1", ResourceOwner: "instance1",
}, },
ID: "sessionID", ID: "sessionID",
NewToken: "token", NewToken: "token",
@ -828,13 +826,13 @@ func TestCommands_updateSession(t *testing.T) {
} }
func TestCheckTOTP(t *testing.T) { func TestCheckTOTP(t *testing.T) {
ctx := authz.NewMockContext("", "org1", "user1") ctx := authz.NewMockContext("instance1", "org1", "user1")
cryptoAlg := crypto.CreateMockEncryptionAlg(gomock.NewController(t)) cryptoAlg := crypto.CreateMockEncryptionAlg(gomock.NewController(t))
key, secret, err := domain.NewTOTPKey("example.com", "user1", cryptoAlg) key, secret, err := domain.NewTOTPKey("example.com", "user1", cryptoAlg)
require.NoError(t, err) require.NoError(t, err)
sessAgg := &session.NewAggregate("session1", "org1").Aggregate sessAgg := &session.NewAggregate("session1", "instance1").Aggregate
userAgg := &user.NewAggregate("user1", "org1").Aggregate userAgg := &user.NewAggregate("user1", "org1").Aggregate
code, err := totp.GenerateCode(key.Secret(), testNow) code, err := totp.GenerateCode(key.Secret(), testNow)
@ -961,8 +959,9 @@ func TestCheckTOTP(t *testing.T) {
func TestCommands_TerminateSession(t *testing.T) { func TestCommands_TerminateSession(t *testing.T) {
type fields struct { type fields struct {
eventstore *eventstore.Eventstore eventstore func(t *testing.T) *eventstore.Eventstore
tokenVerifier func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) tokenVerifier func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error)
checkPermission domain.PermissionCheck
} }
type args struct { type args struct {
ctx context.Context ctx context.Context
@ -982,7 +981,7 @@ func TestCommands_TerminateSession(t *testing.T) {
{ {
"eventstore failed", "eventstore failed",
fields{ fields{
eventstore: eventstoreExpect(t, eventstore: expectEventstore(
expectFilterError(caos_errs.ThrowInternal(nil, "id", "filter failed")), expectFilterError(caos_errs.ThrowInternal(nil, "id", "filter failed")),
), ),
}, },
@ -996,11 +995,11 @@ func TestCommands_TerminateSession(t *testing.T) {
{ {
"invalid session token", "invalid session token",
fields{ fields{
eventstore: eventstoreExpect(t, eventstore: expectEventstore(
expectFilter( expectFilter(
eventFromEventPusher( eventFromEventPusher(
session.NewAddedEvent(context.Background(), session.NewAddedEvent(context.Background(),
&session.NewAggregate("sessionID", "org1").Aggregate, &session.NewAggregate("sessionID", "instance1").Aggregate,
&domain.UserAgent{ &domain.UserAgent{
FingerprintID: gu.Ptr("fp1"), FingerprintID: gu.Ptr("fp1"),
IP: net.ParseIP("1.2.3.4"), IP: net.ParseIP("1.2.3.4"),
@ -1009,13 +1008,11 @@ func TestCommands_TerminateSession(t *testing.T) {
}, },
)), )),
eventFromEventPusher( eventFromEventPusher(
session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
"tokenID")), "tokenID")),
), ),
), ),
tokenVerifier: func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) { tokenVerifier: newMockTokenVerifierInvalid(),
return caos_errs.ThrowPermissionDenied(nil, "COMMAND-sGr42", "Errors.Session.Token.Invalid")
},
}, },
args{ args{
ctx: context.Background(), ctx: context.Background(),
@ -1027,13 +1024,13 @@ func TestCommands_TerminateSession(t *testing.T) {
}, },
}, },
{ {
"not active", "missing permission",
fields{ fields{
eventstore: eventstoreExpect(t, eventstore: expectEventstore(
expectFilter( expectFilter(
eventFromEventPusher( eventFromEventPusher(
session.NewAddedEvent(context.Background(), session.NewAddedEvent(context.Background(),
&session.NewAggregate("sessionID", "org1").Aggregate, &session.NewAggregate("sessionID", "instance1").Aggregate,
&domain.UserAgent{ &domain.UserAgent{
FingerprintID: gu.Ptr("fp1"), FingerprintID: gu.Ptr("fp1"),
IP: net.ParseIP("1.2.3.4"), IP: net.ParseIP("1.2.3.4"),
@ -1042,10 +1039,41 @@ func TestCommands_TerminateSession(t *testing.T) {
}, },
)), )),
eventFromEventPusher( eventFromEventPusher(
session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
"tokenID")),
),
),
checkPermission: newMockPermissionCheckNotAllowed(),
},
args{
ctx: context.Background(),
sessionID: "sessionID",
sessionToken: "",
},
res{
err: caos_errs.ThrowPermissionDenied(nil, "AUTHZ-HKJD33", "Errors.PermissionDenied"),
},
},
{
"not active",
fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
session.NewAddedEvent(context.Background(),
&session.NewAggregate("sessionID", "instance1").Aggregate,
&domain.UserAgent{
FingerprintID: gu.Ptr("fp1"),
IP: net.ParseIP("1.2.3.4"),
Description: gu.Ptr("firefox"),
Header: http.Header{"foo": []string{"bar"}},
},
)),
eventFromEventPusher(
session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
"tokenID")), "tokenID")),
eventFromEventPusher( eventFromEventPusher(
session.NewTerminateEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate)), session.NewTerminateEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate)),
), ),
), ),
tokenVerifier: func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) { tokenVerifier: func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) {
@ -1059,18 +1087,18 @@ func TestCommands_TerminateSession(t *testing.T) {
}, },
res{ res{
want: &domain.ObjectDetails{ want: &domain.ObjectDetails{
ResourceOwner: "org1", ResourceOwner: "instance1",
}, },
}, },
}, },
{ {
"push failed", "push failed",
fields{ fields{
eventstore: eventstoreExpect(t, eventstore: expectEventstore(
expectFilter( expectFilter(
eventFromEventPusher( eventFromEventPusher(
session.NewAddedEvent(context.Background(), session.NewAddedEvent(context.Background(),
&session.NewAggregate("sessionID", "org1").Aggregate, &session.NewAggregate("sessionID", "instance1").Aggregate,
&domain.UserAgent{ &domain.UserAgent{
FingerprintID: gu.Ptr("fp1"), FingerprintID: gu.Ptr("fp1"),
IP: net.ParseIP("1.2.3.4"), IP: net.ParseIP("1.2.3.4"),
@ -1079,13 +1107,13 @@ func TestCommands_TerminateSession(t *testing.T) {
}, },
)), )),
eventFromEventPusher( eventFromEventPusher(
session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
"tokenID"), "tokenID"),
), ),
), ),
expectPushFailed( expectPushFailed(
caos_errs.ThrowInternal(nil, "id", "pushed failed"), caos_errs.ThrowInternal(nil, "id", "pushed failed"),
session.NewTerminateEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate), session.NewTerminateEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate),
), ),
), ),
tokenVerifier: func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) { tokenVerifier: func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) {
@ -1102,13 +1130,13 @@ func TestCommands_TerminateSession(t *testing.T) {
}, },
}, },
{ {
"terminate", "terminate with token",
fields{ fields{
eventstore: eventstoreExpect(t, eventstore: expectEventstore(
expectFilter( expectFilter(
eventFromEventPusher( eventFromEventPusher(
session.NewAddedEvent(context.Background(), session.NewAddedEvent(context.Background(),
&session.NewAggregate("sessionID", "org1").Aggregate, &session.NewAggregate("sessionID", "instance1").Aggregate,
&domain.UserAgent{ &domain.UserAgent{
FingerprintID: gu.Ptr("fp1"), FingerprintID: gu.Ptr("fp1"),
IP: net.ParseIP("1.2.3.4"), IP: net.ParseIP("1.2.3.4"),
@ -1117,12 +1145,12 @@ func TestCommands_TerminateSession(t *testing.T) {
}, },
)), )),
eventFromEventPusher( eventFromEventPusher(
session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
"tokenID"), "tokenID"),
), ),
), ),
expectPush( expectPush(
session.NewTerminateEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate), session.NewTerminateEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate),
), ),
), ),
tokenVerifier: func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) { tokenVerifier: func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) {
@ -1136,7 +1164,90 @@ func TestCommands_TerminateSession(t *testing.T) {
}, },
res{ res{
want: &domain.ObjectDetails{ want: &domain.ObjectDetails{
ResourceOwner: "org1", ResourceOwner: "instance1",
},
},
},
{
"terminate own session",
fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
session.NewAddedEvent(context.Background(),
&session.NewAggregate("sessionID", "instance1").Aggregate,
&domain.UserAgent{
FingerprintID: gu.Ptr("fp1"),
IP: net.ParseIP("1.2.3.4"),
Description: gu.Ptr("firefox"),
Header: http.Header{"foo": []string{"bar"}},
},
),
),
eventFromEventPusher(
session.NewUserCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
"user1", "org1", testNow),
),
eventFromEventPusher(
session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
"tokenID"),
),
),
expectPush(
session.NewTerminateEvent(authz.NewMockContext("instance1", "org1", "user1"), &session.NewAggregate("sessionID", "instance1").Aggregate),
),
),
},
args{
ctx: authz.NewMockContext("instance1", "org1", "user1"),
sessionID: "sessionID",
sessionToken: "",
},
res{
want: &domain.ObjectDetails{
ResourceOwner: "instance1",
},
},
},
{
"terminate with permission",
fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
session.NewAddedEvent(context.Background(),
&session.NewAggregate("sessionID", "instance1").Aggregate,
&domain.UserAgent{
FingerprintID: gu.Ptr("fp1"),
IP: net.ParseIP("1.2.3.4"),
Description: gu.Ptr("firefox"),
Header: http.Header{"foo": []string{"bar"}},
},
),
),
eventFromEventPusher(
session.NewUserCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
"userID", "org1", testNow),
),
eventFromEventPusher(
session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
"tokenID"),
),
),
expectPush(
session.NewTerminateEvent(authz.NewMockContext("instance1", "org1", "admin1"), &session.NewAggregate("sessionID", "instance1").Aggregate),
),
),
checkPermission: newMockPermissionCheckAllowed(),
},
args{
ctx: authz.NewMockContext("instance1", "org1", "admin1"),
sessionID: "sessionID",
sessionToken: "",
},
res{
want: &domain.ObjectDetails{
ResourceOwner: "instance1",
}, },
}, },
}, },
@ -1144,8 +1255,9 @@ func TestCommands_TerminateSession(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
c := &Commands{ c := &Commands{
eventstore: tt.fields.eventstore, eventstore: tt.fields.eventstore(t),
sessionTokenVerifier: tt.fields.tokenVerifier, sessionTokenVerifier: tt.fields.tokenVerifier,
checkPermission: tt.fields.checkPermission,
} }
got, err := c.TerminateSession(tt.args.ctx, tt.args.sessionID, tt.args.sessionToken) got, err := c.TerminateSession(tt.args.ctx, tt.args.sessionID, tt.args.sessionToken)
require.ErrorIs(t, err, tt.res.err) require.ErrorIs(t, err, tt.res.err)

View File

@ -29,9 +29,9 @@ func (s *SessionCommands) getHumanWebAuthNTokens(ctx context.Context, userVerifi
} }
func (s *SessionCommands) getHumanWebAuthNTokenReadModel(ctx context.Context, userVerification domain.UserVerificationRequirement) (readModel HumanWebAuthNTokensReadModel, err error) { func (s *SessionCommands) getHumanWebAuthNTokenReadModel(ctx context.Context, userVerification domain.UserVerificationRequirement) (readModel HumanWebAuthNTokensReadModel, err error) {
readModel = NewHumanU2FTokensReadModel(s.sessionWriteModel.UserID, "") readModel = NewHumanU2FTokensReadModel(s.sessionWriteModel.UserID, s.sessionWriteModel.UserResourceOwner)
if userVerification == domain.UserVerificationRequirementRequired { if userVerification == domain.UserVerificationRequirementRequired {
readModel = NewHumanPasswordlessTokensReadModel(s.sessionWriteModel.UserID, "") readModel = NewHumanPasswordlessTokensReadModel(s.sessionWriteModel.UserID, s.sessionWriteModel.UserResourceOwner)
} }
err = s.eventstore.FilterToQueryReducer(ctx, readModel) err = s.eventstore.FilterToQueryReducer(ctx, readModel)
if err != nil { if err != nil {

View File

@ -14,7 +14,7 @@ import (
) )
const ( const (
SessionsProjectionTable = "projections.sessions7" SessionsProjectionTable = "projections.sessions8"
SessionColumnID = "id" SessionColumnID = "id"
SessionColumnCreationDate = "creation_date" SessionColumnCreationDate = "creation_date"
@ -25,6 +25,7 @@ const (
SessionColumnInstanceID = "instance_id" SessionColumnInstanceID = "instance_id"
SessionColumnCreator = "creator" SessionColumnCreator = "creator"
SessionColumnUserID = "user_id" SessionColumnUserID = "user_id"
SessionColumnUserResourceOwner = "user_resource_owner"
SessionColumnUserCheckedAt = "user_checked_at" SessionColumnUserCheckedAt = "user_checked_at"
SessionColumnPasswordCheckedAt = "password_checked_at" SessionColumnPasswordCheckedAt = "password_checked_at"
SessionColumnIntentCheckedAt = "intent_checked_at" SessionColumnIntentCheckedAt = "intent_checked_at"
@ -64,6 +65,7 @@ func (*sessionProjection) Init() *old_handler.Check {
handler.NewColumn(SessionColumnInstanceID, handler.ColumnTypeText), handler.NewColumn(SessionColumnInstanceID, handler.ColumnTypeText),
handler.NewColumn(SessionColumnCreator, handler.ColumnTypeText), handler.NewColumn(SessionColumnCreator, handler.ColumnTypeText),
handler.NewColumn(SessionColumnUserID, handler.ColumnTypeText, handler.Nullable()), handler.NewColumn(SessionColumnUserID, handler.ColumnTypeText, handler.Nullable()),
handler.NewColumn(SessionColumnUserResourceOwner, handler.ColumnTypeText, handler.Nullable()),
handler.NewColumn(SessionColumnUserCheckedAt, handler.ColumnTypeTimestamp, handler.Nullable()), handler.NewColumn(SessionColumnUserCheckedAt, handler.ColumnTypeTimestamp, handler.Nullable()),
handler.NewColumn(SessionColumnPasswordCheckedAt, handler.ColumnTypeTimestamp, handler.Nullable()), handler.NewColumn(SessionColumnPasswordCheckedAt, handler.ColumnTypeTimestamp, handler.Nullable()),
handler.NewColumn(SessionColumnIntentCheckedAt, handler.ColumnTypeTimestamp, handler.Nullable()), handler.NewColumn(SessionColumnIntentCheckedAt, handler.ColumnTypeTimestamp, handler.Nullable()),
@ -213,6 +215,7 @@ func (p *sessionProjection) reduceUserChecked(event eventstore.Event) (*handler.
handler.NewCol(SessionColumnChangeDate, e.CreationDate()), handler.NewCol(SessionColumnChangeDate, e.CreationDate()),
handler.NewCol(SessionColumnSequence, e.Sequence()), handler.NewCol(SessionColumnSequence, e.Sequence()),
handler.NewCol(SessionColumnUserID, e.UserID), handler.NewCol(SessionColumnUserID, e.UserID),
handler.NewCol(SessionColumnUserResourceOwner, e.UserResourceOwner),
handler.NewCol(SessionColumnUserCheckedAt, e.CheckedAt), handler.NewCol(SessionColumnUserCheckedAt, e.CheckedAt),
}, },
[]handler.Condition{ []handler.Condition{
@ -430,6 +433,7 @@ func (p *sessionProjection) reducePasswordChanged(event eventstore.Event) (*hand
}, },
[]handler.Condition{ []handler.Condition{
handler.NewCond(SessionColumnUserID, e.Aggregate().ID), handler.NewCond(SessionColumnUserID, e.Aggregate().ID),
handler.NewCond(SessionColumnInstanceID, e.Aggregate().InstanceID),
handler.NewLessThanCond(SessionColumnPasswordCheckedAt, e.CreationDate()), handler.NewLessThanCond(SessionColumnPasswordCheckedAt, e.CreationDate()),
}, },
), nil ), nil

View File

@ -51,7 +51,7 @@ func TestSessionProjection_reduces(t *testing.T) {
executer: &testExecuter{ executer: &testExecuter{
executions: []execution{ executions: []execution{
{ {
expectedStmt: "INSERT INTO projections.sessions7 (id, instance_id, creation_date, change_date, resource_owner, state, sequence, creator, user_agent_fingerprint_id, user_agent_description, user_agent_ip, user_agent_header) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)", expectedStmt: "INSERT INTO projections.sessions8 (id, instance_id, creation_date, change_date, resource_owner, state, sequence, creator, user_agent_fingerprint_id, user_agent_description, user_agent_ip, user_agent_header) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)",
expectedArgs: []interface{}{ expectedArgs: []interface{}{
"agg-id", "agg-id",
"instance-id", "instance-id",
@ -79,6 +79,7 @@ func TestSessionProjection_reduces(t *testing.T) {
session.AggregateType, session.AggregateType,
[]byte(`{ []byte(`{
"userId": "user-id", "userId": "user-id",
"userResourceOwner": "org-id",
"checkedAt": "2023-05-04T00:00:00Z" "checkedAt": "2023-05-04T00:00:00Z"
}`), }`),
), session.UserCheckedEventMapper), ), session.UserCheckedEventMapper),
@ -90,11 +91,12 @@ func TestSessionProjection_reduces(t *testing.T) {
executer: &testExecuter{ executer: &testExecuter{
executions: []execution{ executions: []execution{
{ {
expectedStmt: "UPDATE projections.sessions7 SET (change_date, sequence, user_id, user_checked_at) = ($1, $2, $3, $4) WHERE (id = $5) AND (instance_id = $6)", expectedStmt: "UPDATE projections.sessions8 SET (change_date, sequence, user_id, user_resource_owner, user_checked_at) = ($1, $2, $3, $4, $5) WHERE (id = $6) AND (instance_id = $7)",
expectedArgs: []interface{}{ expectedArgs: []interface{}{
anyArg{}, anyArg{},
anyArg{}, anyArg{},
"user-id", "user-id",
"org-id",
time.Date(2023, time.May, 4, 0, 0, 0, 0, time.UTC), time.Date(2023, time.May, 4, 0, 0, 0, 0, time.UTC),
"agg-id", "agg-id",
"instance-id", "instance-id",
@ -122,7 +124,7 @@ func TestSessionProjection_reduces(t *testing.T) {
executer: &testExecuter{ executer: &testExecuter{
executions: []execution{ executions: []execution{
{ {
expectedStmt: "UPDATE projections.sessions7 SET (change_date, sequence, password_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedStmt: "UPDATE projections.sessions8 SET (change_date, sequence, password_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{ expectedArgs: []interface{}{
anyArg{}, anyArg{},
anyArg{}, anyArg{},
@ -154,7 +156,7 @@ func TestSessionProjection_reduces(t *testing.T) {
executer: &testExecuter{ executer: &testExecuter{
executions: []execution{ executions: []execution{
{ {
expectedStmt: "UPDATE projections.sessions7 SET (change_date, sequence, webauthn_checked_at, webauthn_user_verified) = ($1, $2, $3, $4) WHERE (id = $5) AND (instance_id = $6)", expectedStmt: "UPDATE projections.sessions8 SET (change_date, sequence, webauthn_checked_at, webauthn_user_verified) = ($1, $2, $3, $4) WHERE (id = $5) AND (instance_id = $6)",
expectedArgs: []interface{}{ expectedArgs: []interface{}{
anyArg{}, anyArg{},
anyArg{}, anyArg{},
@ -186,7 +188,7 @@ func TestSessionProjection_reduces(t *testing.T) {
executer: &testExecuter{ executer: &testExecuter{
executions: []execution{ executions: []execution{
{ {
expectedStmt: "UPDATE projections.sessions7 SET (change_date, sequence, intent_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedStmt: "UPDATE projections.sessions8 SET (change_date, sequence, intent_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{ expectedArgs: []interface{}{
anyArg{}, anyArg{},
anyArg{}, anyArg{},
@ -217,7 +219,7 @@ func TestSessionProjection_reduces(t *testing.T) {
executer: &testExecuter{ executer: &testExecuter{
executions: []execution{ executions: []execution{
{ {
expectedStmt: "UPDATE projections.sessions7 SET (change_date, sequence, totp_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedStmt: "UPDATE projections.sessions8 SET (change_date, sequence, totp_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{ expectedArgs: []interface{}{
anyArg{}, anyArg{},
anyArg{}, anyArg{},
@ -248,7 +250,7 @@ func TestSessionProjection_reduces(t *testing.T) {
executer: &testExecuter{ executer: &testExecuter{
executions: []execution{ executions: []execution{
{ {
expectedStmt: "UPDATE projections.sessions7 SET (change_date, sequence, token_id) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedStmt: "UPDATE projections.sessions8 SET (change_date, sequence, token_id) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{ expectedArgs: []interface{}{
anyArg{}, anyArg{},
anyArg{}, anyArg{},
@ -281,7 +283,7 @@ func TestSessionProjection_reduces(t *testing.T) {
executer: &testExecuter{ executer: &testExecuter{
executions: []execution{ executions: []execution{
{ {
expectedStmt: "UPDATE projections.sessions7 SET (change_date, sequence, metadata) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedStmt: "UPDATE projections.sessions8 SET (change_date, sequence, metadata) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{ expectedArgs: []interface{}{
anyArg{}, anyArg{},
anyArg{}, anyArg{},
@ -314,7 +316,7 @@ func TestSessionProjection_reduces(t *testing.T) {
executer: &testExecuter{ executer: &testExecuter{
executions: []execution{ executions: []execution{
{ {
expectedStmt: "UPDATE projections.sessions7 SET (change_date, sequence, expiration) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedStmt: "UPDATE projections.sessions8 SET (change_date, sequence, expiration) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{ expectedArgs: []interface{}{
anyArg{}, anyArg{},
anyArg{}, anyArg{},
@ -343,7 +345,7 @@ func TestSessionProjection_reduces(t *testing.T) {
executer: &testExecuter{ executer: &testExecuter{
executions: []execution{ executions: []execution{
{ {
expectedStmt: "DELETE FROM projections.sessions7 WHERE (id = $1) AND (instance_id = $2)", expectedStmt: "DELETE FROM projections.sessions8 WHERE (id = $1) AND (instance_id = $2)",
expectedArgs: []interface{}{ expectedArgs: []interface{}{
"agg-id", "agg-id",
"instance-id", "instance-id",
@ -370,7 +372,7 @@ func TestSessionProjection_reduces(t *testing.T) {
executer: &testExecuter{ executer: &testExecuter{
executions: []execution{ executions: []execution{
{ {
expectedStmt: "DELETE FROM projections.sessions7 WHERE (instance_id = $1)", expectedStmt: "DELETE FROM projections.sessions8 WHERE (instance_id = $1)",
expectedArgs: []interface{}{ expectedArgs: []interface{}{
"agg-id", "agg-id",
}, },
@ -400,10 +402,11 @@ func TestSessionProjection_reduces(t *testing.T) {
executer: &testExecuter{ executer: &testExecuter{
executions: []execution{ executions: []execution{
{ {
expectedStmt: "UPDATE projections.sessions7 SET password_checked_at = $1 WHERE (user_id = $2) AND (password_checked_at < $3)", expectedStmt: "UPDATE projections.sessions8 SET password_checked_at = $1 WHERE (user_id = $2) AND (instance_id = $3) AND (password_checked_at < $4)",
expectedArgs: []interface{}{ expectedArgs: []interface{}{
nil, nil,
"agg-id", "agg-id",
"instance-id",
anyArg{}, anyArg{},
}, },
}, },

View File

@ -130,6 +130,10 @@ var (
name: projection.SessionColumnUserID, name: projection.SessionColumnUserID,
table: sessionsTable, table: sessionsTable,
} }
SessionColumnUserResourceOwner = Column{
name: projection.SessionColumnUserResourceOwner,
table: sessionsTable,
}
SessionColumnUserCheckedAt = Column{ SessionColumnUserCheckedAt = Column{
name: projection.SessionColumnUserCheckedAt, name: projection.SessionColumnUserCheckedAt,
table: sessionsTable, table: sessionsTable,
@ -287,10 +291,10 @@ func prepareSessionQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuil
SessionColumnResourceOwner.identifier(), SessionColumnResourceOwner.identifier(),
SessionColumnCreator.identifier(), SessionColumnCreator.identifier(),
SessionColumnUserID.identifier(), SessionColumnUserID.identifier(),
SessionColumnUserResourceOwner.identifier(),
SessionColumnUserCheckedAt.identifier(), SessionColumnUserCheckedAt.identifier(),
LoginNameNameCol.identifier(), LoginNameNameCol.identifier(),
HumanDisplayNameCol.identifier(), HumanDisplayNameCol.identifier(),
UserResourceOwnerCol.identifier(),
SessionColumnPasswordCheckedAt.identifier(), SessionColumnPasswordCheckedAt.identifier(),
SessionColumnIntentCheckedAt.identifier(), SessionColumnIntentCheckedAt.identifier(),
SessionColumnWebAuthNCheckedAt.identifier(), SessionColumnWebAuthNCheckedAt.identifier(),
@ -314,10 +318,10 @@ func prepareSessionQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuil
var ( var (
userID sql.NullString userID sql.NullString
userResourceOwner sql.NullString
userCheckedAt sql.NullTime userCheckedAt sql.NullTime
loginName sql.NullString loginName sql.NullString
displayName sql.NullString displayName sql.NullString
userResourceOwner sql.NullString
passwordCheckedAt sql.NullTime passwordCheckedAt sql.NullTime
intentCheckedAt sql.NullTime intentCheckedAt sql.NullTime
webAuthNCheckedAt sql.NullTime webAuthNCheckedAt sql.NullTime
@ -341,10 +345,10 @@ func prepareSessionQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuil
&session.ResourceOwner, &session.ResourceOwner,
&session.Creator, &session.Creator,
&userID, &userID,
&userResourceOwner,
&userCheckedAt, &userCheckedAt,
&loginName, &loginName,
&displayName, &displayName,
&userResourceOwner,
&passwordCheckedAt, &passwordCheckedAt,
&intentCheckedAt, &intentCheckedAt,
&webAuthNCheckedAt, &webAuthNCheckedAt,
@ -369,10 +373,10 @@ func prepareSessionQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuil
} }
session.UserFactor.UserID = userID.String session.UserFactor.UserID = userID.String
session.UserFactor.ResourceOwner = userResourceOwner.String
session.UserFactor.UserCheckedAt = userCheckedAt.Time session.UserFactor.UserCheckedAt = userCheckedAt.Time
session.UserFactor.LoginName = loginName.String session.UserFactor.LoginName = loginName.String
session.UserFactor.DisplayName = displayName.String session.UserFactor.DisplayName = displayName.String
session.UserFactor.ResourceOwner = userResourceOwner.String
session.PasswordFactor.PasswordCheckedAt = passwordCheckedAt.Time session.PasswordFactor.PasswordCheckedAt = passwordCheckedAt.Time
session.IntentFactor.IntentCheckedAt = intentCheckedAt.Time session.IntentFactor.IntentCheckedAt = intentCheckedAt.Time
session.WebAuthNFactor.WebAuthNCheckedAt = webAuthNCheckedAt.Time session.WebAuthNFactor.WebAuthNCheckedAt = webAuthNCheckedAt.Time
@ -400,10 +404,10 @@ func prepareSessionsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBui
SessionColumnResourceOwner.identifier(), SessionColumnResourceOwner.identifier(),
SessionColumnCreator.identifier(), SessionColumnCreator.identifier(),
SessionColumnUserID.identifier(), SessionColumnUserID.identifier(),
SessionColumnUserResourceOwner.identifier(),
SessionColumnUserCheckedAt.identifier(), SessionColumnUserCheckedAt.identifier(),
LoginNameNameCol.identifier(), LoginNameNameCol.identifier(),
HumanDisplayNameCol.identifier(), HumanDisplayNameCol.identifier(),
UserResourceOwnerCol.identifier(),
SessionColumnPasswordCheckedAt.identifier(), SessionColumnPasswordCheckedAt.identifier(),
SessionColumnIntentCheckedAt.identifier(), SessionColumnIntentCheckedAt.identifier(),
SessionColumnWebAuthNCheckedAt.identifier(), SessionColumnWebAuthNCheckedAt.identifier(),
@ -426,10 +430,10 @@ func prepareSessionsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBui
var ( var (
userID sql.NullString userID sql.NullString
userResourceOwner sql.NullString
userCheckedAt sql.NullTime userCheckedAt sql.NullTime
loginName sql.NullString loginName sql.NullString
displayName sql.NullString displayName sql.NullString
userResourceOwner sql.NullString
passwordCheckedAt sql.NullTime passwordCheckedAt sql.NullTime
intentCheckedAt sql.NullTime intentCheckedAt sql.NullTime
webAuthNCheckedAt sql.NullTime webAuthNCheckedAt sql.NullTime
@ -450,10 +454,10 @@ func prepareSessionsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBui
&session.ResourceOwner, &session.ResourceOwner,
&session.Creator, &session.Creator,
&userID, &userID,
&userResourceOwner,
&userCheckedAt, &userCheckedAt,
&loginName, &loginName,
&displayName, &displayName,
&userResourceOwner,
&passwordCheckedAt, &passwordCheckedAt,
&intentCheckedAt, &intentCheckedAt,
&webAuthNCheckedAt, &webAuthNCheckedAt,
@ -470,10 +474,10 @@ func prepareSessionsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBui
return nil, errors.ThrowInternal(err, "QUERY-SAfeg", "Errors.Internal") return nil, errors.ThrowInternal(err, "QUERY-SAfeg", "Errors.Internal")
} }
session.UserFactor.UserID = userID.String session.UserFactor.UserID = userID.String
session.UserFactor.ResourceOwner = userResourceOwner.String
session.UserFactor.UserCheckedAt = userCheckedAt.Time session.UserFactor.UserCheckedAt = userCheckedAt.Time
session.UserFactor.LoginName = loginName.String session.UserFactor.LoginName = loginName.String
session.UserFactor.DisplayName = displayName.String session.UserFactor.DisplayName = displayName.String
session.UserFactor.ResourceOwner = userResourceOwner.String
session.PasswordFactor.PasswordCheckedAt = passwordCheckedAt.Time session.PasswordFactor.PasswordCheckedAt = passwordCheckedAt.Time
session.IntentFactor.IntentCheckedAt = intentCheckedAt.Time session.IntentFactor.IntentCheckedAt = intentCheckedAt.Time
session.WebAuthNFactor.WebAuthNCheckedAt = webAuthNCheckedAt.Time session.WebAuthNFactor.WebAuthNCheckedAt = webAuthNCheckedAt.Time

View File

@ -20,63 +20,63 @@ import (
) )
var ( var (
expectedSessionQuery = regexp.QuoteMeta(`SELECT projections.sessions7.id,` + expectedSessionQuery = regexp.QuoteMeta(`SELECT projections.sessions8.id,` +
` projections.sessions7.creation_date,` + ` projections.sessions8.creation_date,` +
` projections.sessions7.change_date,` + ` projections.sessions8.change_date,` +
` projections.sessions7.sequence,` + ` projections.sessions8.sequence,` +
` projections.sessions7.state,` + ` projections.sessions8.state,` +
` projections.sessions7.resource_owner,` + ` projections.sessions8.resource_owner,` +
` projections.sessions7.creator,` + ` projections.sessions8.creator,` +
` projections.sessions7.user_id,` + ` projections.sessions8.user_id,` +
` projections.sessions7.user_checked_at,` + ` projections.sessions8.user_resource_owner,` +
` projections.sessions8.user_checked_at,` +
` projections.login_names2.login_name,` + ` projections.login_names2.login_name,` +
` projections.users8_humans.display_name,` + ` projections.users8_humans.display_name,` +
` projections.users8.resource_owner,` + ` projections.sessions8.password_checked_at,` +
` projections.sessions7.password_checked_at,` + ` projections.sessions8.intent_checked_at,` +
` projections.sessions7.intent_checked_at,` + ` projections.sessions8.webauthn_checked_at,` +
` projections.sessions7.webauthn_checked_at,` + ` projections.sessions8.webauthn_user_verified,` +
` projections.sessions7.webauthn_user_verified,` + ` projections.sessions8.totp_checked_at,` +
` projections.sessions7.totp_checked_at,` + ` projections.sessions8.otp_sms_checked_at,` +
` projections.sessions7.otp_sms_checked_at,` + ` projections.sessions8.otp_email_checked_at,` +
` projections.sessions7.otp_email_checked_at,` + ` projections.sessions8.metadata,` +
` projections.sessions7.metadata,` + ` projections.sessions8.token_id,` +
` projections.sessions7.token_id,` + ` projections.sessions8.user_agent_fingerprint_id,` +
` projections.sessions7.user_agent_fingerprint_id,` + ` projections.sessions8.user_agent_ip,` +
` projections.sessions7.user_agent_ip,` + ` projections.sessions8.user_agent_description,` +
` projections.sessions7.user_agent_description,` + ` projections.sessions8.user_agent_header,` +
` projections.sessions7.user_agent_header,` + ` projections.sessions8.expiration` +
` projections.sessions7.expiration` + ` FROM projections.sessions8` +
` FROM projections.sessions7` + ` LEFT JOIN projections.login_names2 ON projections.sessions8.user_id = projections.login_names2.user_id AND projections.sessions8.instance_id = projections.login_names2.instance_id` +
` LEFT JOIN projections.login_names2 ON projections.sessions7.user_id = projections.login_names2.user_id AND projections.sessions7.instance_id = projections.login_names2.instance_id` + ` LEFT JOIN projections.users8_humans ON projections.sessions8.user_id = projections.users8_humans.user_id AND projections.sessions8.instance_id = projections.users8_humans.instance_id` +
` LEFT JOIN projections.users8_humans ON projections.sessions7.user_id = projections.users8_humans.user_id AND projections.sessions7.instance_id = projections.users8_humans.instance_id` + ` LEFT JOIN projections.users8 ON projections.sessions8.user_id = projections.users8.id AND projections.sessions8.instance_id = projections.users8.instance_id` +
` LEFT JOIN projections.users8 ON projections.sessions7.user_id = projections.users8.id AND projections.sessions7.instance_id = projections.users8.instance_id` +
` AS OF SYSTEM TIME '-1 ms'`) ` AS OF SYSTEM TIME '-1 ms'`)
expectedSessionsQuery = regexp.QuoteMeta(`SELECT projections.sessions7.id,` + expectedSessionsQuery = regexp.QuoteMeta(`SELECT projections.sessions8.id,` +
` projections.sessions7.creation_date,` + ` projections.sessions8.creation_date,` +
` projections.sessions7.change_date,` + ` projections.sessions8.change_date,` +
` projections.sessions7.sequence,` + ` projections.sessions8.sequence,` +
` projections.sessions7.state,` + ` projections.sessions8.state,` +
` projections.sessions7.resource_owner,` + ` projections.sessions8.resource_owner,` +
` projections.sessions7.creator,` + ` projections.sessions8.creator,` +
` projections.sessions7.user_id,` + ` projections.sessions8.user_id,` +
` projections.sessions7.user_checked_at,` + ` projections.sessions8.user_resource_owner,` +
` projections.sessions8.user_checked_at,` +
` projections.login_names2.login_name,` + ` projections.login_names2.login_name,` +
` projections.users8_humans.display_name,` + ` projections.users8_humans.display_name,` +
` projections.users8.resource_owner,` + ` projections.sessions8.password_checked_at,` +
` projections.sessions7.password_checked_at,` + ` projections.sessions8.intent_checked_at,` +
` projections.sessions7.intent_checked_at,` + ` projections.sessions8.webauthn_checked_at,` +
` projections.sessions7.webauthn_checked_at,` + ` projections.sessions8.webauthn_user_verified,` +
` projections.sessions7.webauthn_user_verified,` + ` projections.sessions8.totp_checked_at,` +
` projections.sessions7.totp_checked_at,` + ` projections.sessions8.otp_sms_checked_at,` +
` projections.sessions7.otp_sms_checked_at,` + ` projections.sessions8.otp_email_checked_at,` +
` projections.sessions7.otp_email_checked_at,` + ` projections.sessions8.metadata,` +
` projections.sessions7.metadata,` + ` projections.sessions8.expiration,` +
` projections.sessions7.expiration,` +
` COUNT(*) OVER ()` + ` COUNT(*) OVER ()` +
` FROM projections.sessions7` + ` FROM projections.sessions8` +
` LEFT JOIN projections.login_names2 ON projections.sessions7.user_id = projections.login_names2.user_id AND projections.sessions7.instance_id = projections.login_names2.instance_id` + ` LEFT JOIN projections.login_names2 ON projections.sessions8.user_id = projections.login_names2.user_id AND projections.sessions8.instance_id = projections.login_names2.instance_id` +
` LEFT JOIN projections.users8_humans ON projections.sessions7.user_id = projections.users8_humans.user_id AND projections.sessions7.instance_id = projections.users8_humans.instance_id` + ` LEFT JOIN projections.users8_humans ON projections.sessions8.user_id = projections.users8_humans.user_id AND projections.sessions8.instance_id = projections.users8_humans.instance_id` +
` LEFT JOIN projections.users8 ON projections.sessions7.user_id = projections.users8.id AND projections.sessions7.instance_id = projections.users8.instance_id` + ` LEFT JOIN projections.users8 ON projections.sessions8.user_id = projections.users8.id AND projections.sessions8.instance_id = projections.users8.instance_id` +
` AS OF SYSTEM TIME '-1 ms'`) ` AS OF SYSTEM TIME '-1 ms'`)
sessionCols = []string{ sessionCols = []string{
@ -88,10 +88,10 @@ var (
"resource_owner", "resource_owner",
"creator", "creator",
"user_id", "user_id",
"user_resource_owner",
"user_checked_at", "user_checked_at",
"login_name", "login_name",
"display_name", "display_name",
"user_resource_owner",
"password_checked_at", "password_checked_at",
"intent_checked_at", "intent_checked_at",
"webauthn_checked_at", "webauthn_checked_at",
@ -117,10 +117,10 @@ var (
"resource_owner", "resource_owner",
"creator", "creator",
"user_id", "user_id",
"user_resource_owner",
"user_checked_at", "user_checked_at",
"login_name", "login_name",
"display_name", "display_name",
"user_resource_owner",
"password_checked_at", "password_checked_at",
"intent_checked_at", "intent_checked_at",
"webauthn_checked_at", "webauthn_checked_at",
@ -174,10 +174,10 @@ func Test_SessionsPrepare(t *testing.T) {
"ro", "ro",
"creator", "creator",
"user-id", "user-id",
"resourceOwner",
testNow, testNow,
"login-name", "login-name",
"display-name", "display-name",
"resourceOwner",
testNow, testNow,
testNow, testNow,
testNow, testNow,
@ -255,10 +255,10 @@ func Test_SessionsPrepare(t *testing.T) {
"ro", "ro",
"creator", "creator",
"user-id", "user-id",
"resourceOwner",
testNow, testNow,
"login-name", "login-name",
"display-name", "display-name",
"resourceOwner",
testNow, testNow,
testNow, testNow,
testNow, testNow,
@ -278,10 +278,10 @@ func Test_SessionsPrepare(t *testing.T) {
"ro", "ro",
"creator2", "creator2",
"user-id2", "user-id2",
"resourceOwner",
testNow, testNow,
"login-name2", "login-name2",
"display-name2", "display-name2",
"resourceOwner",
testNow, testNow,
testNow, testNow,
testNow, testNow,
@ -451,10 +451,10 @@ func Test_SessionPrepare(t *testing.T) {
"ro", "ro",
"creator", "creator",
"user-id", "user-id",
"resourceOwner",
testNow, testNow,
"login-name", "login-name",
"display-name", "display-name",
"resourceOwner",
testNow, testNow,
testNow, testNow,
testNow, testNow,

View File

@ -75,8 +75,9 @@ func AddedEventMapper(event eventstore.Event) (eventstore.Event, error) {
type UserCheckedEvent struct { type UserCheckedEvent struct {
eventstore.BaseEvent `json:"-"` eventstore.BaseEvent `json:"-"`
UserID string `json:"userID"` UserID string `json:"userID"`
CheckedAt time.Time `json:"checkedAt"` UserResourceOwner string `json:"userResourceOwner"`
CheckedAt time.Time `json:"checkedAt"`
} }
func (e *UserCheckedEvent) Payload() interface{} { func (e *UserCheckedEvent) Payload() interface{} {
@ -90,7 +91,8 @@ func (e *UserCheckedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
func NewUserCheckedEvent( func NewUserCheckedEvent(
ctx context.Context, ctx context.Context,
aggregate *eventstore.Aggregate, aggregate *eventstore.Aggregate,
userID string, userID,
userResourceOwner string,
checkedAt time.Time, checkedAt time.Time,
) *UserCheckedEvent { ) *UserCheckedEvent {
return &UserCheckedEvent{ return &UserCheckedEvent{
@ -99,8 +101,9 @@ func NewUserCheckedEvent(
aggregate, aggregate,
UserCheckedType, UserCheckedType,
), ),
UserID: userID, UserID: userID,
CheckedAt: checkedAt, UserResourceOwner: userResourceOwner,
CheckedAt: checkedAt,
} }
} }

View File

@ -290,12 +290,12 @@ message CreateSessionResponse{
string session_id = 2 [ string session_id = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "\"id of the session\""; description: "\"id of the session\"";
example: "\"d654e6ba-70a3-48ef-a95d-37c8d8a7901a\""; example: "\"222430354126975533\"";
} }
]; ];
string session_token = 3 [ string session_token = 3 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "\"token of the session, which is required for further updates of the session or the request other resources\""; description: "\"The current token of the session, which is required for further updates of the session or the request other resources.\"";
} }
]; ];
Challenges challenges = 4; Challenges challenges = 4;
@ -308,7 +308,7 @@ message SetSessionRequest{
min_length: 1; min_length: 1;
max_length: 200; max_length: 200;
description: "\"id of the session to update\""; description: "\"id of the session to update\"";
example: "\"d654e6ba-70a3-48ef-a95d-37c8d8a7901a\""; example: "\"222430354126975533\"";
} }
]; ];
string session_token = 2 [ string session_token = 2 [
@ -316,7 +316,7 @@ message SetSessionRequest{
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
min_length: 1; min_length: 1;
max_length: 200; max_length: 200;
description: "\"token of the session, previously returned on the create / update request\""; description: "\"The current token of the session, previously returned on the create / update request.\"";
} }
]; ];
Checks checks = 3[ Checks checks = 3[
@ -342,7 +342,7 @@ message SetSessionResponse{
zitadel.object.v2beta.Details details = 1; zitadel.object.v2beta.Details details = 1;
string session_token = 2 [ string session_token = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "\"token of the session, which is required for further updates of the session or the request other resources\""; description: "\"The current token of the session, which is required for further updates of the session or to request other resources.\"";
} }
]; ];
Challenges challenges = 3; Challenges challenges = 3;
@ -355,12 +355,12 @@ message DeleteSessionRequest{
min_length: 1; min_length: 1;
max_length: 200; max_length: 200;
description: "\"id of the session to terminate\""; description: "\"id of the session to terminate\"";
example: "\"d654e6ba-70a3-48ef-a95d-37c8d8a7901a\""; example: "\"222430354126975533\"";
} }
]; ];
optional string session_token = 2 [ optional string session_token = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "\"token of the session, previously returned on the create / update request\""; description: "\"The current token of the session, previously returned on the create / update request. The token is required unless the authenticated user terminates the own session or is granted the `session.delete` permission.\"";
} }
]; ];
} }