mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 17:27:31 +00:00
Merge commit from fork
* fix: prevent intent token reuse and add expiry * fix duplicate * fix expiration
This commit is contained in:
@@ -81,6 +81,7 @@ type Commands struct {
|
||||
publicKeyLifetime time.Duration
|
||||
certificateLifetime time.Duration
|
||||
defaultSecretGenerators *SecretGenerators
|
||||
maxIdPIntentLifetime time.Duration
|
||||
|
||||
samlCertificateAndKeyGenerator func(id string) ([]byte, []byte, error)
|
||||
webKeyGenerator func(keyID string, alg crypto.EncryptionAlgorithm, genConfig crypto.WebKeyConfig) (encryptedPrivate *crypto.CryptoValue, public *jose.JSONWebKey, err error)
|
||||
@@ -152,6 +153,7 @@ func StartCommands(
|
||||
privateKeyLifetime: defaults.KeyConfig.PrivateKeyLifetime,
|
||||
publicKeyLifetime: defaults.KeyConfig.PublicKeyLifetime,
|
||||
certificateLifetime: defaults.KeyConfig.CertificateLifetime,
|
||||
maxIdPIntentLifetime: defaults.MaxIdPIntentLifetime,
|
||||
idpConfigEncryption: idpConfigEncryption,
|
||||
smtpEncryption: smtpEncryption,
|
||||
smsEncryption: smsEncryption,
|
||||
|
@@ -7,7 +7,6 @@ import (
|
||||
"encoding/xml"
|
||||
"net/url"
|
||||
|
||||
"github.com/crewjam/saml"
|
||||
"github.com/crewjam/saml/samlsp"
|
||||
"github.com/zitadel/oidc/v3/pkg/oidc"
|
||||
|
||||
@@ -19,8 +18,10 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/idp/providers/apple"
|
||||
"github.com/zitadel/zitadel/internal/idp/providers/azuread"
|
||||
"github.com/zitadel/zitadel/internal/idp/providers/jwt"
|
||||
"github.com/zitadel/zitadel/internal/idp/providers/ldap"
|
||||
"github.com/zitadel/zitadel/internal/idp/providers/oauth"
|
||||
openid "github.com/zitadel/zitadel/internal/idp/providers/oidc"
|
||||
"github.com/zitadel/zitadel/internal/idp/providers/saml"
|
||||
"github.com/zitadel/zitadel/internal/repository/idpintent"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
@@ -68,7 +69,7 @@ func (c *Commands) CreateIntent(ctx context.Context, intentID, idpID, successURL
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
writeModel := NewIDPIntentWriteModel(intentID, resourceOwner)
|
||||
writeModel := NewIDPIntentWriteModel(intentID, resourceOwner, c.maxIdPIntentLifetime)
|
||||
|
||||
//nolint: staticcheck
|
||||
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareCreateIntent(writeModel, idpID, successURL, failureURL, idpArguments))
|
||||
@@ -180,6 +181,7 @@ func (c *Commands) SucceedIDPIntent(ctx context.Context, writeModel *IDPIntentWr
|
||||
userID,
|
||||
accessToken,
|
||||
idToken,
|
||||
idpSession.ExpiresAt(),
|
||||
)
|
||||
err = c.pushAppendAndReduce(ctx, writeModel, cmd)
|
||||
if err != nil {
|
||||
@@ -188,7 +190,7 @@ func (c *Commands) SucceedIDPIntent(ctx context.Context, writeModel *IDPIntentWr
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (c *Commands) SucceedSAMLIDPIntent(ctx context.Context, writeModel *IDPIntentWriteModel, idpUser idp.User, userID string, assertion *saml.Assertion) (string, error) {
|
||||
func (c *Commands) SucceedSAMLIDPIntent(ctx context.Context, writeModel *IDPIntentWriteModel, idpUser idp.User, userID string, session *saml.Session) (string, error) {
|
||||
token, err := c.generateIntentToken(writeModel.AggregateID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -197,7 +199,7 @@ func (c *Commands) SucceedSAMLIDPIntent(ctx context.Context, writeModel *IDPInte
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
assertionData, err := xml.Marshal(assertion)
|
||||
assertionData, err := xml.Marshal(session.Assertion)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -213,6 +215,7 @@ func (c *Commands) SucceedSAMLIDPIntent(ctx context.Context, writeModel *IDPInte
|
||||
idpUser.GetPreferredUsername(),
|
||||
userID,
|
||||
assertionEnc,
|
||||
session.ExpiresAt(),
|
||||
)
|
||||
err = c.pushAppendAndReduce(ctx, writeModel, cmd)
|
||||
if err != nil {
|
||||
@@ -237,7 +240,7 @@ func (c *Commands) generateIntentToken(intentID string) (string, error) {
|
||||
return base64.RawURLEncoding.EncodeToString(token), nil
|
||||
}
|
||||
|
||||
func (c *Commands) SucceedLDAPIDPIntent(ctx context.Context, writeModel *IDPIntentWriteModel, idpUser idp.User, userID string, attributes map[string][]string) (string, error) {
|
||||
func (c *Commands) SucceedLDAPIDPIntent(ctx context.Context, writeModel *IDPIntentWriteModel, idpUser idp.User, userID string, session *ldap.Session) (string, error) {
|
||||
token, err := c.generateIntentToken(writeModel.AggregateID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -246,6 +249,10 @@ func (c *Commands) SucceedLDAPIDPIntent(ctx context.Context, writeModel *IDPInte
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
attributes := make(map[string][]string, len(session.Entry.Attributes))
|
||||
for _, item := range session.Entry.Attributes {
|
||||
attributes[item.Name] = item.Values
|
||||
}
|
||||
cmd := idpintent.NewLDAPSucceededEvent(
|
||||
ctx,
|
||||
IDPIntentAggregateFromWriteModel(&writeModel.WriteModel),
|
||||
@@ -254,6 +261,7 @@ func (c *Commands) SucceedLDAPIDPIntent(ctx context.Context, writeModel *IDPInte
|
||||
idpUser.GetPreferredUsername(),
|
||||
userID,
|
||||
attributes,
|
||||
session.ExpiresAt(),
|
||||
)
|
||||
err = c.pushAppendAndReduce(ctx, writeModel, cmd)
|
||||
if err != nil {
|
||||
@@ -273,7 +281,7 @@ func (c *Commands) FailIDPIntent(ctx context.Context, writeModel *IDPIntentWrite
|
||||
}
|
||||
|
||||
func (c *Commands) GetIntentWriteModel(ctx context.Context, id, resourceOwner string) (*IDPIntentWriteModel, error) {
|
||||
writeModel := NewIDPIntentWriteModel(id, resourceOwner)
|
||||
writeModel := NewIDPIntentWriteModel(id, resourceOwner, c.maxIdPIntentLifetime)
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, writeModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@@ -2,6 +2,7 @@ package command
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
@@ -29,18 +30,29 @@ type IDPIntentWriteModel struct {
|
||||
RequestID string
|
||||
Assertion *crypto.CryptoValue
|
||||
|
||||
State domain.IDPIntentState
|
||||
State domain.IDPIntentState
|
||||
succeededAt time.Time
|
||||
maxIdPIntentLifetime time.Duration
|
||||
expiresAt time.Time
|
||||
}
|
||||
|
||||
func NewIDPIntentWriteModel(id, resourceOwner string) *IDPIntentWriteModel {
|
||||
func NewIDPIntentWriteModel(id, resourceOwner string, maxIdPIntentLifetime time.Duration) *IDPIntentWriteModel {
|
||||
return &IDPIntentWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: id,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
maxIdPIntentLifetime: maxIdPIntentLifetime,
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *IDPIntentWriteModel) ExpiresAt() time.Time {
|
||||
if wm.expiresAt.IsZero() {
|
||||
return wm.succeededAt.Add(wm.maxIdPIntentLifetime)
|
||||
}
|
||||
return wm.expiresAt
|
||||
}
|
||||
|
||||
func (wm *IDPIntentWriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
@@ -56,6 +68,8 @@ func (wm *IDPIntentWriteModel) Reduce() error {
|
||||
wm.reduceLDAPSucceededEvent(e)
|
||||
case *idpintent.FailedEvent:
|
||||
wm.reduceFailedEvent(e)
|
||||
case *idpintent.ConsumedEvent:
|
||||
wm.reduceConsumedEvent(e)
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
@@ -74,6 +88,7 @@ func (wm *IDPIntentWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
idpintent.SAMLRequestEventType,
|
||||
idpintent.LDAPSucceededEventType,
|
||||
idpintent.FailedEventType,
|
||||
idpintent.ConsumedEventType,
|
||||
).
|
||||
Builder()
|
||||
}
|
||||
@@ -93,6 +108,8 @@ func (wm *IDPIntentWriteModel) reduceSAMLSucceededEvent(e *idpintent.SAMLSucceed
|
||||
wm.IDPUserName = e.IDPUserName
|
||||
wm.Assertion = e.Assertion
|
||||
wm.State = domain.IDPIntentStateSucceeded
|
||||
wm.succeededAt = e.CreationDate()
|
||||
wm.expiresAt = e.ExpiresAt
|
||||
}
|
||||
|
||||
func (wm *IDPIntentWriteModel) reduceLDAPSucceededEvent(e *idpintent.LDAPSucceededEvent) {
|
||||
@@ -102,6 +119,8 @@ func (wm *IDPIntentWriteModel) reduceLDAPSucceededEvent(e *idpintent.LDAPSucceed
|
||||
wm.IDPUserName = e.IDPUserName
|
||||
wm.IDPEntryAttributes = e.EntryAttributes
|
||||
wm.State = domain.IDPIntentStateSucceeded
|
||||
wm.succeededAt = e.CreationDate()
|
||||
wm.expiresAt = e.ExpiresAt
|
||||
}
|
||||
|
||||
func (wm *IDPIntentWriteModel) reduceOAuthSucceededEvent(e *idpintent.SucceededEvent) {
|
||||
@@ -112,6 +131,8 @@ func (wm *IDPIntentWriteModel) reduceOAuthSucceededEvent(e *idpintent.SucceededE
|
||||
wm.IDPAccessToken = e.IDPAccessToken
|
||||
wm.IDPIDToken = e.IDPIDToken
|
||||
wm.State = domain.IDPIntentStateSucceeded
|
||||
wm.succeededAt = e.CreationDate()
|
||||
wm.expiresAt = e.ExpiresAt
|
||||
}
|
||||
|
||||
func (wm *IDPIntentWriteModel) reduceSAMLRequestEvent(e *idpintent.SAMLRequestEvent) {
|
||||
@@ -122,6 +143,10 @@ func (wm *IDPIntentWriteModel) reduceFailedEvent(e *idpintent.FailedEvent) {
|
||||
wm.State = domain.IDPIntentStateFailed
|
||||
}
|
||||
|
||||
func (wm *IDPIntentWriteModel) reduceConsumedEvent(e *idpintent.ConsumedEvent) {
|
||||
wm.State = domain.IDPIntentStateConsumed
|
||||
}
|
||||
|
||||
func IDPIntentAggregateFromWriteModel(wm *eventstore.WriteModel) *eventstore.Aggregate {
|
||||
return &eventstore.Aggregate{
|
||||
Type: idpintent.AggregateType,
|
||||
|
@@ -4,8 +4,10 @@ import (
|
||||
"context"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/crewjam/saml"
|
||||
crewjam_saml "github.com/crewjam/saml"
|
||||
goldap "github.com/go-ldap/ldap/v3"
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -26,6 +28,7 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/idp/providers/ldap"
|
||||
"github.com/zitadel/zitadel/internal/idp/providers/oauth"
|
||||
openid "github.com/zitadel/zitadel/internal/idp/providers/oidc"
|
||||
"github.com/zitadel/zitadel/internal/idp/providers/saml"
|
||||
rep_idp "github.com/zitadel/zitadel/internal/repository/idp"
|
||||
"github.com/zitadel/zitadel/internal/repository/idpintent"
|
||||
"github.com/zitadel/zitadel/internal/repository/instance"
|
||||
@@ -867,7 +870,7 @@ func TestCommands_SucceedIDPIntent(t *testing.T) {
|
||||
},
|
||||
args{
|
||||
ctx: context.Background(),
|
||||
writeModel: NewIDPIntentWriteModel("id", "ro"),
|
||||
writeModel: NewIDPIntentWriteModel("id", "ro", 0),
|
||||
},
|
||||
res{
|
||||
err: zerrors.ThrowInternal(nil, "id", "encryption failed"),
|
||||
@@ -888,7 +891,7 @@ func TestCommands_SucceedIDPIntent(t *testing.T) {
|
||||
},
|
||||
args{
|
||||
ctx: context.Background(),
|
||||
writeModel: NewIDPIntentWriteModel("id", "ro"),
|
||||
writeModel: NewIDPIntentWriteModel("id", "ro", 0),
|
||||
idpSession: &oauth.Session{
|
||||
Tokens: &oidc.Tokens[*oidc.IDTokenClaims]{
|
||||
Token: &oauth2.Token{
|
||||
@@ -922,6 +925,7 @@ func TestCommands_SucceedIDPIntent(t *testing.T) {
|
||||
Crypted: []byte("accessToken"),
|
||||
},
|
||||
"idToken",
|
||||
time.Time{},
|
||||
)
|
||||
return event
|
||||
}(),
|
||||
@@ -930,7 +934,7 @@ func TestCommands_SucceedIDPIntent(t *testing.T) {
|
||||
},
|
||||
args{
|
||||
ctx: context.Background(),
|
||||
writeModel: NewIDPIntentWriteModel("id", "instance"),
|
||||
writeModel: NewIDPIntentWriteModel("id", "instance", 0),
|
||||
idpSession: &openid.Session{
|
||||
Tokens: &oidc.Tokens[*oidc.IDTokenClaims]{
|
||||
Token: &oauth2.Token{
|
||||
@@ -973,7 +977,7 @@ func TestCommands_SucceedSAMLIDPIntent(t *testing.T) {
|
||||
ctx context.Context
|
||||
writeModel *IDPIntentWriteModel
|
||||
idpUser idp.User
|
||||
assertion *saml.Assertion
|
||||
session *saml.Session
|
||||
userID string
|
||||
}
|
||||
type res struct {
|
||||
@@ -998,7 +1002,7 @@ func TestCommands_SucceedSAMLIDPIntent(t *testing.T) {
|
||||
},
|
||||
args{
|
||||
ctx: context.Background(),
|
||||
writeModel: NewIDPIntentWriteModel("id", "ro"),
|
||||
writeModel: NewIDPIntentWriteModel("id", "ro", 0),
|
||||
},
|
||||
res{
|
||||
err: zerrors.ThrowInternal(nil, "id", "encryption failed"),
|
||||
@@ -1023,14 +1027,17 @@ func TestCommands_SucceedSAMLIDPIntent(t *testing.T) {
|
||||
KeyID: "id",
|
||||
Crypted: []byte("<Assertion xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"id\" IssueInstant=\"0001-01-01T00:00:00Z\" Version=\"\"><Issuer xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" NameQualifier=\"\" SPNameQualifier=\"\" Format=\"\" SPProvidedID=\"\"></Issuer></Assertion>"),
|
||||
},
|
||||
time.Time{},
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args{
|
||||
ctx: context.Background(),
|
||||
writeModel: NewIDPIntentWriteModel("id", "instance"),
|
||||
assertion: &saml.Assertion{ID: "id"},
|
||||
writeModel: NewIDPIntentWriteModel("id", "instance", 0),
|
||||
session: &saml.Session{
|
||||
Assertion: &crewjam_saml.Assertion{ID: "id"},
|
||||
},
|
||||
idpUser: openid.NewUser(&oidc.UserInfo{
|
||||
Subject: "id",
|
||||
UserInfoProfile: oidc.UserInfoProfile{
|
||||
@@ -1061,14 +1068,17 @@ func TestCommands_SucceedSAMLIDPIntent(t *testing.T) {
|
||||
KeyID: "id",
|
||||
Crypted: []byte("<Assertion xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"id\" IssueInstant=\"0001-01-01T00:00:00Z\" Version=\"\"><Issuer xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" NameQualifier=\"\" SPNameQualifier=\"\" Format=\"\" SPProvidedID=\"\"></Issuer></Assertion>"),
|
||||
},
|
||||
time.Time{},
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args{
|
||||
ctx: context.Background(),
|
||||
writeModel: NewIDPIntentWriteModel("id", "instance"),
|
||||
assertion: &saml.Assertion{ID: "id"},
|
||||
writeModel: NewIDPIntentWriteModel("id", "instance", 0),
|
||||
session: &saml.Session{
|
||||
Assertion: &crewjam_saml.Assertion{ID: "id"},
|
||||
},
|
||||
idpUser: openid.NewUser(&oidc.UserInfo{
|
||||
Subject: "id",
|
||||
UserInfoProfile: oidc.UserInfoProfile{
|
||||
@@ -1088,7 +1098,7 @@ func TestCommands_SucceedSAMLIDPIntent(t *testing.T) {
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
idpConfigEncryption: tt.fields.idpConfigEncryption,
|
||||
}
|
||||
got, err := c.SucceedSAMLIDPIntent(tt.args.ctx, tt.args.writeModel, tt.args.idpUser, tt.args.userID, tt.args.assertion)
|
||||
got, err := c.SucceedSAMLIDPIntent(tt.args.ctx, tt.args.writeModel, tt.args.idpUser, tt.args.userID, tt.args.session)
|
||||
require.ErrorIs(t, err, tt.res.err)
|
||||
assert.Equal(t, tt.res.token, got)
|
||||
})
|
||||
@@ -1128,7 +1138,7 @@ func TestCommands_RequestSAMLIDPIntent(t *testing.T) {
|
||||
},
|
||||
args{
|
||||
ctx: context.Background(),
|
||||
writeModel: NewIDPIntentWriteModel("id", "instance"),
|
||||
writeModel: NewIDPIntentWriteModel("id", "instance", 0),
|
||||
request: "request",
|
||||
},
|
||||
res{},
|
||||
@@ -1156,7 +1166,7 @@ func TestCommands_SucceedLDAPIDPIntent(t *testing.T) {
|
||||
writeModel *IDPIntentWriteModel
|
||||
idpUser idp.User
|
||||
userID string
|
||||
attributes map[string][]string
|
||||
session *ldap.Session
|
||||
}
|
||||
type res struct {
|
||||
token string
|
||||
@@ -1180,7 +1190,7 @@ func TestCommands_SucceedLDAPIDPIntent(t *testing.T) {
|
||||
},
|
||||
args{
|
||||
ctx: context.Background(),
|
||||
writeModel: NewIDPIntentWriteModel("id", "instance"),
|
||||
writeModel: NewIDPIntentWriteModel("id", "instance", 0),
|
||||
},
|
||||
res{
|
||||
err: zerrors.ThrowInternal(nil, "id", "encryption failed"),
|
||||
@@ -1200,14 +1210,24 @@ func TestCommands_SucceedLDAPIDPIntent(t *testing.T) {
|
||||
"username",
|
||||
"",
|
||||
map[string][]string{"id": {"id"}},
|
||||
time.Time{},
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args{
|
||||
ctx: context.Background(),
|
||||
writeModel: NewIDPIntentWriteModel("id", "instance"),
|
||||
attributes: map[string][]string{"id": {"id"}},
|
||||
writeModel: NewIDPIntentWriteModel("id", "instance", 0),
|
||||
session: &ldap.Session{
|
||||
Entry: &goldap.Entry{
|
||||
Attributes: []*goldap.EntryAttribute{
|
||||
{
|
||||
Name: "id",
|
||||
Values: []string{"id"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
idpUser: ldap.NewUser(
|
||||
"id",
|
||||
"",
|
||||
@@ -1235,7 +1255,7 @@ func TestCommands_SucceedLDAPIDPIntent(t *testing.T) {
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
idpConfigEncryption: tt.fields.idpConfigEncryption,
|
||||
}
|
||||
got, err := c.SucceedLDAPIDPIntent(tt.args.ctx, tt.args.writeModel, tt.args.idpUser, tt.args.userID, tt.args.attributes)
|
||||
got, err := c.SucceedLDAPIDPIntent(tt.args.ctx, tt.args.writeModel, tt.args.idpUser, tt.args.userID, tt.args.session)
|
||||
require.ErrorIs(t, err, tt.res.err)
|
||||
assert.Equal(t, tt.res.token, got)
|
||||
})
|
||||
@@ -1275,7 +1295,7 @@ func TestCommands_FailIDPIntent(t *testing.T) {
|
||||
},
|
||||
args{
|
||||
ctx: context.Background(),
|
||||
writeModel: NewIDPIntentWriteModel("id", "instance"),
|
||||
writeModel: NewIDPIntentWriteModel("id", "instance", 0),
|
||||
reason: "reason",
|
||||
},
|
||||
res{
|
||||
|
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/id"
|
||||
"github.com/zitadel/zitadel/internal/notification/senders"
|
||||
"github.com/zitadel/zitadel/internal/repository/idpintent"
|
||||
"github.com/zitadel/zitadel/internal/repository/session"
|
||||
"github.com/zitadel/zitadel/internal/repository/user"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
@@ -32,31 +33,33 @@ type SessionCommands struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
eventCommands []eventstore.Command
|
||||
|
||||
hasher *crypto.Hasher
|
||||
intentAlg crypto.EncryptionAlgorithm
|
||||
totpAlg crypto.EncryptionAlgorithm
|
||||
otpAlg crypto.EncryptionAlgorithm
|
||||
createCode encryptedCodeWithDefaultFunc
|
||||
createPhoneCode encryptedCodeGeneratorWithDefaultFunc
|
||||
createToken func(sessionID string) (id string, token string, err error)
|
||||
getCodeVerifier func(ctx context.Context, id string) (senders.CodeGenerator, error)
|
||||
now func() time.Time
|
||||
hasher *crypto.Hasher
|
||||
intentAlg crypto.EncryptionAlgorithm
|
||||
totpAlg crypto.EncryptionAlgorithm
|
||||
otpAlg crypto.EncryptionAlgorithm
|
||||
createCode encryptedCodeWithDefaultFunc
|
||||
createPhoneCode encryptedCodeGeneratorWithDefaultFunc
|
||||
createToken func(sessionID string) (id string, token string, err error)
|
||||
getCodeVerifier func(ctx context.Context, id string) (senders.CodeGenerator, error)
|
||||
now func() time.Time
|
||||
maxIdPIntentLifetime time.Duration
|
||||
}
|
||||
|
||||
func (c *Commands) NewSessionCommands(cmds []SessionCommand, session *SessionWriteModel) *SessionCommands {
|
||||
return &SessionCommands{
|
||||
sessionCommands: cmds,
|
||||
sessionWriteModel: session,
|
||||
eventstore: c.eventstore,
|
||||
hasher: c.userPasswordHasher,
|
||||
intentAlg: c.idpConfigEncryption,
|
||||
totpAlg: c.multifactors.OTP.CryptoMFA,
|
||||
otpAlg: c.userEncryption,
|
||||
createCode: c.newEncryptedCodeWithDefault,
|
||||
createPhoneCode: c.newPhoneCode,
|
||||
createToken: c.sessionTokenCreator,
|
||||
getCodeVerifier: c.phoneCodeVerifierFromConfig,
|
||||
now: time.Now,
|
||||
sessionCommands: cmds,
|
||||
sessionWriteModel: session,
|
||||
eventstore: c.eventstore,
|
||||
hasher: c.userPasswordHasher,
|
||||
intentAlg: c.idpConfigEncryption,
|
||||
totpAlg: c.multifactors.OTP.CryptoMFA,
|
||||
otpAlg: c.userEncryption,
|
||||
createCode: c.newEncryptedCodeWithDefault,
|
||||
createPhoneCode: c.newPhoneCode,
|
||||
createToken: c.sessionTokenCreator,
|
||||
getCodeVerifier: c.phoneCodeVerifierFromConfig,
|
||||
now: time.Now,
|
||||
maxIdPIntentLifetime: c.maxIdPIntentLifetime,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +95,7 @@ func CheckIntent(intentID, token string) SessionCommand {
|
||||
if err := crypto.CheckToken(cmd.intentAlg, token, intentID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd.intentWriteModel = NewIDPIntentWriteModel(intentID, "")
|
||||
cmd.intentWriteModel = NewIDPIntentWriteModel(intentID, "", cmd.maxIdPIntentLifetime)
|
||||
err := cmd.eventstore.FilterToQueryReducer(ctx, cmd.intentWriteModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -100,6 +103,9 @@ func CheckIntent(intentID, token string) SessionCommand {
|
||||
if cmd.intentWriteModel.State != domain.IDPIntentStateSucceeded {
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-Df4bw", "Errors.Intent.NotSucceeded")
|
||||
}
|
||||
if time.Now().After(cmd.intentWriteModel.ExpiresAt()) {
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-SAf42", "Errors.Intent.Expired")
|
||||
}
|
||||
if cmd.intentWriteModel.UserID != "" {
|
||||
if cmd.intentWriteModel.UserID != cmd.sessionWriteModel.UserID {
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-O8xk3w", "Errors.Intent.OtherUser")
|
||||
@@ -168,6 +174,7 @@ func (s *SessionCommands) PasswordChecked(ctx context.Context, checkedAt time.Ti
|
||||
|
||||
func (s *SessionCommands) IntentChecked(ctx context.Context, checkedAt time.Time) {
|
||||
s.eventCommands = append(s.eventCommands, session.NewIntentCheckedEvent(ctx, s.sessionWriteModel.aggregate, checkedAt))
|
||||
s.eventCommands = append(s.eventCommands, idpintent.NewConsumedEvent(ctx, IDPIntentAggregateFromWriteModel(&s.intentWriteModel.WriteModel)))
|
||||
}
|
||||
|
||||
func (s *SessionCommands) WebAuthNChallenged(ctx context.Context, challenge string, allowedCrentialIDs [][]byte, userVerification domain.UserVerificationRequirement, rpid string) {
|
||||
|
@@ -695,6 +695,7 @@ func TestCommands_updateSession(t *testing.T) {
|
||||
"userID2",
|
||||
nil,
|
||||
"",
|
||||
time.Now().Add(time.Hour),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -757,6 +758,111 @@ func TestCommands_updateSession(t *testing.T) {
|
||||
err: zerrors.ThrowPermissionDenied(nil, "CRYPTO-CRYPTO", "Errors.Intent.InvalidToken"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"set user, intent token already consumed",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(), &user.NewAggregate("userID", "org1").Aggregate,
|
||||
"username", "", "", "", "", language.English, domain.GenderUnspecified, "", false),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
idpintent.NewSucceededEvent(context.Background(),
|
||||
&idpintent.NewAggregate("intent", "instance1").Aggregate,
|
||||
nil,
|
||||
"idpUserID",
|
||||
"idpUsername",
|
||||
"userID",
|
||||
nil,
|
||||
"",
|
||||
time.Now().Add(time.Hour),
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
idpintent.NewConsumedEvent(context.Background(),
|
||||
&idpintent.NewAggregate("intent", "instance1").Aggregate,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instance1", "", ""),
|
||||
checks: &SessionCommands{
|
||||
sessionWriteModel: NewSessionWriteModel("sessionID", "instance1"),
|
||||
sessionCommands: []SessionCommand{
|
||||
CheckUser("userID", "org1", &language.Afrikaans),
|
||||
CheckIntent("intent", "aW50ZW50"),
|
||||
},
|
||||
createToken: func(sessionID string) (string, string, error) {
|
||||
return "tokenID",
|
||||
"token",
|
||||
nil
|
||||
},
|
||||
intentAlg: decryption(nil),
|
||||
now: func() time.Time {
|
||||
return testNow
|
||||
},
|
||||
},
|
||||
metadata: map[string][]byte{
|
||||
"key": []byte("value"),
|
||||
},
|
||||
},
|
||||
res{
|
||||
err: zerrors.ThrowPreconditionFailed(nil, "COMMAND-Df4bw", "Errors.Intent.NotSucceeded"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"set user, intent token already expired",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(), &user.NewAggregate("userID", "org1").Aggregate,
|
||||
"username", "", "", "", "", language.English, domain.GenderUnspecified, "", false),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
idpintent.NewSucceededEvent(context.Background(),
|
||||
&idpintent.NewAggregate("intent", "instance1").Aggregate,
|
||||
nil,
|
||||
"idpUserID",
|
||||
"idpUsername",
|
||||
"userID",
|
||||
nil,
|
||||
"",
|
||||
time.Now().Add(-time.Hour),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instance1", "", ""),
|
||||
checks: &SessionCommands{
|
||||
sessionWriteModel: NewSessionWriteModel("sessionID", "instance1"),
|
||||
sessionCommands: []SessionCommand{
|
||||
CheckUser("userID", "org1", &language.Afrikaans),
|
||||
CheckIntent("intent", "aW50ZW50"),
|
||||
},
|
||||
createToken: func(sessionID string) (string, string, error) {
|
||||
return "tokenID",
|
||||
"token",
|
||||
nil
|
||||
},
|
||||
intentAlg: decryption(nil),
|
||||
now: func() time.Time {
|
||||
return testNow
|
||||
},
|
||||
},
|
||||
metadata: map[string][]byte{
|
||||
"key": []byte("value"),
|
||||
},
|
||||
},
|
||||
res{
|
||||
err: zerrors.ThrowPreconditionFailed(nil, "COMMAND-SAf42", "Errors.Intent.Expired"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"set user, intent, metadata and token",
|
||||
fields{
|
||||
@@ -768,13 +874,14 @@ func TestCommands_updateSession(t *testing.T) {
|
||||
),
|
||||
eventFromEventPusher(
|
||||
idpintent.NewSucceededEvent(context.Background(),
|
||||
&idpintent.NewAggregate("id", "instance1").Aggregate,
|
||||
&idpintent.NewAggregate("intent", "instance1").Aggregate,
|
||||
nil,
|
||||
"idpUserID",
|
||||
"idpUsername",
|
||||
"userID",
|
||||
nil,
|
||||
"",
|
||||
time.Now().Add(time.Hour),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -783,6 +890,7 @@ func TestCommands_updateSession(t *testing.T) {
|
||||
"userID", "org1", testNow, &language.Afrikaans),
|
||||
session.NewIntentCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
|
||||
testNow),
|
||||
idpintent.NewConsumedEvent(context.Background(), &idpintent.NewAggregate("intent", "org1").Aggregate),
|
||||
session.NewMetadataSetEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
|
||||
map[string][]byte{"key": []byte("value")}),
|
||||
session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
|
||||
@@ -842,13 +950,14 @@ func TestCommands_updateSession(t *testing.T) {
|
||||
),
|
||||
eventFromEventPusher(
|
||||
idpintent.NewSucceededEvent(context.Background(),
|
||||
&idpintent.NewAggregate("id", "instance1").Aggregate,
|
||||
&idpintent.NewAggregate("intent", "instance1").Aggregate,
|
||||
nil,
|
||||
"idpUserID",
|
||||
"idpUsername",
|
||||
"",
|
||||
nil,
|
||||
"",
|
||||
time.Now().Add(time.Hour),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -866,6 +975,7 @@ func TestCommands_updateSession(t *testing.T) {
|
||||
"userID", "org1", testNow, &language.Afrikaans),
|
||||
session.NewIntentCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
|
||||
testNow),
|
||||
idpintent.NewConsumedEvent(context.Background(), &idpintent.NewAggregate("intent", "org1").Aggregate),
|
||||
session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "instance1").Aggregate,
|
||||
"tokenID"),
|
||||
),
|
||||
|
Reference in New Issue
Block a user