8000 fix: add postgres triggers to remove deleted users from user_links by Emyrk · Pull Request #12117 · coder/coder · GitHub
[go: up one dir, main page]

Skip to content

fix: add postgres triggers to remove deleted users from user_links #12117

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add migrations to prevent deleted users with links
  • Loading branch information
Emyrk committed Feb 13, 2024
commit 188f03ba50a1d0265b6198461fde655171a6f990
43 changes: 29 additions & 14 deletions coderd/database/dbmem/dbmem.go
Original file line number Diff line number Diff line change
Expand Up @@ -5573,10 +5573,26 @@ func (q *FakeQuerier) InsertUserGroupsByName(_ context.Context, arg database.Ins
return nil
}

// Took the error from the real database.
var deletedUserLinkError = &pq.Error{
Severity: "ERROR",
// "raise_exception" error
Code: "P0001",
Message: "Cannot create user_link for deleted user",
Where: "PL/pgSQL function insert_user_links_fail_if_user_deleted() line 7 at RAISE",
File: "pl_exec.c",
Line: "3864",
Routine: "exec_stmt_raise",
}

func (q *FakeQuerier) InsertUserLink(_ context.Context, args database.InsertUserLinkParams) (database.UserLink, error) {
q.mutex.Lock()
defer q.mutex.Unlock()

if u, err := q.getUserByIDNoLock(args.UserID); err == nil && u.Deleted {
return database.UserLink{}, deletedUserLinkError
}

//nolint:gosimple
link := database.UserLink{
UserID: args.UserID,
Expand Down Expand Up @@ -6736,20 +6752,15 @@ func (q *FakeQuerier) UpdateUserDeletedByID(_ context.Context, params database.U
if u.ID == params.ID {
u.Deleted = params.Deleted
q.users[i] = u
// NOTE: In the real world, this is done by a trigger.
i := 0
for {
if i >= len(q.apiKeys) {
break
}
k := q.apiKeys[i]
if k.UserID == u.ID {
q.apiKeys[i] = q.apiKeys[len(q.apiKeys)-1]
q.apiKeys = q.apiKeys[:len(q.apiKeys)-1]
// We removed an element, so decrement
i--
}
i++
if params.Deleted {
// NOTE: In the real world, this is done by a trigger.
q.apiKeys = slices.DeleteFunc(q.apiKeys, func(u database.APIKey) bool {
return params.ID == u.UserID
})

q.userLinks = slices.DeleteFunc(q.userLinks, func(u database.UserLink) bool {
return params.ID == u.UserID
})
}
return nil
}
Expand Down Expand Up @@ -6804,6 +6815,10 @@ func (q *FakeQuerier) UpdateUserLink(_ context.Context, params database.UpdateUs
q.mutex.Lock()
defer q.mutex.Unlock()

if u, err := q.getUserByIDNoLock(params.UserID); err == nil && u.Deleted {
return database.UserLink{}, deletedUserLinkError
}

for i, link := range q.userLinks {
if link.UserID == params.UserID && link.LoginType == params.LoginType {
link.OAuthAccessToken = params.OAuthAccessToken
Expand Down
25 changes: 23 additions & 2 deletions coderd/userauth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"net/url"
"strings"
"testing"
"time"

"github.com/coreos/go-oidc/v3/oidc"
"github.com/golang-jwt/jwt/v4"
Expand All @@ -24,6 +25,7 @@ import (
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/coderd/coderdtest/oidctest"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbauthz"
"github.com/coder/coder/v2/coderd/database/dbgen"
"github.com/coder/coder/v2/coderd/database/dbtestutil"
"github.com/coder/coder/v2/coderd/promoauth"
Expand Down Expand Up @@ -632,7 +634,7 @@ func TestUserOAuth2Github(t *testing.T) {
coderEmail,
}

owner := coderdtest.New(t, &coderdtest.Options{
owner, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{
Auditor: auditor,
GithubOAuth2Config: &coderd.GithubOAuth2Config{
AllowSignups: true,
Expand All @@ -655,9 +657,12 @@ func TestUserOAuth2Github(t *testing.T) {
},
},
})
coderdtest.CreateFirstUser(t, owner)
first := coderdtest.CreateFirstUser(t, owner)

ctx := testutil.Context(t, testutil.WaitLong)
ownerUser, err := owner.User(context.Background(), "me")
require.NoError(t, err)

// Create the user, then delete the user, then create again.
// This causes the email change to fail.
client := codersdk.New(owner.URL)
Expand All @@ -668,6 +673,22 @@ func TestUserOAuth2Github(t *testing.T) {

err = owner.DeleteUser(ctx, deleted.ID)
require.NoError(t, err)
// Check no user links for the user
links, err := db.GetUserLinksByUserID(dbauthz.As(ctx, coderdtest.AuthzUserSubject(ownerUser, first.OrganizationID)), deleted.ID)
require.NoError(t, err)
6306 require.Empty(t, links)

// Make sure a user_link cannot be created with a deleted user.
_, err = db.InsertUserLink(dbauthz.AsSystemRestricted(ctx), database.InsertUserLinkParams{
UserID: deleted.ID,
LoginType: "github",
LinkedID: "100",
OAuthAccessToken: "random",
OAuthRefreshToken: "random",
OAuthExpiry: time.Now(),
DebugContext: []byte(`{}`),
})
require.ErrorContains(t, err, "Cannot create user_link for deleted user")

// Create the user again.
client, _ = fake.Login(t, client, jwt.MapClaims{})
Expand Down
0