2173: chore(all): replace chrono with time r=irevoire a=irevoire

Chrono has been unmaintained for a few month now and there is a CVE on it.

Also I updated all the error messages related to the API key as you can see here: https://github.com/meilisearch/specifications/pull/114

fix #2172

Co-authored-by: Irevoire <tamo@meilisearch.com>
This commit is contained in:
bors[bot] 2022-02-17 14:12:23 +00:00 committed by GitHub
commit c3e3c900f2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 369 additions and 226 deletions

View file

@ -32,7 +32,6 @@ async-trait = "0.1.51"
bstr = "0.2.17"
byte-unit = { version = "4.0.12", default-features = false, features = ["std", "serde"] }
bytes = "1.1.0"
chrono = { version = "0.4.19", features = ["serde"] }
crossbeam-channel = "0.5.1"
either = "1.6.1"
env_logger = "0.9.0"
@ -43,6 +42,7 @@ futures-util = "0.3.17"
heed = { git = "https://github.com/Kerollmops/heed", tag = "v0.12.1" }
http = "0.2.4"
indexmap = { version = "1.7.0", features = ["serde-1"] }
iso8601-duration = "0.1.0"
itertools = "0.10.1"
jsonwebtoken = "7"
log = "0.4.14"
@ -61,7 +61,7 @@ rayon = "1.5.1"
regex = "1.5.4"
rustls = "0.20.2"
rustls-pemfile = "0.2"
segment = { version = "0.1.2", optional = true }
segment = { version = "0.2.0", optional = true }
serde = { version = "1.0.130", features = ["derive"] }
serde_json = { version = "1.0.67", features = ["preserve_order"] }
sha2 = "0.9.6"
@ -73,6 +73,7 @@ sysinfo = "0.20.2"
tar = "0.4.37"
tempfile = "3.2.0"
thiserror = "1.0.28"
time = { version = "0.3.7", features = ["serde-well-known", "formatting", "parsing", "macros"] }
tokio = { version = "1.11.0", features = ["full"] }
tokio-stream = "0.1.7"
uuid = { version = "0.8.2", features = ["serde"] }

View file

