Add authentication from upstream providers #107
@ -6,6 +6,9 @@ pub const USERS_LIST_FILE: &str = "users.json";
|
|||||||
/// File in storage containing clients list
|
/// File in storage containing clients list
|
||||||
pub const CLIENTS_LIST_FILE: &str = "clients.yaml";
|
pub const CLIENTS_LIST_FILE: &str = "clients.yaml";
|
||||||
|
|
||||||
|
/// File in storage containing providers list
|
||||||
|
pub const PROVIDERS_LIST_FILE: &str = "providers.yaml";
|
||||||
|
|
||||||
/// Default built-in credentials
|
/// Default built-in credentials
|
||||||
pub const DEFAULT_ADMIN_USERNAME: &str = "admin";
|
pub const DEFAULT_ADMIN_USERNAME: &str = "admin";
|
||||||
pub const DEFAULT_ADMIN_PASSWORD: &str = "admin";
|
pub const DEFAULT_ADMIN_PASSWORD: &str = "admin";
|
||||||
|
@ -12,6 +12,7 @@ use crate::controllers::settings_controller::BaseSettingsPage;
|
|||||||
use crate::data::action_logger::{Action, ActionLogger};
|
use crate::data::action_logger::{Action, ActionLogger};
|
||||||
use crate::data::client::{Client, ClientID, ClientManager};
|
use crate::data::client::{Client, ClientID, ClientManager};
|
||||||
use crate::data::current_user::CurrentUser;
|
use crate::data::current_user::CurrentUser;
|
||||||
|
use crate::data::provider::{Provider, ProvidersManager};
|
||||||
use crate::data::user::{GeneralSettings, GrantedClients, User, UserID};
|
use crate::data::user::{GeneralSettings, GrantedClients, User, UserID};
|
||||||
use crate::utils::string_utils::rand_str;
|
use crate::utils::string_utils::rand_str;
|
||||||
|
|
||||||
@ -22,6 +23,13 @@ struct ClientsListTemplate {
|
|||||||
clients: Vec<Client>,
|
clients: Vec<Client>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "settings/providers_list.html")]
|
||||||
|
struct ProvidersListTemplate {
|
||||||
|
_p: BaseSettingsPage,
|
||||||
|
providers: Vec<Provider>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "settings/users_list.html")]
|
#[template(path = "settings/users_list.html")]
|
||||||
struct UsersListTemplate {
|
struct UsersListTemplate {
|
||||||
@ -51,6 +59,20 @@ pub async fn clients_route(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn providers_route(
|
||||||
|
user: CurrentUser,
|
||||||
|
providers: web::Data<Arc<ProvidersManager>>,
|
||||||
|
) -> impl Responder {
|
||||||
|
HttpResponse::Ok().body(
|
||||||
|
ProvidersListTemplate {
|
||||||
|
_p: BaseSettingsPage::get("OpenID Providers list", &user, None, None),
|
||||||
|
providers: providers.cloned(),
|
||||||
|
}
|
||||||
|
.render()
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize, Debug)]
|
#[derive(serde::Deserialize, Debug)]
|
||||||
pub struct UpdateUserQuery {
|
pub struct UpdateUserQuery {
|
||||||
uid: UserID,
|
uid: UserID,
|
||||||
|
@ -2,7 +2,7 @@ use std::path::{Path, PathBuf};
|
|||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
use crate::constants::{APP_NAME, CLIENTS_LIST_FILE, USERS_LIST_FILE};
|
use crate::constants::{APP_NAME, CLIENTS_LIST_FILE, PROVIDERS_LIST_FILE, USERS_LIST_FILE};
|
||||||
|
|
||||||
/// Basic OIDC provider
|
/// Basic OIDC provider
|
||||||
#[derive(Parser, Debug, Clone)]
|
#[derive(Parser, Debug, Clone)]
|
||||||
@ -72,6 +72,10 @@ impl AppConfig {
|
|||||||
self.storage_path().join(CLIENTS_LIST_FILE)
|
self.storage_path().join(CLIENTS_LIST_FILE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn providers_file(&self) -> PathBuf {
|
||||||
|
self.storage_path().join(PROVIDERS_LIST_FILE)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn full_url(&self, uri: &str) -> String {
|
pub fn full_url(&self, uri: &str) -> String {
|
||||||
if uri.starts_with('/') {
|
if uri.starts_with('/') {
|
||||||
format!("{}{}", self.website_origin, uri)
|
format!("{}{}", self.website_origin, uri)
|
||||||
|
@ -11,6 +11,7 @@ pub mod jwt_signer;
|
|||||||
pub mod login_redirect;
|
pub mod login_redirect;
|
||||||
pub mod open_id_user_info;
|
pub mod open_id_user_info;
|
||||||
pub mod openid_config;
|
pub mod openid_config;
|
||||||
|
pub mod provider;
|
||||||
pub mod remote_ip;
|
pub mod remote_ip;
|
||||||
pub mod session_identity;
|
pub mod session_identity;
|
||||||
pub mod totp_key;
|
pub mod totp_key;
|
||||||
|
60
src/data/provider.rs
Normal file
60
src/data/provider.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
use crate::data::entity_manager::EntityManager;
|
||||||
|
use crate::utils::string_utils::apply_env_vars;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq)]
|
||||||
|
pub struct ProviderID(pub String);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Provider {
|
||||||
|
/// The ID of the provider
|
||||||
|
pub id: ProviderID,
|
||||||
|
|
||||||
|
/// The human-readable name of the client
|
||||||
|
pub name: String,
|
||||||
|
|
||||||
|
/// A logo presented to the users of the provider
|
||||||
|
pub logo: String,
|
||||||
|
|
||||||
|
/// The registration id of BasicOIDC on the provider
|
||||||
|
pub client_id: String,
|
||||||
|
|
||||||
|
/// The registration secret of BasicOIDC on the provider
|
||||||
|
pub client_secret: String,
|
||||||
|
|
||||||
|
/// Specify the URL of the OpenID configuration URL
|
||||||
|
///
|
||||||
|
/// (.well-known/openid-configuration endpoint)
|
||||||
|
pub configuration_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Provider {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.id.eq(&other.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Provider {}
|
||||||
|
|
||||||
|
pub type ProvidersManager = EntityManager<Provider>;
|
||||||
|
|
||||||
|
impl EntityManager<Provider> {
|
||||||
|
pub fn find_by_id(&self, u: &ProviderID) -> Option<Provider> {
|
||||||
|
for entry in self.iter() {
|
||||||
|
if entry.id.eq(u) {
|
||||||
|
return Some(entry.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply_environment_variables(&mut self) {
|
||||||
|
for c in self.iter_mut() {
|
||||||
|
c.id = ProviderID(apply_env_vars(&c.id.0));
|
||||||
|
c.name = apply_env_vars(&c.name);
|
||||||
|
c.logo = apply_env_vars(&c.logo);
|
||||||
|
c.client_id = apply_env_vars(&c.client_id);
|
||||||
|
c.client_secret = apply_env_vars(&c.client_secret);
|
||||||
|
c.configuration_url = apply_env_vars(&c.configuration_url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
src/main.rs
11
src/main.rs
@ -20,6 +20,7 @@ use basic_oidc::data::app_config::AppConfig;
|
|||||||
use basic_oidc::data::client::ClientManager;
|
use basic_oidc::data::client::ClientManager;
|
||||||
use basic_oidc::data::entity_manager::EntityManager;
|
use basic_oidc::data::entity_manager::EntityManager;
|
||||||
use basic_oidc::data::jwt_signer::JWTSigner;
|
use basic_oidc::data::jwt_signer::JWTSigner;
|
||||||
|
use basic_oidc::data::provider::ProvidersManager;
|
||||||
use basic_oidc::data::user::User;
|
use basic_oidc::data::user::User;
|
||||||
use basic_oidc::data::webauthn_manager::WebAuthManager;
|
use basic_oidc::data::webauthn_manager::WebAuthManager;
|
||||||
use basic_oidc::middlewares::auth_middleware::AuthMiddleware;
|
use basic_oidc::middlewares::auth_middleware::AuthMiddleware;
|
||||||
@ -77,6 +78,11 @@ async fn main() -> std::io::Result<()> {
|
|||||||
clients.apply_environment_variables();
|
clients.apply_environment_variables();
|
||||||
let clients = Arc::new(clients);
|
let clients = Arc::new(clients);
|
||||||
|
|
||||||
|
let mut providers = ProvidersManager::open_or_create(config.providers_file())
|
||||||
|
.expect("Failed to load providers list!");
|
||||||
|
providers.apply_environment_variables();
|
||||||
|
let providers = Arc::new(providers);
|
||||||
|
|
||||||
log::info!("Server will listen on {}", config.listen_address);
|
log::info!("Server will listen on {}", config.listen_address);
|
||||||
let listen_address = config.listen_address.to_string();
|
let listen_address = config.listen_address.to_string();
|
||||||
|
|
||||||
@ -101,6 +107,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
.app_data(web::Data::new(bruteforce_actor.clone()))
|
.app_data(web::Data::new(bruteforce_actor.clone()))
|
||||||
.app_data(web::Data::new(openid_sessions_actor.clone()))
|
.app_data(web::Data::new(openid_sessions_actor.clone()))
|
||||||
.app_data(web::Data::new(clients.clone()))
|
.app_data(web::Data::new(clients.clone()))
|
||||||
|
.app_data(web::Data::new(providers.clone()))
|
||||||
.app_data(web::Data::new(jwt_signer.clone()))
|
.app_data(web::Data::new(jwt_signer.clone()))
|
||||||
.app_data(web::Data::new(webauthn_manager.clone()))
|
.app_data(web::Data::new(webauthn_manager.clone()))
|
||||||
.wrap(
|
.wrap(
|
||||||
@ -207,6 +214,10 @@ async fn main() -> std::io::Result<()> {
|
|||||||
"/admin/clients",
|
"/admin/clients",
|
||||||
web::get().to(admin_controller::clients_route),
|
web::get().to(admin_controller::clients_route),
|
||||||
)
|
)
|
||||||
|
.route(
|
||||||
|
"/admin/providers",
|
||||||
|
web::get().to(admin_controller::providers_route),
|
||||||
|
)
|
||||||
.route("/admin/users", web::get().to(admin_controller::users_route))
|
.route("/admin/users", web::get().to(admin_controller::users_route))
|
||||||
.route(
|
.route(
|
||||||
"/admin/users",
|
"/admin/users",
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<span class="fs-4">{{ _p.app_name }}</span>
|
<span class="fs-4">{{ _p.app_name }}</span>
|
||||||
</a>
|
</a>
|
||||||
{% if _p.is_admin %}
|
{% if _p.is_admin %}
|
||||||
<span>Version {{ _p.version }}</span>
|
<span>Version {{ _p.version }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<hr>
|
<hr>
|
||||||
<ul class="nav nav-pills flex-column mb-auto">
|
<ul class="nav nav-pills flex-column mb-auto">
|
||||||
@ -42,6 +42,11 @@
|
|||||||
Clients
|
Clients
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/admin/providers" class="nav-link link-dark">
|
||||||
|
Providers
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/admin/users" class="nav-link link-dark">
|
<a href="/admin/users" class="nav-link link-dark">
|
||||||
Users
|
Users
|
||||||
@ -83,6 +88,7 @@
|
|||||||
if(el.href === location.href) el.classList.add("active");
|
if(el.href === location.href) el.classList.add("active");
|
||||||
else el.classList.remove("active")
|
else el.classList.remove("active")
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{% if _p.ip_location_api.is_some() %}
|
{% if _p.ip_location_api.is_some() %}
|
||||||
<script>const IP_LOCATION_API = "{{ _p.ip_location_api.unwrap() }}"</script>
|
<script>const IP_LOCATION_API = "{{ _p.ip_location_api.unwrap() }}"</script>
|
||||||
|
27
templates/settings/providers_list.html
Normal file
27
templates/settings/providers_list.html
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{% extends "base_settings_page.html" %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<table class="table table-hover" style="max-width: 800px;" aria-describedby="OpenID providers list">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">ID</th>
|
||||||
|
<th scope="col">Name</th>
|
||||||
|
<th scope="col">Configuration URL</th>
|
||||||
|
<th scope="col">Client ID</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for c in providers %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ c.id.0 }}</td>
|
||||||
|
<td>{{ c.name }}</td>
|
||||||
|
<td><a href="{{ c.configuration_url }}" target="_blank" rel="noreferrer">
|
||||||
|
{{ c.configuration_url }}
|
||||||
|
</a></td>
|
||||||
|
<td>{{ c.client_id }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% endblock content %}
|
Loading…
Reference in New Issue
Block a user