diff options
| -rw-r--r-- | Cargo.lock | 10 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | src/api/oauth.rs | 79 | ||||
| -rw-r--r-- | src/resources/templates.rs | 32 | ||||
| -rw-r--r-- | static/languages/en.ini | 10 | ||||
| -rw-r--r-- | static/templates/base.html | 2 | ||||
| -rw-r--r-- | static/templates/error.html | 7 |
7 files changed, 129 insertions, 12 deletions
@@ -1688,6 +1688,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "serde_variant", "sha2", "sqlx", "tera", @@ -1808,6 +1809,15 @@ dependencies = [ ] [[package]] +name = "serde_variant" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47a8ec0b2fd0506290348d9699c0e3eb2e3e8c0498b5a9a6158b3bd4d6970076" +dependencies = [ + "serde", +] + +[[package]] name = "sha1" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -33,3 +33,4 @@ sqlx = { version = "0.6", features = [ "runtime-actix-rustls", "mysql", "uuid", log = "0.4" chrono = { version = "0.4", features = ["serde"] } hex = "0.4" +serde_variant = "0.1" diff --git a/src/api/oauth.rs b/src/api/oauth.rs index de98e80..353f287 100644 --- a/src/api/oauth.rs +++ b/src/api/oauth.rs @@ -131,12 +131,18 @@ async fn authorize( db: web::Data<MySqlPool>, req: web::Query<AuthorizationParameters>, credentials: web::Json<AuthorizeCredentials>, + tera: web::Data<Tera>, + translations: web::Data<languages::Translations>, ) -> HttpResponse { // TODO use sessions to verify that the request was previously validated // TODO handle internal server error let db = db.get_ref(); let Some(client_id) = db::get_client_id_by_alias(db, &req.client_id).await.unwrap() else { - todo!("client not found") + // TODO find a better way of doing languages + let language = Language::from_str("en").unwrap(); + let translations = translations.get_ref().clone(); + let page = templates::error_page(&tera, language, translations, templates::ErrorPage::ClientNotFound).unwrap(); + return HttpResponse::NotFound().content_type("text/html").body(page); }; let self_id = Url::parse("www.google.com").unwrap(); // TODO find the actual value let state = req.state.clone(); @@ -147,7 +153,18 @@ async fn authorize( } else { let redirect_uris = db::get_client_redirect_uris(db, client_id).await.unwrap(); if redirect_uris.len() != 1 { - todo!("no redirect uri"); + let language = Language::from_str("en").unwrap(); + let translations = translations.get_ref().clone(); + let page = templates::error_page( + &tera, + language, + translations, + templates::ErrorPage::MissingRedirectUri, + ) + .unwrap(); + return HttpResponse::NotFound() + .content_type("text/html") + .body(page); } redirect_uris[0].clone() @@ -230,21 +247,44 @@ async fn authorize_page( translations: web::Data<languages::Translations>, request: HttpRequest, ) -> HttpResponse { + // TODO handle internal server error + let language = Language::from_str("en").unwrap(); + let translations = translations.get_ref().clone(); + let params = request.query_string(); let params = serde_urlencoded::from_str::<AuthorizationParameters>(params); let Ok(params) = params else { - todo!("invalid request") + let page = templates::error_page( + &tera, + language, + translations, + templates::ErrorPage::InvalidRequest, + ) + .unwrap(); + return HttpResponse::BadRequest() + .content_type("text/html") + .body(page); }; let db = db.get_ref(); let Some(client_id) = db::get_client_id_by_alias(db, ¶ms.client_id).await.unwrap() else { - todo!("client not found") + let page = templates::error_page( + &tera, + language, + translations, + templates::ErrorPage::ClientNotFound, + ) + .unwrap(); + return HttpResponse::NotFound() + .content_type("text/html") + .body(page); }; // verify scope - let Some(allowed_scopes) = db::get_client_allowed_scopes(db, client_id).await.unwrap() else { - todo!("client not found") - }; + let allowed_scopes = db::get_client_allowed_scopes(db, client_id) + .await + .unwrap() + .unwrap(); // verify redirect uri let redirect_uri: Url; @@ -254,12 +294,30 @@ async fn authorize_page( .await .unwrap() { - todo!("access denied") + let page = templates::error_page( + &tera, + language, + translations, + templates::ErrorPage::InvalidRedirectUri, + ) + .unwrap(); + return HttpResponse::BadRequest() + .content_type("text/html") + .body(page); } } else { let redirect_uris = db::get_client_redirect_uris(db, client_id).await.unwrap(); if redirect_uris.len() != 1 { - todo!("must have redirect uri") + let page = templates::error_page( + &tera, + language, + translations, + templates::ErrorPage::MissingRedirectUri, + ) + .unwrap(); + return HttpResponse::NotFound() + .content_type("text/html") + .body(page); } redirect_uri = redirect_uris.get(0).unwrap().clone(); @@ -290,8 +348,7 @@ async fn authorize_page( // TODO find a better way of doing languages let language = Language::from_str("en").unwrap(); - let page = - templates::login_page(&tera, ¶ms, language, translations.get_ref().clone()).unwrap(); + let page = templates::login_page(&tera, ¶ms, language, translations).unwrap(); HttpResponse::Ok().content_type("text/html").body(page) } diff --git a/src/resources/templates.rs b/src/resources/templates.rs index 7578256..88c1fad 100644 --- a/src/resources/templates.rs +++ b/src/resources/templates.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use exun::{RawUnexpected, ResultErrorExt}; use raise::yeet; +use serde::Serialize; use tera::{Function, Tera, Value}; use unic_langid::subtags::Language; @@ -36,6 +37,37 @@ pub fn initialize() -> tera::Result<Tera> { Ok(tera) } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum ErrorPage { + InvalidRequest, + ClientNotFound, + MissingRedirectUri, + InvalidRedirectUri, +} + +pub fn error_page( + tera: &Tera, + language: Language, + mut translations: languages::Translations, + error: ErrorPage, +) -> Result<String, RawUnexpected> { + translations.refresh()?; + let mut tera = extend_tera(tera, language, translations)?; + tera.full_reload()?; + + let error = serde_variant::to_variant_name(&error)?; + let header = format!("errorHeader_{error}"); + let message = format!("errorMessage_{error}"); + + let mut context = tera::Context::new(); + context.insert("lang", language.as_str()); + context.insert("errorHeader", &header); + context.insert("errormessage", &message); + + tera.render("error.html", &context).unexpect() +} + pub fn login_page( tera: &Tera, params: &AuthorizationParameters, diff --git a/static/languages/en.ini b/static/languages/en.ini index 1848a7d..e926ec0 100644 --- a/static/languages/en.ini +++ b/static/languages/en.ini @@ -4,3 +4,13 @@ usernamePlaceholder = Enter your username passwordLabel = Password passwordPlaceholder = Enter your password loginSubmitButton = Log In + +errorTitle = Error +errorHeader_invalidRequest = Invalid Request +errorMessage_invalidRequest = The client sent a bad request. +errorHeader_clientNotFound = Client Not Found +errorMessage_clientNotFound = The client gave an incorrect ID, so we cannot redirect to it. +errorHeader_missingRedirectUri = Missing Redirect URI +errorMessage_missingRedirectUri = There are many redirect URIs for the client, but the client did not specify which one to use. +errorHeader_invalidRedirectUri = Invalid Redirect URI +errorMessage_invalidRedirectUri = The client provided a redirect URI that it is not allowed to redirect to. diff --git a/static/templates/base.html b/static/templates/base.html index fe4875e..021f95e 100644 --- a/static/templates/base.html +++ b/static/templates/base.html @@ -10,7 +10,7 @@ <body> {% block content %}{% endblock content %} <footer> - <div id="copyright">© 2023 Amplify Education Inc.</div> + <div id="copyright">© 2023</div> </footer> </body> </html> diff --git a/static/templates/error.html b/static/templates/error.html new file mode 100644 index 0000000..4fbe87f --- /dev/null +++ b/static/templates/error.html @@ -0,0 +1,7 @@ +{% extends "base.html" %} +{% block title %}{{ msg(key="errorTitle") }}{% endblock title %} + +{% block content %} +<p>{{ msg(key=errorHeader) }}</p> +<p>{{ msg(key=errorMessage) }}</p> +{% endblock content %} |
