Skip to content

Commit 78f08a3

Browse files
authored
Update tenant token api for v0.28.1 (#318)
* Update tenant token api for v0.28.1 * Fix clippy warning * Remove public on TenantTokenClaim
1 parent 70f5424 commit 78f08a3

File tree

6 files changed

+103
-43
lines changed

6 files changed

+103
-43
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ serde_json = "1.0"
2020
time = { version = "0.3.7", features = ["serde-well-known", "formatting", "parsing"] }
2121
jsonwebtoken = { version = "8", default-features = false }
2222
yaup = "0.2.0"
23+
uuid = { version = "1.1.2", features = ["v4"] }
2324

2425
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
2526
futures = "0.3"

src/client.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -806,19 +806,21 @@ impl Client {
806806
/// #
807807
/// # futures::executor::block_on(async move {
808808
/// # let client = client::Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY);
809-
/// let token = client.generate_tenant_token(serde_json::json!(["*"]), None, None).unwrap();
809+
/// let api_key_uid = "76cf8b87-fd12-4688-ad34-260d930ca4f4".to_string();
810+
/// let token = client.generate_tenant_token(api_key_uid, serde_json::json!(["*"]), None, None).unwrap();
810811
/// let client = client::Client::new(MEILISEARCH_HOST, token);
811812
/// # });
812813
/// ```
813814
pub fn generate_tenant_token(
814815
&self,
816+
api_key_uid: String,
815817
search_rules: serde_json::Value,
816818
api_key: Option<&str>,
817819
expires_at: Option<OffsetDateTime>,
818820
) -> Result<String, Error> {
819821
let api_key = api_key.unwrap_or(&self.api_key);
820822

821-
crate::tenant_tokens::generate_tenant_token(search_rules, api_key, expires_at)
823+
crate::tenant_tokens::generate_tenant_token(api_key_uid, search_rules, api_key, expires_at)
822824
}
823825
}
824826

src/errors.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ pub enum Error {
3636
HttpError(String),
3737
// The library formating the query parameters encountered an error.
3838
Yaup(yaup::Error),
39+
// The library validating the format of an uuid.
40+
Uuid(uuid::Error),
41+
// Error thrown in case the version of the Uuid is not v4.
42+
InvalidUuid4Version,
3943
}
4044

4145
#[derive(Debug, Clone, Deserialize)]
@@ -75,6 +79,12 @@ impl From<yaup::Error> for Error {
7579
}
7680
}
7781

82+
impl From<uuid::Error> for Error {
83+
fn from(error: uuid::Error) -> Error {
84+
Error::Uuid(error)
85+
}
86+
}
87+
7888
/// The type of error that was encountered.
7989
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
8090
#[serde(rename_all = "snake_case")]
@@ -194,7 +204,9 @@ impl std::fmt::Display for Error {
194204
Error::TenantTokensInvalidApiKey => write!(fmt, "The provided api_key is invalid."),
195205
Error::TenantTokensExpiredSignature => write!(fmt, "The provided expires_at is already expired."),
196206
Error::InvalidTenantToken(e) => write!(fmt, "Impossible to generate the token, jsonwebtoken encountered an error: {}", e),
197-
Error::Yaup(e) => write!(fmt, "Internal Error: could not parse the query parameters: {}", e)
207+
Error::Yaup(e) => write!(fmt, "Internal Error: could not parse the query parameters: {}", e),
208+
Error::Uuid(e) => write!(fmt, "The uid of the token has bit an uuid4 format: {}", e),
209+
Error::InvalidUuid4Version => write!(fmt, "The uid provided to the token is not of version uuidv4")
198210
}
199211
}
200212
}

