mirror of
https://github.com/meilisearch/MeiliSearch
synced 2025-07-04 20:37:15 +02:00
prepare architecture for tests
This commit is contained in:
parent
38d41252e6
commit
5e2861ff55
7 changed files with 213 additions and 277 deletions
100
meilisearch-http/src/helpers/authentication.rs
Normal file
100
meilisearch-http/src/helpers/authentication.rs
Normal file
|
@ -0,0 +1,100 @@
|
|||
use std::cell::RefCell;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use actix_service::{Service, Transform};
|
||||
use actix_web::{dev::ServiceRequest, dev::ServiceResponse, Error};
|
||||
use futures::future::{err, ok, Future, Ready};
|
||||
|
||||
use crate::error::ResponseError;
|
||||
use crate::Data;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Authentication {
|
||||
Public,
|
||||
Private,
|
||||
Admin,
|
||||
}
|
||||
|
||||
impl<S: 'static, B> Transform<S> for Authentication
|
||||
where
|
||||
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
|
||||
S::Future: 'static,
|
||||
B: 'static,
|
||||
{
|
||||
type Request = ServiceRequest;
|
||||
type Response = ServiceResponse<B>;
|
||||
type Error = Error;
|
||||
type InitError = ();
|
||||
type Transform = LoggingMiddleware<S>;
|
||||
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
||||
|
||||
fn new_transform(&self, service: S) -> Self::Future {
|
||||
ok(LoggingMiddleware {
|
||||
acl: (*self).clone(),
|
||||
service: Rc::new(RefCell::new(service)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LoggingMiddleware<S> {
|
||||
acl: Authentication,
|
||||
service: Rc<RefCell<S>>,
|
||||
}
|
||||
|
||||
impl<S, B> Service for LoggingMiddleware<S>
|
||||
where
|
||||
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
|
||||
S::Future: 'static,
|
||||
B: 'static,
|
||||
{
|
||||
type Request = ServiceRequest;
|
||||
type Response = ServiceResponse<B>;
|
||||
type Error = Error;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
|
||||
self.service.poll_ready(cx)
|
||||
}
|
||||
|
||||
fn call(&mut self, req: ServiceRequest) -> Self::Future {
|
||||
let mut svc = self.service.clone();
|
||||
let data = req.app_data::<Data>().unwrap();
|
||||
|
||||
if data.api_keys.master.is_none() {
|
||||
return Box::pin(svc.call(req));
|
||||
}
|
||||
|
||||
let auth_header = match req.headers().get("X-Meili-API-Key") {
|
||||
Some(auth) => match auth.to_str() {
|
||||
Ok(auth) => auth,
|
||||
Err(_) => return Box::pin(err(ResponseError::MissingAuthorizationHeader.into())),
|
||||
},
|
||||
None => {
|
||||
return Box::pin(err(ResponseError::MissingAuthorizationHeader.into()));
|
||||
}
|
||||
};
|
||||
|
||||
let authenticated = match self.acl {
|
||||
Authentication::Admin => data.api_keys.master.as_deref() == Some(auth_header),
|
||||
Authentication::Private => {
|
||||
data.api_keys.master.as_deref() == Some(auth_header)
|
||||
|| data.api_keys.private.as_deref() == Some(auth_header)
|
||||
}
|
||||
Authentication::Public => {
|
||||
data.api_keys.master.as_deref() == Some(auth_header)
|
||||
|| data.api_keys.private.as_deref() == Some(auth_header)
|
||||
|| data.api_keys.public.as_deref() == Some(auth_header)
|
||||
}
|
||||
};
|
||||
|
||||
if authenticated {
|
||||
Box::pin(svc.call(req))
|
||||
} else {
|
||||
Box::pin(err(
|
||||
ResponseError::InvalidToken(auth_header.to_string()).into()
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,2 +1,4 @@
|
|||
pub mod authentication;
|
||||
pub mod meilisearch;
|
||||
// pub mod tide;
|
||||
|
||||
pub use authentication::Authentication;
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
use crate::error::{ResponseError, SResult};
|
||||
use crate::Data;
|
||||
use meilisearch_core::Index;
|
||||
use tide::Request;
|
||||
|
||||
pub enum ACL {
|
||||
Admin,
|
||||
Private,
|
||||
Public,
|
||||
}
|
||||
|
||||
pub trait RequestExt {
|
||||
fn is_allowed(&self, acl: ACL) -> SResult<()>;
|
||||
fn url_param(&self, name: &str) -> SResult<String>;
|
||||
fn index(&self) -> SResult<Index>;
|
||||
fn document_id(&self) -> SResult<String>;
|
||||
}
|
||||
|
||||
impl RequestExt for Request<Data> {
|
||||
fn is_allowed(&self, acl: ACL) -> SResult<()> {
|
||||
let user_api_key = self.header("X-Meili-API-Key");
|
||||
|
||||
if self.state().api_keys.master.is_none() {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
match acl {
|
||||
ACL::Admin => {
|
||||
if user_api_key == self.state().api_keys.master.as_deref() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
ACL::Private => {
|
||||
if user_api_key == self.state().api_keys.master.as_deref() {
|
||||
return Ok(());
|
||||
}
|
||||
if user_api_key == self.state().api_keys.private.as_deref() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
ACL::Public => {
|
||||
if user_api_key == self.state().api_keys.master.as_deref() {
|
||||
return Ok(());
|
||||
}
|
||||
if user_api_key == self.state().api_keys.private.as_deref() {
|
||||
return Ok(());
|
||||
}
|
||||
if user_api_key == self.state().api_keys.public.as_deref() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(ResponseError::InvalidToken(
|
||||
user_api_key.unwrap_or("Need a token").to_owned(),
|
||||
))
|
||||
}
|
||||
|
||||
fn url_param(&self, name: &str) -> SResult<String> {
|
||||
let param = self
|
||||
.param::<String>(name)
|
||||
.map_err(|e| ResponseError::bad_parameter(name, e))?;
|
||||
Ok(param)
|
||||
}
|
||||
|
||||
fn index(&self) -> SResult<Index> {
|
||||
let index_uid = self.url_param("index")?;
|
||||
let index = self
|
||||
.state()
|
||||
.db
|
||||
.open_index(&index_uid)
|
||||
.ok_or(ResponseError::index_not_found(index_uid))?;
|
||||
Ok(index)
|
||||
}
|
||||
|
||||
fn document_id(&self) -> SResult<String> {
|
||||
let name = self
|
||||
.param::<String>("document_id")
|
||||
.map_err(|_| ResponseError::bad_parameter("documentId", "primaryKey"))?;
|
||||
|
||||
Ok(name)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue