forked from didirus/AstralRinth
2FA + Add/Remove Auth Providers (#652)
* 2FA + Add/Remove Auth Providers * fix fmt issue
This commit is contained in:
15
migrations/20230711004131_2fa.sql
Normal file
15
migrations/20230711004131_2fa.sql
Normal file
@@ -0,0 +1,15 @@
|
||||
-- Add migration script here
|
||||
ALTER TABLE states ADD COLUMN user_id bigint references users ON UPDATE CASCADE NULL;
|
||||
|
||||
ALTER TABLE users ADD COLUMN totp_secret varchar(24) null;
|
||||
|
||||
ALTER TABLE users ADD CONSTRAINT email_unique UNIQUE (email);
|
||||
|
||||
DROP TABLE flows;
|
||||
DROP TABLE states;
|
||||
|
||||
CREATE TABLE user_backup_codes (
|
||||
user_id BIGINT NOT NULL REFERENCES users(id),
|
||||
code BIGINT NOT NULL,
|
||||
PRIMARY KEY (user_id, code)
|
||||
);
|
||||
712
sqlx-data.json
712
sqlx-data.json
@@ -125,38 +125,6 @@
|
||||
},
|
||||
"query": "\n INSERT INTO mods_categories (joining_mod_id, joining_category_id, is_additional)\n VALUES ($1, $2, FALSE)\n "
|
||||
},
|
||||
"0640761c8c14cfbcd7009bd50185f39fd7f1467ea81ae3d1a2477887075d72d5": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "url",
|
||||
"ordinal": 0,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "expires",
|
||||
"ordinal": 1,
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"name": "provider",
|
||||
"ordinal": 2,
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n SELECT url, expires, provider FROM states\n WHERE id = $1\n "
|
||||
},
|
||||
"06a92b638c77276f36185788748191e7731a2cce874ecca4af913d0d0412d223": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@@ -825,26 +793,6 @@
|
||||
},
|
||||
"query": "\n UPDATE users\n SET avatar_url = $1\n WHERE (id = $2)\n "
|
||||
},
|
||||
"1c7b0eb4341af5a7942e52f632cf582561f10b4b6a41a082fb8a60f04ac17c6e": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "exists",
|
||||
"ordinal": 0,
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
null
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "SELECT EXISTS(SELECT 1 FROM states WHERE id=$1)"
|
||||
},
|
||||
"1cefe4924d3c1f491739858ce844a22903d2dbe26f255219299f1833a10ce3d7": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
@@ -1065,6 +1013,19 @@
|
||||
},
|
||||
"query": "\n SELECT m.id id, tm.user_id user_id, tm.payouts_split payouts_split\n FROM mods m\n INNER JOIN team_members tm on m.team_id = tm.team_id AND tm.accepted = TRUE\n WHERE m.id = ANY($1)\n "
|
||||
},
|
||||
"282190042b5ccc85e9b1643072a8703edae76dbbcc9493d5c9db392dca60230d": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Varchar"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n UPDATE users\n SET google_id = $2\n WHERE (id = $1)\n "
|
||||
},
|
||||
"294f264382ad55475b51776cd5d306c4867e8e6966ab79921bba69dc023f8337": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@@ -1161,20 +1122,6 @@
|
||||
},
|
||||
"query": "\n DELETE FROM team_members\n WHERE user_id = $1\n "
|
||||
},
|
||||
"2ce3c13256d774b389ce2296c8f3d2b0e9d764b6ead010d37616bd8eec8b647e": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n INSERT INTO states (id, url, provider)\n VALUES ($1, $2, $3)\n "
|
||||
},
|
||||
"2d460f25461e95c744c835af5d67f8a7dd2438a46e3033611dfc0edd74fb9180": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
@@ -1196,6 +1143,26 @@
|
||||
},
|
||||
"query": "\n SELECT COUNT(v.id)\n FROM versions v\n INNER JOIN mods m on v.mod_id = m.id AND m.status = ANY($1)\n WHERE v.status = ANY($2)\n "
|
||||
},
|
||||
"2d68489b978c7a19bbea6a9736d23ca253f4038c0e3e060720d669825073b242": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "code",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n SELECT code FROM user_backup_codes\n WHERE user_id = $1\n "
|
||||
},
|
||||
"2e14706127d9822d5a0d7ada02425d224805637d03eda1343e12111f7deba443": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@@ -1234,32 +1201,6 @@
|
||||
},
|
||||
"query": "\n INSERT INTO users (\n id, username, name, email,\n avatar_url, bio, created,\n github_id, discord_id, gitlab_id, google_id, steam_id, microsoft_id,\n email_verified, password\n )\n VALUES (\n $1, $2, $3, $4, $5,\n $6, $7,\n $8, $9, $10, $11, $12, $13,\n $14, $15\n )\n "
|
||||
},
|
||||
"2eeb8e6fe76c13bcab19ec983234d6fc10a57ea4452740c01504ea4443c18b83": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"name": "password",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
true
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n SELECT id, password FROM users\n WHERE email = $1\n "
|
||||
},
|
||||
"2f4a620f954c7488e8bdb94a3d6968cec6d1332942b9e9f60925d14a8c2040f7": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
@@ -1281,6 +1222,38 @@
|
||||
},
|
||||
"query": "\n SELECT m.id FROM mods m\n INNER JOIN team_members tm ON tm.team_id = m.team_id\n WHERE tm.user_id = $1 AND tm.role = $2\n "
|
||||
},
|
||||
"304aaf99f8909f8315b57fb42b4320de66e7abb2fe1e7bdd19d8c4fd7d5b06be": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n SELECT id FROM users\n WHERE email = $1\n "
|
||||
},
|
||||
"31415c0678f9f968181d1b1ee83e0ded54a99afc62a643dce73636f9bb20fd57": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n UPDATE users\n SET microsoft_id = NULL\n WHERE (id = $1)\n "
|
||||
},
|
||||
"320d73cd900a6e00f0e74b7a8c34a7658d16034b01a35558cb42fa9c16185eb5": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
@@ -1325,6 +1298,18 @@
|
||||
},
|
||||
"query": "\n SELECT tm.id, tm.author_id, tm.thread_id, tm.body, tm.created\n FROM threads_messages tm\n WHERE tm.id = ANY($1)\n "
|
||||
},
|
||||
"332f1d23442b4a637d4bccf29363a7aa4da974a1b6c5752eb1b611da75030741": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n DELETE FROM pats\n WHERE user_id = $1\n "
|
||||
},
|
||||
"33a965c7dc615d3b701c05299889357db8dd36d378850625d2602ba471af4885": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@@ -1390,6 +1375,18 @@
|
||||
},
|
||||
"query": "SELECT EXISTS(SELECT 1 FROM versions WHERE id = $1)"
|
||||
},
|
||||
"36d4b7a18f35cd177cec15e64d3cf4c156980167f0629af37c3947e5ed317faa": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n UPDATE users\n SET discord_id = NULL\n WHERE (id = $1)\n "
|
||||
},
|
||||
"371048e45dd74c855b84cdb8a6a565ccbef5ad166ec9511ab20621c336446da6": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@@ -1804,6 +1801,18 @@
|
||||
},
|
||||
"query": "\n INSERT INTO threads (\n id, thread_type, mod_id, report_id\n )\n VALUES (\n $1, $2, $3, $4\n )\n "
|
||||
},
|
||||
"51e53fa0cc848654300067d4f598da49a16f5ce3aa046d1b08628566b80ce88f": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n DELETE FROM user_backup_codes\n WHERE user_id = $1\n "
|
||||
},
|
||||
"5295fba2053675c8414c0b37a59943535b9a438a642ea1c68045e987f05ade13": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
@@ -1840,6 +1849,18 @@
|
||||
},
|
||||
"query": "\n SELECT l.id id, l.loader loader, l.icon icon,\n ARRAY_AGG(DISTINCT pt.name) filter (where pt.name is not null) project_types\n FROM loaders l\n LEFT OUTER JOIN loaders_project_types lpt ON joining_loader_id = l.id\n LEFT OUTER JOIN project_types pt ON lpt.joining_project_type_id = pt.id\n GROUP BY l.id;\n "
|
||||
},
|
||||
"52d947ff389e17378ff6d978916a85c2d6e7ef3cd4f09f4d5f070a6c33619cd9": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n DELETE FROM user_backup_codes\n WHERE user_id = $1\n "
|
||||
},
|
||||
"53a8966ac345cc334ad65ea907be81af74e90b1217696c7eedcf8a8e3fca736e": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@@ -2223,6 +2244,18 @@
|
||||
},
|
||||
"query": "\n UPDATE mods_gallery\n SET ordering = $2\n WHERE id = $1\n "
|
||||
},
|
||||
"5f614c000f1a83ec87ebd0d514c9b484554dc0d097c6e105c42717930fd58058": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n UPDATE users\n SET steam_id = NULL\n WHERE (id = $1)\n "
|
||||
},
|
||||
"5f94e9e767ec4be7f9136b991b4a29373dbe48feb2f61281e3212721095ed675": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@@ -2239,6 +2272,153 @@
|
||||
},
|
||||
"query": "\n INSERT INTO dependencies (dependent_id, dependency_type, dependency_id, mod_dependency_id, dependency_file_name)\n VALUES ($1, $2, $3, $4, $5)\n "
|
||||
},
|
||||
"60a251aea1efbc7d9357255e520f0ac13f3697fecb84b1e9edd5d9ea61fe0cb0": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"ordinal": 1,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "email",
|
||||
"ordinal": 2,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "avatar_url",
|
||||
"ordinal": 3,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "username",
|
||||
"ordinal": 4,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "bio",
|
||||
"ordinal": 5,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "created",
|
||||
"ordinal": 6,
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"name": "role",
|
||||
"ordinal": 7,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "badges",
|
||||
"ordinal": 8,
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"name": "balance",
|
||||
"ordinal": 9,
|
||||
"type_info": "Numeric"
|
||||
},
|
||||
{
|
||||
"name": "payout_wallet",
|
||||
"ordinal": 10,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "payout_wallet_type",
|
||||
"ordinal": 11,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "payout_address",
|
||||
"ordinal": 12,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "github_id",
|
||||
"ordinal": 13,
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"name": "discord_id",
|
||||
"ordinal": 14,
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"name": "gitlab_id",
|
||||
"ordinal": 15,
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"name": "google_id",
|
||||
"ordinal": 16,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "steam_id",
|
||||
"ordinal": 17,
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"name": "microsoft_id",
|
||||
"ordinal": 18,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "email_verified",
|
||||
"ordinal": 19,
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"name": "password",
|
||||
"ordinal": 20,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "totp_secret",
|
||||
"ordinal": 21,
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8Array",
|
||||
"TextArray"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n SELECT id, name, email,\n avatar_url, username, bio,\n created, role, badges,\n balance, payout_wallet, payout_wallet_type, payout_address,\n github_id, discord_id, gitlab_id, google_id, steam_id, microsoft_id,\n email_verified, password, totp_secret\n FROM users\n WHERE id = ANY($1) OR LOWER(username) = ANY($2)\n "
|
||||
},
|
||||
"61a7f29e024bf2f1368370e3f6e8ef70317c7e8545b5b6d4235f21164948ba27": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@@ -2252,6 +2432,19 @@
|
||||
},
|
||||
"query": "\n UPDATE mods_gallery\n SET featured = $2\n WHERE mod_id = $1\n "
|
||||
},
|
||||
"6289dead78b0e128603f996be47e2d2a487668417124c48828eba8e01e18e651": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n UPDATE users\n SET discord_id = $2\n WHERE (id = $1)\n "
|
||||
},
|
||||
"64d5e7cfb8472fbedcd06143db0db2f4c9677c42f73c540e85ccb5aee1a7b6f9": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@@ -2349,6 +2542,18 @@
|
||||
},
|
||||
"query": "\n DELETE FROM notifications\n WHERE user_id = $1\n "
|
||||
},
|
||||
"69b093cad9109ccf4779bfd969897f6b9ebc9d0d4230c958de4fa07435776349": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n DELETE FROM sessions\n WHERE user_id = $1\n "
|
||||
},
|
||||
"6a7b7704c2a0c52a70f5d881a1e6d3e8e77ddaa83ecc5688cd86bf327775fb76": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
@@ -2511,6 +2716,18 @@
|
||||
},
|
||||
"query": "\n SELECT hp.created, hp.amount, hp.status\n FROM historical_payouts hp\n WHERE hp.user_id = $1\n ORDER BY hp.created DESC\n "
|
||||
},
|
||||
"6d2f27f02fe1073153a95ed2f7431a19170c9641588b444e45c94d0c6ba5b52c": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n UPDATE users\n SET github_id = NULL\n WHERE (id = $1)\n "
|
||||
},
|
||||
"6d883ea05aead20f571a0f63bfd63f1d432717ec7a0fb9ab29e01fcb061b3afc": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@@ -2523,6 +2740,19 @@
|
||||
},
|
||||
"query": "\n UPDATE files\n SET is_primary = FALSE\n WHERE (version_id = $1)\n "
|
||||
},
|
||||
"6db583cb1c69cffeefaacc728046d62cc7d43018ced98420f9f629145f65f81a": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n UPDATE users\n SET steam_id = $2\n WHERE (id = $1)\n "
|
||||
},
|
||||
"6db607d629be3047d53ff92bb82c07700595e8f4fcb7b602918540af4ae50d8b": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@@ -2597,6 +2827,19 @@
|
||||
},
|
||||
"query": "\n INSERT INTO threads_members (\n thread_id, user_id\n )\n VALUES (\n $1, $2\n )\n "
|
||||
},
|
||||
"71650a60acac690130e53c347c5882ead19c9b071f72de45e8737dc077c593d6": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n UPDATE users\n SET gitlab_id = $2\n WHERE (id = $1)\n "
|
||||
},
|
||||
"71abd207410d123f9a50345ddcddee335fea0d0cc6f28762713ee01a36aee8a0": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
@@ -2762,6 +3005,18 @@
|
||||
},
|
||||
"query": "\n SELECT id, short, name FROM donation_platforms\n "
|
||||
},
|
||||
"7711b7c651015510a101cc409fa6f5229ac93d7209df8bc158f4dd4442f611f2": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n DELETE FROM user_backup_codes\n WHERE user_id = $1\n "
|
||||
},
|
||||
"78699c6d2ca0f13f4609310df479903e8d5e0d2d4c2603df0333be7dc040a4ee": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@@ -3292,147 +3547,6 @@
|
||||
},
|
||||
"query": "\n SELECT t.id, t.thread_type, t.mod_id, t.report_id, t.show_in_mod_inbox,\n ARRAY_AGG(DISTINCT tm.user_id) filter (where tm.user_id is not null) members,\n JSONB_AGG(DISTINCT jsonb_build_object('id', tmsg.id, 'author_id', tmsg.author_id, 'thread_id', tmsg.thread_id, 'body', tmsg.body, 'created', tmsg.created)) filter (where tmsg.id is not null) messages\n FROM threads t\n LEFT OUTER JOIN threads_messages tmsg ON tmsg.thread_id = t.id\n LEFT OUTER JOIN threads_members tm ON tm.thread_id = t.id\n WHERE t.id = ANY($1)\n GROUP BY t.id\n "
|
||||
},
|
||||
"95c131d3ea36d53f9dccc6ff8bb7efd3fb571e4175857178c24f5c841a1ec7ed": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"ordinal": 1,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "email",
|
||||
"ordinal": 2,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "avatar_url",
|
||||
"ordinal": 3,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "username",
|
||||
"ordinal": 4,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "bio",
|
||||
"ordinal": 5,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "created",
|
||||
"ordinal": 6,
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"name": "role",
|
||||
"ordinal": 7,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "badges",
|
||||
"ordinal": 8,
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"name": "balance",
|
||||
"ordinal": 9,
|
||||
"type_info": "Numeric"
|
||||
},
|
||||
{
|
||||
"name": "payout_wallet",
|
||||
"ordinal": 10,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "payout_wallet_type",
|
||||
"ordinal": 11,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "payout_address",
|
||||
"ordinal": 12,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "github_id",
|
||||
"ordinal": 13,
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"name": "discord_id",
|
||||
"ordinal": 14,
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"name": "gitlab_id",
|
||||
"ordinal": 15,
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"name": "google_id",
|
||||
"ordinal": 16,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "steam_id",
|
||||
"ordinal": 17,
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"name": "microsoft_id",
|
||||
"ordinal": 18,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "email_verified",
|
||||
"ordinal": 19,
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"name": "password",
|
||||
"ordinal": 20,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8Array",
|
||||
"TextArray"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n SELECT id, name, email,\n avatar_url, username, bio,\n created, role, badges,\n balance, payout_wallet, payout_wallet_type, payout_address,\n github_id, discord_id, gitlab_id, google_id, steam_id, microsoft_id,\n email_verified, password\n FROM users\n WHERE id = ANY($1) OR LOWER(username) = ANY($2)\n "
|
||||
},
|
||||
"95cb791af4ea4d5b959de9e451bb8875336db33238024812086b5237b4dac350": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@@ -3505,6 +3619,18 @@
|
||||
},
|
||||
"query": "\n UPDATE mods\n SET wiki_url = $1\n WHERE (id = $2)\n "
|
||||
},
|
||||
"99e7779380ebae726051ba8e2810f37bee36f3fb36729c07ef11d0ac1b611d7e": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n UPDATE users\n SET totp_secret = NULL\n WHERE (id = $1)\n "
|
||||
},
|
||||
"9aab2350d576fd934b0541d1f71f320ac939b44a179fee3d1638113cdb3ddfe7": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@@ -3519,6 +3645,19 @@
|
||||
},
|
||||
"query": "\n INSERT INTO mods_donations (joining_mod_id, joining_platform_id, url)\n VALUES ($1, $2, $3)\n "
|
||||
},
|
||||
"9bf8862af8f636c4ef77e8c9f1f5d31d4f2d3f5b73fb6e6ca8a09ad5224250c3": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n UPDATE users\n SET totp_secret = $1\n WHERE (id = $2)\n "
|
||||
},
|
||||
"9c8f3f9503b5bb52e05bbc8a8eee7f640ab7d6b04a59ec111ce8b23e886911de": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@@ -3562,6 +3701,18 @@
|
||||
},
|
||||
"query": "\n DELETE FROM mods_categories\n WHERE joining_mod_id = $1 AND is_additional = FALSE\n "
|
||||
},
|
||||
"a04c2455d4ad53e2559927f1783322196c2e067666716407da737a7a44b23e66": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n UPDATE users\n SET google_id = NULL\n WHERE (id = $1)\n "
|
||||
},
|
||||
"a0c91184d5a02b986decac3c34e78b61451ff90e103bcf1ec46f8da3bbcc1ff2": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@@ -3684,18 +3835,6 @@
|
||||
},
|
||||
"query": "\n SELECT tm.id, tm.team_id, tm.user_id, tm.role, tm.permissions, tm.accepted, tm.payouts_split, tm.ordering FROM mods m\n INNER JOIN team_members tm ON tm.team_id = m.team_id AND user_id = $2 AND accepted = TRUE\n WHERE m.id = $1\n "
|
||||
},
|
||||
"a39ce28b656032f862b205cffa393a76b989f4803654a615477a94fda5f57354": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n DELETE FROM states\n WHERE id = $1\n "
|
||||
},
|
||||
"a3e27b758ca441fa82f6bcd42915b92fb23a7db19a7eb27db7ed92eeba4b566e": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
@@ -3742,6 +3881,19 @@
|
||||
},
|
||||
"query": "\n UPDATE mods\n SET status = $1, approved = $2\n WHERE (id = $3)\n "
|
||||
},
|
||||
"a48b717b74531dc457069ee811ec1adc1da195f00a42fff7f08667b139cd8fea": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n INSERT INTO user_backup_codes (\n user_id, code\n )\n VALUES (\n $1, $2\n )\n "
|
||||
},
|
||||
"a62767e812783e8836a11b22878a4248123f3fe212a876e192f549acd6edcb39": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
@@ -3858,15 +4010,17 @@
|
||||
},
|
||||
"query": "\n SELECT v.id id, v.mod_id mod_id, v.author_id author_id, v.name version_name, v.version_number version_number,\n v.changelog changelog, v.date_published date_published, v.downloads downloads,\n v.version_type version_type, v.featured featured, v.status status, v.requested_status requested_status,\n JSONB_AGG(DISTINCT jsonb_build_object('version', gv.version, 'created', gv.created)) filter (where gv.version is not null) game_versions,\n ARRAY_AGG(DISTINCT l.loader) filter (where l.loader is not null) loaders,\n JSONB_AGG(DISTINCT jsonb_build_object('id', f.id, 'url', f.url, 'filename', f.filename, 'primary', f.is_primary, 'size', f.size, 'file_type', f.file_type)) filter (where f.id is not null) files,\n JSONB_AGG(DISTINCT jsonb_build_object('algorithm', h.algorithm, 'hash', encode(h.hash, 'escape'), 'file_id', h.file_id)) filter (where h.hash is not null) hashes,\n JSONB_AGG(DISTINCT jsonb_build_object('project_id', d.mod_dependency_id, 'version_id', d.dependency_id, 'dependency_type', d.dependency_type,'file_name', dependency_file_name)) filter (where d.dependency_type is not null) dependencies\n FROM versions v\n LEFT OUTER JOIN game_versions_versions gvv on v.id = gvv.joining_version_id\n LEFT OUTER JOIN game_versions gv on gvv.game_version_id = gv.id\n LEFT OUTER JOIN loaders_versions lv on v.id = lv.version_id\n LEFT OUTER JOIN loaders l on lv.loader_id = l.id\n LEFT OUTER JOIN files f on v.id = f.version_id\n LEFT OUTER JOIN hashes h on f.id = h.file_id\n LEFT OUTER JOIN dependencies d on v.id = d.dependent_id\n WHERE v.id = ANY($1)\n GROUP BY v.id\n ORDER BY v.date_published ASC;\n "
|
||||
},
|
||||
"a90bb6904e1b790c0e29e060dac5ba4c2a6087e07c1197dc1f59f0aff31944c9": {
|
||||
"aa1d5c33e4ef370e372c6ea467cef103e494bbd167e8d1608ebd6786b87d4129": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n DELETE FROM states\n WHERE expires < CURRENT_DATE\n "
|
||||
"query": "\n UPDATE users\n SET gitlab_id = NULL\n WHERE (id = $1)\n "
|
||||
},
|
||||
"aaec611bae08eac41c163367dc508208178170de91165095405f1b41e47f5e7f": {
|
||||
"describe": {
|
||||
@@ -4225,29 +4379,6 @@
|
||||
},
|
||||
"query": "\n UPDATE mods\n SET license_url = $1\n WHERE (id = $2)\n "
|
||||
},
|
||||
"c15ec51ec0e9900e5569557a618760cb4bbb303f0f9ca1189f18557e67d18b56": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Text",
|
||||
"Int8",
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n SELECT v.id FROM versions v\n INNER JOIN mods m ON mod_id = m.id\n WHERE (m.id = $1 OR m.slug = $2) AND (v.id = $3 OR v.version_number = $4)\n ORDER BY date_published ASC\n "
|
||||
},
|
||||
"c1a3f6dcef6110d6ea884670fb82bac14b98e922bb5673c048ccce7b7300539b": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
@@ -5349,6 +5480,32 @@
|
||||
},
|
||||
"query": "\n SELECT v.id id, v.mod_id mod_id, file_type FROM files f\n INNER JOIN versions v ON v.id = f.version_id\n WHERE f.url = $1\n "
|
||||
},
|
||||
"de1bf7e33a99a10154cefdbe3b8322e4c6a19448b6ee3c6087b1b8163bc52cb1": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n DELETE FROM user_backup_codes\n WHERE user_id = $1 AND code = $2\n "
|
||||
},
|
||||
"de644eab84656a7e2b0d3108580763ebcd149cd6256d474521423d3e6ffaae65": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Varchar"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n UPDATE users\n SET microsoft_id = $2\n WHERE (id = $1)\n "
|
||||
},
|
||||
"debb47a2718f79684c8776da7f289b8d178c302bb5a69562b963b8d008973b8d": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@@ -5362,6 +5519,19 @@
|
||||
},
|
||||
"query": "\n UPDATE threads_messages\n SET body = '{\"type\": \"deleted\"}', author_id = $2\n WHERE author_id = $1\n "
|
||||
},
|
||||
"df816c410a7bd4c0616ed24436bfc84308477b6d40de0b70adbc0c723a1f5cea": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n UPDATE users\n SET github_id = $2\n WHERE (id = $1)\n "
|
||||
},
|
||||
"df871bd959ba97f105ac575f34d8d2a39cbc44a07e0339750a0e477e6fd582ed": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -38,6 +38,8 @@ pub enum AuthenticationError {
|
||||
InvalidAuthMethod,
|
||||
#[error("GitHub Token from incorrect Client ID")]
|
||||
InvalidClientId,
|
||||
#[error("User email/account is already registered on Modrinth")]
|
||||
DuplicateUser,
|
||||
#[error("Invalid callback URL specified")]
|
||||
Url,
|
||||
}
|
||||
@@ -56,6 +58,7 @@ impl actix_web::ResponseError for AuthenticationError {
|
||||
AuthenticationError::InvalidClientId => StatusCode::UNAUTHORIZED,
|
||||
AuthenticationError::Url => StatusCode::BAD_REQUEST,
|
||||
AuthenticationError::FileHosting(..) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
AuthenticationError::DuplicateUser => StatusCode::BAD_REQUEST,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +76,7 @@ impl actix_web::ResponseError for AuthenticationError {
|
||||
AuthenticationError::InvalidClientId => "invalid_client_id",
|
||||
AuthenticationError::Url => "url_error",
|
||||
AuthenticationError::FileHosting(..) => "file_hosting",
|
||||
AuthenticationError::DuplicateUser => "duplicate_user",
|
||||
},
|
||||
description: &self.to_string(),
|
||||
})
|
||||
|
||||
@@ -19,34 +19,38 @@ pub async fn get_user_from_headers<'a, E>(
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy,
|
||||
{
|
||||
let headers = req.headers();
|
||||
let token: Option<&HeaderValue> = headers.get(AUTHORIZATION);
|
||||
|
||||
// Fetch DB user record and minos user from headers
|
||||
let (scopes, db_user) = get_user_record_from_bearer_token(
|
||||
req,
|
||||
token
|
||||
.ok_or_else(|| AuthenticationError::InvalidAuthMethod)?
|
||||
.to_str()
|
||||
.map_err(|_| AuthenticationError::InvalidCredentials)?,
|
||||
executor,
|
||||
redis,
|
||||
session_queue,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| AuthenticationError::InvalidCredentials)?;
|
||||
let (scopes, db_user) =
|
||||
get_user_record_from_bearer_token(req, None, executor, redis, session_queue)
|
||||
.await?
|
||||
.ok_or_else(|| AuthenticationError::InvalidCredentials)?;
|
||||
|
||||
let mut auth_providers = Vec::new();
|
||||
if db_user.github_id.is_some() {
|
||||
auth_providers.push(AuthProvider::GitHub)
|
||||
}
|
||||
if db_user.gitlab_id.is_some() {
|
||||
auth_providers.push(AuthProvider::GitLab)
|
||||
}
|
||||
if db_user.discord_id.is_some() {
|
||||
auth_providers.push(AuthProvider::Discord)
|
||||
}
|
||||
if db_user.google_id.is_some() {
|
||||
auth_providers.push(AuthProvider::Google)
|
||||
}
|
||||
if db_user.microsoft_id.is_some() {
|
||||
auth_providers.push(AuthProvider::Microsoft)
|
||||
}
|
||||
if db_user.steam_id.is_some() {
|
||||
auth_providers.push(AuthProvider::Steam)
|
||||
}
|
||||
|
||||
let user = User {
|
||||
id: UserId::from(db_user.id),
|
||||
github_id: db_user.github_id.map(|x| x as u64),
|
||||
// discord_id: minos_user.discord_id,
|
||||
// google_id: minos_user.google_id,
|
||||
// microsoft_id: minos_user.microsoft_id,
|
||||
// apple_id: minos_user.apple_id,
|
||||
// gitlab_id: minos_user.gitlab_id,
|
||||
username: db_user.username,
|
||||
name: db_user.name,
|
||||
email: db_user.email,
|
||||
email_verified: Some(db_user.email_verified),
|
||||
avatar_url: db_user.avatar_url,
|
||||
bio: db_user.bio,
|
||||
created: db_user.created,
|
||||
@@ -58,6 +62,10 @@ where
|
||||
payout_wallet_type: db_user.payout_wallet_type,
|
||||
payout_address: db_user.payout_address,
|
||||
}),
|
||||
auth_providers: Some(auth_providers),
|
||||
has_password: Some(db_user.password.is_some()),
|
||||
has_totp: Some(db_user.totp_secret.is_some()),
|
||||
github_id: None,
|
||||
};
|
||||
|
||||
if let Some(required_scopes) = required_scopes {
|
||||
@@ -73,7 +81,7 @@ where
|
||||
|
||||
pub async fn get_user_record_from_bearer_token<'a, 'b, E>(
|
||||
req: &HttpRequest,
|
||||
token: &str,
|
||||
token: Option<&str>,
|
||||
executor: E,
|
||||
redis: &deadpool_redis::Pool,
|
||||
session_queue: &AuthQueue,
|
||||
@@ -81,6 +89,17 @@ pub async fn get_user_record_from_bearer_token<'a, 'b, E>(
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy,
|
||||
{
|
||||
let token = if let Some(token) = token {
|
||||
token
|
||||
} else {
|
||||
let headers = req.headers();
|
||||
let token_val: Option<&HeaderValue> = headers.get(AUTHORIZATION);
|
||||
token_val
|
||||
.ok_or_else(|| AuthenticationError::InvalidAuthMethod)?
|
||||
.to_str()
|
||||
.map_err(|_| AuthenticationError::InvalidCredentials)?
|
||||
};
|
||||
|
||||
let possible_user = match token.split_once('_') {
|
||||
Some(("mrp", _)) => {
|
||||
let pat =
|
||||
|
||||
88
src/database/models/flow_item.rs
Normal file
88
src/database/models/flow_item.rs
Normal file
@@ -0,0 +1,88 @@
|
||||
use super::ids::*;
|
||||
use crate::auth::flows::AuthProvider;
|
||||
use crate::database::models::DatabaseError;
|
||||
use chrono::{DateTime, Timelike, Utc};
|
||||
use rand::distributions::Alphanumeric;
|
||||
use rand::Rng;
|
||||
use rand_chacha::rand_core::SeedableRng;
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
use redis::cmd;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const FLOWS_NAMESPACE: &str = "flows";
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum Flow {
|
||||
OAuth {
|
||||
user_id: Option<UserId>,
|
||||
url: String,
|
||||
provider: AuthProvider,
|
||||
},
|
||||
Login2FA {
|
||||
user_id: UserId,
|
||||
},
|
||||
Initialize2FA {
|
||||
user_id: UserId,
|
||||
secret: String,
|
||||
},
|
||||
ForgotPassword {
|
||||
user_id: UserId,
|
||||
},
|
||||
ConfirmEmail {
|
||||
user_id: UserId,
|
||||
confirm_email: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl Flow {
|
||||
pub async fn insert(
|
||||
&self,
|
||||
expires: DateTime<Utc>,
|
||||
redis: &deadpool_redis::Pool,
|
||||
) -> Result<String, DatabaseError> {
|
||||
let mut redis = redis.get().await?;
|
||||
|
||||
let flow = ChaCha20Rng::from_entropy()
|
||||
.sample_iter(&Alphanumeric)
|
||||
.take(32)
|
||||
.map(char::from)
|
||||
.collect::<String>();
|
||||
|
||||
cmd("SET")
|
||||
.arg(format!("{}:{}", FLOWS_NAMESPACE, flow))
|
||||
.arg(serde_json::to_string(&self)?)
|
||||
.arg("EX")
|
||||
.arg(expires.second())
|
||||
.query_async::<_, ()>(&mut redis)
|
||||
.await?;
|
||||
|
||||
Ok(flow)
|
||||
}
|
||||
|
||||
pub async fn get(
|
||||
id: &str,
|
||||
redis: &deadpool_redis::Pool,
|
||||
) -> Result<Option<Flow>, DatabaseError> {
|
||||
let mut redis = redis.get().await?;
|
||||
|
||||
let res = cmd("GET")
|
||||
.arg(format!("{}:{}", FLOWS_NAMESPACE, id))
|
||||
.query_async::<_, Option<String>>(&mut redis)
|
||||
.await?;
|
||||
|
||||
Ok(res.and_then(|x| serde_json::from_str(&x).ok()))
|
||||
}
|
||||
|
||||
pub async fn remove(
|
||||
id: &str,
|
||||
redis: &deadpool_redis::Pool,
|
||||
) -> Result<Option<()>, DatabaseError> {
|
||||
let mut redis = redis.get().await?;
|
||||
let mut cmd = cmd("DEL");
|
||||
cmd.arg(format!("{}:{}", FLOWS_NAMESPACE, id));
|
||||
cmd.query_async::<_, ()>(&mut redis).await?;
|
||||
|
||||
Ok(Some(()))
|
||||
}
|
||||
}
|
||||
@@ -76,13 +76,6 @@ generate_ids!(
|
||||
"SELECT EXISTS(SELECT 1 FROM team_members WHERE id=$1)",
|
||||
TeamMemberId
|
||||
);
|
||||
generate_ids!(
|
||||
pub generate_state_id,
|
||||
StateId,
|
||||
8,
|
||||
"SELECT EXISTS(SELECT 1 FROM states WHERE id=$1)",
|
||||
StateId
|
||||
);
|
||||
generate_ids!(
|
||||
pub generate_pat_id,
|
||||
PatId,
|
||||
@@ -189,10 +182,6 @@ pub struct ReportTypeId(pub i32);
|
||||
#[sqlx(transparent)]
|
||||
pub struct FileId(pub i64);
|
||||
|
||||
#[derive(Copy, Clone, Debug, Type)]
|
||||
#[sqlx(transparent)]
|
||||
pub struct StateId(pub i64);
|
||||
|
||||
#[derive(Copy, Clone, Debug, Type, Deserialize, Serialize)]
|
||||
#[sqlx(transparent)]
|
||||
pub struct PatId(pub i64);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use thiserror::Error;
|
||||
|
||||
pub mod categories;
|
||||
pub mod flow_item;
|
||||
pub mod ids;
|
||||
pub mod notification_item;
|
||||
pub mod pat_item;
|
||||
|
||||
@@ -135,7 +135,8 @@ impl Report {
|
||||
.await?;
|
||||
|
||||
if let Some(thread_id) = thread_id {
|
||||
crate::database::models::Thread::remove_full(ThreadId(thread_id.id), transaction).await?;
|
||||
crate::database::models::Thread::remove_full(ThreadId(thread_id.id), transaction)
|
||||
.await?;
|
||||
}
|
||||
|
||||
sqlx::query!(
|
||||
|
||||
@@ -24,6 +24,8 @@ pub struct User {
|
||||
pub microsoft_id: Option<String>,
|
||||
pub password: Option<String>,
|
||||
|
||||
pub totp_secret: Option<String>,
|
||||
|
||||
pub username: String,
|
||||
pub name: Option<String>,
|
||||
pub email: Option<String>,
|
||||
@@ -204,7 +206,7 @@ impl User {
|
||||
created, role, badges,
|
||||
balance, payout_wallet, payout_wallet_type, payout_address,
|
||||
github_id, discord_id, gitlab_id, google_id, steam_id, microsoft_id,
|
||||
email_verified, password
|
||||
email_verified, password, totp_secret
|
||||
FROM users
|
||||
WHERE id = ANY($1) OR LOWER(username) = ANY($2)
|
||||
",
|
||||
@@ -240,6 +242,7 @@ impl User {
|
||||
.map(|x| RecipientType::from_string(&x)),
|
||||
payout_address: u.payout_address,
|
||||
password: u.password,
|
||||
totp_secret: u.totp_secret,
|
||||
}))
|
||||
})
|
||||
.try_collect::<Vec<User>>()
|
||||
@@ -272,6 +275,23 @@ impl User {
|
||||
Ok(found_users)
|
||||
}
|
||||
|
||||
pub async fn get_email<'a, E>(email: &str, exec: E) -> Result<Option<UserId>, sqlx::Error>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy,
|
||||
{
|
||||
let user_pass = sqlx::query!(
|
||||
"
|
||||
SELECT id FROM users
|
||||
WHERE email = $1
|
||||
",
|
||||
email
|
||||
)
|
||||
.fetch_optional(exec)
|
||||
.await?;
|
||||
|
||||
Ok(user_pass.map(|x| UserId(x.id)))
|
||||
}
|
||||
|
||||
pub async fn get_projects<'a, E>(
|
||||
user_id: UserId,
|
||||
exec: E,
|
||||
@@ -298,6 +318,30 @@ impl User {
|
||||
Ok(projects)
|
||||
}
|
||||
|
||||
pub async fn get_backup_codes<'a, E>(
|
||||
user_id: UserId,
|
||||
exec: E,
|
||||
) -> Result<Vec<String>, sqlx::Error>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy,
|
||||
{
|
||||
use futures::stream::TryStreamExt;
|
||||
|
||||
let codes = sqlx::query!(
|
||||
"
|
||||
SELECT code FROM user_backup_codes
|
||||
WHERE user_id = $1
|
||||
",
|
||||
user_id as UserId,
|
||||
)
|
||||
.fetch_many(exec)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|m| to_base62(m.code as u64))) })
|
||||
.try_collect::<Vec<String>>()
|
||||
.await?;
|
||||
|
||||
Ok(codes)
|
||||
}
|
||||
|
||||
pub async fn clear_caches(
|
||||
user_ids: &[(UserId, Option<String>)],
|
||||
redis: &deadpool_redis::Pool,
|
||||
@@ -486,6 +530,36 @@ impl User {
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
DELETE FROM sessions
|
||||
WHERE user_id = $1
|
||||
",
|
||||
id as UserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
DELETE FROM pats
|
||||
WHERE user_id = $1
|
||||
",
|
||||
id as UserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
DELETE FROM user_backup_codes
|
||||
WHERE user_id = $1
|
||||
",
|
||||
id as UserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
DELETE FROM users
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use super::ids::*;
|
||||
use super::DatabaseError;
|
||||
use crate::models::ids::base62_impl::parse_base62;
|
||||
use crate::models::projects::{FileType, VersionStatus};
|
||||
use chrono::{DateTime, Utc};
|
||||
use itertools::Itertools;
|
||||
@@ -751,40 +750,6 @@ impl Version {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: Needs to be cached
|
||||
pub async fn get_full_from_id_slug<'a, 'b, E>(
|
||||
project_id_or_slug: &str,
|
||||
slug: &str,
|
||||
executor: E,
|
||||
redis: &deadpool_redis::Pool,
|
||||
) -> Result<Option<QueryVersion>, DatabaseError>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy,
|
||||
{
|
||||
let project_id_opt = parse_base62(project_id_or_slug).ok().map(|x| x as i64);
|
||||
let id_opt = parse_base62(slug).ok().map(|x| x as i64);
|
||||
let id = sqlx::query!(
|
||||
"
|
||||
SELECT v.id FROM versions v
|
||||
INNER JOIN mods m ON mod_id = m.id
|
||||
WHERE (m.id = $1 OR m.slug = $2) AND (v.id = $3 OR v.version_number = $4)
|
||||
ORDER BY date_published ASC
|
||||
",
|
||||
project_id_opt,
|
||||
project_id_or_slug,
|
||||
id_opt,
|
||||
slug
|
||||
)
|
||||
.fetch_optional(executor)
|
||||
.await?;
|
||||
|
||||
if let Some(version_id) = id {
|
||||
Ok(Version::get(VersionId(version_id.id), executor, redis).await?)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
|
||||
28
src/main.rs
28
src/main.rs
@@ -130,34 +130,6 @@ async fn main() -> std::io::Result<()> {
|
||||
}
|
||||
});
|
||||
|
||||
// Deleting old authentication states from the database every 15 minutes
|
||||
let pool_ref = pool.clone();
|
||||
scheduler.run(std::time::Duration::from_secs(15 * 60), move || {
|
||||
let pool_ref = pool_ref.clone();
|
||||
// Use sqlx to delete records more than an hour old
|
||||
info!("Deleting old records from temporary tables");
|
||||
|
||||
async move {
|
||||
let states_result = sqlx::query!(
|
||||
"
|
||||
DELETE FROM states
|
||||
WHERE expires < CURRENT_DATE
|
||||
"
|
||||
)
|
||||
.execute(&pool_ref)
|
||||
.await;
|
||||
|
||||
if let Err(e) = states_result {
|
||||
warn!(
|
||||
"Deleting old records from temporary table states failed: {:?}",
|
||||
e
|
||||
);
|
||||
}
|
||||
|
||||
info!("Finished deleting old records from temporary tables");
|
||||
}
|
||||
});
|
||||
|
||||
// Changes statuses of scheduled projects/versions
|
||||
let pool_ref = pool.clone();
|
||||
// TODO: Clear cache when these are run
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use super::ids::Base62Id;
|
||||
use crate::models::ids::{ProjectId, ReportId};
|
||||
use crate::models::projects::ProjectStatus;
|
||||
use crate::models::users::{User, UserId};
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::models::ids::{ProjectId, ReportId};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(from = "Base62Id")]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::ids::Base62Id;
|
||||
use crate::auth::flows::AuthProvider;
|
||||
use chrono::{DateTime, Utc};
|
||||
use rust_decimal::Decimal;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -39,19 +40,21 @@ pub struct User {
|
||||
pub id: UserId,
|
||||
pub username: String,
|
||||
pub name: Option<String>,
|
||||
pub email: Option<String>,
|
||||
pub avatar_url: Option<String>,
|
||||
pub bio: Option<String>,
|
||||
pub created: DateTime<Utc>,
|
||||
pub role: Role,
|
||||
pub badges: Badges,
|
||||
|
||||
pub payout_data: Option<UserPayoutData>,
|
||||
pub auth_providers: Option<Vec<AuthProvider>>,
|
||||
pub email: Option<String>,
|
||||
pub email_verified: Option<bool>,
|
||||
pub has_password: Option<bool>,
|
||||
pub has_totp: Option<bool>,
|
||||
|
||||
// DEPRECATED. Always returns None
|
||||
pub github_id: Option<u64>,
|
||||
// pub discord_id: Option<u64>,
|
||||
// pub google_id: Option<u128>,
|
||||
// pub microsoft_id: Option<u64>,
|
||||
// pub apple_id: Option<u64>,
|
||||
// pub gitlab_id: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
@@ -138,18 +141,17 @@ impl From<DBUser> for User {
|
||||
username: data.username,
|
||||
name: data.name,
|
||||
email: None,
|
||||
email_verified: None,
|
||||
avatar_url: data.avatar_url,
|
||||
bio: data.bio,
|
||||
created: data.created,
|
||||
role: Role::from_string(&data.role),
|
||||
badges: data.badges,
|
||||
payout_data: None,
|
||||
auth_providers: None,
|
||||
has_password: None,
|
||||
has_totp: None,
|
||||
github_id: None,
|
||||
// discord_id: None,
|
||||
// google_id: None,
|
||||
// microsoft_id: None,
|
||||
// apple_id: None,
|
||||
// gitlab_id: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -785,8 +785,8 @@ async fn project_create_inner(
|
||||
project_id: Some(id),
|
||||
report_id: None,
|
||||
}
|
||||
.insert(&mut *transaction)
|
||||
.await?;
|
||||
.insert(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
let response = crate::models::projects::Project {
|
||||
id: project_id,
|
||||
|
||||
@@ -584,8 +584,8 @@ pub async fn project_edit(
|
||||
},
|
||||
thread_id: project_item.thread_id,
|
||||
}
|
||||
.insert(&mut transaction)
|
||||
.await?;
|
||||
.insert(&mut transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
|
||||
@@ -153,8 +153,8 @@ pub async fn report_create(
|
||||
project_id: None,
|
||||
report_id: Some(report.id),
|
||||
}
|
||||
.insert(&mut transaction)
|
||||
.await?;
|
||||
.insert(&mut transaction)
|
||||
.await?;
|
||||
|
||||
transaction.commit().await?;
|
||||
|
||||
@@ -395,8 +395,8 @@ pub async fn report_edit(
|
||||
},
|
||||
thread_id: report.thread_id,
|
||||
}
|
||||
.insert(&mut transaction)
|
||||
.await?;
|
||||
.insert(&mut transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
|
||||
@@ -45,12 +45,14 @@ pub async fn is_authorized_thread(
|
||||
report_id as database::models::ids::ReportId,
|
||||
user_id as database::models::ids::UserId,
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.await?
|
||||
.exists;
|
||||
.fetch_one(pool)
|
||||
.await?
|
||||
.exists;
|
||||
|
||||
report_exists.unwrap_or(false)
|
||||
} else { false }
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
ThreadType::Project => {
|
||||
if let Some(project_id) = thread.project_id {
|
||||
@@ -379,12 +381,7 @@ pub async fn thread_send_message(
|
||||
.await?;
|
||||
|
||||
let mod_notif = if let Some(project_id) = thread.project_id {
|
||||
let project = database::models::Project::get_id(
|
||||
project_id,
|
||||
&**pool,
|
||||
&redis,
|
||||
)
|
||||
.await?;
|
||||
let project = database::models::Project::get_id(project_id, &**pool, &redis).await?;
|
||||
|
||||
if let Some(project) = project {
|
||||
if project.inner.status != ProjectStatus::Processing && user.role.is_mod() {
|
||||
@@ -393,7 +390,7 @@ pub async fn thread_send_message(
|
||||
&**pool,
|
||||
&redis,
|
||||
)
|
||||
.await?;
|
||||
.await?;
|
||||
|
||||
NotificationBuilder {
|
||||
body: NotificationBody::ModeratorMessage {
|
||||
@@ -403,11 +400,11 @@ pub async fn thread_send_message(
|
||||
report_id: None,
|
||||
},
|
||||
}
|
||||
.insert_many(
|
||||
members.into_iter().map(|x| x.user_id).collect(),
|
||||
&mut transaction,
|
||||
)
|
||||
.await?;
|
||||
.insert_many(
|
||||
members.into_iter().map(|x| x.user_id).collect(),
|
||||
&mut transaction,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
project.inner.status == ProjectStatus::Processing && !user.role.is_mod()
|
||||
@@ -415,11 +412,7 @@ pub async fn thread_send_message(
|
||||
!user.role.is_mod()
|
||||
}
|
||||
} else if let Some(report_id) = thread.report_id {
|
||||
let report = database::models::report_item::Report::get(
|
||||
report_id,
|
||||
&**pool,
|
||||
)
|
||||
.await?;
|
||||
let report = database::models::report_item::Report::get(report_id, &**pool).await?;
|
||||
|
||||
if let Some(report) = report {
|
||||
if report.closed && !user.role.is_mod() {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::auth::flows::AuthProvider;
|
||||
use crate::auth::{get_user_from_headers, AuthenticationError};
|
||||
use crate::database::models::User;
|
||||
use crate::file_hosting::FileHost;
|
||||
@@ -193,6 +194,7 @@ pub struct EditUser {
|
||||
#[validate]
|
||||
pub payout_data: Option<Option<EditPayoutData>>,
|
||||
pub password: Option<(Option<String>, Option<String>)>,
|
||||
pub remove_auth_providers: Option<Vec<AuthProvider>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Validate)]
|
||||
@@ -412,7 +414,7 @@ pub async fn user_edit(
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(pass) = actual_user.password {
|
||||
if let Some(pass) = actual_user.password.as_ref() {
|
||||
let old_password = old_password.as_ref().ok_or_else(|| {
|
||||
ApiError::CustomAuthentication(
|
||||
"You must specify the old password to change your password!"
|
||||
@@ -421,7 +423,7 @@ pub async fn user_edit(
|
||||
})?;
|
||||
|
||||
let hasher = Argon2::default();
|
||||
hasher.verify_password(old_password.as_bytes(), &PasswordHash::new(&pass)?)?;
|
||||
hasher.verify_password(old_password.as_bytes(), &PasswordHash::new(pass)?)?;
|
||||
}
|
||||
|
||||
let update_password = if let Some(new_password) = new_password {
|
||||
@@ -483,6 +485,116 @@ pub async fn user_edit(
|
||||
.await?;
|
||||
}
|
||||
|
||||
if let Some(remove_auth_providers) = &new_user.remove_auth_providers {
|
||||
if !scopes.contains(Scopes::USER_AUTH_WRITE) {
|
||||
return Err(ApiError::Authentication(
|
||||
AuthenticationError::InvalidCredentials,
|
||||
));
|
||||
}
|
||||
|
||||
let mut auth_providers = Vec::new();
|
||||
if actual_user.github_id.is_some() {
|
||||
auth_providers.push(AuthProvider::GitHub)
|
||||
}
|
||||
if actual_user.gitlab_id.is_some() {
|
||||
auth_providers.push(AuthProvider::GitLab)
|
||||
}
|
||||
if actual_user.discord_id.is_some() {
|
||||
auth_providers.push(AuthProvider::Discord)
|
||||
}
|
||||
if actual_user.google_id.is_some() {
|
||||
auth_providers.push(AuthProvider::Google)
|
||||
}
|
||||
if actual_user.microsoft_id.is_some() {
|
||||
auth_providers.push(AuthProvider::Microsoft)
|
||||
}
|
||||
if actual_user.steam_id.is_some() {
|
||||
auth_providers.push(AuthProvider::Steam)
|
||||
}
|
||||
|
||||
if auth_providers.len() <= remove_auth_providers.len()
|
||||
&& actual_user.password.is_none()
|
||||
{
|
||||
return Err(ApiError::InvalidInput(
|
||||
"You must have another authentication method added to this method!"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if remove_auth_providers.contains(&AuthProvider::GitHub) {
|
||||
sqlx::query!(
|
||||
"
|
||||
UPDATE users
|
||||
SET github_id = NULL
|
||||
WHERE (id = $1)
|
||||
",
|
||||
id as crate::database::models::ids::UserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
}
|
||||
if remove_auth_providers.contains(&AuthProvider::GitLab) {
|
||||
sqlx::query!(
|
||||
"
|
||||
UPDATE users
|
||||
SET gitlab_id = NULL
|
||||
WHERE (id = $1)
|
||||
",
|
||||
id as crate::database::models::ids::UserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
}
|
||||
if remove_auth_providers.contains(&AuthProvider::Google) {
|
||||
sqlx::query!(
|
||||
"
|
||||
UPDATE users
|
||||
SET google_id = NULL
|
||||
WHERE (id = $1)
|
||||
",
|
||||
id as crate::database::models::ids::UserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
}
|
||||
if remove_auth_providers.contains(&AuthProvider::Steam) {
|
||||
sqlx::query!(
|
||||
"
|
||||
UPDATE users
|
||||
SET steam_id = NULL
|
||||
WHERE (id = $1)
|
||||
",
|
||||
id as crate::database::models::ids::UserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
}
|
||||
if remove_auth_providers.contains(&AuthProvider::Discord) {
|
||||
sqlx::query!(
|
||||
"
|
||||
UPDATE users
|
||||
SET discord_id = NULL
|
||||
WHERE (id = $1)
|
||||
",
|
||||
id as crate::database::models::ids::UserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
}
|
||||
if remove_auth_providers.contains(&AuthProvider::Microsoft) {
|
||||
sqlx::query!(
|
||||
"
|
||||
UPDATE users
|
||||
SET microsoft_id = NULL
|
||||
WHERE (id = $1)
|
||||
",
|
||||
id as crate::database::models::ids::UserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
User::clear_caches(&[(id, Some(actual_user.username))], &redis).await?;
|
||||
transaction.commit().await?;
|
||||
Ok(HttpResponse::NoContent().body(""))
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::auth::{
|
||||
};
|
||||
use crate::database;
|
||||
use crate::models;
|
||||
use crate::models::ids::base62_impl::parse_base62;
|
||||
use crate::models::pats::Scopes;
|
||||
use crate::models::projects::{Dependency, FileType, VersionStatus, VersionType};
|
||||
use crate::models::teams::Permissions;
|
||||
@@ -165,8 +166,8 @@ pub async fn version_project_get(
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let id = info.into_inner();
|
||||
let version_data =
|
||||
database::models::Version::get_full_from_id_slug(&id.0, &id.1, &**pool, &redis).await?;
|
||||
|
||||
let result = database::models::Project::get(&id.0, &**pool, &redis).await?;
|
||||
|
||||
let user_option = get_user_from_headers(
|
||||
&req,
|
||||
@@ -179,9 +180,23 @@ pub async fn version_project_get(
|
||||
.map(|x| x.1)
|
||||
.ok();
|
||||
|
||||
if let Some(data) = version_data {
|
||||
if is_authorized_version(&data.inner, &user_option, &pool).await? {
|
||||
return Ok(HttpResponse::Ok().json(models::projects::Version::from(data)));
|
||||
if let Some(project) = result {
|
||||
if !is_authorized(&project.inner, &user_option, &pool).await? {
|
||||
return Ok(HttpResponse::NotFound().body(""));
|
||||
}
|
||||
|
||||
let versions =
|
||||
database::models::Version::get_many(&project.versions, &**pool, &redis).await?;
|
||||
|
||||
let id_opt = parse_base62(&id.1).ok();
|
||||
let version = versions
|
||||
.into_iter()
|
||||
.find(|x| Some(x.inner.id.0 as u64) == id_opt || x.inner.version_number == id.1);
|
||||
|
||||
if let Some(version) = version {
|
||||
if is_authorized_version(&version.inner, &user_option, &pool).await? {
|
||||
return Ok(HttpResponse::Ok().json(models::projects::Version::from(version)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user