make keys shorter by encoding bytes as base64
This commit is contained in:
parent
b3870cd48a
commit
c9912ff332
4 changed files with 49 additions and 6 deletions
|
@ -1,3 +1,5 @@
|
|||
use anyhow::Result;
|
||||
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _};
|
||||
use chrono::{DateTime, Utc};
|
||||
use deadpool_diesel::postgres::Connection;
|
||||
use diesel::{
|
||||
|
@ -53,3 +55,28 @@ impl ApiKey {
|
|||
api_keys::team_id.eq(team_id)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode big-endian bytes of a UUID as URL-safe base64.
|
||||
*/
|
||||
pub fn compact_uuid(id: &Uuid) -> String {
|
||||
URL_SAFE_NO_PAD.encode(id.as_bytes())
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to parse a string as either a standard formatted UUID or a big-endian
|
||||
* base64 encoding of one.
|
||||
*/
|
||||
pub fn try_parse_as_uuid(value: &str) -> Result<Uuid> {
|
||||
if value.len() < 32 {
|
||||
let bytes: Vec<u8> = URL_SAFE_NO_PAD
|
||||
.decode(value)
|
||||
.or(Err(anyhow::anyhow!("failed to parse")))?;
|
||||
let bytes: [u8; 16] = bytes
|
||||
.try_into()
|
||||
.or(Err(anyhow::anyhow!("failed to parse")))?;
|
||||
Ok(Uuid::from_bytes(bytes))
|
||||
} else {
|
||||
Uuid::try_parse(value).or(Err(anyhow::anyhow!("failed to parse")))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -248,6 +248,20 @@ async fn projects_page(
|
|||
.await
|
||||
.unwrap()?;
|
||||
|
||||
mod filters {
|
||||
use uuid::Uuid;
|
||||
|
||||
pub fn compact_uuid(id: &Uuid) -> askama::Result<String> {
|
||||
Ok(crate::api_keys::compact_uuid(id))
|
||||
}
|
||||
|
||||
pub fn redact(value: &str) -> askama::Result<String> {
|
||||
Ok(format!(
|
||||
"********{}",
|
||||
value[value.char_indices().nth_back(3).unwrap().0..].to_string()
|
||||
))
|
||||
}
|
||||
}
|
||||
#[derive(Template)]
|
||||
#[template(path = "projects.html")]
|
||||
struct ResponseTemplate {
|
||||
|
|
|
@ -16,7 +16,7 @@ use uuid::Uuid;
|
|||
use validator::Validate;
|
||||
|
||||
use crate::{
|
||||
api_keys::ApiKey,
|
||||
api_keys::{try_parse_as_uuid, ApiKey},
|
||||
app_error::AppError,
|
||||
app_state::{AppState, DbConn},
|
||||
channels::Channel,
|
||||
|
@ -38,7 +38,7 @@ pub fn new_router(state: AppState) -> Router<AppState> {
|
|||
#[derive(Deserialize, Validate)]
|
||||
struct SayQuery {
|
||||
#[serde(alias = "k")]
|
||||
key: Uuid,
|
||||
key: String,
|
||||
#[serde(alias = "p")]
|
||||
#[serde(default = "default_project")]
|
||||
#[validate(regex(
|
||||
|
@ -67,7 +67,9 @@ async fn say_get(
|
|||
query.validate().map_err(AppError::from_validation_errors)?;
|
||||
|
||||
let api_key = {
|
||||
let query_key = query.key.clone();
|
||||
let query_key = try_parse_as_uuid(&query.key).or(Err(AppError::ForbiddenError(
|
||||
"key not accepted".to_string(),
|
||||
)))?;
|
||||
db_conn
|
||||
.interact::<_, Result<ApiKey, AppError>>(move |conn| {
|
||||
update(api_keys::table.filter(ApiKey::with_id(query_key)))
|
||||
|
@ -76,7 +78,7 @@ async fn say_get(
|
|||
.get_result(conn)
|
||||
.optional()
|
||||
.context("failed to get API key")?
|
||||
.ok_or(AppError::ForbiddenError("Key not accepted.".to_string()))
|
||||
.ok_or(AppError::ForbiddenError("key not accepted.".to_string()))
|
||||
})
|
||||
.await
|
||||
.unwrap()?
|
||||
|
|
|
@ -78,7 +78,7 @@
|
|||
<tr>
|
||||
<td>
|
||||
<code>
|
||||
********{{ key.id.simple().to_string()[key.id.simple().to_string().char_indices().nth_back(3).unwrap().0..] }}
|
||||
{{ key.id|compact_uuid|redact }}
|
||||
</code>
|
||||
</td>
|
||||
<td>
|
||||
|
@ -94,7 +94,7 @@
|
|||
class="btn btn-outline-light"
|
||||
type="button"
|
||||
name="api-key-copy-button"
|
||||
data-copy="{{ key.id.simple() }}"
|
||||
data-copy="{{ key.id|compact_uuid }}"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
|
|
Loading…
Add table
Reference in a new issue