src/indexes.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -370,10 +370,8 @@ impl Index {
370370

371371
request::<(), DocumentsResults<T>>(&url, &self.client.api_key, Method::Get(()), 200).await
372372
}
373+
373374
/// Get [Document]s by batch with parameters.
374-
///
375-
/// # Example
376-
///
377375
/// ```
378376
/// use serde::{Serialize, Deserialize};
379377
///
@@ -409,7 +407,6 @@ impl Index {
409407
pub async fn get_documents_with<T: DeserializeOwned + 'static>(
410408
&self,
411409
documents_query: &DocumentsQuery<'_>,
412-
) -> Result<DocumentsResults<T>, Error> {
413410
let url = format!("{}/indexes/{}/documents", self.client.host, self.uid);
414411
request::<&DocumentsQuery, DocumentsResults<T>>(
415412
&url,

src/search.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,6 @@ mod tests {
666666
query.with_highlight_post_tag(" ⊂(´• ω •`⊂)");
667667

668668
let results: SearchResults<Document> = index.execute_query(&query).await?;
669-
dbg!(&results);
670669
assert_eq!(
671670
&Document {
672671
id: 2,
@@ -784,9 +783,10 @@ mod tests {
784783

785784
for rules in search_rules {
786785
let token = allowed_client
787-
.generate_tenant_token(rules, None, None)
786+
.generate_tenant_token(key.uid.clone(), rules, None, None)
788787
.expect("Cannot generate tenant token.");
789-
let new_client = Client::new(meilisearch_host, token);
788+
789+
let new_client = Client::new(meilisearch_host, token.clone());
790790

791791
let result: SearchResults<Document> = new_client
792792
.index(index.uid.to_string())

src/tenant_tokens.rs

Lines changed: 81 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,41 @@
1-
use crate::{
2-
errors::*
3-
};
4-
use serde::{Serialize, Deserialize};
5-
use jsonwebtoken::{encode, Header, EncodingKey};
6-
use time::{OffsetDateTime};
1+
use crate::errors::*;
2+
use jsonwebtoken::{encode, EncodingKey, Header};
3+
use serde::{Deserialize, Serialize};
74
use serde_json::Value;
5+
use time::OffsetDateTime;
6+
use uuid::Uuid;
87

98
#[derive(Debug, Serialize, Deserialize)]
10-
#[serde(rename_all = "camelCase")]
9+
#[serde(rename_all = "camelCase")]
1110
struct TenantTokenClaim {
12-
api_key_prefix: String,
11+
api_key_uid: String,
1312
search_rules: Value,
1413
#[serde(with = "time::serde::timestamp::option")]
1514
exp: Option<OffsetDateTime>,
1615
}
1716

18-
pub fn generate_tenant_token(search_rules: Value, api_key: impl AsRef<str>, expires_at: Option<OffsetDateTime>) -> Result<String, Error> {
19-
if api_key.as_ref().chars().count() < 8 {
20-
return Err(Error::TenantTokensInvalidApiKey)
17+
pub fn generate_tenant_token(
18+
api_key_uid: String,
19+
search_rules: Value,
20+
api_key: impl AsRef<str>,
21+
expires_at: Option<OffsetDateTime>,
22+
) -> Result<String, Error> {
23+
// Validate uuid format
24+
let uid = Uuid::try_parse(&api_key_uid)?;
25+
26+
// Validate uuid version
27+
if uid.get_version_num() != 4 {
28+
return Err(Error::InvalidUuid4Version);
2129
}
2230

2331
if expires_at.map_or(false, |expires_at| OffsetDateTime::now_utc() > expires_at) {
24-
return Err(Error::TenantTokensExpiredSignature)
32+
return Err(Error::TenantTokensExpiredSignature);
2533
}
2634

27-
let key_prefix = api_key.as_ref().chars().take(8).collect();
2835
let claims = TenantTokenClaim {
29-
api_key_prefix: key_prefix,
36+
api_key_uid,
3037
exp: expires_at,
31-
search_rules
38+
search_rules,
3239
};
3340

3441
let token = encode(
@@ -42,9 +49,9 @@ pub fn generate_tenant_token(search_rules: Value, api_key: impl AsRef<str>, expi
4249

4350
#[cfg(test)]
4451
mod tests {
45-
use serde_json::json;
4652
use crate::tenant_tokens::*;
47-
use jsonwebtoken::{decode, DecodingKey, Validation, Algorithm};
53+
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
54+
use serde_json::json;
4855
use std::collections::HashSet;
4956

5057
const SEARCH_RULES: [&str; 1] = ["*"];
@@ -60,68 +67,109 @@ mod tests {
6067

6168
#[test]
6269
fn test_generate_token_with_given_key() {
63-
let token = generate_tenant_token(json!(SEARCH_RULES), VALID_KEY, None).unwrap();
70+
let api_key_uid = "76cf8b87-fd12-4688-ad34-260d930ca4f4".to_string();
71+
let token =
72+
generate_tenant_token(api_key_uid, json!(SEARCH_RULES), VALID_KEY, None).unwrap();
6473

6574
let valid_key = decode::<TenantTokenClaim>(
66-
&token, &DecodingKey::from_secret(VALID_KEY.as_ref()), &build_validation()
75+
&token,
76+
&DecodingKey::from_secret(VALID_KEY.as_ref()),
77+
&build_validation(),
6778
);
6879
let invalid_key = decode::<TenantTokenClaim>(
69-
&token, &DecodingKey::from_secret("not-the-same-key".as_ref()), &build_validation()
80+
&token,
81+
&DecodingKey::from_secret("not-the-same-key".as_ref()),
82+
&build_validation(),
7083
);
7184

7285
assert!(valid_key.is_ok());
7386
assert!(invalid_key.is_err());
7487
}
7588

7689
#[test]
77-
fn test_generate_token_without_key() {
90+
fn test_generate_token_without_uid() {
91+
let api_key_uid = "".to_string();
7892
let key = String::from("");
79-
let token = generate_tenant_token(json!(SEARCH_RULES), &key, None);
93+
let token = generate_tenant_token(api_key_uid, json!(SEARCH_RULES), &key, None);
8094

8195
assert!(token.is_err());
8296
}
8397

8498
#[test]
8599
fn test_generate_token_with_expiration() {
100+
let api_key_uid = "76cf8b87-fd12-4688-ad34-260d930ca4f4".to_string();
86101
let exp = OffsetDateTime::now_utc() + time::Duration::HOUR;
87-
let token = generate_tenant_token(json!(SEARCH_RULES), VALID_KEY, Some(exp)).unwrap();
102+
let token =
103+
generate_tenant_token(api_key_uid, json!(SEARCH_RULES), VALID_KEY, Some(exp)).unwrap();
88104

89105
let decoded = decode::<TenantTokenClaim>(
90-
&token, &DecodingKey::from_secret(VALID_KEY.as_ref()), &Validation::new(Algorithm::HS256)
106+
&token,
107+
&DecodingKey::from_secret(VALID_KEY.as_ref()),
108+
&Validation::new(Algorithm::HS256),
91109
);
92110

93111
assert!(decoded.is_ok());
94112
}
95113

96114
#[test]
97115
fn test_generate_token_with_expires_at_in_the_past() {
116+
let api_key_uid = "76cf8b87-fd12-4688-ad34-260d930ca4f4".to_string();
98117
let exp = OffsetDateTime::now_utc() - time::Duration::HOUR;
99-
let token = generate_tenant_token(json!(SEARCH_RULES), VALID_KEY, Some(exp));
118+
let token = generate_tenant_token(api_key_uid, json!(SEARCH_RULES), VALID_KEY, Some(exp));
100119

101120
assert!(token.is_err());
102121
}
103122

104123
#[test]
105124
fn test_generate_token_contains_claims() {
106-
let token = generate_tenant_token(json!(SEARCH_RULES), VALID_KEY, None).unwrap();
125+
let api_key_uid = "76cf8b87-fd12-4688-ad34-260d930ca4f4".to_string();
126+
let token =
127+
generate_tenant_token(api_key_uid.clone(), json!(SEARCH_RULES), VALID_KEY, None)
128+
.unwrap();
107129

108130
let decoded = decode::<TenantTokenClaim>(
109-
&token, &DecodingKey::from_secret(VALID_KEY.as_ref()), &build_validation()
110-
).expect("Cannot decode the token");
131+
&token,
132+
&DecodingKey::from_secret(VALID_KEY.as_ref()),
133+
&build_validation(),
134+
)
135+
.expect("Cannot decode the token");
111136

112-
assert_eq!(decoded.claims.api_key_prefix, &VALID_KEY[..8]);
137+
assert_eq!(decoded.claims.api_key_uid, api_key_uid);
113138
assert_eq!(decoded.claims.search_rules, json!(SEARCH_RULES));
114139
}
115140

116141
#[test]
117142
fn test_generate_token_with_multi_byte_chars() {
143+
let api_key_uid = "76cf8b87-fd12-4688-ad34-260d930ca4f4".to_string();
118144
let key = "Ëa1ทt9bVcL-vãUทtP3OpXW5qPc%bWH5ทvw09";
119-
let token = generate_tenant_token(json!(SEARCH_RULES), key, None).unwrap();
145+
let token =
146+
generate_tenant_token(api_key_uid.clone(), json!(SEARCH_RULES), key, None).unwrap();
120147

121148
let decoded = decode::<TenantTokenClaim>(
122-
&token, &DecodingKey::from_secret(key.as_ref()), &build_validation()
123-
).expect("Cannot decode the token");
149+
&token,
150+
&DecodingKey::from_secret(key.as_ref()),
151+
&build_validation(),
152+
)
153+
.expect("Cannot decode the token");
154+
155+
assert_eq!(decoded.claims.api_key_uid, api_key_uid);
156+
}
157+
158+
#[test]
159+
fn test_generate_token_with_wrongly_formated_uid() {
160+
let api_key_uid = "xxx".to_string();
161+
let key = "Ëa1ทt9bVcL-vãUทtP3OpXW5qPc%bWH5ทvw09";
162+
let token = generate_tenant_token(api_key_uid.clone(), json!(SEARCH_RULES), key, None);
124163

125-
assert_eq!(decoded.claims.api_key_prefix, "Ëa1ทt9bV");
164+
assert!(token.is_err());
165+
}
166+
167+
#[test]
168+
fn test_generate_token_with_wrong_uid_version() {
169+
let api_key_uid = "6a11eb96-2485-11ed-861d-0242ac120002".to_string();
170+
let key = "Ëa1ทt9bVcL-vãUทtP3OpXW5qPc%bWH5ทvw09";
171+
let token = generate_tenant_token(api_key_uid.clone(), json!(SEARCH_RULES), key, None);
172+
173+
assert!(token.is_err());
126174
}
127175
}

0 commit comments

Comments
 (0)