diff options
-rw-r--r-- | Cargo.lock | 75 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | tests/ci_test.rs | 103 |
3 files changed, 178 insertions, 1 deletions
@@ -90,6 +90,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d606d0fba62e13cf04db20536c05cb7f13673c161cb47a47a82b9b9e7d3f1daa" +dependencies = [ + "cookie", + "idna 0.2.3", + "log", + "publicsuffix", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + +[[package]] name = "core-foundation" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -358,6 +386,27 @@ dependencies = [ [[package]] name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" @@ -428,6 +477,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] name = "memchr" version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -586,6 +641,22 @@ dependencies = [ ] [[package]] +name = "psl-types" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + +[[package]] +name = "publicsuffix" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a8c1bda5ae1af7f99a2962e49df150414a43d62404644d98dd5c3a93d07457" +dependencies = [ + "idna 0.3.0", + "psl-types", +] + +[[package]] name = "quote" version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -611,6 +682,8 @@ checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ "base64", "bytes", + "cookie", + "cookie_store", "encoding_rs", "futures-core", "futures-util", @@ -1005,7 +1078,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", - "idna", + "idna 0.5.0", "percent-encoding", "serde", ] @@ -24,4 +24,5 @@ zeroize = "1.7.0" [dev-dependencies] eyre = "0.6.9" +reqwest = { version = "0.11.18", features = ["cookies"] } tokio = { version = "1.29.1", features = ["net", "fs", "rt", "macros"] } diff --git a/tests/ci_test.rs b/tests/ci_test.rs index 6eca774..269c522 100644 --- a/tests/ci_test.rs +++ b/tests/ci_test.rs @@ -430,3 +430,106 @@ async fn admin() { .await .expect("failed to delete hook"); } + +#[tokio::test] +async fn oauth2_login() { + let api = get_api(); + let opt = forgejo_api::structs::CreateOAuth2ApplicationOptions { + confidential_client: Some(true), + name: Some("Test Application".into()), + redirect_uris: Some(vec!["http://127.0.0.1:48879/".into()]), + }; + let app = api.user_create_oauth2_application(opt).await.unwrap(); + let client_id = app.client_id.unwrap(); + let client_secret = app.client_secret.unwrap(); + + let base_url = &std::env::var("FORGEJO_API_CI_INSTANCE_URL").unwrap(); + + let client = reqwest::Client::builder() + .cookie_store(true) + .redirect(reqwest::redirect::Policy::none()) + .build() + .unwrap(); + + // Log in via the web interface + let _ = client + .post(&format!("{base_url}user/login")) + .form(&[("user_name", "TestingAdmin"), ("password", "password")]) + .send() + .await + .unwrap() + .error_for_status() + .unwrap(); + + // Load the authorization page + let response = client + .get(&format!( + "{base_url}login/oauth/authorize\ + ?client_id={client_id}\ + &redirect_uri=http%3A%2F%2F127.0.0.1%3A48879%2F\ + &response_type=code\ + &state=theyve" + )) + .send() + .await + .unwrap() + .error_for_status() + .unwrap(); + let csrf = response.cookies().find(|x| x.name() == "_csrf").unwrap(); + + // Authorize the new application via the web interface + let response = client + .post(&format!("{base_url}login/oauth/grant")) + .form(&[ + ("_csrf", csrf.value()), + ("client_id", &client_id), + ("state", "theyve"), + ("scope", ""), + ("nonce", ""), + ("redirect_uri", "http://127.0.0.1:48879/"), + ]) + .send() + .await + .unwrap() + .error_for_status() + .unwrap(); + + // Extract the code from the redirect url + let location = response.headers().get(reqwest::header::LOCATION).unwrap(); + let location = url::Url::parse(dbg!(location.to_str().unwrap())).unwrap(); + let mut code = None; + for (key, value) in location.query_pairs() { + if key == "code" { + code = Some(value.into_owned()); + } else if key == "error_description" { + panic!("{value}"); + } + } + let code = code.unwrap(); + + // Redeem the code and check it works + let url = url::Url::parse(&base_url).unwrap(); + let api = Forgejo::new(forgejo_api::Auth::None, url.clone()).unwrap(); + + let request = forgejo_api::structs::OAuthTokenRequest::Confidential { + client_id: &client_id, + client_secret: &client_secret, + code: &code, + redirect_uri: url::Url::parse("http://127.0.0.1:48879/").unwrap(), + }; + let token = api.oauth_get_access_token(request).await.unwrap(); + let token_api = + Forgejo::new(forgejo_api::Auth::OAuth2(&token.access_token), url.clone()).unwrap(); + let myself = token_api.user_get_current().await.unwrap(); + assert_eq!(myself.login.as_deref(), Some("TestingAdmin")); + + let request = forgejo_api::structs::OAuthTokenRequest::Refresh { + refresh_token: &token.refresh_token, + client_id: &client_id, + client_secret: &client_secret, + }; + let token = token_api.oauth_get_access_token(request).await.unwrap(); + let token_api = Forgejo::new(forgejo_api::Auth::OAuth2(&token.access_token), url).unwrap(); + let myself = token_api.user_get_current().await.unwrap(); + assert_eq!(myself.login.as_deref(), Some("TestingAdmin")); +} |