fix: use unique ID for linked accounts (#3441)

- move OAuth-related fields off of api_keys into a new user_links table
- restrict users to single form of login
- process updates to user email/usernames for OIDC
- added a login_type column to users
This commit is contained in:
Jon Ayers
2022-08-17 18:00:53 -05:00
committed by GitHub
parent 53d1fb36db
commit c3eea98db0
29 changed files with 931 additions and 266 deletions
+89 -17
View File
@@ -73,6 +73,7 @@ type data struct {
organizations []database.Organization
organizationMembers []database.OrganizationMember
users []database.User
userLinks []database.UserLink
// New tables
auditLogs []database.AuditLog
@@ -1454,20 +1455,16 @@ func (q *fakeQuerier) InsertAPIKey(_ context.Context, arg database.InsertAPIKeyP
//nolint:gosimple
key := database.APIKey{
ID: arg.ID,
LifetimeSeconds: arg.LifetimeSeconds,
HashedSecret: arg.HashedSecret,
IPAddress: arg.IPAddress,
UserID: arg.UserID,
ExpiresAt: arg.ExpiresAt,
CreatedAt: arg.CreatedAt,
UpdatedAt: arg.UpdatedAt,
LastUsed: arg.LastUsed,
LoginType: arg.LoginType,
OAuthAccessToken: arg.OAuthAccessToken,
OAuthRefreshToken: arg.OAuthRefreshToken,
OAuthIDToken: arg.OAuthIDToken,
OAuthExpiry: arg.OAuthExpiry,
ID: arg.ID,
LifetimeSeconds: arg.LifetimeSeconds,
HashedSecret: arg.HashedSecret,
IPAddress: arg.IPAddress,
UserID: arg.UserID,
ExpiresAt: arg.ExpiresAt,
CreatedAt: arg.CreatedAt,
UpdatedAt: arg.UpdatedAt,
LastUsed: arg.LastUsed,
LoginType: arg.LoginType,
}
q.apiKeys = append(q.apiKeys, key)
return key, nil
@@ -1744,6 +1741,7 @@ func (q *fakeQuerier) InsertUser(_ context.Context, arg database.InsertUserParam
Username: arg.Username,
Status: database.UserStatusActive,
RBACRoles: arg.RBACRoles,
LoginType: arg.LoginType,
}
q.users = append(q.users, user)
return user, nil
@@ -1899,9 +1897,6 @@ func (q *fakeQuerier) UpdateAPIKeyByID(_ context.Context, arg database.UpdateAPI
apiKey.LastUsed = arg.LastUsed
apiKey.ExpiresAt = arg.ExpiresAt
apiKey.IPAddress = arg.IPAddress
apiKey.OAuthAccessToken = arg.OAuthAccessToken
apiKey.OAuthRefreshToken = arg.OAuthRefreshToken
apiKey.OAuthExpiry = arg.OAuthExpiry
q.apiKeys[index] = apiKey
return nil
}
@@ -2260,3 +2255,80 @@ func (q *fakeQuerier) GetDeploymentID(_ context.Context) (string, error) {
return q.deploymentID, nil
}
func (q *fakeQuerier) GetUserLinkByLinkedID(_ context.Context, id string) (database.UserLink, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
for _, link := range q.userLinks {
if link.LinkedID == id {
return link, nil
}
}
return database.UserLink{}, sql.ErrNoRows
}
func (q *fakeQuerier) GetUserLinkByUserIDLoginType(_ context.Context, params database.GetUserLinkByUserIDLoginTypeParams) (database.UserLink, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
for _, link := range q.userLinks {
if link.UserID == params.UserID && link.LoginType == params.LoginType {
return link, nil
}
}
return database.UserLink{}, sql.ErrNoRows
}
func (q *fakeQuerier) InsertUserLink(_ context.Context, args database.InsertUserLinkParams) (database.UserLink, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
//nolint:gosimple
link := database.UserLink{
UserID: args.UserID,
LoginType: args.LoginType,
LinkedID: args.LinkedID,
OAuthAccessToken: args.OAuthAccessToken,
OAuthRefreshToken: args.OAuthRefreshToken,
OAuthExpiry: args.OAuthExpiry,
}
q.userLinks = append(q.userLinks, link)
return link, nil
}
func (q *fakeQuerier) UpdateUserLinkedID(_ context.Context, params database.UpdateUserLinkedIDParams) (database.UserLink, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
for i, link := range q.userLinks {
if link.UserID == params.UserID && link.LoginType == params.LoginType {
link.LinkedID = params.LinkedID
q.userLinks[i] = link
return link, nil
}
}
return database.UserLink{}, sql.ErrNoRows
}
func (q *fakeQuerier) UpdateUserLink(_ context.Context, params database.UpdateUserLinkParams) (database.UserLink, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
for i, link := range q.userLinks {
if link.UserID == params.UserID && link.LoginType == params.LoginType {
link.OAuthAccessToken = params.OAuthAccessToken
link.OAuthRefreshToken = params.OAuthRefreshToken
link.OAuthExpiry = params.OAuthExpiry
q.userLinks[i] = link
return link, nil
}
}
return database.UserLink{}, sql.ErrNoRows
}
+1
View File
@@ -37,6 +37,7 @@ func TestNestedInTx(t *testing.T) {
CreatedAt: database.Now(),
UpdatedAt: database.Now(),
RBACRoles: []string{},
LoginType: database.LoginTypeGithub,
})
return err
})
+17 -5
View File
@@ -96,10 +96,6 @@ CREATE TABLE api_keys (
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
login_type login_type NOT NULL,
oauth_access_token text DEFAULT ''::text NOT NULL,
oauth_refresh_token text DEFAULT ''::text NOT NULL,
oauth_id_token text DEFAULT ''::text NOT NULL,
oauth_expiry timestamp with time zone DEFAULT '0001-01-01 00:00:00+00'::timestamp with time zone NOT NULL,
lifetime_seconds bigint DEFAULT 86400 NOT NULL,
ip_address inet DEFAULT '0.0.0.0'::inet NOT NULL
);
@@ -267,6 +263,15 @@ CREATE TABLE templates (
created_by uuid NOT NULL
);
CREATE TABLE user_links (
user_id uuid NOT NULL,
login_type login_type NOT NULL,
linked_id text DEFAULT ''::text NOT NULL,
oauth_access_token text DEFAULT ''::text NOT NULL,
oauth_refresh_token text DEFAULT ''::text NOT NULL,
oauth_expiry timestamp with time zone DEFAULT '0001-01-01 00:00:00+00'::timestamp with time zone NOT NULL
);
CREATE TABLE users (
id uuid NOT NULL,
email text NOT NULL,
@@ -275,7 +280,8 @@ CREATE TABLE users (
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
status user_status DEFAULT 'active'::public.user_status NOT NULL,
rbac_roles text[] DEFAULT '{}'::text[] NOT NULL
rbac_roles text[] DEFAULT '{}'::text[] NOT NULL,
login_type login_type DEFAULT 'password'::public.login_type NOT NULL
);
CREATE TABLE workspace_agents (
@@ -416,6 +422,9 @@ ALTER TABLE ONLY template_versions
ALTER TABLE ONLY templates
ADD CONSTRAINT templates_pkey PRIMARY KEY (id);
ALTER TABLE ONLY user_links
ADD CONSTRAINT user_links_pkey PRIMARY KEY (user_id, login_type);
ALTER TABLE ONLY users
ADD CONSTRAINT users_pkey PRIMARY KEY (id);
@@ -513,6 +522,9 @@ ALTER TABLE ONLY templates
ALTER TABLE ONLY templates
ADD CONSTRAINT templates_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;
ALTER TABLE ONLY user_links
ADD CONSTRAINT user_links_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
ALTER TABLE ONLY workspace_agents
ADD CONSTRAINT workspace_agents_resource_id_fkey FOREIGN KEY (resource_id) REFERENCES workspace_resources(id) ON DELETE CASCADE;
+2
View File
@@ -13,6 +13,8 @@ SCRIPT_DIR=$(dirname "${BASH_SOURCE[0]}")
(
cd "$SCRIPT_DIR"
# Dump the updated schema.
go run dump/main.go
# The logic below depends on the exact version being correct :(
go run github.com/kyleconroy/sqlc/cmd/sqlc@v1.13.0 generate
@@ -0,0 +1,23 @@
-- This migration makes no attempt to try to populate
-- the oauth_access_token, oauth_refresh_token, and oauth_expiry
-- columns of api_key rows with the values from the dropped user_links
-- table.
BEGIN;
DROP TABLE IF EXISTS user_links;
ALTER TABLE
api_keys
ADD COLUMN oauth_access_token text DEFAULT ''::text NOT NULL;
ALTER TABLE
api_keys
ADD COLUMN oauth_refresh_token text DEFAULT ''::text NOT NULL;
ALTER TABLE
api_keys
ADD COLUMN oauth_expiry timestamp with time zone DEFAULT '0001-01-01 00:00:00+00'::timestamp with time zone NOT NULL;
ALTER TABLE users DROP COLUMN login_type;
COMMIT;
@@ -0,0 +1,74 @@
BEGIN;
CREATE TABLE IF NOT EXISTS user_links (
user_id uuid NOT NULL,
login_type login_type NOT NULL,
linked_id text DEFAULT ''::text NOT NULL,
oauth_access_token text DEFAULT ''::text NOT NULL,
oauth_refresh_token text DEFAULT ''::text NOT NULL,
oauth_expiry timestamp with time zone DEFAULT '0001-01-01 00:00:00+00'::timestamp with time zone NOT NULL,
PRIMARY KEY(user_id, login_type),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- This migrates columns on api_keys to the new user_links table.
-- It does this by finding all the API keys for each user, choosing
-- the most recently updated for each one and then assigning its relevant
-- values to the user_links table.
-- A user should at most have a row for an OIDC account and a Github account.
-- 'password' login types are ignored.
INSERT INTO user_links
(
user_id,
login_type,
linked_id,
oauth_access_token,
oauth_refresh_token,
oauth_expiry
)
SELECT
keys.user_id,
keys.login_type,
'',
keys.oauth_access_token,
keys.oauth_refresh_token,
keys.oauth_expiry
FROM
(
SELECT
row_number() OVER (partition by user_id, login_type ORDER BY last_used DESC) AS x,
api_keys.* FROM api_keys
) as keys
WHERE x=1 AND keys.login_type != 'password';
-- Drop columns that have been migrated to user_links.
-- It appears the 'oauth_id_token' was unused and so it has
-- been dropped here as well to avoid future confusion.
ALTER TABLE api_keys
DROP COLUMN oauth_access_token,
DROP COLUMN oauth_refresh_token,
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
ORDER BY oauth_expiry DESC
LIMIT 1
)
FROM
user_links
WHERE
user_links.user_id = users.id;
COMMIT;
+20 -14
View File
@@ -313,20 +313,16 @@ func (e *WorkspaceTransition) Scan(src interface{}) error {
}
type APIKey struct {
ID string `db:"id" json:"id"`
HashedSecret []byte `db:"hashed_secret" json:"hashed_secret"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
LastUsed time.Time `db:"last_used" json:"last_used"`
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
LoginType LoginType `db:"login_type" json:"login_type"`
OAuthAccessToken string `db:"oauth_access_token" json:"oauth_access_token"`
OAuthRefreshToken string `db:"oauth_refresh_token" json:"oauth_refresh_token"`
OAuthIDToken string `db:"oauth_id_token" json:"oauth_id_token"`
OAuthExpiry time.Time `db:"oauth_expiry" json:"oauth_expiry"`
LifetimeSeconds int64 `db:"lifetime_seconds" json:"lifetime_seconds"`
IPAddress pqtype.Inet `db:"ip_address" json:"ip_address"`
ID string `db:"id" json:"id"`
HashedSecret []byte `db:"hashed_secret" json:"hashed_secret"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
LastUsed time.Time `db:"last_used" json:"last_used"`
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
LoginType LoginType `db:"login_type" json:"login_type"`
LifetimeSeconds int64 `db:"lifetime_seconds" json:"lifetime_seconds"`
IPAddress pqtype.Inet `db:"ip_address" json:"ip_address"`
}
type AuditLog struct {
@@ -491,6 +487,16 @@ type User struct {
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Status UserStatus `db:"status" json:"status"`
RBACRoles []string `db:"rbac_roles" json:"rbac_roles"`
LoginType LoginType `db:"login_type" json:"login_type"`
}
type UserLink struct {
UserID uuid.UUID `db:"user_id" json:"user_id"`
LoginType LoginType `db:"login_type" json:"login_type"`
LinkedID string `db:"linked_id" json:"linked_id"`
OAuthAccessToken string `db:"oauth_access_token" json:"oauth_access_token"`
OAuthRefreshToken string `db:"oauth_refresh_token" json:"oauth_refresh_token"`
OAuthExpiry time.Time `db:"oauth_expiry" json:"oauth_expiry"`
}
type Workspace struct {
+5
View File
@@ -64,6 +64,8 @@ type querier interface {
GetUserByEmailOrUsername(ctx context.Context, arg GetUserByEmailOrUsernameParams) (User, error)
GetUserByID(ctx context.Context, id uuid.UUID) (User, error)
GetUserCount(ctx context.Context) (int64, error)
GetUserLinkByLinkedID(ctx context.Context, linkedID string) (UserLink, error)
GetUserLinkByUserIDLoginType(ctx context.Context, arg GetUserLinkByUserIDLoginTypeParams) (UserLink, error)
GetUsers(ctx context.Context, arg GetUsersParams) ([]User, error)
GetUsersByIDs(ctx context.Context, ids []uuid.UUID) ([]User, error)
GetWorkspaceAgentByAuthToken(ctx context.Context, authToken uuid.UUID) (WorkspaceAgent, error)
@@ -106,6 +108,7 @@ type querier interface {
InsertTemplate(ctx context.Context, arg InsertTemplateParams) (Template, error)
InsertTemplateVersion(ctx context.Context, arg InsertTemplateVersionParams) (TemplateVersion, error)
InsertUser(ctx context.Context, arg InsertUserParams) (User, error)
InsertUserLink(ctx context.Context, arg InsertUserLinkParams) (UserLink, error)
InsertWorkspace(ctx context.Context, arg InsertWorkspaceParams) (Workspace, error)
InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspaceAgentParams) (WorkspaceAgent, error)
InsertWorkspaceApp(ctx context.Context, arg InsertWorkspaceAppParams) (WorkspaceApp, error)
@@ -127,6 +130,8 @@ type querier interface {
UpdateTemplateVersionByID(ctx context.Context, arg UpdateTemplateVersionByIDParams) error
UpdateTemplateVersionDescriptionByJobID(ctx context.Context, arg UpdateTemplateVersionDescriptionByJobIDParams) error
UpdateUserHashedPassword(ctx context.Context, arg UpdateUserHashedPasswordParams) error
UpdateUserLink(ctx context.Context, arg UpdateUserLinkParams) (UserLink, error)
UpdateUserLinkedID(ctx context.Context, arg UpdateUserLinkedIDParams) (UserLink, error)
UpdateUserProfile(ctx context.Context, arg UpdateUserProfileParams) (User, error)
UpdateUserRoles(ctx context.Context, arg UpdateUserRolesParams) (User, error)
UpdateUserStatus(ctx context.Context, arg UpdateUserStatusParams) (User, error)
+208 -67
View File
@@ -31,7 +31,7 @@ func (q *sqlQuerier) DeleteAPIKeyByID(ctx context.Context, id string) error {
const getAPIKeyByID = `-- name: GetAPIKeyByID :one
SELECT
id, hashed_secret, user_id, last_used, expires_at, created_at, updated_at, login_type, oauth_access_token, oauth_refresh_token, oauth_id_token, oauth_expiry, lifetime_seconds, ip_address
id, hashed_secret, user_id, last_used, expires_at, created_at, updated_at, login_type, lifetime_seconds, ip_address
FROM
api_keys
WHERE
@@ -52,10 +52,6 @@ func (q *sqlQuerier) GetAPIKeyByID(ctx context.Context, id string) (APIKey, erro
&i.CreatedAt,
&i.UpdatedAt,
&i.LoginType,
&i.OAuthAccessToken,
&i.OAuthRefreshToken,
&i.OAuthIDToken,
&i.OAuthExpiry,
&i.LifetimeSeconds,
&i.IPAddress,
)
@@ -63,7 +59,7 @@ func (q *sqlQuerier) GetAPIKeyByID(ctx context.Context, id string) (APIKey, erro
}
const getAPIKeysLastUsedAfter = `-- name: GetAPIKeysLastUsedAfter :many
SELECT id, hashed_secret, user_id, last_used, expires_at, created_at, updated_at, login_type, oauth_access_token, oauth_refresh_token, oauth_id_token, oauth_expiry, lifetime_seconds, ip_address FROM api_keys WHERE last_used > $1
SELECT id, hashed_secret, user_id, last_used, expires_at, created_at, updated_at, login_type, lifetime_seconds, ip_address FROM api_keys WHERE last_used > $1
`
func (q *sqlQuerier) GetAPIKeysLastUsedAfter(ctx context.Context, lastUsed time.Time) ([]APIKey, error) {
@@ -84,10 +80,6 @@ func (q *sqlQuerier) GetAPIKeysLastUsedAfter(ctx context.Context, lastUsed time.
&i.CreatedAt,
&i.UpdatedAt,
&i.LoginType,
&i.OAuthAccessToken,
&i.OAuthRefreshToken,
&i.OAuthIDToken,
&i.OAuthExpiry,
&i.LifetimeSeconds,
&i.IPAddress,
); err != nil {
@@ -116,11 +108,7 @@ INSERT INTO
expires_at,
created_at,
updated_at,
login_type,
oauth_access_token,
oauth_refresh_token,
oauth_id_token,
oauth_expiry
login_type
)
VALUES
($1,
@@ -129,24 +117,20 @@ VALUES
WHEN 0 THEN 86400
ELSE $2::bigint
END
, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) RETURNING id, hashed_secret, user_id, last_used, expires_at, created_at, updated_at, login_type, oauth_access_token, oauth_refresh_token, oauth_id_token, oauth_expiry, lifetime_seconds, ip_address
, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id, hashed_secret, user_id, last_used, expires_at, created_at, updated_at, login_type, lifetime_seconds, ip_address
`
type InsertAPIKeyParams struct {
ID string `db:"id" json:"id"`
LifetimeSeconds int64 `db:"lifetime_seconds" json:"lifetime_seconds"`
HashedSecret []byte `db:"hashed_secret" json:"hashed_secret"`
IPAddress pqtype.Inet `db:"ip_address" json:"ip_address"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
LastUsed time.Time `db:"last_used" json:"last_used"`
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
LoginType LoginType `db:"login_type" json:"login_type"`
OAuthAccessToken string `db:"oauth_access_token" json:"oauth_access_token"`
OAuthRefreshToken string `db:"oauth_refresh_token" json:"oauth_refresh_token"`
OAuthIDToken string `db:"oauth_id_token" json:"oauth_id_token"`
OAuthExpiry time.Time `db:"oauth_expiry" json:"oauth_expiry"`
ID string `db:"id" json:"id"`
LifetimeSeconds int64 `db:"lifetime_seconds" json:"lifetime_seconds"`
HashedSecret []byte `db:"hashed_secret" json:"hashed_secret"`
IPAddress pqtype.Inet `db:"ip_address" json:"ip_address"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
LastUsed time.Time `db:"last_used" json:"last_used"`
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
LoginType LoginType `db:"login_type" json:"login_type"`
}
func (q *sqlQuerier) InsertAPIKey(ctx context.Context, arg InsertAPIKeyParams) (APIKey, error) {
@@ -161,10 +145,6 @@ func (q *sqlQuerier) InsertAPIKey(ctx context.Context, arg InsertAPIKeyParams) (
arg.CreatedAt,
arg.UpdatedAt,
arg.LoginType,
arg.OAuthAccessToken,
arg.OAuthRefreshToken,
arg.OAuthIDToken,
arg.OAuthExpiry,
)
var i APIKey
err := row.Scan(
@@ -176,10 +156,6 @@ func (q *sqlQuerier) InsertAPIKey(ctx context.Context, arg InsertAPIKeyParams) (
&i.CreatedAt,
&i.UpdatedAt,
&i.LoginType,
&i.OAuthAccessToken,
&i.OAuthRefreshToken,
&i.OAuthIDToken,
&i.OAuthExpiry,
&i.LifetimeSeconds,
&i.IPAddress,
)
@@ -192,22 +168,16 @@ UPDATE
SET
last_used = $2,
expires_at = $3,
ip_address = $4,
oauth_access_token = $5,
oauth_refresh_token = $6,
oauth_expiry = $7
ip_address = $4
WHERE
id = $1
`
type UpdateAPIKeyByIDParams struct {
ID string `db:"id" json:"id"`
LastUsed time.Time `db:"last_used" json:"last_used"`
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
IPAddress pqtype.Inet `db:"ip_address" json:"ip_address"`
OAuthAccessToken string `db:"oauth_access_token" json:"oauth_access_token"`
OAuthRefreshToken string `db:"oauth_refresh_token" json:"oauth_refresh_token"`
OAuthExpiry time.Time `db:"oauth_expiry" json:"oauth_expiry"`
ID string `db:"id" json:"id"`
LastUsed time.Time `db:"last_used" json:"last_used"`
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
IPAddress pqtype.Inet `db:"ip_address" json:"ip_address"`
}
func (q *sqlQuerier) UpdateAPIKeyByID(ctx context.Context, arg UpdateAPIKeyByIDParams) error {
@@ -216,9 +186,6 @@ func (q *sqlQuerier) UpdateAPIKeyByID(ctx context.Context, arg UpdateAPIKeyByIDP
arg.LastUsed,
arg.ExpiresAt,
arg.IPAddress,
arg.OAuthAccessToken,
arg.OAuthRefreshToken,
arg.OAuthExpiry,
)
return err
}
@@ -2447,6 +2414,169 @@ func (q *sqlQuerier) UpdateTemplateVersionDescriptionByJobID(ctx context.Context
return err
}
const getUserLinkByLinkedID = `-- name: GetUserLinkByLinkedID :one
SELECT
user_id, login_type, linked_id, oauth_access_token, oauth_refresh_token, oauth_expiry
FROM
user_links
WHERE
linked_id = $1
`
func (q *sqlQuerier) GetUserLinkByLinkedID(ctx context.Context, linkedID string) (UserLink, error) {
row := q.db.QueryRowContext(ctx, getUserLinkByLinkedID, linkedID)
var i UserLink
err := row.Scan(
&i.UserID,
&i.LoginType,
&i.LinkedID,
&i.OAuthAccessToken,
&i.OAuthRefreshToken,
&i.OAuthExpiry,
)
return i, err
}
const getUserLinkByUserIDLoginType = `-- name: GetUserLinkByUserIDLoginType :one
SELECT
user_id, login_type, linked_id, oauth_access_token, oauth_refresh_token, oauth_expiry
FROM
user_links
WHERE
user_id = $1 AND login_type = $2
`
type GetUserLinkByUserIDLoginTypeParams struct {
UserID uuid.UUID `db:"user_id" json:"user_id"`
LoginType LoginType `db:"login_type" json:"login_type"`
}
func (q *sqlQuerier) GetUserLinkByUserIDLoginType(ctx context.Context, arg GetUserLinkByUserIDLoginTypeParams) (UserLink, error) {
row := q.db.QueryRowContext(ctx, getUserLinkByUserIDLoginType, arg.UserID, arg.LoginType)
var i UserLink
err := row.Scan(
&i.UserID,
&i.LoginType,
&i.LinkedID,
&i.OAuthAccessToken,
&i.OAuthRefreshToken,
&i.OAuthExpiry,
)
return i, err
}
const insertUserLink = `-- name: InsertUserLink :one
INSERT INTO
user_links (
user_id,
login_type,
linked_id,
oauth_access_token,
oauth_refresh_token,
oauth_expiry
)
VALUES
( $1, $2, $3, $4, $5, $6 ) RETURNING user_id, login_type, linked_id, oauth_access_token, oauth_refresh_token, oauth_expiry
`
type InsertUserLinkParams struct {
UserID uuid.UUID `db:"user_id" json:"user_id"`
LoginType LoginType `db:"login_type" json:"login_type"`
LinkedID string `db:"linked_id" json:"linked_id"`
OAuthAccessToken string `db:"oauth_access_token" json:"oauth_access_token"`
OAuthRefreshToken string `db:"oauth_refresh_token" json:"oauth_refresh_token"`
OAuthExpiry time.Time `db:"oauth_expiry" json:"oauth_expiry"`
}
func (q *sqlQuerier) InsertUserLink(ctx context.Context, arg InsertUserLinkParams) (UserLink, error) {
row := q.db.QueryRowContext(ctx, insertUserLink,
arg.UserID,
arg.LoginType,
arg.LinkedID,
arg.OAuthAccessToken,
arg.OAuthRefreshToken,
arg.OAuthExpiry,
)
var i UserLink
err := row.Scan(
&i.UserID,
&i.LoginType,
&i.LinkedID,
&i.OAuthAccessToken,
&i.OAuthRefreshToken,
&i.OAuthExpiry,
)
return i, err
}
const updateUserLink = `-- name: UpdateUserLink :one
UPDATE
user_links
SET
oauth_access_token = $1,
oauth_refresh_token = $2,
oauth_expiry = $3
WHERE
user_id = $4 AND login_type = $5 RETURNING user_id, login_type, linked_id, oauth_access_token, oauth_refresh_token, oauth_expiry
`
type UpdateUserLinkParams struct {
OAuthAccessToken string `db:"oauth_access_token" json:"oauth_access_token"`
OAuthRefreshToken string `db:"oauth_refresh_token" json:"oauth_refresh_token"`
OAuthExpiry time.Time `db:"oauth_expiry" json:"oauth_expiry"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
LoginType LoginType `db:"login_type" json:"login_type"`
}
func (q *sqlQuerier) UpdateUserLink(ctx context.Context, arg UpdateUserLinkParams) (UserLink, error) {
row := q.db.QueryRowContext(ctx, updateUserLink,
arg.OAuthAccessToken,
arg.OAuthRefreshToken,
arg.OAuthExpiry,
arg.UserID,
arg.LoginType,
)
var i UserLink
err := row.Scan(
&i.UserID,
&i.LoginType,
&i.LinkedID,
&i.OAuthAccessToken,
&i.OAuthRefreshToken,
&i.OAuthExpiry,
)
return i, err
}
const updateUserLinkedID = `-- name: UpdateUserLinkedID :one
UPDATE
user_links
SET
linked_id = $1
WHERE
user_id = $2 AND login_type = $3 RETURNING user_id, login_type, linked_id, oauth_access_token, oauth_refresh_token, oauth_expiry
`
type UpdateUserLinkedIDParams struct {
LinkedID string `db:"linked_id" json:"linked_id"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
LoginType LoginType `db:"login_type" json:"login_type"`
}
func (q *sqlQuerier) UpdateUserLinkedID(ctx context.Context, arg UpdateUserLinkedIDParams) (UserLink, error) {
row := q.db.QueryRowContext(ctx, updateUserLinkedID, arg.LinkedID, arg.UserID, arg.LoginType)
var i UserLink
err := row.Scan(
&i.UserID,
&i.LoginType,
&i.LinkedID,
&i.OAuthAccessToken,
&i.OAuthRefreshToken,
&i.OAuthExpiry,
)
return i, err
}
const getAuthorizationUserRoles = `-- name: GetAuthorizationUserRoles :one
SELECT
-- username is returned just to help for logging purposes
@@ -2458,13 +2588,13 @@ SELECT
array_append(users.rbac_roles, 'member'),
-- All org_members get the org-member role for their orgs
array_append(organization_members.roles, 'organization-member:'||organization_members.organization_id::text)) :: text[]
AS roles
AS roles
FROM
users
LEFT JOIN organization_members
ON id = user_id
WHERE
id = $1
id = $1
`
type GetAuthorizationUserRolesRow struct {
@@ -2490,7 +2620,7 @@ func (q *sqlQuerier) GetAuthorizationUserRoles(ctx context.Context, userID uuid.
const getUserByEmailOrUsername = `-- name: GetUserByEmailOrUsername :one
SELECT
id, email, username, hashed_password, created_at, updated_at, status, rbac_roles
id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, login_type
FROM
users
WHERE
@@ -2517,13 +2647,14 @@ func (q *sqlQuerier) GetUserByEmailOrUsername(ctx context.Context, arg GetUserBy
&i.UpdatedAt,
&i.Status,
pq.Array(&i.RBACRoles),
&i.LoginType,
)
return i, err
}
const getUserByID = `-- name: GetUserByID :one
SELECT
id, email, username, hashed_password, created_at, updated_at, status, rbac_roles
id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, login_type
FROM
users
WHERE
@@ -2544,6 +2675,7 @@ func (q *sqlQuerier) GetUserByID(ctx context.Context, id uuid.UUID) (User, error
&i.UpdatedAt,
&i.Status,
pq.Array(&i.RBACRoles),
&i.LoginType,
)
return i, err
}
@@ -2564,7 +2696,7 @@ func (q *sqlQuerier) GetUserCount(ctx context.Context) (int64, error) {
const getUsers = `-- name: GetUsers :many
SELECT
id, email, username, hashed_password, created_at, updated_at, status, rbac_roles
id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, login_type
FROM
users
WHERE
@@ -2614,8 +2746,8 @@ WHERE
END
-- End of filters
ORDER BY
-- Deterministic and consistent ordering of all users, even if they share
-- a timestamp. This is to ensure consistent pagination.
-- Deterministic and consistent ordering of all users, even if they share
-- a timestamp. This is to ensure consistent pagination.
(created_at, id) ASC OFFSET $5
LIMIT
-- A null limit means "no limit", so 0 means return all
@@ -2656,6 +2788,7 @@ func (q *sqlQuerier) GetUsers(ctx context.Context, arg GetUsersParams) ([]User,
&i.UpdatedAt,
&i.Status,
pq.Array(&i.RBACRoles),
&i.LoginType,
); err != nil {
return nil, err
}
@@ -2671,7 +2804,7 @@ func (q *sqlQuerier) GetUsers(ctx context.Context, arg GetUsersParams) ([]User,
}
const getUsersByIDs = `-- name: GetUsersByIDs :many
SELECT id, email, username, hashed_password, created_at, updated_at, status, rbac_roles FROM users WHERE id = ANY($1 :: uuid [ ])
SELECT id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, login_type FROM users WHERE id = ANY($1 :: uuid [ ])
`
func (q *sqlQuerier) GetUsersByIDs(ctx context.Context, ids []uuid.UUID) ([]User, error) {
@@ -2692,6 +2825,7 @@ func (q *sqlQuerier) GetUsersByIDs(ctx context.Context, ids []uuid.UUID) ([]User
&i.UpdatedAt,
&i.Status,
pq.Array(&i.RBACRoles),
&i.LoginType,
); err != nil {
return nil, err
}
@@ -2715,10 +2849,11 @@ INSERT INTO
hashed_password,
created_at,
updated_at,
rbac_roles
rbac_roles,
login_type
)
VALUES
($1, $2, $3, $4, $5, $6, $7) RETURNING id, email, username, hashed_password, created_at, updated_at, status, rbac_roles
($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, login_type
`
type InsertUserParams struct {
@@ -2729,6 +2864,7 @@ type InsertUserParams struct {
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
RBACRoles []string `db:"rbac_roles" json:"rbac_roles"`
LoginType LoginType `db:"login_type" json:"login_type"`
}
func (q *sqlQuerier) InsertUser(ctx context.Context, arg InsertUserParams) (User, error) {
@@ -2740,6 +2876,7 @@ func (q *sqlQuerier) InsertUser(ctx context.Context, arg InsertUserParams) (User
arg.CreatedAt,
arg.UpdatedAt,
pq.Array(arg.RBACRoles),
arg.LoginType,
)
var i User
err := row.Scan(
@@ -2751,6 +2888,7 @@ func (q *sqlQuerier) InsertUser(ctx context.Context, arg InsertUserParams) (User
&i.UpdatedAt,
&i.Status,
pq.Array(&i.RBACRoles),
&i.LoginType,
)
return i, err
}
@@ -2782,7 +2920,7 @@ SET
username = $3,
updated_at = $4
WHERE
id = $1 RETURNING id, email, username, hashed_password, created_at, updated_at, status, rbac_roles
id = $1 RETURNING id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, login_type
`
type UpdateUserProfileParams struct {
@@ -2809,19 +2947,20 @@ func (q *sqlQuerier) UpdateUserProfile(ctx context.Context, arg UpdateUserProfil
&i.UpdatedAt,
&i.Status,
pq.Array(&i.RBACRoles),
&i.LoginType,
)
return i, err
}
const updateUserRoles = `-- name: UpdateUserRoles :one
UPDATE
users
users
SET
-- Remove all duplicates from the roles.
rbac_roles = ARRAY(SELECT DISTINCT UNNEST($1 :: text[]))
WHERE
id = $2
RETURNING id, email, username, hashed_password, created_at, updated_at, status, rbac_roles
id = $2
RETURNING id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, login_type
`
type UpdateUserRolesParams struct {
@@ -2841,6 +2980,7 @@ func (q *sqlQuerier) UpdateUserRoles(ctx context.Context, arg UpdateUserRolesPar
&i.UpdatedAt,
&i.Status,
pq.Array(&i.RBACRoles),
&i.LoginType,
)
return i, err
}
@@ -2852,7 +2992,7 @@ SET
status = $2,
updated_at = $3
WHERE
id = $1 RETURNING id, email, username, hashed_password, created_at, updated_at, status, rbac_roles
id = $1 RETURNING id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, login_type
`
type UpdateUserStatusParams struct {
@@ -2873,6 +3013,7 @@ func (q *sqlQuerier) UpdateUserStatus(ctx context.Context, arg UpdateUserStatusP
&i.UpdatedAt,
&i.Status,
pq.Array(&i.RBACRoles),
&i.LoginType,
)
return i, err
}
+3 -10
View File
@@ -23,11 +23,7 @@ INSERT INTO
expires_at,
created_at,
updated_at,
login_type,
oauth_access_token,
oauth_refresh_token,
oauth_id_token,
oauth_expiry
login_type
)
VALUES
(@id,
@@ -36,7 +32,7 @@ VALUES
WHEN 0 THEN 86400
ELSE @lifetime_seconds::bigint
END
, @hashed_secret, @ip_address, @user_id, @last_used, @expires_at, @created_at, @updated_at, @login_type, @oauth_access_token, @oauth_refresh_token, @oauth_id_token, @oauth_expiry) RETURNING *;
, @hashed_secret, @ip_address, @user_id, @last_used, @expires_at, @created_at, @updated_at, @login_type) RETURNING *;
-- name: UpdateAPIKeyByID :exec
UPDATE
@@ -44,10 +40,7 @@ UPDATE
SET
last_used = $2,
expires_at = $3,
ip_address = $4,
oauth_access_token = $5,
oauth_refresh_token = $6,
oauth_expiry = $7
ip_address = $4
WHERE
id = $1;
+46
View File
@@ -0,0 +1,46 @@
-- name: GetUserLinkByLinkedID :one
SELECT
*
FROM
user_links
WHERE
linked_id = $1;
-- name: GetUserLinkByUserIDLoginType :one
SELECT
*
FROM
user_links
WHERE
user_id = $1 AND login_type = $2;
-- name: InsertUserLink :one
INSERT INTO
user_links (
user_id,
login_type,
linked_id,
oauth_access_token,
oauth_refresh_token,
oauth_expiry
)
VALUES
( $1, $2, $3, $4, $5, $6 ) RETURNING *;
-- name: UpdateUserLinkedID :one
UPDATE
user_links
SET
linked_id = $1
WHERE
user_id = $2 AND login_type = $3 RETURNING *;
-- name: UpdateUserLink :one
UPDATE
user_links
SET
oauth_access_token = $1,
oauth_refresh_token = $2,
oauth_expiry = $3
WHERE
user_id = $4 AND login_type = $5 RETURNING *;
+9 -8
View File
@@ -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
@@ -54,12 +55,12 @@ WHERE
-- name: UpdateUserRoles :one
UPDATE
users
users
SET
-- Remove all duplicates from the roles.
rbac_roles = ARRAY(SELECT DISTINCT UNNEST(@granted_roles :: text[]))
WHERE
id = @id
id = @id
RETURNING *;
-- name: UpdateUserHashedPassword :exec
@@ -122,8 +123,8 @@ WHERE
END
-- End of filters
ORDER BY
-- Deterministic and consistent ordering of all users, even if they share
-- a timestamp. This is to ensure consistent pagination.
-- Deterministic and consistent ordering of all users, even if they share
-- a timestamp. This is to ensure consistent pagination.
(created_at, id) ASC OFFSET @offset_opt
LIMIT
-- A null limit means "no limit", so 0 means return all
@@ -152,10 +153,10 @@ SELECT
array_append(users.rbac_roles, 'member'),
-- All org_members get the org-member role for their orgs
array_append(organization_members.roles, 'organization-member:'||organization_members.organization_id::text)) :: text[]
AS roles
AS roles
FROM
users
LEFT JOIN organization_members
ON id = user_id
WHERE
id = @user_id;
id = @user_id;