Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
305f696
fix: use unique ID for linked accounts
sreya Aug 9, 2022
b4ab301
fixup a bunch of stuff
sreya Aug 9, 2022
dd2df9c
gofmt
sreya Aug 9, 2022
0356f46
make fake db happy
sreya Aug 9, 2022
6b1b900
make audit happy
sreya Aug 9, 2022
8f63d5c
fix some tests
sreya Aug 10, 2022
de7db33
make gen
sreya Aug 10, 2022
5fdf899
fix tests
sreya Aug 10, 2022
3a4d049
fmt
sreya Aug 10, 2022
4108ece
begin refactoring PR
sreya Aug 11, 2022
14b5382
finish migration
sreya Aug 12, 2022
8553501
use main sql.dump
sreya Aug 12, 2022
f748d3d
lift error
sreya Aug 12, 2022
c1b9871
new migration
sreya Aug 12, 2022
e41c103
more rewriting
sreya Aug 12, 2022
bb9b777
even more rewriting
sreya Aug 12, 2022
d940dae
finish up some test fixing
sreya Aug 12, 2022
c97d572
typos
sreya Aug 12, 2022
10bfe77
Merge branch 'main' into jon/userauth
sreya Aug 12, 2022
28a37f1
fix some remaining tests
sreya Aug 12, 2022
c889bf0
fix a gnarly bug
sreya Aug 12, 2022
0196a49
add a down migration
sreya Aug 12, 2022
b5dc95b
add fkey on user_links, fix tests, add comments
sreya Aug 12, 2022
f2f76e9
add login_type to users table
sreya Aug 12, 2022
940ced4
Merge branch 'main' into jon/userauth
sreya Aug 12, 2022
eb266db
fix login_type query
sreya Aug 13, 2022
4671bf6
fix tests
sreya Aug 13, 2022
c41f4e6
fix audit
sreya Aug 13, 2022
f3d8392
fix down
sreya Aug 13, 2022
cc8400b
fix one more test
sreya Aug 13, 2022
5c7cbae
Merge branch 'main' into jon/userauth
sreya Aug 17, 2022
083d256
pr comments
sreya Aug 17, 2022
92c185d
fix conflicting migration file
sreya Aug 17, 2022
05595d8
generate.sh
sreya Aug 17, 2022
aa90148
butcher the english language to appease colin
sreya Aug 17, 2022
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 login_type to users table
  • Loading branch information
sreya committed Aug 12, 2022
commit f2f76e97c30f9e8c4943097fa5818c37215f9fe8
3 changes: 2 additions & 1 deletion coderd/database/dump.sql

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions coderd/database/migrations/000034_linked_user_id.up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,19 @@ ALTER TABLE api_keys
DROP COLUMN oauth_id_token,
DROP COLUMN oauth_expiry;

ALTER TABLE users ADD COLUMN login_type login_type NOT NULL DEFAULT 'password';

UPDATE
users
SET
login_type = (
SELECT
login_type
FROM
user_links
WHERE
user_links.user_id = users.id
LIMIT 1
);

COMMIT;
1 change: 1 addition & 0 deletions coderd/database/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion coderd/database/postgres/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@ func Open() (string, func(), error) {
}
err = database.MigrateUp(db)
if err != nil {
fmt.Printf("err: %v\n", err)
return xerrors.Errorf("migrate db: %w", err)
}

Expand Down
29 changes: 20 additions & 9 deletions coderd/database/queries.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions coderd/database/queries/users.sql
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ INSERT INTO
hashed_password,
created_at,
updated_at,
rbac_roles
rbac_roles,
login_type
)
VALUES
($1, $2, $3, $4, $5, $6, $7) RETURNING *;
($1, $2, $3, $4, $5, $6, $7, $8) RETURNING *;

-- name: UpdateUserProfile :one
UPDATE
Expand Down
42 changes: 24 additions & 18 deletions coderd/userauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) {
return
}

user, link, err := findLinkedUser(ctx, api.Database, database.LoginTypeGithub, githubLinkedID(ghUser), verifiedEmails...)
user, link, err := findLinkedUser(ctx, api.Database, githubLinkedID(ghUser), verifiedEmails...)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{
Message: "An internal error occurred.",
Expand All @@ -146,9 +146,9 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) {
return
}

