mirror of
https://tangled.org/tranquil.farm/tranquil-pds
synced 2026-09-03 08:46:55 +00:00
refactor(tranquil-pds): consolidate repos, rate limits, validation, and auth helpers
This commit is contained in:
+22
@@ -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"
|
||||
}
|
||||
+15
@@ -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"
|
||||
}
|
||||
+22
@@ -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"
|
||||
}
|
||||
+22
@@ -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"
|
||||
}
|
||||
+22
@@ -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"
|
||||
}
|
||||
+28
@@ -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"
|
||||
}
|
||||
+38
@@ -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"
|
||||
}
|
||||
+14
@@ -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"
|
||||
}
|
||||
+32
@@ -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"
|
||||
}
|
||||
+22
@@ -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"
|
||||
}
|
||||
+16
@@ -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"
|
||||
}
|
||||
+55
@@ -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"
|
||||
}
|
||||
+22
@@ -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"
|
||||
}
|
||||
+28
@@ -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"
|
||||
}
|
||||
+22
@@ -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"
|
||||
}
|
||||
+22
@@ -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"
|
||||
}
|
||||
+15
@@ -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"
|
||||
}
|
||||
+33
@@ -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"
|
||||
}
|
||||
+81
@@ -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"
|
||||
}
|
||||
+28
@@ -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"
|
||||
}
|
||||
+22
@@ -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"
|
||||
}
|
||||
+43
@@ -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"
|
||||
}
|
||||
+33
@@ -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"
|
||||
}
|
||||
+28
@@ -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"
|
||||
}
|
||||
+66
@@ -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"
|
||||
}
|
||||
+22
@@ -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"
|
||||
}
|
||||
+33
@@ -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"
|
||||
}
|
||||
+22
@@ -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"
|
||||
}
|
||||
+22
@@ -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"
|
||||
}
|
||||
+28
@@ -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"
|
||||
}
|
||||
+28
@@ -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"
|
||||
}
|
||||
+34
@@ -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"
|
||||
}
|
||||
+22
@@ -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"
|
||||
}
|
||||
+28
@@ -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"
|
||||
}
|
||||
+22
@@ -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"
|
||||
}
|
||||
+66
@@ -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"
|
||||
}
|
||||
+34
@@ -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"
|
||||
}
|
||||
+15
@@ -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"
|
||||
}
|
||||
+22
@@ -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"
|
||||
}
|
||||
+40
@@ -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"
|
||||
}
|
||||
+28
@@ -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"
|
||||
}
|
||||
+31
@@ -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"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM sso_auth_state WHERE expires_at < NOW()",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "bb4460f75d30f48b79d71b97f2c7d54190260deba2d2ade177dbdaa507ab275b"
|
||||
}
|
||||
+22
@@ -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"
|
||||
}
|
||||
+22
@@ -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"
|
||||
}
|
||||
+31
@@ -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"
|
||||
}
|
||||
+14
@@ -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"
|
||||
}
|
||||
+40
@@ -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"
|
||||
}
|
||||
+22
@@ -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"
|
||||
}
|
||||
+22
@@ -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"
|
||||
}
|
||||
+30
@@ -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"
|
||||
}
|
||||
+15
@@ -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"
|
||||
}
|
||||
+22
@@ -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"
|
||||
}
|
||||
+14
@@ -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"
|
||||
}
|
||||
+28
@@ -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"
|
||||
}
|
||||
@@ -56,7 +56,6 @@ pub enum ApiError {
|
||||
TotpAlreadyEnabled,
|
||||
TotpNotEnabled,
|
||||
InvalidCode(Option<String>),
|
||||
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(),
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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<T: Serialize> {
|
||||
pub options: T,
|
||||
|
||||
@@ -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<str>) -> Result<Self, HandleValidationError> {
|
||||
let validated = validate_short_handle(handle.as_ref())?;
|
||||
Ok(Self(validated))
|
||||
}
|
||||
|
||||
pub fn new_allow_reserved(handle: impl AsRef<str>) -> Result<Self, HandleValidationError> {
|
||||
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<String> for ValidatedLocalHandle {
|
||||
type Error = HandleValidationError;
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
Self::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ValidatedLocalHandle> 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<str>) -> Result<Self, EmailValidationError> {
|
||||
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<String> for ValidatedEmail {
|
||||
type Error = EmailValidationError;
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
Self::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ValidatedEmail> 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 {
|
||||
|
||||
@@ -22,7 +22,7 @@ pub async fn require_verified_or_delegated<'a>(
|
||||
user: &'a AuthenticatedUser,
|
||||
) -> Result<AccountVerified<'a>, 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) => {
|
||||
|
||||
@@ -232,7 +232,7 @@ async fn verify_oauth_token_and_build_user(
|
||||
uri: &str,
|
||||
) -> Result<AuthenticatedUser, AuthError> {
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -76,12 +76,12 @@ pub async fn require_legacy_session_mfa<'a>(
|
||||
) -> Result<MfaVerified<'a>, 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<MfaVerified<'a>, 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(
|
||||
|
||||
@@ -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<i32>,
|
||||
) -> Option<Vec<u8>> {
|
||||
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<Vec<u8>, 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,
|
||||
|
||||
@@ -26,7 +26,7 @@ pub struct ResolvedIdentity {
|
||||
|
||||
pub async fn resolve_identity(state: &AppState, did: &Did) -> Option<ResolvedIdentity> {
|
||||
let is_local = state
|
||||
.user_repo
|
||||
.repos.user
|
||||
.get_by_did(did)
|
||||
.await
|
||||
.ok()
|
||||
|
||||
@@ -29,9 +29,9 @@ async fn check_delegation_flag(
|
||||
error_msg: &str,
|
||||
) -> Result<bool, ApiError> {
|
||||
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())),
|
||||
|
||||
@@ -302,7 +302,7 @@ impl FromRequestParts<AppState> 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<AppState> 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,
|
||||
|
||||
@@ -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<String, DefaultKeyedStateStore<String>, DefaultClock>;
|
||||
pub type GlobalRateLimiter = RateLimiter<NotKeyed, InMemoryState, DefaultClock>;
|
||||
|
||||
fn keyed_limiter(kind: RateLimitKind) -> Arc<KeyedRateLimiter> {
|
||||
Arc::new(RateLimiter::keyed(kind.params().to_governor_quota()))
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RateLimiters {
|
||||
pub login: Arc<KeyedRateLimiter>,
|
||||
@@ -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<KeyedRateLimiter> {
|
||||
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());
|
||||
|
||||
@@ -1,5 +1 @@
|
||||
pub use tranquil_repo::{PostgresBlockStore, TrackingBlockStore};
|
||||
|
||||
pub mod tracking {
|
||||
pub use tranquil_repo::TrackingBlockStore;
|
||||
}
|
||||
|
||||
@@ -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<CommitError> for ApiError {
|
||||
|
||||
pub async fn get_current_root_cid(state: &AppState, user_id: Uuid) -> Result<CommitCid, ApiError> {
|
||||
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<Com
|
||||
}
|
||||
|
||||
pub fn extract_blob_cids(record: &Value) -> Vec<String> {
|
||||
let mut blobs = Vec::new();
|
||||
extract_blob_cids_recursive(record, &mut blobs);
|
||||
blobs
|
||||
}
|
||||
|
||||
fn extract_blob_cids_recursive(value: &Value, blobs: &mut Vec<String>) {
|
||||
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<SequenceNumber, CommitError> {
|
||||
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<SequenceNumber, CommitError> {
|
||||
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)))
|
||||
|
||||
@@ -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<PostgresRepositories>,
|
||||
pub user_repo: Arc<dyn UserRepository>,
|
||||
pub oauth_repo: Arc<dyn OAuthRepository>,
|
||||
pub session_repo: Arc<dyn SessionRepository>,
|
||||
pub delegation_repo: Arc<dyn DelegationRepository>,
|
||||
pub repo_repo: Arc<dyn RepoRepository>,
|
||||
pub blob_repo: Arc<dyn BlobRepository>,
|
||||
pub infra_repo: Arc<dyn InfraRepository>,
|
||||
pub backlink_repo: Arc<dyn BacklinkRepository>,
|
||||
pub event_notifier: Arc<dyn RepoEventNotifier>,
|
||||
pub block_store: PostgresBlockStore,
|
||||
pub blob_store: Arc<dyn BlobStorage>,
|
||||
pub firehose_tx: broadcast::Sender<SequencedEvent>,
|
||||
@@ -54,7 +41,6 @@ pub struct AppState {
|
||||
pub cache: Arc<dyn Cache>,
|
||||
pub distributed_rate_limiter: Arc<dyn DistributedRateLimiter>,
|
||||
pub did_resolver: Arc<DidResolver>,
|
||||
pub sso_repo: Arc<dyn SsoRepository>,
|
||||
pub sso_manager: SsoManager,
|
||||
pub webauthn_config: Arc<WebAuthnConfig>,
|
||||
pub cross_pds_oauth: Arc<CrossPdsOAuthClient>,
|
||||
@@ -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,
|
||||
|
||||
@@ -25,21 +25,14 @@ static DISCORD_APP_ID: OnceLock<String> = OnceLock::new();
|
||||
static TELEGRAM_BOT_USERNAME: OnceLock<String> = 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<char> = BASE32_ALPHABET.chars().collect();
|
||||
|
||||
(0..parts)
|
||||
.map(|_| {
|
||||
(0..part_len)
|
||||
.map(|_| chars[rng.gen_range(0..chars.len())])
|
||||
.collect::<String>()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.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<String> {
|
||||
@@ -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!({
|
||||
|
||||
Reference in New Issue
Block a user