Add authentication from upstream providers #107
@ -2,6 +2,7 @@ use actix::Addr;
|
|||||||
use actix_identity::Identity;
|
use actix_identity::Identity;
|
||||||
use actix_web::{web, HttpRequest, HttpResponse, Responder};
|
use actix_web::{web, HttpRequest, HttpResponse, Responder};
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::actors::bruteforce_actor::BruteForceActor;
|
use crate::actors::bruteforce_actor::BruteForceActor;
|
||||||
use crate::actors::users_actor::{LoginResult, UsersActor};
|
use crate::actors::users_actor::{LoginResult, UsersActor};
|
||||||
@ -12,6 +13,7 @@ use crate::controllers::base_controller::{
|
|||||||
};
|
};
|
||||||
use crate::data::action_logger::{Action, ActionLogger};
|
use crate::data::action_logger::{Action, ActionLogger};
|
||||||
use crate::data::login_redirect::LoginRedirect;
|
use crate::data::login_redirect::LoginRedirect;
|
||||||
|
use crate::data::provider::{Provider, ProvidersManager};
|
||||||
use crate::data::remote_ip::RemoteIP;
|
use crate::data::remote_ip::RemoteIP;
|
||||||
use crate::data::session_identity::{SessionIdentity, SessionStatus};
|
use crate::data::session_identity::{SessionIdentity, SessionStatus};
|
||||||
use crate::data::user::User;
|
use crate::data::user::User;
|
||||||
@ -30,6 +32,7 @@ struct BaseLoginPage<'a> {
|
|||||||
struct LoginTemplate<'a> {
|
struct LoginTemplate<'a> {
|
||||||
_p: BaseLoginPage<'a>,
|
_p: BaseLoginPage<'a>,
|
||||||
login: String,
|
login: String,
|
||||||
|
providers: Vec<Provider>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
@ -77,6 +80,7 @@ pub struct LoginRequestQuery {
|
|||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub async fn login_route(
|
pub async fn login_route(
|
||||||
remote_ip: RemoteIP,
|
remote_ip: RemoteIP,
|
||||||
|
providers: web::Data<Arc<ProvidersManager>>,
|
||||||
users: web::Data<Addr<UsersActor>>,
|
users: web::Data<Addr<UsersActor>>,
|
||||||
bruteforce: web::Data<Addr<BruteForceActor>>,
|
bruteforce: web::Data<Addr<BruteForceActor>>,
|
||||||
query: web::Query<LoginRequestQuery>,
|
query: web::Query<LoginRequestQuery>,
|
||||||
@ -121,7 +125,7 @@ pub async fn login_route(
|
|||||||
query.redirect.get_encoded()
|
query.redirect.get_encoded()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
// Check if the user has to valide a second factor
|
// Check if the user has to validate a second factor
|
||||||
else if SessionIdentity(id.as_ref()).need_2fa_auth() {
|
else if SessionIdentity(id.as_ref()).need_2fa_auth() {
|
||||||
return redirect_user(&format!(
|
return redirect_user(&format!(
|
||||||
"/2fa_auth?redirect={}",
|
"/2fa_auth?redirect={}",
|
||||||
@ -203,6 +207,7 @@ pub async fn login_route(
|
|||||||
redirect_uri: &query.redirect,
|
redirect_uri: &query.redirect,
|
||||||
},
|
},
|
||||||
login,
|
login,
|
||||||
|
providers: providers.cloned(),
|
||||||
}
|
}
|
||||||
.render()
|
.render()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::data::entity_manager::EntityManager;
|
use crate::data::entity_manager::EntityManager;
|
||||||
|
use crate::data::login_redirect::LoginRedirect;
|
||||||
use crate::utils::string_utils::apply_env_vars;
|
use crate::utils::string_utils::apply_env_vars;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq)]
|
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq)]
|
||||||
@ -28,6 +29,11 @@ pub struct Provider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Provider {
|
impl Provider {
|
||||||
|
/// Get URL-encoded provider id
|
||||||
|
pub fn id_encoded(&self) -> String {
|
||||||
|
urlencoding::encode(&self.id.0).to_string()
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the URL where the logo can be located
|
/// Get the URL where the logo can be located
|
||||||
pub fn logo_url(&self) -> &str {
|
pub fn logo_url(&self) -> &str {
|
||||||
match self.logo.as_str() {
|
match self.logo.as_str() {
|
||||||
@ -39,6 +45,15 @@ impl Provider {
|
|||||||
s => s,
|
s => s,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the URL to use to login with the provider
|
||||||
|
pub fn login_url(&self, redirect_url: &LoginRedirect) -> String {
|
||||||
|
format!(
|
||||||
|
"/login_with_prov?id={}&redirect_url={}",
|
||||||
|
self.id_encoded(),
|
||||||
|
redirect_url.get_encoded()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Provider {
|
impl PartialEq for Provider {
|
||||||
|
@ -30,8 +30,6 @@
|
|||||||
font-size: 3.5rem;
|
font-size: 3.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
@ -45,7 +43,7 @@
|
|||||||
|
|
||||||
<main class="form-signin">
|
<main class="form-signin">
|
||||||
|
|
||||||
<h1 class="h3 mb-3 fw-normal">{{ _p.page_title }}</h1>
|
<h1 class="h3 mb-3 fw-normal" style="margin-bottom: 2rem !important;">{{ _p.page_title }}</h1>
|
||||||
|
|
||||||
{% if let Some(danger) = _p.danger %}
|
{% if let Some(danger) = _p.danger %}
|
||||||
<div class="alert alert-danger" role="alert">
|
<div class="alert alert-danger" role="alert">
|
||||||
|
@ -1,5 +1,23 @@
|
|||||||
{% extends "base_login_page.html" %}
|
{% extends "base_login_page.html" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<style>
|
||||||
|
#providers {
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.provider-button {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.provider-button img {
|
||||||
|
margin-right: 1em;
|
||||||
|
width: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
<form action="/login?redirect={{ _p.redirect_uri.get_encoded() }}" method="post">
|
<form action="/login?redirect={{ _p.redirect_uri.get_encoded() }}" method="post">
|
||||||
<div>
|
<div>
|
||||||
<div class="form-floating">
|
<div class="form-floating">
|
||||||
@ -19,4 +37,18 @@
|
|||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<!-- Upstream providers -->
|
||||||
|
{% if !providers.is_empty() %}
|
||||||
|
<div id="providers">
|
||||||
|
{% for prov in providers %}
|
||||||
|
<a class="btn btn-secondary btn-lg provider-button" href="{{ prov.login_url(_p.redirect_uri) }}">
|
||||||
|
<img src="{{ prov.logo_url() }}" alt="Provider icon"/>
|
||||||
|
<div style="text-align: left;">
|
||||||
|
Login using {{ prov.name }} <br/>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
Loading…
Reference in New Issue
Block a user