Compare commits
12 commits
aeffe0457c
...
5dfb926dc8
Author | SHA1 | Date | |
---|---|---|---|
Aravinth Manivannan | 5dfb926dc8 | ||
Aravinth Manivannan | 81e3bf68d1 | ||
Aravinth Manivannan | 64e76cd75a | ||
Aravinth Manivannan | 023f80b896 | ||
Aravinth Manivannan | 48ab1c9238 | ||
Aravinth Manivannan | 57d0579971 | ||
Aravinth Manivannan | 8eff4adbb8 | ||
Aravinth Manivannan | c82e2ea4a2 | ||
Aravinth Manivannan | 54840987b5 | ||
Aravinth Manivannan | e1ae648c8b | ||
Aravinth Manivannan | 6a40fcdf50 | ||
Aravinth Manivannan | 77502c8732 |
94
Cargo.lock
generated
94
Cargo.lock
generated
|
@ -261,6 +261,16 @@ version = "1.0.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
|
||||
|
||||
[[package]]
|
||||
name = "assert-json-diff"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.80"
|
||||
|
@ -455,6 +465,16 @@ dependencies = [
|
|||
"phf_codegen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "config"
|
||||
version = "0.14.0"
|
||||
|
@ -829,6 +849,7 @@ dependencies = [
|
|||
"lazy_static",
|
||||
"log",
|
||||
"mockall",
|
||||
"mockito",
|
||||
"pretty_env_logger",
|
||||
"rand",
|
||||
"reqwest",
|
||||
|
@ -1113,6 +1134,17 @@ dependencies = [
|
|||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http 0.2.12",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "1.0.0"
|
||||
|
@ -1132,7 +1164,7 @@ dependencies = [
|
|||
"bytes",
|
||||
"futures-core",
|
||||
"http 1.1.0",
|
||||
"http-body",
|
||||
"http-body 1.0.0",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
|
@ -1163,6 +1195,29 @@ version = "2.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2 0.3.26",
|
||||
"http 0.2.12",
|
||||
"http-body 0.4.6",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.3.1"
|
||||
|
@ -1174,7 +1229,7 @@ dependencies = [
|
|||
"futures-util",
|
||||
"h2 0.4.4",
|
||||
"http 1.1.0",
|
||||
"http-body",
|
||||
"http-body 1.0.0",
|
||||
"httparse",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
|
@ -1191,7 +1246,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
|
|||
dependencies = [
|
||||
"bytes",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper 1.3.1",
|
||||
"hyper-util",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
|
@ -1209,8 +1264,8 @@ dependencies = [
|
|||
"futures-channel",
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"http-body 1.0.0",
|
||||
"hyper 1.3.1",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio",
|
||||
|
@ -1498,6 +1553,25 @@ dependencies = [
|
|||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mockito"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2f6e023aa5bdf392aa06c78e4a4e6d498baab5138d0c993503350ebbc37bf1e"
|
||||
dependencies = [
|
||||
"assert-json-diff",
|
||||
"colored",
|
||||
"futures-core",
|
||||
"hyper 0.14.28",
|
||||
"log",
|
||||
"rand",
|
||||
"regex",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"similar",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mutually_exclusive_features"
|
||||
version = "0.0.3"
|
||||
|
@ -2011,9 +2085,9 @@ dependencies = [
|
|||
"futures-util",
|
||||
"h2 0.4.4",
|
||||
"http 1.1.0",
|
||||
"http-body",
|
||||
"http-body 1.0.0",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper 1.3.1",
|
||||
"hyper-tls",
|
||||
"hyper-util",
|
||||
"ipnet",
|
||||
|
@ -2368,6 +2442,12 @@ dependencies = [
|
|||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "similar"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.11"
|
||||
|
|
|
@ -34,3 +34,4 @@ mockall = "0.12"
|
|||
[dev-dependencies]
|
||||
actix-rt= "2.9"
|
||||
mockall = { version = "0.12", features = ["nightly"] }
|
||||
mockito = "1.4.0"
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use actix_web::{http::header, HttpResponse};
|
||||
use url::Url;
|
||||
|
||||
use crate::auth::application::port::input::ui::{errors::*, login::RequestAuthorizationInterface};
|
||||
|
@ -38,7 +37,7 @@ impl RequestAuthorizationHandler {
|
|||
#[async_trait::async_trait]
|
||||
impl RequestAuthorizationInterface for RequestAuthorizationHandler {
|
||||
#[tracing::instrument(name = "web adapter request_oauth_authorization", skip(self))]
|
||||
async fn request_oauth_authorization(&self, forge_name: String) -> InUIResult<HttpResponse> {
|
||||
async fn request_oauth_authorization(&self, forge_name: String) -> InUIResult<Url> {
|
||||
let service = RequestAuthorizationService::new(
|
||||
self.save_oauth_state_adapter.clone(),
|
||||
self.oauth_auth_req_uri_adapter.clone(),
|
||||
|
@ -48,16 +47,12 @@ impl RequestAuthorizationInterface for RequestAuthorizationHandler {
|
|||
|
||||
let cmd = RequestAuthorizationCommand::new_command(forge_name)?;
|
||||
let auth_page = service.request_authorization(cmd).await?;
|
||||
Ok(HttpResponse::Found()
|
||||
.insert_header((header::LOCATION, auth_page.as_str()))
|
||||
.finish())
|
||||
Ok(auth_page)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use actix_web::http::{header, StatusCode};
|
||||
|
||||
use super::*;
|
||||
use crate::auth::application::port::out::{
|
||||
db::save_oauth_state::tests::*, forge::oauth_auth_req_uri::tests::*,
|
||||
|
@ -89,14 +84,6 @@ mod tests {
|
|||
.request_oauth_authorization(oauth_provider.into())
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(res.status(), StatusCode::FOUND);
|
||||
assert_eq!(
|
||||
res.headers()
|
||||
.get(header::LOCATION)
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
redirect_uri.as_str()
|
||||
);
|
||||
assert_eq!(res, redirect_uri);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use actix_web::{post, web, HttpResponse};
|
||||
use actix_web::{http::header, post, web, HttpResponse};
|
||||
use url::Url;
|
||||
|
||||
use super::types;
|
||||
|
@ -47,9 +47,13 @@ async fn handler(
|
|||
process_authorization_response_redirect_uri,
|
||||
);
|
||||
|
||||
web_adapter
|
||||
let redirect_to = web_adapter
|
||||
.request_oauth_authorization(SupportedForges::Forgejo.to_string())
|
||||
.await
|
||||
.await?;
|
||||
|
||||
Ok(HttpResponse::Found()
|
||||
.insert_header((header::LOCATION, redirect_to.as_str()))
|
||||
.finish())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -79,7 +79,7 @@ mod tests {
|
|||
let page = LoginPageTemplate.get_login_page(ctx).unwrap();
|
||||
for forge in forges.iter() {
|
||||
assert!(page.contains(&forge.to_string()));
|
||||
assert!(page.contains(&routes.oauth_login(&forge)));
|
||||
assert!(page.contains(&routes.oauth_login(forge)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ use std::sync::Arc;
|
|||
use actix_web::web;
|
||||
|
||||
pub mod login;
|
||||
mod process_authorization;
|
||||
mod routes;
|
||||
mod template;
|
||||
pub mod types;
|
||||
|
@ -15,6 +16,7 @@ pub fn load_ctx() -> impl FnOnce(&mut web::ServiceConfig) {
|
|||
let f = move |cfg: &mut web::ServiceConfig| {
|
||||
cfg.app_data(routes);
|
||||
cfg.configure(login::services);
|
||||
cfg.configure(process_authorization::services);
|
||||
};
|
||||
|
||||
Box::new(f)
|
||||
|
|
141
src/auth/adapter/input/web/process_authorization/adapter.rs
Normal file
141
src/auth/adapter/input/web/process_authorization/adapter.rs
Normal file
|
@ -0,0 +1,141 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use url::Url;
|
||||
|
||||
use crate::auth::application::port::input::ui::{
|
||||
errors::*, process_authorization::ProcessAuthorizationInterface,
|
||||
};
|
||||
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 crate::auth::application::services::process_authorization_response::{
|
||||
command::ProcessAuthorizationResponseCommand, service::ProcessAuthorizationResponseService,
|
||||
ProcessAuthorizationResponseUseCase,
|
||||
};
|
||||
|
||||
pub struct ProcessAuthorizationAdapter {
|
||||
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 ProcessAuthorizationAdapter {
|
||||
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 ProcessAuthorizationInterface for ProcessAuthorizationAdapter {
|
||||
#[tracing::instrument(name = "web adapter process_authorization", skip(self, code))]
|
||||
async fn process_authorization(
|
||||
&self,
|
||||
code: String,
|
||||
state: String,
|
||||
oauth_provider: String,
|
||||
redirect_uri: Option<Url>,
|
||||
) -> InUIResult<()> {
|
||||
let service = ProcessAuthorizationResponseService::new(
|
||||
self.oauth_state_exists_adapter.clone(),
|
||||
self.delete_oauth_state_adapter.clone(),
|
||||
self.save_oauth_access_token_adapter.clone(),
|
||||
self.request_access_token_adapter.clone(),
|
||||
self.get_username_adapter.clone(),
|
||||
self.process_authorization_response_redirect_uri.clone(),
|
||||
);
|
||||
|
||||
let cmd = ProcessAuthorizationResponseCommand::new_command(
|
||||
redirect_uri,
|
||||
state,
|
||||
code,
|
||||
oauth_provider,
|
||||
)?;
|
||||
service.process_authorization_response(cmd).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::*,
|
||||
},
|
||||
forge::{get_username::tests::*, request_access_token::tests::*},
|
||||
};
|
||||
use crate::tests::bdd::*;
|
||||
|
||||
#[actix_web::test]
|
||||
async fn test_adapter() {
|
||||
let random_string = "foorand";
|
||||
let username = "processauthouathusername";
|
||||
let url = Url::parse("http://test_ui_req_auth_interface_adapter").unwrap();
|
||||
let oauth_provider = "test_ui_req_auth_interface_adapter";
|
||||
let code = "code";
|
||||
let state = "state";
|
||||
let mut redirect_uri = url.clone();
|
||||
redirect_uri.set_query(Some(&format!("state={random_string}")));
|
||||
|
||||
// authorization response with redirect_uri = None
|
||||
{
|
||||
let adapter = ProcessAuthorizationAdapter::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),
|
||||
url.clone(),
|
||||
);
|
||||
|
||||
adapter
|
||||
.process_authorization(code.into(), state.into(), oauth_provider.into(), None)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// authorization response with redirect_uri = Some(Url)
|
||||
{
|
||||
let adapter = ProcessAuthorizationAdapter::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),
|
||||
url.clone(),
|
||||
);
|
||||
|
||||
adapter
|
||||
.process_authorization(
|
||||
code.into(),
|
||||
state.into(),
|
||||
oauth_provider.into(),
|
||||
Some(url.clone()),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
189
src/auth/adapter/input/web/process_authorization/handlers.rs
Normal file
189
src/auth/adapter/input/web/process_authorization/handlers.rs
Normal file
|
@ -0,0 +1,189 @@
|
|||
use actix_web::{get, web, HttpResponse};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
use super::types;
|
||||
use crate::auth::adapter::out::forge::SupportedForges;
|
||||
use crate::auth::application::port::input::ui::{
|
||||
errors::*, process_authorization::ProcessAuthorizationInterface,
|
||||
};
|
||||
|
||||
pub fn services(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(handler);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
|
||||
struct ProcessAuthorizationReqQueryParams {
|
||||
code: String,
|
||||
state: String,
|
||||
redirect_uri: Option<Url>,
|
||||
}
|
||||
|
||||
#[get("/oauth/forgejo/authorize")]
|
||||
#[tracing::instrument(
|
||||
name = "login page handler",
|
||||
skip(
|
||||
forges,
|
||||
oauth_state_exists_adapter,
|
||||
delete_oauth_state_adapter,
|
||||
save_oauth_access_token_adapter,
|
||||
routes,
|
||||
settings,
|
||||
q,
|
||||
)
|
||||
)]
|
||||
async fn handler(
|
||||
forges: types::WebForgeRepositoryInterface,
|
||||
oauth_state_exists_adapter: types::WebOauthStateExists,
|
||||
delete_oauth_state_adapter: types::WebDeleteOauthState,
|
||||
save_oauth_access_token_adapter: types::WebSaveOAuthAccessToken,
|
||||
routes: types::WebRouteRepository,
|
||||
settings: types::WebSettings,
|
||||
q: web::Query<ProcessAuthorizationReqQueryParams>,
|
||||
) -> InUIResult<HttpResponse> {
|
||||
let request_access_token_adapter = forges
|
||||
.get_forge_factory(&SupportedForges::Forgejo)
|
||||
.unwrap()
|
||||
.request_access_token_adapter();
|
||||
|
||||
let get_username_adapter = forges
|
||||
.get_forge_factory(&SupportedForges::Forgejo)
|
||||
.unwrap()
|
||||
.get_username_adapter();
|
||||
|
||||
let process_authorization_response_redirect_uri = Url::parse(&format!(
|
||||
"{}://{}{}",
|
||||
"http",
|
||||
&settings.server.domain,
|
||||
&routes.process_oauth_authorization_response(&SupportedForges::Forgejo)
|
||||
))
|
||||
.map_err(|_| InUIError::InternalServerError)?;
|
||||
|
||||
let web_adapter = super::adapter::ProcessAuthorizationAdapter::new(
|
||||
oauth_state_exists_adapter.as_ref().clone(),
|
||||
delete_oauth_state_adapter.as_ref().clone(),
|
||||
save_oauth_access_token_adapter.as_ref().clone(),
|
||||
request_access_token_adapter.clone(),
|
||||
get_username_adapter.clone(),
|
||||
process_authorization_response_redirect_uri,
|
||||
);
|
||||
let q = q.into_inner();
|
||||
|
||||
web_adapter
|
||||
.process_authorization(
|
||||
q.code,
|
||||
q.state,
|
||||
SupportedForges::Forgejo.to_string(),
|
||||
q.redirect_uri,
|
||||
)
|
||||
.await?;
|
||||
Ok(HttpResponse::Ok().finish())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::*;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::{http::header::ContentType, test, App};
|
||||
|
||||
use crate::auth::adapter::input::web::routes::RoutesRepository;
|
||||
use crate::auth::adapter::out::forge::forge_factory::{
|
||||
ForgeAdapterFactoryInterface, MockForgeAdapterFactoryInterface,
|
||||
};
|
||||
use crate::auth::adapter::out::forge::forge_repository::MockForgeRepositoryInterface;
|
||||
|
||||
use crate::auth::application::port::out::{
|
||||
db::{
|
||||
delete_oauth_state::tests::*, oauth_state_exists::tests::*,
|
||||
save_oauth_access_token::tests::*,
|
||||
},
|
||||
forge::{get_username::tests::*, request_access_token::tests::*},
|
||||
};
|
||||
use crate::tests::bdd::*;
|
||||
|
||||
#[actix_web::test]
|
||||
async fn test_ui_handler_process_oauth_authorization_forgejo() {
|
||||
let username = "foorand";
|
||||
let state = "hstate";
|
||||
let code = "hcode";
|
||||
let settings = crate::settings::Settings::new().unwrap();
|
||||
|
||||
let mock_delete_oauth_state =
|
||||
types::WebDeleteOauthState::new(mock_delete_oauth_state(IS_CALLED_ONLY_ONCE));
|
||||
|
||||
let mock_oauth_state_exists = types::WebOauthStateExists::new(mock_oauth_state_exists(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
RETURNS_TRUE,
|
||||
));
|
||||
|
||||
let mock_save_oauth_access_token =
|
||||
types::WebSaveOAuthAccessToken::new(mock_save_oauth_access_token(IS_CALLED_ONLY_ONCE));
|
||||
|
||||
let mock_forges = {
|
||||
let mut mock_forge_factory = MockForgeAdapterFactoryInterface::default();
|
||||
mock_forge_factory
|
||||
.expect_request_access_token_adapter()
|
||||
.times(IS_CALLED_ONLY_ONCE.unwrap())
|
||||
.returning(|| mock_request_access_token(IS_CALLED_ONLY_ONCE));
|
||||
|
||||
mock_forge_factory
|
||||
.expect_get_username_adapter()
|
||||
.times(IS_CALLED_ONLY_ONCE.unwrap())
|
||||
.returning(|| mock_get_username(username.into(), IS_CALLED_ONLY_ONCE));
|
||||
|
||||
let mock_forge_factory: Arc<dyn ForgeAdapterFactoryInterface> =
|
||||
Arc::new(mock_forge_factory);
|
||||
|
||||
let mut mock_forges = MockForgeRepositoryInterface::default();
|
||||
mock_forges
|
||||
.expect_get_forge_factory()
|
||||
.times(IS_CALLED_ONLY_TWICE.unwrap())
|
||||
.returning(move |_| Some(mock_forge_factory.clone()));
|
||||
|
||||
types::WebForgeRepositoryInterface::new(Arc::new(mock_forges))
|
||||
};
|
||||
|
||||
let routes = RoutesRepository::default();
|
||||
|
||||
let process_authorization_response_redirect_uri = Url::parse(&format!(
|
||||
"{}://{}{}",
|
||||
"http",
|
||||
&settings.server.domain,
|
||||
&routes.process_oauth_authorization_response(&SupportedForges::Forgejo)
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
let app = test::init_service(
|
||||
App::new()
|
||||
.wrap(tracing_actix_web::TracingLogger::default())
|
||||
.app_data(mock_oauth_state_exists)
|
||||
.app_data(mock_save_oauth_access_token)
|
||||
.app_data(mock_forges)
|
||||
.app_data(mock_delete_oauth_state)
|
||||
.app_data(web::Data::new(Arc::new(routes.clone())))
|
||||
.app_data(web::Data::new(settings.clone()))
|
||||
.service(handler),
|
||||
)
|
||||
.await;
|
||||
|
||||
let path = {
|
||||
let mut u = process_authorization_response_redirect_uri.clone();
|
||||
u.set_path("/oauth/forgejo/authorize");
|
||||
u.set_query(Some(&format!(
|
||||
"state={state}&code={code}&redirect_uri={}",
|
||||
process_authorization_response_redirect_uri
|
||||
)));
|
||||
format!("{}?{}", u.path(), u.query().unwrap())
|
||||
};
|
||||
println!("path: {path}");
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&path)
|
||||
.insert_header(ContentType::html())
|
||||
.to_request();
|
||||
let resp = test::call_service(&app, req).await;
|
||||
let status = resp.status();
|
||||
assert_eq!(status, StatusCode::OK);
|
||||
}
|
||||
}
|
10
src/auth/adapter/input/web/process_authorization/mod.rs
Normal file
10
src/auth/adapter/input/web/process_authorization/mod.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
use actix_web::web;
|
||||
|
||||
use super::types;
|
||||
|
||||
mod adapter;
|
||||
mod handlers;
|
||||
|
||||
pub fn services(cfg: &mut web::ServiceConfig) {
|
||||
handlers::services(cfg);
|
||||
}
|
|
@ -7,9 +7,6 @@ use crate::auth::application::port::out::db::{
|
|||
delete_oauth_state::DeleteOAuthState, oauth_state_exists::OAuthStateExists,
|
||||
save_oauth_access_token::SaveOAuthAccessToken, save_oauth_state::SaveOAuthState,
|
||||
};
|
||||
use crate::auth::application::port::out::forge::{
|
||||
get_username::GetUsername, request_access_token::RequestAccessToken,
|
||||
};
|
||||
pub(super) use crate::utils::random_string::WebGenerateRandomStringInterface;
|
||||
|
||||
use super::RoutesRepository;
|
||||
|
@ -24,6 +21,3 @@ pub type WebSaveOauthState = web::Data<Arc<dyn SaveOAuthState>>;
|
|||
pub type WebOauthStateExists = web::Data<Arc<dyn OAuthStateExists>>;
|
||||
pub type WebDeleteOauthState = web::Data<Arc<dyn DeleteOAuthState>>;
|
||||
pub type WebSaveOAuthAccessToken = web::Data<Arc<dyn SaveOAuthAccessToken>>;
|
||||
|
||||
pub type WebGetUsername = web::Data<Arc<dyn GetUsername>>;
|
||||
pub type WebRequestAccessToken = web::Data<Arc<dyn RequestAccessToken>>;
|
||||
|
|
|
@ -4,11 +4,16 @@ use mockall::predicate::*;
|
|||
use mockall::*;
|
||||
|
||||
use super::forgejo::Forgejo;
|
||||
use crate::auth::application::port::out::forge::oauth_auth_req_uri::OAuthAuthReqUri;
|
||||
use crate::auth::application::port::out::forge::{
|
||||
get_username::GetUsername, oauth_auth_req_uri::OAuthAuthReqUri,
|
||||
request_access_token::RequestAccessToken,
|
||||
};
|
||||
|
||||
#[automock]
|
||||
pub trait ForgeAdapterFactoryInterface: Send + Sync {
|
||||
fn get_oauth_auth_req_uri_adapter(&self) -> Arc<dyn OAuthAuthReqUri>;
|
||||
fn oauth_auth_req_uri_adapter(&self) -> Arc<dyn OAuthAuthReqUri>;
|
||||
fn request_access_token_adapter(&self) -> Arc<dyn RequestAccessToken>;
|
||||
fn get_username_adapter(&self) -> Arc<dyn GetUsername>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -17,7 +22,13 @@ pub struct ForgeAdapterFactory {
|
|||
}
|
||||
|
||||
impl ForgeAdapterFactoryInterface for ForgeAdapterFactory {
|
||||
fn get_oauth_auth_req_uri_adapter(&self) -> Arc<dyn OAuthAuthReqUri> {
|
||||
fn oauth_auth_req_uri_adapter(&self) -> Arc<dyn OAuthAuthReqUri> {
|
||||
self.forgejo.clone()
|
||||
}
|
||||
fn request_access_token_adapter(&self) -> Arc<dyn RequestAccessToken> {
|
||||
self.forgejo.clone()
|
||||
}
|
||||
fn get_username_adapter(&self) -> Arc<dyn GetUsername> {
|
||||
self.forgejo.clone()
|
||||
}
|
||||
}
|
||||
|
|
82
src/auth/adapter/out/forge/forgejo/get_username.rs
Normal file
82
src/auth/adapter/out/forge/forgejo/get_username.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::Forgejo;
|
||||
use crate::auth::application::port::out::forge::{
|
||||
errors::OutForgePortResult, get_username::GetUsername,
|
||||
};
|
||||
use crate::auth::domain::OAuthAccessToken;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
struct ForgejoUserResponse {
|
||||
login: String,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl GetUsername for Forgejo {
|
||||
async fn get_username(&self, access_token: &OAuthAccessToken) -> OutForgePortResult<String> {
|
||||
let u = {
|
||||
let mut u = self.url().to_owned();
|
||||
u.set_path("/api/v1/user");
|
||||
u
|
||||
};
|
||||
let res = self
|
||||
.http_client
|
||||
.get(u)
|
||||
.header(
|
||||
reqwest::header::AUTHORIZATION,
|
||||
format!("token {}", access_token.access_token),
|
||||
)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
let res: ForgejoUserResponse = res.json().await.unwrap();
|
||||
Ok(res.login)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use url::Url;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_forgejo_get_username() {
|
||||
let client_id = "statetestpostgres";
|
||||
let client_secret = "oauthprovitestpostgres";
|
||||
let access_token = "access_token_uuu";
|
||||
let username = "access_token_user";
|
||||
|
||||
let mut settings = crate::settings::tests::get_settings().await;
|
||||
|
||||
let mut srv = mockito::Server::new_async().await;
|
||||
settings.forges.forgejo.url = Url::parse(&srv.url()).unwrap();
|
||||
|
||||
let f = Forgejo::new(
|
||||
settings.forges.forgejo.url.clone(),
|
||||
client_id.into(),
|
||||
client_secret.into(),
|
||||
);
|
||||
|
||||
let mock = {
|
||||
let resp = ForgejoUserResponse {
|
||||
login: username.into(),
|
||||
};
|
||||
srv.mock("GET", "/api/v1/user")
|
||||
.with_status(200)
|
||||
.match_header("Authorization", format!("token {access_token}").as_str())
|
||||
.with_header("Content-Type", "application/json")
|
||||
.with_body(serde_json::to_string(&resp).unwrap())
|
||||
.create_async()
|
||||
.await
|
||||
};
|
||||
|
||||
let req = OAuthAccessToken {
|
||||
access_token: access_token.into(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
assert_eq!(f.get_username(&req).await.unwrap(), username);
|
||||
mock.assert_async().await;
|
||||
}
|
||||
}
|
|
@ -1,12 +1,16 @@
|
|||
use reqwest::Client;
|
||||
use url::Url;
|
||||
|
||||
pub mod get_username;
|
||||
pub mod oauth_auth_req_uri;
|
||||
pub mod request_access_token;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Forgejo {
|
||||
url: Url,
|
||||
client_id: String,
|
||||
client_secret: String,
|
||||
pub http_client: Client,
|
||||
}
|
||||
|
||||
impl Forgejo {
|
||||
|
@ -15,6 +19,7 @@ impl Forgejo {
|
|||
url,
|
||||
client_id,
|
||||
client_secret,
|
||||
http_client: Client::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
133
src/auth/adapter/out/forge/forgejo/request_access_token.rs
Normal file
133
src/auth/adapter/out/forge/forgejo/request_access_token.rs
Normal file
|
@ -0,0 +1,133 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
use super::Forgejo;
|
||||
use crate::auth::application::port::out::forge::{
|
||||
errors::OutForgePortResult, request_access_token::RequestAccessToken,
|
||||
};
|
||||
use crate::auth::domain::OAuthAccessToken;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
struct ForgejoAccessTokenResponse {
|
||||
refresh_token: String,
|
||||
access_token: String,
|
||||
token_type: String,
|
||||
expires_in: u64,
|
||||
}
|
||||
|
||||
impl From<ForgejoAccessTokenResponse> for OAuthAccessToken {
|
||||
fn from(v: ForgejoAccessTokenResponse) -> Self {
|
||||
let expires_at = Duration::from_secs(v.expires_in);
|
||||
Self {
|
||||
refresh_token: v.refresh_token,
|
||||
access_token: v.access_token,
|
||||
token_type: v.token_type,
|
||||
expires_at, // unix epoch of the instant
|
||||
expires_in: v.expires_in,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
struct ForgejoAccessTokenRequest {
|
||||
code: String,
|
||||
client_id: String,
|
||||
client_secret: String,
|
||||
grant_type: String,
|
||||
redirect_uri: Url,
|
||||
}
|
||||
|
||||
impl ForgejoAccessTokenRequest {
|
||||
pub fn new(code: String, redirect_uri: Url, forgejo: &Forgejo) -> Self {
|
||||
Self {
|
||||
code,
|
||||
client_id: forgejo.client_id.clone(),
|
||||
client_secret: forgejo.client_secret.clone(),
|
||||
grant_type: "authorization_code".into(),
|
||||
redirect_uri,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl RequestAccessToken for Forgejo {
|
||||
async fn request_access_token(
|
||||
&self,
|
||||
code: String,
|
||||
redirect_uri: Url,
|
||||
) -> OutForgePortResult<OAuthAccessToken> {
|
||||
let u = {
|
||||
let mut u = self.url().to_owned();
|
||||
u.set_path("/login/oauth/access_token");
|
||||
u
|
||||
};
|
||||
|
||||
let payload = ForgejoAccessTokenRequest::new(code, redirect_uri, self);
|
||||
let res: ForgejoAccessTokenResponse = self
|
||||
.http_client
|
||||
.post(u)
|
||||
.json(&payload)
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.json()
|
||||
.await
|
||||
.unwrap();
|
||||
Ok(res.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_forgejo_request_access_token() {
|
||||
let client_id = "statetestpostgres";
|
||||
let client_secret = "oauthprovitestpostgres";
|
||||
let code = "oauthprovitestpostgres";
|
||||
let redirect_uri = Url::parse("https://oauthprovitestpostgres").unwrap();
|
||||
|
||||
let mut settings = crate::settings::tests::get_settings().await;
|
||||
|
||||
let mut srv = mockito::Server::new_async().await;
|
||||
settings.forges.forgejo.url = Url::parse(&srv.url()).unwrap();
|
||||
|
||||
let data = ForgejoAccessTokenResponse {
|
||||
access_token: "foo".into(),
|
||||
expires_in: 9999,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let f = Forgejo::new(
|
||||
settings.forges.forgejo.url.clone(),
|
||||
client_id.into(),
|
||||
client_secret.into(),
|
||||
);
|
||||
|
||||
let mock = {
|
||||
let payload = ForgejoAccessTokenRequest::new(code.into(), redirect_uri.clone(), &f);
|
||||
|
||||
srv.mock("POST", "/login/oauth/access_token")
|
||||
.with_status(200)
|
||||
.match_header("content-type", "application/json")
|
||||
.match_body(mockito::Matcher::PartialJsonString(
|
||||
serde_json::to_string(&payload).unwrap(),
|
||||
))
|
||||
.with_header("Content-Type", "application/json")
|
||||
.with_body(serde_json::to_string(&data).unwrap())
|
||||
.create_async()
|
||||
.await
|
||||
};
|
||||
|
||||
let access_token = f
|
||||
.request_access_token(code.into(), redirect_uri)
|
||||
.await
|
||||
.unwrap();
|
||||
let expected_access_token: OAuthAccessToken = data.into();
|
||||
assert_eq!(expected_access_token, access_token);
|
||||
mock.assert_async().await;
|
||||
}
|
||||
}
|
2
src/auth/application/mod.rs
Normal file
2
src/auth/application/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub mod port;
|
||||
pub mod services;
|
1
src/auth/application/port/input/mod.rs
Normal file
1
src/auth/application/port/input/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod ui;
|
|
@ -1,7 +1,8 @@
|
|||
use super::errors::*;
|
||||
use actix_web::HttpResponse;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait RequestAuthorizationInterface: Send + Sync {
|
||||
async fn request_oauth_authorization(&self, forge_name: String) -> InUIResult<HttpResponse>;
|
||||
// TODO: input ports must not be dependent on the type of port. In this case,
|
||||
// it should return redirect URL only
|
||||
async fn request_oauth_authorization(&self, forge_name: String) -> InUIResult<url::Url>;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
pub mod errors;
|
||||
pub mod login;
|
||||
pub mod process_authorization;
|
||||
// login
|
||||
|
|
14
src/auth/application/port/input/ui/process_authorization.rs
Normal file
14
src/auth/application/port/input/ui/process_authorization.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
use url::Url;
|
||||
|
||||
use super::errors::*;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait ProcessAuthorizationInterface: Send + Sync {
|
||||
async fn process_authorization(
|
||||
&self,
|
||||
code: String,
|
||||
state: String,
|
||||
oauth_provider: String,
|
||||
redirect_uri: Option<Url>,
|
||||
) -> InUIResult<()>;
|
||||
}
|
2
src/auth/application/port/mod.rs
Normal file
2
src/auth/application/port/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub mod input;
|
||||
pub mod out;
|
32
src/auth/application/port/out/forge/get_username.rs
Normal file
32
src/auth/application/port/out/forge/get_username.rs
Normal file
|
@ -0,0 +1,32 @@
|
|||
use mockall::predicate::*;
|
||||
use mockall::*;
|
||||
|
||||
use super::errors::*;
|
||||
use crate::auth::domain::OAuthAccessToken;
|
||||
|
||||
#[automock]
|
||||
#[async_trait::async_trait]
|
||||
pub trait GetUsername: Send + Sync {
|
||||
async fn get_username(&self, access_token: &OAuthAccessToken) -> OutForgePortResult<String>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
pub fn mock_get_username(username: String, times: Option<usize>) -> Arc<dyn GetUsername> {
|
||||
let mut m = MockGetUsername::default();
|
||||
if let Some(times) = times {
|
||||
m.expect_get_username()
|
||||
.times(times)
|
||||
.returning(move |_| Ok(username.clone()));
|
||||
} else {
|
||||
m.expect_get_username()
|
||||
.returning(move |_| Ok(username.clone()));
|
||||
}
|
||||
|
||||
Arc::new(m)
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
use mockall::predicate::*;
|
||||
use mockall::*;
|
||||
use url::Url;
|
||||
|
||||
use super::errors::*;
|
||||
use crate::auth::domain::OAuthAccessToken;
|
||||
|
@ -7,7 +8,11 @@ use crate::auth::domain::OAuthAccessToken;
|
|||
#[automock]
|
||||
#[async_trait::async_trait]
|
||||
pub trait RequestAccessToken: Send + Sync {
|
||||
async fn request_access_token(&self, code: &str) -> OutForgePortResult<OAuthAccessToken>;
|
||||
async fn request_access_token(
|
||||
&self,
|
||||
code: String,
|
||||
redirect_uri: Url,
|
||||
) -> OutForgePortResult<OAuthAccessToken>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -21,10 +26,10 @@ pub mod tests {
|
|||
if let Some(times) = times {
|
||||
m.expect_request_access_token()
|
||||
.times(times)
|
||||
.returning(|_| Ok(OAuthAccessToken::default()));
|
||||
.returning(|_, _| Ok(OAuthAccessToken::default()));
|
||||
} else {
|
||||
m.expect_request_access_token()
|
||||
.returning(|_| Ok(OAuthAccessToken::default()));
|
||||
.returning(|_, _| Ok(OAuthAccessToken::default()));
|
||||
}
|
||||
|
||||
Arc::new(m)
|
||||
|
|
2
src/auth/application/port/out/mod.rs
Normal file
2
src/auth/application/port/out/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub mod db;
|
||||
pub mod forge;
|
|
@ -1,9 +1,9 @@
|
|||
mod command;
|
||||
pub mod command;
|
||||
pub mod errors;
|
||||
mod service;
|
||||
pub mod service;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait ProcessAuthorizationResponseUserCase {
|
||||
pub trait ProcessAuthorizationResponseUseCase {
|
||||
async fn process_authorization_response(
|
||||
&self,
|
||||
cmd: command::ProcessAuthorizationResponseCommand,
|
||||
|
|
|
@ -10,11 +10,9 @@ use crate::auth::application::port::out::forge::{
|
|||
get_username::GetUsername, request_access_token::RequestAccessToken,
|
||||
};
|
||||
|
||||
use super::{command, errors::*, ProcessAuthorizationResponseUserCase};
|
||||
use super::{command, errors::*, ProcessAuthorizationResponseUseCase};
|
||||
|
||||
const STATE_LEN: usize = 8;
|
||||
|
||||
pub struct ProcessAuthorizationResponseProcessAuthorizationService {
|
||||
pub struct ProcessAuthorizationResponseService {
|
||||
oauth_state_exists_adapter: Arc<dyn OAuthStateExists>,
|
||||
delete_oauth_state_adapter: Arc<dyn DeleteOAuthState>,
|
||||
save_oauth_access_token_adapter: Arc<dyn SaveOAuthAccessToken>,
|
||||
|
@ -23,7 +21,7 @@ pub struct ProcessAuthorizationResponseProcessAuthorizationService {
|
|||
process_authorization_response_redirect_uri: Url,
|
||||
}
|
||||
|
||||
impl ProcessAuthorizationResponseProcessAuthorizationService {
|
||||
impl ProcessAuthorizationResponseService {
|
||||
pub fn new(
|
||||
oauth_state_exists_adapter: Arc<dyn OAuthStateExists>,
|
||||
delete_oauth_state_adapter: Arc<dyn DeleteOAuthState>,
|
||||
|
@ -44,9 +42,7 @@ impl ProcessAuthorizationResponseProcessAuthorizationService {
|
|||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ProcessAuthorizationResponseUserCase
|
||||
for ProcessAuthorizationResponseProcessAuthorizationService
|
||||
{
|
||||
impl ProcessAuthorizationResponseUseCase for ProcessAuthorizationResponseService {
|
||||
async fn process_authorization_response(
|
||||
&self,
|
||||
cmd: command::ProcessAuthorizationResponseCommand,
|
||||
|
@ -55,7 +51,7 @@ impl ProcessAuthorizationResponseUserCase
|
|||
if u.host() != self.process_authorization_response_redirect_uri.host()
|
||||
&& u.path() != self.process_authorization_response_redirect_uri.path()
|
||||
{
|
||||
return Err(ProcessAuthorizationServiceError::BadRequest);
|
||||
return Err(ProcessAuthorizationServiceError::InteralError);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,7 +84,7 @@ impl ProcessAuthorizationResponseUserCase
|
|||
.await?;
|
||||
|
||||
self.save_oauth_access_token_adapter
|
||||
.save_oauth_access_token(&username, &cmd.oauth_provider(), &access_token)
|
||||
.save_oauth_access_token(&username, cmd.oauth_provider(), &access_token)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
|
@ -128,7 +124,7 @@ mod tests {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
let s = ProcessAuthorizationResponseProcessAuthorizationService::new(
|
||||
let s = ProcessAuthorizationResponseService::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),
|
||||
|
|
|
@ -71,8 +71,6 @@ impl RequestAuthorizationUserCase for RequestAuthorizationService {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::utils::random_string::tests::*;
|
||||
use crate::{
|
||||
auth::application::{
|
||||
|
|
36
src/auth/domain/mod.rs
Normal file
36
src/auth/domain/mod.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct OAuthAccessToken {
|
||||
//pub username: String,
|
||||
pub refresh_token: String,
|
||||
pub access_token: String,
|
||||
pub token_type: String,
|
||||
pub expires_at: Duration, // unix epoch of the instant
|
||||
pub expires_in: u64,
|
||||
}
|
||||
|
||||
impl OAuthAccessToken {
|
||||
pub fn new(
|
||||
// username: String,
|
||||
refresh_token: String,
|
||||
access_token: String,
|
||||
token_type: String,
|
||||
expires_in: u64,
|
||||
) -> Self {
|
||||
let mut expires_at = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
|
||||
expires_at = expires_at
|
||||
.checked_add(Duration::from_secs(expires_in))
|
||||
.unwrap();
|
||||
Self {
|
||||
// username,
|
||||
refresh_token,
|
||||
access_token,
|
||||
token_type,
|
||||
expires_in,
|
||||
expires_at,
|
||||
}
|
||||
}
|
||||
}
|
3
src/auth/mod.rs
Normal file
3
src/auth/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub mod adapter;
|
||||
mod application;
|
||||
mod domain;
|
|
@ -1,2 +1,4 @@
|
|||
pub const IS_CALLED_ONLY_ONCE: Option<usize> = Some(1);
|
||||
pub const IS_CALLED_ONLY_TWICE: Option<usize> = Some(2);
|
||||
|
||||
pub const RETURNS_TRUE: bool = true;
|
||||
|
|
Loading…
Reference in a new issue