feat(api/v2): store user agent details in the session (#6711)

This change adds the ability to set and get user agent data, such as fingerprint, IP, request headers and a description to the session. All fields are optional.

Closes #6028
This commit is contained in:
Tim Möhlmann
2023-10-12 15:16:59 +03:00
committed by GitHub
parent a272b1201f
commit c71bf85b7a
16 changed files with 634 additions and 147 deletions

View File

@@ -2,6 +2,8 @@ package command
import (
"context"
"net"
"net/http"
"testing"
"time"
@@ -358,7 +360,15 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
),
expectFilter(
eventFromEventPusher(
session.NewAddedEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate)),
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) {
@@ -401,7 +411,15 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
),
expectFilter(
eventFromEventPusher(
session.NewAddedEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate)),
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) {
@@ -444,8 +462,15 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
),
expectFilter(
eventFromEventPusher(
session.NewAddedEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate),
),
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"}},
},
)),
eventFromEventPusher(
session.NewUserCheckedEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate,
"userID", testNow),
@@ -523,8 +548,15 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
),
expectFilter(
eventFromEventPusher(
session.NewAddedEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate),
),
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"}},
},
)),
eventFromEventPusher(
session.NewUserCheckedEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate,
"userID", testNow),

View File

@@ -2,6 +2,8 @@ package command
import (
"context"
"net"
"net/http"
"testing"
"time"
@@ -164,7 +166,15 @@ func TestCommands_AddOIDCSessionAccessToken(t *testing.T) {
),
expectFilter(
eventFromEventPusher(
session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate),
session.NewAddedEvent(context.Background(),
&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"}},
},
),
),
eventFromEventPusher(
session.NewUserCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate,
@@ -365,7 +375,15 @@ func TestCommands_AddOIDCSessionRefreshAndAccessToken(t *testing.T) {
),
expectFilter(
eventFromEventPusher(
session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate),
session.NewAddedEvent(context.Background(),
&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"}},
},
),
),
eventFromEventPusher(
session.NewUserCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate,

View File

@@ -166,8 +166,8 @@ func (s *SessionCommands) Exec(ctx context.Context) error {
return nil
}
func (s *SessionCommands) Start(ctx context.Context) {
s.eventCommands = append(s.eventCommands, session.NewAddedEvent(ctx, s.sessionWriteModel.aggregate))
func (s *SessionCommands) Start(ctx context.Context, userAgent *domain.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 {
@@ -280,7 +280,7 @@ func (s *SessionCommands) commands(ctx context.Context) (string, []eventstore.Co
return token, s.eventCommands, nil
}
func (c *Commands) CreateSession(ctx context.Context, cmds []SessionCommand, metadata map[string][]byte) (set *SessionChanged, err error) {
func (c *Commands) CreateSession(ctx context.Context, cmds []SessionCommand, metadata map[string][]byte, userAgent *domain.UserAgent) (set *SessionChanged, err error) {
sessionID, err := c.idGenerator.Next()
if err != nil {
return nil, err
@@ -291,7 +291,7 @@ func (c *Commands) CreateSession(ctx context.Context, cmds []SessionCommand, met
return nil, err
}
cmd := c.NewSessionCommands(cmds, sessionWriteModel)
cmd.Start(ctx)
cmd.Start(ctx, userAgent)
return c.updateSession(ctx, cmd, metadata)
}

View File

@@ -3,10 +3,13 @@ package command
import (
"context"
"io"
"net"
"net/http"
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/muhlemmer/gu"
"github.com/pquerna/otp/totp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -145,9 +148,10 @@ func TestCommands_CreateSession(t *testing.T) {
tokenCreator func(sessionID string) (string, string, error)
}
type args struct {
ctx context.Context
checks []SessionCommand
metadata map[string][]byte
ctx context.Context
checks []SessionCommand
metadata map[string][]byte
userAgent *domain.UserAgent
}
type res struct {
want *SessionChanged
@@ -200,12 +204,26 @@ func TestCommands_CreateSession(t *testing.T) {
},
args{
ctx: authz.NewMockContext("", "org1", ""),
userAgent: &domain.UserAgent{
FingerprintID: gu.Ptr("fp1"),
IP: net.ParseIP("1.2.3.4"),
Description: gu.Ptr("firefox"),
Header: http.Header{"foo": []string{"bar"}},
},
},
[]expect{
expectFilter(),
expectPush(
eventPusherToEvents(
session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate),
session.NewAddedEvent(context.Background(),
&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"}},
},
),
session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate,
"tokenID",
),
@@ -229,7 +247,7 @@ func TestCommands_CreateSession(t *testing.T) {
idGenerator: tt.fields.idGenerator,
sessionTokenCreator: tt.fields.tokenCreator,
}
got, err := c.CreateSession(tt.args.ctx, tt.args.checks, tt.args.metadata)
got, err := c.CreateSession(tt.args.ctx, tt.args.checks, tt.args.metadata, tt.args.userAgent)
require.ErrorIs(t, err, tt.res.err)
assert.Equal(t, tt.res.want, got)
})
@@ -278,7 +296,15 @@ func TestCommands_UpdateSession(t *testing.T) {
eventstore: eventstoreExpect(t,
expectFilter(
eventFromEventPusher(
session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate)),
session.NewAddedEvent(context.Background(),
&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"}},
},
)),
eventFromEventPusher(
session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate,
"tokenID")),
@@ -303,7 +329,15 @@ func TestCommands_UpdateSession(t *testing.T) {
eventstore: eventstoreExpect(t,
expectFilter(
eventFromEventPusher(
session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate)),
session.NewAddedEvent(context.Background(),
&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"}},
},
)),
eventFromEventPusher(
session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate,
"tokenID")),
@@ -868,7 +902,15 @@ func TestCommands_TerminateSession(t *testing.T) {
eventstore: eventstoreExpect(t,
expectFilter(
eventFromEventPusher(
session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate)),
session.NewAddedEvent(context.Background(),
&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"}},
},
)),
eventFromEventPusher(
session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate,
"tokenID")),
@@ -893,7 +935,15 @@ func TestCommands_TerminateSession(t *testing.T) {
eventstore: eventstoreExpect(t,
expectFilter(
eventFromEventPusher(
session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate)),
session.NewAddedEvent(context.Background(),
&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"}},
},
)),
eventFromEventPusher(
session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate,
"tokenID")),
@@ -922,7 +972,15 @@ func TestCommands_TerminateSession(t *testing.T) {
eventstore: eventstoreExpect(t,
expectFilter(
eventFromEventPusher(
session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate)),
session.NewAddedEvent(context.Background(),
&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"}},
},
)),
eventFromEventPusher(
session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate,
"tokenID"),
@@ -953,7 +1011,15 @@ func TestCommands_TerminateSession(t *testing.T) {
eventstore: eventstoreExpect(t,
expectFilter(
eventFromEventPusher(
session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate)),
session.NewAddedEvent(context.Background(),
&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"}},
},
)),
eventFromEventPusher(
session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate,
"tokenID"),