feat: update to rAuth v1
This commit is contained in:
79
.env
Normal file
79
.env
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
# MongoDB URI
|
||||||
|
MONGODB=mongodb://localhost
|
||||||
|
|
||||||
|
# URL to where the Revolt app is publicly accessible
|
||||||
|
REVOLT_APP_URL=http://local.revolt.chat:5000
|
||||||
|
|
||||||
|
# URL to where the API is publicly accessible
|
||||||
|
REVOLT_PUBLIC_URL=http://local.revolt.chat:8000
|
||||||
|
VITE_API_URL=http://local.revolt.chat:8000
|
||||||
|
|
||||||
|
# URL to where the WebSocket server is publicly accessible
|
||||||
|
REVOLT_EXTERNAL_WS_URL=ws://local.revolt.chat:9000
|
||||||
|
|
||||||
|
# URL to where Autumn is publicly available
|
||||||
|
AUTUMN_PUBLIC_URL=http://local.revolt.chat:3000
|
||||||
|
|
||||||
|
# URL to where January is publicly available
|
||||||
|
JANUARY_PUBLIC_URL=http://local.revolt.chat:7000
|
||||||
|
|
||||||
|
# URL to where Vortex is publicly available
|
||||||
|
# VOSO_PUBLIC_URL=https://voso.revolt.chat
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
## hCaptcha Settings
|
||||||
|
##
|
||||||
|
|
||||||
|
# If you are sure that you don't want to use hCaptcha, set to 1.
|
||||||
|
REVOLT_UNSAFE_NO_CAPTCHA=1
|
||||||
|
|
||||||
|
# hCaptcha API key
|
||||||
|
# REVOLT_HCAPTCHA_KEY=0x0000000000000000000000000000000000000000
|
||||||
|
|
||||||
|
# hCaptcha site key
|
||||||
|
# REVOLT_HCAPTCHA_SITEKEY=10000000-ffff-ffff-ffff-000000000001
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
## Email Settings
|
||||||
|
##
|
||||||
|
|
||||||
|
# If you are sure that you don't want to use email verification, set to 1.
|
||||||
|
REVOLT_UNSAFE_NO_EMAIL=1
|
||||||
|
|
||||||
|
# SMTP host
|
||||||
|
# REVOLT_SMTP_HOST=smtp.example.com
|
||||||
|
|
||||||
|
# SMTP username
|
||||||
|
# REVOLT_SMTP_USERNAME=noreply@example.com
|
||||||
|
|
||||||
|
# SMTP password
|
||||||
|
# REVOLT_SMTP_PASSWORD=CHANGEME
|
||||||
|
|
||||||
|
# SMTP From header
|
||||||
|
# REVOLT_SMTP_FROM=Revolt <noreply@example.com>
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
## Application Settings
|
||||||
|
##
|
||||||
|
|
||||||
|
# Whether to only allow users to sign up if they have an invite code
|
||||||
|
REVOLT_INVITE_ONLY=0
|
||||||
|
|
||||||
|
# Maximum number of people that can be in a group chat
|
||||||
|
REVOLT_MAX_GROUP_SIZE=150
|
||||||
|
|
||||||
|
# VAPID keys for push notifications
|
||||||
|
# Generate using this guide: https://gitlab.insrt.uk/revolt/delta/-/wikis/vapid
|
||||||
|
# --> Please replace these keys before going into production! <--
|
||||||
|
REVOLT_VAPID_PRIVATE_KEY=LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUJSUWpyTWxLRnBiVWhsUHpUbERvcEliYk1yeVNrNXpKYzVYVzIxSjJDS3hvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFWnkrQkg2TGJQZ2hEa3pEempXOG0rUXVPM3pCajRXT1phdkR6ZU00c0pqbmFwd1psTFE0WAp1ZDh2TzVodU94QWhMQlU3WWRldVovWHlBdFpWZmNyQi9BPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
|
||||||
|
REVOLT_VAPID_PUBLIC_KEY=BGcvgR-i2z4IQ5Mw841vJvkLjt8wY-FjmWrw83jOLCY52qcGZS0OF7nfLzuYbjsQISwVO2HXrmf18gLWVX3Kwfw=
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
## Vortex configuration
|
||||||
|
##
|
||||||
|
|
||||||
|
# VOSO_MANAGE_TOKEN=CHANGEME
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
Rocket.toml
|
Rocket.toml
|
||||||
target
|
target
|
||||||
|
.data
|
||||||
|
|||||||
1026
Cargo.lock
generated
1026
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -44,7 +44,6 @@ async-std = { version = "1.8.0", features = ["tokio1", "tokio02", "attributes"]
|
|||||||
|
|
||||||
# internal util
|
# internal util
|
||||||
lettre = "0.10.0-alpha.4"
|
lettre = "0.10.0-alpha.4"
|
||||||
rauth = { git = "https://github.com/insertish/rauth", rev = "001a9698c56cea79e69e4ae71d7bc2cb48aec1a6" }
|
|
||||||
|
|
||||||
# redis
|
# redis
|
||||||
redis = { version = "0.21.2", features = ["async-std-comp"] }
|
redis = { version = "0.21.2", features = ["async-std-comp"] }
|
||||||
@@ -52,15 +51,15 @@ mobc = { version = "0.7.3" }
|
|||||||
mobc-redis = { version = "0.7.0", default-features = false, features = ["async-std-comp"] }
|
mobc-redis = { version = "0.7.0", default-features = false, features = ["async-std-comp"] }
|
||||||
|
|
||||||
# web
|
# web
|
||||||
rocket_empty = { git = "https://github.com/insertish/rocket_empty", branch = "rc1" }
|
rocket = { version = "0.5.0-rc.2", default-features = false, features = ["json"] }
|
||||||
rocket = { version = "0.5.0-rc.1", default-features = false, features = ["json"] }
|
rocket_empty = { git = "https://github.com/insertish/rocket_empty", branch = "master" }
|
||||||
mongodb = { version = "1.2.2", features = ["async-std-runtime"], default-features = false }
|
rocket_rauth = { git = "https://github.com/insertish/rauth", rev = "a82cb5d23da4787e75d6f3c795189ce31e1590c4" }
|
||||||
rocket_cors = { git = "https://github.com/lawliet89/rocket_cors", rev = "5843861a88958c16bfaa0b40f0d8910772bcd2f6" }
|
rocket_cors = { git = "https://github.com/lawliet89/rocket_cors", rev = "5843861a88958c16bfaa0b40f0d8910772bcd2f6" }
|
||||||
|
|
||||||
# spec generation
|
# spec generation
|
||||||
schemars = "0.8.8"
|
schemars = "0.8.8"
|
||||||
# rocket_okapi = "0.8.0-rc.1"
|
# rocket_okapi = "0.8.0-rc.1"
|
||||||
rocket_okapi = { git = "https://github.com/insertish/okapi", rev = "dcf0df115596ee07a587a7a543cddf3d7944645b", features = [ "swagger" ] }
|
rocket_okapi = { git = "https://github.com/insertish/okapi", rev = "a1048d0c8cd771e424ec97d33d825c32e06aa120", features = [ "swagger" ] }
|
||||||
|
|
||||||
# quark
|
# quark
|
||||||
revolt-quark = { path = "../quark" }
|
revolt-quark = { path = "../quark" }
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ pub mod util;
|
|||||||
pub mod version;
|
pub mod version;
|
||||||
|
|
||||||
use log::info;
|
use log::info;
|
||||||
use rauth::{
|
use revolt_quark::rauth::config::{
|
||||||
config::{Captcha, Config, EmailVerification, SMTPSettings, Template, Templates},
|
Captcha, Config, EmailVerificationConfig, SMTPSettings, Template, Templates,
|
||||||
logic::Auth,
|
|
||||||
};
|
};
|
||||||
|
use revolt_quark::rauth::RAuth;
|
||||||
use revolt_quark::variables::delta::{
|
use revolt_quark::variables::delta::{
|
||||||
APP_URL, HCAPTCHA_KEY, INVITE_ONLY, SMTP_FROM, SMTP_HOST, SMTP_PASSWORD, SMTP_USERNAME,
|
APP_URL, HCAPTCHA_KEY, INVITE_ONLY, SMTP_FROM, SMTP_HOST, SMTP_PASSWORD, SMTP_USERNAME,
|
||||||
USE_EMAIL, USE_HCAPTCHA,
|
USE_EMAIL, USE_HCAPTCHA,
|
||||||
@@ -25,8 +25,8 @@ use revolt_quark::DatabaseInfo;
|
|||||||
use rocket_cors::AllowedOrigins;
|
use rocket_cors::AllowedOrigins;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[async_std::main]
|
#[launch]
|
||||||
async fn main() {
|
async fn rocket() -> _ {
|
||||||
let _guard = revolt_quark::setup_logging();
|
let _guard = revolt_quark::setup_logging();
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
@@ -58,7 +58,7 @@ async fn main() {
|
|||||||
|
|
||||||
let mut config = Config {
|
let mut config = Config {
|
||||||
email_verification: if *USE_EMAIL {
|
email_verification: if *USE_EMAIL {
|
||||||
EmailVerification::Enabled {
|
EmailVerificationConfig::Enabled {
|
||||||
smtp: SMTPSettings {
|
smtp: SMTPSettings {
|
||||||
from: (*SMTP_FROM).to_string(),
|
from: (*SMTP_FROM).to_string(),
|
||||||
host: (*SMTP_HOST).to_string(),
|
host: (*SMTP_HOST).to_string(),
|
||||||
@@ -86,7 +86,7 @@ async fn main() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
EmailVerification::Disabled
|
EmailVerificationConfig::Disabled
|
||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
@@ -101,23 +101,20 @@ async fn main() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup database
|
||||||
let db = DatabaseInfo::Auto.connect().await.unwrap();
|
let db = DatabaseInfo::Auto.connect().await.unwrap();
|
||||||
db.migrate_database().await.unwrap();
|
db.migrate_database().await.unwrap();
|
||||||
|
|
||||||
// This is entirely temporary code until rauth is migrated to quark.
|
// Setup rAuth
|
||||||
// (and / or otherwise gets updated to MongoDB v2 driver)
|
let rauth = RAuth {
|
||||||
let mongo_db = mongodb::Client::with_uri_str(
|
database: db.clone().into(),
|
||||||
&std::env::var("MONGODB").unwrap_or_else(|_| "mongodb://localhost".to_string()),
|
config,
|
||||||
)
|
};
|
||||||
.await
|
|
||||||
.expect("Failed to init db connection.");
|
|
||||||
|
|
||||||
rauth::entities::sync_models(&mongo_db.database("revolt")).await;
|
|
||||||
|
|
||||||
// Launch background task workers.
|
// Launch background task workers.
|
||||||
async_std::task::spawn(revolt_quark::tasks::start_workers(db.clone()));
|
async_std::task::spawn(revolt_quark::tasks::start_workers(db.clone()));
|
||||||
|
|
||||||
let auth = Auth::new(mongo_db.database("revolt"), config);
|
// Configure Rocket
|
||||||
let rocket = rocket::build();
|
let rocket = rocket::build();
|
||||||
routes::mount(rocket)
|
routes::mount(rocket)
|
||||||
.mount("/", rocket_cors::catch_all_options_routes())
|
.mount("/", rocket_cors::catch_all_options_routes())
|
||||||
@@ -129,14 +126,11 @@ async fn main() {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.manage(auth)
|
.manage(rauth)
|
||||||
.manage(db)
|
.manage(db)
|
||||||
.manage(cors.clone())
|
.manage(cors.clone())
|
||||||
.attach(util::ratelimiter::RatelimitFairing)
|
.attach(util::ratelimiter::RatelimitFairing)
|
||||||
.attach(cors)
|
.attach(cors)
|
||||||
.launch()
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve asset
|
/// Resolve asset
|
||||||
|
|||||||
@@ -25,8 +25,9 @@ pub fn mount(mut rocket: Rocket<Build>) -> Rocket<Build> {
|
|||||||
"/channels" => channels::routes(),
|
"/channels" => channels::routes(),
|
||||||
"/servers" => servers::routes(),
|
"/servers" => servers::routes(),
|
||||||
"/invites" => invites::routes(),
|
"/invites" => invites::routes(),
|
||||||
"/auth/account" => rauth::web::account::routes(),
|
"/auth/account" => rocket_rauth::routes::account::routes(),
|
||||||
"/auth/session" => rauth::web::session::routes(),
|
"/auth/session" => rocket_rauth::routes::session::routes(),
|
||||||
|
"/auth/mfa" => rocket_rauth::routes::mfa::routes(),
|
||||||
"/onboard" => onboard::routes(),
|
"/onboard" => onboard::routes(),
|
||||||
"/push" => push::routes(),
|
"/push" => push::routes(),
|
||||||
"/sync" => sync::routes(),
|
"/sync" => sync::routes(),
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use crate::util::regex::RE_USERNAME;
|
use crate::util::regex::RE_USERNAME;
|
||||||
use revolt_quark::{models::User, Database, EmptyResponse, Error, Result};
|
use revolt_quark::{models::User, rauth::models::Session, Database, EmptyResponse, Error, Result};
|
||||||
|
|
||||||
use rauth::entities::Session;
|
|
||||||
use rocket::{serde::json::Json, State};
|
use rocket::{serde::json::Json, State};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use rauth::entities::Session;
|
use revolt_quark::{models::User, rauth::models::Session};
|
||||||
use revolt_quark::models::User;
|
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
use revolt_quark::{EmptyResponse, Error, Result};
|
use revolt_quark::{
|
||||||
|
rauth::{
|
||||||
use rauth::{
|
models::{Session, WebPushSubscription},
|
||||||
entities::{Model, Session, WebPushSubscription},
|
RAuth,
|
||||||
logic::Auth,
|
},
|
||||||
|
EmptyResponse, Error, Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
use rocket::{serde::json::Json, State};
|
use rocket::{serde::json::Json, State};
|
||||||
|
|
||||||
/// # Push Subscribe
|
/// # Push Subscribe
|
||||||
@@ -14,13 +16,13 @@ use rocket::{serde::json::Json, State};
|
|||||||
#[openapi(tag = "Web Push")]
|
#[openapi(tag = "Web Push")]
|
||||||
#[post("/subscribe", data = "<data>")]
|
#[post("/subscribe", data = "<data>")]
|
||||||
pub async fn req(
|
pub async fn req(
|
||||||
auth: &State<Auth>,
|
rauth: &State<RAuth>,
|
||||||
mut session: Session,
|
mut session: Session,
|
||||||
data: Json<WebPushSubscription>,
|
data: Json<WebPushSubscription>,
|
||||||
) -> Result<EmptyResponse> {
|
) -> Result<EmptyResponse> {
|
||||||
session.subscription = Some(data.into_inner());
|
session.subscription = Some(data.into_inner());
|
||||||
session
|
session
|
||||||
.save(&auth.db, None)
|
.save(&rauth)
|
||||||
.await
|
.await
|
||||||
.map(|_| EmptyResponse)
|
.map(|_| EmptyResponse)
|
||||||
.map_err(|_| Error::DatabaseError {
|
.map_err(|_| Error::DatabaseError {
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
use revolt_quark::{EmptyResponse, Error, Result};
|
use revolt_quark::{
|
||||||
|
rauth::{models::Session, RAuth},
|
||||||
use rauth::{
|
EmptyResponse, Error, Result,
|
||||||
entities::{Model, Session},
|
|
||||||
logic::Auth,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use rocket::State;
|
use rocket::State;
|
||||||
|
|
||||||
/// # Unsubscribe
|
/// # Unsubscribe
|
||||||
@@ -11,10 +10,10 @@ use rocket::State;
|
|||||||
/// Remove the Web Push subscription associated with the current session.
|
/// Remove the Web Push subscription associated with the current session.
|
||||||
#[openapi(tag = "Web Push")]
|
#[openapi(tag = "Web Push")]
|
||||||
#[post("/unsubscribe")]
|
#[post("/unsubscribe")]
|
||||||
pub async fn req(auth: &State<Auth>, mut session: Session) -> Result<EmptyResponse> {
|
pub async fn req(rauth: &State<RAuth>, mut session: Session) -> Result<EmptyResponse> {
|
||||||
session.subscription = None;
|
session.subscription = None;
|
||||||
session
|
session
|
||||||
.save(&auth.db, None)
|
.save(&rauth)
|
||||||
.await
|
.await
|
||||||
.map(|_| EmptyResponse)
|
.map(|_| EmptyResponse)
|
||||||
.map_err(|_| Error::DatabaseError {
|
.map_err(|_| Error::DatabaseError {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use revolt_quark::models::User;
|
use revolt_quark::models::User;
|
||||||
use revolt_quark::r#impl::generic::users::user_settings::UserSettingsImpl;
|
use revolt_quark::r#impl::UserSettingsImpl;
|
||||||
use revolt_quark::{Db, EmptyResponse, Result};
|
use revolt_quark::{Db, EmptyResponse, Result};
|
||||||
|
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use crate::util::regex::RE_USERNAME;
|
use crate::util::regex::RE_USERNAME;
|
||||||
use rauth::entities::Account;
|
use revolt_quark::{models::User, rauth::models::Account, Database, Error, Result};
|
||||||
use revolt_quark::{models::User, Database, Error, Result};
|
|
||||||
use rocket::{serde::json::Json, State};
|
use rocket::{serde::json::Json, State};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use async_std::sync::Mutex;
|
use async_std::sync::Mutex;
|
||||||
use mongodb::bson::doc;
|
|
||||||
use revolt_quark::{Error, Result};
|
use revolt_quark::{Error, Result};
|
||||||
use rocket::http::Status;
|
use rocket::http::Status;
|
||||||
use rocket::request::{FromRequest, Outcome};
|
use rocket::request::{FromRequest, Outcome};
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use std::hash::Hasher;
|
|||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
use revolt_quark::rauth::models::Session;
|
||||||
use rocket::fairing::{Fairing, Info, Kind};
|
use rocket::fairing::{Fairing, Info, Kind};
|
||||||
use rocket::http::uri::Origin;
|
use rocket::http::uri::Origin;
|
||||||
use rocket::http::{Method, Status};
|
use rocket::http::{Method, Status};
|
||||||
@@ -207,10 +208,9 @@ impl<'r> FromRequest<'r> for Ratelimiter {
|
|||||||
let ratelimiter = request
|
let ratelimiter = request
|
||||||
.local_cache_async(async {
|
.local_cache_async(async {
|
||||||
use rocket::outcome::Outcome;
|
use rocket::outcome::Outcome;
|
||||||
let identifier = if let Outcome::Success(session) =
|
let identifier = if let Outcome::Success(session) = request.guard::<Session>().await
|
||||||
request.guard::<rauth::entities::Session>().await
|
|
||||||
{
|
{
|
||||||
session.id.expect("`id` on User")
|
session.id
|
||||||
} else {
|
} else {
|
||||||
to_real_ip(request)
|
to_real_ip(request)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ edition = "2021"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
mongo = [ "mongodb" ]
|
mongo = [ "mongodb" ]
|
||||||
rocket_impl = [ "rocket", "rocket_empty" ]
|
rocket_impl = [ "rocket", "rocket_empty", "rauth/database-mongodb", "rauth/rocket_impl", "rauth/okapi_impl" ]
|
||||||
test = [ "async-std", "mongo", "mongodb/async-std-runtime", "rocket_impl", "rauth" ]
|
|
||||||
|
test = [ "async-std", "mongo", "mongodb/async-std-runtime", "rocket_impl" ]
|
||||||
default = [ "test" ]
|
default = [ "test" ]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
@@ -26,8 +27,8 @@ bson = { version = "2.1.0", features = ["chrono-0_4"] }
|
|||||||
|
|
||||||
# Spec Generation
|
# Spec Generation
|
||||||
schemars = "0.8.8"
|
schemars = "0.8.8"
|
||||||
okapi = { git = "https://github.com/insertish/okapi", rev = "dcf0df115596ee07a587a7a543cddf3d7944645b" }
|
okapi = { git = "https://github.com/insertish/okapi", rev = "a1048d0c8cd771e424ec97d33d825c32e06aa120" }
|
||||||
rocket_okapi = { git = "https://github.com/insertish/okapi", rev = "dcf0df115596ee07a587a7a543cddf3d7944645b" }
|
rocket_okapi = { git = "https://github.com/insertish/okapi", rev = "a1048d0c8cd771e424ec97d33d825c32e06aa120" }
|
||||||
# okapi = "0.7.0-rc.1"
|
# okapi = "0.7.0-rc.1"
|
||||||
# rocket_okapi = "0.8.0-rc.1"
|
# rocket_okapi = "0.8.0-rc.1"
|
||||||
|
|
||||||
@@ -63,10 +64,12 @@ base64 = "0.13.0"
|
|||||||
web-push = "0.7.2"
|
web-push = "0.7.2"
|
||||||
|
|
||||||
# Implementations
|
# Implementations
|
||||||
rauth = { optional = true, git = "https://github.com/insertish/rauth", rev = "001a9698c56cea79e69e4ae71d7bc2cb48aec1a6" }
|
rocket_http = { optional = true, version = "0.5.0-rc.2" }
|
||||||
rocket = { optional = true, version = "=0.5.0-rc.1", default-features = false, features = ["json"] }
|
rocket = { optional = true, version = "0.5.0-rc.2", default-features = false, features = ["json"] }
|
||||||
rocket_http = { optional = true, version = "=0.5.0-rc.1" }
|
rocket_empty = { optional = true, git = "https://github.com/insertish/rocket_empty", branch = "master" }
|
||||||
rocket_empty = { optional = true, git = "https://github.com/insertish/rocket_empty", branch = "rc1" }
|
|
||||||
|
# rAuth
|
||||||
|
rauth = { git = "https://github.com/insertish/rauth", rev = "a82cb5d23da4787e75d6f3c795189ce31e1590c4", features = [ "async-std-runtime" ] }
|
||||||
|
|
||||||
# Sentry
|
# Sentry
|
||||||
sentry = "0.25.0"
|
sentry = "0.25.0"
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
use std::env;
|
use std::env;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use crate::r#impl::dummy::DummyDb;
|
use crate::r#impl::{DummyDb, MongoDb};
|
||||||
use crate::r#impl::mongo::MongoDb;
|
|
||||||
use crate::AbstractDatabase;
|
use crate::AbstractDatabase;
|
||||||
|
|
||||||
/// Database information to use to create a client
|
/// Database information to use to create a client
|
||||||
@@ -61,3 +60,14 @@ impl Deref for Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Database> for rauth::Database {
|
||||||
|
fn from(val: Database) -> Self {
|
||||||
|
match val {
|
||||||
|
Database::Dummy(_) => rauth::Database::default(),
|
||||||
|
Database::MongoDb(MongoDb(client)) => {
|
||||||
|
rauth::Database::MongoDb(rauth::database::MongoDb(client.database("revolt")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,9 +8,6 @@ use crate::{perms, Database, Error, Result};
|
|||||||
|
|
||||||
use futures::try_join;
|
use futures::try_join;
|
||||||
use impl_ops::impl_op_ex_commutative;
|
use impl_ops::impl_op_ex_commutative;
|
||||||
use okapi::openapi3::{SecurityScheme, SecuritySchemeData};
|
|
||||||
use rocket_okapi::gen::OpenApiGenerator;
|
|
||||||
use rocket_okapi::request::{OpenApiFromRequest, RequestHeaderInput};
|
|
||||||
use std::ops;
|
use std::ops;
|
||||||
|
|
||||||
impl_op_ex_commutative!(+ |a: &i32, b: &Badges| -> i32 { *a | *b as i32 });
|
impl_op_ex_commutative!(+ |a: &i32, b: &Badges| -> i32 { *a | *b as i32 });
|
||||||
@@ -359,72 +356,3 @@ impl User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use rauth::entities::Session;
|
|
||||||
use rocket::http::Status;
|
|
||||||
use rocket::request::{self, FromRequest, Outcome, Request};
|
|
||||||
|
|
||||||
#[rocket::async_trait]
|
|
||||||
impl<'r> FromRequest<'r> for User {
|
|
||||||
type Error = rauth::util::Error;
|
|
||||||
|
|
||||||
async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
|
||||||
let user: &Option<User> = request
|
|
||||||
.local_cache_async(async {
|
|
||||||
let db = request
|
|
||||||
.rocket()
|
|
||||||
.state::<Database>()
|
|
||||||
.expect("Database state not reachable!");
|
|
||||||
|
|
||||||
let header_bot_token = request
|
|
||||||
.headers()
|
|
||||||
.get("x-bot-token")
|
|
||||||
.next()
|
|
||||||
.map(|x| x.to_string());
|
|
||||||
|
|
||||||
if let Some(bot_token) = header_bot_token {
|
|
||||||
if let Ok(user) = User::from_token(db, &bot_token, UserHint::Bot).await {
|
|
||||||
return Some(user);
|
|
||||||
}
|
|
||||||
} else if let Outcome::Success(session) = request.guard::<Session>().await {
|
|
||||||
// This uses a guard so can't really easily be refactored into from_token at this stage.
|
|
||||||
if let Ok(user) = db.fetch_user(&session.user_id).await {
|
|
||||||
return Some(user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
|
|
||||||
if let Some(user) = user {
|
|
||||||
Outcome::Success(user.clone())
|
|
||||||
} else {
|
|
||||||
Outcome::Failure((Status::Unauthorized, rauth::util::Error::InvalidSession))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'r> OpenApiFromRequest<'r> for User {
|
|
||||||
fn from_request_input(
|
|
||||||
_gen: &mut OpenApiGenerator,
|
|
||||||
_name: String,
|
|
||||||
_required: bool,
|
|
||||||
) -> rocket_okapi::Result<RequestHeaderInput> {
|
|
||||||
let mut requirements = schemars::Map::new();
|
|
||||||
requirements.insert("Api Key".to_owned(), vec![]);
|
|
||||||
|
|
||||||
Ok(RequestHeaderInput::Security(
|
|
||||||
"Api Key".to_owned(),
|
|
||||||
SecurityScheme {
|
|
||||||
data: SecuritySchemeData::ApiKey {
|
|
||||||
name: "x-session-token".to_owned(),
|
|
||||||
location: "header".to_owned(),
|
|
||||||
},
|
|
||||||
description: Some("Session Token".to_owned()),
|
|
||||||
extensions: schemars::Map::new(),
|
|
||||||
},
|
|
||||||
requirements,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
pub mod dummy;
|
mod dummy;
|
||||||
pub mod generic;
|
mod generic;
|
||||||
pub mod mongo;
|
mod mongo;
|
||||||
|
|
||||||
|
#[cfg(feature = "rocket_impl")]
|
||||||
|
mod rocket;
|
||||||
|
|
||||||
|
pub use self::generic::users::user_settings::UserSettingsImpl;
|
||||||
|
pub use dummy::DummyDb;
|
||||||
|
pub use mongo::MongoDb;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ struct MigrationInfo {
|
|||||||
revision: i32,
|
revision: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const LATEST_REVISION: i32 = 15;
|
pub const LATEST_REVISION: i32 = 16;
|
||||||
|
|
||||||
pub async fn migrate_database(db: &MongoDb) {
|
pub async fn migrate_database(db: &MongoDb) {
|
||||||
let migrations = db.col::<Document>("migrations");
|
let migrations = db.col::<Document>("migrations");
|
||||||
@@ -603,6 +603,15 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if revision <= 15 {
|
||||||
|
info!("Running migration [revision 15 / 04-06-2022]: Migrate rAuth to latest version.");
|
||||||
|
|
||||||
|
let db = rauth::Database::MongoDb(rauth::database::MongoDb(db.db()));
|
||||||
|
db.run_migration(rauth::Migration::M2022_06_03EnsureUpToSpec)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
// Need to migrate fields on attachments, change `user_id`, `object_id`, etc to `parent`.
|
// Need to migrate fields on attachments, change `user_id`, `object_id`, etc to `parent`.
|
||||||
|
|
||||||
// Reminder to update LATEST_REVISION when adding new migrations.
|
// Reminder to update LATEST_REVISION when adding new migrations.
|
||||||
|
|||||||
73
crates/quark/src/impl/rocket.rs
Normal file
73
crates/quark/src/impl/rocket.rs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
use okapi::openapi3::{SecurityScheme, SecuritySchemeData};
|
||||||
|
use rauth::models::Session;
|
||||||
|
use rocket_okapi::gen::OpenApiGenerator;
|
||||||
|
use rocket_okapi::request::{OpenApiFromRequest, RequestHeaderInput};
|
||||||
|
|
||||||
|
use rocket::http::Status;
|
||||||
|
use rocket::request::{self, FromRequest, Outcome, Request};
|
||||||
|
|
||||||
|
use crate::models::user::UserHint;
|
||||||
|
use crate::models::User;
|
||||||
|
use crate::Database;
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
|
impl<'r> FromRequest<'r> for User {
|
||||||
|
type Error = rauth::Error;
|
||||||
|
|
||||||
|
async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||||
|
let user: &Option<User> = request
|
||||||
|
.local_cache_async(async {
|
||||||
|
let db = request.rocket().state::<Database>().expect("`Database`");
|
||||||
|
|
||||||
|
let header_bot_token = request
|
||||||
|
.headers()
|
||||||
|
.get("x-bot-token")
|
||||||
|
.next()
|
||||||
|
.map(|x| x.to_string());
|
||||||
|
|
||||||
|
if let Some(bot_token) = header_bot_token {
|
||||||
|
if let Ok(user) = User::from_token(db, &bot_token, UserHint::Bot).await {
|
||||||
|
return Some(user);
|
||||||
|
}
|
||||||
|
} else if let Outcome::Success(session) = request.guard::<Session>().await {
|
||||||
|
// This uses a guard so can't really easily be refactored into from_token at this stage.
|
||||||
|
if let Ok(user) = db.fetch_user(&session.user_id).await {
|
||||||
|
return Some(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
if let Some(user) = user {
|
||||||
|
Outcome::Success(user.clone())
|
||||||
|
} else {
|
||||||
|
Outcome::Failure((Status::Unauthorized, rauth::Error::InvalidSession))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'r> OpenApiFromRequest<'r> for User {
|
||||||
|
fn from_request_input(
|
||||||
|
_gen: &mut OpenApiGenerator,
|
||||||
|
_name: String,
|
||||||
|
_required: bool,
|
||||||
|
) -> rocket_okapi::Result<RequestHeaderInput> {
|
||||||
|
let mut requirements = schemars::Map::new();
|
||||||
|
requirements.insert("Api Key".to_owned(), vec![]);
|
||||||
|
|
||||||
|
Ok(RequestHeaderInput::Security(
|
||||||
|
"Api Key".to_owned(),
|
||||||
|
SecurityScheme {
|
||||||
|
data: SecuritySchemeData::ApiKey {
|
||||||
|
name: "x-session-token".to_owned(),
|
||||||
|
location: "header".to_owned(),
|
||||||
|
},
|
||||||
|
description: Some("Session Token".to_owned()),
|
||||||
|
extensions: schemars::Map::new(),
|
||||||
|
},
|
||||||
|
requirements,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ extern crate bitfield;
|
|||||||
pub extern crate bson;
|
pub extern crate bson;
|
||||||
|
|
||||||
pub use iso8601_timestamp::Timestamp;
|
pub use iso8601_timestamp::Timestamp;
|
||||||
|
pub use rauth;
|
||||||
pub use redis_kiss;
|
pub use redis_kiss;
|
||||||
|
|
||||||
pub mod events;
|
pub mod events;
|
||||||
@@ -27,6 +28,9 @@ pub mod presence;
|
|||||||
pub mod tasks;
|
pub mod tasks;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
|
#[cfg(feature = "rocket_impl")]
|
||||||
|
pub mod web;
|
||||||
|
|
||||||
mod database;
|
mod database;
|
||||||
mod permissions;
|
mod permissions;
|
||||||
mod traits;
|
mod traits;
|
||||||
@@ -38,38 +42,11 @@ pub use traits::*;
|
|||||||
pub use permissions::defn::*;
|
pub use permissions::defn::*;
|
||||||
pub use permissions::{get_relationship, perms};
|
pub use permissions::{get_relationship, perms};
|
||||||
|
|
||||||
pub use util::r#ref::Ref;
|
pub use util::{
|
||||||
pub use util::result::{Error, Result};
|
log::setup_logging,
|
||||||
pub use util::variables;
|
r#ref::Ref,
|
||||||
|
result::{Error, Result},
|
||||||
|
variables,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "rocket_impl")]
|
pub use web::{Db, EmptyResponse};
|
||||||
pub use rocket_empty::EmptyResponse;
|
|
||||||
|
|
||||||
#[cfg(feature = "rocket_impl")]
|
|
||||||
use rocket::State;
|
|
||||||
|
|
||||||
#[cfg(feature = "rocket_impl")]
|
|
||||||
pub type Db = State<Database>;
|
|
||||||
|
|
||||||
/// Configure logging and common Rust variables
|
|
||||||
pub fn setup_logging() -> sentry::ClientInitGuard {
|
|
||||||
dotenv::dotenv().ok();
|
|
||||||
|
|
||||||
if std::env::var("RUST_LOG").is_err() {
|
|
||||||
std::env::set_var("RUST_LOG", "info");
|
|
||||||
}
|
|
||||||
|
|
||||||
if std::env::var("ROCKET_ADDRESS").is_err() {
|
|
||||||
std::env::set_var("ROCKET_ADDRESS", "0.0.0.0");
|
|
||||||
}
|
|
||||||
|
|
||||||
pretty_env_logger::init();
|
|
||||||
|
|
||||||
sentry::init((
|
|
||||||
"https://62fd0e02c5354905b4e286757f4beb16@sentry.insert.moe/4",
|
|
||||||
sentry::ClientOptions {
|
|
||||||
release: sentry::release_name!(),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ pub async fn start_workers(db: Database) {
|
|||||||
task::spawn(ack::worker(db.clone()));
|
task::spawn(ack::worker(db.clone()));
|
||||||
task::spawn(last_message_id::worker(db.clone()));
|
task::spawn(last_message_id::worker(db.clone()));
|
||||||
task::spawn(process_embeds::worker(db.clone()));
|
task::spawn(process_embeds::worker(db.clone()));
|
||||||
task::spawn(web_push::worker(db.clone()));
|
task::spawn(web_push::worker(db.clone().into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
|
use crate::bson::doc;
|
||||||
use crate::util::variables::delta::VAPID_PRIVATE_KEY;
|
use crate::util::variables::delta::VAPID_PRIVATE_KEY;
|
||||||
use crate::{bson::doc, r#impl::mongo::MongoDb, Database};
|
|
||||||
|
|
||||||
use deadqueue::limited::Queue;
|
use deadqueue::limited::Queue;
|
||||||
use futures::StreamExt;
|
use rauth::Database;
|
||||||
use rauth::entities::Session;
|
|
||||||
use web_push::{
|
use web_push::{
|
||||||
ContentEncoding, SubscriptionInfo, SubscriptionKeys, VapidSignatureBuilder, WebPushClient,
|
ContentEncoding, SubscriptionInfo, SubscriptionKeys, VapidSignatureBuilder, WebPushClient,
|
||||||
WebPushMessageBuilder,
|
WebPushMessageBuilder,
|
||||||
@@ -43,90 +42,69 @@ pub async fn worker(db: Database) {
|
|||||||
let key = base64::decode_config(VAPID_PRIVATE_KEY.clone(), base64::URL_SAFE)
|
let key = base64::decode_config(VAPID_PRIVATE_KEY.clone(), base64::URL_SAFE)
|
||||||
.expect("valid `VAPID_PRIVATE_KEY`");
|
.expect("valid `VAPID_PRIVATE_KEY`");
|
||||||
|
|
||||||
if let Database::MongoDb(MongoDb(db)) = db {
|
loop {
|
||||||
loop {
|
let task = Q.pop().await;
|
||||||
let task = Q.pop().await;
|
|
||||||
|
|
||||||
// ! FIXME: this is hard-coded until rauth is merged into quark
|
if let Ok(sessions) = db.find_sessions_with_subscription(&task.recipients).await {
|
||||||
if let Ok(mut cursor) = db
|
for session in sessions {
|
||||||
.database("revolt")
|
if let Some(sub) = session.subscription {
|
||||||
.collection::<Session>("sessions")
|
let subscription = SubscriptionInfo {
|
||||||
.find(
|
endpoint: sub.endpoint,
|
||||||
doc! {
|
keys: SubscriptionKeys {
|
||||||
"user_id": {
|
auth: sub.auth,
|
||||||
"$in": task.recipients
|
p256dh: sub.p256dh,
|
||||||
},
|
},
|
||||||
"subscription": {
|
};
|
||||||
"$exists": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
while let Some(Ok(session)) = cursor.next().await {
|
|
||||||
if let Some(sub) = session.subscription {
|
|
||||||
let subscription = SubscriptionInfo {
|
|
||||||
endpoint: sub.endpoint,
|
|
||||||
keys: SubscriptionKeys {
|
|
||||||
auth: sub.auth,
|
|
||||||
p256dh: sub.p256dh,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
match WebPushMessageBuilder::new(&subscription) {
|
match WebPushMessageBuilder::new(&subscription) {
|
||||||
Ok(mut builder) => {
|
Ok(mut builder) => {
|
||||||
match VapidSignatureBuilder::from_pem(
|
match VapidSignatureBuilder::from_pem(
|
||||||
std::io::Cursor::new(&key),
|
std::io::Cursor::new(&key),
|
||||||
&subscription,
|
&subscription,
|
||||||
) {
|
) {
|
||||||
Ok(sig_builder) => match sig_builder.build() {
|
Ok(sig_builder) => match sig_builder.build() {
|
||||||
Ok(signature) => {
|
Ok(signature) => {
|
||||||
builder.set_vapid_signature(signature);
|
builder.set_vapid_signature(signature);
|
||||||
builder.set_payload(
|
builder.set_payload(
|
||||||
ContentEncoding::AesGcm,
|
ContentEncoding::AesGcm,
|
||||||
task.payload.as_bytes(),
|
task.payload.as_bytes(),
|
||||||
);
|
);
|
||||||
|
|
||||||
match builder.build() {
|
match builder.build() {
|
||||||
Ok(msg) => match client.send(msg).await {
|
Ok(msg) => match client.send(msg).await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
info!(
|
info!(
|
||||||
"Sent Web Push notification to {:?}.",
|
"Sent Web Push notification to {:?}.",
|
||||||
session.id
|
session.id
|
||||||
)
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
error!(
|
|
||||||
"Hit error sending Web Push! {:?}",
|
|
||||||
err
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
error!(
|
|
||||||
"Failed to build message for {}! {:?}",
|
|
||||||
session.user_id, err
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("Hit error sending Web Push! {:?}", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
error!(
|
||||||
|
"Failed to build message for {}! {:?}",
|
||||||
|
session.user_id, err
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => error!(
|
}
|
||||||
"Failed to build signature for {}! {:?}",
|
|
||||||
session.user_id, err
|
|
||||||
),
|
|
||||||
},
|
|
||||||
Err(err) => error!(
|
Err(err) => error!(
|
||||||
"Failed to create signature builder for {}! {:?}",
|
"Failed to build signature for {}! {:?}",
|
||||||
session.user_id, err
|
session.user_id, err
|
||||||
),
|
),
|
||||||
}
|
},
|
||||||
|
Err(err) => error!(
|
||||||
|
"Failed to create signature builder for {}! {:?}",
|
||||||
|
session.user_id, err
|
||||||
|
),
|
||||||
}
|
}
|
||||||
Err(err) => error!(
|
|
||||||
"Invalid subscription information for {}! {:?}",
|
|
||||||
session.user_id, err
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
Err(err) => error!(
|
||||||
|
"Invalid subscription information for {}! {:?}",
|
||||||
|
session.user_id, err
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
22
crates/quark/src/util/log.rs
Normal file
22
crates/quark/src/util/log.rs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
/// Configure logging and common Rust variables
|
||||||
|
pub fn setup_logging() -> sentry::ClientInitGuard {
|
||||||
|
dotenv::dotenv().ok();
|
||||||
|
|
||||||
|
if std::env::var("RUST_LOG").is_err() {
|
||||||
|
std::env::set_var("RUST_LOG", "info");
|
||||||
|
}
|
||||||
|
|
||||||
|
if std::env::var("ROCKET_ADDRESS").is_err() {
|
||||||
|
std::env::set_var("ROCKET_ADDRESS", "0.0.0.0");
|
||||||
|
}
|
||||||
|
|
||||||
|
pretty_env_logger::init();
|
||||||
|
|
||||||
|
sentry::init((
|
||||||
|
"https://62fd0e02c5354905b4e286757f4beb16@sentry.insert.moe/4",
|
||||||
|
sentry::ClientOptions {
|
||||||
|
release: sentry::release_name!(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
pub mod log;
|
||||||
pub mod manipulation;
|
pub mod manipulation;
|
||||||
pub mod r#ref;
|
pub mod r#ref;
|
||||||
pub mod result;
|
pub mod result;
|
||||||
|
|||||||
5
crates/quark/src/web/mod.rs
Normal file
5
crates/quark/src/web/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
use crate::Database;
|
||||||
|
use rocket::State;
|
||||||
|
|
||||||
|
pub use rocket_empty::EmptyResponse;
|
||||||
|
pub type Db = State<Database>;
|
||||||
12
docker-compose.yml
Normal file
12
docker-compose.yml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
version: "3.3"
|
||||||
|
services:
|
||||||
|
redis:
|
||||||
|
image: eqalpha/keydb
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
||||||
|
database:
|
||||||
|
image: mongo
|
||||||
|
ports:
|
||||||
|
- "27017:27017"
|
||||||
|
volumes:
|
||||||
|
- ./.data/db:/data/db
|
||||||
Reference in New Issue
Block a user