Validate token
This commit is contained in:
parent
2c0104ec2d
commit
1667270d73
@ -1,8 +1,12 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use actix::{Addr, Message};
|
use actix::{Addr, Message};
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
|
use hmac::digest::KeyInit;
|
||||||
|
use hmac::Hmac;
|
||||||
|
use sha2::Sha256;
|
||||||
|
|
||||||
use crate::database::{Database, TokenByJti};
|
use crate::database::{Database, TokenByJti};
|
||||||
use crate::model::{AccountId, Audience, Token, TokenString};
|
use crate::model::{AccountId, Audience, Token, TokenString};
|
||||||
@ -73,7 +77,7 @@ pub struct CreateToken {
|
|||||||
|
|
||||||
token_async_handler!(CreateToken, create_token, (Token, TokenString));
|
token_async_handler!(CreateToken, create_token, (Token, TokenString));
|
||||||
|
|
||||||
async fn create_token(
|
pub(crate) async fn create_token(
|
||||||
msg: CreateToken,
|
msg: CreateToken,
|
||||||
db: Addr<Database>,
|
db: Addr<Database>,
|
||||||
secret: Arc<String>,
|
secret: Arc<String>,
|
||||||
@ -107,19 +111,9 @@ async fn create_token(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let token_string = {
|
let token_string = {
|
||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
use hmac::{Hmac, Mac};
|
|
||||||
use jwt::SignWithKey;
|
use jwt::SignWithKey;
|
||||||
use sha2::Sha256;
|
|
||||||
|
|
||||||
let key: Hmac<Sha256> = match Hmac::new_from_slice(secret.as_bytes()) {
|
let key: Hmac<Sha256> = build_key(secret)?;
|
||||||
Ok(key) => key,
|
|
||||||
Err(e) => {
|
|
||||||
log::error!("{e:?}");
|
|
||||||
return Err(Error::SaveInternal);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let mut claims = BTreeMap::new();
|
let mut claims = BTreeMap::new();
|
||||||
|
|
||||||
// cti (customer id): Customer uuid identifier used by payment service
|
// cti (customer id): Customer uuid identifier used by payment service
|
||||||
@ -168,21 +162,11 @@ pub(crate) async fn validate(
|
|||||||
db: Addr<Database>,
|
db: Addr<Database>,
|
||||||
secret: Arc<String>,
|
secret: Arc<String>,
|
||||||
) -> Result<(Token, bool)> {
|
) -> Result<(Token, bool)> {
|
||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
use hmac::{Hmac, Mac};
|
|
||||||
use jwt::VerifyWithKey;
|
use jwt::VerifyWithKey;
|
||||||
use sha2::Sha256;
|
|
||||||
|
|
||||||
log::info!("Validating token {:?}", msg.token);
|
log::info!("Validating token {:?}", msg.token);
|
||||||
|
|
||||||
let key: Hmac<Sha256> = match Hmac::new_from_slice(secret.as_bytes()) {
|
let key: Hmac<Sha256> = build_key(secret)?;
|
||||||
Ok(key) => key,
|
|
||||||
Err(e) => {
|
|
||||||
log::error!("{e:?}");
|
|
||||||
return Err(Error::ValidateInternal);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let claims: BTreeMap<String, String> = match msg.token.verify_with_key(&key) {
|
let claims: BTreeMap<String, String> = match msg.token.verify_with_key(&key) {
|
||||||
Ok(claims) => claims,
|
Ok(claims) => claims,
|
||||||
_ => return Err(Error::Validate),
|
_ => return Err(Error::Validate),
|
||||||
@ -209,59 +193,72 @@ pub(crate) async fn validate(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match (claims.get("cti"), &token.customer_id) {
|
if !validate_pair(&claims, "cti", token.customer_id, validate_uuid) {
|
||||||
(Some(cti), id) => {
|
|
||||||
if !uuid::Uuid::from_str(cti)
|
|
||||||
.map(|u| u == *id)
|
|
||||||
.unwrap_or_default()
|
|
||||||
{
|
|
||||||
return Ok((token, false));
|
return Ok((token, false));
|
||||||
}
|
}
|
||||||
}
|
// if !validate_pair(&claims, "arl", token.role, |left, right| right == left) {
|
||||||
_ => return Ok((token, false)),
|
// return Ok((token, false));
|
||||||
}
|
// }
|
||||||
match (claims.get("arl"), &token.role) {
|
match (claims.get("arl"), &token.role) {
|
||||||
(Some(arl), role) if arl == role.as_str() => {}
|
(Some(arl), role) if role == arl.as_str() => {}
|
||||||
_ => return Ok((token, false)),
|
_ => return Ok((token, false)),
|
||||||
}
|
}
|
||||||
match (claims.get("iss"), &token.issuer) {
|
match (claims.get("iss"), &token.issuer) {
|
||||||
(Some(iss), issuer) if iss == issuer => {}
|
(Some(iss), issuer) if iss == issuer => {}
|
||||||
_ => return Ok((token, false)),
|
_ => return Ok((token, false)),
|
||||||
}
|
}
|
||||||
match (claims.get("sub"), &token.subject) {
|
if !validate_pair(&claims, "sub", token.subject, validate_num) {
|
||||||
(Some(sub), subject) => {
|
|
||||||
if !sub
|
|
||||||
.parse::<i32>()
|
|
||||||
.map(|n| n == *subject)
|
|
||||||
.unwrap_or_default()
|
|
||||||
{
|
|
||||||
return Ok((token, false));
|
return Ok((token, false));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
_ => return Ok((token, false)),
|
|
||||||
}
|
|
||||||
match (claims.get("aud"), &token.audience) {
|
match (claims.get("aud"), &token.audience) {
|
||||||
(Some(aud), audience) if aud == audience.as_str() => {}
|
(Some(aud), audience) if aud == audience.as_str() => {}
|
||||||
_ => return Ok((token, false)),
|
_ => return Ok((token, false)),
|
||||||
}
|
}
|
||||||
match (claims.get("exp"), &token.expiration_time) {
|
|
||||||
(Some(left), right) if validate_time(left, right) => {}
|
if !validate_pair(&claims, "exp", &token.expiration_time, validate_time) {
|
||||||
_ => return Ok((token, false)),
|
return Ok((token, false));
|
||||||
}
|
}
|
||||||
match (claims.get("nbt"), &token.not_before_time) {
|
if !validate_pair(&claims, "nbt", &token.not_before_time, validate_time) {
|
||||||
(Some(left), right) if validate_time(left, right) => {}
|
return Ok((token, false));
|
||||||
_ => return Ok((token, false)),
|
|
||||||
}
|
}
|
||||||
match (claims.get("iat"), &token.issued_at_time) {
|
if !validate_pair(&claims, "iat", &token.issued_at_time, validate_time) {
|
||||||
(Some(left), right) if validate_time(left, right) => {}
|
return Ok((token, false));
|
||||||
_ => return Ok((token, false)),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((token, true))
|
Ok((token, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_key(secret: Arc<String>) -> Result<Hmac<Sha256>> {
|
||||||
|
match Hmac::new_from_slice(secret.as_bytes()) {
|
||||||
|
Ok(key) => Ok(key),
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("{e:?}");
|
||||||
|
Err(Error::ValidateInternal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_pair<F, V>(claims: &BTreeMap<String, String>, key: &str, v: V, cmp: F) -> bool
|
||||||
|
where
|
||||||
|
F: FnOnce(&str, V) -> bool,
|
||||||
|
V: PartialEq,
|
||||||
|
{
|
||||||
|
claims.get(key).map(|s| cmp(s, v)).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
fn validate_time(left: &str, right: &NaiveDateTime) -> bool {
|
fn validate_time(left: &str, right: &NaiveDateTime) -> bool {
|
||||||
chrono::DateTime::parse_from_str(left, "%+")
|
chrono::DateTime::parse_from_str(left, "%+")
|
||||||
.map(|t| t.naive_utc() == *right)
|
.map(|t| t.naive_utc() == *right)
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validate_num(left: &str, right: i32) -> bool {
|
||||||
|
left.parse::<i32>().map(|n| n == right).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_uuid(left: &str, right: uuid::Uuid) -> bool {
|
||||||
|
uuid::Uuid::from_str(left)
|
||||||
|
.map(|u| u == right)
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
@ -31,7 +31,7 @@ pub enum OrderStatus {
|
|||||||
Refunded,
|
Refunded,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(sqlx::Type, Copy, Clone, Debug, Display, Deserialize, Serialize)]
|
#[derive(sqlx::Type, Copy, Clone, Debug, Display, Deserialize, Serialize, PartialEq)]
|
||||||
#[sqlx(rename_all = "snake_case")]
|
#[sqlx(rename_all = "snake_case")]
|
||||||
pub enum Role {
|
pub enum Role {
|
||||||
#[display(fmt = "Adminitrator")]
|
#[display(fmt = "Adminitrator")]
|
||||||
@ -40,6 +40,12 @@ pub enum Role {
|
|||||||
User,
|
User,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq<str> for Role {
|
||||||
|
fn eq(&self, other: &str) -> bool {
|
||||||
|
self.as_str() == other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Role {
|
impl Role {
|
||||||
pub fn as_str(&self) -> &str {
|
pub fn as_str(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
|
Loading…
Reference in New Issue
Block a user