diff --git a/.sqlx/query-230fe22c6c9907c9ebab1dab9ef5ca984604c70823b13390169a5da57086df1a.json b/.sqlx/query-1ba009aaa52a3f96a98e94c16992cb4d9a5da539a159d550b7f6207e67d5e802.json similarity index 91% rename from .sqlx/query-230fe22c6c9907c9ebab1dab9ef5ca984604c70823b13390169a5da57086df1a.json rename to .sqlx/query-1ba009aaa52a3f96a98e94c16992cb4d9a5da539a159d550b7f6207e67d5e802.json index 3ffbde7ccd..4a871dbc90 100644 --- a/.sqlx/query-230fe22c6c9907c9ebab1dab9ef5ca984604c70823b13390169a5da57086df1a.json +++ b/.sqlx/query-1ba009aaa52a3f96a98e94c16992cb4d9a5da539a159d550b7f6207e67d5e802.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id \"id?\", username, password_hash, last_name, first_name, email, phone, mfa_enabled, totp_enabled, email_mfa_enabled, totp_secret, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_login FROM \"user\" WHERE email = $1", + "query": "SELECT id \"id?\", username, password_hash, last_name, first_name, email, phone, mfa_enabled, totp_enabled, email_mfa_enabled, totp_secret, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_sub FROM \"user\" WHERE email = $1", "describe": { "columns": [ { @@ -93,8 +93,8 @@ }, { "ordinal": 15, - "name": "openid_login", - "type_info": "Bool" + "name": "openid_sub", + "type_info": "Text" } ], "parameters": { @@ -118,8 +118,8 @@ false, false, false, - false + true ] }, - "hash": "230fe22c6c9907c9ebab1dab9ef5ca984604c70823b13390169a5da57086df1a" + "hash": "1ba009aaa52a3f96a98e94c16992cb4d9a5da539a159d550b7f6207e67d5e802" } diff --git a/.sqlx/query-a3d548764fe912cc9009618d41504205b64526e89acd2fefd74d221b3c3ea620.json b/.sqlx/query-3bdf311fd1b919363cf8de9292cbbe511c5f2c1e4b572ae791852daabe71981e.json similarity index 91% rename from .sqlx/query-a3d548764fe912cc9009618d41504205b64526e89acd2fefd74d221b3c3ea620.json rename to .sqlx/query-3bdf311fd1b919363cf8de9292cbbe511c5f2c1e4b572ae791852daabe71981e.json index 6344d31c1c..2f7fc501bb 100644 --- a/.sqlx/query-a3d548764fe912cc9009618d41504205b64526e89acd2fefd74d221b3c3ea620.json +++ b/.sqlx/query-3bdf311fd1b919363cf8de9292cbbe511c5f2c1e4b572ae791852daabe71981e.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id \"id?\", username, password_hash, last_name, first_name, email, phone, mfa_enabled, totp_enabled, email_mfa_enabled, totp_secret, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_login FROM \"user\" WHERE id = ANY($1)", + "query": "SELECT id \"id?\", username, password_hash, last_name, first_name, email, phone, mfa_enabled, totp_enabled, email_mfa_enabled, totp_secret, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_sub FROM \"user\" WHERE id = ANY($1)", "describe": { "columns": [ { @@ -93,8 +93,8 @@ }, { "ordinal": 15, - "name": "openid_login", - "type_info": "Bool" + "name": "openid_sub", + "type_info": "Text" } ], "parameters": { @@ -118,8 +118,8 @@ false, false, false, - false + true ] }, - "hash": "a3d548764fe912cc9009618d41504205b64526e89acd2fefd74d221b3c3ea620" + "hash": "3bdf311fd1b919363cf8de9292cbbe511c5f2c1e4b572ae791852daabe71981e" } diff --git a/.sqlx/query-6e3362d9973d75894d3bd289e7d10d8f76bf8c7b2e3e11de5590e67c2898fe28.json b/.sqlx/query-79b071befac3742d78ff5e928920448547ee257d17a082be2c1debb935ff86a1.json similarity index 90% rename from .sqlx/query-6e3362d9973d75894d3bd289e7d10d8f76bf8c7b2e3e11de5590e67c2898fe28.json rename to .sqlx/query-79b071befac3742d78ff5e928920448547ee257d17a082be2c1debb935ff86a1.json index 6672cb3680..1a445b94f2 100644 --- a/.sqlx/query-6e3362d9973d75894d3bd289e7d10d8f76bf8c7b2e3e11de5590e67c2898fe28.json +++ b/.sqlx/query-79b071befac3742d78ff5e928920448547ee257d17a082be2c1debb935ff86a1.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id \"id?\", username, password_hash, last_name, first_name, email, phone, mfa_enabled, totp_enabled, email_mfa_enabled, totp_secret, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_login FROM \"user\" WHERE username = $1", + "query": "SELECT id \"id?\", username, password_hash, last_name, first_name, email, phone, mfa_enabled, totp_enabled, email_mfa_enabled, totp_secret, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_sub FROM \"user\" WHERE username = $1", "describe": { "columns": [ { @@ -93,8 +93,8 @@ }, { "ordinal": 15, - "name": "openid_login", - "type_info": "Bool" + "name": "openid_sub", + "type_info": "Text" } ], "parameters": { @@ -118,8 +118,8 @@ false, false, false, - false + true ] }, - "hash": "6e3362d9973d75894d3bd289e7d10d8f76bf8c7b2e3e11de5590e67c2898fe28" + "hash": "79b071befac3742d78ff5e928920448547ee257d17a082be2c1debb935ff86a1" } diff --git a/.sqlx/query-a7194f2160706f9d661d0d270aaad92ba54c1a84d7083d47a684a3b1d5ad206f.json b/.sqlx/query-7a5b9fab7d61d39f7d886bf666e4cf783748131a945a943ffb5c9736f17070af.json similarity index 70% rename from .sqlx/query-a7194f2160706f9d661d0d270aaad92ba54c1a84d7083d47a684a3b1d5ad206f.json rename to .sqlx/query-7a5b9fab7d61d39f7d886bf666e4cf783748131a945a943ffb5c9736f17070af.json index d4f8666455..0307d1aef4 100644 --- a/.sqlx/query-a7194f2160706f9d661d0d270aaad92ba54c1a84d7083d47a684a3b1d5ad206f.json +++ b/.sqlx/query-7a5b9fab7d61d39f7d886bf666e4cf783748131a945a943ffb5c9736f17070af.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE \"user\" SET \"username\" = $2,\"password_hash\" = $3,\"last_name\" = $4,\"first_name\" = $5,\"email\" = $6,\"phone\" = $7,\"mfa_enabled\" = $8,\"is_active\" = $9,\"openid_login\" = $10,\"totp_enabled\" = $11,\"email_mfa_enabled\" = $12,\"totp_secret\" = $13,\"email_mfa_secret\" = $14,\"mfa_method\" = $15,\"recovery_codes\" = $16 WHERE id = $1", + "query": "UPDATE \"user\" SET \"username\" = $2,\"password_hash\" = $3,\"last_name\" = $4,\"first_name\" = $5,\"email\" = $6,\"phone\" = $7,\"mfa_enabled\" = $8,\"is_active\" = $9,\"openid_sub\" = $10,\"totp_enabled\" = $11,\"email_mfa_enabled\" = $12,\"totp_secret\" = $13,\"email_mfa_secret\" = $14,\"mfa_method\" = $15,\"recovery_codes\" = $16 WHERE id = $1", "describe": { "columns": [], "parameters": { @@ -14,7 +14,7 @@ "Text", "Bool", "Bool", - "Bool", + "Text", "Bool", "Bool", "Bytea", @@ -38,5 +38,5 @@ }, "nullable": [] }, - "hash": "a7194f2160706f9d661d0d270aaad92ba54c1a84d7083d47a684a3b1d5ad206f" + "hash": "7a5b9fab7d61d39f7d886bf666e4cf783748131a945a943ffb5c9736f17070af" } diff --git a/.sqlx/query-935d9b9ad81c18d764b54ea4fc1d742916968a18854c2f6d9d8004742b9afd54.json b/.sqlx/query-8bf8ce7230829c1994d0505a947e92e284593d2ba102aecff5569cff0536a616.json similarity index 74% rename from .sqlx/query-935d9b9ad81c18d764b54ea4fc1d742916968a18854c2f6d9d8004742b9afd54.json rename to .sqlx/query-8bf8ce7230829c1994d0505a947e92e284593d2ba102aecff5569cff0536a616.json index 781eaeef91..128d80763e 100644 --- a/.sqlx/query-935d9b9ad81c18d764b54ea4fc1d742916968a18854c2f6d9d8004742b9afd54.json +++ b/.sqlx/query-8bf8ce7230829c1994d0505a947e92e284593d2ba102aecff5569cff0536a616.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO \"user\" (\"username\",\"password_hash\",\"last_name\",\"first_name\",\"email\",\"phone\",\"mfa_enabled\",\"is_active\",\"openid_login\",\"totp_enabled\",\"email_mfa_enabled\",\"totp_secret\",\"email_mfa_secret\",\"mfa_method\",\"recovery_codes\") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15) RETURNING id", + "query": "INSERT INTO \"user\" (\"username\",\"password_hash\",\"last_name\",\"first_name\",\"email\",\"phone\",\"mfa_enabled\",\"is_active\",\"openid_sub\",\"totp_enabled\",\"email_mfa_enabled\",\"totp_secret\",\"email_mfa_secret\",\"mfa_method\",\"recovery_codes\") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15) RETURNING id", "describe": { "columns": [ { @@ -19,7 +19,7 @@ "Text", "Bool", "Bool", - "Bool", + "Text", "Bool", "Bool", "Bytea", @@ -45,5 +45,5 @@ false ] }, - "hash": "935d9b9ad81c18d764b54ea4fc1d742916968a18854c2f6d9d8004742b9afd54" + "hash": "8bf8ce7230829c1994d0505a947e92e284593d2ba102aecff5569cff0536a616" } diff --git a/.sqlx/query-891d8d88e0eaba3d67a23dec7de6292046c0be2a179424b57c16767dd1d8b212.json b/.sqlx/query-965e1d9ae54914a8a34a651f489d218f060fc9608c6d233b6f6a4137cf199964.json similarity index 90% rename from .sqlx/query-891d8d88e0eaba3d67a23dec7de6292046c0be2a179424b57c16767dd1d8b212.json rename to .sqlx/query-965e1d9ae54914a8a34a651f489d218f060fc9608c6d233b6f6a4137cf199964.json index 0f55514a6e..17b428d943 100644 --- a/.sqlx/query-891d8d88e0eaba3d67a23dec7de6292046c0be2a179424b57c16767dd1d8b212.json +++ b/.sqlx/query-965e1d9ae54914a8a34a651f489d218f060fc9608c6d233b6f6a4137cf199964.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT u.id \"id?\", u.username, u.password_hash, u.last_name, u.first_name, u.email, u.phone, u.mfa_enabled, u.totp_enabled, u.email_mfa_enabled, u.totp_secret, u.email_mfa_secret, u.mfa_method \"mfa_method: _\", u.recovery_codes, u.is_active, u.openid_login FROM \"user\" as u JOIN \"device\" as d ON u.id = d.user_id WHERE d.id = $1", + "query": "SELECT u.id \"id?\", u.username, u.password_hash, u.last_name, u.first_name, u.email, u.phone, u.mfa_enabled, u.totp_enabled, u.email_mfa_enabled, u.totp_secret, u.email_mfa_secret, u.mfa_method \"mfa_method: _\", u.recovery_codes, u.is_active, u.openid_sub FROM \"user\" as u JOIN \"device\" as d ON u.id = d.user_id WHERE d.id = $1", "describe": { "columns": [ { @@ -93,8 +93,8 @@ }, { "ordinal": 15, - "name": "openid_login", - "type_info": "Bool" + "name": "openid_sub", + "type_info": "Text" } ], "parameters": { @@ -118,8 +118,8 @@ false, false, false, - false + true ] }, - "hash": "891d8d88e0eaba3d67a23dec7de6292046c0be2a179424b57c16767dd1d8b212" + "hash": "965e1d9ae54914a8a34a651f489d218f060fc9608c6d233b6f6a4137cf199964" } diff --git a/.sqlx/query-2f420621d8767a0014924246904b28e05088fe14b53d1b54105aa0d8f50c5d9c.json b/.sqlx/query-afd7f44318c4c9d79211e2978ee5a2953ae5a36ad1fe509b84a9d71ff8c24f79.json similarity index 88% rename from .sqlx/query-2f420621d8767a0014924246904b28e05088fe14b53d1b54105aa0d8f50c5d9c.json rename to .sqlx/query-afd7f44318c4c9d79211e2978ee5a2953ae5a36ad1fe509b84a9d71ff8c24f79.json index a792078586..5066745f62 100644 --- a/.sqlx/query-2f420621d8767a0014924246904b28e05088fe14b53d1b54105aa0d8f50c5d9c.json +++ b/.sqlx/query-afd7f44318c4c9d79211e2978ee5a2953ae5a36ad1fe509b84a9d71ff8c24f79.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT \"user\".id \"id?\", username, password_hash, last_name, first_name, email, phone, mfa_enabled, totp_enabled, totp_secret, email_mfa_enabled, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_login FROM \"user\" JOIN group_user ON \"user\".id = group_user.user_id WHERE group_user.group_id = $1", + "query": "SELECT \"user\".id \"id?\", username, password_hash, last_name, first_name, email, phone, mfa_enabled, totp_enabled, totp_secret, email_mfa_enabled, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_sub FROM \"user\" JOIN group_user ON \"user\".id = group_user.user_id WHERE group_user.group_id = $1", "describe": { "columns": [ { @@ -93,8 +93,8 @@ }, { "ordinal": 15, - "name": "openid_login", - "type_info": "Bool" + "name": "openid_sub", + "type_info": "Text" } ], "parameters": { @@ -118,8 +118,8 @@ false, false, false, - false + true ] }, - "hash": "2f420621d8767a0014924246904b28e05088fe14b53d1b54105aa0d8f50c5d9c" + "hash": "afd7f44318c4c9d79211e2978ee5a2953ae5a36ad1fe509b84a9d71ff8c24f79" } diff --git a/.sqlx/query-0572e003ba6f926f1dcceecb1e534a6ef98659ae7ddf3a68c059886f0b9c2bfa.json b/.sqlx/query-b2428c955f71b2b71ed436c99e913348520849a2e08096962604858abcaeae6c.json similarity index 88% rename from .sqlx/query-0572e003ba6f926f1dcceecb1e534a6ef98659ae7ddf3a68c059886f0b9c2bfa.json rename to .sqlx/query-b2428c955f71b2b71ed436c99e913348520849a2e08096962604858abcaeae6c.json index 52e7998636..66d6c1d769 100644 --- a/.sqlx/query-0572e003ba6f926f1dcceecb1e534a6ef98659ae7ddf3a68c059886f0b9c2bfa.json +++ b/.sqlx/query-b2428c955f71b2b71ed436c99e913348520849a2e08096962604858abcaeae6c.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, mfa_enabled, totp_enabled, email_mfa_enabled, mfa_method as \"mfa_method: MFAMethod\", password_hash, is_active, openid_login FROM \"user\"", + "query": "SELECT id, mfa_enabled, totp_enabled, email_mfa_enabled, mfa_method as \"mfa_method: MFAMethod\", password_hash, is_active, openid_sub FROM \"user\"", "describe": { "columns": [ { @@ -53,8 +53,8 @@ }, { "ordinal": 7, - "name": "openid_login", - "type_info": "Bool" + "name": "openid_sub", + "type_info": "Text" } ], "parameters": { @@ -68,8 +68,8 @@ false, true, false, - false + true ] }, - "hash": "0572e003ba6f926f1dcceecb1e534a6ef98659ae7ddf3a68c059886f0b9c2bfa" + "hash": "b2428c955f71b2b71ed436c99e913348520849a2e08096962604858abcaeae6c" } diff --git a/.sqlx/query-fd4750032e0fcd91f0f8fad9aa946211c645f786602e89561f03a5a60661d191.json b/.sqlx/query-beafeefdf6fe9ac1c3ccce8a4c3917ba8a651c86061a3912365c390492da0ef9.json similarity index 86% rename from .sqlx/query-fd4750032e0fcd91f0f8fad9aa946211c645f786602e89561f03a5a60661d191.json rename to .sqlx/query-beafeefdf6fe9ac1c3ccce8a4c3917ba8a651c86061a3912365c390492da0ef9.json index f0dc610017..b4cf4cc0c7 100644 --- a/.sqlx/query-fd4750032e0fcd91f0f8fad9aa946211c645f786602e89561f03a5a60661d191.json +++ b/.sqlx/query-beafeefdf6fe9ac1c3ccce8a4c3917ba8a651c86061a3912365c390492da0ef9.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id \"id?\", \"username\",\"password_hash\",\"last_name\",\"first_name\",\"email\",\"phone\",\"mfa_enabled\",\"is_active\",\"openid_login\",\"totp_enabled\",\"email_mfa_enabled\",\"totp_secret\",\"email_mfa_secret\",\"mfa_method\" \"mfa_method: _\",\"recovery_codes\" \"recovery_codes: _\" FROM \"user\" WHERE id = $1", + "query": "SELECT id \"id?\", \"username\",\"password_hash\",\"last_name\",\"first_name\",\"email\",\"phone\",\"mfa_enabled\",\"is_active\",\"openid_sub\",\"totp_enabled\",\"email_mfa_enabled\",\"totp_secret\",\"email_mfa_secret\",\"mfa_method\" \"mfa_method: _\",\"recovery_codes\" \"recovery_codes: _\" FROM \"user\" WHERE id = $1", "describe": { "columns": [ { @@ -50,8 +50,8 @@ }, { "ordinal": 9, - "name": "openid_login", - "type_info": "Bool" + "name": "openid_sub", + "type_info": "Text" }, { "ordinal": 10, @@ -112,7 +112,7 @@ true, false, false, - false, + true, false, false, true, @@ -121,5 +121,5 @@ false ] }, - "hash": "fd4750032e0fcd91f0f8fad9aa946211c645f786602e89561f03a5a60661d191" + "hash": "beafeefdf6fe9ac1c3ccce8a4c3917ba8a651c86061a3912365c390492da0ef9" } diff --git a/.sqlx/query-42fbc74f13a7febfbf6bda66cd024e61bf3fdef17222de87443fa9428ea2e30b.json b/.sqlx/query-c3f735782212d2b2d218f2ad66f6febabc15367dd4b7fdba1ae0b8045a84c4ee.json similarity index 86% rename from .sqlx/query-42fbc74f13a7febfbf6bda66cd024e61bf3fdef17222de87443fa9428ea2e30b.json rename to .sqlx/query-c3f735782212d2b2d218f2ad66f6febabc15367dd4b7fdba1ae0b8045a84c4ee.json index 82c8bb185c..5fc015afb4 100644 --- a/.sqlx/query-42fbc74f13a7febfbf6bda66cd024e61bf3fdef17222de87443fa9428ea2e30b.json +++ b/.sqlx/query-c3f735782212d2b2d218f2ad66f6febabc15367dd4b7fdba1ae0b8045a84c4ee.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id \"id?\", \"username\",\"password_hash\",\"last_name\",\"first_name\",\"email\",\"phone\",\"mfa_enabled\",\"is_active\",\"openid_login\",\"totp_enabled\",\"email_mfa_enabled\",\"totp_secret\",\"email_mfa_secret\",\"mfa_method\" \"mfa_method: _\",\"recovery_codes\" \"recovery_codes: _\" FROM \"user\"", + "query": "SELECT id \"id?\", \"username\",\"password_hash\",\"last_name\",\"first_name\",\"email\",\"phone\",\"mfa_enabled\",\"is_active\",\"openid_sub\",\"totp_enabled\",\"email_mfa_enabled\",\"totp_secret\",\"email_mfa_secret\",\"mfa_method\" \"mfa_method: _\",\"recovery_codes\" \"recovery_codes: _\" FROM \"user\"", "describe": { "columns": [ { @@ -50,8 +50,8 @@ }, { "ordinal": 9, - "name": "openid_login", - "type_info": "Bool" + "name": "openid_sub", + "type_info": "Text" }, { "ordinal": 10, @@ -110,7 +110,7 @@ true, false, false, - false, + true, false, false, true, @@ -119,5 +119,5 @@ false ] }, - "hash": "42fbc74f13a7febfbf6bda66cd024e61bf3fdef17222de87443fa9428ea2e30b" + "hash": "c3f735782212d2b2d218f2ad66f6febabc15367dd4b7fdba1ae0b8045a84c4ee" } diff --git a/.sqlx/query-c969fef31aa7d70c9d34eee9ecd363094359cc1c1e21516dad2f082d5224b708.json b/.sqlx/query-c969fef31aa7d70c9d34eee9ecd363094359cc1c1e21516dad2f082d5224b708.json new file mode 100644 index 0000000000..4dbf61a842 --- /dev/null +++ b/.sqlx/query-c969fef31aa7d70c9d34eee9ecd363094359cc1c1e21516dad2f082d5224b708.json @@ -0,0 +1,125 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id \"id?\", username, password_hash, last_name, first_name, email, phone, mfa_enabled, totp_enabled, email_mfa_enabled, totp_secret, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_sub FROM \"user\" WHERE openid_sub = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id?", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "username", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "password_hash", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "last_name", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "first_name", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "email", + "type_info": "Text" + }, + { + "ordinal": 6, + "name": "phone", + "type_info": "Text" + }, + { + "ordinal": 7, + "name": "mfa_enabled", + "type_info": "Bool" + }, + { + "ordinal": 8, + "name": "totp_enabled", + "type_info": "Bool" + }, + { + "ordinal": 9, + "name": "email_mfa_enabled", + "type_info": "Bool" + }, + { + "ordinal": 10, + "name": "totp_secret", + "type_info": "Bytea" + }, + { + "ordinal": 11, + "name": "email_mfa_secret", + "type_info": "Bytea" + }, + { + "ordinal": 12, + "name": "mfa_method: _", + "type_info": { + "Custom": { + "name": "mfa_method", + "kind": { + "Enum": [ + "none", + "one_time_password", + "webauthn", + "web3", + "email" + ] + } + } + } + }, + { + "ordinal": 13, + "name": "recovery_codes", + "type_info": "TextArray" + }, + { + "ordinal": 14, + "name": "is_active", + "type_info": "Bool" + }, + { + "ordinal": 15, + "name": "openid_sub", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + true, + false, + false, + false, + true, + false, + false, + false, + true, + true, + false, + false, + false, + true + ] + }, + "hash": "c969fef31aa7d70c9d34eee9ecd363094359cc1c1e21516dad2f082d5224b708" +} diff --git a/.sqlx/query-246af063a89129453ca514f8f32f8a644fcfc0bbb3ebf17f1f2410e24c3a2316.json b/.sqlx/query-eaf6524ec567e823b3d3dbf90ac1f18ea4a3d84e3d79bde1991c529c803fd2bb.json similarity index 84% rename from .sqlx/query-246af063a89129453ca514f8f32f8a644fcfc0bbb3ebf17f1f2410e24c3a2316.json rename to .sqlx/query-eaf6524ec567e823b3d3dbf90ac1f18ea4a3d84e3d79bde1991c529c803fd2bb.json index 2a93c87f47..03fcdd4ad0 100644 --- a/.sqlx/query-246af063a89129453ca514f8f32f8a644fcfc0bbb3ebf17f1f2410e24c3a2316.json +++ b/.sqlx/query-eaf6524ec567e823b3d3dbf90ac1f18ea4a3d84e3d79bde1991c529c803fd2bb.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT \"user\".id \"id?\", username, password_hash, last_name, first_name, email, phone, mfa_enabled, totp_enabled, totp_secret, email_mfa_enabled, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_login FROM \"user\"\n INNER JOIN \"group_user\" ON \"user\".id = \"group_user\".user_id\n INNER JOIN \"group\" ON \"group_user\".group_id = \"group\".id\n WHERE \"group\".name = $1", + "query": "SELECT \"user\".id \"id?\", username, password_hash, last_name, first_name, email, phone, mfa_enabled, totp_enabled, totp_secret, email_mfa_enabled, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_sub FROM \"user\"\n INNER JOIN \"group_user\" ON \"user\".id = \"group_user\".user_id\n INNER JOIN \"group\" ON \"group_user\".group_id = \"group\".id\n WHERE \"group\".name = $1", "describe": { "columns": [ { @@ -93,8 +93,8 @@ }, { "ordinal": 15, - "name": "openid_login", - "type_info": "Bool" + "name": "openid_sub", + "type_info": "Text" } ], "parameters": { @@ -118,8 +118,8 @@ false, false, false, - false + true ] }, - "hash": "246af063a89129453ca514f8f32f8a644fcfc0bbb3ebf17f1f2410e24c3a2316" + "hash": "eaf6524ec567e823b3d3dbf90ac1f18ea4a3d84e3d79bde1991c529c803fd2bb" } diff --git a/migrations/20240909143350_add_openid_sub.down.sql b/migrations/20240909143350_add_openid_sub.down.sql new file mode 100644 index 0000000000..a440b1d58e --- /dev/null +++ b/migrations/20240909143350_add_openid_sub.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE "user" DROP COLUMN "openid_sub"; +ALTER TABLE "user" ADD COLUMN "openid_login" BOOLEAN NOT NULL DEFAULT FALSE; diff --git a/migrations/20240909143350_add_openid_sub.up.sql b/migrations/20240909143350_add_openid_sub.up.sql new file mode 100644 index 0000000000..9cbf71c504 --- /dev/null +++ b/migrations/20240909143350_add_openid_sub.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE "user" DROP COLUMN "openid_login"; +ALTER TABLE "user" ADD COLUMN "openid_sub" TEXT DEFAULT NULL; diff --git a/src/db/models/group.rs b/src/db/models/group.rs index 15b495af56..b2e1f6e72c 100644 --- a/src/db/models/group.rs +++ b/src/db/models/group.rs @@ -61,7 +61,7 @@ impl Group { User, "SELECT \"user\".id \"id?\", username, password_hash, last_name, first_name, email, \ phone, mfa_enabled, totp_enabled, totp_secret, email_mfa_enabled, email_mfa_secret, \ - mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_login \ + mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_sub \ FROM \"user\" \ JOIN group_user ON \"user\".id = group_user.user_id \ WHERE group_user.group_id = $1", diff --git a/src/db/models/user.rs b/src/db/models/user.rs index 6032543af8..99ec98034d 100644 --- a/src/db/models/user.rs +++ b/src/db/models/user.rs @@ -78,8 +78,9 @@ pub struct User { pub phone: Option, pub mfa_enabled: bool, pub is_active: bool, - // Whether the user has logged in using OIDC - pub openid_login: bool, + /// The user's sub claim returned by the OpenID provider. Also indicates whether the user has + /// used OpenID to log in. + pub openid_sub: Option, // secret has been verified and TOTP can be used pub(crate) totp_enabled: bool, pub(crate) email_mfa_enabled: bool, @@ -126,7 +127,7 @@ impl User { mfa_method: MFAMethod::None, recovery_codes: Vec::new(), is_active: true, - openid_login: false, + openid_sub: None, } } @@ -162,7 +163,7 @@ impl User { /// or they have logged in using an external OIDC. #[must_use] pub fn is_enrolled(&self) -> bool { - self.password_hash.is_some() || self.openid_login + self.password_hash.is_some() || self.openid_sub.is_some() } /// Generate new TOTP secret, save it, then return it as RFC 4648 base32-encoded string. @@ -466,7 +467,7 @@ impl User { ) -> Result, SqlxError> { let users = query!( "SELECT id, mfa_enabled, totp_enabled, email_mfa_enabled, \ - mfa_method as \"mfa_method: MFAMethod\", password_hash, is_active, openid_login \ + mfa_method as \"mfa_method: MFAMethod\", password_hash, is_active, openid_sub \ FROM \"user\"" ) .fetch_all(pool) @@ -480,7 +481,7 @@ impl User { mfa_enabled: u.mfa_enabled, id: u.id, is_active: u.is_active, - enrolled: u.password_hash.is_some() || u.openid_login, + enrolled: u.password_hash.is_some() || u.openid_sub.is_some(), }) .collect(); Ok(res) @@ -496,7 +497,7 @@ impl User { "SELECT \"user\".id \"id?\", username, password_hash, last_name, first_name, email, \ phone, mfa_enabled, totp_enabled, totp_secret, \ email_mfa_enabled, email_mfa_secret, \ - mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_login \ + mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_sub \ FROM \"user\" INNER JOIN \"group_user\" ON \"user\".id = \"group_user\".user_id INNER JOIN \"group\" ON \"group_user\".group_id = \"group\".id @@ -624,7 +625,7 @@ impl User { Self, "SELECT id \"id?\", username, password_hash, last_name, first_name, email, \ phone, mfa_enabled, totp_enabled, email_mfa_enabled, \ - totp_secret, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_login \ + totp_secret, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_sub \ FROM \"user\" WHERE username = $1", username ) @@ -640,7 +641,7 @@ impl User { Self, "SELECT id \"id?\", username, password_hash, last_name, first_name, email, \ phone, mfa_enabled, totp_enabled, email_mfa_enabled, \ - totp_secret, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_login \ + totp_secret, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_sub \ FROM \"user\" WHERE email = $1", email ) @@ -648,6 +649,22 @@ impl User { .await } + pub async fn find_by_sub<'e, E>(executor: E, sub: &str) -> Result, SqlxError> + where + E: PgExecutor<'e>, + { + query_as!( + Self, + "SELECT id \"id?\", username, password_hash, last_name, first_name, email, \ + phone, mfa_enabled, totp_enabled, email_mfa_enabled, \ + totp_secret, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_sub \ + FROM \"user\" WHERE openid_sub = $1", + sub + ) + .fetch_optional(executor) + .await + } + pub async fn member_of_names<'e, E>(&self, executor: E) -> Result, SqlxError> where E: PgExecutor<'e>, @@ -883,7 +900,7 @@ impl User { Self, "SELECT u.id \"id?\", u.username, u.password_hash, u.last_name, u.first_name, u.email, \ u.phone, u.mfa_enabled, u.totp_enabled, u.email_mfa_enabled, \ - u.totp_secret, u.email_mfa_secret, u.mfa_method \"mfa_method: _\", u.recovery_codes, u.is_active, u.openid_login \ + u.totp_secret, u.email_mfa_secret, u.mfa_method \"mfa_method: _\", u.recovery_codes, u.is_active, u.openid_sub \ FROM \"user\" as u \ JOIN \"device\" as d ON u.id = d.user_id \ WHERE d.id = $1", diff --git a/src/enterprise/handlers/openid_login.rs b/src/enterprise/handlers/openid_login.rs index e180d9c267..3548a1c1d9 100644 --- a/src/enterprise/handlers/openid_login.rs +++ b/src/enterprise/handlers/openid_login.rs @@ -245,68 +245,90 @@ pub async fn auth_callback( username }; + // Get the sub claim from the token + let sub = token_claims.subject().to_string(); + // Handle logging in or creating the user let settings = Settings::get_settings(&appstate.pool).await?; - let user = match User::find_by_email(&appstate.pool, email).await { - Ok(Some(mut user)) => { + let user = match User::find_by_sub(&appstate.pool, &sub).await { + Ok(Some(user)) => { + debug!( + "User {} is trying to log in using an OpenID provider.", + user.username + ); // Make sure the user is not disabled if !user.is_active { + debug!("User {} tried to log in, but is disabled", user.username); return Err(WebError::Authorization("User is disabled".to_string())); } - - if !user.openid_login { - user.openid_login = true; - user.save(&appstate.pool).await?; - } user } Ok(None) => { - // Check if the user should be created if they don't exist (default: true) - if settings.openid_create_account { - // Check if user with the same username already exists - if User::find_by_username(&appstate.pool, &username) - .await? - .is_some() - { - return Err(WebError::Authorization(format!( - "User with username {username} already exists" - ))); - } - - // Extract all necessary information from the token needed to create an account - let given_name_error = - "Given name not found in the information returned from provider."; - let given_name = token_claims - .given_name() - .ok_or(WebError::BadRequest(given_name_error.to_string()))? - // 'None' gets you the default value from a localized claim. Otherwise you would need to pass a locale. - .get(None) - .ok_or(WebError::BadRequest(given_name_error.to_string()))?; - let family_name_error = - "Family name not found in the information returned from provider."; - let family_name = token_claims - .family_name() - .ok_or(WebError::BadRequest(family_name_error.to_string()))? - .get(None) - .ok_or(WebError::BadRequest(family_name_error.to_string()))?; - let phone = token_claims.phone_number(); - - let mut user = User::new( - username.to_string(), - None, - family_name.to_string(), - given_name.to_string(), - email.to_string(), - phone.map(|v| v.to_string()), - ); - user.openid_login = true; + if let Some(mut user) = User::find_by_email(&appstate.pool, &email).await? { + // User with the same email already exists, merge the accounts + info!( + "User with email address {} is logging in through OpenID Connect for the first time and we've found an existing account with the same email address. Merging accounts.", + user.email + ); + user.openid_sub = Some(sub); user.save(&appstate.pool).await?; user } else { - return Err(WebError::Authorization( - "User not found. The user needs to be created first in order to login using OIDC." - .to_string(), - )); + // Check if the user should be created if they don't exist (default: true) + if settings.openid_create_account { + info!( + "User {} is logging in through OpenID Connect for the first time and there is no account with the same email address ({}). Creating a new account.", + username, email.as_str() + ); + // Check if user with the same username already exists + // Usernames are unique + if User::find_by_username(&appstate.pool, &username) + .await? + .is_some() + { + return Err(WebError::Authorization(format!( + "User with username {username} already exists" + ))); + } + + // Extract all necessary information from the token needed to create an account + let given_name_error = + "Given name not found in the information returned from provider."; + let given_name = token_claims + .given_name() + .ok_or(WebError::BadRequest(given_name_error.to_string()))? + // 'None' gets you the default value from a localized claim. Otherwise you would need to pass a locale. + .get(None) + .ok_or(WebError::BadRequest(given_name_error.to_string()))?; + let family_name_error = + "Family name not found in the information returned from provider."; + let family_name = token_claims + .family_name() + .ok_or(WebError::BadRequest(family_name_error.to_string()))? + .get(None) + .ok_or(WebError::BadRequest(family_name_error.to_string()))?; + let phone = token_claims.phone_number(); + + let mut user = User::new( + username.to_string(), + None, + family_name.to_string(), + given_name.to_string(), + email.to_string(), + phone.map(|v| v.to_string()), + ); + user.openid_sub = Some(sub); + user.save(&appstate.pool).await?; + user + } else { + warn!( + "User with email address {} is trying to log in through OpenID Connect for the first time, but the account creation is disabled. They should perform an enrollment first.", + email.as_str() + ); + return Err(WebError::Authorization( + "User not found. The user needs to be created first in order to login using OIDC.".to_string(), + )); + } } } Err(e) => { @@ -347,8 +369,9 @@ pub async fn auth_callback( let cookies = cookies.add(auth_cookie); let login_event_type = "AUTHENTICATION".to_string(); - info!("Authenticated user {username} with external OpenID provider. Veryfing MFA status..."); + info!("Authenticated user {username} with external OpenID provider."); if user.mfa_enabled { + debug!("User {username} has MFA enabled, sending MFA info for further authentication."); if let Some(mfa_info) = MFAInfo::for_user(&appstate.pool, &user).await? { check_new_device_login( &appstate.pool, @@ -373,6 +396,7 @@ pub async fn auth_callback( Err(WebError::DbError("MFA info read error".into())) } } else { + debug!("User {username} has MFA disabled, returning user info for login."); let user_info = UserInfo::from_user(&appstate.pool, &user).await?; check_new_device_login( @@ -387,7 +411,7 @@ pub async fn auth_callback( .await?; if let Some(openid_cookie) = private_cookies.get(SIGN_IN_COOKIE_NAME) { - debug!("Found openid session cookie."); + debug!("Found openid session cookie, returning the redirect URL stored in the cookie."); let redirect_url = openid_cookie.value().to_string(); Ok(( cookies, @@ -401,7 +425,7 @@ pub async fn auth_callback( }, )) } else { - debug!("No OpenID session found"); + debug!("No OpenID session found, proceeding with login to defguard."); Ok(( cookies, private_cookies, diff --git a/src/handlers/group.rs b/src/handlers/group.rs index c4a099026e..ee5c7e3ff3 100644 --- a/src/handlers/group.rs +++ b/src/handlers/group.rs @@ -63,7 +63,7 @@ pub(crate) async fn bulk_assign_to_groups( User, "SELECT id \"id?\", username, password_hash, last_name, first_name, email, \ phone, mfa_enabled, totp_enabled, email_mfa_enabled, \ - totp_secret, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_login \ + totp_secret, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_sub \ FROM \"user\" WHERE id = ANY($1)", &data.users )