Plume/plume-models/src/api_tokens.rs
2021-11-28 08:06:29 +09:00

114 lines
3.1 KiB
Rust

use crate::{db_conn::DbConn, schema::api_tokens, Error, Result};
use chrono::NaiveDateTime;
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
use rocket::{
http::Status,
request::{self, FromRequest, Request},
Outcome,
};
#[derive(Clone, Queryable)]
pub struct ApiToken {
pub id: i32,
pub creation_date: NaiveDateTime,
pub value: String,
/// Scopes, separated by +
/// Global scopes are read and write
/// and both can be limited to an endpoint by affixing them with :ENDPOINT
///
/// Examples :
///
/// read
/// read+write
/// read:posts
/// read:posts+write:posts
pub scopes: String,
pub app_id: i32,
pub user_id: i32,
}
#[derive(Insertable)]
#[table_name = "api_tokens"]
pub struct NewApiToken {
pub value: String,
pub scopes: String,
pub app_id: i32,
pub user_id: i32,
}
impl ApiToken {
get!(api_tokens);
insert!(api_tokens, NewApiToken);
find_by!(api_tokens, find_by_value, value as &str);
pub fn can(&self, what: &'static str, scope: &'static str) -> bool {
let full_scope = what.to_owned() + ":" + scope;
for s in self.scopes.split('+') {
if s == what || s == full_scope {
return true;
}
}
false
}
pub fn can_read(&self, scope: &'static str) -> bool {
self.can("read", scope)
}
pub fn can_write(&self, scope: &'static str) -> bool {
self.can("write", scope)
}
}
#[derive(Debug)]
pub enum TokenError {
/// The Authorization header was not present
NoHeader,
/// The type of the token was not specified ("Basic" or "Bearer" for instance)
NoType,
/// No value was provided
NoValue,
/// Error while connecting to the database to retrieve all the token metadata
DbError,
}
impl<'a, 'r> FromRequest<'a, 'r> for ApiToken {
type Error = TokenError;
fn from_request(request: &'a Request<'r>) -> request::Outcome<ApiToken, TokenError> {
let headers: Vec<_> = request.headers().get("Authorization").collect();
if headers.len() != 1 {
return Outcome::Failure((Status::BadRequest, TokenError::NoHeader));
}
let mut parsed_header = headers[0].split(' ');
let auth_type = parsed_header
.next()
.map_or_else::<rocket::Outcome<&str, _, ()>, _, _>(
|| Outcome::Failure((Status::BadRequest, TokenError::NoType)),
Outcome::Success,
)?;
let val = parsed_header
.next()
.map_or_else::<rocket::Outcome<&str, _, ()>, _, _>(
|| Outcome::Failure((Status::BadRequest, TokenError::NoValue)),
Outcome::Success,
)?;
if auth_type == "Bearer" {
let conn = request
.guard::<DbConn>()
.map_failure(|_| (Status::InternalServerError, TokenError::DbError))?;
if let Ok(token) = ApiToken::find_by_value(&*conn, val) {
return Outcome::Success(token);
}
}
Outcome::Forward(())
}
}