diff --git a/Cargo.lock b/Cargo.lock index 0a69b522b..aa6f99b8a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/meilisearch-http/Cargo.toml b/meilisearch-http/Cargo.toml index 54c983757..fccf67ebf 100644 --- a/meilisearch-http/Cargo.toml +++ b/meilisearch-http/Cargo.toml @@ -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" diff --git a/meilisearch-http/src/data.rs b/meilisearch-http/src/data.rs index 37227ef89..3cd6a514b 100644 --- a/meilisearch-http/src/data.rs +++ b/meilisearch-http/src/data.rs @@ -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, pub db_path: String, - pub api_key: Option, + pub api_keys: ApiKeys, pub server_pid: Pid, } +#[derive(Default, Clone)] +pub struct ApiKeys { + pub public: Option, + pub private: Option, + pub master: Option, +} + +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, index: &str) -> MResult> { 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, }; diff --git a/meilisearch-http/src/helpers/tide.rs b/meilisearch-http/src/helpers/tide.rs index 3091bb3b3..96003ce91 100644 --- a/meilisearch-http/src/helpers/tide.rs +++ b/meilisearch-http/src/helpers/tide.rs @@ -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; fn url_param(&self, name: &str) -> SResult; fn index(&self) -> SResult; fn identifier(&self) -> SResult; @@ -16,73 +18,36 @@ pub trait RequestExt { impl RequestExt for Request { 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 = None; //self.param::("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>(&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 { - 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 { diff --git a/meilisearch-http/src/models/mod.rs b/meilisearch-http/src/models/mod.rs index 5f26bab01..82e7e77c4 100644 --- a/meilisearch-http/src/models/mod.rs +++ b/meilisearch-http/src/models/mod.rs @@ -1,2 +1 @@ -pub mod token; pub mod update_operation; diff --git a/meilisearch-http/src/models/token.rs b/meilisearch-http/src/models/token.rs deleted file mode 100644 index b1d266735..000000000 --- a/meilisearch-http/src/models/token.rs +++ /dev/null @@ -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, - pub indexes: Vec, - pub created_at: DateTime, - pub updated_at: DateTime, - pub expires_at: DateTime, - 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")); - } -} diff --git a/meilisearch-http/src/option.rs b/meilisearch-http/src/option.rs index 828d68130..31cfab361 100644 --- a/meilisearch-http/src/option.rs +++ b/meilisearch-http/src/option.rs @@ -11,8 +11,8 @@ 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, + #[structopt(long, env = "MEILI_MASTER_KEY")] + pub master_key: Option, /// Do not send analytics to Meili. #[structopt(long, env = "MEILI_NO_ANALYTICS")] diff --git a/meilisearch-http/src/routes/document.rs b/meilisearch-http/src/routes/document.rs index 1114bfa12..1e70db296 100644 --- a/meilisearch-http/src/routes/document.rs +++ b/meilisearch-http/src/routes/document.rs @@ -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) -> SResult { - 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) -> SResult { - 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) -> SResult { - 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, is_partial: bool) -> SResult { - 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) -> SResult) -> SResult { - ctx.is_allowed(DocumentsWrite)?; + ctx.is_allowed(Private)?; let data: Vec = 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) -> SResult) -> SResult { - ctx.is_allowed(DocumentsWrite)?; + ctx.is_allowed(Private)?; let index = ctx.index()?; diff --git a/meilisearch-http/src/routes/health.rs b/meilisearch-http/src/routes/health.rs index a6dd761df..66a59627a 100644 --- a/meilisearch-http/src/routes/health.rs +++ b/meilisearch-http/src/routes/health.rs @@ -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}; diff --git a/meilisearch-http/src/routes/index.rs b/meilisearch-http/src/routes/index.rs index 14d55068c..eb12145d0 100644 --- a/meilisearch-http/src/routes/index.rs +++ b/meilisearch-http/src/routes/index.rs @@ -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) -> SResult { - 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) -> SResult { - 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) -> SResult { - ctx.is_allowed(IndexesWrite)?; + ctx.is_allowed(Private)?; let body = ctx .body_json::() @@ -201,7 +201,7 @@ struct UpdateIndexResponse { } pub async fn update_index(mut ctx: Request) -> SResult { - ctx.is_allowed(IndexesWrite)?; + ctx.is_allowed(Private)?; let body = ctx .body_json::() @@ -250,7 +250,7 @@ pub async fn update_index(mut ctx: Request) -> SResult { } pub async fn get_update_status(ctx: Request) -> SResult { - 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) -> SResult { } pub async fn get_all_updates_status(ctx: Request) -> SResult { - 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) -> SResult { } pub async fn delete_index(ctx: Request) -> SResult { - 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)?; diff --git a/meilisearch-http/src/routes/key.rs b/meilisearch-http/src/routes/key.rs index 0f56d4701..6ab85645b 100644 --- a/meilisearch-http/src/routes/key.rs +++ b/meilisearch-http/src/routes/key.rs @@ -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) -> SResult { 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 = Vec::new(); - - let iter = - common_store.prefix_iter::<_, Str, SerdeBincode>(&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) -> SResult { - 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>(&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, - indexes: Vec, - #[serde(with = "ts_seconds")] - expires_at: DateTime, -} - -pub async fn create(mut ctx: Request) -> SResult { - 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>( - &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, - acl: Option>, - indexes: Option>, - expires_at: Option>, - revoked: Option, -} - -pub async fn update(mut ctx: Request) -> SResult { - 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>(&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>(&mut writer, &token_key, &token_config)?; - writer.commit()?; - - Ok(tide::Response::new(200).body_json(&token_config).unwrap()) -} - -pub async fn delete(ctx: Request) -> SResult { - 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, + }))?) } diff --git a/meilisearch-http/src/routes/mod.rs b/meilisearch-http/src/routes/mod.rs index 608bc8a67..24e7f2203 100644 --- a/meilisearch-http/src/routes/mod.rs +++ b/meilisearch-http/src/routes/mod.rs @@ -118,13 +118,7 @@ pub fn load_routes(app: &mut tide::Server) { .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))) diff --git a/meilisearch-http/src/routes/search.rs b/meilisearch-http/src/routes/search.rs index 2ed423334..bfb8372aa 100644 --- a/meilisearch-http/src/routes/search.rs +++ b/meilisearch-http/src/routes/search.rs @@ -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) -> SResult { - // 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) -> SResult { - // ctx.is_allowed(DocumentsRead)?; + ctx.is_allowed(Public)?; let body = ctx .body_json::() .await diff --git a/meilisearch-http/src/routes/setting.rs b/meilisearch-http/src/routes/setting.rs index 2c3a69fee..277170fad 100644 --- a/meilisearch-http/src/routes/setting.rs +++ b/meilisearch-http/src/routes/setting.rs @@ -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) -> SResult { - 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) -> SResult { - 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) -> SResult { } pub async fn delete_all(ctx: Request) -> SResult { - 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) -> SResult { } pub async fn get_rules(ctx: Request) -> SResult { - 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) -> SResult { } pub async fn update_rules(mut ctx: Request) -> SResult { - ctx.is_allowed(SettingsWrite)?; + ctx.is_allowed(Private)?; let index = ctx.index()?; let ranking_rules: Option> = ctx.body_json().await.map_err(ResponseError::bad_request)?; @@ -190,7 +190,7 @@ pub async fn update_rules(mut ctx: Request) -> SResult { } pub async fn delete_rules(ctx: Request) -> SResult { - 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) -> SResult { } pub async fn get_distinct(ctx: Request) -> SResult { - 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) -> SResult { } pub async fn update_distinct(mut ctx: Request) -> SResult { - ctx.is_allowed(SettingsWrite)?; + ctx.is_allowed(Private)?; let index = ctx.index()?; let ranking_distinct: Option = ctx.body_json().await.map_err(ResponseError::bad_request)?; @@ -242,7 +242,7 @@ pub async fn update_distinct(mut ctx: Request) -> SResult { } pub async fn delete_distinct(ctx: Request) -> SResult { - 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) -> SResult { } pub async fn get_identifier(ctx: Request) -> SResult { - 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) -> SResult { } pub async fn get_searchable(ctx: Request) -> SResult { - 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) -> SResult { } pub async fn update_searchable(mut ctx: Request) -> SResult { - ctx.is_allowed(SettingsWrite)?; + ctx.is_allowed(Private)?; let index = ctx.index()?; let searchable_attributes: Option> = ctx.body_json().await.map_err(ResponseError::bad_request)?; @@ -310,7 +310,7 @@ pub async fn update_searchable(mut ctx: Request) -> SResult { } pub async fn delete_searchable(ctx: Request) -> SResult { - 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) -> SResult { } pub async fn displayed(ctx: Request) -> SResult { - 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) -> SResult { } pub async fn update_displayed(mut ctx: Request) -> SResult { - ctx.is_allowed(SettingsWrite)?; + ctx.is_allowed(Private)?; let index = ctx.index()?; let displayed_attributes: Option> = ctx.body_json().await.map_err(ResponseError::bad_request)?; @@ -368,7 +368,7 @@ pub async fn update_displayed(mut ctx: Request) -> SResult { } pub async fn delete_displayed(ctx: Request) -> SResult { - 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) -> SResult { } pub async fn get_index_new_fields(ctx: Request) -> SResult { - 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) -> SResult { } pub async fn update_index_new_fields(mut ctx: Request) -> SResult { - ctx.is_allowed(SettingsWrite)?; + ctx.is_allowed(Private)?; let index = ctx.index()?; let index_new_fields: Option = ctx.body_json().await.map_err(ResponseError::bad_request)?; diff --git a/meilisearch-http/src/routes/stats.rs b/meilisearch-http/src/routes/stats.rs index a158d16da..38e1b7c45 100644 --- a/meilisearch-http/src/routes/stats.rs +++ b/meilisearch-http/src/routes/stats.rs @@ -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)] diff --git a/meilisearch-http/src/routes/stop_words.rs b/meilisearch-http/src/routes/stop_words.rs index 898e4740b..f9d0154a7 100644 --- a/meilisearch-http/src/routes/stop_words.rs +++ b/meilisearch-http/src/routes/stop_words.rs @@ -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) -> SResult { - 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) -> SResult { } pub async fn update(mut ctx: Request) -> SResult { - ctx.is_allowed(SettingsRead)?; + ctx.is_allowed(Private)?; let index = ctx.index()?; let data: BTreeSet = ctx.body_json().await.map_err(ResponseError::bad_request)?; @@ -43,7 +43,7 @@ pub async fn update(mut ctx: Request) -> SResult { } pub async fn delete(ctx: Request) -> SResult { - ctx.is_allowed(SettingsRead)?; + ctx.is_allowed(Private)?; let index = ctx.index()?; let db = &ctx.state().db; diff --git a/meilisearch-http/src/routes/synonym.rs b/meilisearch-http/src/routes/synonym.rs index e407d1a61..60fca7e98 100644 --- a/meilisearch-http/src/routes/synonym.rs +++ b/meilisearch-http/src/routes/synonym.rs @@ -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) -> SResult { - 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) -> SResult { } pub async fn update(mut ctx: Request) -> SResult { - ctx.is_allowed(SettingsWrite)?; + ctx.is_allowed(Private)?; let data: BTreeMap> = ctx.body_json().await.map_err(ResponseError::bad_request)?; @@ -61,7 +61,7 @@ pub async fn update(mut ctx: Request) -> SResult { } pub async fn delete(ctx: Request) -> SResult { - ctx.is_allowed(SettingsWrite)?; + ctx.is_allowed(Private)?; let index = ctx.index()?; diff --git a/meilisearch-http/tests/common.rs b/meilisearch-http/tests/common.rs index 0211caa78..2f12d511e 100644 --- a/meilisearch-http/tests/common.rs +++ b/meilisearch-http/tests/common.rs @@ -22,7 +22,7 @@ pub fn setup_server() -> Result>, Box> { 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, no_analytics: true, };