Merge pull request #464 from meilisearch/simplify-keys

Simplify keys & add launcher resume
This commit is contained in:
Clément Renault 2020-02-17 13:59:41 +01:00 committed by GitHub
commit 8e6560d102
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 274 additions and 368 deletions

76
Cargo.lock generated
View File

@ -153,6 +153,25 @@ name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "block-buffer"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "block-padding"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "broadcaster"
version = "0.2.6"
@ -182,6 +201,11 @@ name = "bumpalo"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byte-tools"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byteorder"
version = "1.3.2"
@ -414,6 +438,14 @@ name = "deunicode"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "digest"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "discard"
version = "1.0.4"
@ -470,6 +502,11 @@ dependencies = [
"synstructure 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "fnv"
version = "1.0.6"
@ -640,6 +677,14 @@ dependencies = [
"slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "generic-array"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "getopts"
version = "0.2.21"
@ -1043,6 +1088,7 @@ dependencies = [
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_qs 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"siphasher 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"sysinfo 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1202,6 +1248,11 @@ name = "once_cell"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "opaque-debug"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "ordered-float"
version = "1.0.2"
@ -1649,6 +1700,17 @@ name = "sha1"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "sha2"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "siphasher"
version = "0.3.1"
@ -2043,6 +2105,11 @@ name = "try-lock"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "typenum"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-bidi"
version = "0.3.4"
@ -2377,9 +2444,12 @@ dependencies = [
"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
"checksum bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8ab639324e3ee8774d296864fbc0dbbb256cf1a41c490b94cba90c082915f92"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
"checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
"checksum broadcaster 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "07a1446420a56f1030271649ba0da46d23239b3a68c73591cea5247f15a788a0"
"checksum bstr 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8d6c2c5b58ab920a4f5aeaaca34b4488074e8cc7596af94e6f8c6ff247c60245"
"checksum bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad807f2fc2bf185eeb98ff3a901bd46dc5ad58163d0fa4577ba0d25674d71708"
"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c"
"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
@ -2406,6 +2476,7 @@ dependencies = [
"checksum csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c"
"checksum data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4f47ca1860a761136924ddd2422ba77b2ea54fe8cc75b9040804a0d9d32ad97"
"checksum deunicode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca8a0f5bbdedde60605d0719b998e282af68e2b1c50203110211fe4abe857560"
"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
"checksum discard 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
"checksum doc-comment 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "923dea538cea0aa3025e8685b20d6ee21ef99c4f77e954a30febbaac5ec73a97"
"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
@ -2413,6 +2484,7 @@ dependencies = [
"checksum error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ab49e9dcb602294bc42f9a7dfc9bc6e936fca4418ea300dbfb84fe16de0b7d9"
"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9"
"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08"
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
"checksum fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5f2a4a2034423744d2cc7ca2068453168dcdb82c438419e639a26bd87839c674"
"checksum fst 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "927fb434ff9f0115b215dc0efd2e4fbdd7448522a92a1aa37c77d6a2f8f1ebd6"
@ -2435,6 +2507,7 @@ dependencies = [
"checksum futures-timer 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a1de7508b218029b0f01662ed8f61b1c964b3ae99d6f25462d0f55a595109df6"
"checksum futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c0d66274fb76985d3c62c886d1da7ac4c0903a8c9f754e8fe0f35a6a6cc39e76"
"checksum futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "5ce968633c17e5f97936bd2797b6e38fb56cf16a7422319f7ec2e30d3c470e8d"
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
"checksum getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407"
"checksum h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462"
@ -2484,6 +2557,7 @@ dependencies = [
"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32"
"checksum num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76dac5ed2a876980778b8b85f75a71b6cbf0db0b1232ee12f826bccb00d09d72"
"checksum once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "891f486f630e5c5a4916c7e16c4b24a53e78c860b646e9f8e005e4f16847bfed"
"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
"checksum ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518"
"checksum page_size 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f89ef58b3d32420dbd1a43d2f38ae92f6239ef12bb556ab09ca55445f5a67242"
"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252"
@ -2539,6 +2613,7 @@ dependencies = [
"checksum serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)" = "2f72eb2a68a7dc3f9a691bfda9305a1c017a6215e5a4545c258500d2099a37c2"
"checksum serde_qs 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "36278a86e341c46a42b0413ac3aa781902af93f5f4b10af098c704f4b96d81d8"
"checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
"checksum sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0"
"checksum siphasher 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "83da420ee8d1a89e640d0948c646c1c088758d3a3c538f943bfa97bdac17929d"
"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
"checksum slice-group-by 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1f7474f0b646d228360ab62ed974744617bc869d959eac8403bfa3665931a7fb"
@ -2579,6 +2654,7 @@ dependencies = [
"checksum tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f2106812d500ed25a4f38235b9cae8f78a09edf43203e16e59c3b769a342a60e"
"checksum toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "01d1404644c8b12b16bfcffa4322403a91a451584daaaa7c28d3152e6cbc98cf"
"checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382"
"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9"
"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
"checksum unicode-normalization 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "09c8070a9942f5e7cfccd93f490fdebd230ee3c3c9f107cb25bad5351ef671cf"
"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"

View File

@ -41,6 +41,7 @@ tide = "0.6.0"
ureq = { version = "0.11.2", features = ["tls"], default-features = false }
walkdir = "2.2.9"
whoami = "0.6"
sha2 = "0.8.1"
[dev-dependencies]
http-service-mock = "0.4.0"

View File

@ -7,6 +7,7 @@ use heed::types::{SerdeBincode, Str};
use log::error;
use meilisearch_core::{Database, Error as MError, MResult, MainT, UpdateT};
use sysinfo::Pid;
use sha2::Digest;
use crate::option::Opt;
use crate::routes::index::index_update_callback;
@ -32,10 +33,34 @@ impl Deref for Data {
pub struct DataInner {
pub db: Arc<Database>,
pub db_path: String,
pub api_key: Option<String>,
pub api_keys: ApiKeys,
pub server_pid: Pid,
}
#[derive(Default, Clone)]
pub struct ApiKeys {
pub public: Option<String>,
pub private: Option<String>,
pub master: Option<String>,
}
impl ApiKeys {
pub fn generate_missing_api_keys(&mut self) {
if let Some(master_key) = &self.master {
if self.private.is_none() {
let key = format!("{}-private", master_key);
let sha = sha2::Sha256::digest(key.as_bytes());
self.private = Some(format!("{:x}", sha));
}
if self.public.is_none() {
let key = format!("{}-public", master_key);
let sha = sha2::Sha256::digest(key.as_bytes());
self.public = Some(format!("{:x}", sha));
}
}
}
}
impl DataInner {
pub fn is_indexing(&self, reader: &heed::RoTxn<UpdateT>, index: &str) -> MResult<Option<bool>> {
match self.db.open_index(&index) {
@ -107,15 +132,22 @@ impl DataInner {
impl Data {
pub fn new(opt: Opt) -> Data {
let db_path = opt.db_path.clone();
let api_key = opt.api_key.clone();
let server_pid = sysinfo::get_current_pid().unwrap();
let db = Arc::new(Database::open_or_create(opt.db_path).unwrap());
let mut api_keys = ApiKeys {
master: opt.master_key.clone(),
private: None,
public: None,
};
api_keys.generate_missing_api_keys();
let inner_data = DataInner {
db: db.clone(),
db_path,
api_key,
api_keys,
server_pid,
};

View File

@ -1,14 +1,16 @@
use crate::error::{ResponseError, SResult};
use crate::models::token::*;
use crate::Data;
use chrono::Utc;
use heed::types::{SerdeBincode, Str};
use meilisearch_core::Index;
use tide::Request;
pub enum ACL {
Admin,
Private,
Public
}
pub trait RequestExt {
fn is_allowed(&self, acl: ACL) -> SResult<()>;
fn header(&self, name: &str) -> SResult<String>;
fn url_param(&self, name: &str) -> SResult<String>;
fn index(&self) -> SResult<Index>;
fn identifier(&self) -> SResult<String>;
@ -16,73 +18,36 @@ pub trait RequestExt {
impl RequestExt for Request<Data> {
fn is_allowed(&self, acl: ACL) -> SResult<()> {
let api_key = match &self.state().api_key {
Some(api_key) => api_key,
None => return Ok(()),
};
let user_api_key = self.header("X-Meili-API-Key");
let user_api_key = self
.header("X-Meili-API-Key")
.ok_or(ResponseError::missing_header("X-Meili-API-Key"))?;
if user_api_key == *api_key {
return Ok(());
}
let request_index: Option<String> = None; //self.param::<String>("index").ok();
let db = &self.state().db;
let reader = db.main_read_txn()?;
let token_key = format!("{}{}", TOKEN_PREFIX_KEY, user_api_key);
let token_config = db
.common_store()
.get::<_, Str, SerdeBincode<Token>>(&reader, &token_key)?
.ok_or(ResponseError::invalid_token(format!(
"Api key does not exist: {}",
user_api_key
)))?;
if token_config.revoked {
return Err(ResponseError::invalid_token("token revoked"));
}
if let Some(index) = request_index {
if !token_config
.indexes
.iter()
.any(|r| match_wildcard(&r, &index))
{
return Err(ResponseError::invalid_token(
"token is not allowed to access to this index",
));
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(())
}
}
}
if token_config.expires_at < Utc::now() {
return Err(ResponseError::invalid_token("token expired"));
}
if token_config.acl.contains(&ACL::All) {
return Ok(());
}
if !token_config.acl.contains(&acl) {
return Err(ResponseError::invalid_token("no permission"));
}
Ok(())
}
fn header(&self, name: &str) -> SResult<String> {
let header = self
.headers()
.get(name)
.ok_or(ResponseError::missing_header(name))?
.to_str()
.map_err(|_| ResponseError::missing_header("X-Meili-API-Key"))?
.to_string();
Ok(header)
Err(ResponseError::InvalidToken(user_api_key.unwrap_or("Need a token").to_owned()))
}
fn url_param(&self, name: &str) -> SResult<String> {

View File

@ -1,4 +1,3 @@
use std::env::VarError::NotPresent;
use std::{env, thread};
use async_std::task;
@ -19,20 +18,35 @@ mod analytics;
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
pub fn main() -> Result<(), MainError> {
env_logger::init();
let opt = Opt::from_args();
let data = Data::new(opt.clone());
if env::var("MEILI_NO_ANALYTICS") == Err(NotPresent) {
match opt.env.as_ref() {
"production" => {
if opt.master_key.is_none() {
return Err("In production mode, the environment variable MEILI_MASTER_KEY is mandatory".into());
}
env_logger::init();
},
"development" => {
env_logger::from_env(env_logger::Env::default().default_filter_or("info")).init();
},
_ => unreachable!(),
}
if !opt.no_analytics {
thread::spawn(analytics::analytics_sender);
}
let data = Data::new(opt.clone());
let data_cloned = data.clone();
data.db.set_update_callback(Box::new(move |name, status| {
index_update_callback(name, &data_cloned, status);
}));
print_launch_resume(&opt, &data);
let mut app = tide::with_state(data);
app.middleware(Cors::new());
@ -40,8 +54,48 @@ pub fn main() -> Result<(), MainError> {
routes::load_routes(&mut app);
info!("Server HTTP enabled");
task::block_on(app.listen(opt.http_addr))?;
Ok(())
}
pub fn print_launch_resume(opt: &Opt, data: &Data) {
let ascii_name = r#"
888b d888 d8b 888 d8b .d8888b. 888
8888b d8888 Y8P 888 Y8P d88P Y88b 888
88888b.d88888 888 Y88b. 888
888Y88888P888 .d88b. 888 888 888 "Y888b. .d88b. 8888b. 888d888 .d8888b 88888b.
888 Y888P 888 d8P Y8b 888 888 888 "Y88b. d8P Y8b "88b 888P" d88P" 888 "88b
888 Y8P 888 88888888 888 888 888 "888 88888888 .d888888 888 888 888 888
888 " 888 Y8b. 888 888 888 Y88b d88P Y8b. 888 888 888 Y88b. 888 888
888 888 "Y8888 888 888 888 "Y8888P" "Y8888 "Y888888 888 "Y8888P 888 888
"#;
println!("{}", ascii_name);
info!("Database path: {:?}", opt.db_path);
info!("Start server on: {:?}", opt.http_addr);
info!("Environment: {:?}", opt.env);
info!("Commit SHA: {:?}", env!("VERGEN_SHA").to_string());
info!("Build date: {:?}", env!("VERGEN_BUILD_TIMESTAMP").to_string());
info!("Package version: {:?}", env!("CARGO_PKG_VERSION").to_string());
if let Some(master_key) = &data.api_keys.master {
info!("Master Key: {:?}", master_key);
if let Some(private_key) = &data.api_keys.private {
info!("Private Key: {:?}", private_key);
}
if let Some(public_key) = &data.api_keys.public {
info!("Public Key: {:?}", public_key);
}
} else {
info!("No master key found; The server will have no securities.\
If you need some protection in development mode, please export a key. export MEILI_MASTER_KEY=xxx");
}
info!("If you need extra information; Please refer to the documentation: http://docs.meilisearch.com");
info!("If you want to support us or help us; Please consult our Github repo: http://github.com/meilisearch/meilisearch");
info!("If you want to contact us; Please chat with us on http://meilisearch.com or by email to bonjour@meilisearch.com");
}

View File

@ -1,2 +1 @@
pub mod token;
pub mod update_operation;

View File

@ -1,72 +0,0 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
pub const TOKEN_PREFIX_KEY: &str = "_token_";
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum ACL {
IndexesRead,
IndexesWrite,
DocumentsRead,
DocumentsWrite,
SettingsRead,
SettingsWrite,
Admin,
#[serde(rename = "*")]
All,
}
pub type Wildcard = String;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Token {
pub key: String,
pub description: String,
pub acl: Vec<ACL>,
pub indexes: Vec<Wildcard>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub expires_at: DateTime<Utc>,
pub revoked: bool,
}
fn cleanup_wildcard(input: &str) -> (bool, &str, bool) {
let first = input.chars().next().filter(|&c| c == '*').is_some();
let last = input.chars().last().filter(|&c| c == '*').is_some();
let bound_last = std::cmp::max(input.len().saturating_sub(last as usize), first as usize);
let output = input.get(first as usize..bound_last).unwrap();
(first, output, last)
}
pub fn match_wildcard(pattern: &str, input: &str) -> bool {
let (first, pattern, last) = cleanup_wildcard(pattern);
match (first, last) {
(false, false) => pattern == input,
(true, false) => input.ends_with(pattern),
(false, true) => input.starts_with(pattern),
(true, true) => input.contains(pattern),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_match_wildcard() {
assert!(match_wildcard("*", "qqq"));
assert!(match_wildcard("*", ""));
assert!(match_wildcard("*ab", "qqqab"));
assert!(match_wildcard("*ab*", "qqqabqq"));
assert!(match_wildcard("ab*", "abqqq"));
assert!(match_wildcard("**", "ab"));
assert!(match_wildcard("ab", "ab"));
assert!(match_wildcard("ab*", "ab"));
assert!(match_wildcard("*ab", "ab"));
assert!(match_wildcard("*ab*", "ab"));
assert!(match_wildcard("*😆*", "ab😆dsa"));
}
}

View File

@ -1,5 +1,7 @@
use structopt::StructOpt;
const POSSIBLE_ENV: [&str; 2] = ["development", "production"];
#[derive(Debug, Clone, StructOpt)]
pub struct Opt {
/// The destination where the database must be created.
@ -11,8 +13,15 @@ pub struct Opt {
pub http_addr: String,
/// The master key allowing you to do everything on the server.
#[structopt(long, env = "MEILI_API_KEY")]
pub api_key: Option<String>,
#[structopt(long, env = "MEILI_MASTER_KEY")]
pub master_key: Option<String>,
/// This environment variable must be set to `production` if your are running in production.
/// Could be `production` or `development`
/// - `production`: Force api keys
/// - `development`: Show logs in "info" mode + not mendatory to specify the api keys
#[structopt(long, env = "MEILI_ENV", default_value = "development", possible_values = &POSSIBLE_ENV)]
pub env: String,
/// Do not send analytics to Meili.
#[structopt(long, env = "MEILI_NO_ANALYTICS")]

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()?;

View File

@ -22,7 +22,8 @@ pub fn setup_server() -> Result<TestBackend<Service<Data>>, Box<dyn Error>> {
let opt = Opt {
db_path: tmp_dir.path().to_str().unwrap().to_string(),
http_addr: "127.0.0.1:7700".to_owned(),
api_key: None,
master_key: None,
env: "development".to_owned(),
no_analytics: true,
};