@ -6,7 +6,6 @@ use std::time::{Duration, Instant};
use actix_web::http::header::USER_AGENT;
use actix_web::HttpRequest;
use chrono::{DateTime, Utc};
use http::header::CONTENT_TYPE;
use meilisearch_auth::SearchRules;
use meilisearch_lib::index::{SearchQuery, SearchResult};
@ -18,6 +17,7 @@ use segment::message::{Identify, Track, User};
use segment::{AutoBatcher, Batcher, HttpClient};
use serde_json::{json, Value};
use sysinfo::{DiskExt, System, SystemExt};
use time::OffsetDateTime;
use tokio::select;
use tokio::sync::mpsc::{self, Receiver, Sender};
use uuid::Uuid;
@ -323,7 +323,7 @@ impl Segment {
#[derive(Default)]
pub struct SearchAggregator {
timestamp: Option<DateTime<Utc>>,
timestamp: Option<OffsetDateTime>,
// context
user_agents: HashSet<String>,
@ -360,7 +360,7 @@ pub struct SearchAggregator {
impl SearchAggregator {
pub fn from_query(query: &SearchQuery, request: &HttpRequest) -> Self {
let mut ret = Self::default();
ret.timestamp = Some(chrono::offset::Utc::now());
ret.timestamp = Some(OffsetDateTime::now_utc());
ret.total_received = 1;
ret.user_agents = extract_user_agents(request).into_iter().collect();
@ -504,7 +504,7 @@ impl SearchAggregator {
#[derive(Default)]
pub struct DocumentsAggregator {
timestamp: Option<DateTime<Utc>>,
timestamp: Option<OffsetDateTime>,
// set to true when at least one request was received
updated: bool,
@ -524,7 +524,7 @@ impl DocumentsAggregator {
request: &HttpRequest,
) -> Self {
let mut ret = Self::default();
ret.timestamp = Some(chrono::offset::Utc::now());
ret.timestamp = Some(OffsetDateTime::now_utc());
ret.updated = true;
ret.user_agents = extract_user_agents(request).into_iter().collect();

View file

@ -94,10 +94,10 @@ pub trait Policy {
}
pub mod policies {
use chrono::Utc;
use jsonwebtoken::{dangerous_insecure_decode, decode, Algorithm, DecodingKey, Validation};
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use crate::extractors::authentication::Policy;
use meilisearch_auth::{Action, AuthController, AuthFilter, SearchRules};
@ -183,7 +183,7 @@ pub mod policies {
// Check if token is expired.
if let Some(exp) = exp {
if Utc::now().timestamp() > exp {
if OffsetDateTime::now_utc().unix_timestamp() > exp {
return None;
}
}

View file

@ -1,11 +1,11 @@
use std::str;
use actix_web::{web, HttpRequest, HttpResponse};
use chrono::SecondsFormat;
use meilisearch_auth::{Action, AuthController, Key};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use time::OffsetDateTime;
use crate::extractors::authentication::{policies::*, GuardedData};
use meilisearch_error::ResponseError;
@ -92,9 +92,12 @@ struct KeyView {
key: String,
actions: Vec<Action>,
indexes: Vec<String>,
expires_at: Option<String>,
created_at: String,
updated_at: String,
#[serde(serialize_with = "time::serde::rfc3339::option::serialize")]
expires_at: Option<OffsetDateTime>,
#[serde(serialize_with = "time::serde::rfc3339::serialize")]
created_at: OffsetDateTime,
#[serde(serialize_with = "time::serde::rfc3339::serialize")]
updated_at: OffsetDateTime,
}
impl KeyView {
@ -107,11 +110,9 @@ impl KeyView {
key: generated_key,
actions: key.actions,
indexes: key.indexes,
expires_at: key
.expires_at
.map(|dt| dt.to_rfc3339_opts(SecondsFormat::Secs, true)),
created_at: key.created_at.to_rfc3339_opts(SecondsFormat::Secs, true),
updated_at: key.updated_at.to_rfc3339_opts(SecondsFormat::Secs, true),
expires_at: key.expires_at,
created_at: key.created_at,
updated_at: key.updated_at,
}
}
}

View file

@ -1,11 +1,11 @@
use actix_web::{web, HttpRequest, HttpResponse};
use chrono::{DateTime, Utc};
use log::debug;
use meilisearch_error::ResponseError;
use meilisearch_lib::index_controller::Update;
use meilisearch_lib::MeiliSearch;
use serde::{Deserialize, Serialize};
use serde_json::json;
use time::OffsetDateTime;
use crate::analytics::Analytics;
use crate::extractors::authentication::{policies::*, GuardedData};
@ -95,9 +95,12 @@ pub struct UpdateIndexRequest {
pub struct UpdateIndexResponse {
name: String,
uid: String,
created_at: DateTime<Utc>,
updated_at: DateTime<Utc>,
primary_key: Option<String>,
#[serde(serialize_with = "time::serde::rfc3339::serialize")]
created_at: OffsetDateTime,
#[serde(serialize_with = "time::serde::rfc3339::serialize")]
updated_at: OffsetDateTime,
#[serde(serialize_with = "time::serde::rfc3339::serialize")]
primary_key: OffsetDateTime,
}
pub async fn get_index(

View file

@ -1,10 +1,10 @@
use actix_web::{web, HttpRequest, HttpResponse};
use chrono::{DateTime, Utc};
use log::debug;
use meilisearch_error::ResponseError;
use meilisearch_lib::MeiliSearch;
use serde::{Deserialize, Serialize};
use serde_json::json;
use time::OffsetDateTime;
use crate::analytics::Analytics;
use crate::extractors::authentication::{policies::*, GuardedData};
@ -20,9 +20,12 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
pub struct UpdateIndexResponse {
name: String,
uid: String,
created_at: DateTime<Utc>,
updated_at: DateTime<Utc>,
primary_key: Option<String>,
#[serde(serialize_with = "time::serde::rfc3339::serialize")]
created_at: OffsetDateTime,
#[serde(serialize_with = "time::serde::rfc3339::serialize")]
updated_at: OffsetDateTime,
#[serde(serialize_with = "time::serde::rfc3339::serialize")]
primary_key: OffsetDateTime,
}
#[derive(Deserialize)]

View file

@ -1,7 +1,7 @@
use actix_web::{web, HttpResponse};
use chrono::{DateTime, Utc};
use log::debug;
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use meilisearch_error::ResponseError;
use meilisearch_lib::index::{Settings, Unchecked};
@ -54,8 +54,10 @@ pub struct ProcessedUpdateResult {
#[serde(rename = "type")]
pub update_type: UpdateType,
pub duration: f64, // in seconds
pub enqueued_at: DateTime<Utc>,
pub processed_at: DateTime<Utc>,
#[serde(serialize_with = "time::serde::rfc3339::serialize")]
pub enqueued_at: OffsetDateTime,
#[serde(serialize_with = "time::serde::rfc3339::serialize")]
pub processed_at: OffsetDateTime,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -66,8 +68,10 @@ pub struct FailedUpdateResult {
pub update_type: UpdateType,
pub error: ResponseError,
pub duration: f64, // in seconds
pub enqueued_at: DateTime<Utc>,
pub processed_at: DateTime<Utc>,
#[serde(serialize_with = "time::serde::rfc3339::serialize")]
pub enqueued_at: OffsetDateTime,
#[serde(serialize_with = "time::serde::rfc3339::serialize")]
pub processed_at: OffsetDateTime,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -76,9 +80,13 @@ pub struct EnqueuedUpdateResult {
pub update_id: u64,
#[serde(rename = "type")]
pub update_type: UpdateType,
pub enqueued_at: DateTime<Utc>,
#[serde(skip_serializing_if = "Option::is_none")]
pub started_processing_at: Option<DateTime<Utc>>,
#[serde(serialize_with = "time::serde::rfc3339::serialize")]
pub enqueued_at: OffsetDateTime,
#[serde(
skip_serializing_if = "Option::is_none",
serialize_with = "time::serde::rfc3339::option::serialize"
)]
pub started_processing_at: Option<OffsetDateTime>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]

View file

@ -1,4 +1,6 @@
use chrono::{DateTime, Duration, Utc};
use std::fmt::Write;
use std::write;
use meilisearch_error::ResponseError;
use meilisearch_lib::index::{Settings, Unchecked};
use meilisearch_lib::milli::update::IndexDocumentsMethod;
@ -7,6 +9,7 @@ use meilisearch_lib::tasks::task::{
DocumentDeletion, Task, TaskContent, TaskEvent, TaskId, TaskResult,
};
use serde::{Serialize, Serializer};
use time::{Duration, OffsetDateTime};
use crate::AUTOBATCHING_ENABLED;
@ -79,14 +82,52 @@ enum TaskDetails {
ClearAll { deleted_documents: Option<u64> },
}
/// Serialize a `time::Duration` as a best effort ISO 8601 while waiting for
/// https://github.com/time-rs/time/issues/378.
/// This code is a port of the old code of time that was removed in 0.2.
fn serialize_duration<S: Serializer>(
duration: &Option<Duration>,
serializer: S,
) -> Result<S::Ok, S::Error> {
match duration {
Some(duration) => {
let duration_str = duration.to_string();
serializer.serialize_str(&duration_str)
// technically speaking, negative duration is not valid ISO 8601
if duration.is_negative() {
return serializer.serialize_none();
}
const SECS_PER_DAY: i64 = Duration::DAY.whole_seconds();
let secs = duration.whole_seconds();
let days = secs / SECS_PER_DAY;
let secs = secs - days * SECS_PER_DAY;
let hasdate = days != 0;
let nanos = duration.subsec_nanoseconds();
let hastime = (secs != 0 || nanos != 0) || !hasdate;
// all the following unwrap can't fail
let mut res = String::new();
write!(&mut res, "P").unwrap();
if hasdate {
write!(&mut res, "{}D", days).unwrap();
}
const NANOS_PER_MILLI: i32 = Duration::MILLISECOND.subsec_nanoseconds();
const NANOS_PER_MICRO: i32 = Duration::MICROSECOND.subsec_nanoseconds();
if hastime {
if nanos == 0 {
write!(&mut res, "T{}S", secs).unwrap();
} else if nanos % NANOS_PER_MILLI == 0 {
write!(&mut res, "T{}.{:03}S", secs, nanos / NANOS_PER_MILLI).unwrap();
} else if nanos % NANOS_PER_MICRO == 0 {
write!(&mut res, "T{}.{:06}S", secs, nanos / NANOS_PER_MICRO).unwrap();
} else {
write!(&mut res, "T{}.{:09}S", secs, nanos).unwrap();
}
}
serializer.serialize_str(&res)
}
None => serializer.serialize_none(),
}
@ -106,9 +147,12 @@ pub struct TaskView {
error: Option<ResponseError>,
#[serde(serialize_with = "serialize_duration")]
duration: Option<Duration>,
enqueued_at: DateTime<Utc>,
started_at: Option<DateTime<Utc>>,
finished_at: Option<DateTime<Utc>>,
#[serde(serialize_with = "time::serde::rfc3339::serialize")]
enqueued_at: OffsetDateTime,
#[serde(serialize_with = "time::serde::rfc3339::option::serialize")]
started_at: Option<OffsetDateTime>,
#[serde(serialize_with = "time::serde::rfc3339::option::serialize")]
finished_at: Option<OffsetDateTime>,
#[serde(skip_serializing_if = "Option::is_none")]
batch_uid: Option<Option<BatchId>>,
}
@ -302,7 +346,8 @@ pub struct SummarizedTaskView {
status: TaskStatus,
#[serde(rename = "type")]
task_type: TaskType,
enqueued_at: DateTime<Utc>,
#[serde(serialize_with = "time::serde::rfc3339::serialize")]
enqueued_at: OffsetDateTime,
}
impl From<Task> for SummarizedTaskView {

View file

@ -257,7 +257,7 @@ async fn error_add_api_key_missing_parameter() {
"message": "`indexes` field is mandatory.",
"code": "missing_parameter",
"type": "invalid_request",
"link":"https://docs.meilisearch.com/errors#missing_parameter"
"link": "https://docs.meilisearch.com/errors#missing_parameter"
});
assert_eq!(response, expected_response);
@ -275,7 +275,7 @@ async fn error_add_api_key_missing_parameter() {
"message": "`actions` field is mandatory.",
"code": "missing_parameter",
"type": "invalid_request",
"link":"https://docs.meilisearch.com/errors#missing_parameter"
"link": "https://docs.meilisearch.com/errors#missing_parameter"
});
assert_eq!(response, expected_response);
@ -293,7 +293,7 @@ async fn error_add_api_key_missing_parameter() {
"message": "`expiresAt` field is mandatory.",
"code": "missing_parameter",
"type": "invalid_request",
"link":"https://docs.meilisearch.com/errors#missing_parameter"
"link": "https://docs.meilisearch.com/errors#missing_parameter"
});
assert_eq!(response, expected_response);
@ -316,7 +316,7 @@ async fn error_add_api_key_invalid_parameters_description() {
let (response, code) = server.add_api_key(content).await;
let expected_response = json!({
"message": r#"description field value `{"name":"products"}` is invalid. It should be a string or specified as a null value."#,
"message": r#"`description` field value `{"name":"products"}` is invalid. It should be a string or specified as a null value."#,
"code": "invalid_api_key_description",
"type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_api_key_description"
@ -342,7 +342,7 @@ async fn error_add_api_key_invalid_parameters_indexes() {
let (response, code) = server.add_api_key(content).await;
let expected_response = json!({
"message": r#"indexes field value `{"name":"products"}` is invalid. It should be an array of string representing index names."#,
"message": r#"`indexes` field value `{"name":"products"}` is invalid. It should be an array of string representing index names."#,
"code": "invalid_api_key_indexes",
"type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_api_key_indexes"
@ -366,7 +366,7 @@ async fn error_add_api_key_invalid_parameters_actions() {
let (response, code) = server.add_api_key(content).await;
let expected_response = json!({
"message": r#"actions field value `{"name":"products"}` is invalid. It should be an array of string representing action names."#,
"message": r#"`actions` field value `{"name":"products"}` is invalid. It should be an array of string representing action names."#,
"code": "invalid_api_key_actions",
"type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_api_key_actions"
@ -386,7 +386,7 @@ async fn error_add_api_key_invalid_parameters_actions() {
let (response, code) = server.add_api_key(content).await;
let expected_response = json!({
"message": r#"actions field value `["doc.add"]` is invalid. It should be an array of string representing action names."#,
"message": r#"`actions` field value `["doc.add"]` is invalid. It should be an array of string representing action names."#,
"code": "invalid_api_key_actions",
"type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_api_key_actions"
@ -412,7 +412,7 @@ async fn error_add_api_key_invalid_parameters_expires_at() {
let (response, code) = server.add_api_key(content).await;
let expected_response = json!({
"message": r#"expiresAt field value `{"name":"products"}` is invalid. It should be in ISO-8601 format to represents a date or datetime in the future or specified as a null value. e.g. 'YYYY-MM-DD' or 'YYYY-MM-DDTHH:MM:SS'."#,
"message": r#"`expiresAt` field value `{"name":"products"}` is invalid. It should follow the RFC 3339 format to represents a date or datetime in the future or specified as a null value. e.g. 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'."#,
"code": "invalid_api_key_expires_at",
"type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_api_key_expires_at"
@ -438,7 +438,7 @@ async fn error_add_api_key_invalid_parameters_expires_at_in_the_past() {
let (response, code) = server.add_api_key(content).await;
let expected_response = json!({
"message": r#"expiresAt field value `"2010-11-13T00:00:00Z"` is invalid. It should be in ISO-8601 format to represents a date or datetime in the future or specified as a null value. e.g. 'YYYY-MM-DD' or 'YYYY-MM-DDTHH:MM:SS'."#,
"message": r#"`expiresAt` field value `"2010-11-13T00:00:00Z"` is invalid. It should follow the RFC 3339 format to represents a date or datetime in the future or specified as a null value. e.g. 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'."#,
"code": "invalid_api_key_expires_at",
"type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_api_key_expires_at"
@ -1213,7 +1213,7 @@ async fn error_patch_api_key_indexes_invalid_parameters() {
let (response, code) = server.patch_api_key(&key, content).await;
let expected_response = json!({
"message": "description field value `13` is invalid. It should be a string or specified as a null value.",
"message": "`description` field value `13` is invalid. It should be a string or specified as a null value.",
"code": "invalid_api_key_description",
"type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_api_key_description"
@ -1230,7 +1230,7 @@ async fn error_patch_api_key_indexes_invalid_parameters() {
let (response, code) = server.patch_api_key(&key, content).await;
let expected_response = json!({
"message": "indexes field value `13` is invalid. It should be an array of string representing index names.",
"message": "`indexes` field value `13` is invalid. It should be an array of string representing index names.",
"code": "invalid_api_key_indexes",
"type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_api_key_indexes"
@ -1246,7 +1246,7 @@ async fn error_patch_api_key_indexes_invalid_parameters() {
let (response, code) = server.patch_api_key(&key, content).await;
let expected_response = json!({
"message": "actions field value `13` is invalid. It should be an array of string representing action names.",
"message": "`actions` field value `13` is invalid. It should be an array of string representing action names.",
"code": "invalid_api_key_actions",
"type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_api_key_actions"
@ -1262,7 +1262,7 @@ async fn error_patch_api_key_indexes_invalid_parameters() {
let (response, code) = server.patch_api_key(&key, content).await;
let expected_response = json!({
"message": "expiresAt field value `13` is invalid. It should be in ISO-8601 format to represents a date or datetime in the future or specified as a null value. e.g. 'YYYY-MM-DD' or 'YYYY-MM-DDTHH:MM:SS'.",
"message": "`expiresAt` field value `13` is invalid. It should follow the RFC 3339 format to represents a date or datetime in the future or specified as a null value. e.g. 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'.",
"code": "invalid_api_key_expires_at",
"type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_api_key_expires_at"

View file

@ -1,9 +1,10 @@
use crate::common::Server;
use chrono::{Duration, Utc};
use ::time::format_description::well_known::Rfc3339;
use maplit::{hashmap, hashset};
use once_cell::sync::Lazy;
use serde_json::{json, Value};
use std::collections::{HashMap, HashSet};
use time::{Duration, OffsetDateTime};
pub static AUTHORIZATIONS: Lazy<HashMap<(&'static str, &'static str), HashSet<&'static str>>> =
Lazy::new(|| {
@ -76,7 +77,7 @@ async fn error_access_expired_key() {
let content = json!({
"indexes": ["products"],
"actions": ALL_ACTIONS.clone(),
"expiresAt": (Utc::now() + Duration::seconds(1)),
"expiresAt": (OffsetDateTime::now_utc() + Duration::seconds(1)).format(&Rfc3339).unwrap(),
});
let (response, code) = server.add_api_key(content).await;
@ -106,7 +107,7 @@ async fn error_access_unauthorized_index() {
let content = json!({
"indexes": ["sales"],
"actions": ALL_ACTIONS.clone(),
"expiresAt": Utc::now() + Duration::hours(1),
"expiresAt": (OffsetDateTime::now_utc() + Duration::hours(1)).format(&Rfc3339).unwrap(),
});
let (response, code) = server.add_api_key(content).await;
@ -137,7 +138,7 @@ async fn error_access_unauthorized_action() {
let content = json!({
"indexes": ["products"],
"actions": [],
"expiresAt": Utc::now() + Duration::hours(1),
"expiresAt": (OffsetDateTime::now_utc() + Duration::hours(1)).format(&Rfc3339).unwrap(),
});
let (response, code) = server.add_api_key(content).await;
@ -174,7 +175,7 @@ async fn access_authorized_restricted_index() {
let content = json!({
"indexes": ["products"],
"actions": [],
"expiresAt": Utc::now() + Duration::hours(1),
"expiresAt": (OffsetDateTime::now_utc() + Duration::hours(1)).format(&Rfc3339).unwrap(),
});
let (response, code) = server.add_api_key(content).await;
@ -213,7 +214,7 @@ async fn access_authorized_no_index_restriction() {
let content = json!({
"indexes": ["*"],
"actions": [],
"expiresAt": Utc::now() + Duration::hours(1),
"expiresAt": (OffsetDateTime::now_utc() + Duration::hours(1)).format(&Rfc3339).unwrap(),
});
let (response, code) = server.add_api_key(content).await;
@ -263,7 +264,7 @@ async fn access_authorized_stats_restricted_index() {
let content = json!({
"indexes": ["products"],
"actions": ["stats.get"],
"expiresAt": Utc::now() + Duration::hours(1),
"expiresAt": (OffsetDateTime::now_utc() + Duration::hours(1)).format(&Rfc3339).unwrap(),
});
let (response, code) = server.add_api_key(content).await;
assert_eq!(code, 201);
@ -303,7 +304,7 @@ async fn access_authorized_stats_no_index_restriction() {
let content = json!({
"indexes": ["*"],
"actions": ["stats.get"],
"expiresAt": Utc::now() + Duration::hours(1),
"expiresAt": (OffsetDateTime::now_utc() + Duration::hours(1)).format(&Rfc3339).unwrap(),
});
let (response, code) = server.add_api_key(content).await;
assert_eq!(code, 201);
@ -343,7 +344,7 @@ async fn list_authorized_indexes_restricted_index() {
let content = json!({
"indexes": ["products"],
"actions": ["indexes.get"],
"expiresAt": Utc::now() + Duration::hours(1),
"expiresAt": (OffsetDateTime::now_utc() + Duration::hours(1)).format(&Rfc3339).unwrap(),
});
let (response, code) = server.add_api_key(content).await;
assert_eq!(code, 201);
@ -384,7 +385,7 @@ async fn list_authorized_indexes_no_index_restriction() {
let content = json!({
"indexes": ["*"],
"actions": ["indexes.get"],
"expiresAt": Utc::now() + Duration::hours(1),
"expiresAt": (OffsetDateTime::now_utc() + Duration::hours(1)).format(&Rfc3339).unwrap(),
});
let (response, code) = server.add_api_key(content).await;
assert_eq!(code, 201);
@ -424,7 +425,7 @@ async fn list_authorized_tasks_restricted_index() {
let content = json!({
"indexes": ["products"],
"actions": ["tasks.get"],
"expiresAt": Utc::now() + Duration::hours(1),
"expiresAt": (OffsetDateTime::now_utc() + Duration::hours(1)).format(&Rfc3339).unwrap(),
});
let (response, code) = server.add_api_key(content).await;
assert_eq!(code, 201);
@ -464,7 +465,7 @@ async fn list_authorized_tasks_no_index_restriction() {
let content = json!({
"indexes": ["*"],
"actions": ["tasks.get"],
"expiresAt": Utc::now() + Duration::hours(1),
"expiresAt": (OffsetDateTime::now_utc() + Duration::hours(1)).format(&Rfc3339).unwrap(),
});
let (response, code) = server.add_api_key(content).await;
assert_eq!(code, 201);

View file

@ -1,9 +1,10 @@
use crate::common::Server;
use chrono::{Duration, Utc};
use ::time::format_description::well_known::Rfc3339;
use maplit::hashmap;
use once_cell::sync::Lazy;
use serde_json::{json, Value};
use std::collections::HashMap;
use time::{Duration, OffsetDateTime};
use super::authorization::{ALL_ACTIONS, AUTHORIZATIONS};
@ -63,22 +64,22 @@ static ACCEPTED_KEYS: Lazy<Vec<Value>> = Lazy::new(|| {
json!({
"indexes": ["*"],
"actions": ["*"],
"expiresAt": Utc::now() + Duration::days(1)
"expiresAt": (OffsetDateTime::now_utc() + Duration::days(1)).format(&Rfc3339).unwrap()
}),
json!({
"indexes": ["*"],
"actions": ["search"],
"expiresAt": Utc::now() + Duration::days(1)
"expiresAt": (OffsetDateTime::now_utc() + Duration::days(1)).format(&Rfc3339).unwrap()
}),
json!({
"indexes": ["sales"],
"actions": ["*"],
"expiresAt": Utc::now() + Duration::days(1)
"expiresAt": (OffsetDateTime::now_utc() + Duration::days(1)).format(&Rfc3339).unwrap()
}),
json!({
"indexes": ["sales"],
"actions": ["search"],
"expiresAt": Utc::now() + Duration::days(1)
"expiresAt": (OffsetDateTime::now_utc() + Duration::days(1)).format(&Rfc3339).unwrap()
}),
]
});
@ -89,23 +90,23 @@ static REFUSED_KEYS: Lazy<Vec<Value>> = Lazy::new(|| {
json!({
"indexes": ["*"],
"actions": ALL_ACTIONS.iter().cloned().filter(|a| *a != "search" && *a != "*").collect::<Vec<_>>(),
"expiresAt": Utc::now() + Duration::days(1)
"expiresAt": (OffsetDateTime::now_utc() + Duration::days(1)).format(&Rfc3339).unwrap()
}),
json!({
"indexes": ["sales"],
"actions": ALL_ACTIONS.iter().cloned().filter(|a| *a != "search" && *a != "*").collect::<Vec<_>>(),
"expiresAt": Utc::now() + Duration::days(1)
"expiresAt": (OffsetDateTime::now_utc() + Duration::days(1)).format(&Rfc3339).unwrap()
}),
// bad index
json!({
"indexes": ["products"],
"actions": ["*"],
"expiresAt": Utc::now() + Duration::days(1)
"expiresAt": (OffsetDateTime::now_utc() + Duration::days(1)).format(&Rfc3339).unwrap()
}),
json!({
"indexes": ["products"],
"actions": ["search"],
"expiresAt": Utc::now() + Duration::days(1)
"expiresAt": (OffsetDateTime::now_utc() + Duration::days(1)).format(&Rfc3339).unwrap()
}),
]
});
@ -204,19 +205,19 @@ async fn search_authorized_simple_token() {
let tenant_tokens = vec![
hashmap! {
"searchRules" => json!({"*": {}}),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!(["*"]),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!({"sales": {}}),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!(["sales"]),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!({"*": {}}),
@ -253,19 +254,19 @@ async fn search_authorized_filter_token() {
let tenant_tokens = vec![
hashmap! {
"searchRules" => json!({"*": {"filter": "color = blue"}}),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!({"sales": {"filter": "color = blue"}}),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!({"*": {"filter": ["color = blue"]}}),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!({"sales": {"filter": ["color = blue"]}}),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
// filter on sales should override filters on *
hashmap! {
@ -273,28 +274,28 @@ async fn search_authorized_filter_token() {
"*": {"filter": "color = green"},
"sales": {"filter": "color = blue"}
}),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!({
"*": {},
"sales": {"filter": "color = blue"}
}),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!({
"*": {"filter": "color = green"},
"sales": {"filter": ["color = blue"]}
}),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!({
"*": {},
"sales": {"filter": ["color = blue"]}
}),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
];
@ -307,19 +308,19 @@ async fn filter_search_authorized_filter_token() {
let tenant_tokens = vec![
hashmap! {
"searchRules" => json!({"*": {"filter": "color = blue"}}),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!({"sales": {"filter": "color = blue"}}),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!({"*": {"filter": ["color = blue"]}}),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!({"sales": {"filter": ["color = blue"]}}),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
// filter on sales should override filters on *
hashmap! {
@ -327,28 +328,28 @@ async fn filter_search_authorized_filter_token() {
"*": {"filter": "color = green"},
"sales": {"filter": "color = blue"}
}),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!({
"*": {},
"sales": {"filter": "color = blue"}
}),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!({
"*": {"filter": "color = green"},
"sales": {"filter": ["color = blue"]}
}),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!({
"*": {},
"sales": {"filter": ["color = blue"]}
}),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
];
@ -361,27 +362,27 @@ async fn error_search_token_forbidden_parent_key() {
let tenant_tokens = vec![
hashmap! {
"searchRules" => json!({"*": {}}),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!({"*": Value::Null}),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!(["*"]),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!({"sales": {}}),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!({"sales": Value::Null}),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!(["sales"]),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
];
@ -395,11 +396,11 @@ async fn error_search_forbidden_token() {
// bad index
hashmap! {
"searchRules" => json!({"products": {}}),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!(["products"]),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!({"products": {}}),
@ -416,27 +417,27 @@ async fn error_search_forbidden_token() {
// expired token
hashmap! {
"searchRules" => json!({"*": {}}),
"exp" => json!((Utc::now() - Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() - Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!({"*": Value::Null}),
"exp" => json!((Utc::now() - Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() - Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!(["*"]),
"exp" => json!((Utc::now() - Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() - Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!({"sales": {}}),
"exp" => json!((Utc::now() - Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() - Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!({"sales": Value::Null}),
"exp" => json!((Utc::now() - Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() - Duration::hours(1)).unix_timestamp())
},
hashmap! {
"searchRules" => json!(["sales"]),
"exp" => json!((Utc::now() - Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() - Duration::hours(1)).unix_timestamp())
},
];
@ -452,7 +453,7 @@ async fn error_access_forbidden_routes() {
let content = json!({
"indexes": ["*"],
"actions": ["*"],
"expiresAt": (Utc::now() + Duration::hours(1)),
"expiresAt": (OffsetDateTime::now_utc() + Duration::hours(1)).format(&Rfc3339).unwrap(),
});
let (response, code) = server.add_api_key(content).await;
@ -463,7 +464,7 @@ async fn error_access_forbidden_routes() {
let tenant_token = hashmap! {
"searchRules" => json!(["*"]),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
};
let web_token = generate_tenant_token(&key, tenant_token);
server.use_api_key(&web_token);
@ -487,7 +488,7 @@ async fn error_access_expired_parent_key() {
let content = json!({
"indexes": ["*"],
"actions": ["*"],
"expiresAt": (Utc::now() + Duration::seconds(1)),
"expiresAt": (OffsetDateTime::now_utc() + Duration::seconds(1)).format(&Rfc3339).unwrap(),
});
let (response, code) = server.add_api_key(content).await;
@ -498,7 +499,7 @@ async fn error_access_expired_parent_key() {
let tenant_token = hashmap! {
"searchRules" => json!(["*"]),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
};
let web_token = generate_tenant_token(&key, tenant_token);
server.use_api_key(&web_token);
@ -529,7 +530,7 @@ async fn error_access_modified_token() {
let content = json!({
"indexes": ["*"],
"actions": ["*"],
"expiresAt": (Utc::now() + Duration::hours(1)),
"expiresAt": (OffsetDateTime::now_utc() + Duration::hours(1)).format(&Rfc3339).unwrap(),
});
let (response, code) = server.add_api_key(content).await;
@ -540,7 +541,7 @@ async fn error_access_modified_token() {
let tenant_token = hashmap! {
"searchRules" => json!(["products"]),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
};
let web_token = generate_tenant_token(&key, tenant_token);
server.use_api_key(&web_token);
@ -554,7 +555,7 @@ async fn error_access_modified_token() {
let tenant_token = hashmap! {
"searchRules" => json!(["*"]),
"exp" => json!((Utc::now() + Duration::hours(1)).timestamp())
"exp" => json!((OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp())
};
let alt = generate_tenant_token(&key, tenant_token);

View file

@ -1,8 +1,8 @@
use crate::common::{GetAllDocumentsOptions, Server};
use actix_web::test;
use chrono::DateTime;
use meilisearch_http::{analytics, create_app};
use serde_json::{json, Value};
use time::{format_description::well_known::Rfc3339, OffsetDateTime};
/// This is the basic usage of our API and every other tests uses the content-type application/json
#[actix_rt::test]
@ -568,9 +568,9 @@ async fn add_documents_no_index_creation() {
assert_eq!(response["details"]["indexedDocuments"], 1);
let processed_at =
DateTime::parse_from_rfc3339(response["finishedAt"].as_str().unwrap()).unwrap();
OffsetDateTime::parse(response["finishedAt"].as_str().unwrap(), &Rfc3339).unwrap();
let enqueued_at =
DateTime::parse_from_rfc3339(response["enqueuedAt"].as_str().unwrap()).unwrap();
OffsetDateTime::parse(response["enqueuedAt"].as_str().unwrap(), &Rfc3339).unwrap();
assert!(processed_at > enqueued_at);
// index was created, and primary key was infered.

View file

@ -1,6 +1,6 @@
use crate::common::Server;
use chrono::DateTime;
use serde_json::json;
use time::{format_description::well_known::Rfc3339, OffsetDateTime};
#[actix_rt::test]
async fn update_primary_key() {
@ -25,8 +25,10 @@ async fn update_primary_key() {
assert!(response.get("createdAt").is_some());
assert!(response.get("updatedAt").is_some());
let created_at = DateTime::parse_from_rfc3339(response["createdAt"].as_str().unwrap()).unwrap();
let updated_at = DateTime::parse_from_rfc3339(response["updatedAt"].as_str().unwrap()).unwrap();
let created_at =
OffsetDateTime::parse(response["createdAt"].as_str().unwrap(), &Rfc3339).unwrap();
let updated_at =
OffsetDateTime::parse(response["updatedAt"].as_str().unwrap(), &Rfc3339).unwrap();
assert!(created_at < updated_at);
assert_eq!(response["primaryKey"], "primary");

View file

@ -1,6 +1,7 @@
use crate::common::Server;
use chrono::{DateTime, Utc};
use serde_json::json;
use time::format_description::well_known::Rfc3339;
use time::OffsetDateTime;
#[actix_rt::test]
async fn error_get_task_unexisting_index() {
@ -98,7 +99,8 @@ macro_rules! assert_valid_summarized_task {
assert_eq!($response["status"], "enqueued");
assert_eq!($response["type"], $task_type);
let date = $response["enqueuedAt"].as_str().expect("missing date");
date.parse::<DateTime<Utc>>().unwrap();
OffsetDateTime::parse(date, &Rfc3339).unwrap();
}};
}