Skip to content

Commit ed1071a

Browse files
kelkarajayAjay Kelkar
authored and
Ajay Kelkar
committed
feat: session devices upsert (#2733)
1 parent 042a8ad commit ed1071a

9 files changed

+46
-52
lines changed

persistence/sql/migratest/testdata/20220907132836_testdata.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ VALUES ('7458af86-c1d8-401c-978a-8da89133f98b', '884f556e-eb3a-4b9f-bee3-1134564
55
'eVwBt7UAAAAVwBt7XAMw', '5ff66179-c240-4703-b0d8-494592cefff5', true, '123eVwBt7UAAAeVwBt7XAMw', 'aal2',
66
'[{"method":"password"},{"method":"totp"}]');
77

8-
INSERT INTO session_devices (id, nid, session_id, ip_address, user_agent, location, created_at, last_seen)
8+
INSERT INTO session_devices (id, nid, session_id, ip_address, user_agent, location, created_at, updated_at)
99
VALUES ('884f556e-eb3a-4b9f-bee3-11763642c6c0', '884f556e-eb3a-4b9f-bee3-11345642c6c0',
1010
'7458af86-c1d8-401c-978a-8da89133f98b', '54.155.246.232',
1111
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36',

persistence/sql/migrations/sql/20220907132836000000_sessions_add_client_fields.cockroach.up.sql

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ CREATE TABLE "session_devices"
88
"session_id" UUID NOT NULL,
99
"nid" UUID NOT NULL,
1010
"created_at" timestamp NOT NULL,
11-
"last_seen" timestamp NOT NULL,
11+
"updated_at" timestamp NOT NULL,
1212
CONSTRAINT "session_metadata_sessions_id_fk" FOREIGN KEY ("session_id") REFERENCES "sessions" ("id") ON DELETE cascade,
13-
CONSTRAINT "session_metadata_nid_fk" FOREIGN KEY ("nid") REFERENCES "networks" ("id") ON DELETE cascade
13+
CONSTRAINT "session_metadata_nid_fk" FOREIGN KEY ("nid") REFERENCES "networks" ("id") ON DELETE cascade,
14+
CONSTRAINT unique_session_device UNIQUE (nid, session_id, ip_address, user_agent)
1415
);

persistence/sql/migrations/sql/20220907132836000000_sessions_add_client_fields.mysql.up.sql

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ CREATE TABLE `session_devices`
88
`session_id` char(36) NOT NULL,
99
`nid` char(36) NOT NULL,
1010
`created_at` DATETIME NOT NULL,
11-
`last_seen` DATETIME NOT NULL,
11+
`updated_at` DATETIME NOT NULL,
1212
FOREIGN KEY (`session_id`) REFERENCES `sessions` (`id`) ON DELETE cascade,
13-
FOREIGN KEY (`nid`) REFERENCES `networks` (`id`) ON DELETE cascade
13+
FOREIGN KEY (`nid`) REFERENCES `networks` (`id`) ON DELETE cascade,
14+
CONSTRAINT unique_session_device UNIQUE (nid, session_id, ip_address, user_agent)
1415
) ENGINE = InnoDB;

persistence/sql/migrations/sql/20220907132836000000_sessions_add_client_fields.postgres.up.sql

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ CREATE TABLE "session_devices"
88
"nid" UUID NOT NULL,
99
"session_id" UUID NOT NULL,
1010
"created_at" timestamp NOT NULL,
11-
"last_seen" timestamp NOT NULL,
11+
"updated_at" timestamp NOT NULL,
1212
FOREIGN KEY ("session_id") REFERENCES "sessions" ("id") ON DELETE cascade,
13-
FOREIGN KEY ("nid") REFERENCES "networks" ("id") ON DELETE cascade
13+
FOREIGN KEY ("nid") REFERENCES "networks" ("id") ON DELETE cascade,
14+
CONSTRAINT unique_session_device UNIQUE (nid, session_id, ip_address, user_agent)
1415
);

