This commit is contained in:
		@@ -19,12 +19,12 @@ use crate::data::session_identity::{SessionIdentity, SessionStatus};
 | 
			
		||||
use crate::data::user::User;
 | 
			
		||||
use crate::data::webauthn_manager::WebAuthManagerReq;
 | 
			
		||||
 | 
			
		||||
struct BaseLoginPage<'a> {
 | 
			
		||||
    danger: Option<String>,
 | 
			
		||||
    success: Option<String>,
 | 
			
		||||
    page_title: &'static str,
 | 
			
		||||
    app_name: &'static str,
 | 
			
		||||
    redirect_uri: &'a LoginRedirect,
 | 
			
		||||
pub struct BaseLoginPage<'a> {
 | 
			
		||||
    pub danger: Option<String>,
 | 
			
		||||
    pub success: Option<String>,
 | 
			
		||||
    pub page_title: &'static str,
 | 
			
		||||
    pub app_name: &'static str,
 | 
			
		||||
    pub redirect_uri: &'a LoginRedirect,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Template)]
 | 
			
		||||
 
 | 
			
		||||
@@ -2,16 +2,43 @@ use std::sync::Arc;
 | 
			
		||||
 | 
			
		||||
use actix::Addr;
 | 
			
		||||
use actix_web::{web, HttpResponse, Responder};
 | 
			
		||||
use askama::Template;
 | 
			
		||||
 | 
			
		||||
use crate::actors::providers_states_actor;
 | 
			
		||||
use crate::actors::providers_states_actor::{ProviderLoginState, ProvidersStatesActor};
 | 
			
		||||
use crate::constants::APP_NAME;
 | 
			
		||||
use crate::controllers::base_controller::{build_fatal_error_page, redirect_user};
 | 
			
		||||
use crate::controllers::login_controller::BaseLoginPage;
 | 
			
		||||
use crate::data::action_logger::{Action, ActionLogger};
 | 
			
		||||
use crate::data::login_redirect::LoginRedirect;
 | 
			
		||||
use crate::data::provider::{ProviderID, ProvidersManager};
 | 
			
		||||
use crate::data::provider_configuration::ProviderConfigurationHelper;
 | 
			
		||||
use crate::data::remote_ip::RemoteIP;
 | 
			
		||||
 | 
			
		||||
#[derive(askama::Template)]
 | 
			
		||||
#[template(path = "login/prov_login_error.html")]
 | 
			
		||||
struct ProviderLoginError<'a> {
 | 
			
		||||
    _p: BaseLoginPage<'a>,
 | 
			
		||||
    message: &'a str,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> ProviderLoginError<'a> {
 | 
			
		||||
    pub fn get(message: &'a str, redirect_uri: &'a LoginRedirect) -> String {
 | 
			
		||||
        Self {
 | 
			
		||||
            _p: BaseLoginPage {
 | 
			
		||||
                danger: None,
 | 
			
		||||
                success: None,
 | 
			
		||||
                page_title: "Upstream login",
 | 
			
		||||
                app_name: APP_NAME,
 | 
			
		||||
                redirect_uri,
 | 
			
		||||
            },
 | 
			
		||||
            message,
 | 
			
		||||
        }
 | 
			
		||||
        .render()
 | 
			
		||||
        .unwrap()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Deserialize)]
 | 
			
		||||
pub struct StartLoginQuery {
 | 
			
		||||
    #[serde(default)]
 | 
			
		||||
@@ -20,7 +47,6 @@ pub struct StartLoginQuery {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Start user authentication using a provider
 | 
			
		||||
#[allow(clippy::too_many_arguments)]
 | 
			
		||||
pub async fn start_login(
 | 
			
		||||
    remote_ip: RemoteIP,
 | 
			
		||||
    providers: web::Data<Arc<ProvidersManager>>,
 | 
			
		||||
@@ -71,3 +97,66 @@ pub async fn start_login(
 | 
			
		||||
    // Redirect user
 | 
			
		||||
    redirect_user(&url)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Deserialize)]
 | 
			
		||||
pub struct FinishLoginSuccess {
 | 
			
		||||
    code: String,
 | 
			
		||||
    state: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Deserialize)]
 | 
			
