Add user cache, no more "guard refs".

This commit is contained in:
Paul Makles
2020-08-10 21:11:01 +02:00
parent cd5c7ce02c
commit 8cb697dfcd
11 changed files with 165 additions and 247 deletions

View File

@@ -1,8 +1,7 @@
use super::mutual::has_mutual_connection;
use crate::database::channel::Channel;
use crate::database::guild::{fetch_guild, fetch_member, Guild, Member, MemberKey};
use crate::database::user::UserRelationship;
use crate::guards::auth::UserRef;
use crate::database::user::{User, UserRelationship};
use num_enum::TryFromPrimitive;
@@ -77,23 +76,23 @@ pub fn get_relationship_internal(
Relationship::NONE
}
pub fn get_relationship(a: &UserRef, b: &UserRef) -> Relationship {
pub fn get_relationship(a: &User, b: &User) -> Relationship {
if a.id == b.id {
return Relationship::SELF;
}
get_relationship_internal(&a.id, &b.id, &a.fetch_relationships())
get_relationship_internal(&a.id, &b.id, &a.relations)
}
pub struct PermissionCalculator {
pub user: UserRef,
pub user: User,
pub channel: Option<Channel>,
pub guild: Option<Guild>,
pub member: Option<Member>,
}
impl PermissionCalculator {
pub fn new(user: UserRef) -> PermissionCalculator {
pub fn new(user: User) -> PermissionCalculator {
PermissionCalculator {
user,
channel: None,
@@ -174,9 +173,8 @@ impl PermissionCalculator {
}
if let Some(other) = other_user {
let relationships = self.user.fetch_relationships();
let relationship =
get_relationship_internal(&self.user.id, &other, &relationships);
get_relationship_internal(&self.user.id, &other, &self.user.relations);
if relationship == Relationship::Friend {
permissions = 1024 + 128 + 32 + 16 + 1;

View File

@@ -1,4 +1,11 @@
use mongodb::bson::DateTime;
use super::get_collection;
use lru::LruCache;
use mongodb::bson::{doc, from_bson, Bson, DateTime};
use rocket::http::{RawStr, Status};
use rocket::request::{self, FromParam, FromRequest, Request};
use rocket::Outcome;
use std::sync::{Arc, Mutex};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
@@ -28,3 +35,98 @@ pub struct User {
pub email_verification: UserEmailVerification,
pub relations: Option<Vec<UserRelationship>>,
}
lazy_static! {
static ref CACHE: Arc<Mutex<LruCache<String, User>>> =
Arc::new(Mutex::new(LruCache::new(4_000_000)));
}
pub fn fetch_user(id: &str) -> Result<Option<User>, String> {
{
if let Ok(mut cache) = CACHE.lock() {
let existing = cache.get(&id.to_string());
if let Some(user) = existing {
return Ok(Some((*user).clone()));
}
} else {
return Err("Failed to lock cache.".to_string());
}
}
let col = get_collection("users");
if let Ok(result) = col.find_one(doc! { "_id": id }, None) {
if let Some(doc) = result {
if let Ok(user) = from_bson(Bson::Document(doc)) as Result<User, _> {
let mut cache = CACHE.lock().unwrap();
cache.put(id.to_string(), user.clone());
Ok(Some(user))
} else {
Err("Failed to deserialize user!".to_string())
}
} else {
Ok(None)
}
} else {
Err("Failed to fetch user from database.".to_string())
}
}
#[derive(Debug)]
pub enum AuthError {
Failed,
Missing,
Invalid,
}
impl<'a, 'r> FromRequest<'a, 'r> for User {
type Error = AuthError;
fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
let u = request.headers().get("x-user").next();
let t = request.headers().get("x-auth-token").next();
if let Some(uid) = u {
if let Some(token) = t {
if let Ok(result) = fetch_user(uid) {
if let Some(user) = result {
if let Some(access_token) = &user.access_token {
if access_token == token {
Outcome::Success(user)
} else {
Outcome::Failure((Status::Forbidden, AuthError::Invalid))
}
} else {
Outcome::Failure((Status::Forbidden, AuthError::Invalid))
}
} else {
Outcome::Failure((Status::Forbidden, AuthError::Invalid))
}
} else {
Outcome::Failure((Status::Forbidden, AuthError::Failed))
}
} else {
Outcome::Failure((Status::Forbidden, AuthError::Missing))
}
} else {
Outcome::Failure((Status::Forbidden, AuthError::Missing))
}
}
}
impl<'r> FromParam<'r> for User {
type Error = &'r RawStr;
fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> {
if let Ok(result) = fetch_user(&param.to_string()) {
if let Some(user) = result {
Ok(user)
} else {
Err(param)
}
} else {
Err(param)
}
}
}