persistence/sql/migrations/sql/20220907132836000000_sessions_add_client_fields.sqlite3.up.sql

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ CREATE TABLE IF NOT EXISTS "session_devices"
77
"session_id" UUID NOT NULL,
88
"nid" UUID NOT NULL,
99
"created_at" timestamp NOT NULL,
10-
"last_seen" timestamp NOT NULL,
10+
"updated_at" timestamp NOT NULL,
1111
FOREIGN KEY ("session_id") REFERENCES "sessions" ("id") ON DELETE cascade,
12-
FOREIGN KEY ("nid") REFERENCES "networks" ("id") ON DELETE cascade
12+
FOREIGN KEY ("nid") REFERENCES "networks" ("id") ON DELETE cascade,
13+
CONSTRAINT unique_session_device UNIQUE (nid, session_id, ip_address, user_agent)
1314
);

persistence/sql/persister_session.go

Lines changed: 30 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ func (p *Persister) GetSession(ctx context.Context, sid uuid.UUID) (*session.Ses
2424
defer span.End()
2525

2626
var s session.Session
27+
s.Devices = make([]session.Device, 0)
2728
nid := p.NetworkID(ctx)
28-
if err := p.GetConnection(ctx).Where("id = ? AND nid = ?", sid, nid).First(&s); err != nil {
29+
if err := p.GetConnection(ctx).EagerPreload("Devices").Where("id = ? AND nid = ?", sid, nid).First(&s); err != nil {
2930
return nil, sqlcon.HandleError(err)
3031
}
3132

@@ -36,29 +37,10 @@ func (p *Persister) GetSession(ctx context.Context, sid uuid.UUID) (*session.Ses
3637
return nil, err
3738
}
3839

39-
devices, err := p.GetSessionDevices(ctx, sid)
40-
if err != nil {
41-
return nil, err
42-
}
43-
4440
s.Identity = i
45-
s.Devices = devices
4641
return &s, nil
4742
}
4843

49-
func (p *Persister) GetSessionDevices(ctx context.Context, sid uuid.UUID) ([]session.Device, error) {
50-
ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.GetSessionDevices")
51-
defer span.End()
52-
53-
devices := make([]session.Device, 0)
54-
nid := p.NetworkID(ctx)
55-
if err := p.GetConnection(ctx).Where("session_id = ? AND nid = ?", sid, nid).All(&devices); err != nil {
56-
return nil, sqlcon.HandleError(err)
57-
}
58-
59-
return devices, nil
60-
}
61-
6244
// ListSessionsByIdentity retrieves sessions for an identity from the store.
6345
func (p *Persister) ListSessionsByIdentity(ctx context.Context, iID uuid.UUID, active *bool, page, perPage int, except uuid.UUID) ([]*session.Session, error) {
6446
ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.ListSessionsByIdentity")
@@ -75,7 +57,7 @@ func (p *Persister) ListSessionsByIdentity(ctx context.Context, iID uuid.UUID, a
7557
if active != nil {
7658
q = q.Where("active = ?", *active)
7759
}
78-
if err := q.All(&s); err != nil {
60+
if err := q.Eager("Devices").All(&s); err != nil {
7961
return sqlcon.HandleError(err)
8062
}
8163

@@ -85,13 +67,7 @@ func (p *Persister) ListSessionsByIdentity(ctx context.Context, iID uuid.UUID, a
8567
return err
8668
}
8769

88-
devices, err := p.GetSessionDevices(ctx, s.ID)
89-
if err != nil {
90-
return err
91-
}
92-
9370
s.Identity = i
94-
s.Devices = devices
9571
}
9672
return nil
9773
}); err != nil {
@@ -107,15 +83,34 @@ func (p *Persister) UpsertSession(ctx context.Context, s *session.Session) error
10783

10884
s.NID = p.NetworkID(ctx)
10985

110-
if err := p.Connection(ctx).Find(new(session.Session), s.ID); errors.Is(err, sql.ErrNoRows) {
111-
// This must not be eager or identities will be created / updated
112-
return errors.WithStack(p.GetConnection(ctx).Create(s))
113-
} else if err != nil {
114-
return errors.WithStack(err)
115-
}
86+
return errors.WithStack(p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error {
87+
if err := tx.Find(new(session.Session), s.ID); errors.Is(err, sql.ErrNoRows) {
88+
// This must not be eager or identities will be created / updated
89+
err = errors.WithStack(tx.Create(s))
90+
if err != nil {
91+
return err
92+
}
11693

117-
// This must not be eager or identities will be created / updated
118-
return p.GetConnection(ctx).Update(s)
94+
for i := range s.Devices {
95+
device := &(s.Devices[i])
96+
device.SessionID = s.ID
97+
device.NID = s.NID
98+
99+
if err := tx.Create(device); err != nil {
100+
return err
101+
}
102+
103+
s.Devices[i] = *device
104+
}
105+
106+
return nil
107+
} else if err != nil {
108+
return errors.WithStack(err)
109+
}
110+
111+
// This must not be eager or identities will be created / updated
112+
return tx.Update(s)
113+
}))
119114
}
120115

