143 lines
4.7 KiB
Rust
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();
|
|
}
|
|
}
|