diff --git a/internal/clientcache/internal/cache/repository_sessions.go b/internal/clientcache/internal/cache/repository_sessions.go index 6f0337ed9..b2f348a88 100644 --- a/internal/clientcache/internal/cache/repository_sessions.go +++ b/internal/clientcache/internal/cache/repository_sessions.go @@ -111,8 +111,8 @@ func (r *Repository) refreshSessions(ctx context.Context, u *user, tokens map[Au _, err = r.rw.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, func(_ db.Reader, w db.Writer) error { switch { case oldRefreshToken == nil: - if _, err := w.Exec(ctx, "delete from session where user_id = @user_id", - []any{sql.Named("user_id", u.Id)}); err != nil { + if _, err := w.Exec(ctx, "delete from session where fk_user_id = @fk_user_id", + []any{sql.Named("fk_user_id", u.Id)}); err != nil { return err } case len(removedIds) > 0: @@ -185,8 +185,8 @@ func (r *Repository) fullFetchSessions(ctx context.Context, u *user, tokens map[ event.WriteSysEvent(ctx, op, fmt.Sprintf("updating %d sessions for user %v", len(resp), u)) _, err = r.rw.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, func(_ db.Reader, w db.Writer) error { - if _, err := w.Exec(ctx, "delete from session where user_id = @user_id", - []any{sql.Named("user_id", u.Id)}); err != nil { + if _, err := w.Exec(ctx, "delete from session where fk_user_id = @fk_user_id", + []any{sql.Named("fk_user_id", u.Id)}); err != nil { return err } @@ -222,18 +222,19 @@ func upsertSessions(ctx context.Context, w db.Writer, u *user, in []*sessions.Se return errors.Wrap(ctx, err, op) } newSession := &Session{ - UserId: u.Id, + FkUserId: u.Id, Id: s.Id, Type: s.Type, Status: s.Status, Endpoint: s.Endpoint, ScopeId: s.ScopeId, TargetId: s.TargetId, + UserId: s.UserId, Item: string(item), } onConflict := db.OnConflict{ - Target: db.Columns{"user_id", "id"}, - Action: db.UpdateAll(true), + Target: db.Columns{"fk_user_id", "id"}, + Action: db.SetColumns([]string{"type", "status", "endpoint", "scope_id", "target_id", "user_id", "item"}), } if err := w.Create(ctx, newSession, db.WithOnConflict(&onConflict)); err != nil { return errors.Wrap(ctx, err, op) @@ -264,7 +265,7 @@ func (r *Repository) QuerySessions(ctx context.Context, authTokenId, query strin return nil, errors.New(ctx, errors.InvalidParameter, op, "query is missing") } - w, err := mql.Parse(query, Session{}, mql.WithIgnoredFields("UserId", "Item")) + w, err := mql.Parse(query, Session{}, mql.WithIgnoredFields("FkUserId", "Item")) if err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithCode(errors.InvalidParameter)) } @@ -292,10 +293,10 @@ func (r *Repository) searchSessions(ctx context.Context, condition string, searc case opts.withAuthTokenId == "" && opts.withUserId == "": return nil, errors.New(ctx, errors.InvalidParameter, op, "neither user id nor auth token id were provided") case opts.withAuthTokenId != "": - condition = fmt.Sprintf("%s and user_id in (select user_id from auth_token where id = ?)", condition) + condition = fmt.Sprintf("%s and fk_user_id in (select user_id from auth_token where id = ?)", condition) searchArgs = append(searchArgs, opts.withAuthTokenId) case opts.withUserId != "": - condition = fmt.Sprintf("%s and user_id = ?", condition) + condition = fmt.Sprintf("%s and fk_user_id = ?", condition) searchArgs = append(searchArgs, opts.withUserId) } @@ -316,13 +317,14 @@ func (r *Repository) searchSessions(ctx context.Context, condition string, searc } type Session struct { - UserId string `gorm:"primaryKey"` + FkUserId string `gorm:"primaryKey"` Id string `gorm:"primaryKey"` Type string `gorm:"default:null"` Endpoint string `gorm:"default:null"` Status string `gorm:"default:null"` ScopeId string `gorm:"default:null"` TargetId string `gorm:"default:null"` + UserId string `gorm:"default:null"` Item string `gorm:"default:null"` } diff --git a/internal/clientcache/internal/cache/repository_sessions_test.go b/internal/clientcache/internal/cache/repository_sessions_test.go index 83461659a..3a8e7c512 100644 --- a/internal/clientcache/internal/cache/repository_sessions_test.go +++ b/internal/clientcache/internal/cache/repository_sessions_test.go @@ -5,6 +5,7 @@ package cache import ( "context" + "encoding/json" "sync" "testing" @@ -57,6 +58,7 @@ func TestRepository_refreshSessions(t *testing.T) { Endpoint: "address1", ScopeId: "p_123", TargetId: "ttcp_123", + UserId: "u_123", Type: "tcp", }, { @@ -65,6 +67,7 @@ func TestRepository_refreshSessions(t *testing.T) { Endpoint: "address2", ScopeId: "p_123", TargetId: "ttcp_123", + UserId: "u_123", Type: "tcp", }, { @@ -73,14 +76,31 @@ func TestRepository_refreshSessions(t *testing.T) { Endpoint: "address3", ScopeId: "p_123", TargetId: "ttcp_123", + UserId: "u_123", Type: "tcp", }, } + var want []*Session + for _, sess := range ss { + si, err := json.Marshal(sess) + require.NoError(t, err) + want = append(want, &Session{ + FkUserId: u.Id, + Id: sess.Id, + Type: sess.Type, + Status: sess.Status, + Endpoint: sess.Endpoint, + ScopeId: sess.ScopeId, + TargetId: sess.TargetId, + UserId: sess.UserId, + Item: string(si), + }) + } cases := []struct { name string u *user sess []*sessions.Session - wantCount int + want []*Session errorContains string }{ { @@ -89,8 +109,8 @@ func TestRepository_refreshSessions(t *testing.T) { Address: addr, Id: at.UserId, }, - sess: ss, - wantCount: len(ss), + sess: ss, + want: want, }, { name: "repeated session with different values", @@ -102,7 +122,12 @@ func TestRepository_refreshSessions(t *testing.T) { Id: ss[0].Id, Status: "a different status", }), - wantCount: len(ss), + want: append(want[1:], &Session{ + FkUserId: want[0].FkUserId, + Id: want[0].Id, + Status: "a different status", + Item: `{"id":"ttcp_1","created_time":"0001-01-01T00:00:00Z","updated_time":"0001-01-01T00:00:00Z","expiration_time":"0001-01-01T00:00:00Z","status":"a different status"}`, + }), }, { name: "nil user", @@ -129,7 +154,7 @@ func TestRepository_refreshSessions(t *testing.T) { rw := db.New(s) var got []*Session require.NoError(t, rw.SearchWhere(ctx, &got, "true", nil)) - assert.Len(t, got, tc.wantCount) + assert.ElementsMatch(t, got, tc.want) t.Cleanup(func() { refTok := &refreshToken{ @@ -181,6 +206,7 @@ func TestRepository_RefreshSessions_withRefreshTokens(t *testing.T) { Endpoint: "address1", ScopeId: "p_123", TargetId: "ttcp_123", + UserId: "u_123", Type: "tcp", }, { @@ -189,6 +215,7 @@ func TestRepository_RefreshSessions_withRefreshTokens(t *testing.T) { Endpoint: "address2", ScopeId: "p_123", TargetId: "ttcp_123", + UserId: "u_123", Type: "tcp", }, }, @@ -199,6 +226,7 @@ func TestRepository_RefreshSessions_withRefreshTokens(t *testing.T) { Endpoint: "address3", ScopeId: "p_123", TargetId: "ttcp_123", + UserId: "u_123", Type: "tcp", }, }, @@ -298,6 +326,7 @@ func TestRepository_ListSessions(t *testing.T) { Endpoint: "address1", ScopeId: "p_123", TargetId: "ttcp_123", + UserId: "u_123", Type: "tcp", }, { @@ -306,6 +335,7 @@ func TestRepository_ListSessions(t *testing.T) { Endpoint: "address2", ScopeId: "p_123", TargetId: "ttcp_123", + UserId: "u_123", Type: "tcp", }, { @@ -314,6 +344,7 @@ func TestRepository_ListSessions(t *testing.T) { Endpoint: "address3", ScopeId: "p_123", TargetId: "ttcp_123", + UserId: "u_123", Type: "tcp", }, } @@ -411,6 +442,7 @@ func TestRepository_QuerySessions(t *testing.T) { Endpoint: "address1", ScopeId: "p_123", TargetId: "ttcp_123", + UserId: "u_123", Type: "tcp", }, { @@ -419,6 +451,7 @@ func TestRepository_QuerySessions(t *testing.T) { Endpoint: "address2", ScopeId: "p_123", TargetId: "ttcp_123", + UserId: "u_123", Type: "tcp", }, { @@ -427,6 +460,7 @@ func TestRepository_QuerySessions(t *testing.T) { Endpoint: "address3", ScopeId: "p_123", TargetId: "ttcp_123", + UserId: "u_123", Type: "tcp", }, } diff --git a/internal/clientcache/internal/cache/repository_targets.go b/internal/clientcache/internal/cache/repository_targets.go index c4d34b32f..5cad4e59b 100644 --- a/internal/clientcache/internal/cache/repository_targets.go +++ b/internal/clientcache/internal/cache/repository_targets.go @@ -109,8 +109,8 @@ func (r *Repository) refreshTargets(ctx context.Context, u *user, tokens map[Aut _, err = r.rw.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, func(r db.Reader, w db.Writer) error { switch { case oldRefreshToken == nil: - if _, err := w.Exec(ctx, "delete from target where user_id = @user_id", - []any{sql.Named("user_id", u.Id)}); err != nil { + if _, err := w.Exec(ctx, "delete from target where fk_user_id = @fk_user_id", + []any{sql.Named("fk_user_id", u.Id)}); err != nil { return err } case len(removedIds) > 0: @@ -182,8 +182,8 @@ func (r *Repository) fullFetchTargets(ctx context.Context, u *user, tokens map[A event.WriteSysEvent(ctx, op, fmt.Sprintf("updating %d targets for user %v", len(resp), u)) _, err = r.rw.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, func(r db.Reader, w db.Writer) error { - if _, err := w.Exec(ctx, "delete from target where user_id = @user_id", - []any{sql.Named("user_id", u.Id)}); err != nil { + if _, err := w.Exec(ctx, "delete from target where fk_user_id = @fk_user_id", + []any{sql.Named("fk_user_id", u.Id)}); err != nil { return err } @@ -221,17 +221,18 @@ func upsertTargets(ctx context.Context, w db.Writer, u *user, in []*targets.Targ return errors.Wrap(ctx, err, op) } newTarget := &Target{ - UserId: u.Id, + FkUserId: u.Id, Id: t.Id, Name: t.Name, Description: t.Description, Address: t.Address, ScopeId: t.ScopeId, + Type: t.Type, Item: string(item), } onConflict := db.OnConflict{ - Target: db.Columns{"user_id", "id"}, - Action: db.UpdateAll(true), + Target: db.Columns{"fk_user_id", "id"}, + Action: db.SetColumns([]string{"name", "description", "address", "scope_id", "type", "item"}), } if err := w.Create(ctx, newTarget, db.WithOnConflict(&onConflict)); err != nil { return errors.Wrap(ctx, err, op) @@ -262,7 +263,7 @@ func (r *Repository) QueryTargets(ctx context.Context, authTokenId, query string return nil, errors.New(ctx, errors.InvalidParameter, op, "query is missing") } - w, err := mql.Parse(query, Target{}, mql.WithIgnoredFields("UserId", "Item")) + w, err := mql.Parse(query, Target{}, mql.WithIgnoredFields("FkUserId", "Item")) if err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithCode(errors.InvalidParameter)) } @@ -290,10 +291,10 @@ func (r *Repository) searchTargets(ctx context.Context, condition string, search case opts.withAuthTokenId == "" && opts.withUserId == "": return nil, errors.New(ctx, errors.InvalidParameter, op, "neither user id nor auth token id were provided") case opts.withAuthTokenId != "": - condition = fmt.Sprintf("%s and user_id in (select user_id from auth_token where id = ?)", condition) + condition = fmt.Sprintf("%s and fk_user_id in (select user_id from auth_token where id = ?)", condition) searchArgs = append(searchArgs, opts.withAuthTokenId) case opts.withUserId != "": - condition = fmt.Sprintf("%s and user_id = ?", condition) + condition = fmt.Sprintf("%s and fk_user_id = ?", condition) searchArgs = append(searchArgs, opts.withUserId) } @@ -314,8 +315,9 @@ func (r *Repository) searchTargets(ctx context.Context, condition string, search } type Target struct { - UserId string `gorm:"primaryKey"` + FkUserId string `gorm:"primaryKey"` Id string `gorm:"primaryKey"` + Type string `gorm:"default:null"` Name string `gorm:"default:null"` Description string `gorm:"default:null"` Address string `gorm:"default:null"` diff --git a/internal/clientcache/internal/cache/repository_targets_test.go b/internal/clientcache/internal/cache/repository_targets_test.go index c767e54cb..a6923ac81 100644 --- a/internal/clientcache/internal/cache/repository_targets_test.go +++ b/internal/clientcache/internal/cache/repository_targets_test.go @@ -5,6 +5,7 @@ package cache import ( "context" + "encoding/json" "sync" "testing" @@ -47,6 +48,7 @@ func TestRepository_refreshTargets(t *testing.T) { { Id: "ttcp_1", Name: "name1", + Description: "description1", Type: "tcp", ScopeId: "p_123", SessionMaxSeconds: 111, @@ -68,11 +70,26 @@ func TestRepository_refreshTargets(t *testing.T) { SessionMaxSeconds: 333, }, } + var want []*Target + for _, tar := range ts { + ti, err := json.Marshal(tar) + require.NoError(t, err) + want = append(want, &Target{ + FkUserId: u.Id, + Id: tar.Id, + Name: tar.Name, + Description: tar.Description, + Address: tar.Address, + ScopeId: tar.ScopeId, + Type: tar.Type, + Item: string(ti), + }) + } cases := []struct { name string u *user targets []*targets.Target - wantCount int + want []*Target errorContains string }{ { @@ -81,9 +98,11 @@ func TestRepository_refreshTargets(t *testing.T) { Id: at.UserId, Address: addr, }, - targets: ts, - wantCount: len(ts), + targets: ts, + want: want, }, + // this test case must run after the above test case so as to exercise + // the update logic of refresh. { name: "repeated target with different values", u: &user{ @@ -94,7 +113,13 @@ func TestRepository_refreshTargets(t *testing.T) { Id: ts[0].Id, Name: "a different name", }), - wantCount: len(ts), + want: append(want[1:], + &Target{ + FkUserId: want[0].FkUserId, + Id: want[0].Id, + Name: "a different name", + Item: `{"id":"ttcp_1","name":"a different name","created_time":"0001-01-01T00:00:00Z","updated_time":"0001-01-01T00:00:00Z"}`, + }), }, { name: "nil user", @@ -121,7 +146,7 @@ func TestRepository_refreshTargets(t *testing.T) { rw := db.New(s) var got []*Target require.NoError(t, rw.SearchWhere(ctx, &got, "true", nil)) - assert.Len(t, got, tc.wantCount) + assert.ElementsMatch(t, got, tc.want) t.Cleanup(func() { refTok := &refreshToken{ diff --git a/internal/clientcache/internal/cache/search_test.go b/internal/clientcache/internal/cache/search_test.go index cbeaf491c..d4a83963c 100644 --- a/internal/clientcache/internal/cache/search_test.go +++ b/internal/clientcache/internal/cache/search_test.go @@ -119,14 +119,14 @@ func TestSearch(t *testing.T) { require.NoError(t, rw.Create(ctx, at)) targets := []any{ - &Target{UserId: u.Id, Id: "t_1", Name: "one", Item: `{"id": "t_1", "name": "one"}`}, - &Target{UserId: u.Id, Id: "t_2", Name: "two", Item: `{"id": "t_2", "name": "two"}`}, + &Target{FkUserId: u.Id, Id: "t_1", Name: "one", Type: "tcp", Item: `{"id": "t_1", "name": "one", "type": "tcp"}`}, + &Target{FkUserId: u.Id, Id: "t_2", Name: "two", Type: "tcp", Item: `{"id": "t_2", "name": "two", "type": "tcp"}`}, } require.NoError(t, rw.CreateItems(ctx, targets)) sessions := []any{ - &Session{UserId: u.Id, Id: "s_1", Endpoint: "one", Item: `{"id": "s_1", "endpoint": "one"}`}, - &Session{UserId: u.Id, Id: "s_2", Endpoint: "two", Item: `{"id": "s_2", "endpoint": "two"}`}, + &Session{FkUserId: u.Id, Id: "s_1", Endpoint: "one", Type: "tcp", UserId: "u123", Item: `{"id": "s_1", "endpoint": "one", "type": "tcp", "user_id": "u123"}`}, + &Session{FkUserId: u.Id, Id: "s_2", Endpoint: "two", Type: "ssh", UserId: "u321", Item: `{"id": "s_2", "endpoint": "two", "type": "ssh", "user_id": "u321"}`}, } require.NoError(t, rw.CreateItems(ctx, sessions)) } @@ -147,8 +147,8 @@ func TestSearch(t *testing.T) { }) assert.NoError(t, err) assert.EqualValues(t, &SearchResult{Targets: []*targets.Target{ - {Id: "t_1", Name: "one"}, - {Id: "t_2", Name: "two"}, + {Id: "t_1", Name: "one", Type: "tcp"}, + {Id: "t_2", Name: "two", Type: "tcp"}, }}, got) }) @@ -160,7 +160,20 @@ func TestSearch(t *testing.T) { }) assert.NoError(t, err) assert.EqualValues(t, &SearchResult{Targets: []*targets.Target{ - {Id: "t_1", Name: "one"}, + {Id: "t_1", Name: "one", Type: "tcp"}, + }}, got) + }) + + t.Run("query targets on type", func(t *testing.T) { + got, err := ss.Search(ctx, SearchParams{ + Resource: "targets", + AuthTokenId: at.Id, + Query: `type="tcp"`, + }) + assert.NoError(t, err) + assert.EqualValues(t, &SearchResult{Targets: []*targets.Target{ + {Id: "t_1", Name: "one", Type: "tcp"}, + {Id: "t_2", Name: "two", Type: "tcp"}, }}, got) }) @@ -175,6 +188,17 @@ func TestSearch(t *testing.T) { assert.Nil(t, got) }) + t.Run("query targets bad column owner user id", func(t *testing.T) { + got, err := ss.Search(ctx, SearchParams{ + Resource: "targets", + AuthTokenId: at.Id, + Query: `fk_user_id % "u"`, + }) + assert.Error(t, err) + assert.ErrorContains(t, err, `invalid column "fk_user_id"`) + assert.Nil(t, got) + }) + t.Run("Filter targets", func(t *testing.T) { got, err := ss.Search(ctx, SearchParams{ Resource: "targets", @@ -183,7 +207,7 @@ func TestSearch(t *testing.T) { }) assert.NoError(t, err) assert.EqualValues(t, &SearchResult{Targets: []*targets.Target{ - {Id: "t_1", Name: "one"}, + {Id: "t_1", Name: "one", Type: "tcp"}, }}, got) }) @@ -194,8 +218,8 @@ func TestSearch(t *testing.T) { }) assert.NoError(t, err) assert.EqualValues(t, &SearchResult{Sessions: []*sessions.Session{ - {Id: "s_1", Endpoint: "one"}, - {Id: "s_2", Endpoint: "two"}, + {Id: "s_1", Endpoint: "one", Type: "tcp", UserId: "u123"}, + {Id: "s_2", Endpoint: "two", Type: "ssh", UserId: "u321"}, }}, got) }) @@ -207,10 +231,33 @@ func TestSearch(t *testing.T) { }) assert.NoError(t, err) assert.EqualValues(t, &SearchResult{Sessions: []*sessions.Session{ - {Id: "s_1", Endpoint: "one"}, + {Id: "s_1", Endpoint: "one", Type: "tcp", UserId: "u123"}, }}, got) }) + t.Run("query sessions user id", func(t *testing.T) { + got, err := ss.Search(ctx, SearchParams{ + Resource: "sessions", + AuthTokenId: at.Id, + Query: `user_id="u123"`, + }) + assert.NoError(t, err) + assert.EqualValues(t, &SearchResult{Sessions: []*sessions.Session{ + {Id: "s_1", Endpoint: "one", Type: "tcp", UserId: "u123"}, + }}, got) + }) + + t.Run("query sessions bad column owner user id", func(t *testing.T) { + got, err := ss.Search(ctx, SearchParams{ + Resource: "sessions", + AuthTokenId: at.Id, + Query: `fk_user_id % "u"`, + }) + assert.Error(t, err) + assert.ErrorContains(t, err, `invalid column "fk_user_id"`) + assert.Nil(t, got) + }) + t.Run("Filter sessions", func(t *testing.T) { got, err := ss.Search(ctx, SearchParams{ Resource: "sessions", @@ -219,7 +266,7 @@ func TestSearch(t *testing.T) { }) assert.NoError(t, err) assert.EqualValues(t, &SearchResult{Sessions: []*sessions.Session{ - {Id: "s_1", Endpoint: "one"}, + {Id: "s_1", Endpoint: "one", Type: "tcp", UserId: "u123"}, }}, got) }) diff --git a/internal/clientcache/internal/cache/status.go b/internal/clientcache/internal/cache/status.go index 11fdf0ece..880233f5c 100644 --- a/internal/clientcache/internal/cache/status.go +++ b/internal/clientcache/internal/cache/status.go @@ -149,7 +149,7 @@ func (s *StatusService) resourceStatus(ctx context.Context, u *user, rt resource ret.LastError = errStatus err = func() error { - query := fmt.Sprintf("select count(*) from %s where user_id = @user_id", rt) + query := fmt.Sprintf("select count(*) from %s where fk_user_id = @user_id", rt) r, err := s.repo.rw.Query(ctx, query, []any{sql.Named("user_id", u.Id)}) if err != nil { return errors.Wrap(ctx, err, op) diff --git a/internal/clientcache/internal/cache/store_test.go b/internal/clientcache/internal/cache/store_test.go index 928fb3551..0475d140a 100644 --- a/internal/clientcache/internal/cache/store_test.go +++ b/internal/clientcache/internal/cache/store_test.go @@ -444,6 +444,7 @@ func TestTarget(t *testing.T) { Description: "target desc", Address: "some address", ScopeId: "p_123", + Type: "tcp", Item: "{id:'tssh_1234567890'}", } require.ErrorContains(t, rw.Create(ctx, unknownTarget), "constraint failed") @@ -451,12 +452,13 @@ func TestTarget(t *testing.T) { t.Run("target actions", func(t *testing.T) { target := &Target{ - UserId: u.Id, + FkUserId: u.Id, Id: "tssh_1234567890", Name: "target", Description: "target desc", Address: "some address", ScopeId: "p_123", + Type: "tcp", Item: "{id:'tssh_1234567890'}", } @@ -471,27 +473,28 @@ func TestTarget(t *testing.T) { // TODO: Once the sqlite driver properly builds the delete query call // n, err = rw.Delete(ctx, target) instead of the Exec call - n, err = rw.Exec(ctx, "delete from target where (user_id, id) IN (values (?, ?))", - []any{target.UserId, target.Id}) + n, err = rw.Exec(ctx, "delete from target where (fk_user_id, id) IN (values (?, ?))", + []any{target.FkUserId, target.Id}) assert.NoError(t, err) assert.Equal(t, 1, n) }) t.Run("lookup a target", func(t *testing.T) { target := &Target{ - UserId: u.Id, + FkUserId: u.Id, Id: "tssh_1234567890", Name: "target", Description: "target desc", Address: "some address", ScopeId: "p_123", + Type: "tcp", Item: "{id:'tssh_1234567890'}", } require.NoError(t, rw.Create(ctx, target)) lookTar := &Target{ - UserId: target.UserId, - Id: target.Id, + FkUserId: target.FkUserId, + Id: target.Id, } assert.NoError(t, rw.LookupById(ctx, lookTar)) assert.NotNil(t, lookTar) @@ -503,12 +506,13 @@ func TestTarget(t *testing.T) { t.Run("deleting the user deletes the target", func(t *testing.T) { target := &Target{ - UserId: u.Id, + FkUserId: u.Id, Id: "tssh_1234567890", Name: "target", Description: "target desc", Address: "some address", ScopeId: "p_123", + Type: "tcp", Item: "{id:'tssh_1234567890'}", } require.NoError(t, rw.Create(ctx, target)) @@ -521,8 +525,8 @@ func TestTarget(t *testing.T) { require.Equal(t, 1, n) lookTar := &Target{ - UserId: target.UserId, - Id: target.Id, + FkUserId: target.FkUserId, + Id: target.Id, } assert.ErrorContains(t, rw.LookupById(ctx, lookTar), "not found") }) @@ -552,11 +556,12 @@ func TestSession(t *testing.T) { }) t.Run("session actions", func(t *testing.T) { session := &Session{ - UserId: u.Id, + FkUserId: u.Id, Id: "s_1234567890", Endpoint: "endpoint", ScopeId: "p_123", TargetId: "ttcp_123", + UserId: "u_123", Item: "{id:'s_1234567890'}", } @@ -571,26 +576,27 @@ func TestSession(t *testing.T) { // TODO: Once the sqlite driver properly builds the delete query call // n, err = rw.Delete(ctx, session) instead of the Exec call - n, err = rw.Exec(ctx, "delete from session where (user_id, id) IN (values (?, ?))", - []any{session.UserId, session.Id}) + n, err = rw.Exec(ctx, "delete from session where (fk_user_id, id) IN (values (?, ?))", + []any{session.FkUserId, session.Id}) assert.NoError(t, err) assert.Equal(t, 1, n) }) t.Run("lookup a session", func(t *testing.T) { session := &Session{ - UserId: u.Id, + FkUserId: u.Id, Id: "s_1234567890", Endpoint: "endpoint", ScopeId: "p_123", TargetId: "ttcp_123", + UserId: "u_123", Item: "{id:'s_1234567890'}", } require.NoError(t, rw.Create(ctx, session)) lookSess := &Session{ - UserId: u.Id, - Id: session.Id, + FkUserId: u.Id, + Id: session.Id, } assert.NoError(t, rw.LookupById(ctx, lookSess)) assert.NotNil(t, lookSess) @@ -602,11 +608,12 @@ func TestSession(t *testing.T) { t.Run("deleting the user deletes the session", func(t *testing.T) { session := &Session{ - UserId: u.Id, + FkUserId: u.Id, Id: "s_1234567890", Endpoint: "endpoint", ScopeId: "p_123", TargetId: "ttcp_123", + UserId: "u_123", Item: "{id:'s_1234567890'}", } require.NoError(t, rw.Create(ctx, session)) @@ -619,8 +626,8 @@ func TestSession(t *testing.T) { require.Equal(t, 1, n) lookSess := &Session{ - UserId: session.UserId, - Id: session.Id, + FkUserId: session.FkUserId, + Id: session.Id, } assert.ErrorContains(t, rw.LookupById(ctx, lookSess), "not found") }) diff --git a/internal/clientcache/internal/daemon/token_handler.go b/internal/clientcache/internal/daemon/token_handler.go index 7f58a0591..79e49874e 100644 --- a/internal/clientcache/internal/daemon/token_handler.go +++ b/internal/clientcache/internal/daemon/token_handler.go @@ -114,7 +114,7 @@ func newTokenHandlerFunc(ctx context.Context, repo *cache.Repository, refresher AuthTokenId: perReq.AuthTokenId, } if err = repo.AddKeyringToken(ctx, perReq.BoundaryAddr, kt); err != nil { - writeError(w, "Failed to add a keyring stored token", http.StatusInternalServerError) + writeError(w, fmt.Sprintf("Failed to add a keyring stored token: %v", err), http.StatusInternalServerError) return } case perReq.AuthToken != "": diff --git a/internal/clientcache/internal/db/schema.sql b/internal/clientcache/internal/db/schema.sql index d296504f0..5372ee7ab 100644 --- a/internal/clientcache/internal/db/schema.sql +++ b/internal/clientcache/internal/db/schema.sql @@ -109,7 +109,7 @@ create table if not exists keyring_token ( -- specific fields extracted to facilitate searching over those fields create table if not exists target ( -- the boundary user id of the user who has was able to read/list this target - user_id text not null + fk_user_id text not null references user(id) on delete cascade, -- the boundary id of the target @@ -119,19 +119,20 @@ create table if not exists target ( -- from the boundary resource name text, description text, + type text, address text, scope_id text, -- item is the json representation of this resource from the perspective of -- the the requesting user. item text, - primary key (user_id, id) + primary key (fk_user_id, id) ); -- session contains cached boundary session resource for a specific user and -- with specific fields extracted to facilitate searching over those fields create table if not exists session ( -- the boundary user id of the user who has was able to read/list this resource - user_id text not null + fk_user_id text not null references user(id) on delete cascade, -- the resource id from boundary of this session @@ -144,10 +145,14 @@ create table if not exists session ( status text, scope_id text, target_id text, + -- The user_id is the the id of the user that created this session. This can + -- be different from the fk_user_id which is the id of the boundary user + -- which synced this record into the cache. + user_id text, -- item is the json representation of this resource from the perspective of - -- of the user whose id is set in user_id + -- of the user whose id is set in fk_user_id item text, - primary key (user_id, id) + primary key (fk_user_id, id) ); -- contains errors from the last attempt to sync data from boundary for a