Skip to content

Commit

Permalink
Merge pull request #38 from ECDAR-AAU-SW-P5/auth-validation-refactor
Browse files Browse the repository at this point in the history
Auth validation refactor
  • Loading branch information
sabotack authored Nov 13, 2023
2 parents 6270e34 + c021bb3 commit 47d9cb4
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 71 deletions.
2 changes: 1 addition & 1 deletion Ecdar-ProtoBuf
Submodule Ecdar-ProtoBuf updated 2 files
+4 −13 api.proto
+1 −1 services.proto
74 changes: 49 additions & 25 deletions src/api/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use chrono::Utc;
use jsonwebtoken::{
decode, encode,
errors::{Error, ErrorKind},
Algorithm, DecodingKey, EncodingKey, Header, Validation,
Algorithm, DecodingKey, EncodingKey, Header, TokenData, Validation,
};
use serde::{Deserialize, Serialize};
use std::{env, str::FromStr};
Expand Down Expand Up @@ -60,44 +60,68 @@ pub fn create_refresh_token(uid: &str) -> Result<String, Error> {
.map_err(|_| ErrorKind::InvalidToken.into())
}

pub fn token_validation(mut req: Request<()>) -> Result<Request<()>, Status> {
pub fn validation_interceptor(mut req: Request<()>) -> Result<Request<()>, Status> {
let token = match get_token_from_request(&req) {
Ok(token) => token,
Err(err) => return Err(err),
};

match validate_token(token, false) {
Ok(token_data) => {
req.metadata_mut().insert(
"uid",
metadata::MetadataValue::from_str(&token_data.claims.sub).unwrap(),
);
Ok(req)
}
Err(err) => Err(err),
}
}

pub fn get_token_from_request<T>(req: &Request<T>) -> Result<String, Status> {
let token = match req.metadata().get("authorization") {
Some(token) => token.to_str(),
None => return Err(Status::unauthenticated("Token not found")),
};

let secret = env::var("HS512_SECRET").expect("Expected HS512_SECRET to be set.");
if token.is_ok() {
Ok(token.unwrap().to_string())
} else {
Err(Status::unauthenticated(
"Could not read token from metadata",
))
}
}

pub fn validate_token(token: String, is_refresh_token: bool) -> Result<TokenData<Claims>, Status> {
let secret: String;

if is_refresh_token {
secret = env::var("REFRESH_TOKEN_HS512_SECRET").expect("Expected HS512_SECRET to be set.");
} else {
secret = env::var("ACCESS_TOKEN_HS512_SECRET").expect("Expected HS512_SECRET to be set.");
}

let mut validation = Validation::new(Algorithm::HS512);

validation.validate_exp = true;
let token_data = match decode::<Claims>(
token.unwrap().trim_start_matches("Bearer "),

match decode::<Claims>(
token.trim_start_matches("Bearer "),
&DecodingKey::from_secret(secret.as_bytes()),
&validation,
) {
Ok(c) => c,
Ok(c) => Ok(c),
Err(err) => match *err.kind() {
ErrorKind::InvalidToken => {
return Err(Status::new(Code::Unauthenticated, "Token is invalid!"))
}
ErrorKind::InvalidSignature => {
return Err(Status::new(
Code::Unauthenticated,
"Token signature is invalid!",
))
}
ErrorKind::InvalidToken => Err(Status::new(Code::Unauthenticated, "Token is invalid!")),
ErrorKind::InvalidSignature => Err(Status::new(
Code::Unauthenticated,
"Token signature is invalid!",
)),
ErrorKind::ExpiredSignature => {
return Err(Status::new(Code::Unauthenticated, "Token is expired!"))
Err(Status::new(Code::Unauthenticated, "Token is expired!"))
}
_ => return Err(Status::new(Code::Internal, err.to_string())), // Example on how to handle a error generically
_ => Err(Status::new(Code::Internal, err.to_string())),
},
};

req.metadata_mut().insert(
"uid",
metadata::MetadataValue::from_str(&token_data.claims.sub).unwrap(),
);

Ok(req)
}
}
80 changes: 46 additions & 34 deletions src/api/ecdar_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::env;
use std::sync::Arc;

use crate::api::ecdar_api::helpers::helpers::{setup_db_with_entities, AnyEntity};
use crate::api::server::server::get_auth_token_request::{user_credentials, AuthOption};
use crate::api::server::server::get_auth_token_request::user_credentials;
use regex::Regex;
use sea_orm::SqlErr;
use tonic::{Code, Request, Response, Status};
Expand All @@ -28,10 +28,9 @@ use crate::entities::user::Model as User;
use super::{
auth,
server::server::{
ecdar_backend_server::EcdarBackend, CreateUserRequest, DeleteUserRequest,
GetAuthTokenRequest, GetAuthTokenResponse, QueryRequest, QueryResponse,
SimulationStartRequest, SimulationStepRequest, SimulationStepResponse, UpdateUserRequest,
UserTokenResponse,
ecdar_backend_server::EcdarBackend, CreateUserRequest, GetAuthTokenRequest,
GetAuthTokenResponse, QueryRequest, QueryResponse, SimulationStartRequest,
SimulationStepRequest, SimulationStepResponse, UpdateUserRequest, UserTokenResponse,
},
};

Expand Down Expand Up @@ -168,10 +167,7 @@ impl EcdarApi for ConcreteEcdarApi {
/// # Errors
/// Returns an error if the database context fails to delete the user or
/// if the uid could not be parsed from the request metadata.
async fn delete_user(
&self,
request: Request<DeleteUserRequest>,
) -> Result<Response<()>, Status> {
async fn delete_user(&self, request: Request<()>) -> Result<Response<()>, Status> {
// Get uid from request metadata
let uid = get_uid_from_request(&request)?;

Expand Down Expand Up @@ -214,36 +210,51 @@ impl EcdarApiAuth for ConcreteEcdarApi {
request: Request<GetAuthTokenRequest>,
) -> Result<Response<GetAuthTokenResponse>, Status> {
let message = request.get_ref().clone();
let uid = match message.auth_option {
Some(auth_option) => match auth_option {
AuthOption::RefreshToken(_refresh_token) => {
get_uid_from_request(&request).unwrap().to_string()
}
AuthOption::UserCredentials(user_credentials) => {
if let Some(user) = user_credentials.user {
match user {
user_credentials::User::Username(username) => {
match self.user_context.get_by_username(username).await {
Ok(Some(user)) => user.id.to_string(),
Ok(None) => Err(Status::new(Code::Internal, "No user found"))?,
Err(err) => Err(Status::new(Code::Internal, err.to_string()))?,
}
let uid: String;
let temp: User;

if let Some(user_credentials) = message.user_credentials {
if let Some(user) = user_credentials.user {
temp = match user {
user_credentials::User::Username(username) => {
match self.user_context.get_by_username(username).await {
Ok(Some(user)) => user,
Ok(None) => {
return Err(Status::new(
Code::Internal,
"No user found with given username",
))
}
user_credentials::User::Email(email) => {
match self.user_context.get_by_email(email).await {
Ok(Some(user)) => user.id.to_string(),
Ok(None) => Err(Status::new(Code::Internal, "No user found"))?,
Err(err) => Err(Status::new(Code::Internal, err.to_string()))?,
}
Err(err) => return Err(Status::new(Code::Internal, err.to_string())),
}
}
user_credentials::User::Email(email) => {
match self.user_context.get_by_email(email).await {
Ok(Some(user)) => user,
Ok(None) => {
return Err(Status::new(
Code::Internal,
"No user found with given email",
))
}
Err(err) => return Err(Status::new(Code::Internal, err.to_string())),
}
} else {
Err(Status::new(Code::Internal, "No user provided"))?
}
};

uid = temp.id.to_string();

if user_credentials.password != temp.password {
return Err(Status::new(Code::Unauthenticated, "Wrong password"));
}
},
None => Err(Status::new(Code::Internal, "No auth option provided"))?,
};
} else {
return Err(Status::new(Code::Internal, "No user provided"));
}
} else {
let refresh_token = auth::get_token_from_request(&request)?;
let token_data = auth::validate_token(refresh_token, true)?;
uid = token_data.claims.sub;
}

let access_token = match auth::create_access_token(&uid) {
Ok(token) => token,
Expand All @@ -258,6 +269,7 @@ impl EcdarApiAuth for ConcreteEcdarApi {
refresh_token,
}))
}

async fn create_user(
&self,
request: Request<CreateUserRequest>,
Expand Down
2 changes: 1 addition & 1 deletion src/api/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ pub async fn start_grpc_server(
.add_service(EcdarApiAuthServer::new(svc.clone()))
.add_service(EcdarApiServer::with_interceptor(
svc.clone(),
auth::token_validation,
auth::validation_interceptor,
))
.add_service(EcdarBackendServer::new(svc.clone()))
.serve(addr)
Expand Down
13 changes: 3 additions & 10 deletions src/tests/api/ecdar_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ mod ecdar_api {
use crate::api::ecdar_api::helpers::helpers::AnyEntity;
use crate::api::ecdar_api::ConcreteEcdarApi;
use crate::api::server::server::ecdar_api_auth_server::EcdarApiAuth;
use crate::api::server::server::{CreateUserRequest, DeleteUserRequest, UpdateUserRequest};
use crate::api::server::server::{CreateUserRequest, UpdateUserRequest};
use crate::database::entity_context::EntityContextTrait;
use crate::database::user_context::UserContextTrait;
use crate::{api::server::server::ecdar_api_server::EcdarApi, entities::user::Model as User};
Expand All @@ -16,9 +16,7 @@ mod ecdar_api {
async fn delete_user_nonexistent_user_returns_err() {
let api = ConcreteEcdarApi::setup_in_memory_db(vec![AnyEntity::User]).await;

let mut delete_request = Request::new(DeleteUserRequest {
token: "token".to_owned(),
});
let mut delete_request = Request::new({});

// Insert uid into request metadata
delete_request
Expand All @@ -45,9 +43,7 @@ mod ecdar_api {
.await
.unwrap();

let mut delete_request = Request::new(DeleteUserRequest {
token: "token".to_owned(),
});
let mut delete_request = Request::new({});

// Insert uid into request metadata
delete_request
Expand Down Expand Up @@ -199,7 +195,6 @@ mod ecdar_api {
api.user_context.create(user.clone()).await.unwrap();

let mut update_user_request = Request::new(UpdateUserRequest {
token: "token".to_string(),
email: Some("new_test@test".to_string()),
username: Some("new_test_user".to_string()),
password: Some("new_test_pass".to_string()),
Expand All @@ -219,7 +214,6 @@ mod ecdar_api {
let api = ConcreteEcdarApi::setup_in_memory_db(vec![AnyEntity::User]).await;

let mut update_user_request = Request::new(UpdateUserRequest {
token: "token".to_string(),
email: Some("new_test@test".to_string()),
username: Some("new_test_user".to_string()),
password: Some("new_test_pass".to_string()),
Expand Down Expand Up @@ -248,7 +242,6 @@ mod ecdar_api {
api.user_context.create(user.clone()).await.unwrap();

let mut update_user_request = Request::new(UpdateUserRequest {
token: "token".to_string(),
email: Some("new_test@test".to_string()),
username: None,
password: None,
Expand Down

0 comments on commit 47d9cb4

Please sign in to comment.