introduce new key management

This commit is contained in:
qdequele 2020-02-06 15:41:11 +01:00
parent 5ac757a5fd
commit 257b7b4df4
No known key found for this signature in database
GPG key ID: B3F0A000EBF11745
18 changed files with 204 additions and 362 deletions

View file

@ -8,11 +8,11 @@ use tide::{Request, Response};
use crate::error::{ResponseError, SResult};
use crate::helpers::tide::RequestExt;
use crate::models::token::ACL::*;
use crate::helpers::tide::ACL::*;
use crate::Data;
pub async fn get_document(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(DocumentsRead)?;
ctx.is_allowed(Public)?;
let index = ctx.index()?;
@ -40,7 +40,7 @@ pub struct IndexUpdateResponse {
}
pub async fn delete_document(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(DocumentsWrite)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
let identifier = ctx.identifier()?;
@ -66,7 +66,7 @@ struct BrowseQuery {
}
pub async fn get_all_documents(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(DocumentsRead)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
let query: BrowseQuery = ctx.query().unwrap_or_default();
@ -125,7 +125,7 @@ struct UpdateDocumentsQuery {
}
async fn update_multiple_documents(mut ctx: Request<Data>, is_partial: bool) -> SResult<Response> {
ctx.is_allowed(DocumentsWrite)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
@ -178,7 +178,7 @@ pub async fn add_or_update_multiple_documents(ctx: Request<Data>) -> SResult<Res
}
pub async fn delete_multiple_documents(mut ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(DocumentsWrite)?;
ctx.is_allowed(Private)?;
let data: Vec<Value> = ctx.body_json().await.map_err(ResponseError::bad_request)?;
let index = ctx.index()?;
@ -204,7 +204,7 @@ pub async fn delete_multiple_documents(mut ctx: Request<Data>) -> SResult<Respon
}
pub async fn clear_all_documents(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(DocumentsWrite)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;

View file

@ -1,6 +1,6 @@
use crate::error::{ResponseError, SResult};
use crate::helpers::tide::RequestExt;
use crate::models::token::ACL::*;
use crate::helpers::tide::ACL::*;
use crate::Data;
use heed::types::{Str, Unit};

View file

@ -9,7 +9,7 @@ use tide::{Request, Response};
use crate::error::{IntoInternalError, ResponseError, SResult};
use crate::helpers::tide::RequestExt;
use crate::models::token::ACL::*;
use crate::helpers::tide::ACL::*;
use crate::Data;
fn generate_uid() -> String {
@ -22,7 +22,7 @@ fn generate_uid() -> String {
}
pub async fn list_indexes(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(IndexesRead)?;
ctx.is_allowed(Private)?;
let indexes_uids = ctx.state().db.indexes_uids();
@ -75,7 +75,7 @@ struct IndexResponse {
}
pub async fn get_index(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(IndexesRead)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
@ -122,7 +122,7 @@ struct IndexCreateResponse {
}
pub async fn create_index(mut ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(IndexesWrite)?;
ctx.is_allowed(Private)?;
let body = ctx
.body_json::<IndexCreateRequest>()
@ -201,7 +201,7 @@ struct UpdateIndexResponse {
}
pub async fn update_index(mut ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(IndexesWrite)?;
ctx.is_allowed(Private)?;
let body = ctx
.body_json::<UpdateIndexRequest>()
@ -250,7 +250,7 @@ pub async fn update_index(mut ctx: Request<Data>) -> SResult<Response> {
}
pub async fn get_update_status(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(IndexesRead)?;
ctx.is_allowed(Private)?;
let db = &ctx.state().db;
let reader = db.update_read_txn()?;
@ -273,7 +273,7 @@ pub async fn get_update_status(ctx: Request<Data>) -> SResult<Response> {
}
pub async fn get_all_updates_status(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(IndexesRead)?;
ctx.is_allowed(Private)?;
let db = &ctx.state().db;
let reader = db.update_read_txn()?;
let index = ctx.index()?;
@ -282,7 +282,7 @@ pub async fn get_all_updates_status(ctx: Request<Data>) -> SResult<Response> {
}
pub async fn delete_index(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(IndexesWrite)?;
ctx.is_allowed(Private)?;
let _ = ctx.index()?;
let index_uid = ctx.url_param("index")?;
ctx.state().db.delete_index(&index_uid)?;

View file

@ -1,172 +1,18 @@
use chrono::serde::ts_seconds;
use chrono::{DateTime, Utc};
use heed::types::{SerdeBincode, Str};
use rand::seq::SliceRandom;
use serde::{Deserialize, Serialize};
use tide::{Request, Response};
use crate::error::{ResponseError, SResult};
use serde_json::json;
use crate::error::SResult;
use crate::helpers::tide::RequestExt;
use crate::models::token::ACL::*;
use crate::models::token::*;
use crate::helpers::tide::ACL::*;
use crate::Data;
fn generate_api_key() -> String {
let mut rng = rand::thread_rng();
let sample = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
sample
.choose_multiple(&mut rng, 40)
.map(|c| *c as char)
.collect()
}
pub async fn list(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(Admin)?;
let db = &ctx.state().db;
let reader = db.main_read_txn()?;
let keys = &ctx.state().api_keys;
let common_store = db.common_store();
let mut response: Vec<Token> = Vec::new();
let iter =
common_store.prefix_iter::<_, Str, SerdeBincode<Token>>(&reader, TOKEN_PREFIX_KEY)?;
for result in iter {
let (_, token) = result?;
response.push(token);
}
Ok(tide::Response::new(200).body_json(&response).unwrap())
}
pub async fn get(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(Admin)?;
let request_key = ctx.url_param("key")?;
let db = &ctx.state().db;
let reader = db.main_read_txn()?;
let token_key = format!("{}{}", TOKEN_PREFIX_KEY, request_key);
let token_config = db
.common_store()
.get::<_, Str, SerdeBincode<Token>>(&reader, &token_key)?
.ok_or(ResponseError::not_found(format!(
"token key: {}",
token_key
)))?;
Ok(tide::Response::new(200).body_json(&token_config).unwrap())
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct CreatedRequest {
description: String,
acl: Vec<ACL>,
indexes: Vec<Wildcard>,
#[serde(with = "ts_seconds")]
expires_at: DateTime<Utc>,
}
pub async fn create(mut ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(Admin)?;
let data: CreatedRequest = ctx.body_json().await.map_err(ResponseError::bad_request)?;
let key = generate_api_key();
let token_key = format!("{}{}", TOKEN_PREFIX_KEY, key);
let token_definition = Token {
key,
description: data.description,
acl: data.acl,
indexes: data.indexes,
expires_at: data.expires_at,
created_at: Utc::now(),
updated_at: Utc::now(),
revoked: false,
};
let db = &ctx.state().db;
let mut writer = db.main_write_txn()?;
db.common_store().put::<_, Str, SerdeBincode<Token>>(
&mut writer,
&token_key,
&token_definition,
)?;
writer.commit()?;
Ok(tide::Response::new(201)
.body_json(&token_definition)
.unwrap())
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct UpdatedRequest {
description: Option<String>,
acl: Option<Vec<ACL>>,
indexes: Option<Vec<Wildcard>>,
expires_at: Option<DateTime<Utc>>,
revoked: Option<bool>,
}
pub async fn update(mut ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(Admin)?;
let request_key = ctx.url_param("key")?;
let data: UpdatedRequest = ctx.body_json().await.map_err(ResponseError::bad_request)?;
let db = &ctx.state().db;
let mut writer = db.main_write_txn()?;
let common_store = db.common_store();
let token_key = format!("{}{}", TOKEN_PREFIX_KEY, request_key);
let mut token_config = common_store
.get::<_, Str, SerdeBincode<Token>>(&writer, &token_key)?
.ok_or(ResponseError::not_found(format!(
"token key: {}",
token_key
)))?;
// apply the modifications
if let Some(description) = data.description {
token_config.description = description;
}
if let Some(acl) = data.acl {
token_config.acl = acl;
}
if let Some(indexes) = data.indexes {
token_config.indexes = indexes;
}
if let Some(expires_at) = data.expires_at {
token_config.expires_at = expires_at;
}
if let Some(revoked) = data.revoked {
token_config.revoked = revoked;
}
token_config.updated_at = Utc::now();
common_store.put::<_, Str, SerdeBincode<Token>>(&mut writer, &token_key, &token_config)?;
writer.commit()?;
Ok(tide::Response::new(200).body_json(&token_config).unwrap())
}
pub async fn delete(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(Admin)?;
let request_key = ctx.url_param("key")?;
let db = &ctx.state().db;
let mut writer = db.main_write_txn()?;
let common_store = db.common_store();
let token_key = format!("{}{}", TOKEN_PREFIX_KEY, request_key);
common_store.delete::<_, Str>(&mut writer, &token_key)?;
writer.commit()?;
Ok(tide::Response::new(204))
Ok(tide::Response::new(200)
.body_json(&json!({
"private": keys.private,
"public": keys.public,
}))?)
}

View file

@ -118,13 +118,7 @@ pub fn load_routes(app: &mut tide::Server<Data>) {
.get(|ctx| into_response(stats::index_stats(ctx)));
app.at("/keys/")
.get(|ctx| into_response(key::list(ctx)))
.post(|ctx| into_response(key::create(ctx)));
app.at("/keys/:key")
.get(|ctx| into_response(key::get(ctx)))
.put(|ctx| into_response(key::update(ctx)))
.delete(|ctx| into_response(key::delete(ctx)));
.get(|ctx| into_response(key::list(ctx)));
app.at("/health")
.get(|ctx| into_response(health::get_health(ctx)))

View file

@ -7,6 +7,7 @@ use rayon::iter::{IntoParallelIterator, ParallelIterator};
use serde::{Deserialize, Serialize};
use tide::{Request, Response};
use crate::helpers::tide::ACL::*;
use crate::error::{ResponseError, SResult};
use crate::helpers::meilisearch::{Error, IndexSearchExt, SearchHit};
use crate::helpers::tide::RequestExt;
@ -28,7 +29,7 @@ struct SearchQuery {
}
pub async fn search_with_url_query(ctx: Request<Data>) -> SResult<Response> {
// ctx.is_allowed(DocumentsRead)?;
ctx.is_allowed(Public)?;
let index = ctx.index()?;
let db = &ctx.state().db;
@ -143,7 +144,7 @@ struct SearchMultiBodyResponse {
}
pub async fn search_multi_index(mut ctx: Request<Data>) -> SResult<Response> {
// ctx.is_allowed(DocumentsRead)?;
ctx.is_allowed(Public)?;
let body = ctx
.body_json::<SearchMultiBody>()
.await

View file

@ -5,12 +5,12 @@ use tide::{Request, Response};
use crate::error::{ResponseError, SResult};
use crate::helpers::tide::RequestExt;
use crate::models::token::ACL::*;
use crate::helpers::tide::ACL::*;
use crate::routes::document::IndexUpdateResponse;
use crate::Data;
pub async fn get_all(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(SettingsRead)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
let db = &ctx.state().db;
let reader = db.main_read_txn()?;
@ -106,7 +106,7 @@ pub struct UpdateSettings {
}
pub async fn update_all(mut ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(SettingsWrite)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
let settings_update: UpdateSettings =
ctx.body_json().await.map_err(ResponseError::bad_request)?;
@ -131,7 +131,7 @@ pub async fn update_all(mut ctx: Request<Data>) -> SResult<Response> {
}
pub async fn delete_all(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(SettingsWrite)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
let db = &ctx.state().db;
let mut writer = db.update_write_txn()?;
@ -156,7 +156,7 @@ pub async fn delete_all(ctx: Request<Data>) -> SResult<Response> {
}
pub async fn get_rules(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(SettingsRead)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
let db = &ctx.state().db;
let reader = db.main_read_txn()?;
@ -170,7 +170,7 @@ pub async fn get_rules(ctx: Request<Data>) -> SResult<Response> {
}
pub async fn update_rules(mut ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(SettingsWrite)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
let ranking_rules: Option<Vec<String>> =
ctx.body_json().await.map_err(ResponseError::bad_request)?;
@ -190,7 +190,7 @@ pub async fn update_rules(mut ctx: Request<Data>) -> SResult<Response> {
}
pub async fn delete_rules(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(SettingsWrite)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
let db = &ctx.state().db;
let mut writer = db.update_write_txn()?;
@ -209,7 +209,7 @@ pub async fn delete_rules(ctx: Request<Data>) -> SResult<Response> {
}
pub async fn get_distinct(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(SettingsRead)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
let db = &ctx.state().db;
let reader = db.main_read_txn()?;
@ -222,7 +222,7 @@ pub async fn get_distinct(ctx: Request<Data>) -> SResult<Response> {
}
pub async fn update_distinct(mut ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(SettingsWrite)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
let ranking_distinct: Option<String> =
ctx.body_json().await.map_err(ResponseError::bad_request)?;
@ -242,7 +242,7 @@ pub async fn update_distinct(mut ctx: Request<Data>) -> SResult<Response> {
}
pub async fn delete_distinct(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(SettingsWrite)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
let db = &ctx.state().db;
let mut writer = db.update_write_txn()?;
@ -261,7 +261,7 @@ pub async fn delete_distinct(ctx: Request<Data>) -> SResult<Response> {
}
pub async fn get_identifier(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(SettingsRead)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
let db = &ctx.state().db;
let reader = db.main_read_txn()?;
@ -274,7 +274,7 @@ pub async fn get_identifier(ctx: Request<Data>) -> SResult<Response> {
}
pub async fn get_searchable(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(SettingsRead)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
let db = &ctx.state().db;
let reader = db.main_read_txn()?;
@ -290,7 +290,7 @@ pub async fn get_searchable(ctx: Request<Data>) -> SResult<Response> {
}
pub async fn update_searchable(mut ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(SettingsWrite)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
let searchable_attributes: Option<Vec<String>> =
ctx.body_json().await.map_err(ResponseError::bad_request)?;
@ -310,7 +310,7 @@ pub async fn update_searchable(mut ctx: Request<Data>) -> SResult<Response> {
}
pub async fn delete_searchable(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(SettingsWrite)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
let db = &ctx.state().db;
@ -328,7 +328,7 @@ pub async fn delete_searchable(ctx: Request<Data>) -> SResult<Response> {
}
pub async fn displayed(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(SettingsRead)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
let db = &ctx.state().db;
let reader = db.main_read_txn()?;
@ -348,7 +348,7 @@ pub async fn displayed(ctx: Request<Data>) -> SResult<Response> {
}
pub async fn update_displayed(mut ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(SettingsWrite)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
let displayed_attributes: Option<HashSet<String>> =
ctx.body_json().await.map_err(ResponseError::bad_request)?;
@ -368,7 +368,7 @@ pub async fn update_displayed(mut ctx: Request<Data>) -> SResult<Response> {
}
pub async fn delete_displayed(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(SettingsWrite)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
let db = &ctx.state().db;
@ -386,7 +386,7 @@ pub async fn delete_displayed(ctx: Request<Data>) -> SResult<Response> {
}
pub async fn get_index_new_fields(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(SettingsRead)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
let db = &ctx.state().db;
let reader = db.main_read_txn()?;
@ -401,7 +401,7 @@ pub async fn get_index_new_fields(ctx: Request<Data>) -> SResult<Response> {
}
pub async fn update_index_new_fields(mut ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(SettingsWrite)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
let index_new_fields: Option<bool> =
ctx.body_json().await.map_err(ResponseError::bad_request)?;

View file

@ -10,7 +10,7 @@ use walkdir::WalkDir;
use crate::error::{IntoInternalError, SResult};
use crate::helpers::tide::RequestExt;
use crate::models::token::ACL::*;
use crate::helpers::tide::ACL::*;
use crate::Data;
#[derive(Serialize)]

View file

@ -5,12 +5,12 @@ use tide::{Request, Response};
use crate::error::{ResponseError, SResult};
use crate::helpers::tide::RequestExt;
use crate::models::token::ACL::*;
use crate::helpers::tide::ACL::*;
use crate::routes::document::IndexUpdateResponse;
use crate::Data;
pub async fn get(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(SettingsRead)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
let db = &ctx.state().db;
let reader = db.main_read_txn()?;
@ -21,7 +21,7 @@ pub async fn get(ctx: Request<Data>) -> SResult<Response> {
}
pub async fn update(mut ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(SettingsRead)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
let data: BTreeSet<String> = ctx.body_json().await.map_err(ResponseError::bad_request)?;
@ -43,7 +43,7 @@ pub async fn update(mut ctx: Request<Data>) -> SResult<Response> {
}
pub async fn delete(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(SettingsRead)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
let db = &ctx.state().db;

View file

@ -6,12 +6,12 @@ use tide::{Request, Response};
use crate::error::{ResponseError, SResult};
use crate::helpers::tide::RequestExt;
use crate::models::token::ACL::*;
use crate::helpers::tide::ACL::*;
use crate::routes::document::IndexUpdateResponse;
use crate::Data;
pub async fn get(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(SettingsRead)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;
let db = &ctx.state().db;
@ -37,7 +37,7 @@ pub async fn get(ctx: Request<Data>) -> SResult<Response> {
}
pub async fn update(mut ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(SettingsWrite)?;
ctx.is_allowed(Private)?;
let data: BTreeMap<String, Vec<String>> =
ctx.body_json().await.map_err(ResponseError::bad_request)?;
@ -61,7 +61,7 @@ pub async fn update(mut ctx: Request<Data>) -> SResult<Response> {
}
pub async fn delete(ctx: Request<Data>) -> SResult<Response> {
ctx.is_allowed(SettingsWrite)?;
ctx.is_allowed(Private)?;
let index = ctx.index()?;