mirror of
https://github.com/meilisearch/MeiliSearch
synced 2024-11-26 06:44:27 +01:00
Authentication: is_index_authorized takes into account API key indexes even with a tenant token
This commit is contained in:
parent
4b65851793
commit
c8c5944094
@ -88,9 +88,11 @@ impl AuthController {
|
|||||||
let mut filters = AuthFilter::default();
|
let mut filters = AuthFilter::default();
|
||||||
let key = self.get_key(uid)?;
|
let key = self.get_key(uid)?;
|
||||||
|
|
||||||
|
filters.key_authorized_indexes = SearchRules::Set(key.indexes.into_iter().collect());
|
||||||
|
|
||||||
filters.search_rules = match search_rules {
|
filters.search_rules = match search_rules {
|
||||||
Some(search_rules) => search_rules,
|
Some(search_rules) => search_rules,
|
||||||
None => SearchRules::Set(key.indexes.into_iter().collect()),
|
None => filters.key_authorized_indexes.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
filters.allow_index_creation = self.is_key_authorized(uid, Action::IndexesAdd, None)?;
|
filters.allow_index_creation = self.is_key_authorized(uid, Action::IndexesAdd, None)?;
|
||||||
@ -160,13 +162,42 @@ impl AuthController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct AuthFilter {
|
pub struct AuthFilter {
|
||||||
pub search_rules: SearchRules,
|
search_rules: SearchRules,
|
||||||
|
key_authorized_indexes: SearchRules,
|
||||||
pub allow_index_creation: bool,
|
pub allow_index_creation: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AuthFilter {
|
impl Default for AuthFilter {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self { search_rules: SearchRules::default(), allow_index_creation: true }
|
Self {
|
||||||
|
search_rules: SearchRules::default(),
|
||||||
|
key_authorized_indexes: SearchRules::default(),
|
||||||
|
allow_index_creation: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuthFilter {
|
||||||
|
pub fn is_index_authorized(&self, index: &str) -> bool {
|
||||||
|
self.key_authorized_indexes.is_index_authorized(index)
|
||||||
|
&& self.search_rules.is_index_authorized(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_index_search_rules(&self, index: &str) -> Option<IndexSearchRules> {
|
||||||
|
if !self.is_index_authorized(index) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
self.search_rules.get_index_search_rules(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the list of indexes such that `self.is_index_authorized(index) == true`,
|
||||||
|
/// or `None` if all indexes satisfy this condition.
|
||||||
|
///
|
||||||
|
/// FIXME: this works only when there are no tenant tokens, otherwise it ignores the rules of the API key.
|
||||||
|
///
|
||||||
|
/// It is better to use `is_index_authorized` when possible.
|
||||||
|
pub fn authorized_indexes(&self) -> Option<Vec<IndexUidPattern>> {
|
||||||
|
self.search_rules.authorized_indexes()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,7 +216,7 @@ impl Default for SearchRules {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SearchRules {
|
impl SearchRules {
|
||||||
pub fn is_index_authorized(&self, index: &str) -> bool {
|
fn is_index_authorized(&self, index: &str) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Set(set) => {
|
Self::Set(set) => {
|
||||||
set.contains("*")
|
set.contains("*")
|
||||||
@ -200,7 +231,7 @@ impl SearchRules {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_index_search_rules(&self, index: &str) -> Option<IndexSearchRules> {
|
fn get_index_search_rules(&self, index: &str) -> Option<IndexSearchRules> {
|
||||||
match self {
|
match self {
|
||||||
Self::Set(_) => {
|
Self::Set(_) => {
|
||||||
if self.is_index_authorized(index) {
|
if self.is_index_authorized(index) {
|
||||||
@ -221,7 +252,7 @@ impl SearchRules {
|
|||||||
|
|
||||||
/// Return the list of indexes such that `self.is_index_authorized(index) == true`,
|
/// Return the list of indexes such that `self.is_index_authorized(index) == true`,
|
||||||
/// or `None` if all indexes satisfy this condition.
|
/// or `None` if all indexes satisfy this condition.
|
||||||
pub fn authorized_indexes(&self) -> Option<Vec<IndexUidPattern>> {
|
fn authorized_indexes(&self) -> Option<Vec<IndexUidPattern>> {
|
||||||
match self {
|
match self {
|
||||||
SearchRules::Set(set) => {
|
SearchRules::Set(set) => {
|
||||||
if set.contains("*") {
|
if set.contains("*") {
|
||||||
|
@ -9,7 +9,7 @@ use actix_web::HttpRequest;
|
|||||||
use byte_unit::Byte;
|
use byte_unit::Byte;
|
||||||
use http::header::CONTENT_TYPE;
|
use http::header::CONTENT_TYPE;
|
||||||
use index_scheduler::IndexScheduler;
|
use index_scheduler::IndexScheduler;
|
||||||
use meilisearch_auth::{AuthController, SearchRules};
|
use meilisearch_auth::{AuthController, AuthFilter};
|
||||||
use meilisearch_types::InstanceUid;
|
use meilisearch_types::InstanceUid;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
@ -401,7 +401,7 @@ impl Segment {
|
|||||||
auth_controller: AuthController,
|
auth_controller: AuthController,
|
||||||
) {
|
) {
|
||||||
if let Ok(stats) =
|
if let Ok(stats) =
|
||||||
create_all_stats(index_scheduler.into(), auth_controller, &SearchRules::default())
|
create_all_stats(index_scheduler.into(), auth_controller, &AuthFilter::default())
|
||||||
{
|
{
|
||||||
// Replace the version number with the prototype name if any.
|
// Replace the version number with the prototype name if any.
|
||||||
let version = if let Some(prototype) = crate::prototype_name() {
|
let version = if let Some(prototype) = crate::prototype_name() {
|
||||||
|
@ -89,11 +89,11 @@ pub async fn list_indexes(
|
|||||||
index_scheduler: GuardedData<ActionPolicy<{ actions::INDEXES_GET }>, Data<IndexScheduler>>,
|
index_scheduler: GuardedData<ActionPolicy<{ actions::INDEXES_GET }>, Data<IndexScheduler>>,
|
||||||
paginate: AwebQueryParameter<ListIndexes, DeserrQueryParamError>,
|
paginate: AwebQueryParameter<ListIndexes, DeserrQueryParamError>,
|
||||||
) -> Result<HttpResponse, ResponseError> {
|
) -> Result<HttpResponse, ResponseError> {
|
||||||
let search_rules = &index_scheduler.filters().search_rules;
|
let filters = index_scheduler.filters();
|
||||||
let indexes: Vec<_> = index_scheduler.indexes()?;
|
let indexes: Vec<_> = index_scheduler.indexes()?;
|
||||||
let indexes = indexes
|
let indexes = indexes
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|(name, _)| search_rules.is_index_authorized(name))
|
.filter(|(name, _)| filters.is_index_authorized(name))
|
||||||
.map(|(name, index)| IndexView::new(name, &index))
|
.map(|(name, index)| IndexView::new(name, &index))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
@ -120,7 +120,8 @@ pub async fn create_index(
|
|||||||
) -> Result<HttpResponse, ResponseError> {
|
) -> Result<HttpResponse, ResponseError> {
|
||||||
let IndexCreateRequest { primary_key, uid } = body.into_inner();
|
let IndexCreateRequest { primary_key, uid } = body.into_inner();
|
||||||
|
|
||||||
let allow_index_creation = index_scheduler.filters().search_rules.is_index_authorized(&uid);
|
// FIXME: allow_index_creation?
|
||||||
|
let allow_index_creation = index_scheduler.filters().is_index_authorized(&uid);
|
||||||
if allow_index_creation {
|
if allow_index_creation {
|
||||||
analytics.publish(
|
analytics.publish(
|
||||||
"Index Created".to_string(),
|
"Index Created".to_string(),
|
||||||
|
@ -159,9 +159,7 @@ pub async fn search_with_url_query(
|
|||||||
let mut query: SearchQuery = params.into_inner().into();
|
let mut query: SearchQuery = params.into_inner().into();
|
||||||
|
|
||||||
// Tenant token search_rules.
|
// Tenant token search_rules.
|
||||||
if let Some(search_rules) =
|
if let Some(search_rules) = index_scheduler.filters().get_index_search_rules(&index_uid) {
|
||||||
index_scheduler.filters().search_rules.get_index_search_rules(&index_uid)
|
|
||||||
{
|
|
||||||
add_search_rules(&mut query, search_rules);
|
add_search_rules(&mut query, search_rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,9 +191,7 @@ pub async fn search_with_post(
|
|||||||
debug!("search called with params: {:?}", query);
|
debug!("search called with params: {:?}", query);
|
||||||
|
|
||||||
// Tenant token search_rules.
|
// Tenant token search_rules.
|
||||||
if let Some(search_rules) =
|
if let Some(search_rules) = index_scheduler.filters().get_index_search_rules(&index_uid) {
|
||||||
index_scheduler.filters().search_rules.get_index_search_rules(&index_uid)
|
|
||||||
{
|
|
||||||
add_search_rules(&mut query, search_rules);
|
add_search_rules(&mut query, search_rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,10 +237,9 @@ async fn get_stats(
|
|||||||
analytics: web::Data<dyn Analytics>,
|
analytics: web::Data<dyn Analytics>,
|
||||||
) -> Result<HttpResponse, ResponseError> {
|
) -> Result<HttpResponse, ResponseError> {
|
||||||
analytics.publish("Stats Seen".to_string(), json!({ "per_index_uid": false }), Some(&req));
|
analytics.publish("Stats Seen".to_string(), json!({ "per_index_uid": false }), Some(&req));
|
||||||
let search_rules = &index_scheduler.filters().search_rules;
|
let filters = index_scheduler.filters();
|
||||||
|
|
||||||
let stats =
|
let stats = create_all_stats((*index_scheduler).clone(), (*auth_controller).clone(), filters)?;
|
||||||
create_all_stats((*index_scheduler).clone(), (*auth_controller).clone(), search_rules)?;
|
|
||||||
|
|
||||||
debug!("returns: {:?}", stats);
|
debug!("returns: {:?}", stats);
|
||||||
Ok(HttpResponse::Ok().json(stats))
|
Ok(HttpResponse::Ok().json(stats))
|
||||||
@ -249,19 +248,19 @@ async fn get_stats(
|
|||||||
pub fn create_all_stats(
|
pub fn create_all_stats(
|
||||||
index_scheduler: Data<IndexScheduler>,
|
index_scheduler: Data<IndexScheduler>,
|
||||||
auth_controller: AuthController,
|
auth_controller: AuthController,
|
||||||
search_rules: &meilisearch_auth::SearchRules,
|
filters: &meilisearch_auth::AuthFilter,
|
||||||
) -> Result<Stats, ResponseError> {
|
) -> Result<Stats, ResponseError> {
|
||||||
let mut last_task: Option<OffsetDateTime> = None;
|
let mut last_task: Option<OffsetDateTime> = None;
|
||||||
let mut indexes = BTreeMap::new();
|
let mut indexes = BTreeMap::new();
|
||||||
let mut database_size = 0;
|
let mut database_size = 0;
|
||||||
let processing_task = index_scheduler.get_tasks_from_authorized_indexes(
|
let processing_task = index_scheduler.get_tasks_from_authorized_indexes(
|
||||||
Query { statuses: Some(vec![Status::Processing]), limit: Some(1), ..Query::default() },
|
Query { statuses: Some(vec![Status::Processing]), limit: Some(1), ..Query::default() },
|
||||||
search_rules.authorized_indexes(),
|
filters.authorized_indexes(),
|
||||||
)?;
|
)?;
|
||||||
// accumulate the size of each indexes
|
// accumulate the size of each indexes
|
||||||
let processing_index = processing_task.first().and_then(|task| task.index_uid());
|
let processing_index = processing_task.first().and_then(|task| task.index_uid());
|
||||||
for (name, index) in index_scheduler.indexes()? {
|
for (name, index) in index_scheduler.indexes()? {
|
||||||
if !search_rules.is_index_authorized(&name) {
|
if !filters.is_index_authorized(&name) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ pub async fn swap_indexes(
|
|||||||
}),
|
}),
|
||||||
Some(&req),
|
Some(&req),
|
||||||
);
|
);
|
||||||
let search_rules = &index_scheduler.filters().search_rules;
|
let filters = index_scheduler.filters();
|
||||||
|
|
||||||
let mut swaps = vec![];
|
let mut swaps = vec![];
|
||||||
for SwapIndexesPayload { indexes } in params.into_iter() {
|
for SwapIndexesPayload { indexes } in params.into_iter() {
|
||||||
@ -53,7 +53,7 @@ pub async fn swap_indexes(
|
|||||||
return Err(MeilisearchHttpError::SwapIndexPayloadWrongLength(indexes).into());
|
return Err(MeilisearchHttpError::SwapIndexPayloadWrongLength(indexes).into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if !search_rules.is_index_authorized(lhs) || !search_rules.is_index_authorized(rhs) {
|
if !filters.is_index_authorized(lhs) || !filters.is_index_authorized(rhs) {
|
||||||
return Err(AuthenticationError::InvalidToken.into());
|
return Err(AuthenticationError::InvalidToken.into());
|
||||||
}
|
}
|
||||||
swaps.push(IndexSwap { indexes: (lhs.to_string(), rhs.to_string()) });
|
swaps.push(IndexSwap { indexes: (lhs.to_string(), rhs.to_string()) });
|
||||||
|
@ -319,7 +319,7 @@ async fn cancel_tasks(
|
|||||||
let tasks = index_scheduler.get_task_ids_from_authorized_indexes(
|
let tasks = index_scheduler.get_task_ids_from_authorized_indexes(
|
||||||
&index_scheduler.read_txn()?,
|
&index_scheduler.read_txn()?,
|
||||||
&query,
|
&query,
|
||||||
&index_scheduler.filters().search_rules.authorized_indexes(),
|
&index_scheduler.filters().authorized_indexes(),
|
||||||
)?;
|
)?;
|
||||||
let task_cancelation =
|
let task_cancelation =
|
||||||
KindWithContent::TaskCancelation { query: format!("?{}", req.query_string()), tasks };
|
KindWithContent::TaskCancelation { query: format!("?{}", req.query_string()), tasks };
|
||||||
@ -364,7 +364,7 @@ async fn delete_tasks(
|
|||||||
let tasks = index_scheduler.get_task_ids_from_authorized_indexes(
|
let tasks = index_scheduler.get_task_ids_from_authorized_indexes(
|
||||||
&index_scheduler.read_txn()?,
|
&index_scheduler.read_txn()?,
|
||||||
&query,
|
&query,
|
||||||
&index_scheduler.filters().search_rules.authorized_indexes(),
|
&index_scheduler.filters().authorized_indexes(),
|
||||||
)?;
|
)?;
|
||||||
let task_deletion =
|
let task_deletion =
|
||||||
KindWithContent::TaskDeletion { query: format!("?{}", req.query_string()), tasks };
|
KindWithContent::TaskDeletion { query: format!("?{}", req.query_string()), tasks };
|
||||||
@ -398,10 +398,7 @@ async fn get_tasks(
|
|||||||
let query = params.into_query();
|
let query = params.into_query();
|
||||||
|
|
||||||
let mut tasks_results: Vec<TaskView> = index_scheduler
|
let mut tasks_results: Vec<TaskView> = index_scheduler
|
||||||
.get_tasks_from_authorized_indexes(
|
.get_tasks_from_authorized_indexes(query, index_scheduler.filters().authorized_indexes())?
|
||||||
query,
|
|
||||||
index_scheduler.filters().search_rules.authorized_indexes(),
|
|
||||||
)?
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|t| TaskView::from_task(&t))
|
.map(|t| TaskView::from_task(&t))
|
||||||
.collect();
|
.collect();
|
||||||
@ -440,10 +437,7 @@ async fn get_task(
|
|||||||
let query = index_scheduler::Query { uids: Some(vec![task_uid]), ..Query::default() };
|
let query = index_scheduler::Query { uids: Some(vec![task_uid]), ..Query::default() };
|
||||||
|
|
||||||
if let Some(task) = index_scheduler
|
if let Some(task) = index_scheduler
|
||||||
.get_tasks_from_authorized_indexes(
|
.get_tasks_from_authorized_indexes(query, index_scheduler.filters().authorized_indexes())?
|
||||||
query,
|
|
||||||
index_scheduler.filters().search_rules.authorized_indexes(),
|
|
||||||
)?
|
|
||||||
.first()
|
.first()
|
||||||
{
|
{
|
||||||
let task_view = TaskView::from_task(task);
|
let task_view = TaskView::from_task(task);
|
||||||
|
Loading…
Reference in New Issue
Block a user