Can set authorized authentication providers for a given account
This commit is contained in:
		@@ -1,5 +1,6 @@
 | 
			
		||||
use std::net::IpAddr;
 | 
			
		||||
 | 
			
		||||
use crate::data::provider::ProviderID;
 | 
			
		||||
use actix::{Actor, Context, Handler, Message, MessageResult};
 | 
			
		||||
 | 
			
		||||
use crate::data::user::{FactorID, GeneralSettings, GrantedClients, TwoFactor, User, UserID};
 | 
			
		||||
@@ -97,6 +98,7 @@ pub struct Clear2FALoginHistory(pub UserID);
 | 
			
		||||
#[derive(Eq, PartialEq, Debug, Clone)]
 | 
			
		||||
pub struct AuthorizedAuthenticationSources {
 | 
			
		||||
    pub local: bool,
 | 
			
		||||
    pub upstream: Vec<ProviderID>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Message)]
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ use crate::controllers::settings_controller::BaseSettingsPage;
 | 
			
		||||
use crate::data::action_logger::{Action, ActionLogger};
 | 
			
		||||
use crate::data::client::{Client, ClientID, ClientManager};
 | 
			
		||||
use crate::data::current_user::CurrentUser;
 | 
			
		||||
use crate::data::provider::{Provider, ProvidersManager};
 | 
			
		||||
use crate::data::provider::{Provider, ProviderID, ProvidersManager};
 | 
			
		||||
use crate::data::user::{GeneralSettings, GrantedClients, User, UserID};
 | 
			
		||||
use crate::utils::string_utils::rand_str;
 | 
			
		||||
 | 
			
		||||
