115 lines
3.7 KiB
Rust
115 lines
3.7 KiB
Rust
use std::sync::Arc;
|
|
|
|
use url::Url;
|
|
|
|
use crate::auth::application::port::out::db::save_oauth_state::SaveOAuthState;
|
|
use crate::auth::application::port::out::forge::oauth_auth_req_uri::OAuthAuthReqUri;
|
|
use crate::{
|
|
auth::application::port::out::db::errors::OutDBPortError,
|
|
utils::random_string::GenerateRandomStringInterface,
|
|
};
|
|
|
|
use super::{errors::*, RequestAuthorizationUserCase};
|
|
|
|
const STATE_LEN: usize = 8;
|
|
|
|
#[derive(Clone)]
|
|
pub struct RequestAuthorizationService {
|
|
save_oauth_state_adapter: Arc<dyn SaveOAuthState>,
|
|
oauth_auth_req_uri_adapter: Arc<dyn OAuthAuthReqUri>,
|
|
process_authorization_response_redirect_uri: Url,
|
|
generate_random_string: Arc<dyn GenerateRandomStringInterface>,
|
|
}
|
|
|
|
impl RequestAuthorizationService {
|
|
pub fn new(
|
|
save_oauth_state_adapter: Arc<dyn SaveOAuthState>,
|
|
oauth_auth_req_uri_adapter: Arc<dyn OAuthAuthReqUri>,
|
|
process_authorization_response_redirect_uri: Url,
|
|
generate_random_string: Arc<dyn GenerateRandomStringInterface>,
|
|
) -> Self {
|
|
Self {
|
|
save_oauth_state_adapter,
|
|
oauth_auth_req_uri_adapter,
|
|
process_authorization_response_redirect_uri,
|
|
generate_random_string,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[async_trait::async_trait]
|
|
impl RequestAuthorizationUserCase for RequestAuthorizationService {
|
|
async fn request_authorization(
|
|
&self,
|
|
cmd: super::command::RequestAuthorizationCommand,
|
|
) -> RequestAuthorizationServiceResult<Url> {
|
|
let mut state = self.generate_random_string.get_random(STATE_LEN);
|
|
loop {
|
|
match self
|
|
.save_oauth_state_adapter
|
|
.save_oauth_state(
|
|
&state,
|
|
cmd.oauth_provider(),
|
|
&self.process_authorization_response_redirect_uri,
|
|
)
|
|
.await
|
|
{
|
|
Err(OutDBPortError::DuplicateState) => {
|
|
state = self.generate_random_string.get_random(STATE_LEN);
|
|
continue;
|
|
}
|
|
Ok(_) => break,
|
|
Err(e) => return Err(e)?,
|
|
}
|
|
}
|
|
let redirect = self
|
|
.oauth_auth_req_uri_adapter
|
|
.oauth_auth_req_uri(&state, &self.process_authorization_response_redirect_uri)?;
|
|
Ok(redirect)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::sync::Arc;
|
|
|
|
use crate::utils::random_string::tests::*;
|
|
use crate::{
|
|
auth::application::{
|
|
port::out::{db::save_oauth_state::tests::*, forge::oauth_auth_req_uri::tests::*},
|
|
services::request_authorization::command::RequestAuthorizationCommand,
|
|
},
|
|
tests::bdd::IS_CALLED_ONLY_ONCE,
|
|
};
|
|
|
|
use super::*;
|
|
|
|
#[actix_rt::test]
|
|
async fn test_service() {
|
|
let random_string = "foorand";
|
|
|
|
let url = Url::parse("http://test_service_request_auth").unwrap();
|
|
let oauth_provider = "test_service_request_auth_oauth_provider";
|
|
let mut redirect_uri = url.clone();
|
|
redirect_uri.set_query(Some(&format!("state={random_string}")));
|
|
|
|
let mock_random_generate_string =
|
|
mock_generate_random_string(IS_CALLED_ONLY_ONCE, random_string.into());
|
|
|
|
let mock_oauth_req_uri = mock_oauth_auth_req_uri(IS_CALLED_ONLY_ONCE, redirect_uri.clone());
|
|
|
|
let mock_save_oauth_state = mock_save_oauth_state(IS_CALLED_ONLY_ONCE);
|
|
|
|
let s = RequestAuthorizationService::new(
|
|
mock_save_oauth_state,
|
|
mock_oauth_req_uri,
|
|
url.clone(),
|
|
mock_random_generate_string,
|
|
);
|
|
let cmd = RequestAuthorizationCommand::new_command(oauth_provider.to_owned()).unwrap();
|
|
|
|
let res = s.request_authorization(cmd).await.unwrap();
|
|
assert_eq!(res, redirect_uri);
|
|
}
|
|
}
|