if link.UserID != uuid.Nil && link.LoginType != database.LoginTypeGithub {
httpapi.Write(rw, http.StatusConflict, codersdk.Response{
Message: fmt.Sprintf("Incorrect login type, attempting to use %q but user is of login type %q", database.LoginTypeOIDC, link.LoginType),
if user.ID != uuid.Nil && user.LoginType != database.LoginTypeGithub {
httpapi.Write(rw, http.StatusForbidden, codersdk.Response{
Message: fmt.Sprintf("Incorrect login type, attempting to use %q but user is of login type %q", database.LoginTypeOIDC, user.LoginType),
})
return
}
Expand Down Expand Up @@ -184,10 +184,13 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) {
})
return
}
user, _, err = api.createUser(r.Context(), codersdk.CreateUserRequest{
Email: *verifiedEmail.Email,
Username: *ghUser.Login,
OrganizationID: organizationID,
user, _, err = api.createUser(ctx, createUserRequest{
CreateUserRequest: codersdk.CreateUserRequest{
Email: *verifiedEmail.Email,
Username: *ghUser.Login,
OrganizationID: organizationID,
},
LoginType: database.LoginTypeGithub,
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{
Expand Down Expand Up @@ -332,7 +335,7 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
}
}

user, link, err := findLinkedUser(ctx, api.Database, database.LoginTypeOIDC, oidcLinkedID(idToken), claims.Email)
user, link, err := findLinkedUser(ctx, api.Database, oidcLinkedID(idToken), claims.Email)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{
Message: "Failed to find user.",
Expand All @@ -348,9 +351,9 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
return
}

if link.UserID != uuid.Nil && link.LoginType != database.LoginTypeOIDC {
httpapi.Write(rw, http.StatusConflict, codersdk.Response{
Message: fmt.Sprintf("Incorrect login type, attempting to use %q but user is of login type %q", database.LoginTypeOIDC, link.LoginType),
if user.ID != uuid.Nil && user.LoginType != database.LoginTypeOIDC {
httpapi.Write(rw, http.StatusForbidden, codersdk.Response{
Message: fmt.Sprintf("Incorrect login type, attempting to use %q but user is of login type %q", database.LoginTypeOIDC, user.LoginType),
})
return
}
Expand All @@ -365,10 +368,13 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
organizationID = organizations[0].ID
}

user, _, err = api.createUser(ctx, codersdk.CreateUserRequest{
Email: claims.Email,
Username: claims.Username,
OrganizationID: organizationID,
user, _, err = api.createUser(ctx, createUserRequest{
CreateUserRequest: codersdk.CreateUserRequest{
Email: claims.Email,
Username: claims.Username,
OrganizationID: organizationID,
},
LoginType: database.LoginTypeOIDC,
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{
Expand Down Expand Up @@ -477,7 +483,7 @@ func oidcLinkedID(tok *oidc.IDToken) string {

// findLinkedUser tries to find a user by their unique OAuth-linked ID.
// If it doesn't not find it, it returns the user by their email.
func findLinkedUser(ctx context.Context, db database.Store, loginType database.LoginType, linkedID string, emails ...string) (database.User, database.UserLink, error) {
func findLinkedUser(ctx context.Context, db database.Store, linkedID string, emails ...string) (database.User, database.UserLink, error) {
var (
user database.User
link database.UserLink
Expand Down Expand Up @@ -518,7 +524,7 @@ func findLinkedUser(ctx context.Context, db database.Store, loginType database.L
// possible that a user_link exists without a populated 'linked_id'.
link, err = db.GetUserLinkByUserIDLoginType(ctx, database.GetUserLinkByUserIDLoginTypeParams{
UserID: user.ID,
LoginType: loginType,
LoginType: user.LoginType,
})
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return database.User{}, database.UserLink{}, xerrors.Errorf("get user link by user id and login type: %w", err)
Expand Down
3 changes: 2 additions & 1 deletion coderd/userauth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ func TestUserOAuth2Github(t *testing.T) {
resp := oauth2Callback(t, client)
require.Equal(t, http.StatusForbidden, resp.StatusCode)
})
t.Run("Login", func(t *testing.T) {
t.Run("MultiLoginNotAllowed", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, &coderdtest.Options{
GithubOAuth2Config: &coderd.GithubOAuth2Config{
Expand All @@ -199,6 +199,7 @@ func TestUserOAuth2Github(t *testing.T) {
},
},
})
// Creates the first user with login_type 'password'.
_ = coderdtest.CreateFirstUser(t, client)
resp := oauth2Callback(t, client)
require.Equal(t, http.StatusTemporaryRedirect, resp.StatusCode)
Expand Down
31 changes: 25 additions & 6 deletions coderd/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,13 @@ func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) {
return
}

user, organizationID, err := api.createUser(r.Context(), codersdk.CreateUserRequest{
Email: createUser.Email,
Username: createUser.Username,
Password: createUser.Password,
user, organizationID, err := api.createUser(r.Context(), createUserRequest{
CreateUserRequest: codersdk.CreateUserRequest{
Email: createUser.Email,
Username: createUser.Username,
Password: createUser.Password,
},
LoginType: database.LoginTypePassword,
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{
Expand Down Expand Up @@ -243,7 +246,10 @@ func (api *API) postUser(rw http.ResponseWriter, r *http.Request) {
return
}

user, _, err := api.createUser(r.Context(), req)
user, _, err := api.createUser(r.Context(), createUserRequest{
CreateUserRequest: req,
LoginType: database.LoginTypePassword,
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error creating user.",
Expand Down Expand Up @@ -684,6 +690,13 @@ func (api *API) postLogin(rw http.ResponseWriter, r *http.Request) {
return
}

if user.LoginType != database.LoginTypePassword {
httpapi.Write(rw, http.StatusForbidden, codersdk.Response{
Message: fmt.Sprintf("Incorrect login type, attempting to use %q but user is of login type %q", database.LoginTypeOIDC, user.LoginType),
})
return
}

// If the user doesn't exist, it will be a default struct.
equal, err := userpassword.Compare(string(user.HashedPassword), loginWithPassword.Password)
if err != nil {
Expand Down Expand Up @@ -896,7 +909,12 @@ func (api *API) createAPIKey(rw http.ResponseWriter, r *http.Request, params cre
return sessionToken, true
}

func (api *API) createUser(ctx context.Context, req codersdk.CreateUserRequest) (database.User, uuid.UUID, error) {
type createUserRequest struct {
codersdk.CreateUserRequest
LoginType database.LoginType
}

func (api *API) createUser(ctx context.Context, req createUserRequest) (database.User, uuid.UUID, error) {
var user database.User
return user, req.OrganizationID, api.Database.InTx(func(db database.Store) error {
orgRoles := make([]string, 0)
Expand All @@ -923,6 +941,7 @@ func (api *API) createUser(ctx context.Context, req codersdk.CreateUserRequest)
UpdatedAt: database.Now(),
// All new users are defaulted to members of the site.
RBACRoles: []string{},
LoginType: req.LoginType,
}
// If a user signs up with OAuth, they can have no password!
if req.Password != "" {
Expand Down