mirror of
https://github.com/meilisearch/meilisearch.git
synced 2024-11-26 20:15:07 +08:00
add authentication middleware
This commit is contained in:
parent
5fed155f15
commit
38d41252e6
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1505,6 +1505,7 @@ dependencies = [
|
|||||||
"actix-files",
|
"actix-files",
|
||||||
"actix-http",
|
"actix-http",
|
||||||
"actix-rt",
|
"actix-rt",
|
||||||
|
"actix-service",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"assert-json-diff",
|
"assert-json-diff",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
@ -46,6 +46,7 @@ actix-web = "2"
|
|||||||
actix-http = "1"
|
actix-http = "1"
|
||||||
actix-files = "0.2.1"
|
actix-files = "0.2.1"
|
||||||
actix-cors = "0.2.0"
|
actix-cors = "0.2.0"
|
||||||
|
actix-service = "1.0.5"
|
||||||
tokio = { version = "0.2.0", features = ["macros"] }
|
tokio = { version = "0.2.0", features = ["macros"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
@ -9,6 +9,7 @@ use serde_json::json;
|
|||||||
pub enum ResponseError {
|
pub enum ResponseError {
|
||||||
Internal(String),
|
Internal(String),
|
||||||
BadRequest(String),
|
BadRequest(String),
|
||||||
|
MissingAuthorizationHeader,
|
||||||
InvalidToken(String),
|
InvalidToken(String),
|
||||||
NotFound(String),
|
NotFound(String),
|
||||||
IndexNotFound(String),
|
IndexNotFound(String),
|
||||||
@ -27,6 +28,7 @@ impl fmt::Display for ResponseError {
|
|||||||
match self {
|
match self {
|
||||||
Self::Internal(err) => write!(f, "Internal server error: {}", err),
|
Self::Internal(err) => write!(f, "Internal server error: {}", err),
|
||||||
Self::BadRequest(err) => write!(f, "Bad request: {}", err),
|
Self::BadRequest(err) => write!(f, "Bad request: {}", err),
|
||||||
|
Self::MissingAuthorizationHeader => write!(f, "You must have an authorization token"),
|
||||||
Self::InvalidToken(err) => write!(f, "Invalid API key: {}", err),
|
Self::InvalidToken(err) => write!(f, "Invalid API key: {}", err),
|
||||||
Self::NotFound(err) => write!(f, "{} not found", err),
|
Self::NotFound(err) => write!(f, "{} not found", err),
|
||||||
Self::IndexNotFound(index_uid) => write!(f, "Index {} not found", index_uid),
|
Self::IndexNotFound(index_uid) => write!(f, "Index {} not found", index_uid),
|
||||||
@ -53,7 +55,8 @@ impl aweb::error::ResponseError for ResponseError {
|
|||||||
match *self {
|
match *self {
|
||||||
Self::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
Self::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
Self::BadRequest(_) => StatusCode::BAD_REQUEST,
|
Self::BadRequest(_) => StatusCode::BAD_REQUEST,
|
||||||
Self::InvalidToken(_) => StatusCode::FORBIDDEN,
|
Self::MissingAuthorizationHeader => StatusCode::FORBIDDEN,
|
||||||
|
Self::InvalidToken(_) => StatusCode::UNAUTHORIZED,
|
||||||
Self::NotFound(_) => StatusCode::NOT_FOUND,
|
Self::NotFound(_) => StatusCode::NOT_FOUND,
|
||||||
Self::IndexNotFound(_) => StatusCode::NOT_FOUND,
|
Self::IndexNotFound(_) => StatusCode::NOT_FOUND,
|
||||||
Self::DocumentNotFound(_) => StatusCode::NOT_FOUND,
|
Self::DocumentNotFound(_) => StatusCode::NOT_FOUND,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::{env, thread};
|
use std::{env, thread};
|
||||||
|
|
||||||
use actix_cors::Cors;
|
use actix_cors::Cors;
|
||||||
use actix_web::{web, App, HttpServer, middleware};
|
use actix_web::{middleware, web, App, HttpServer};
|
||||||
use log::info;
|
use log::info;
|
||||||
use main_error::MainError;
|
use main_error::MainError;
|
||||||
use meilisearch_http::data::Data;
|
use meilisearch_http::data::Data;
|
||||||
@ -54,15 +54,21 @@ async fn main() -> Result<(), MainError> {
|
|||||||
App::new()
|
App::new()
|
||||||
.wrap(
|
.wrap(
|
||||||
Cors::new()
|
Cors::new()
|
||||||
.send_wildcard()
|
.send_wildcard()
|
||||||
.allowed_header("x-meili-api-key")
|
.allowed_header("x-meili-api-key")
|
||||||
.finish()
|
.finish(),
|
||||||
)
|
)
|
||||||
.wrap(middleware::Logger::default())
|
.wrap(middleware::Logger::default())
|
||||||
.wrap(middleware::Compress::default())
|
.wrap(middleware::Compress::default())
|
||||||
.app_data(web::Data::new(data.clone()))
|
.app_data(web::Data::new(data.clone()))
|
||||||
|
.wrap(routes::Authentication::Public)
|
||||||
.service(routes::load_html)
|
.service(routes::load_html)
|
||||||
.service(routes::load_css)
|
.service(routes::load_css)
|
||||||
|
.service(routes::search::search_with_url_query)
|
||||||
|
.service(routes::search::search_multi_index)
|
||||||
|
.service(routes::document::get_document)
|
||||||
|
.service(routes::document::get_all_documents)
|
||||||
|
.wrap(routes::Authentication::Private)
|
||||||
.service(routes::index::list_indexes)
|
.service(routes::index::list_indexes)
|
||||||
.service(routes::index::get_index)
|
.service(routes::index::get_index)
|
||||||
.service(routes::index::create_index)
|
.service(routes::index::create_index)
|
||||||
@ -70,11 +76,7 @@ async fn main() -> Result<(), MainError> {
|
|||||||
.service(routes::index::delete_index)
|
.service(routes::index::delete_index)
|
||||||
.service(routes::index::get_update_status)
|
.service(routes::index::get_update_status)
|
||||||
.service(routes::index::get_all_updates_status)
|
.service(routes::index::get_all_updates_status)
|
||||||
.service(routes::search::search_with_url_query)
|
|
||||||
.service(routes::search::search_multi_index)
|
|
||||||
.service(routes::document::get_document)
|
|
||||||
.service(routes::document::delete_document)
|
.service(routes::document::delete_document)
|
||||||
.service(routes::document::get_all_documents)
|
|
||||||
.service(routes::document::add_documents)
|
.service(routes::document::add_documents)
|
||||||
.service(routes::document::update_documents)
|
.service(routes::document::update_documents)
|
||||||
.service(routes::document::delete_documents)
|
.service(routes::document::delete_documents)
|
||||||
@ -102,7 +104,6 @@ async fn main() -> Result<(), MainError> {
|
|||||||
.service(routes::synonym::get)
|
.service(routes::synonym::get)
|
||||||
.service(routes::synonym::update)
|
.service(routes::synonym::update)
|
||||||
.service(routes::synonym::delete)
|
.service(routes::synonym::delete)
|
||||||
.service(routes::key::list)
|
|
||||||
.service(routes::stats::index_stats)
|
.service(routes::stats::index_stats)
|
||||||
.service(routes::stats::get_stats)
|
.service(routes::stats::get_stats)
|
||||||
.service(routes::stats::get_version)
|
.service(routes::stats::get_version)
|
||||||
@ -110,6 +111,8 @@ async fn main() -> Result<(), MainError> {
|
|||||||
.service(routes::stats::get_sys_info_pretty)
|
.service(routes::stats::get_sys_info_pretty)
|
||||||
.service(routes::health::get_health)
|
.service(routes::health::get_health)
|
||||||
.service(routes::health::change_healthyness)
|
.service(routes::health::change_healthyness)
|
||||||
|
.wrap(routes::Authentication::Admin)
|
||||||
|
.service(routes::key::list)
|
||||||
})
|
})
|
||||||
.bind(opt.http_addr)?
|
.bind(opt.http_addr)?
|
||||||
.run()
|
.run()
|
||||||
|
@ -1,8 +1,17 @@
|
|||||||
|
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 actix_web::{get, HttpResponse};
|
use actix_web::{get, HttpResponse};
|
||||||
|
use futures::future::{err, ok, Future, Ready};
|
||||||
use log::error;
|
use log::error;
|
||||||
use meilisearch_core::ProcessedUpdateResult;
|
use meilisearch_core::ProcessedUpdateResult;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::error::ResponseError;
|
||||||
use crate::Data;
|
use crate::Data;
|
||||||
|
|
||||||
pub mod document;
|
pub mod document;
|
||||||
@ -79,109 +88,91 @@ pub fn index_update_callback(index_uid: &str, data: &Data, status: ProcessedUpda
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn load_routes(app: &mut tide::Server<Data>) {
|
#[derive(Clone)]
|
||||||
// app.at("/").get(|_| async {
|
pub enum Authentication {
|
||||||
// tide::Response::new(200)
|
Public,
|
||||||
// .body_string()
|
Private,
|
||||||
// .set_mime(mime::TEXT_HTML_UTF_8)
|
Admin,
|
||||||
// });
|
}
|
||||||
// app.at("/bulma.min.css").get(|_| async {
|
|
||||||
// tide::Response::new(200)
|
|
||||||
// .body_string(include_str!("../../public/bulma.min.css").to_string())
|
|
||||||
// .set_mime(mime::TEXT_CSS_UTF_8)
|
|
||||||
// });
|
|
||||||
|
|
||||||
// app.at("/indexes")
|
impl<S: 'static, B> Transform<S> for Authentication
|
||||||
// .get(|ctx| into_response(index::list_indexes(ctx)))
|
where
|
||||||
// .post(|ctx| into_response(index::create_index(ctx)));
|
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>>;
|
||||||
|
|
||||||
// app.at("/indexes/search")
|
fn new_transform(&self, service: S) -> Self::Future {
|
||||||
// .post(|ctx| into_response(search::search_multi_index(ctx)));
|
ok(LoggingMiddleware {
|
||||||
|
acl: (*self).clone(),
|
||||||
|
service: Rc::new(RefCell::new(service)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// app.at("/indexes/:index")
|
pub struct LoggingMiddleware<S> {
|
||||||
// .get(|ctx| into_response(index::get_index(ctx)))
|
acl: Authentication,
|
||||||
// .put(|ctx| into_response(index::update_index(ctx)))
|
service: Rc<RefCell<S>>,
|
||||||
// .delete(|ctx| into_response(index::delete_index(ctx)));
|
}
|
||||||
|
|
||||||
// app.at("/indexes/:index/search")
|
impl<S, B> Service for LoggingMiddleware<S>
|
||||||
// .get(|ctx| into_response(search::search_with_url_query(ctx)));
|
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>>>>;
|
||||||
|
|
||||||
// app.at("/indexes/:index/updates")
|
fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
|
||||||
// .get(|ctx| into_response(index::get_all_updates_status(ctx)));
|
self.service.poll_ready(cx)
|
||||||
|
}
|
||||||
|
|
||||||
// app.at("/indexes/:index/updates/:update_id")
|
fn call(&mut self, req: ServiceRequest) -> Self::Future {
|
||||||
// .get(|ctx| into_response(index::get_update_status(ctx)));
|
let mut svc = self.service.clone();
|
||||||
|
let data = req.app_data::<Data>().unwrap();
|
||||||
|
|
||||||
// app.at("/indexes/:index/documents")
|
if data.api_keys.master.is_none() {
|
||||||
// .get(|ctx| into_response(document::get_all_documents(ctx)))
|
return Box::pin(svc.call(req));
|
||||||
// .post(|ctx| into_response(document::add_or_replace_multiple_documents(ctx)))
|
}
|
||||||
// .put(|ctx| into_response(document::add_or_update_multiple_documents(ctx)))
|
|
||||||
// .delete(|ctx| into_response(document::clear_all_documents(ctx)));
|
|
||||||
|
|
||||||
// app.at("/indexes/:index/documents/:document_id")
|
let auth_header = match req.headers().get("X-Meili-API-Key") {
|
||||||
// .get(|ctx| into_response(document::get_document(ctx)))
|
Some(auth) => match auth.to_str() {
|
||||||
// .delete(|ctx| into_response(document::delete_document(ctx)));
|
Ok(auth) => auth,
|
||||||
|
Err(_) => return Box::pin(err(ResponseError::MissingAuthorizationHeader.into())),
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
return Box::pin(err(ResponseError::MissingAuthorizationHeader.into()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// app.at("/indexes/:index/documents/delete-batch")
|
let authenticated = match self.acl {
|
||||||
// .post(|ctx| into_response(document::delete_multiple_documents(ctx)));
|
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)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// app.at("/indexes/:index/settings")
|
if authenticated {
|
||||||
// .get(|ctx| into_response(setting::get_all(ctx)))
|
Box::pin(svc.call(req))
|
||||||
// .post(|ctx| into_response(setting::update_all(ctx)))
|
} else {
|
||||||
// .delete(|ctx| into_response(setting::delete_all(ctx)));
|
Box::pin(err(
|
||||||
|
ResponseError::InvalidToken(auth_header.to_string()).into()
|
||||||
// app.at("/indexes/:index/settings/ranking-rules")
|
))
|
||||||
// .get(|ctx| into_response(setting::get_rules(ctx)))
|
}
|
||||||
// .post(|ctx| into_response(setting::update_rules(ctx)))
|
}
|
||||||
// .delete(|ctx| into_response(setting::delete_rules(ctx)));
|
}
|
||||||
|
|
||||||
// app.at("/indexes/:index/settings/distinct-attribute")
|
|
||||||
// .get(|ctx| into_response(setting::get_distinct(ctx)))
|
|
||||||
// .post(|ctx| into_response(setting::update_distinct(ctx)))
|
|
||||||
// .delete(|ctx| into_response(setting::delete_distinct(ctx)));
|
|
||||||
|
|
||||||
// app.at("/indexes/:index/settings/searchable-attributes")
|
|
||||||
// .get(|ctx| into_response(setting::get_searchable(ctx)))
|
|
||||||
// .post(|ctx| into_response(setting::update_searchable(ctx)))
|
|
||||||
// .delete(|ctx| into_response(setting::delete_searchable(ctx)));
|
|
||||||
|
|
||||||
// app.at("/indexes/:index/settings/displayed-attributes")
|
|
||||||
// .get(|ctx| into_response(setting::displayed(ctx)))
|
|
||||||
// .post(|ctx| into_response(setting::update_displayed(ctx)))
|
|
||||||
// .delete(|ctx| into_response(setting::delete_displayed(ctx)));
|
|
||||||
|
|
||||||
// app.at("/indexes/:index/settings/accept-new-fields")
|
|
||||||
// .get(|ctx| into_response(setting::get_accept_new_fields(ctx)))
|
|
||||||
// .post(|ctx| into_response(setting::update_accept_new_fields(ctx)));
|
|
||||||
|
|
||||||
// app.at("/indexes/:index/settings/synonyms")
|
|
||||||
// .get(|ctx| into_response(synonym::get(ctx)))
|
|
||||||
// .post(|ctx| into_response(synonym::update(ctx)))
|
|
||||||
// .delete(|ctx| into_response(synonym::delete(ctx)));
|
|
||||||
|
|
||||||
// app.at("/indexes/:index/settings/stop-words")
|
|
||||||
// .get(|ctx| into_response(stop_words::get(ctx)))
|
|
||||||
// .post(|ctx| into_response(stop_words::update(ctx)))
|
|
||||||
// .delete(|ctx| into_response(stop_words::delete(ctx)));
|
|
||||||
|
|
||||||
// app.at("/indexes/:index/stats")
|
|
||||||
// .get(|ctx| into_response(stats::index_stats(ctx)));
|
|
||||||
|
|
||||||
// app.at("/keys").get(|ctx| into_response(key::list(ctx)));
|
|
||||||
|
|
||||||
// app.at("/health")
|
|
||||||
// .get(|ctx| into_response(health::get_health(ctx)))
|
|
||||||
// .put(|ctx| into_response(health::change_healthyness(ctx)));
|
|
||||||
|
|
||||||
// app.at("/stats")
|
|
||||||
// .get(|ctx| into_response(stats::get_stats(ctx)));
|
|
||||||
|
|
||||||
// app.at("/version")
|
|
||||||
// .get(|ctx| into_response(stats::get_version(ctx)));
|
|
||||||
|
|
||||||
// app.at("/sys-info")
|
|
||||||
// .get(|ctx| into_response(stats::get_sys_info(ctx)));
|
|
||||||
|
|
||||||
// app.at("/sys-info/pretty")
|
|
||||||
// .get(|ctx| into_response(stats::get_sys_info_pretty(ctx)));
|
|
||||||
// }
|
|
||||||
|
Loading…
Reference in New Issue
Block a user