diff --git a/.sqlx/query-05fd99170e31e68fa5028c862417cdf535cd70e09fde0a8a28249df0070eb2fc.json b/.sqlx/query-05fd99170e31e68fa5028c862417cdf535cd70e09fde0a8a28249df0070eb2fc.json new file mode 100644 index 0000000..15ba9ce --- /dev/null +++ b/.sqlx/query-05fd99170e31e68fa5028c862417cdf535cd70e09fde0a8a28249df0070eb2fc.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT t.token FROM plc_operation_tokens t JOIN users u ON t.user_id = u.id WHERE u.did = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "token", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "05fd99170e31e68fa5028c862417cdf535cd70e09fde0a8a28249df0070eb2fc" +} diff --git a/.sqlx/query-0710b57fb9aa933525f617b15e6e2e5feaa9c59c38ec9175568abdacda167107.json b/.sqlx/query-0710b57fb9aa933525f617b15e6e2e5feaa9c59c38ec9175568abdacda167107.json new file mode 100644 index 0000000..734848a --- /dev/null +++ b/.sqlx/query-0710b57fb9aa933525f617b15e6e2e5feaa9c59c38ec9175568abdacda167107.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE users SET deactivated_at = $1 WHERE did = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Timestamptz", + "Text" + ] + }, + "nullable": [] + }, + "hash": "0710b57fb9aa933525f617b15e6e2e5feaa9c59c38ec9175568abdacda167107" +} diff --git a/.sqlx/query-0ec60bb854a4991d0d7249a68f7445b65c8cc8c723baca221d85f5e4f2478b99.json b/.sqlx/query-0ec60bb854a4991d0d7249a68f7445b65c8cc8c723baca221d85f5e4f2478b99.json new file mode 100644 index 0000000..9e5e897 --- /dev/null +++ b/.sqlx/query-0ec60bb854a4991d0d7249a68f7445b65c8cc8c723baca221d85f5e4f2478b99.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT body FROM comms_queue WHERE user_id = (SELECT id FROM users WHERE did = $1) AND comms_type = 'email_update' ORDER BY created_at DESC LIMIT 1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "body", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "0ec60bb854a4991d0d7249a68f7445b65c8cc8c723baca221d85f5e4f2478b99" +} diff --git a/.sqlx/query-0fae1be7a75bdc58c69a9af97cad4aec23c32a9378764b8d6d7eb2cc89c562b1.json b/.sqlx/query-0fae1be7a75bdc58c69a9af97cad4aec23c32a9378764b8d6d7eb2cc89c562b1.json new file mode 100644 index 0000000..7da18e9 --- /dev/null +++ b/.sqlx/query-0fae1be7a75bdc58c69a9af97cad4aec23c32a9378764b8d6d7eb2cc89c562b1.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT token FROM sso_pending_registration WHERE token = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "token", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "0fae1be7a75bdc58c69a9af97cad4aec23c32a9378764b8d6d7eb2cc89c562b1" +} diff --git a/.sqlx/query-1c84643fd6bc57c76517849a64d2d877df337e823d4c2c2b077f695bbfc9e9ac.json b/.sqlx/query-1c84643fd6bc57c76517849a64d2d877df337e823d4c2c2b077f695bbfc9e9ac.json new file mode 100644 index 0000000..d2228ff --- /dev/null +++ b/.sqlx/query-1c84643fd6bc57c76517849a64d2d877df337e823d4c2c2b077f695bbfc9e9ac.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "\n DELETE FROM sso_pending_registration\n WHERE token = $1 AND expires_at > NOW()\n RETURNING token\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "token", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "1c84643fd6bc57c76517849a64d2d877df337e823d4c2c2b077f695bbfc9e9ac" +} diff --git a/.sqlx/query-24b823043ab60f36c29029137fef30dfe33922bb06067f2fdbfc1fbb4b0a2a81.json b/.sqlx/query-24b823043ab60f36c29029137fef30dfe33922bb06067f2fdbfc1fbb4b0a2a81.json new file mode 100644 index 0000000..06e8ff2 --- /dev/null +++ b/.sqlx/query-24b823043ab60f36c29029137fef30dfe33922bb06067f2fdbfc1fbb4b0a2a81.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "\n DELETE FROM sso_pending_registration\n WHERE token = $1 AND expires_at > NOW()\n RETURNING token, request_uri\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "token", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "request_uri", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "24b823043ab60f36c29029137fef30dfe33922bb06067f2fdbfc1fbb4b0a2a81" +} diff --git a/.sqlx/query-2841093a67480e75e1e9e4046bf3eb74afae2d04f5ea0ec17a4d433983e6d71c.json b/.sqlx/query-2841093a67480e75e1e9e4046bf3eb74afae2d04f5ea0ec17a4d433983e6d71c.json new file mode 100644 index 0000000..ffaa87b --- /dev/null +++ b/.sqlx/query-2841093a67480e75e1e9e4046bf3eb74afae2d04f5ea0ec17a4d433983e6d71c.json @@ -0,0 +1,38 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO external_identities (did, provider, provider_user_id)\n VALUES ($1, $2, $3)\n RETURNING id\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Text", + { + "Custom": { + "name": "sso_provider_type", + "kind": { + "Enum": [ + "github", + "discord", + "google", + "gitlab", + "oidc", + "apple" + ] + } + } + }, + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "2841093a67480e75e1e9e4046bf3eb74afae2d04f5ea0ec17a4d433983e6d71c" +} diff --git a/.sqlx/query-29ef76852bb89af1ab9e679ceaa4abcf8bc8268a348d3be0da9840d1708d20b5.json b/.sqlx/query-29ef76852bb89af1ab9e679ceaa4abcf8bc8268a348d3be0da9840d1708d20b5.json new file mode 100644 index 0000000..8d0d055 --- /dev/null +++ b/.sqlx/query-29ef76852bb89af1ab9e679ceaa4abcf8bc8268a348d3be0da9840d1708d20b5.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE users SET password_reset_code_expires_at = NOW() - INTERVAL '1 hour' WHERE email = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [] + }, + "hash": "29ef76852bb89af1ab9e679ceaa4abcf8bc8268a348d3be0da9840d1708d20b5" +} diff --git a/.sqlx/query-376b72306b50f747bc9161985ff4f50c35c53025a55ccf5e9933dc3795d29313.json b/.sqlx/query-376b72306b50f747bc9161985ff4f50c35c53025a55ccf5e9933dc3795d29313.json new file mode 100644 index 0000000..621ea6b --- /dev/null +++ b/.sqlx/query-376b72306b50f747bc9161985ff4f50c35c53025a55ccf5e9933dc3795d29313.json @@ -0,0 +1,32 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO sso_pending_registration (token, request_uri, provider, provider_user_id, provider_email_verified)\n VALUES ($1, $2, $3, $4, $5)\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + { + "Custom": { + "name": "sso_provider_type", + "kind": { + "Enum": [ + "github", + "discord", + "google", + "gitlab", + "oidc", + "apple" + ] + } + } + }, + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "376b72306b50f747bc9161985ff4f50c35c53025a55ccf5e9933dc3795d29313" +} diff --git a/.sqlx/query-3933ea5b147ab6294936de147b98e116cfae848ecd76ea5d367585eb5117f2ad.json b/.sqlx/query-3933ea5b147ab6294936de147b98e116cfae848ecd76ea5d367585eb5117f2ad.json new file mode 100644 index 0000000..8192722 --- /dev/null +++ b/.sqlx/query-3933ea5b147ab6294936de147b98e116cfae848ecd76ea5d367585eb5117f2ad.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id FROM external_identities WHERE id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + false + ] + }, + "hash": "3933ea5b147ab6294936de147b98e116cfae848ecd76ea5d367585eb5117f2ad" +} diff --git a/.sqlx/query-3bed8d4843545f4a9676207513806603c50eb2af92957994abaf1c89c0294c12.json b/.sqlx/query-3bed8d4843545f4a9676207513806603c50eb2af92957994abaf1c89c0294c12.json new file mode 100644 index 0000000..097e2cc --- /dev/null +++ b/.sqlx/query-3bed8d4843545f4a9676207513806603c50eb2af92957994abaf1c89c0294c12.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO users (did, handle, email, password_hash) VALUES ($1, $2, $3, 'hash')", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "3bed8d4843545f4a9676207513806603c50eb2af92957994abaf1c89c0294c12" +} diff --git a/.sqlx/query-4445cc86cdf04894b340e67661b79a3c411917144a011f50849b737130b24dbe.json b/.sqlx/query-4445cc86cdf04894b340e67661b79a3c411917144a011f50849b737130b24dbe.json new file mode 100644 index 0000000..ac57c00 --- /dev/null +++ b/.sqlx/query-4445cc86cdf04894b340e67661b79a3c411917144a011f50849b737130b24dbe.json @@ -0,0 +1,55 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT subject, body, comms_type as \"comms_type: String\" FROM comms_queue WHERE user_id = $1 AND comms_type = 'admin_email' ORDER BY created_at DESC LIMIT 1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "subject", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "body", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "comms_type: String", + "type_info": { + "Custom": { + "name": "comms_type", + "kind": { + "Enum": [ + "welcome", + "email_verification", + "password_reset", + "email_update", + "account_deletion", + "admin_email", + "plc_operation", + "two_factor_code", + "channel_verification", + "passkey_recovery", + "legacy_login_alert", + "migration_verification", + "channel_verified" + ] + } + } + } + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + true, + false, + false + ] + }, + "hash": "4445cc86cdf04894b340e67661b79a3c411917144a011f50849b737130b24dbe" +} diff --git a/.sqlx/query-4560c237741ce9d4166aecd669770b3360a3ac71e649b293efb88d92c3254068.json b/.sqlx/query-4560c237741ce9d4166aecd669770b3360a3ac71e649b293efb88d92c3254068.json new file mode 100644 index 0000000..b81fee7 --- /dev/null +++ b/.sqlx/query-4560c237741ce9d4166aecd669770b3360a3ac71e649b293efb88d92c3254068.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id FROM users WHERE email = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "4560c237741ce9d4166aecd669770b3360a3ac71e649b293efb88d92c3254068" +} diff --git a/.sqlx/query-47fe4a54857344d8f789f37092a294cd58f64b4fb431b54b5deda13d64525e88.json b/.sqlx/query-47fe4a54857344d8f789f37092a294cd58f64b4fb431b54b5deda13d64525e88.json new file mode 100644 index 0000000..e232907 --- /dev/null +++ b/.sqlx/query-47fe4a54857344d8f789f37092a294cd58f64b4fb431b54b5deda13d64525e88.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT token, expires_at FROM account_deletion_requests WHERE did = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "token", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "expires_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "47fe4a54857344d8f789f37092a294cd58f64b4fb431b54b5deda13d64525e88" +} diff --git a/.sqlx/query-49cbc923cc4a0dcf7dea4ead5ab9580ff03b717586c4ca2d5343709e2dac86b6.json b/.sqlx/query-49cbc923cc4a0dcf7dea4ead5ab9580ff03b717586c4ca2d5343709e2dac86b6.json new file mode 100644 index 0000000..873f0f7 --- /dev/null +++ b/.sqlx/query-49cbc923cc4a0dcf7dea4ead5ab9580ff03b717586c4ca2d5343709e2dac86b6.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT email_verified FROM users WHERE did = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "email_verified", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "49cbc923cc4a0dcf7dea4ead5ab9580ff03b717586c4ca2d5343709e2dac86b6" +} diff --git a/.sqlx/query-4fef326fa2d03d04869af3fec702c901d1ecf392545a3a032438b2c1859d46cc.json b/.sqlx/query-4fef326fa2d03d04869af3fec702c901d1ecf392545a3a032438b2c1859d46cc.json new file mode 100644 index 0000000..3780e70 --- /dev/null +++ b/.sqlx/query-4fef326fa2d03d04869af3fec702c901d1ecf392545a3a032438b2c1859d46cc.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT token FROM sso_pending_registration\n WHERE token = $1 AND expires_at > NOW()\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "token", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "4fef326fa2d03d04869af3fec702c901d1ecf392545a3a032438b2c1859d46cc" +} diff --git a/.sqlx/query-575c1e5529874f8f523e6fe22ccf4ee3296806581b1765dfb91a84ffab347f15.json b/.sqlx/query-575c1e5529874f8f523e6fe22ccf4ee3296806581b1765dfb91a84ffab347f15.json new file mode 100644 index 0000000..e11b68f --- /dev/null +++ b/.sqlx/query-575c1e5529874f8f523e6fe22ccf4ee3296806581b1765dfb91a84ffab347f15.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO oauth_authorization_request (id, client_id, parameters, expires_at)\n VALUES ($1, 'https://test.example.com', $2, NOW() + INTERVAL '1 hour')\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "575c1e5529874f8f523e6fe22ccf4ee3296806581b1765dfb91a84ffab347f15" +} diff --git a/.sqlx/query-596c3400a60c77c7645fd46fcea61fa7898b6832e58c0f647f382b23b81d350e.json b/.sqlx/query-596c3400a60c77c7645fd46fcea61fa7898b6832e58c0f647f382b23b81d350e.json new file mode 100644 index 0000000..dfa5ed9 --- /dev/null +++ b/.sqlx/query-596c3400a60c77c7645fd46fcea61fa7898b6832e58c0f647f382b23b81d350e.json @@ -0,0 +1,33 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO sso_pending_registration (token, request_uri, provider, provider_user_id, provider_username, provider_email)\n VALUES ($1, $2, $3, $4, $5, $6)\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + { + "Custom": { + "name": "sso_provider_type", + "kind": { + "Enum": [ + "github", + "discord", + "google", + "gitlab", + "oidc", + "apple" + ] + } + } + }, + "Text", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "596c3400a60c77c7645fd46fcea61fa7898b6832e58c0f647f382b23b81d350e" +} diff --git a/.sqlx/query-59e63c5cf92985714e9586d1ce012efef733d4afaa4ea09974daf8303805e5d2.json b/.sqlx/query-59e63c5cf92985714e9586d1ce012efef733d4afaa4ea09974daf8303805e5d2.json new file mode 100644 index 0000000..6b1501f --- /dev/null +++ b/.sqlx/query-59e63c5cf92985714e9586d1ce012efef733d4afaa4ea09974daf8303805e5d2.json @@ -0,0 +1,81 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT id, did, provider as \"provider: SsoProviderType\", provider_user_id, provider_username, provider_email\n FROM external_identities\n WHERE provider = $1 AND provider_user_id = $2\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "did", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "provider: SsoProviderType", + "type_info": { + "Custom": { + "name": "sso_provider_type", + "kind": { + "Enum": [ + "github", + "discord", + "google", + "gitlab", + "oidc", + "apple" + ] + } + } + } + }, + { + "ordinal": 3, + "name": "provider_user_id", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "provider_username", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "provider_email", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + { + "Custom": { + "name": "sso_provider_type", + "kind": { + "Enum": [ + "github", + "discord", + "google", + "gitlab", + "oidc", + "apple" + ] + } + } + }, + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + true, + true + ] + }, + "hash": "59e63c5cf92985714e9586d1ce012efef733d4afaa4ea09974daf8303805e5d2" +} diff --git a/.sqlx/query-5a016f289caf75177731711e56e92881ba343c73a9a6e513e205c801c5943ec0.json b/.sqlx/query-5a016f289caf75177731711e56e92881ba343c73a9a6e513e205c801c5943ec0.json new file mode 100644 index 0000000..32bd1aa --- /dev/null +++ b/.sqlx/query-5a016f289caf75177731711e56e92881ba343c73a9a6e513e205c801c5943ec0.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT k.key_bytes, k.encryption_version\n FROM user_keys k\n JOIN users u ON k.user_id = u.id\n WHERE u.did = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "key_bytes", + "type_info": "Bytea" + }, + { + "ordinal": 1, + "name": "encryption_version", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + true + ] + }, + "hash": "5a016f289caf75177731711e56e92881ba343c73a9a6e513e205c801c5943ec0" +} diff --git a/.sqlx/query-5af4a386c1632903ad7102551a5bd148bcf541baab6a84c8649666a695f9c4d1.json b/.sqlx/query-5af4a386c1632903ad7102551a5bd148bcf541baab6a84c8649666a695f9c4d1.json new file mode 100644 index 0000000..0f764da --- /dev/null +++ b/.sqlx/query-5af4a386c1632903ad7102551a5bd148bcf541baab6a84c8649666a695f9c4d1.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "\n DELETE FROM sso_auth_state\n WHERE state = $1 AND expires_at > NOW()\n RETURNING state\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "state", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "5af4a386c1632903ad7102551a5bd148bcf541baab6a84c8649666a695f9c4d1" +} diff --git a/.sqlx/query-5e4c0dd92ac3c4b5e2eae5d129f2649cf3a8f068105f44a8dca9625427affc06.json b/.sqlx/query-5e4c0dd92ac3c4b5e2eae5d129f2649cf3a8f068105f44a8dca9625427affc06.json new file mode 100644 index 0000000..525f4ef --- /dev/null +++ b/.sqlx/query-5e4c0dd92ac3c4b5e2eae5d129f2649cf3a8f068105f44a8dca9625427affc06.json @@ -0,0 +1,43 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT provider_user_id, provider_email_verified\n FROM external_identities\n WHERE did = $1 AND provider = $2\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "provider_user_id", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "provider_email_verified", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text", + { + "Custom": { + "name": "sso_provider_type", + "kind": { + "Enum": [ + "github", + "discord", + "google", + "gitlab", + "oidc", + "apple" + ] + } + } + } + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "5e4c0dd92ac3c4b5e2eae5d129f2649cf3a8f068105f44a8dca9625427affc06" +} diff --git a/.sqlx/query-5e9c6ec72c2c0ea1c8dff551d01baddd1dd953c828a5656db2ee39dea996f890.json b/.sqlx/query-5e9c6ec72c2c0ea1c8dff551d01baddd1dd953c828a5656db2ee39dea996f890.json new file mode 100644 index 0000000..f109216 --- /dev/null +++ b/.sqlx/query-5e9c6ec72c2c0ea1c8dff551d01baddd1dd953c828a5656db2ee39dea996f890.json @@ -0,0 +1,33 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO sso_auth_state (state, request_uri, provider, action, nonce, code_verifier)\n VALUES ($1, $2, $3, $4, $5, $6)\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + { + "Custom": { + "name": "sso_provider_type", + "kind": { + "Enum": [ + "github", + "discord", + "google", + "gitlab", + "oidc", + "apple" + ] + } + } + }, + "Text", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "5e9c6ec72c2c0ea1c8dff551d01baddd1dd953c828a5656db2ee39dea996f890" +} diff --git a/.sqlx/query-63f6f2a89650794fe90e10ce7fc785a6b9f7d37c12b31a6ff13f7c5214eef19e.json b/.sqlx/query-63f6f2a89650794fe90e10ce7fc785a6b9f7d37c12b31a6ff13f7c5214eef19e.json new file mode 100644 index 0000000..cb057f9 --- /dev/null +++ b/.sqlx/query-63f6f2a89650794fe90e10ce7fc785a6b9f7d37c12b31a6ff13f7c5214eef19e.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT did, email_verified FROM users WHERE did = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "did", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "email_verified", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "63f6f2a89650794fe90e10ce7fc785a6b9f7d37c12b31a6ff13f7c5214eef19e" +} diff --git a/.sqlx/query-6c7ace2a64848adc757af6c93b9162e1d95788b372370a7ad0d7540338bb73ee.json b/.sqlx/query-6c7ace2a64848adc757af6c93b9162e1d95788b372370a7ad0d7540338bb73ee.json new file mode 100644 index 0000000..eed954b --- /dev/null +++ b/.sqlx/query-6c7ace2a64848adc757af6c93b9162e1d95788b372370a7ad0d7540338bb73ee.json @@ -0,0 +1,66 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT state, request_uri, provider as \"provider: SsoProviderType\", action, nonce, code_verifier\n FROM sso_auth_state\n WHERE state = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "state", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "request_uri", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "provider: SsoProviderType", + "type_info": { + "Custom": { + "name": "sso_provider_type", + "kind": { + "Enum": [ + "github", + "discord", + "google", + "gitlab", + "oidc", + "apple" + ] + } + } + } + }, + { + "ordinal": 3, + "name": "action", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "nonce", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "code_verifier", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + true, + true + ] + }, + "hash": "6c7ace2a64848adc757af6c93b9162e1d95788b372370a7ad0d7540338bb73ee" +} diff --git a/.sqlx/query-6fbcff0206599484bfb6cef165b6f729d27e7a342f7718ee4ac07f0ca94412ba.json b/.sqlx/query-6fbcff0206599484bfb6cef165b6f729d27e7a342f7718ee4ac07f0ca94412ba.json new file mode 100644 index 0000000..dbd52e6 --- /dev/null +++ b/.sqlx/query-6fbcff0206599484bfb6cef165b6f729d27e7a342f7718ee4ac07f0ca94412ba.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT state FROM sso_auth_state WHERE state = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "state", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "6fbcff0206599484bfb6cef165b6f729d27e7a342f7718ee4ac07f0ca94412ba" +} diff --git a/.sqlx/query-712459c27fc037f45389e2766cf1057e86e93ef756a784ed12beb453b03c5da1.json b/.sqlx/query-712459c27fc037f45389e2766cf1057e86e93ef756a784ed12beb453b03c5da1.json new file mode 100644 index 0000000..e831be8 --- /dev/null +++ b/.sqlx/query-712459c27fc037f45389e2766cf1057e86e93ef756a784ed12beb453b03c5da1.json @@ -0,0 +1,33 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO sso_pending_registration (token, request_uri, provider, provider_user_id, provider_username, provider_email_verified)\n VALUES ($1, $2, $3, $4, $5, $6)\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + { + "Custom": { + "name": "sso_provider_type", + "kind": { + "Enum": [ + "github", + "discord", + "google", + "gitlab", + "oidc", + "apple" + ] + } + } + }, + "Text", + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "712459c27fc037f45389e2766cf1057e86e93ef756a784ed12beb453b03c5da1" +} diff --git a/.sqlx/query-785a864944c5939331704c71b0cd3ed26ffdd64f3fd0f26ecc28b6a0557bbe8f.json b/.sqlx/query-785a864944c5939331704c71b0cd3ed26ffdd64f3fd0f26ecc28b6a0557bbe8f.json new file mode 100644 index 0000000..af80ee3 --- /dev/null +++ b/.sqlx/query-785a864944c5939331704c71b0cd3ed26ffdd64f3fd0f26ecc28b6a0557bbe8f.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT subject FROM comms_queue WHERE user_id = $1 AND comms_type = 'admin_email' AND body = 'Email without subject' LIMIT 1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "subject", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + true + ] + }, + "hash": "785a864944c5939331704c71b0cd3ed26ffdd64f3fd0f26ecc28b6a0557bbe8f" +} diff --git a/.sqlx/query-7caa8f9083b15ec1209dda35c4c6f6fba9fe338e4a6a10636b5389d426df1631.json b/.sqlx/query-7caa8f9083b15ec1209dda35c4c6f6fba9fe338e4a6a10636b5389d426df1631.json new file mode 100644 index 0000000..416f681 --- /dev/null +++ b/.sqlx/query-7caa8f9083b15ec1209dda35c4c6f6fba9fe338e4a6a10636b5389d426df1631.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT t.token\n FROM plc_operation_tokens t\n JOIN users u ON t.user_id = u.id\n WHERE u.did = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "token", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "7caa8f9083b15ec1209dda35c4c6f6fba9fe338e4a6a10636b5389d426df1631" +} diff --git a/.sqlx/query-7d24e744a4e63570b1410e50b45b745ce8915ab3715b3eff7efc2d84f27735d0.json b/.sqlx/query-7d24e744a4e63570b1410e50b45b745ce8915ab3715b3eff7efc2d84f27735d0.json new file mode 100644 index 0000000..25e1b1d --- /dev/null +++ b/.sqlx/query-7d24e744a4e63570b1410e50b45b745ce8915ab3715b3eff7efc2d84f27735d0.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT provider_username, last_login_at FROM external_identities WHERE id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "provider_username", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "last_login_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + true, + true + ] + }, + "hash": "7d24e744a4e63570b1410e50b45b745ce8915ab3715b3eff7efc2d84f27735d0" +} diff --git a/.sqlx/query-82717b6f61cd79347e1ca7e92c4413743ba168d1e0d8b85566711e54d4048f81.json b/.sqlx/query-82717b6f61cd79347e1ca7e92c4413743ba168d1e0d8b85566711e54d4048f81.json new file mode 100644 index 0000000..8d5a9b7 --- /dev/null +++ b/.sqlx/query-82717b6f61cd79347e1ca7e92c4413743ba168d1e0d8b85566711e54d4048f81.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT t.token, t.expires_at FROM plc_operation_tokens t JOIN users u ON t.user_id = u.id WHERE u.did = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "token", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "expires_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "82717b6f61cd79347e1ca7e92c4413743ba168d1e0d8b85566711e54d4048f81" +} diff --git a/.sqlx/query-85ffc37a77af832d7795f5f37efe304fced4bf56b4f2287fe9aeb3fc97e1b191.json b/.sqlx/query-85ffc37a77af832d7795f5f37efe304fced4bf56b4f2287fe9aeb3fc97e1b191.json new file mode 100644 index 0000000..7938c39 --- /dev/null +++ b/.sqlx/query-85ffc37a77af832d7795f5f37efe304fced4bf56b4f2287fe9aeb3fc97e1b191.json @@ -0,0 +1,34 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO sso_pending_registration (token, request_uri, provider, provider_user_id, provider_username, provider_email, provider_email_verified)\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + { + "Custom": { + "name": "sso_provider_type", + "kind": { + "Enum": [ + "github", + "discord", + "google", + "gitlab", + "oidc", + "apple" + ] + } + } + }, + "Text", + "Text", + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "85ffc37a77af832d7795f5f37efe304fced4bf56b4f2287fe9aeb3fc97e1b191" +} diff --git a/.sqlx/query-9ad422bf3c43e3cfd86fc88c73594246ead214ca794760d3fe77bb5cf4f27be5.json b/.sqlx/query-9ad422bf3c43e3cfd86fc88c73594246ead214ca794760d3fe77bb5cf4f27be5.json new file mode 100644 index 0000000..ef52899 --- /dev/null +++ b/.sqlx/query-9ad422bf3c43e3cfd86fc88c73594246ead214ca794760d3fe77bb5cf4f27be5.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT body FROM comms_queue WHERE user_id = (SELECT id FROM users WHERE did = $1) AND comms_type = 'email_verification' ORDER BY created_at DESC LIMIT 1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "body", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "9ad422bf3c43e3cfd86fc88c73594246ead214ca794760d3fe77bb5cf4f27be5" +} diff --git a/.sqlx/query-9b035b051769e6b9d45910a8bb42ac0f84c73de8c244ba4560f004ee3f4b7002.json b/.sqlx/query-9b035b051769e6b9d45910a8bb42ac0f84c73de8c244ba4560f004ee3f4b7002.json new file mode 100644 index 0000000..178bc9e --- /dev/null +++ b/.sqlx/query-9b035b051769e6b9d45910a8bb42ac0f84c73de8c244ba4560f004ee3f4b7002.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT did, public_key_did_key FROM reserved_signing_keys WHERE public_key_did_key = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "did", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "public_key_did_key", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + true, + false + ] + }, + "hash": "9b035b051769e6b9d45910a8bb42ac0f84c73de8c244ba4560f004ee3f4b7002" +} diff --git a/.sqlx/query-9dba64081d4f95b5490c9a9bf30a7175db3429f39df4f25e212f38f33882fc65.json b/.sqlx/query-9dba64081d4f95b5490c9a9bf30a7175db3429f39df4f25e212f38f33882fc65.json new file mode 100644 index 0000000..ea2847f --- /dev/null +++ b/.sqlx/query-9dba64081d4f95b5490c9a9bf30a7175db3429f39df4f25e212f38f33882fc65.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT id FROM external_identities WHERE did = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "9dba64081d4f95b5490c9a9bf30a7175db3429f39df4f25e212f38f33882fc65" +} diff --git a/.sqlx/query-9fd56986c1c843d386d1e5884acef8573eb55a3e9f5cb0122fcf8b93d6d667a5.json b/.sqlx/query-9fd56986c1c843d386d1e5884acef8573eb55a3e9f5cb0122fcf8b93d6d667a5.json new file mode 100644 index 0000000..c77d51a --- /dev/null +++ b/.sqlx/query-9fd56986c1c843d386d1e5884acef8573eb55a3e9f5cb0122fcf8b93d6d667a5.json @@ -0,0 +1,66 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT token, request_uri, provider as \"provider: SsoProviderType\", provider_user_id,\n provider_username, provider_email\n FROM sso_pending_registration\n WHERE token = $1 AND expires_at > NOW()\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "token", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "request_uri", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "provider: SsoProviderType", + "type_info": { + "Custom": { + "name": "sso_provider_type", + "kind": { + "Enum": [ + "github", + "discord", + "google", + "gitlab", + "oidc", + "apple" + ] + } + } + } + }, + { + "ordinal": 3, + "name": "provider_user_id", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "provider_username", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "provider_email", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + true, + true + ] + }, + "hash": "9fd56986c1c843d386d1e5884acef8573eb55a3e9f5cb0122fcf8b93d6d667a5" +} diff --git a/.sqlx/query-a23a390659616779d7dbceaa3b5d5171e70fa25e3b8393e142cebcbff752f0f5.json b/.sqlx/query-a23a390659616779d7dbceaa3b5d5171e70fa25e3b8393e142cebcbff752f0f5.json new file mode 100644 index 0000000..2640f70 --- /dev/null +++ b/.sqlx/query-a23a390659616779d7dbceaa3b5d5171e70fa25e3b8393e142cebcbff752f0f5.json @@ -0,0 +1,34 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT private_key_bytes, expires_at, used_at FROM reserved_signing_keys WHERE public_key_did_key = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "private_key_bytes", + "type_info": "Bytea" + }, + { + "ordinal": 1, + "name": "expires_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 2, + "name": "used_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + true + ] + }, + "hash": "a23a390659616779d7dbceaa3b5d5171e70fa25e3b8393e142cebcbff752f0f5" +} diff --git a/.sqlx/query-a3d549a32e76c24e265c73a98dd739067623f275de0740bd576ee288f4444496.json b/.sqlx/query-a3d549a32e76c24e265c73a98dd739067623f275de0740bd576ee288f4444496.json new file mode 100644 index 0000000..06c9dee --- /dev/null +++ b/.sqlx/query-a3d549a32e76c24e265c73a98dd739067623f275de0740bd576ee288f4444496.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE external_identities\n SET provider_username = $2, last_login_at = NOW()\n WHERE id = $1\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Text" + ] + }, + "nullable": [] + }, + "hash": "a3d549a32e76c24e265c73a98dd739067623f275de0740bd576ee288f4444496" +} diff --git a/.sqlx/query-a802d7d860f263eace39ce82bb27b633cec7287c1cc177f0e1d47ec6571564d5.json b/.sqlx/query-a802d7d860f263eace39ce82bb27b633cec7287c1cc177f0e1d47ec6571564d5.json new file mode 100644 index 0000000..f10b2bc --- /dev/null +++ b/.sqlx/query-a802d7d860f263eace39ce82bb27b633cec7287c1cc177f0e1d47ec6571564d5.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT token FROM account_deletion_requests WHERE did = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "token", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "a802d7d860f263eace39ce82bb27b633cec7287c1cc177f0e1d47ec6571564d5" +} diff --git a/.sqlx/query-a844774d8dd3c50c5faf3de5d43f534b80234759c8437434e467ca33ea10fd1f.json b/.sqlx/query-a844774d8dd3c50c5faf3de5d43f534b80234759c8437434e467ca33ea10fd1f.json new file mode 100644 index 0000000..9cfbdba --- /dev/null +++ b/.sqlx/query-a844774d8dd3c50c5faf3de5d43f534b80234759c8437434e467ca33ea10fd1f.json @@ -0,0 +1,40 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT preferred_comms_channel as \"preferred_comms_channel: String\", discord_username FROM users WHERE did = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "preferred_comms_channel: String", + "type_info": { + "Custom": { + "name": "comms_channel", + "kind": { + "Enum": [ + "email", + "discord", + "telegram", + "signal" + ] + } + } + } + }, + { + "ordinal": 1, + "name": "discord_username", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + true + ] + }, + "hash": "a844774d8dd3c50c5faf3de5d43f534b80234759c8437434e467ca33ea10fd1f" +} diff --git a/.sqlx/query-aee3e8e1d8924d41bec7d866e274f8bb2ddef833eb03326103c2d0a17ee56154.json b/.sqlx/query-aee3e8e1d8924d41bec7d866e274f8bb2ddef833eb03326103c2d0a17ee56154.json new file mode 100644 index 0000000..3e281aa --- /dev/null +++ b/.sqlx/query-aee3e8e1d8924d41bec7d866e274f8bb2ddef833eb03326103c2d0a17ee56154.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "\n DELETE FROM sso_auth_state\n WHERE state = $1 AND expires_at > NOW()\n RETURNING state, request_uri\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "state", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "request_uri", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "aee3e8e1d8924d41bec7d866e274f8bb2ddef833eb03326103c2d0a17ee56154" +} diff --git a/.sqlx/query-ba9684872fad5201b8504c2606c29364a2df9631fe98817e7bfacd3f3f51f6cb.json b/.sqlx/query-ba9684872fad5201b8504c2606c29364a2df9631fe98817e7bfacd3f3f51f6cb.json new file mode 100644 index 0000000..5a2db77 --- /dev/null +++ b/.sqlx/query-ba9684872fad5201b8504c2606c29364a2df9631fe98817e7bfacd3f3f51f6cb.json @@ -0,0 +1,31 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO sso_pending_registration (token, request_uri, provider, provider_user_id, expires_at)\n VALUES ($1, $2, $3, $4, NOW() - INTERVAL '1 hour')\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + { + "Custom": { + "name": "sso_provider_type", + "kind": { + "Enum": [ + "github", + "discord", + "google", + "gitlab", + "oidc", + "apple" + ] + } + } + }, + "Text" + ] + }, + "nullable": [] + }, + "hash": "ba9684872fad5201b8504c2606c29364a2df9631fe98817e7bfacd3f3f51f6cb" +} diff --git a/.sqlx/query-bb4460f75d30f48b79d71b97f2c7d54190260deba2d2ade177dbdaa507ab275b.json b/.sqlx/query-bb4460f75d30f48b79d71b97f2c7d54190260deba2d2ade177dbdaa507ab275b.json new file mode 100644 index 0000000..97297bf --- /dev/null +++ b/.sqlx/query-bb4460f75d30f48b79d71b97f2c7d54190260deba2d2ade177dbdaa507ab275b.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM sso_auth_state WHERE expires_at < NOW()", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "bb4460f75d30f48b79d71b97f2c7d54190260deba2d2ade177dbdaa507ab275b" +} diff --git a/.sqlx/query-cd3b8098ad4c1056c1d23acd8a6b29f7abfe18ee6f559bd94ab16274b1cfdfee.json b/.sqlx/query-cd3b8098ad4c1056c1d23acd8a6b29f7abfe18ee6f559bd94ab16274b1cfdfee.json new file mode 100644 index 0000000..6b53208 --- /dev/null +++ b/.sqlx/query-cd3b8098ad4c1056c1d23acd8a6b29f7abfe18ee6f559bd94ab16274b1cfdfee.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT password_reset_code FROM users WHERE email = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "password_reset_code", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + true + ] + }, + "hash": "cd3b8098ad4c1056c1d23acd8a6b29f7abfe18ee6f559bd94ab16274b1cfdfee" +} diff --git a/.sqlx/query-cda68f9b6c60295a196fc853b70ec5fd51a8ffaa2bac5942c115c99d1cbcafa3.json b/.sqlx/query-cda68f9b6c60295a196fc853b70ec5fd51a8ffaa2bac5942c115c99d1cbcafa3.json new file mode 100644 index 0000000..08e8e23 --- /dev/null +++ b/.sqlx/query-cda68f9b6c60295a196fc853b70ec5fd51a8ffaa2bac5942c115c99d1cbcafa3.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT COUNT(*) as \"count!\" FROM plc_operation_tokens t JOIN users u ON t.user_id = u.id WHERE u.did = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count!", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "cda68f9b6c60295a196fc853b70ec5fd51a8ffaa2bac5942c115c99d1cbcafa3" +} diff --git a/.sqlx/query-d0d4fb4b44cda3442b20037b4d5efaa032e1d004c775e2b6077c5050d7d62041.json b/.sqlx/query-d0d4fb4b44cda3442b20037b4d5efaa032e1d004c775e2b6077c5050d7d62041.json new file mode 100644 index 0000000..d122744 --- /dev/null +++ b/.sqlx/query-d0d4fb4b44cda3442b20037b4d5efaa032e1d004c775e2b6077c5050d7d62041.json @@ -0,0 +1,31 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO sso_auth_state (state, request_uri, provider, action, expires_at)\n VALUES ($1, $2, $3, $4, NOW() - INTERVAL '1 hour')\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + { + "Custom": { + "name": "sso_provider_type", + "kind": { + "Enum": [ + "github", + "discord", + "google", + "gitlab", + "oidc", + "apple" + ] + } + } + }, + "Text" + ] + }, + "nullable": [] + }, + "hash": "d0d4fb4b44cda3442b20037b4d5efaa032e1d004c775e2b6077c5050d7d62041" +} diff --git a/.sqlx/query-d529d6dc9858c1da360f0417e94a3b40041b043bae57e95002d4bf5df46a4ab4.json b/.sqlx/query-d529d6dc9858c1da360f0417e94a3b40041b043bae57e95002d4bf5df46a4ab4.json new file mode 100644 index 0000000..be751e8 --- /dev/null +++ b/.sqlx/query-d529d6dc9858c1da360f0417e94a3b40041b043bae57e95002d4bf5df46a4ab4.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE account_deletion_requests SET expires_at = NOW() - INTERVAL '1 hour' WHERE token = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [] + }, + "hash": "d529d6dc9858c1da360f0417e94a3b40041b043bae57e95002d4bf5df46a4ab4" +} diff --git a/.sqlx/query-dd7d80d4d118a5fc95b574e2ca9ffaccf974e52fb6ac368f716409c55f9d3ab0.json b/.sqlx/query-dd7d80d4d118a5fc95b574e2ca9ffaccf974e52fb6ac368f716409c55f9d3ab0.json new file mode 100644 index 0000000..03f2acb --- /dev/null +++ b/.sqlx/query-dd7d80d4d118a5fc95b574e2ca9ffaccf974e52fb6ac368f716409c55f9d3ab0.json @@ -0,0 +1,40 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO external_identities (did, provider, provider_user_id, provider_username, provider_email)\n VALUES ($1, $2, $3, $4, $5)\n RETURNING id\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Text", + { + "Custom": { + "name": "sso_provider_type", + "kind": { + "Enum": [ + "github", + "discord", + "google", + "gitlab", + "oidc", + "apple" + ] + } + } + }, + "Text", + "Text", + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "dd7d80d4d118a5fc95b574e2ca9ffaccf974e52fb6ac368f716409c55f9d3ab0" +} diff --git a/.sqlx/query-e20cbe2a939d790aaea718b084a80d8ede655ba1cc0fd4346d7e91d6de7d6cf3.json b/.sqlx/query-e20cbe2a939d790aaea718b084a80d8ede655ba1cc0fd4346d7e91d6de7d6cf3.json new file mode 100644 index 0000000..0576d91 --- /dev/null +++ b/.sqlx/query-e20cbe2a939d790aaea718b084a80d8ede655ba1cc0fd4346d7e91d6de7d6cf3.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT COUNT(*) FROM comms_queue WHERE user_id = $1 AND comms_type = 'password_reset'", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + null + ] + }, + "hash": "e20cbe2a939d790aaea718b084a80d8ede655ba1cc0fd4346d7e91d6de7d6cf3" +} diff --git a/.sqlx/query-e64cd36284d10ab7f3d9f6959975a1a627809f444b0faff7e611d985f31b90e9.json b/.sqlx/query-e64cd36284d10ab7f3d9f6959975a1a627809f444b0faff7e611d985f31b90e9.json new file mode 100644 index 0000000..edb2f4b --- /dev/null +++ b/.sqlx/query-e64cd36284d10ab7f3d9f6959975a1a627809f444b0faff7e611d985f31b90e9.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT used_at FROM reserved_signing_keys WHERE public_key_did_key = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "used_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + true + ] + }, + "hash": "e64cd36284d10ab7f3d9f6959975a1a627809f444b0faff7e611d985f31b90e9" +} diff --git a/.sqlx/query-eb54d2ce02cab7c2e7f9926bd469b19e5f0513f47173b2738fc01a57082d7abb.json b/.sqlx/query-eb54d2ce02cab7c2e7f9926bd469b19e5f0513f47173b2738fc01a57082d7abb.json new file mode 100644 index 0000000..494c6cb --- /dev/null +++ b/.sqlx/query-eb54d2ce02cab7c2e7f9926bd469b19e5f0513f47173b2738fc01a57082d7abb.json @@ -0,0 +1,30 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO external_identities (did, provider, provider_user_id)\n VALUES ($1, $2, $3)\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + { + "Custom": { + "name": "sso_provider_type", + "kind": { + "Enum": [ + "github", + "discord", + "google", + "gitlab", + "oidc", + "apple" + ] + } + } + }, + "Text" + ] + }, + "nullable": [] + }, + "hash": "eb54d2ce02cab7c2e7f9926bd469b19e5f0513f47173b2738fc01a57082d7abb" +} diff --git a/.sqlx/query-ec22a8cc89e480c403a239eac44288e144d83364129491de6156760616666d3d.json b/.sqlx/query-ec22a8cc89e480c403a239eac44288e144d83364129491de6156760616666d3d.json new file mode 100644 index 0000000..8cc56b0 --- /dev/null +++ b/.sqlx/query-ec22a8cc89e480c403a239eac44288e144d83364129491de6156760616666d3d.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM external_identities WHERE id = $1 AND did = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Text" + ] + }, + "nullable": [] + }, + "hash": "ec22a8cc89e480c403a239eac44288e144d83364129491de6156760616666d3d" +} diff --git a/.sqlx/query-f26c13023b47b908ec96da2e6b8bf8b34ca6a2246c20fc96f76f0e95530762a7.json b/.sqlx/query-f26c13023b47b908ec96da2e6b8bf8b34ca6a2246c20fc96f76f0e95530762a7.json new file mode 100644 index 0000000..e8c8434 --- /dev/null +++ b/.sqlx/query-f26c13023b47b908ec96da2e6b8bf8b34ca6a2246c20fc96f76f0e95530762a7.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT email FROM users WHERE did = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "email", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + true + ] + }, + "hash": "f26c13023b47b908ec96da2e6b8bf8b34ca6a2246c20fc96f76f0e95530762a7" +} diff --git a/.sqlx/query-f29da3bdfbbc547b339b4cdb059fac26435b0feec65cf1c56f851d1c4d6b1814.json b/.sqlx/query-f29da3bdfbbc547b339b4cdb059fac26435b0feec65cf1c56f851d1c4d6b1814.json new file mode 100644 index 0000000..db8bde6 --- /dev/null +++ b/.sqlx/query-f29da3bdfbbc547b339b4cdb059fac26435b0feec65cf1c56f851d1c4d6b1814.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE users SET is_admin = TRUE WHERE did = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [] + }, + "hash": "f29da3bdfbbc547b339b4cdb059fac26435b0feec65cf1c56f851d1c4d6b1814" +} diff --git a/.sqlx/query-f7af28963099aec12cf1d4f8a9a03699bb3a90f39bc9c4c0f738a37827e8f382.json b/.sqlx/query-f7af28963099aec12cf1d4f8a9a03699bb3a90f39bc9c4c0f738a37827e8f382.json new file mode 100644 index 0000000..32320a7 --- /dev/null +++ b/.sqlx/query-f7af28963099aec12cf1d4f8a9a03699bb3a90f39bc9c4c0f738a37827e8f382.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT password_reset_code, password_reset_code_expires_at FROM users WHERE email = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "password_reset_code", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "password_reset_code_expires_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + true, + true + ] + }, + "hash": "f7af28963099aec12cf1d4f8a9a03699bb3a90f39bc9c4c0f738a37827e8f382" +} diff --git a/crates/tranquil-pds/src/api/error.rs b/crates/tranquil-pds/src/api/error.rs index 1842390..96a68e9 100644 --- a/crates/tranquil-pds/src/api/error.rs +++ b/crates/tranquil-pds/src/api/error.rs @@ -56,7 +56,6 @@ pub enum ApiError { TotpAlreadyEnabled, TotpNotEnabled, InvalidCode(Option), - InvalidChannel, IdentifierMismatch, NoPasskeys, NoChallengeInProgress, @@ -182,7 +181,6 @@ impl ApiError { | Self::InvalidCollection | Self::InvalidRecord(_) | Self::TotpNotEnabled - | Self::InvalidChannel | Self::IdentifierMismatch | Self::NoPasskeys | Self::NoChallengeInProgress @@ -272,7 +270,6 @@ impl ApiError { Self::TotpAlreadyEnabled => Cow::Borrowed("TotpAlreadyEnabled"), Self::TotpNotEnabled => Cow::Borrowed("TotpNotEnabled"), Self::InvalidCode(_) => Cow::Borrowed("InvalidCode"), - Self::InvalidChannel => Cow::Borrowed("InvalidChannel"), Self::IdentifierMismatch => Cow::Borrowed("IdentifierMismatch"), Self::NoPasskeys => Cow::Borrowed("NoPasskeys"), Self::NoChallengeInProgress => Cow::Borrowed("NoChallengeInProgress"), @@ -390,7 +387,6 @@ impl ApiError { Self::Forbidden => "Forbidden".into(), Self::InvitesDisabled => "Invite codes are disabled on this server".into(), Self::InvalidCollection => "Invalid collection".into(), - Self::InvalidChannel => "Invalid notification channel".into(), Self::TotpAlreadyEnabled => "TOTP is already enabled".into(), Self::TotpNotEnabled => "TOTP is not enabled".into(), Self::DuplicateAppPassword => "An app password with this name already exists".into(), diff --git a/crates/tranquil-pds/src/api/mod.rs b/crates/tranquil-pds/src/api/mod.rs index 10a8b52..2bb4534 100644 --- a/crates/tranquil-pds/src/api/mod.rs +++ b/crates/tranquil-pds/src/api/mod.rs @@ -8,7 +8,7 @@ pub use error::ApiError; pub use proxy_client::{AtUriParts, proxy_client, validate_at_uri, validate_limit}; pub use responses::{ AccountsOutput, AuditLogOutput, ControllersOutput, DidResponse, EmailUpdateStatusOutput, - EmptyResponse, EnabledResponse, HasPasswordResponse, InUseOutput, OptionsResponse, + EmptyResponse, HasPasswordResponse, InUseOutput, OptionsResponse, PasswordResetOutput, PreferredLocaleOutput, PresetsOutput, StatusResponse, SuccessResponse, TokenRequiredResponse, VerifiedResponse, }; diff --git a/crates/tranquil-pds/src/api/proxy.rs b/crates/tranquil-pds/src/api/proxy.rs index 1744fbc..bb870cb 100644 --- a/crates/tranquil-pds/src/api/proxy.rs +++ b/crates/tranquil-pds/src/api/proxy.rs @@ -229,8 +229,8 @@ async fn proxy_handler( let http_uri = crate::util::build_full_url(&format!("/xrpc{}", uri)); match crate::auth::validate_token_with_dpop( - state.user_repo.as_ref(), - state.oauth_repo.as_ref(), + state.repos.user.as_ref(), + state.repos.oauth.as_ref(), &token, extracted.scheme, dpop_proof, @@ -252,7 +252,7 @@ async fn proxy_handler( let key_bytes = match auth_user.key_bytes { Some(kb) => kb, - None => match state.user_repo.get_user_info_by_did(&auth_user.did).await { + None => match state.repos.user.get_user_info_by_did(&auth_user.did).await { Ok(Some(info)) => match info.key_bytes { Some(key_bytes_enc) => { match crate::config::decrypt_key( diff --git a/crates/tranquil-pds/src/api/responses.rs b/crates/tranquil-pds/src/api/responses.rs index b186cb5..937761b 100644 --- a/crates/tranquil-pds/src/api/responses.rs +++ b/crates/tranquil-pds/src/api/responses.rs @@ -70,17 +70,6 @@ impl VerifiedResponse { } } -#[derive(Debug, Serialize)] -pub struct EnabledResponse { - pub enabled: bool, -} - -impl EnabledResponse { - pub fn response(enabled: bool) -> impl IntoResponse { - Json(Self { enabled }) - } -} - #[derive(Debug, Serialize)] pub struct StatusResponse { pub status: String, @@ -94,18 +83,6 @@ impl StatusResponse { } } -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct DidDocumentResponse { - pub did_document: serde_json::Value, -} - -impl DidDocumentResponse { - pub fn response(did_document: serde_json::Value) -> impl IntoResponse { - Json(Self { did_document }) - } -} - #[derive(Debug, Serialize)] pub struct OptionsResponse { pub options: T, diff --git a/crates/tranquil-pds/src/api/validation.rs b/crates/tranquil-pds/src/api/validation.rs index f8f5969..f0fe0b8 100644 --- a/crates/tranquil-pds/src/api/validation.rs +++ b/crates/tranquil-pds/src/api/validation.rs @@ -1,6 +1,4 @@ -use serde::{Deserialize, Serialize}; use std::fmt; -use std::ops::Deref; pub const MAX_EMAIL_LENGTH: usize = 254; pub const MAX_LOCAL_PART_LENGTH: usize = 64; @@ -12,56 +10,6 @@ pub const MIN_HANDLE_LENGTH: usize = 3; pub const MAX_HANDLE_LENGTH: usize = 253; pub const MAX_SERVICE_HANDLE_LOCAL_PART: usize = 18; -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[serde(try_from = "String", into = "String")] -pub struct ValidatedLocalHandle(String); - -impl ValidatedLocalHandle { - pub fn new(handle: impl AsRef) -> Result { - let validated = validate_short_handle(handle.as_ref())?; - Ok(Self(validated)) - } - - pub fn new_allow_reserved(handle: impl AsRef) -> Result { - let validated = validate_service_handle(handle.as_ref(), ReservedHandlePolicy::Allow)?; - Ok(Self(validated)) - } - - pub fn as_str(&self) -> &str { - &self.0 - } - - pub fn into_inner(self) -> String { - self.0 - } -} - -impl Deref for ValidatedLocalHandle { - type Target = str; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl fmt::Display for ValidatedLocalHandle { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl TryFrom for ValidatedLocalHandle { - type Error = HandleValidationError; - fn try_from(value: String) -> Result { - Self::new(value) - } -} - -impl From for String { - fn from(handle: ValidatedLocalHandle) -> Self { - handle.0 - } -} - #[derive(Debug, Clone, PartialEq, Eq)] pub enum EmailValidationError { Empty, @@ -99,66 +47,6 @@ impl fmt::Display for EmailValidationError { impl std::error::Error for EmailValidationError {} -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[serde(try_from = "String", into = "String")] -pub struct ValidatedEmail(String); - -impl ValidatedEmail { - pub fn new(email: impl AsRef) -> Result { - let email = email.as_ref().trim(); - validate_email_detailed(email)?; - Ok(Self(email.to_string())) - } - - pub fn as_str(&self) -> &str { - &self.0 - } - - pub fn into_inner(self) -> String { - self.0 - } - - pub fn local_part(&self) -> &str { - self.0 - .rsplit_once('@') - .map(|(local, _)| local) - .unwrap_or("") - } - - pub fn domain(&self) -> &str { - self.0 - .rsplit_once('@') - .map(|(_, domain)| domain) - .unwrap_or("") - } -} - -impl Deref for ValidatedEmail { - type Target = str; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl fmt::Display for ValidatedEmail { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl TryFrom for ValidatedEmail { - type Error = EmailValidationError; - fn try_from(value: String) -> Result { - Self::new(value) - } -} - -impl From for String { - fn from(email: ValidatedEmail) -> Self { - email.0 - } -} - fn validate_email_detailed(email: &str) -> Result<(), EmailValidationError> { if email.is_empty() { return Err(EmailValidationError::Empty); @@ -386,44 +274,7 @@ pub fn validate_service_handle( } pub fn is_valid_email(email: &str) -> bool { - let email = email.trim(); - if email.is_empty() || email.len() > MAX_EMAIL_LENGTH { - return false; - } - let parts: Vec<&str> = email.rsplitn(2, '@').collect(); - if parts.len() != 2 { - return false; - } - let domain = parts[0]; - let local = parts[1]; - if local.is_empty() || local.len() > MAX_LOCAL_PART_LENGTH { - return false; - } - if local.starts_with('.') || local.ends_with('.') { - return false; - } - if local.contains("..") { - return false; - } - if !local - .chars() - .all(|c| c.is_ascii_alphanumeric() || EMAIL_LOCAL_SPECIAL_CHARS.contains(c)) - { - return false; - } - if domain.is_empty() || domain.len() > MAX_DOMAIN_LENGTH { - return false; - } - if !domain.contains('.') { - return false; - } - domain.split('.').all(|label| { - !label.is_empty() - && label.len() <= MAX_DOMAIN_LABEL_LENGTH - && !label.starts_with('-') - && !label.ends_with('-') - && label.chars().all(|c| c.is_ascii_alphanumeric() || c == '-') - }) + validate_email_detailed(email.trim()).is_ok() } pub fn is_valid_telegram_username(username: &str) -> bool { diff --git a/crates/tranquil-pds/src/auth/account_verified.rs b/crates/tranquil-pds/src/auth/account_verified.rs index 4782753..59996cc 100644 --- a/crates/tranquil-pds/src/auth/account_verified.rs +++ b/crates/tranquil-pds/src/auth/account_verified.rs @@ -22,7 +22,7 @@ pub async fn require_verified_or_delegated<'a>( user: &'a AuthenticatedUser, ) -> Result, ApiError> { let is_verified = state - .user_repo + .repos.user .has_verified_comms_channel(&user.did) .await .unwrap_or(false); @@ -32,7 +32,7 @@ pub async fn require_verified_or_delegated<'a>( } let is_delegated = state - .delegation_repo + .repos.delegation .is_delegated_account(&user.did) .await .unwrap_or(false); @@ -45,7 +45,7 @@ pub async fn require_verified_or_delegated<'a>( } pub async fn require_not_migrated(state: &AppState, did: &Did) -> Result<(), ApiError> { - match state.user_repo.is_account_migrated(did).await { + match state.repos.user.is_account_migrated(did).await { Ok(true) => Err(ApiError::AccountMigrated), Ok(false) => Ok(()), Err(e) => { diff --git a/crates/tranquil-pds/src/auth/extractor.rs b/crates/tranquil-pds/src/auth/extractor.rs index c6f9dee..fea80cf 100644 --- a/crates/tranquil-pds/src/auth/extractor.rs +++ b/crates/tranquil-pds/src/auth/extractor.rs @@ -232,7 +232,7 @@ async fn verify_oauth_token_and_build_user( uri: &str, ) -> Result { match crate::oauth::verify::verify_oauth_access_token( - state.oauth_repo.as_ref(), + state.repos.oauth.as_ref(), token, dpop_proof, method, @@ -242,7 +242,7 @@ async fn verify_oauth_token_and_build_user( { Ok(result) => { let user_info = state - .user_repo + .repos.user .get_user_info_by_did(&result.did) .await .ok() @@ -254,9 +254,10 @@ async fn verify_oauth_token_and_build_user( ); Ok(AuthenticatedUser { did: result.did, - key_bytes: user_info.key_bytes.and_then(|kb| { - crate::config::decrypt_key(&kb, user_info.encryption_version).ok() - }), + key_bytes: super::try_decrypt_user_key( + user_info.key_bytes.as_deref(), + user_info.encryption_version, + ), is_admin: user_info.is_admin, status, scope: result.scope, @@ -320,7 +321,7 @@ async fn extract_auth_internal( .unwrap_or_else(|| parts.uri.path().to_string()); let uri = build_full_url(&original_uri); - match validate_bearer_token_for_service_auth(state.user_repo.as_ref(), &extracted.token).await { + match validate_bearer_token_for_service_auth(state.repos.user.as_ref(), &extracted.token).await { Ok(user) if !user.auth_source.is_oauth() => { return Ok(ExtractedAuth::User(user)); } diff --git a/crates/tranquil-pds/src/auth/mfa_verified.rs b/crates/tranquil-pds/src/auth/mfa_verified.rs index 47a13d4..da02933 100644 --- a/crates/tranquil-pds/src/auth/mfa_verified.rs +++ b/crates/tranquil-pds/src/auth/mfa_verified.rs @@ -76,12 +76,12 @@ pub async fn require_legacy_session_mfa<'a>( ) -> Result, ApiError> { use crate::auth::reauth::check_legacy_session_mfa; - if check_legacy_session_mfa(&*state.session_repo, &user.did).await { + if check_legacy_session_mfa(&*state.repos.session, &user.did).await { Ok(MfaVerified::from_session_reauth(user)) } else { let methods = crate::auth::reauth::get_available_reauth_methods( - &*state.user_repo, - &*state.session_repo, + &*state.repos.user, + &*state.repos.session, &user.did, ) .await; @@ -99,7 +99,7 @@ pub async fn require_reauth_window<'a>( use chrono::Utc; let status = state - .session_repo + .repos.session .get_session_mfa_status(&user.did) .await .ok() @@ -114,8 +114,8 @@ pub async fn require_reauth_window<'a>( } } let methods = crate::auth::reauth::get_available_reauth_methods( - &*state.user_repo, - &*state.session_repo, + &*state.repos.user, + &*state.repos.session, &user.did, ) .await; @@ -125,8 +125,8 @@ pub async fn require_reauth_window<'a>( } None => { let methods = crate::auth::reauth::get_available_reauth_methods( - &*state.user_repo, - &*state.session_repo, + &*state.repos.user, + &*state.repos.session, &user.did, ) .await; @@ -144,19 +144,19 @@ pub async fn require_reauth_window_if_available<'a>( use crate::auth::reauth::check_reauth_required_cached; let has_password = state - .user_repo + .repos.user .has_password_by_did(&user.did) .await .ok() .flatten() .unwrap_or(false); let has_passkeys = state - .user_repo + .repos.user .has_passkeys(&user.did) .await .unwrap_or(false); let has_totp = state - .user_repo + .repos.user .has_totp_enabled(&user.did) .await .unwrap_or(false); @@ -167,10 +167,10 @@ pub async fn require_reauth_window_if_available<'a>( return Ok(None); } - if check_reauth_required_cached(&*state.session_repo, &state.cache, &user.did).await { + if check_reauth_required_cached(&*state.repos.session, &state.cache, &user.did).await { let methods = crate::auth::reauth::get_available_reauth_methods( - &*state.user_repo, - &*state.session_repo, + &*state.repos.user, + &*state.repos.session, &user.did, ) .await; @@ -188,7 +188,7 @@ pub async fn verify_password_mfa<'a>( password: &str, ) -> Result, crate::api::error::ApiError> { let hash = state - .user_repo + .repos.user .get_password_hash_by_did(&user.did) .await .ok() @@ -220,7 +220,7 @@ pub async fn verify_totp_mfa<'a>( if is_backup_code_format(code) { let backup_codes = state - .user_repo + .repos.user .get_unused_backup_codes(&user.did) .await .ok() @@ -233,7 +233,7 @@ pub async fn verify_totp_mfa<'a>( return match matched { Some(row) => { - let _ = state.user_repo.mark_backup_code_used(row.id).await; + let _ = state.repos.user.mark_backup_code_used(row.id).await; Ok(MfaVerified::from_recovery_code(user)) } None => Err(crate::api::error::ApiError::InvalidCode(Some( @@ -242,7 +242,7 @@ pub async fn verify_totp_mfa<'a>( }; } - let verified_record = match state.user_repo.get_totp_record_state(&user.did).await { + let verified_record = match state.repos.user.get_totp_record_state(&user.did).await { Ok(Some(TotpRecordState::Verified(record))) => record, _ => { return Err(crate::api::error::ApiError::TotpNotEnabled); @@ -256,7 +256,7 @@ pub async fn verify_totp_mfa<'a>( .map_err(|_| crate::api::error::ApiError::InternalError(None))?; if verify_totp_code(&secret, code) { - let _ = state.user_repo.update_totp_last_used(&user.did).await; + let _ = state.repos.user.update_totp_last_used(&user.did).await; Ok(MfaVerified::from_totp(user)) } else { Err(crate::api::error::ApiError::InvalidCode(Some( diff --git a/crates/tranquil-pds/src/auth/mod.rs b/crates/tranquil-pds/src/auth/mod.rs index aff685e..4a52bc6 100644 --- a/crates/tranquil-pds/src/auth/mod.rs +++ b/crates/tranquil-pds/src/auth/mod.rs @@ -61,6 +61,16 @@ pub fn lxm_permits(lxm: &str, expected: &str) -> bool { lxm == "*" || lxm == expected } +pub fn try_decrypt_user_key( + key_bytes: Option<&[u8]>, + encryption_version: Option, +) -> Option> { + match (key_bytes, encryption_version) { + (Some(kb), Some(ev)) => crate::config::decrypt_key(kb, Some(ev)).ok(), + _ => None, + } +} + pub fn encrypt_totp_secret(secret: &[u8]) -> Result, crate::config::CryptoError> { crate::config::encrypt_key(secret) } @@ -486,13 +496,10 @@ async fn validate_bearer_token_with_options_internal( let now = chrono::Utc::now(); if oauth_token.expires_at > now { - let key_bytes = if let (Some(kb), Some(ev)) = - (&oauth_token.key_bytes, oauth_token.encryption_version) - { - crate::config::decrypt_key(kb, Some(ev)).ok() - } else { - None - }; + let key_bytes = try_decrypt_user_key( + oauth_token.key_bytes.as_deref(), + oauth_token.encryption_version, + ); let did: Did = oauth_token .did .parse() @@ -592,13 +599,10 @@ pub async fn validate_token_with_dpop( if !allow_takendown && status.is_takendown() { return Err(TokenValidationError::AccountTakedown); } - let key_bytes = if let (Some(kb), Some(ev)) = - (&user_info.key_bytes, user_info.encryption_version) - { - crate::config::decrypt_key(kb, Some(ev)).ok() - } else { - None - }; + let key_bytes = try_decrypt_user_key( + user_info.key_bytes.as_deref(), + user_info.encryption_version, + ); Ok(AuthenticatedUser { did: result_did, key_bytes, diff --git a/crates/tranquil-pds/src/delegation/mod.rs b/crates/tranquil-pds/src/delegation/mod.rs index f8d5eef..80e15ef 100644 --- a/crates/tranquil-pds/src/delegation/mod.rs +++ b/crates/tranquil-pds/src/delegation/mod.rs @@ -26,7 +26,7 @@ pub struct ResolvedIdentity { pub async fn resolve_identity(state: &AppState, did: &Did) -> Option { let is_local = state - .user_repo + .repos.user .get_by_did(did) .await .ok() diff --git a/crates/tranquil-pds/src/delegation/roles.rs b/crates/tranquil-pds/src/delegation/roles.rs index ef3b9bb..27679e6 100644 --- a/crates/tranquil-pds/src/delegation/roles.rs +++ b/crates/tranquil-pds/src/delegation/roles.rs @@ -29,9 +29,9 @@ async fn check_delegation_flag( error_msg: &str, ) -> Result { let result = if check_is_delegated { - state.delegation_repo.is_delegated_account(did).await + state.repos.delegation.is_delegated_account(did).await } else { - state.delegation_repo.controls_any_accounts(did).await + state.repos.delegation.controls_any_accounts(did).await }; match result { Ok(true) => Err(ApiError::InvalidDelegation(error_msg.into())), diff --git a/crates/tranquil-pds/src/oauth/verify.rs b/crates/tranquil-pds/src/oauth/verify.rs index 6533785..6e803da 100644 --- a/crates/tranquil-pds/src/oauth/verify.rs +++ b/crates/tranquil-pds/src/oauth/verify.rs @@ -302,7 +302,7 @@ impl FromRequestParts for OAuthUser { .headers .get(crate::util::HEADER_DPOP) .and_then(|v| v.to_str().ok()); - if let Ok(result) = try_legacy_auth(state.user_repo.as_ref(), token).await { + if let Ok(result) = try_legacy_auth(state.repos.user.as_ref(), token).await { return Ok(OAuthUser { did: result.did, client_id: None, @@ -314,7 +314,7 @@ impl FromRequestParts for OAuthUser { let http_method = parts.method.as_str(); let http_uri = crate::util::build_full_url(&parts.uri.to_string()); match verify_oauth_access_token( - state.oauth_repo.as_ref(), + state.repos.oauth.as_ref(), token, dpop_proof, http_method, diff --git a/crates/tranquil-pds/src/rate_limit/mod.rs b/crates/tranquil-pds/src/rate_limit/mod.rs index a5d6f1b..f14d191 100644 --- a/crates/tranquil-pds/src/rate_limit/mod.rs +++ b/crates/tranquil-pds/src/rate_limit/mod.rs @@ -2,16 +2,21 @@ mod extractor; pub use extractor::*; +use crate::state::RateLimitKind; use governor::{ - Quota, RateLimiter, + RateLimiter, clock::DefaultClock, state::{InMemoryState, NotKeyed, keyed::DefaultKeyedStateStore}, }; -use std::{num::NonZeroU32, sync::Arc}; +use std::sync::Arc; pub type KeyedRateLimiter = RateLimiter, DefaultClock>; pub type GlobalRateLimiter = RateLimiter; +fn keyed_limiter(kind: RateLimitKind) -> Arc { + Arc::new(RateLimiter::keyed(kind.params().to_governor_quota())) +} + #[derive(Clone)] pub struct RateLimiters { pub login: Arc, @@ -45,123 +50,67 @@ impl Default for RateLimiters { impl RateLimiters { pub fn new() -> Self { Self { - login: Arc::new(RateLimiter::keyed(Quota::per_minute( - const { NonZeroU32::new(10).unwrap() }, - ))), - oauth_token: Arc::new(RateLimiter::keyed(Quota::per_minute( - const { NonZeroU32::new(300).unwrap() }, - ))), - oauth_authorize: Arc::new(RateLimiter::keyed(Quota::per_minute( - const { NonZeroU32::new(10).unwrap() }, - ))), - password_reset: Arc::new(RateLimiter::keyed(Quota::per_hour( - const { NonZeroU32::new(5).unwrap() }, - ))), - account_creation: Arc::new(RateLimiter::keyed(Quota::per_hour( - const { NonZeroU32::new(10).unwrap() }, - ))), - refresh_session: Arc::new(RateLimiter::keyed(Quota::per_minute( - const { NonZeroU32::new(60).unwrap() }, - ))), - reset_password: Arc::new(RateLimiter::keyed(Quota::per_minute( - const { NonZeroU32::new(10).unwrap() }, - ))), - oauth_par: Arc::new(RateLimiter::keyed(Quota::per_minute( - const { NonZeroU32::new(30).unwrap() }, - ))), - oauth_introspect: Arc::new(RateLimiter::keyed(Quota::per_minute( - const { NonZeroU32::new(30).unwrap() }, - ))), - app_password: Arc::new(RateLimiter::keyed(Quota::per_minute( - const { NonZeroU32::new(10).unwrap() }, - ))), - email_update: Arc::new(RateLimiter::keyed(Quota::per_hour( - const { NonZeroU32::new(5).unwrap() }, - ))), - totp_verify: Arc::new(RateLimiter::keyed( - Quota::with_period(std::time::Duration::from_secs(60)) - .unwrap() - .allow_burst(const { NonZeroU32::new(5).unwrap() }), - )), - handle_update: Arc::new(RateLimiter::keyed( - Quota::with_period(std::time::Duration::from_secs(30)) - .unwrap() - .allow_burst(const { NonZeroU32::new(10).unwrap() }), - )), - handle_update_daily: Arc::new(RateLimiter::keyed( - Quota::with_period(std::time::Duration::from_secs(1728)) - .unwrap() - .allow_burst(const { NonZeroU32::new(50).unwrap() }), - )), - verification_check: Arc::new(RateLimiter::keyed(Quota::per_minute( - const { NonZeroU32::new(60).unwrap() }, - ))), - sso_initiate: Arc::new(RateLimiter::keyed(Quota::per_minute( - const { NonZeroU32::new(10).unwrap() }, - ))), - sso_callback: Arc::new(RateLimiter::keyed(Quota::per_minute( - const { NonZeroU32::new(30).unwrap() }, - ))), - sso_unlink: Arc::new(RateLimiter::keyed(Quota::per_minute( - const { NonZeroU32::new(10).unwrap() }, - ))), - oauth_register_complete: Arc::new(RateLimiter::keyed( - Quota::with_period(std::time::Duration::from_secs(60)) - .unwrap() - .allow_burst(const { NonZeroU32::new(5).unwrap() }), - )), - handle_verification: Arc::new(RateLimiter::keyed(Quota::per_minute( - const { NonZeroU32::new(10).unwrap() }, - ))), + login: keyed_limiter(RateLimitKind::Login), + oauth_token: keyed_limiter(RateLimitKind::OAuthToken), + oauth_authorize: keyed_limiter(RateLimitKind::OAuthAuthorize), + password_reset: keyed_limiter(RateLimitKind::PasswordReset), + account_creation: keyed_limiter(RateLimitKind::AccountCreation), + refresh_session: keyed_limiter(RateLimitKind::RefreshSession), + reset_password: keyed_limiter(RateLimitKind::ResetPassword), + oauth_par: keyed_limiter(RateLimitKind::OAuthPar), + oauth_introspect: keyed_limiter(RateLimitKind::OAuthIntrospect), + app_password: keyed_limiter(RateLimitKind::AppPassword), + email_update: keyed_limiter(RateLimitKind::EmailUpdate), + totp_verify: keyed_limiter(RateLimitKind::TotpVerify), + handle_update: keyed_limiter(RateLimitKind::HandleUpdate), + handle_update_daily: keyed_limiter(RateLimitKind::HandleUpdateDaily), + verification_check: keyed_limiter(RateLimitKind::VerificationCheck), + sso_initiate: keyed_limiter(RateLimitKind::SsoInitiate), + sso_callback: keyed_limiter(RateLimitKind::SsoCallback), + sso_unlink: keyed_limiter(RateLimitKind::SsoUnlink), + oauth_register_complete: keyed_limiter(RateLimitKind::OAuthRegisterComplete), + handle_verification: keyed_limiter(RateLimitKind::HandleVerification), } } + pub fn override_limit(kind: RateLimitKind, limit: u32) -> Arc { + let mut params = kind.params(); + params.limit = limit; + Arc::new(RateLimiter::keyed(params.to_governor_quota())) + } + pub fn with_login_limit(mut self, per_minute: u32) -> Self { - self.login = Arc::new(RateLimiter::keyed(Quota::per_minute( - NonZeroU32::new(per_minute).unwrap_or(const { NonZeroU32::new(10).unwrap() }), - ))); + self.login = Self::override_limit(RateLimitKind::Login, per_minute); self } pub fn with_oauth_token_limit(mut self, per_minute: u32) -> Self { - self.oauth_token = Arc::new(RateLimiter::keyed(Quota::per_minute( - NonZeroU32::new(per_minute).unwrap_or(const { NonZeroU32::new(30).unwrap() }), - ))); + self.oauth_token = Self::override_limit(RateLimitKind::OAuthToken, per_minute); self } pub fn with_oauth_authorize_limit(mut self, per_minute: u32) -> Self { - self.oauth_authorize = Arc::new(RateLimiter::keyed(Quota::per_minute( - NonZeroU32::new(per_minute).unwrap_or(const { NonZeroU32::new(10).unwrap() }), - ))); + self.oauth_authorize = Self::override_limit(RateLimitKind::OAuthAuthorize, per_minute); self } pub fn with_password_reset_limit(mut self, per_hour: u32) -> Self { - self.password_reset = Arc::new(RateLimiter::keyed(Quota::per_hour( - NonZeroU32::new(per_hour).unwrap_or(const { NonZeroU32::new(5).unwrap() }), - ))); + self.password_reset = Self::override_limit(RateLimitKind::PasswordReset, per_hour); self } pub fn with_account_creation_limit(mut self, per_hour: u32) -> Self { - self.account_creation = Arc::new(RateLimiter::keyed(Quota::per_hour( - NonZeroU32::new(per_hour).unwrap_or(const { NonZeroU32::new(10).unwrap() }), - ))); + self.account_creation = Self::override_limit(RateLimitKind::AccountCreation, per_hour); self } pub fn with_email_update_limit(mut self, per_hour: u32) -> Self { - self.email_update = Arc::new(RateLimiter::keyed(Quota::per_hour( - NonZeroU32::new(per_hour).unwrap_or(const { NonZeroU32::new(5).unwrap() }), - ))); + self.email_update = Self::override_limit(RateLimitKind::EmailUpdate, per_hour); self } pub fn with_sso_initiate_limit(mut self, per_minute: u32) -> Self { - self.sso_initiate = Arc::new(RateLimiter::keyed(Quota::per_minute( - NonZeroU32::new(per_minute).unwrap_or(const { NonZeroU32::new(10).unwrap() }), - ))); + self.sso_initiate = Self::override_limit(RateLimitKind::SsoInitiate, per_minute); self } } @@ -178,6 +127,8 @@ mod tests { #[test] fn test_rate_limiter_exhaustion() { + use governor::Quota; + use std::num::NonZeroU32; let limiter = RateLimiter::keyed(Quota::per_minute(const { NonZeroU32::new(2).unwrap() })); let key = "test_ip".to_string(); @@ -188,6 +139,8 @@ mod tests { #[test] fn test_different_keys_have_separate_limits() { + use governor::Quota; + use std::num::NonZeroU32; let limiter = RateLimiter::keyed(Quota::per_minute(const { NonZeroU32::new(1).unwrap() })); assert!(limiter.check_key(&"ip1".to_string()).is_ok()); diff --git a/crates/tranquil-pds/src/repo/mod.rs b/crates/tranquil-pds/src/repo/mod.rs index 32e1fe9..7a97b4f 100644 --- a/crates/tranquil-pds/src/repo/mod.rs +++ b/crates/tranquil-pds/src/repo/mod.rs @@ -1,5 +1 @@ pub use tranquil_repo::{PostgresBlockStore, TrackingBlockStore}; - -pub mod tracking { - pub use tranquil_repo::TrackingBlockStore; -} diff --git a/crates/tranquil-pds/src/repo_ops.rs b/crates/tranquil-pds/src/repo_ops.rs index 83b5cfe..86e6f97 100644 --- a/crates/tranquil-pds/src/repo_ops.rs +++ b/crates/tranquil-pds/src/repo_ops.rs @@ -1,6 +1,6 @@ use crate::api::error::ApiError; use crate::cid_types::CommitCid; -use crate::repo::tracking::TrackingBlockStore; +use crate::repo::TrackingBlockStore; use crate::state::AppState; use crate::types::{Did, Handle, Nsid, Rkey}; use bytes::Bytes; @@ -85,7 +85,7 @@ impl From for ApiError { pub async fn get_current_root_cid(state: &AppState, user_id: Uuid) -> Result { let root_cid_str = state - .repo_repo + .repos.repo .get_repo_root_cid_by_user_id(user_id) .await .map_err(|e| { @@ -98,29 +98,10 @@ pub async fn get_current_root_cid(state: &AppState, user_id: Uuid) -> Result Vec { - let mut blobs = Vec::new(); - extract_blob_cids_recursive(record, &mut blobs); - blobs -} - -fn extract_blob_cids_recursive(value: &Value, blobs: &mut Vec) { - match value { - Value::Object(map) => { - if map.get("$type").and_then(|v| v.as_str()) == Some("blob") - && let Some(ref_obj) = map.get("ref") - && let Some(link) = ref_obj.get("$link").and_then(|v| v.as_str()) - { - blobs.push(link.to_string()); - } - map.values() - .for_each(|v| extract_blob_cids_recursive(v, blobs)); - } - Value::Array(arr) => { - arr.iter() - .for_each(|v| extract_blob_cids_recursive(v, blobs)); - } - _ => {} - } + crate::sync::import::find_blob_refs(record, 0) + .into_iter() + .map(|b| b.cid) + .collect() } use crate::types::AtUri; @@ -187,7 +168,7 @@ pub async fn begin_repo_write( let write_lock = state.repo_write_locks.lock(user_id).await; let root_cid_str = state - .repo_repo + .repos.repo .get_repo_root_cid_by_user_id(user_id) .await .map_err(|e| { @@ -273,7 +254,7 @@ pub async fn finalize_repo_write( if let Some(controller_did) = params.controller_did && let Some(detail) = params.delegation_detail && let Err(e) = state - .delegation_repo + .repos.delegation .log_delegation_action( params.did, controller_did, @@ -370,7 +351,7 @@ pub async fn commit_and_log( obsolete_cids, } = params; let key_row = state - .user_repo + .repos.user .get_user_key_by_id(user_id) .await .map_err(|e| CommitError::DatabaseError(format!("Failed to fetch signing key: {}", e)))? @@ -504,7 +485,7 @@ pub async fn commit_and_log( }; let _result = state - .repo_repo + .repos.repo .apply_commit(input) .await .map_err(|e| match e { @@ -526,7 +507,7 @@ pub async fn create_record_internal( record: &serde_json::Value, ) -> Result<(String, Cid), CommitError> { let user_id: Uuid = state - .user_repo + .repos.user .get_id_by_did(did) .await .map_err(|e| CommitError::DatabaseError(e.to_string()))? @@ -535,7 +516,7 @@ pub async fn create_record_internal( let _write_lock = state.repo_write_locks.lock(user_id).await; let root_cid_link = state - .repo_repo + .repos.repo .get_repo_root_cid_by_user_id(user_id) .await .map_err(|e| CommitError::DatabaseError(e.to_string()))? @@ -629,7 +610,7 @@ pub async fn sequence_identity_event( handle: Option<&Handle>, ) -> Result { state - .repo_repo + .repos.repo .insert_identity_event(did, handle) .await .map_err(|e| CommitError::DatabaseError(format!("identity event: {}", e))) @@ -640,7 +621,7 @@ pub async fn sequence_account_event( status: tranquil_db_traits::AccountStatus, ) -> Result { state - .repo_repo + .repos.repo .insert_account_event(did, status) .await .map_err(|e| CommitError::DatabaseError(format!("account event: {}", e))) @@ -655,7 +636,7 @@ pub async fn sequence_sync_event( .parse() .map_err(|_| CommitError::InvalidCid(commit_cid.to_string()))?; state - .repo_repo + .repos.repo .insert_sync_event(did, &cid_link, rev) .await .map_err(|e| CommitError::DatabaseError(format!("sync event: {}", e))) @@ -671,7 +652,7 @@ pub async fn sequence_genesis_commit( let commit_cid_link = crate::types::CidLink::from(commit_cid); let mst_root_cid_link = crate::types::CidLink::from(mst_root_cid); state - .repo_repo + .repos.repo .insert_genesis_commit_event(did, &commit_cid_link, &mst_root_cid_link, rev) .await .map_err(|e| CommitError::DatabaseError(format!("genesis commit event: {}", e))) diff --git a/crates/tranquil-pds/src/state.rs b/crates/tranquil-pds/src/state.rs index efb085f..3a8050c 100644 --- a/crates/tranquil-pds/src/state.rs +++ b/crates/tranquil-pds/src/state.rs @@ -16,11 +16,7 @@ use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use tokio::sync::broadcast; use tokio_util::sync::CancellationToken; -use tranquil_db::{ - BacklinkRepository, BlobRepository, DelegationRepository, InfraRepository, OAuthRepository, - PostgresRepositories, RepoEventNotifier, RepoRepository, SessionRepository, SsoRepository, - UserRepository, -}; +use tranquil_db::PostgresRepositories; use tranquil_db_traits::SequencedEvent; static RATE_LIMITING_DISABLED: AtomicBool = AtomicBool::new(false); @@ -36,15 +32,6 @@ pub fn init_rate_limit_override() { #[derive(Clone)] pub struct AppState { pub repos: Arc, - pub user_repo: Arc, - pub oauth_repo: Arc, - pub session_repo: Arc, - pub delegation_repo: Arc, - pub repo_repo: Arc, - pub blob_repo: Arc, - pub infra_repo: Arc, - pub backlink_repo: Arc, - pub event_notifier: Arc, pub block_store: PostgresBlockStore, pub blob_store: Arc, pub firehose_tx: broadcast::Sender, @@ -54,7 +41,6 @@ pub struct AppState { pub cache: Arc, pub distributed_rate_limiter: Arc, pub did_resolver: Arc, - pub sso_repo: Arc, pub sso_manager: SsoManager, pub webauthn_config: Arc, pub cross_pds_oauth: Arc, @@ -69,6 +55,17 @@ pub struct RateLimitParams { pub window_ms: u64, } +impl RateLimitParams { + pub fn to_governor_quota(self) -> governor::Quota { + use std::num::NonZeroU32; + let burst = NonZeroU32::new(self.limit).unwrap_or(NonZeroU32::MIN); + let period = std::time::Duration::from_millis(self.window_ms); + governor::Quota::with_period(period) + .expect("rate limit window must be non-zero") + .allow_burst(burst) + } +} + #[derive(Debug, Clone, Copy)] pub enum RateLimitKind { Login, @@ -119,7 +116,7 @@ impl RateLimitKind { } } - const fn params(&self) -> RateLimitParams { + pub const fn params(&self) -> RateLimitParams { match self { Self::Login => RateLimitParams { limit: 10, @@ -286,16 +283,6 @@ impl AppState { ); Self { - user_repo: repos.user.clone(), - oauth_repo: repos.oauth.clone(), - session_repo: repos.session.clone(), - delegation_repo: repos.delegation.clone(), - repo_repo: repos.repo.clone(), - blob_repo: repos.blob.clone(), - infra_repo: repos.infra.clone(), - backlink_repo: repos.backlink.clone(), - event_notifier: repos.event_notifier.clone(), - sso_repo: repos.sso.clone(), repos, block_store, blob_store, diff --git a/crates/tranquil-pds/src/util.rs b/crates/tranquil-pds/src/util.rs index de09056..52fea8e 100644 --- a/crates/tranquil-pds/src/util.rs +++ b/crates/tranquil-pds/src/util.rs @@ -25,21 +25,14 @@ static DISCORD_APP_ID: OnceLock = OnceLock::new(); static TELEGRAM_BOT_USERNAME: OnceLock = OnceLock::new(); pub fn generate_token_code() -> String { - generate_token_code_parts(2, 5) -} - -pub fn generate_token_code_parts(parts: usize, part_len: usize) -> String { let mut rng = rand::thread_rng(); let chars: Vec = BASE32_ALPHABET.chars().collect(); - - (0..parts) - .map(|_| { - (0..part_len) - .map(|_| chars[rng.gen_range(0..chars.len())]) - .collect::() - }) - .collect::>() - .join("-") + let gen_segment = |rng: &mut rand::rngs::ThreadRng| -> String { + (0..5) + .map(|_| chars[rng.gen_range(0..chars.len())]) + .collect() + }; + format!("{}-{}", gen_segment(&mut rng), gen_segment(&mut rng)) } pub fn parse_repeated_query_param(query: Option<&str>, key: &str) -> Vec { @@ -295,15 +288,6 @@ mod tests { ); } - #[test] - fn test_generate_token_code_parts() { - let code = generate_token_code_parts(3, 4); - let parts: Vec<&str> = code.split('-').collect(); - assert_eq!(parts.len(), 3); - - assert!(parts.iter().all(|part| part.len() == 4)); - } - #[test] fn test_json_to_ipld_cid_link() { let json = serde_json::json!({