mirror of
https://github.com/meilisearch/MeiliSearch
synced 2025-07-03 20:07:09 +02:00
feat(auth): Extend API keys
- Add API keys in snapshots - Add API keys in dumps - Rename action indexes.add to indexes.create - fix QA #1979 fix #1979 fix #1995 fix #2001 fix #2003 related to #1890
This commit is contained in:
parent
8096b568f0
commit
ee7970f603
19 changed files with 418 additions and 204 deletions
|
@ -14,8 +14,8 @@ pub enum Action {
|
|||
DocumentsGet = actions::DOCUMENTS_GET,
|
||||
#[serde(rename = "documents.delete")]
|
||||
DocumentsDelete = actions::DOCUMENTS_DELETE,
|
||||
#[serde(rename = "indexes.add")]
|
||||
IndexesAdd = actions::INDEXES_ADD,
|
||||
#[serde(rename = "indexes.create")]
|
||||
IndexesAdd = actions::INDEXES_CREATE,
|
||||
#[serde(rename = "indexes.get")]
|
||||
IndexesGet = actions::INDEXES_GET,
|
||||
#[serde(rename = "indexes.update")]
|
||||
|
@ -47,7 +47,7 @@ impl Action {
|
|||
DOCUMENTS_ADD => Some(Self::DocumentsAdd),
|
||||
DOCUMENTS_GET => Some(Self::DocumentsGet),
|
||||
DOCUMENTS_DELETE => Some(Self::DocumentsDelete),
|
||||
INDEXES_ADD => Some(Self::IndexesAdd),
|
||||
INDEXES_CREATE => Some(Self::IndexesAdd),
|
||||
INDEXES_GET => Some(Self::IndexesGet),
|
||||
INDEXES_UPDATE => Some(Self::IndexesUpdate),
|
||||
INDEXES_DELETE => Some(Self::IndexesDelete),
|
||||
|
@ -70,7 +70,7 @@ impl Action {
|
|||
Self::DocumentsAdd => DOCUMENTS_ADD,
|
||||
Self::DocumentsGet => DOCUMENTS_GET,
|
||||
Self::DocumentsDelete => DOCUMENTS_DELETE,
|
||||
Self::IndexesAdd => INDEXES_ADD,
|
||||
Self::IndexesAdd => INDEXES_CREATE,
|
||||
Self::IndexesGet => INDEXES_GET,
|
||||
Self::IndexesUpdate => INDEXES_UPDATE,
|
||||
Self::IndexesDelete => INDEXES_DELETE,
|
||||
|
@ -90,7 +90,7 @@ pub mod actions {
|
|||
pub const DOCUMENTS_ADD: u8 = 2;
|
||||
pub const DOCUMENTS_GET: u8 = 3;
|
||||
pub const DOCUMENTS_DELETE: u8 = 4;
|
||||
pub const INDEXES_ADD: u8 = 5;
|
||||
pub const INDEXES_CREATE: u8 = 5;
|
||||
pub const INDEXES_GET: u8 = 6;
|
||||
pub const INDEXES_UPDATE: u8 = 7;
|
||||
pub const INDEXES_DELETE: u8 = 8;
|
||||
|
|
40
meilisearch-auth/src/dump.rs
Normal file
40
meilisearch-auth/src/dump.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
use std::fs::File;
|
||||
use std::io::BufRead;
|
||||
use std::io::BufReader;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::{AuthController, HeedAuthStore, Result};
|
||||
|
||||
const KEYS_PATH: &str = "keys";
|
||||
|
||||
impl AuthController {
|
||||
pub fn dump(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()> {
|
||||
let store = HeedAuthStore::new(&src)?;
|
||||
|
||||
let keys_file_path = dst.as_ref().join(KEYS_PATH);
|
||||
|
||||
let keys = store.list_api_keys()?;
|
||||
let mut keys_file = File::create(&keys_file_path)?;
|
||||
for key in keys {
|
||||
serde_json::to_writer(&mut keys_file, &key)?;
|
||||
keys_file.write_all(b"\n")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_dump(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()> {
|
||||
let store = HeedAuthStore::new(&dst)?;
|
||||
|
||||
let keys_file_path = src.as_ref().join(KEYS_PATH);
|
||||
|
||||
let mut reader = BufReader::new(File::open(&keys_file_path)?).lines();
|
||||
while let Some(key) = reader.next().transpose()? {
|
||||
let key = serde_json::from_str(&key)?;
|
||||
store.put_api_key(key)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -24,7 +24,12 @@ pub enum AuthControllerError {
|
|||
Internal(Box<dyn Error + Send + Sync + 'static>),
|
||||
}
|
||||
|
||||
internal_error!(AuthControllerError: heed::Error, std::io::Error);
|
||||
internal_error!(
|
||||
AuthControllerError: heed::Error,
|
||||
std::io::Error,
|
||||
serde_json::Error,
|
||||
std::str::Utf8Error
|
||||
);
|
||||
|
||||
impl ErrorCode for AuthControllerError {
|
||||
fn error_code(&self) -> Code {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::action::Action;
|
||||
use crate::error::{AuthControllerError, Result};
|
||||
use crate::store::{KeyId, KEY_ID_LENGTH};
|
||||
use chrono::{DateTime, Utc};
|
||||
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||
use rand::Rng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{from_value, Value};
|
||||
|
@ -48,11 +48,8 @@ impl Key {
|
|||
|
||||
let expires_at = value
|
||||
.get("expiresAt")
|
||||
.map(|exp| {
|
||||
from_value(exp.clone())
|
||||
.map_err(|_| AuthControllerError::InvalidApiKeyExpiresAt(exp.clone()))
|
||||
})
|
||||
.transpose()?;
|
||||
.map(parse_expiration_date)
|
||||
.ok_or(AuthControllerError::MissingParameter("expiresAt"))??;
|
||||
|
||||
let created_at = Utc::now();
|
||||
let updated_at = Utc::now();
|
||||
|
@ -88,9 +85,7 @@ impl Key {
|
|||
}
|
||||
|
||||
if let Some(exp) = value.get("expiresAt") {
|
||||
let exp = from_value(exp.clone())
|
||||
.map_err(|_| AuthControllerError::InvalidApiKeyExpiresAt(exp.clone()));
|
||||
self.expires_at = exp?;
|
||||
self.expires_at = parse_expiration_date(exp)?;
|
||||
}
|
||||
|
||||
self.updated_at = Utc::now();
|
||||
|
@ -137,3 +132,30 @@ fn generate_id() -> [u8; KEY_ID_LENGTH] {
|
|||
|
||||
bytes
|
||||
}
|
||||
|
||||
fn parse_expiration_date(value: &Value) -> Result<Option<DateTime<Utc>>> {
|
||||
match value {
|
||||
Value::String(string) => DateTime::parse_from_rfc3339(string)
|
||||
.map(|d| d.into())
|
||||
.or_else(|_| {
|
||||
NaiveDateTime::parse_from_str(string, "%Y-%m-%dT%H:%M:%S")
|
||||
.map(|naive| DateTime::from_utc(naive, Utc))
|
||||
})
|
||||
.or_else(|_| {
|
||||
NaiveDateTime::parse_from_str(string, "%Y-%m-%d")
|
||||
.map(|naive| DateTime::from_utc(naive, Utc))
|
||||
})
|
||||
.map_err(|_| AuthControllerError::InvalidApiKeyExpiresAt(value.clone()))
|
||||
// check if the key is already expired.
|
||||
.and_then(|d| {
|
||||
if d > Utc::now() {
|
||||
Ok(d)
|
||||
} else {
|
||||
Err(AuthControllerError::InvalidApiKeyExpiresAt(value.clone()))
|
||||
}
|
||||
})
|
||||
.map(Option::Some),
|
||||
Value::Null => Ok(None),
|
||||
_otherwise => Err(AuthControllerError::InvalidApiKeyExpiresAt(value.clone())),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
mod action;
|
||||
mod dump;
|
||||
pub mod error;
|
||||
mod key;
|
||||
mod store;
|
||||
|
@ -104,7 +105,7 @@ impl AuthController {
|
|||
None => self.store.prefix_first_expiration_date(token, action)?,
|
||||
})
|
||||
{
|
||||
let id = from_utf8(&id).map_err(|e| AuthControllerError::Internal(Box::new(e)))?;
|
||||
let id = from_utf8(&id)?;
|
||||
if exp.map_or(true, |exp| Utc::now() < exp)
|
||||
&& generate_key(master_key.as_bytes(), id).as_bytes() == token
|
||||
{
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use enum_iterator::IntoEnumIterator;
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::Reverse;
|
||||
use std::convert::TryFrom;
|
||||
use std::convert::TryInto;
|
||||
use std::fs::create_dir_all;
|
||||
|
@ -121,6 +122,7 @@ impl HeedAuthStore {
|
|||
let (_, content) = result?;
|
||||
list.push(content);
|
||||
}
|
||||
list.sort_unstable_by_key(|k| Reverse(k.created_at));
|
||||
Ok(list)
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue