2584: Format API keys in hexa instead of base64 r=curquiza a=ManyTheFish

This PR:
- Changes API key generation and formatting to ease the generation of the key made by our users
- updates the `uuid` crate version

The API key can now be generated in bash as below:
```sh
echo -n $HYPHENATED_UUID | openssl dgst -sha256 -hmac $MASTER_KEY
```

fixes the issue raised in [product/discussion#421](https://github.com/meilisearch/product/discussions/421#discussioncomment-3079410), this should not impact anything in documentation nor integration but ease the key generation on the user sides.

poke `@gmourier` 

Co-authored-by: ManyTheFish <many@meilisearch.com>
This commit is contained in:
bors[bot] 2022-07-06 12:49:04 +00:00 committed by GitHub
commit 755b1a59a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 38 additions and 22 deletions

18
Cargo.lock generated
View File

@ -2003,7 +2003,6 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
name = "meilisearch-auth"
version = "0.28.0"
dependencies = [
"base64",
"enum-iterator",
"hmac",
"meilisearch-types",
@ -2014,7 +2013,7 @@ dependencies = [
"sha2",
"thiserror",
"time 0.3.9",
"uuid",
"uuid 1.1.2",
]
[[package]]
@ -2083,7 +2082,7 @@ dependencies = [
"tokio",
"tokio-stream",
"urlencoding",
"uuid",
"uuid 1.1.2",
"vergen",
"walkdir",
"yaup",
@ -2147,7 +2146,7 @@ dependencies = [
"thiserror",
"time 0.3.9",
"tokio",
"uuid",
"uuid 1.1.2",
"walkdir",
"whoami",
]
@ -2229,7 +2228,7 @@ dependencies = [
"tempfile",
"thiserror",
"time 0.3.9",
"uuid",
"uuid 0.8.2",
]
[[package]]
@ -3676,6 +3675,15 @@ name = "uuid"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
dependencies = [
"getrandom",
]
[[package]]
name = "uuid"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f"
dependencies = [
"getrandom",
"serde",

View File

@ -4,7 +4,6 @@ version = "0.28.0"
edition = "2021"
[dependencies]
base64 = "0.13.0"
enum-iterator = "0.7.0"
hmac = "0.12.1"
meilisearch-types = { path = "../meilisearch-types" }
@ -15,4 +14,4 @@ serde_json = { version = "1.0.79", features = ["preserve_order"] }
sha2 = "0.10.2"
thiserror = "1.0.30"
time = { version = "0.3.7", features = ["serde-well-known", "formatting", "parsing", "macros"] }
uuid = { version = "0.8.2", features = ["serde", "v4"] }
uuid = { version = "1.1.2", features = ["serde", "v4"] }

View File

@ -18,7 +18,7 @@ pub use action::{actions, Action};
use error::{AuthControllerError, Result};
pub use key::Key;
use meilisearch_types::star_or::StarOr;
use store::generate_key_as_base64;
use store::generate_key_as_hexa;
pub use store::open_auth_store_env;
use store::HeedAuthStore;
@ -139,7 +139,7 @@ impl AuthController {
pub fn generate_key(&self, uid: Uuid) -> Option<String> {
self.master_key
.as_ref()
.map(|master_key| generate_key_as_base64(uid.as_bytes(), master_key.as_bytes()))
.map(|master_key| generate_key_as_hexa(uid, master_key.as_bytes()))
}
/// Check if the provided key is authorized to make a specific action

View File

@ -13,8 +13,9 @@ use hmac::{Hmac, Mac};
use meilisearch_types::star_or::StarOr;
use milli::heed::types::{ByteSlice, DecodeIgnore, SerdeJson};
use milli::heed::{Database, Env, EnvOpenOptions, RwTxn};
use sha2::{Digest, Sha256};
use sha2::Sha256;
use time::OffsetDateTime;
use uuid::fmt::Hyphenated;
use uuid::Uuid;
use super::error::Result;
@ -132,13 +133,16 @@ impl HeedAuthStore {
.remap_data_type::<DecodeIgnore>()
.iter(&rtxn)?
.filter_map(|res| match res {
Ok((uid, _))
if generate_key_as_base64(uid, master_key).as_bytes() == encoded_key =>
{
Ok((uid, _)) => {
let (uid, _) = try_split_array_at(uid)?;
Some(Uuid::from_bytes(*uid))
let uid = Uuid::from_bytes(*uid);
if generate_key_as_hexa(uid, master_key).as_bytes() == encoded_key {
Some(uid)
} else {
None
}
}
_ => None,
Err(_) => None,
})
.next();
@ -244,13 +248,17 @@ impl<'a> milli::heed::BytesEncode<'a> for KeyIdActionCodec {
}
}
pub fn generate_key_as_base64(uid: &[u8], master_key: &[u8]) -> String {
let master_key_sha = Sha256::digest(master_key);
let mut mac = Hmac::<Sha256>::new_from_slice(master_key_sha.as_slice()).unwrap();
mac.update(uid);
pub fn generate_key_as_hexa(uid: Uuid, master_key: &[u8]) -> String {
// format uid as hyphenated allowing user to generate their own keys.
let mut uid_buffer = [0; Hyphenated::LENGTH];
let uid = uid.hyphenated().encode_lower(&mut uid_buffer);
// new_from_slice function never fail.
let mut mac = Hmac::<Sha256>::new_from_slice(master_key).unwrap();
mac.update(uid.as_bytes());
let result = mac.finalize();
base64::encode_config(result.into_bytes(), base64::URL_SAFE_NO_PAD)
format!("{:x}", result.into_bytes())
}
/// Divides one slice into two at an index, returns `None` if mid is out of bounds.

View File

@ -75,7 +75,7 @@ thiserror = "1.0.30"
time = { version = "0.3.7", features = ["serde-well-known", "formatting", "parsing", "macros"] }
tokio = { version = "1.17.0", features = ["full"] }
tokio-stream = "0.1.8"
uuid = { version = "0.8.2", features = ["serde", "v4"] }
uuid = { version = "1.1.2", features = ["serde", "v4"] }
walkdir = "2.3.2"
[dev-dependencies]

View File

@ -42,6 +42,7 @@ async fn add_valid_api_key() {
"name": "indexing-key",
"description": "Indexing API key",
"uid": "4bc0887a-0e41-4f3b-935d-0c451dcee9c8",
"key": "d9e776b8412f1db6974c9a5556b961c3559440b6588216f4ea5d9ed49f7c8f3c",
"indexes": ["products"],
"actions": [
"search",

View File

@ -51,7 +51,7 @@ tempfile = "3.3.0"
thiserror = "1.0.30"
time = { version = "0.3.7", features = ["serde-well-known", "formatting", "parsing", "macros"] }
tokio = { version = "1.17.0", features = ["full"] }
uuid = { version = "0.8.2", features = ["serde", "v4"] }
uuid = { version = "1.1.2", features = ["serde", "v4"] }
walkdir = "2.3.2"
whoami = { version = "1.2.1", optional = true }