Skip to content

Commit

Permalink
feature: oauth
Browse files Browse the repository at this point in the history
  • Loading branch information
ParzivalEugene committed Aug 6, 2024
1 parent 4476661 commit f647d4f
Show file tree
Hide file tree
Showing 36 changed files with 674 additions and 88 deletions.
23 changes: 23 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,29 @@ services:
options:
loki-url: "https://loki.${HELIOS_DOMAIN}/loki/api/v1/push"

database:
container_name: database
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_DB: ${DATABASE_NAME}
POSTGRES_USER: ${DATABASE_USER}
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
ports:
- "${DATABASE_PORT}:${DATABASE_PORT}"
command: -p ${DATABASE_PORT}
volumes:
- database-volume:/var/lib/postgresql/data
labels:
- "traefik.tcp.routers.database.rule=HostSNI(`database.${HELIOS_DOMAIN}`)"
- "traefik.tcp.routers.database.entrypoints=tcp"
- "traefik.tcp.routers.database.tls.certresolver=myresolver"
- "traefik.tcp.services.database.loadbalancer.server.port=${DATABASE_PORT}"
logging:
driver: "loki"
options:
loki-url: "https://loki.${HELIOS_DOMAIN}/loki/api/v1/push"

volumes:
database-volume:
prometheus-volume:
Expand Down
36 changes: 34 additions & 2 deletions migrations/2024-08-02-114025_initial/up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,32 @@ CREATE TABLE
"user" (
"id" uuid PRIMARY KEY DEFAULT uuid_generate_v4 (),
"email" TEXT NOT NULL,
"password" TEXT NOT NULL,
"banned_at" TIMESTAMP(3),
"banned_till" TIMESTAMP(3),
"status" "UserStatus" NOT NULL DEFAULT 'Active',
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE
"classic_auth" (
"id" uuid PRIMARY KEY DEFAULT uuid_generate_v4 (),
"user_id" uuid NOT NULL,
"password_hash" TEXT NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
)

CREATE TABLE
"oauth" (
"id" uuid PRIMARY KEY DEFAULT uuid_generate_v4 (),
"user_id" uuid NOT NULL,
"provider" TEXT NOT NULL,
"metadata" JSONB NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE
"device" (
"id" uuid PRIMARY KEY DEFAULT uuid_generate_v4 (),
Expand Down Expand Up @@ -101,6 +119,10 @@ ALTER TABLE "session" ADD CONSTRAINT "session_device_id_fkey" FOREIGN KEY ("devi

ALTER TABLE "session" ADD CONSTRAINT "session_config_id_fkey" FOREIGN KEY ("config_id") REFERENCES "config" ("id") ON DELETE RESTRICT ON UPDATE CASCADE;

ALTER TABLE "classic_auth" ADD CONSTRAINT "classic_auth_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user" ("id") ON DELETE RESTRICT ON UPDATE CASCADE;

ALTER TABLE "oauth" ADD CONSTRAINT "oauth_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user" ("id") ON DELETE RESTRICT ON UPDATE CASCADE;

CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
Expand Down Expand Up @@ -130,4 +152,14 @@ EXECUTE FUNCTION update_updated_at();
CREATE TRIGGER table_updated_at_trigger
BEFORE UPDATE ON "device"
FOR EACH ROW
EXECUTE FUNCTION update_updated_at();
EXECUTE FUNCTION update_updated_at();

CREATE TRIGGER table_updated_at_trigger
BEFORE UPDATE ON "classic_auth"
FOR EACH ROW
EXECUTE FUNCTION update_updated_at();

CREATE TRIGGER table_updated_at_trigger
BEFORE UPDATE ON "oauth"
FOR EACH ROW
EXECUTE FUNCTION update_updated_at();
2 changes: 0 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use dotenvy::dotenv;
use once_cell::sync::Lazy;
use std::env;

Expand All @@ -25,7 +24,6 @@ pub struct Config {
}

pub static ENV: Lazy<Config> = Lazy::new(|| {
dotenv().ok();
Config {
master_backend_url: env::var("MASTER_BACKEND_URL").expect("MASTER_BACKEND_URL must be set"),
database_url: env::var("DATABASE_URL").expect("DATABASE_URL must be set"),
Expand Down
3 changes: 3 additions & 0 deletions src/data/enums/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ pub use session_status::SessionStatus;

pub mod user_status;
pub use user_status::UserStatus;

pub mod oauth_provider;
pub use oauth_provider::OAuthProvider;
51 changes: 51 additions & 0 deletions src/data/enums/oauth_provider.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use diesel::{
deserialize::{self, FromSql, FromSqlRow},
expression::AsExpression,
pg::{Pg, PgValue},
serialize::{self, IsNull, Output, ToSql},
};
use serde::{Deserialize, Serialize};
use std::io::Write;

use crate::enums::errors::internal::{AuthError, InternalError};

#[derive(Debug, AsExpression, FromSqlRow, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
#[diesel(sql_type = crate::data::schema::sql_types::OAuthProvider)]
pub enum OAuthProvider {
Github,
Google,
Discord,
}

impl OAuthProvider {
pub fn from_str(s: &str) -> Result<Self, InternalError> {
match s {
"Github" => Ok(OAuthProvider::Github),
"Google" => Ok(OAuthProvider::Google),
"Discord" => Ok(OAuthProvider::Discord),
_ => Err(InternalError::AuthError(AuthError::UnknownOAuthProvider)),
}
}
}

impl ToSql<crate::data::schema::sql_types::OAuthProvider, Pg> for OAuthProvider {
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
match *self {
OAuthProvider::Github => out.write_all(b"Github")?,
OAuthProvider::Google => out.write_all(b"Google")?,
OAuthProvider::Discord => out.write_all(b"Discord")?,
}
Ok(IsNull::No)
}
}

impl FromSql<crate::data::schema::sql_types::OAuthProvider, Pg> for OAuthProvider {
fn from_sql(bytes: PgValue) -> deserialize::Result<Self> {
match bytes.as_bytes() {
b"Github" => Ok(OAuthProvider::Github),
b"Google" => Ok(OAuthProvider::Google),
b"Discord" => Ok(OAuthProvider::Discord),
_ => Err("Unrecognized enum variant".into()),
}
}
}
3 changes: 1 addition & 2 deletions src/data/enums/session_status.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use std::io::Write;

use diesel::{
deserialize::{self, FromSql, FromSqlRow},
expression::AsExpression,
pg::{Pg, PgValue},
serialize::{self, IsNull, Output, ToSql},
};
use std::io::Write;

#[derive(Debug, AsExpression, FromSqlRow, PartialEq, Eq, Clone, Copy)]
#[diesel(sql_type = crate::data::schema::sql_types::SessionStatus)]
Expand Down
4 changes: 2 additions & 2 deletions src/data/enums/user_status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ use diesel::{
pg::{Pg, PgValue},
serialize::{self, IsNull, Output, ToSql},
};
use serde::Serialize;
use serde::{Deserialize, Serialize};
use std::io::Write;

#[derive(Debug, AsExpression, FromSqlRow, PartialEq, Eq, Clone, Copy, Serialize)]
#[derive(Debug, AsExpression, FromSqlRow, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
#[diesel(sql_type = crate::data::schema::sql_types::UserStatus)]
pub enum UserStatus {
Active,
Expand Down
16 changes: 16 additions & 0 deletions src/data/models/classic_auth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use crate::data::schema;
use chrono::NaiveDateTime;
use diesel::{Queryable, Selectable};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

#[derive(Queryable, Selectable, Debug, Clone, Serialize, Deserialize)]
#[diesel(table_name = schema::classic_auth)]
#[diesel(check_for_backend(diesel::pg::Pg))]
pub struct ClassicAuth {
pub id: Uuid,
pub user_id: Uuid,
pub password_hash: String,
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
}
6 changes: 6 additions & 0 deletions src/data/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,9 @@ pub use server::Server;

pub mod user;
pub use user::User;

pub mod classic_auth;
pub use classic_auth::ClassicAuth;

pub mod oauth;
pub use oauth::OAuth;
17 changes: 17 additions & 0 deletions src/data/models/oauth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use crate::data::{enums::OAuthProvider, schema};
use chrono::NaiveDateTime;
use diesel::{Queryable, Selectable};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

#[derive(Queryable, Selectable, Debug, Clone, Serialize, Deserialize)]
#[diesel(table_name = schema::oauth)]
#[diesel(check_for_backend(diesel::pg::Pg))]
pub struct OAuth {
pub id: Uuid,
pub user_id: Uuid,
pub provider: OAuthProvider,
pub metadata: serde_json::Value,
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
}
5 changes: 2 additions & 3 deletions src/data/models/user.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
use crate::data::{enums::UserStatus, schema};
use chrono::NaiveDateTime;
use diesel::{Queryable, Selectable};
use serde::Serialize;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

#[derive(Queryable, Selectable, Debug, Clone, Serialize)]
#[derive(Queryable, Selectable, Debug, Clone, Serialize, Deserialize)]
#[diesel(table_name = schema::user)]
#[diesel(check_for_backend(diesel::pg::Pg))]
pub struct User {
pub id: Uuid,
pub email: String,
pub password: String,
pub banned_at: Option<NaiveDateTime>,
pub banned_till: Option<NaiveDateTime>,
pub status: UserStatus,
Expand Down
43 changes: 41 additions & 2 deletions src/data/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ pub mod sql_types {
#[derive(diesel::query_builder::QueryId, diesel::sql_types::SqlType)]
#[diesel(postgres_type(name = "UserStatus"))]
pub struct UserStatus;

#[derive(diesel::query_builder::QueryId, diesel::sql_types::SqlType)]
#[diesel(postgres_type(name = "OAuthProvider"))]
pub struct OAuthProvider;
}

diesel::table! {
Expand Down Expand Up @@ -95,7 +99,6 @@ diesel::table! {
user (id) {
id -> Uuid,
email -> Text,
password -> Text,
banned_at -> Nullable<Timestamp>,
banned_till -> Nullable<Timestamp>,
status -> UserStatus,
Expand All @@ -104,9 +107,45 @@ diesel::table! {
}
}

diesel::table! {
use diesel::sql_types::*;

classic_auth (id) {
id -> Uuid,
user_id -> Uuid,
password_hash -> Text,
created_at -> Timestamp,
updated_at -> Timestamp,
}
}

diesel::table! {
use diesel::sql_types::*;
use super::sql_types::OAuthProvider;

oauth (id) {
id -> Uuid,
user_id -> Uuid,
provider -> OAuthProvider,
metadata -> Jsonb,
created_at -> Timestamp,
updated_at -> Timestamp,
}
}

diesel::joinable!(classic_auth -> user (user_id));
diesel::joinable!(oauth -> user (user_id));
diesel::joinable!(config -> server (server_id));
diesel::joinable!(device -> user (user_id));
diesel::joinable!(session -> config (config_id));
diesel::joinable!(session -> device (device_id));

diesel::allow_tables_to_appear_in_same_query!(config, device, server, session, user,);
diesel::allow_tables_to_appear_in_same_query!(
config,
device,
server,
session,
user,
classic_auth,
oauth,
);
10 changes: 10 additions & 0 deletions src/dto/auth/internal/full_user.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use serde::{Deserialize, Serialize};

use crate::data::models::{ClassicAuth, OAuth, User};

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct FullUser {
pub user: User,
pub oauth: Option<Vec<OAuth>>,
pub classic_auth: Option<ClassicAuth>,
}
6 changes: 6 additions & 0 deletions src/dto/auth/internal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,9 @@ pub use refresh_token::RefreshToken;

mod oauth_code;
pub use oauth_code::OAuthCode;

mod oauth_user;
pub use oauth_user::OAuthUser;

mod full_user;
pub use full_user::FullUser;
6 changes: 1 addition & 5 deletions src/dto/auth/internal/new_user.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
use crate::data::schema;
use diesel::prelude::*;

#[derive(Insertable, Clone)]
#[diesel(table_name = schema::user)]
#[derive(Clone)]
pub struct NewUser {
pub email: String,
pub password: String,
Expand Down
2 changes: 1 addition & 1 deletion src/dto/auth/internal/oauth_code.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::enums::oauth_providers::OAuthProvider;
use crate::data::enums::OAuthProvider;

#[derive(Clone, Debug)]
pub struct OAuthCode {
Expand Down
7 changes: 7 additions & 0 deletions src/dto/auth/internal/oauth_user.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use serde::{Deserialize, Serialize};

#[derive(Clone, Serialize, Deserialize)]
pub struct OAuthUser {
pub email: String,
pub metadata: serde_json::Value,
}
7 changes: 4 additions & 3 deletions src/dto/auth/request/authorize.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use crate::{dto::device::internal::DeviceInfo, enums::oauth_providers::OAuthProvider};
use crate::dto::device::internal::DeviceInfo;
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;

#[derive(Debug, Serialize, Deserialize, Clone)]
#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
pub struct AuthorizeRequest {
pub code: String,
pub provider: OAuthProvider,
pub provider: String,
pub device: DeviceInfo,
}
Loading

0 comments on commit f647d4f

Please sign in to comment.