Move context to new file, add to context if user is authentified
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Lucien Cartier-Tilet 2023-01-15 18:10:51 +01:00
parent b20fb5f079
commit 05ca82c4e1
Signed by: phundrak
GPG Key ID: BD7789E705CB8DCA
4 changed files with 98 additions and 22 deletions

View File

@ -18,8 +18,8 @@ pub struct APVariables {
impl APVariables { impl APVariables {
pub async fn check_session( pub async fn check_session(
&self, &self,
session_id: String, session_id: &str,
user_id: String, user_id: &str,
) -> Result<bool> { ) -> Result<bool> {
let client = reqwest::Client::new(); let client = reqwest::Client::new();
let url = format!("{}/users/{}/sessions", self.endpoint, user_id); let url = format!("{}/users/{}/sessions", self.endpoint, user_id);

View File

@ -29,7 +29,7 @@ macro_rules! find_element {
use diesel::prelude::*; use diesel::prelude::*;
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct Database { pub struct Database {
conn: Pool<ConnectionManager<PgConnection>>, conn: Pool<ConnectionManager<PgConnection>>,
} }

49
src/graphql/context.rs Normal file
View File

@ -0,0 +1,49 @@
use crate::appwrite::APVariables;
use crate::db::Database;
use tracing::info;
#[derive(Default, Debug, Clone)]
pub struct Context {
pub db: Database,
pub appwrite: APVariables,
pub user_auth: bool,
}
impl Context {
/// HTTP header for a user's session
///
/// This header `Authorization` must be a single string in the
/// form `userId;userSessionId` with `userId` and `userSessionId`
/// being variables given by Appwrite to users that are logged in.
pub async fn user_auth<'r>(&self, auth_token: Option<&'r str>) -> bool {
if let Some(token) = auth_token {
let key = token.split(';').collect::<Vec<_>>();
if key.len() == 2 {
let user_id = key[0];
let session_id = key[1];
match self.appwrite.check_session(session_id, user_id).await {
Ok(val) => val,
Err(e) => {
info!("Error checking user session: {:?}", e);
false
}
}
} else {
info!("Invalid session key: {}", token);
false
}
} else {
false
}
}
pub async fn attach_auth<'r>(&self, auth_token: Option<&'r str>) -> Self {
let mut res = self.clone();
res.user_auth = self.user_auth(auth_token).await;
res
}
}
impl juniper::Context for Context {}

View File

@ -1,31 +1,50 @@
use rocket::request::{FromRequest, Outcome, Request};
use rocket::response::content::RawHtml; use rocket::response::content::RawHtml;
use rocket::State; use rocket::State;
use tracing::debug;
use juniper::EmptySubscription; use juniper::EmptySubscription;
use juniper_rocket::{GraphQLRequest, GraphQLResponse}; use juniper_rocket::{GraphQLRequest, GraphQLResponse};
use crate::appwrite::APVariables; pub mod context;
use crate::db::Database; pub use context::Context;
#[derive(Default, Debug)]
pub struct Context {
pub db: Database,
pub appwrite: APVariables,
}
impl juniper::Context for Context {}
mod query;
use query::Query;
mod mutation; mod mutation;
use mutation::Mutation; mod query;
type Schema = #[derive(Copy, Clone, Debug)]
juniper::RootNode<'static, Query, Mutation, EmptySubscription<Context>>; pub struct UserAuth<'r>(Option<&'r str>);
#[derive(Debug)]
pub enum UserAuthError {}
#[rocket::async_trait]
impl<'r> FromRequest<'r> for UserAuth<'r> {
type Error = UserAuthError;
async fn from_request(
request: &'r Request<'_>,
) -> Outcome<Self, Self::Error> {
match request.headers().get_one("Authorization") {
None => Outcome::Success(UserAuth(None)),
Some(key) => Outcome::Success(UserAuth(Some(key))),
}
}
}
pub type Schema = juniper::RootNode<
'static,
query::Query,
mutation::Mutation,
EmptySubscription<Context>,
>;
pub fn create_schema() -> Schema { pub fn create_schema() -> Schema {
Schema::new(Query {}, Mutation {}, EmptySubscription::default()) Schema::new(
query::Query {},
mutation::Mutation {},
EmptySubscription::default(),
)
} }
#[rocket::get("/")] #[rocket::get("/")]
@ -37,18 +56,26 @@ pub fn graphiql() -> RawHtml<String> {
#[rocket::get("/graphql?<request>")] #[rocket::get("/graphql?<request>")]
pub async fn get_graphql_handler( pub async fn get_graphql_handler(
context: &State<Context>, context: &State<Context>,
user_auth: UserAuth<'_>,
request: GraphQLRequest, request: GraphQLRequest,
schema: &State<Schema>, schema: &State<Schema>,
) -> GraphQLResponse { ) -> GraphQLResponse {
request.execute(schema, context).await debug!("Current context: {:?}", context);
request
.execute(schema, &(*context).attach_auth(user_auth.0).await)
.await
} }
#[allow(clippy::needless_pass_by_value)] #[allow(clippy::needless_pass_by_value)]
#[rocket::post("/graphql", data = "<request>")] #[rocket::post("/graphql", data = "<request>")]
pub async fn post_graphql_handler( pub async fn post_graphql_handler(
context: &State<Context>, context: &State<Context>,
user_auth: UserAuth<'_>,
request: GraphQLRequest, request: GraphQLRequest,
schema: &State<Schema>, schema: &State<Schema>,
) -> GraphQLResponse { ) -> GraphQLResponse {
request.execute(schema, context).await debug!("Current context: {:?}", context);
request
.execute(schema, &(*context).attach_auth(user_auth.0).await)
.await
} }