-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Expand file tree
/
Copy pathuser_links.sql
More file actions
148 lines (139 loc) · 4.33 KB
/
Copy pathuser_links.sql
File metadata and controls
148 lines (139 loc) · 4.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
-- name: GetUserLinkByLinkedID :one
SELECT
user_links.*
FROM
user_links
INNER JOIN
users ON user_links.user_id = users.id
WHERE
linked_id = $1
AND
deleted = false;
-- name: GetUserLinkByUserIDLoginType :one
SELECT
*
FROM
user_links
WHERE
user_id = $1 AND login_type = $2;
-- name: GetUserLinksByUserID :many
SELECT * FROM user_links WHERE user_id = $1;
-- name: InsertUserLink :one
INSERT INTO
user_links (
user_id,
login_type,
linked_id,
oauth_access_token,
oauth_access_token_key_id,
oauth_refresh_token,
oauth_refresh_token_key_id,
oauth_expiry,
claims
)
VALUES
( $1, $2, $3, $4, $5, $6, $7, $8, $9 ) RETURNING *;
-- name: UpdateUserLink :one
UPDATE
user_links
SET
oauth_access_token = $1,
oauth_access_token_key_id = $2,
oauth_refresh_token = $3,
oauth_refresh_token_key_id = $4,
oauth_expiry = $5,
claims = $6
WHERE
user_id = $7 AND login_type = $8 RETURNING *;
-- name: UpdateUserLinkedID :one
-- Backfills linked_id for legacy user_links that were created before
-- linked_id tracking was added. Only updates when linked_id is empty
-- to avoid overwriting a valid binding.
UPDATE
user_links
SET
linked_id = @linked_id
WHERE
user_id = @user_id AND login_type = @login_type AND linked_id = '' RETURNING *;
-- name: OIDCClaimFields :many
-- OIDCClaimFields returns a list of distinct keys in the the merged_claims fields.
-- This query is used to generate the list of available sync fields for idp sync settings.
SELECT
DISTINCT jsonb_object_keys(claims->'merged_claims')
FROM
user_links
WHERE
-- Only return rows where the top level key exists
claims ? 'merged_claims' AND
-- 'null' is the default value for the id_token_claims field
-- jsonb 'null' is not the same as SQL NULL. Strip these out.
jsonb_typeof(claims->'merged_claims') != 'null' AND
login_type = 'oidc'
AND CASE WHEN @organization_id :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN
user_links.user_id = ANY(SELECT organization_members.user_id FROM organization_members WHERE organization_id = @organization_id)
ELSE true
END
;
-- name: OIDCClaimFieldValues :many
SELECT
-- DISTINCT to remove duplicates
DISTINCT jsonb_array_elements_text(CASE
-- When the type is an array, filter out any non-string elements.
-- This is to keep the return type consistent.
WHEN jsonb_typeof(claims->'merged_claims'->sqlc.arg('claim_field')::text) = 'array' THEN
(
SELECT
jsonb_agg(element)
FROM
jsonb_array_elements(claims->'merged_claims'->sqlc.arg('claim_field')::text) AS element
WHERE
-- Filtering out non-string elements
jsonb_typeof(element) = 'string'
)
-- Some IDPs return a single string instead of an array of strings.
WHEN jsonb_typeof(claims->'merged_claims'->sqlc.arg('claim_field')::text) = 'string' THEN
jsonb_build_array(claims->'merged_claims'->sqlc.arg('claim_field')::text)
END)
FROM
user_links
WHERE
-- IDP sync only supports string and array (of string) types
jsonb_typeof(claims->'merged_claims'->sqlc.arg('claim_field')::text) = ANY(ARRAY['string', 'array'])
AND login_type = 'oidc'
AND CASE
WHEN @organization_id :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN
user_links.user_id = ANY(SELECT organization_members.user_id FROM organization_members WHERE organization_id = @organization_id)
ELSE true
END
;
-- name: CountOIDCLinkedIDsByIssuer :many
-- Groups OIDC user links by their issuer prefix (the part before "||" in
-- linked_id) and returns a count for each. Empty linked_ids are reported
-- with an empty issuer_prefix. Used for analysis before resetting
-- mismatched links.
SELECT
(CASE
WHEN user_links.linked_id = '' THEN ''
ELSE split_part(user_links.linked_id, '||', 1)
END)::text AS issuer_prefix,
COUNT(*)::int AS count
FROM
user_links
INNER JOIN
users ON user_links.user_id = users.id
WHERE
user_links.login_type = 'oidc'
AND users.deleted = false
GROUP BY issuer_prefix;
-- name: UnlinkOIDCUsersByIssuerMismatch :execrows
-- Resets linked_id to '' for OIDC links where the linked_id is non-empty
-- and does not begin with the expected issuer prefix. This allows users to
-- re-authenticate under a new OIDC provider.
UPDATE user_links
SET linked_id = ''
FROM users
WHERE user_links.user_id = users.id
AND user_links.login_type = 'oidc'
AND user_links.linked_id != ''
AND NOT starts_with(user_links.linked_id, @expected_prefix)
AND users.deleted = false;