		||||
pub struct FinishLoginError {
 | 
			
		||||
    error: String,
 | 
			
		||||
    error_description: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Deserialize)]
 | 
			
		||||
pub struct FinishLoginQuery {
 | 
			
		||||
    #[serde(flatten)]
 | 
			
		||||
    success: Option<FinishLoginSuccess>,
 | 
			
		||||
    #[serde(flatten)]
 | 
			
		||||
    error: Option<FinishLoginError>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Finish user authentication using a provider
 | 
			
		||||
pub async fn finish_login(
 | 
			
		||||
    remote_ip: RemoteIP,
 | 
			
		||||
    providers: web::Data<Arc<ProvidersManager>>,
 | 
			
		||||
    states: web::Data<Addr<ProvidersStatesActor>>,
 | 
			
		||||
    query: web::Query<FinishLoginQuery>,
 | 
			
		||||
    logger: ActionLogger,
 | 
			
		||||
) -> impl Responder {
 | 
			
		||||
    let query = match query.0.success {
 | 
			
		||||
        Some(q) => q,
 | 
			
		||||
        None => {
 | 
			
		||||
            let error_message = query
 | 
			
		||||
                .0
 | 
			
		||||
                .error
 | 
			
		||||
                .map(|e| e.error_description.unwrap_or(e.error))
 | 
			
		||||
                .unwrap_or("Authentication failed (unspecified error)!".to_string());
 | 
			
		||||
 | 
			
		||||
            return HttpResponse::Unauthorized().body(ProviderLoginError::get(
 | 
			
		||||
                &error_message,
 | 
			
		||||
                &LoginRedirect::default(),
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Get & consume state
 | 
			
		||||
    let state = states
 | 
			
		||||
        .send(providers_states_actor::ConsumeState {
 | 
			
		||||
            ip: remote_ip.0,
 | 
			
		||||
            state_id: query.state.clone(),
 | 
			
		||||
        })
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
    // TODO : rate limiting
 | 
			
		||||
    // TODO : finish login, get user information
 | 
			
		||||
    // TODO : check token signature
 | 
			
		||||
    // TODO : check if user is authorized to access application
 | 
			
		||||
    // TODO : check if 2FA is enabled
 | 
			
		||||
    // TODO : redirect user to login route
 | 
			
		||||
    // TODO : add proper logging
 | 
			
		||||
    HttpResponse::Ok().body("continue")
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -166,6 +166,10 @@ async fn main() -> std::io::Result<()> {
 | 
			
		||||
                "/login_with_prov",
 | 
			
		||||
                web::get().to(providers_controller::start_login),
 | 
			
		||||
            )
 | 
			
		||||
            .route(
 | 
			
		||||
                OIDC_PROVIDER_CB_URI,
 | 
			
		||||
                web::get().to(providers_controller::finish_login),
 | 
			
		||||
            )
 | 
			
		||||
            // Settings routes
 | 
			
		||||
            .route(
 | 
			
		||||
                "/settings",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								templates/login/prov_login_error.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								templates/login/prov_login_error.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
{% extends "base_login_page.html" %}
 | 
			
		||||
{% block content %}
 | 
			
		||||
 | 
			
		||||
<div class="alert alert-danger" style="margin-bottom: 10px;">
 | 
			
		||||
    <strong>Authentication failed!</strong>
 | 
			
		||||
 | 
			
		||||
    <p style="margin-top: 10px; text-align: justify;">{{ message }}</p>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<a href="/login?redirect={{ _p.redirect_uri.get_encoded() }}">Go back to login</a>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
{% endblock content %}
 | 
			
		||||
		Reference in New Issue
	
	Block a user