ForgeFlux/src/auth/application/services/process_authorization_response/service.rs
2024-05-08 15:02:02 +05:30

143 lines
4.7 KiB
Rust

use std::sync::Arc;
use url::Url;
use crate::auth::application::port::out::db::{
delete_oauth_state::DeleteOAuthState, oauth_state_exists::OAuthStateExists,
save_oauth_access_token::SaveOAuthAccessToken,
};
use crate::auth::application::port::out::forge::{
get_username::GetUsername, request_access_token::RequestAccessToken,
};
use super::{command, errors::*, ProcessAuthorizationResponseUserCase};
const STATE_LEN: usize = 8;
pub struct ProcessAuthorizationResponseProcessAuthorizationService {
oauth_state_exists_adapter: Arc<dyn OAuthStateExists>,
delete_oauth_state_adapter: Arc<dyn DeleteOAuthState>,
save_oauth_access_token_adapter: Arc<dyn SaveOAuthAccessToken>,
request_access_token_adapter: Arc<dyn RequestAccessToken>,
get_username_adapter: Arc<dyn GetUsername>,
process_authorization_response_redirect_uri: Url,
}
impl ProcessAuthorizationResponseProcessAuthorizationService {
pub fn new(
oauth_state_exists_adapter: Arc<dyn OAuthStateExists>,
delete_oauth_state_adapter: Arc<dyn DeleteOAuthState>,
save_oauth_access_token_adapter: Arc<dyn SaveOAuthAccessToken>,
request_access_token_adapter: Arc<dyn RequestAccessToken>,
get_username_adapter: Arc<dyn GetUsername>,
process_authorization_response_redirect_uri: Url,
) -> Self {
Self {
oauth_state_exists_adapter,
delete_oauth_state_adapter,
save_oauth_access_token_adapter,
request_access_token_adapter,
get_username_adapter,
process_authorization_response_redirect_uri,
}
}
}
#[async_trait::async_trait]
impl ProcessAuthorizationResponseUserCase
for ProcessAuthorizationResponseProcessAuthorizationService
{
async fn process_authorization_response(
&self,
cmd: command::ProcessAuthorizationResponseCommand,
) -> ProcessAuthorizationServiceResult<()> {
if let Some(u) = cmd.redirect_uri() {
if u.host() != self.process_authorization_response_redirect_uri.host()
&& u.path() != self.process_authorization_response_redirect_uri.path()
{
return Err(ProcessAuthorizationServiceError::BadRequest);
}
}
if !self
.oauth_state_exists_adapter
.oauth_state_exists(
cmd.state(),
cmd.oauth_provider(),
&self.process_authorization_response_redirect_uri,
)
.await?
{
return Err(ProcessAuthorizationServiceError::BadRequest);
}
self.delete_oauth_state_adapter
.delete_oauth_state(cmd.state(), cmd.oauth_provider())
.await?;
let access_token = self
.request_access_token_adapter
.request_access_token(
cmd.code().into(),
self.process_authorization_response_redirect_uri.clone(),
)
.await?;
let username = self
.get_username_adapter
.get_username(&access_token)
.await?;
self.save_oauth_access_token_adapter
.save_oauth_access_token(&username, &cmd.oauth_provider(), &access_token)
.await?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::auth::application::port::out::db::{
delete_oauth_state::tests::*, oauth_state_exists::tests::*,
save_oauth_access_token::tests::*,
};
use crate::auth::application::port::out::forge::{
get_username::tests::*, request_access_token::tests::*,
};
use crate::tests::bdd::*;
#[actix_rt::test]
async fn test_process_authorization_response() {
let username = "foo";
let state = "bar";
let code = "baz";
let oauth_provider = "test_process_authorization_response_service";
let url = Url::parse(&format!("http://{oauth_provider}")).unwrap();
let mut redirect_uri = url.clone();
redirect_uri.set_query(Some(&format!("state={state}")));
let cmd = command::ProcessAuthorizationResponseCommand::new_command(
Some(redirect_uri.clone()),
state.into(),
code.into(),
oauth_provider.into(),
)
.unwrap();
let s = ProcessAuthorizationResponseProcessAuthorizationService::new(
mock_oauth_state_exists(IS_CALLED_ONLY_ONCE, RETURNS_TRUE),
mock_delete_oauth_state(IS_CALLED_ONLY_ONCE),
mock_save_oauth_access_token(IS_CALLED_ONLY_ONCE),
mock_request_access_token(IS_CALLED_ONLY_ONCE),
mock_get_username(username.into(), IS_CALLED_ONLY_ONCE),
redirect_uri,
);
s.process_authorization_response(cmd).await.unwrap();
}
}