121116
func (p *Persister) DeleteSession(ctx context.Context, sid uuid.UUID) error {

session/persistence.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@ type Persister interface {
2323
// GetSession retrieves a session from the store.
2424
GetSession(ctx context.Context, sid uuid.UUID) (*Session, error)
2525

26-
// GetSessionDevices retrieves all the device session history from the store
27-
GetSessionDevices(ctx context.Context, sid uuid.UUID) ([]Device, error)
28-
2926
// ListSessionsByIdentity retrieves sessions for an identity from the store.
3027
ListSessionsByIdentity(ctx context.Context, iID uuid.UUID, active *bool, page, perPage int, except uuid.UUID) ([]*Session, error)
3128

session/session.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ type Device struct {
5757
CreatedAt time.Time `json:"seen_at" faker:"-" db:"created_at"`
5858

5959
// Last seen
60-
LastSeen time.Time `json:"last_seen" faker:"-" db:"last_seen"`
60+
UpdatedAt time.Time `json:"last_seen" faker:"-" db:"updated_at"`
6161

6262
NID uuid.UUID `json:"-" faker:"-" db:"nid"`
6363
}
@@ -119,7 +119,7 @@ type Session struct {
119119
Identity *identity.Identity `json:"identity" faker:"identity" db:"-" belongs_to:"identities" fk_id:"IdentityID"`
120120

121121
// Devices has history of all endpoints where the session was used
122-
Devices []Device `json:"devices" faker:"-" db:"-"`
122+
Devices []Device `json:"devices" faker:"-" has_many:"session_devices" fk_id:"session_id"`
123123

124124
// IdentityID is a helper struct field for gobuffalo.pop.
125125
IdentityID uuid.UUID `json:"-" faker:"-" db:"identity_id"`
@@ -231,8 +231,6 @@ func (s *Session) SaveSessionDeviceInformation(r *http.Request) {
231231

232232
device.ID = x.NewUUID()
233233
device.SessionID = s.ID
234-
device.CreatedAt = time.Now().UTC()
235-
device.LastSeen = time.Now().UTC()
236234

237235
agent := r.Header["User-Agent"]
238236
if len(agent) > 0 {

session/session_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func TestSession(t *testing.T) {
9999
assert.Equal(t, authAt, s.AuthenticatedAt)
100100
assert.Equal(t, 1, len(s.Devices))
101101
assert.Equal(t, s.ID.String(), s.Devices[0].SessionID.String())
102-
assert.NotNil(t, s.Devices[0].LastSeen)
102+
assert.NotNil(t, s.Devices[0].UpdatedAt)
103103
assert.NotNil(t, s.Devices[0].CreatedAt)
104104
assert.Equal(t, "54.155.246.155", *s.Devices[0].IPAddress)
105105
assert.Equal(t, "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36", *s.Devices[0].UserAgent)

0 commit comments

Comments
 (0)