feat(api/v2): implement TOTP session check (#6362)

* feat(api/v2): implement TOTP session check

* add integration test

* correct typo in projection test

* fix event type typos

---------

Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
Tim Möhlmann
2023-08-15 12:50:42 +03:00
committed by GitHub
parent 8953353210
commit 0017542aa2
15 changed files with 437 additions and 21 deletions

View File

@@ -30,6 +30,7 @@ const (
SessionColumnIntentCheckedAt = "intent_checked_at"
SessionColumnWebAuthNCheckedAt = "webauthn_checked_at"
SessionColumnWebAuthNUserVerified = "webauthn_user_verified"
SessionColumnTOTPCheckedAt = "totp_checked_at"
SessionColumnMetadata = "metadata"
SessionColumnTokenID = "token_id"
)
@@ -58,6 +59,7 @@ func newSessionProjection(ctx context.Context, config crdb.StatementHandlerConfi
crdb.NewColumn(SessionColumnIntentCheckedAt, crdb.ColumnTypeTimestamp, crdb.Nullable()),
crdb.NewColumn(SessionColumnWebAuthNCheckedAt, crdb.ColumnTypeTimestamp, crdb.Nullable()),
crdb.NewColumn(SessionColumnWebAuthNUserVerified, crdb.ColumnTypeBool, crdb.Nullable()),
crdb.NewColumn(SessionColumnTOTPCheckedAt, crdb.ColumnTypeTimestamp, crdb.Nullable()),
crdb.NewColumn(SessionColumnMetadata, crdb.ColumnTypeJSONB, crdb.Nullable()),
crdb.NewColumn(SessionColumnTokenID, crdb.ColumnTypeText, crdb.Nullable()),
},
@@ -93,6 +95,10 @@ func (p *sessionProjection) reducers() []handler.AggregateReducer {
Event: session.WebAuthNCheckedType,
Reduce: p.reduceWebAuthNChecked,
},
{
Event: session.TOTPCheckedType,
Reduce: p.reduceTOTPChecked,
},
{
Event: session.TokenSetType,
Reduce: p.reduceTokenSet,
@@ -229,6 +235,26 @@ func (p *sessionProjection) reduceWebAuthNChecked(event eventstore.Event) (*hand
), nil
}
func (p *sessionProjection) reduceTOTPChecked(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*session.TOTPCheckedEvent)
if !ok {
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Oqu8i", "reduce.wrong.event.type %s", session.TOTPCheckedType)
}
return crdb.NewUpdateStatement(
e,
[]handler.Column{
handler.NewCol(SessionColumnChangeDate, e.CreationDate()),
handler.NewCol(SessionColumnSequence, e.Sequence()),
handler.NewCol(SessionColumnTOTPCheckedAt, e.CheckedAt),
},
[]handler.Condition{
handler.NewCond(SessionColumnID, e.Aggregate().ID),
handler.NewCond(SessionColumnInstanceID, e.Aggregate().InstanceID),
},
), nil
}
func (p *sessionProjection) reduceTokenSet(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*session.TokenSetEvent)
if !ok {

View File

@@ -191,6 +191,38 @@ func TestSessionProjection_reduces(t *testing.T) {
},
},
},
{
name: "instance reduceOTPChecked",
args: args{
event: getEvent(testEvent(
session.AddedType,
session.AggregateType,
[]byte(`{
"checkedAt": "2023-05-04T00:00:00Z"
}`),
), eventstore.GenericEventMapper[session.TOTPCheckedEvent]),
},
reduce: (&sessionProjection{}).reduceTOTPChecked,
want: wantReduce{
aggregateType: eventstore.AggregateType("session"),
sequence: 15,
previousSequence: 10,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.sessions4 SET (change_date, sequence, totp_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{
anyArg{},
anyArg{},
time.Date(2023, time.May, 4, 0, 0, 0, 0, time.UTC),
"agg-id",
"instance-id",
},
},
},
},
},
},
{
name: "instance reduceTokenSet",
args: args{