diff --git a/.env b/.env
index d3b4292b..095fba82 100644
--- a/.env
+++ b/.env
@@ -20,3 +20,6 @@ MAX_CURSEFORGE_ID=450000
LOCAL_INDEX_INTERVAL=3600
# 12 hours
EXTERNAL_INDEX_INTERVAL=43200
+
+GITHUB_CLIENT_ID=3acffb2e808d16d4b226
+GITHUB_CLIENT_SECRET=none
\ No newline at end of file
diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml
index b13859f4..a118a4e7 100644
--- a/.idea/sqldialects.xml
+++ b/.idea/sqldialects.xml
@@ -2,5 +2,8 @@
+
+
+
\ No newline at end of file
diff --git a/migrations/20200928020509_states.sql b/migrations/20200928020509_states.sql
new file mode 100644
index 00000000..96e8e140
--- /dev/null
+++ b/migrations/20200928020509_states.sql
@@ -0,0 +1,4 @@
+CREATE TABLE states (
+ id bigint PRIMARY KEY,
+ url varchar(500)
+);
\ No newline at end of file
diff --git a/migrations/20200928033759_edit-states.sql b/migrations/20200928033759_edit-states.sql
new file mode 100644
index 00000000..46fa2495
--- /dev/null
+++ b/migrations/20200928033759_edit-states.sql
@@ -0,0 +1,4 @@
+-- Add migration script here
+ALTER TABLE states
+
+ADD COLUMN expires timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP + interval '1 hour';
\ No newline at end of file
diff --git a/migrations/20200928053955_make-url-not-null.sql b/migrations/20200928053955_make-url-not-null.sql
new file mode 100644
index 00000000..8649f57f
--- /dev/null
+++ b/migrations/20200928053955_make-url-not-null.sql
@@ -0,0 +1,3 @@
+-- Add migration script here
+ALTER TABLE states
+ALTER COLUMN url SET NOT NULL;
\ No newline at end of file
diff --git a/migrations/20200928170310_create-users.sql b/migrations/20200928170310_create-users.sql
new file mode 100644
index 00000000..50fb1c34
--- /dev/null
+++ b/migrations/20200928170310_create-users.sql
@@ -0,0 +1,8 @@
+ALTER TABLE users
+ADD COLUMN github_id bigint NOT NULL default 0,
+ADD COLUMN username varchar(255) NOT NULL default 'username',
+ADD COLUMN name varchar(255) NOT NULL default 'John Doe',
+ADD COLUMN email varchar(255) NULL default 'johndoe@modrinth.com',
+ADD COLUMN avatar_url varchar(500) NOT NULL default '...',
+ADD COLUMN bio varchar(160) NOT NULL default 'I make mods!',
+ADD COLUMN created timestamptz default CURRENT_TIMESTAMP NOT NULL
\ No newline at end of file
diff --git a/migrations/20200928195220_add-roles-to-users.sql b/migrations/20200928195220_add-roles-to-users.sql
new file mode 100644
index 00000000..bfbf6aa4
--- /dev/null
+++ b/migrations/20200928195220_add-roles-to-users.sql
@@ -0,0 +1,3 @@
+-- Add migration script here
+ALTER TABLE users
+ADD COLUMN role varchar(50) NOT NULL default 'developer'
\ No newline at end of file
diff --git a/migrations/20200929034101_add-author-to-versions.sql b/migrations/20200929034101_add-author-to-versions.sql
new file mode 100644
index 00000000..ff256115
--- /dev/null
+++ b/migrations/20200929034101_add-author-to-versions.sql
@@ -0,0 +1,3 @@
+-- Add migration script here
+ALTER TABLE versions
+ADD COLUMN author_id bigint REFERENCES users NOT NULL default 0
\ No newline at end of file
diff --git a/sqlx-data.json b/sqlx-data.json
index 4056865f..2375903f 100644
--- a/sqlx-data.json
+++ b/sqlx-data.json
@@ -1,5 +1,86 @@
{
"db": "PostgreSQL",
+ "03209c5bda2d704e688439919a7b3903db6ad7caebf7ddafb3ea52d312d47bfb": {
+ "query": "\n INSERT INTO users (\n id, github_id, username, name, email,\n avatar_url, bio, created\n )\n VALUES (\n $1, $2, $3, $4, $5,\n $6, $7, $8\n )\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Left": [
+ "Int8",
+ "Int8",
+ "Varchar",
+ "Varchar",
+ "Varchar",
+ "Varchar",
+ "Varchar",
+ "Timestamptz"
+ ]
+ },
+ "nullable": []
+ }
+ },
+ "1016a0bf55e9474357ac5ef725605ac337e82e1a2b93726ae795ec48f0d696dd": {
+ "query": "\n SELECT v.mod_id, v.author_id, v.name, v.version_number,\n v.changelog_url, v.date_published, v.downloads,\n release_channels.channel\n FROM versions v\n INNER JOIN release_channels ON v.release_channel = release_channels.id\n WHERE v.id = $1\n ",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "mod_id",
+ "type_info": "Int8"
+ },
+ {
+ "ordinal": 1,
+ "name": "author_id",
+ "type_info": "Int8"
+ },
+ {
+ "ordinal": 2,
+ "name": "name",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 3,
+ "name": "version_number",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 4,
+ "name": "changelog_url",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 5,
+ "name": "date_published",
+ "type_info": "Timestamptz"
+ },
+ {
+ "ordinal": 6,
+ "name": "downloads",
+ "type_info": "Int4"
+ },
+ {
+ "ordinal": 7,
+ "name": "channel",
+ "type_info": "Varchar"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Int8"
+ ]
+ },
+ "nullable": [
+ false,
+ false,
+ false,
+ false,
+ true,
+ false,
+ false,
+ false
+ ]
+ }
+ },
"1524c0462be70077736ac70fcd037fbf75651456b692e2ce40fa2e3fc8123984": {
"query": "\n SELECT hashes.algorithm, hashes.hash FROM hashes\n WHERE hashes.file_id = $1\n ",
"describe": {
@@ -62,6 +143,26 @@
]
}
},
+ "1c7b0eb4341af5a7942e52f632cf582561f10b4b6a41a082fb8a60f04ac17c6e": {
+ "query": "SELECT EXISTS(SELECT 1 FROM states WHERE id=$1)",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "exists",
+ "type_info": "Bool"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Int8"
+ ]
+ },
+ "nullable": [
+ null
+ ]
+ }
+ },
"1ffce9b2d5c9fa6c8b9abce4bad9f9419c44ad6367b7463b979c91b9b5b4fea1": {
"query": "SELECT EXISTS(SELECT 1 FROM versions WHERE id=$1)",
"describe": {
@@ -102,6 +203,26 @@
]
}
},
+ "275939f581a82197b45b0d56248926063f09ef86754498a720c5568cdb1f5ae0": {
+ "query": "SELECT user_id FROM team_members WHERE team_id=$1",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "user_id",
+ "type_info": "Int8"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Int8"
+ ]
+ },
+ "nullable": [
+ false
+ ]
+ }
+ },
"29e657d26f0fb24a766f5b5eb6a94d01d1616884d8ca10e91536e974d5b585a6": {
"query": "\n INSERT INTO loaders_versions (loader_id, version_id)\n VALUES ($1, $2)\n ",
"describe": {
@@ -145,6 +266,62 @@
"nullable": []
}
},
+ "351af9c9c1c05556bdd8c373f406a66c9358c51dc4222f8abc5095fbf2458471": {
+ "query": "\n SELECT u.id, u.name, u.email,\n u.avatar_url, u.username, u.bio,\n u.created\n FROM users u\n WHERE u.github_id = $1\n ",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "id",
+ "type_info": "Int8"
+ },
+ {
+ "ordinal": 1,
+ "name": "name",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 2,
+ "name": "email",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 3,
+ "name": "avatar_url",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 4,
+ "name": "username",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 5,
+ "name": "bio",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 6,
+ "name": "created",
+ "type_info": "Timestamptz"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Int8"
+ ]
+ },
+ "nullable": [
+ false,
+ false,
+ true,
+ false,
+ false,
+ false,
+ false
+ ]
+ }
+ },
"35272854c6aeb743218e73ccf6f34427ab72f25492dfa752f87a50e3da7204c5": {
"query": "\n SELECT v.mod_id, v.name, v.version_number,\n v.changelog_url, v.date_published, v.downloads,\n release_channels.channel\n FROM versions v\n INNER JOIN release_channels ON v.release_channel = release_channels.id\n WHERE v.id = $1\n ",
"describe": {
@@ -288,6 +465,19 @@
]
}
},
+ "4f307a8851b0cab7870798ba017955c8ebaba7444791dd65ffebcbac32d3585d": {
+ "query": "\n INSERT INTO states (id, url)\n VALUES ($1, $2)\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Left": [
+ "Int8",
+ "Varchar"
+ ]
+ },
+ "nullable": []
+ }
+ },
"560c3ba57c965c3ebdbe393b062da8a30a8a7116a9bace2aa7de2e8431fe0bc7": {
"query": "\n INSERT INTO mods_categories (joining_mod_id, joining_category_id)\n VALUES ($1, $2)\n ",
"describe": {
@@ -409,6 +599,68 @@
]
}
},
+ "6562c876826ad3091a14eb50fa1f961a971c1d1bb158fc3dcb55d469a73facc6": {
+ "query": "\n SELECT v.mod_id, v.author_id, v.name, v.version_number,\n v.changelog_url, v.date_published, v.downloads,\n v.release_channel\n FROM versions v\n WHERE v.id = $1\n ",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "mod_id",
+ "type_info": "Int8"
+ },
+ {
+ "ordinal": 1,
+ "name": "author_id",
+ "type_info": "Int8"
+ },
+ {
+ "ordinal": 2,
+ "name": "name",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 3,
+ "name": "version_number",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 4,
+ "name": "changelog_url",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 5,
+ "name": "date_published",
+ "type_info": "Timestamptz"
+ },
+ {
+ "ordinal": 6,
+ "name": "downloads",
+ "type_info": "Int4"
+ },
+ {
+ "ordinal": 7,
+ "name": "release_channel",
+ "type_info": "Int4"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Int8"
+ ]
+ },
+ "nullable": [
+ false,
+ false,
+ false,
+ false,
+ true,
+ false,
+ false,
+ false
+ ]
+ }
+ },
"6b28cb8b54ef57c9b6f03607611f688455f0e2b27eb5deda5a8cbc5b506b4602": {
"query": "\n DELETE FROM mods\n WHERE id = $1\n ",
"describe": {
@@ -421,6 +673,38 @@
"nullable": []
}
},
+ "71db1bc306ff6da3a92544e1585aa11c5627b50d95b15e794b2fa5dc838ea1a3": {
+ "query": "\n SELECT mod_id, version_number, author_id\n FROM versions\n WHERE id = $1\n ",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "mod_id",
+ "type_info": "Int8"
+ },
+ {
+ "ordinal": 1,
+ "name": "version_number",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 2,
+ "name": "author_id",
+ "type_info": "Int8"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Int8"
+ ]
+ },
+ "nullable": [
+ false,
+ false,
+ false
+ ]
+ }
+ },
"72d6b5f2f11d88981db82c7247c9e7e5ebfd8d34985a1a8209d6628e66490f37": {
"query": "\n SELECT id FROM categories\n WHERE category = $1\n ",
"describe": {
@@ -453,6 +737,26 @@
"nullable": []
}
},
+ "73d9b1e00609919f3adbe5f4ca9e41304bffb1cd4397a85a9911f2260e9a98f5": {
+ "query": "\n INSERT INTO versions (\n id, mod_id, author_id, name, version_number,\n changelog_url, date_published,\n downloads, release_channel\n )\n VALUES (\n $1, $2, $3, $4, $5,\n $6, $7,\n $8, $9\n )\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Left": [
+ "Int8",
+ "Int8",
+ "Int8",
+ "Varchar",
+ "Varchar",
+ "Varchar",
+ "Timestamptz",
+ "Int4",
+ "Int4"
+ ]
+ },
+ "nullable": []
+ }
+ },
"89fbff6249b248d3e150879aaea1662140bcb10d5104992c784285322c8b3b94": {
"query": "\n SELECT version FROM game_versions\n ",
"describe": {
@@ -557,6 +861,18 @@
]
}
},
+ "a39ce28b656032f862b205cffa393a76b989f4803654a615477a94fda5f57354": {
+ "query": "\n DELETE FROM states\n WHERE id = $1\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Left": [
+ "Int8"
+ ]
+ },
+ "nullable": []
+ }
+ },
"a55925860b4a46af864a8c38f942d7cdd85c00638e761b9696de0bf47335173b": {
"query": "\n SELECT mod_id, version_number\n FROM versions\n WHERE id = $1\n ",
"describe": {
@@ -627,6 +943,68 @@
"nullable": []
}
},
+ "a94eb4862ba30ca21f15198d9b7b9fd80ce01d45457e0b4d68270b5e3f9be8c6": {
+ "query": "\n SELECT u.github_id, u.name, u.email,\n u.avatar_url, u.username, u.bio,\n u.created, u.role\n FROM users u\n WHERE u.id = $1\n ",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "github_id",
+ "type_info": "Int8"
+ },
+ {
+ "ordinal": 1,
+ "name": "name",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 2,
+ "name": "email",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 3,
+ "name": "avatar_url",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 4,
+ "name": "username",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 5,
+ "name": "bio",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 6,
+ "name": "created",
+ "type_info": "Timestamptz"
+ },
+ {
+ "ordinal": 7,
+ "name": "role",
+ "type_info": "Varchar"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Int8"
+ ]
+ },
+ "nullable": [
+ false,
+ false,
+ true,
+ false,
+ false,
+ false,
+ false,
+ false
+ ]
+ }
+ },
"b0e3d1c70b87bb54819e3fac04b684a9b857aeedb4dcb7cb400c2af0dbb12922": {
"query": "\n DELETE FROM teams\n WHERE id = $1\n ",
"describe": {
@@ -750,6 +1128,26 @@
]
}
},
+ "bf7f721664f5e0ed41adc41b5483037256635f28ff6c4e5d3cbcec4387f9c8ef": {
+ "query": "SELECT EXISTS(SELECT 1 FROM users WHERE id=$1)",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "exists",
+ "type_info": "Bool"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Int8"
+ ]
+ },
+ "nullable": [
+ null
+ ]
+ }
+ },
"c0899dcff4d7bc1ba3e953e5099210316bff2f98e6ab77ba84bc612eac4bce0a": {
"query": "\n SELECT gv.version FROM versions\n INNER JOIN game_versions_versions gvv ON gvv.joining_version_id=versions.id\n INNER JOIN game_versions gv ON gvv.game_version_id=gv.id\n WHERE versions.mod_id = $1\n ",
"describe": {
@@ -895,6 +1293,26 @@
]
}
},
+ "d0172d12dce3d8ddc888893ec1cdd93ad232685e80f706e70dea22c85d96df63": {
+ "query": "SELECT team_id FROM mods WHERE id=$1",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "team_id",
+ "type_info": "Int8"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Int8"
+ ]
+ },
+ "nullable": [
+ false
+ ]
+ }
+ },
"d12bc07adb4dc8147d0ddccd72a4f23ed38cd31d7db3d36ebbe2c9b627130f0b": {
"query": "\n DELETE FROM team_members\n WHERE team_id = $1\n ",
"describe": {
@@ -1051,6 +1469,68 @@
]
}
},
+ "ea877d50ba461eae97ba3a35c3da71e7cdb7a92de1bb877d6b5dd766aca4e4ef": {
+ "query": "\n SELECT u.id, u.name, u.email,\n u.avatar_url, u.username, u.bio,\n u.created, u.role\n FROM users u\n WHERE u.github_id = $1\n ",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "id",
+ "type_info": "Int8"
+ },
+ {
+ "ordinal": 1,
+ "name": "name",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 2,
+ "name": "email",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 3,
+ "name": "avatar_url",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 4,
+ "name": "username",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 5,
+ "name": "bio",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 6,
+ "name": "created",
+ "type_info": "Timestamptz"
+ },
+ {
+ "ordinal": 7,
+ "name": "role",
+ "type_info": "Varchar"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Int8"
+ ]
+ },
+ "nullable": [
+ false,
+ false,
+ true,
+ false,
+ false,
+ false,
+ false,
+ false
+ ]
+ }
+ },
"eaea3f606f926d7e1fc51a9798ce3c6448f0f02d55ce48bb38e84dc1bdced740": {
"query": "\n INSERT INTO versions (\n id, mod_id, name, version_number,\n changelog_url, date_published,\n downloads, release_channel\n )\n VALUES (\n $1, $2, $3, $4,\n $5, $6,\n $7, $8\n )\n ",
"describe": {
@@ -1184,6 +1664,88 @@
]
}
},
+ "f772d6c3d287da99e00390517ea56cf3190658781da471bef58230e82b892b8c": {
+ "query": "\n SELECT u.github_id, u.name, u.email,\n u.avatar_url, u.username, u.bio,\n u.created\n FROM users u\n WHERE u.id = $1\n ",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "github_id",
+ "type_info": "Int8"
+ },
+ {
+ "ordinal": 1,
+ "name": "name",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 2,
+ "name": "email",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 3,
+ "name": "avatar_url",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 4,
+ "name": "username",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 5,
+ "name": "bio",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 6,
+ "name": "created",
+ "type_info": "Timestamptz"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Int8"
+ ]
+ },
+ "nullable": [
+ false,
+ false,
+ true,
+ false,
+ false,
+ false,
+ false
+ ]
+ }
+ },
+ "f7bea04e8e279e27a24de1bdf3c413daa8677994df5131494b28691ed6611efc": {
+ "query": "\n SELECT url,expires FROM states\n WHERE id = $1\n ",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "url",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 1,
+ "name": "expires",
+ "type_info": "Timestamptz"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Int8"
+ ]
+ },
+ "nullable": [
+ false,
+ false
+ ]
+ }
+ },
"f80ca292323952d10dbd26d3453ced5c12bdd1b71dcd3cb3ade4c7d4dc3590f6": {
"query": "\n SELECT gv.version FROM game_versions_versions gvv\n INNER JOIN game_versions gv ON gvv.game_version_id=gv.id\n WHERE gvv.joining_version_id = $1\n ",
"describe": {
diff --git a/src/auth/mod.rs b/src/auth/mod.rs
new file mode 100644
index 00000000..ee06aa95
--- /dev/null
+++ b/src/auth/mod.rs
@@ -0,0 +1,117 @@
+use crate::database::models;
+use crate::models::users::{Role, User, UserId};
+use actix_web::http::HeaderMap;
+use serde::{Deserialize, Serialize};
+use thiserror::Error;
+
+#[derive(Error, Debug)]
+pub enum AuthenticationError {
+ #[error("An unknown database error occurred")]
+ SqlxDatabaseError(#[from] sqlx::Error),
+ #[error("Database Error: {0}")]
+ DatabaseError(#[from] crate::database::models::DatabaseError),
+ #[error("Error while parsing JSON: {0}")]
+ SerDeError(#[from] serde_json::Error),
+ #[error("Error while communicating to GitHub OAuth2: {0}")]
+ GithubError(#[from] reqwest::Error),
+ #[error("Invalid Authentication Credentials")]
+ InvalidCredentialsError,
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct GitHubUser {
+ pub login: String,
+ pub id: u64,
+ pub avatar_url: String,
+ pub name: String,
+ pub email: Option,
+ pub bio: String,
+}
+
+pub async fn get_github_user_from_token(
+ access_token: &str,
+) -> Result {
+ Ok(reqwest::Client::new()
+ .get("https://api.github.com/user")
+ .header(reqwest::header::USER_AGENT, "Modrinth")
+ .header(
+ reqwest::header::AUTHORIZATION,
+ format!("token {}", access_token),
+ )
+ .send()
+ .await?
+ .json()
+ .await?)
+}
+
+pub async fn get_user_from_token<'a, 'b, E>(
+ access_token: &str,
+ executor: E,
+) -> Result
+where
+ E: sqlx::Executor<'a, Database = sqlx::Postgres>,
+{
+ let github_user = get_github_user_from_token(access_token).await?;
+
+ let res = models::User::get_from_github_id(github_user.id, executor).await?;
+
+ match res {
+ Some(result) => Ok(User {
+ id: UserId::from(result.id),
+ github_id: result.github_id as u64,
+ username: result.username,
+ name: result.name,
+ email: result.email,
+ avatar_url: result.avatar_url,
+ bio: result.bio,
+ created: result.created,
+ role: Role::from_string(&*result.role),
+ }),
+ None => Err(AuthenticationError::InvalidCredentialsError),
+ }
+}
+pub async fn get_user_from_headers<'a, 'b, E>(
+ headers: &HeaderMap,
+ executor: E,
+) -> Result
+where
+ E: sqlx::Executor<'a, Database = sqlx::Postgres>,
+{
+ let token = headers
+ .get("Authentication")
+ .ok_or(AuthenticationError::InvalidCredentialsError)?
+ .to_str()
+ .map_err(|_| AuthenticationError::InvalidCredentialsError)?;
+
+ Ok(get_user_from_token(token, executor).await?)
+}
+
+pub async fn check_is_moderator_from_headers<'a, 'b, E>(
+ headers: &HeaderMap,
+ executor: E,
+) -> Result
+where
+ E: sqlx::Executor<'a, Database = sqlx::Postgres>,
+{
+ let user = get_user_from_headers(headers, executor).await?;
+
+ match user.role {
+ Role::Moderator | Role::Admin => Ok(user),
+ _ => Err(AuthenticationError::InvalidCredentialsError),
+ }
+}
+
+pub async fn check_is_admin_from_headers<'a, 'b, E>(
+ headers: &HeaderMap,
+ executor: E,
+) -> Result
+where
+ E: sqlx::Executor<'a, Database = sqlx::Postgres>,
+{
+ let user = get_user_from_headers(headers, executor).await?;
+
+ match user.role {
+ Role::Admin => Ok(user),
+ _ => Err(AuthenticationError::InvalidCredentialsError),
+ }
+}
diff --git a/src/database/models/ids.rs b/src/database/models/ids.rs
index e2a3bacc..39e7ee02 100644
--- a/src/database/models/ids.rs
+++ b/src/database/models/ids.rs
@@ -73,6 +73,20 @@ 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_user_id,
+ UserId,
+ 8,
+ "SELECT EXISTS(SELECT 1 FROM users WHERE id=$1)",
+ UserId
+);
#[derive(Copy, Clone, Debug, Type)]
#[sqlx(transparent)]
@@ -109,6 +123,10 @@ pub struct CategoryId(pub i32);
#[sqlx(transparent)]
pub struct FileId(pub i64);
+#[derive(Copy, Clone, Debug, Type)]
+#[sqlx(transparent)]
+pub struct StateId(pub i64);
+
use crate::models::ids;
impl From for ModId {
diff --git a/src/database/models/mod.rs b/src/database/models/mod.rs
index 67e8e618..75f32ee4 100644
--- a/src/database/models/mod.rs
+++ b/src/database/models/mod.rs
@@ -7,12 +7,14 @@ pub mod categories;
pub mod ids;
pub mod mod_item;
pub mod team_item;
+pub mod user_item;
pub mod version_item;
pub use ids::*;
pub use mod_item::Mod;
pub use team_item::Team;
pub use team_item::TeamMember;
+pub use user_item::User;
pub use version_item::FileHash;
pub use version_item::Version;
pub use version_item::VersionFile;
diff --git a/src/database/models/user_item.rs b/src/database/models/user_item.rs
new file mode 100644
index 00000000..0e222876
--- /dev/null
+++ b/src/database/models/user_item.rs
@@ -0,0 +1,115 @@
+use super::ids::UserId;
+
+pub struct User {
+ pub id: UserId,
+ pub github_id: i64,
+ pub username: String,
+ pub name: String,
+ pub email: Option,
+ pub avatar_url: String,
+ pub bio: String,
+ pub created: chrono::DateTime,
+ pub role: String,
+}
+
+impl User {
+ pub async fn insert(
+ &self,
+ transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
+ ) -> Result<(), sqlx::error::Error> {
+ sqlx::query!(
+ "
+ INSERT INTO users (
+ id, github_id, username, name, email,
+ avatar_url, bio, created
+ )
+ VALUES (
+ $1, $2, $3, $4, $5,
+ $6, $7, $8
+ )
+ ",
+ self.id as UserId,
+ self.github_id,
+ &self.username,
+ &self.name,
+ self.email.as_ref(),
+ &self.avatar_url,
+ &self.bio,
+ self.created,
+ )
+ .execute(&mut *transaction)
+ .await?;
+
+ Ok(())
+ }
+ pub async fn get<'a, 'b, E>(id: UserId, executor: E) -> Result