@@ -43,6 +43,7 @@ struct EditUserTemplate {
 | 
			
		||||
    _p: BaseSettingsPage,
 | 
			
		||||
    u: User,
 | 
			
		||||
    clients: Vec<Client>,
 | 
			
		||||
    providers: Vec<Provider>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn clients_route(
 | 
			
		||||
@@ -85,6 +86,7 @@ pub struct UpdateUserQuery {
 | 
			
		||||
    two_factor_exemption_after_successful_login: Option<String>,
 | 
			
		||||
    admin: Option<String>,
 | 
			
		||||
    allow_local_login: Option<String>,
 | 
			
		||||
    authorized_sources: String,
 | 
			
		||||
    grant_type: String,
 | 
			
		||||
    granted_clients: String,
 | 
			
		||||
    two_factor: String,
 | 
			
		||||
@@ -162,6 +164,10 @@ pub async fn users_route(
 | 
			
		||||
        // Update the list of authorized authentication sources
 | 
			
		||||
        let auth_sources = AuthorizedAuthenticationSources {
 | 
			
		||||
            local: update.0.allow_local_login.is_some(),
 | 
			
		||||
            upstream: match update.0.authorized_sources.as_str() {
 | 
			
		||||
                "" => vec![],
 | 
			
		||||
                s => s.split(',').map(|s| ProviderID(s.to_string())).collect(),
 | 
			
		||||
            },
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if edited_user.authorized_authentication_sources() != auth_sources {
 | 
			
		||||
@@ -282,6 +288,7 @@ pub async fn users_route(
 | 
			
		||||
pub async fn create_user(
 | 
			
		||||
    admin: CurrentUser,
 | 
			
		||||
    clients: web::Data<Arc<ClientManager>>,
 | 
			
		||||
    providers: web::Data<Arc<ProvidersManager>>,
 | 
			
		||||
) -> impl Responder {
 | 
			
		||||
    let user = User {
 | 
			
		||||
        authorized_clients: Some(
 | 
			
		||||
@@ -299,6 +306,7 @@ pub async fn create_user(
 | 
			
		||||
            _p: BaseSettingsPage::get("Create a new user", admin.deref(), None, None),
 | 
			
		||||
            u: user,
 | 
			
		||||
            clients: clients.cloned(),
 | 
			
		||||
            providers: providers.cloned(),
 | 
			
		||||
        }
 | 
			
		||||
        .render()
 | 
			
		||||
        .unwrap(),
 | 
			
		||||
@@ -313,6 +321,7 @@ pub struct EditUserQuery {
 | 
			
		||||
pub async fn edit_user(
 | 
			
		||||
    admin: CurrentUser,
 | 
			
		||||
    clients: web::Data<Arc<ClientManager>>,
 | 
			
		||||
    providers: web::Data<Arc<ProvidersManager>>,
 | 
			
		||||
    users: web::Data<Addr<UsersActor>>,
 | 
			
		||||
    query: web::Query<EditUserQuery>,
 | 
			
		||||
) -> impl Responder {
 | 
			
		||||
@@ -335,6 +344,7 @@ pub async fn edit_user(
 | 
			
		||||
            ),
 | 
			
		||||
            u: edited_account.unwrap_or_default(),
 | 
			
		||||
            clients: clients.cloned(),
 | 
			
		||||
            providers: providers.cloned(),
 | 
			
		||||
        }
 | 
			
		||||
        .render()
 | 
			
		||||
        .unwrap(),
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,11 @@
 | 
			
		||||
use crate::actors::users_actor::AuthorizedAuthenticationSources;
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
use std::net::IpAddr;
 | 
			
		||||
 | 
			
		||||
use crate::actors::users_actor::AuthorizedAuthenticationSources;
 | 
			
		||||
use crate::constants::SECOND_FACTOR_EXEMPTION_AFTER_SUCCESSFUL_LOGIN;
 | 
			
		||||
use crate::data::client::{Client, ClientID};
 | 
			
		||||
use crate::data::login_redirect::LoginRedirect;
 | 
			
		||||
use crate::data::provider::{Provider, ProviderID};
 | 
			
		||||
use crate::data::totp_key::TotpKey;
 | 
			
		||||
use crate::data::webauthn_manager::WebauthnPubKey;
 | 
			
		||||
use crate::utils::time::{fmt_time, time};
 | 
			
		||||
@@ -151,6 +152,10 @@ pub struct User {
 | 
			
		||||
    /// Authorize connection through local login
 | 
			
		||||
    #[serde(default = "default_true")]
 | 
			
		||||
    pub allow_local_login: bool,
 | 
			
		||||
 | 
			
		||||
    /// Allowed third party providers
 | 
			
		||||
    #[serde(default)]
 | 
			
		||||
    pub allow_login_from_providers: Vec<ProviderID>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl User {
 | 
			
		||||
@@ -175,9 +180,15 @@ impl User {
 | 
			
		||||
    pub fn authorized_authentication_sources(&self) -> AuthorizedAuthenticationSources {
 | 
			
		||||
        AuthorizedAuthenticationSources {
 | 
			
		||||
            local: self.allow_local_login,
 | 
			
		||||
            upstream: self.allow_login_from_providers.clone(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Check if a user can authenticate using a givne provider or not
 | 
			
		||||
    pub fn can_login_from_provider(&self, provider: &Provider) -> bool {
 | 
			
		||||
        self.allow_login_from_providers.contains(&provider.id)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn granted_clients(&self) -> GrantedClients {
 | 
			
		||||
        match self.authorized_clients.as_deref() {
 | 
			
		||||
            None => GrantedClients::AllClients,
 | 
			
		||||
@@ -313,6 +324,7 @@ impl Default for User {
 | 
			
		||||
            last_successful_2fa: Default::default(),
 | 
			
		||||
            authorized_clients: Some(Vec::new()),
 | 
			
		||||
            allow_local_login: true,
 | 
			
		||||
            allow_login_from_providers: vec![],
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -150,6 +150,7 @@ impl UsersSyncBackend for EntityManager<User> {
 | 
			
		||||
    ) -> Res {
 | 
			
		||||
        self.update_user(id, |mut user| {
 | 
			
		||||
            user.allow_local_login = sources.local;
 | 
			
		||||
            user.allow_login_from_providers = sources.upstream;
 | 
			
		||||
            user
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -126,6 +126,8 @@
 | 
			
		||||
    <!-- Authorized authentication sources -->
 | 
			
		||||
    <fieldset class="form-group">
 | 
			
		||||
        <legend class="mt-4">Authorized authentication sources</legend>
 | 
			
		||||
 | 
			
		||||
        <!-- Local login -->
 | 
			
		||||
        <div class="form-check">
 | 
			
		||||
            <input class="form-check-input" type="checkbox" name="allow_local_login" id="allow_local_login"
 | 
			
		||||
                   {% if u.allow_local_login %} checked="" {% endif %}>
 | 
			
		||||
@@ -133,6 +135,20 @@
 | 
			
		||||
                Allow local login
 | 
			
		||||
            </label>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <!-- Provider -->
 | 
			
		||||
        <input type="hidden" name="authorized_sources" id="authorized_sources"/>
 | 
			
		||||
        {% for prov in providers %}
 | 
			
		||||
        <div class="form-check">
 | 
			
		||||
            <input class="form-check-input authorized_provider" type="checkbox" name="prov-{{ prov.id.0 }}"
 | 
			
		||||
                   id="prov-{{ prov.id.0 }}"
 | 
			
		||||
                   data-id="{{ prov.id.0 }}"
 | 
			
		||||
                   {% if u.can_login_from_provider(prov) %} checked="" {% endif %}>
 | 
			
		||||
            <label class="form-check-label" for="prov-{{ prov.id.0 }}">
 | 
			
		||||
                Allow login from {{ prov.name }}
 | 
			
		||||
            </label>
 | 
			
		||||
        </div>
 | 
			
		||||
        {% endfor %}
 | 
			
		||||
    </fieldset>
 | 
			
		||||
 | 
			
		||||
    <!-- Granted clients -->
 | 
			
		||||
@@ -233,6 +249,13 @@
 | 
			
		||||
    form.addEventListener("submit", (ev) => {
 | 
			
		||||
        ev.preventDefault();
 | 
			
		||||
 | 
			
		||||
        const authorized_sources = [...document.querySelectorAll(".authorized_provider")]
 | 
			
		||||
            .filter(e => e.checked)
 | 
			
		||||
            .map(e => e.getAttribute("data-id")).join(",")
 | 
			
		||||
 | 
			
		||||
        document.querySelector("input[name=authorized_sources]").value = authorized_sources;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        const authorized_clients = [...document.querySelectorAll(".authorize_client_checkbox")]
 | 
			
		||||
            .filter(e => e.checked)
 | 
			
		||||
            .map(e => e.getAttribute("data-id")).join(",")
 | 
			
		||||
@@ -250,6 +273,8 @@
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
{% endblock content %}
 | 
			
		||||
		Reference in New Issue
	
	Block a user