18 Commits

Author SHA1 Message Date
aeec88e479 Update Rust crate chrono to 0.4.35
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2024-03-07 00:11:09 +00:00
348c200f39 Update Rust crate env_logger to 0.11.3
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-03-06 00:11:07 +00:00
042b9f3f60 Merge pull request 'Update Rust crate log to 0.4.21' (#243) from renovate/log-0.x into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #243
2024-03-04 09:07:49 +00:00
f57de93ac2 Update Rust crate base64 to 0.22.0
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-03 00:15:46 +00:00
8e303466b0 Update Rust crate actix-identity to 0.7.1
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-03-03 00:11:13 +00:00
5e4ff97b97 Update Rust crate log to 0.4.21
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2024-02-29 00:11:07 +00:00
7eb014d5f9 Update Rust crate serde_json to 1.0.114
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-22 00:10:34 +00:00
753e52ff70 Update Rust crate serde to 1.0.197
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-21 00:15:28 +00:00
5a5913d5fe Update Rust crate jwt-simple to 0.12.9
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-02-21 00:11:15 +00:00
55be4935f1 Updated Docker image
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-19 19:11:48 +01:00
e71fad8546 Check login before logging it 2024-02-19 19:11:13 +01:00
75b70008e3 Updated all dependencies 2024-02-19 18:42:19 +01:00
36399604fc Merge pull request 'Update Rust crate uuid to 1.7.0' (#224) from renovate/uuid-1.x into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #224
2024-02-19 07:03:45 +00:00
281c94349a Merge pull request 'Update Rust crate jwt-simple to 0.12.8' (#235) from renovate/jwt-simple-0.x into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #235
2024-02-19 07:03:28 +00:00
86e723f38c Update Rust crate clap to 4.5.1
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-02-17 00:11:12 +00:00
c9e4bb48e7 Update Rust crate env_logger to 0.11.2
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-02-14 00:11:15 +00:00
42e9ca5cfc Update Rust crate jwt-simple to 0.12.8
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2024-02-10 00:11:14 +00:00
361865574b Update Rust crate uuid to 1.7.0
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2024-01-20 00:30:36 +00:00
21 changed files with 671 additions and 746 deletions

1219
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -7,26 +7,26 @@ edition = "2021"
[dependencies] [dependencies]
actix = "0.13.3" actix = "0.13.3"
actix-identity = "0.7.0" actix-identity = "0.7.1"
actix-web = "4" actix-web = "4.5.1"
actix-session = { version = "0.9.0", features = ["cookie-session"] } actix-session = { version = "0.9.0", features = ["cookie-session"] }
actix-remote-ip = "0.1.0" actix-remote-ip = "0.1.0"
clap = { version = "4.5.0", features = ["derive", "env"] } clap = { version = "4.5.1", features = ["derive", "env"] }
include_dir = "0.7.3" include_dir = "0.7.3"
log = "0.4.20" log = "0.4.21"
serde_json = "1.0.113" serde_json = "1.0.114"
serde_yaml = "0.9.30" serde_yaml = "0.9.32"
env_logger = "0.11.1" env_logger = "0.11.3"
serde = { version = "1.0.196", features = ["derive"] } serde = { version = "1.0.197", features = ["derive"] }
bcrypt = "0.15.0" bcrypt = "0.15.0"
uuid = { version = "1.6.1", features = ["v4"] } uuid = { version = "1.7.0", features = ["v4"] }
mime_guess = "2.0.4" mime_guess = "2.0.4"
askama = "0.12.1" askama = "0.12.1"
futures-util = "0.3.30" futures-util = "0.3.30"
urlencoding = "2.1.3" urlencoding = "2.1.3"
rand = "0.8.5" rand = "0.8.5"
base64 = "0.21.7" base64 = "0.22.0"
jwt-simple = { version = "0.12.7", default-features=false, features=["pure-rust"] } jwt-simple = { version = "0.12.9", default-features = false, features = ["pure-rust"] }
digest = "0.10.7" digest = "0.10.7"
sha2 = "0.10.8" sha2 = "0.10.8"
lazy-regex = "3.1.0" lazy-regex = "3.1.0"
@@ -35,7 +35,8 @@ base32 = "0.4.0"
qrcode-generator = "4.1.9" qrcode-generator = "4.1.9"
webauthn-rs = { version = "0.4.8", features = ["danger-allow-state-serialisation"] } webauthn-rs = { version = "0.4.8", features = ["danger-allow-state-serialisation"] }
url = "2.5.0" url = "2.5.0"
light-openid = { version = "1.0.1", features=["crypto-wrapper"] } light-openid = { version = "1.0.1", features = ["crypto-wrapper"] }
bincode = "2.0.0-rc.3" bincode = "2.0.0-rc.3"
chrono = "0.4.34" chrono = "0.4.35"
lazy_static = "1.4.0" lazy_static = "1.4.0"
mailchecker = "6.0.1"

View File

@@ -1,4 +1,4 @@
FROM debian:bullseye-slim FROM debian:bookworm-slim
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y libcurl4 \ && apt-get install -y libcurl4 \

View File

@@ -6,6 +6,7 @@ use crate::actors::users_actor::{DeleteUserRequest, FindUserByUsername, UsersAct
use crate::data::action_logger::{Action, ActionLogger}; use crate::data::action_logger::{Action, ActionLogger};
use crate::data::current_user::CurrentUser; use crate::data::current_user::CurrentUser;
use crate::data::user::UserID; use crate::data::user::UserID;
use crate::utils::string_utils;
#[derive(serde::Deserialize)] #[derive(serde::Deserialize)]
pub struct FindUserNameReq { pub struct FindUserNameReq {
@@ -21,6 +22,10 @@ pub async fn find_username(
req: web::Form<FindUserNameReq>, req: web::Form<FindUserNameReq>,
users: web::Data<Addr<UsersActor>>, users: web::Data<Addr<UsersActor>>,
) -> impl Responder { ) -> impl Responder {
if !string_utils::is_acceptable_login(&req.username) {
return HttpResponse::BadRequest().json("Invalid login!");
}
let res = users let res = users
.send(FindUserByUsername(req.0.username)) .send(FindUserByUsername(req.0.username))
.await .await

View File

@@ -15,19 +15,20 @@ use crate::data::client::{Client, ClientID, ClientManager};
use crate::data::current_user::CurrentUser; use crate::data::current_user::CurrentUser;
use crate::data::provider::{Provider, ProviderID, ProvidersManager}; use crate::data::provider::{Provider, ProviderID, ProvidersManager};
use crate::data::user::{GeneralSettings, GrantedClients, User, UserID}; use crate::data::user::{GeneralSettings, GrantedClients, User, UserID};
use crate::utils::string_utils;
use crate::utils::string_utils::rand_str; use crate::utils::string_utils::rand_str;
#[derive(Template)] #[derive(Template)]
#[template(path = "settings/clients_list.html")] #[template(path = "settings/clients_list.html")]
struct ClientsListTemplate<'a> { struct ClientsListTemplate<'a> {
_p: BaseSettingsPage<'a>, p: BaseSettingsPage<'a>,
clients: Vec<Client>, clients: Vec<Client>,
} }
#[derive(Template)] #[derive(Template)]
#[template(path = "settings/providers_list.html")] #[template(path = "settings/providers_list.html")]
struct ProvidersListTemplate<'a> { struct ProvidersListTemplate<'a> {
_p: BaseSettingsPage<'a>, p: BaseSettingsPage<'a>,
providers: Vec<Provider>, providers: Vec<Provider>,
redirect_url: String, redirect_url: String,
} }
@@ -35,14 +36,14 @@ struct ProvidersListTemplate<'a> {
#[derive(Template)] #[derive(Template)]
#[template(path = "settings/users_list.html")] #[template(path = "settings/users_list.html")]
struct UsersListTemplate<'a> { struct UsersListTemplate<'a> {
_p: BaseSettingsPage<'a>, p: BaseSettingsPage<'a>,
users: Vec<User>, users: Vec<User>,
} }
#[derive(Template)] #[derive(Template)]
#[template(path = "settings/edit_user.html")] #[template(path = "settings/edit_user.html")]
struct EditUserTemplate<'a> { struct EditUserTemplate<'a> {
_p: BaseSettingsPage<'a>, p: BaseSettingsPage<'a>,
u: User, u: User,
clients: Vec<Client>, clients: Vec<Client>,
providers: Vec<Provider>, providers: Vec<Provider>,
@@ -54,7 +55,7 @@ pub async fn clients_route(
) -> impl Responder { ) -> impl Responder {
HttpResponse::Ok().body( HttpResponse::Ok().body(
ClientsListTemplate { ClientsListTemplate {
_p: BaseSettingsPage::get("Clients list", &user, None, None), p: BaseSettingsPage::get("Clients list", &user, None, None),
clients: clients.cloned(), clients: clients.cloned(),
} }
.render() .render()
@@ -68,7 +69,7 @@ pub async fn providers_route(
) -> impl Responder { ) -> impl Responder {
HttpResponse::Ok().body( HttpResponse::Ok().body(
ProvidersListTemplate { ProvidersListTemplate {
_p: BaseSettingsPage::get("OpenID Providers list", &user, None, None), p: BaseSettingsPage::get("OpenID Providers list", &user, None, None),
providers: providers.cloned(), providers: providers.cloned(),
redirect_url: AppConfig::get().oidc_provider_redirect_url(), redirect_url: AppConfig::get().oidc_provider_redirect_url(),
} }
@@ -105,7 +106,16 @@ pub async fn users_route(
let mut danger = None; let mut danger = None;
let mut success = None; let mut success = None;
if let Some(update) = update_query { // Check update query for invalid input
if update_query
.as_ref()
.map(|l| string_utils::is_acceptable_login(&l.username))
== Some(false)
{
danger = Some("Invalid login provided, the modifications could not be saved!".to_string());
}
// Perform request (if any)
else if let Some(update) = update_query {
let edited_user: Option<User> = users let edited_user: Option<User> = users
.send(users_actor::GetUserRequest(update.uid.clone())) .send(users_actor::GetUserRequest(update.uid.clone()))
.await .await
@@ -280,7 +290,7 @@ pub async fn users_route(
HttpResponse::Ok().body( HttpResponse::Ok().body(
UsersListTemplate { UsersListTemplate {
_p: BaseSettingsPage::get("Users list", &admin, danger, success), p: BaseSettingsPage::get("Users list", &admin, danger, success),
users, users,
} }
.render() .render()
@@ -306,7 +316,7 @@ pub async fn create_user(
HttpResponse::Ok().body( HttpResponse::Ok().body(
EditUserTemplate { EditUserTemplate {
_p: BaseSettingsPage::get("Create a new user", admin.deref(), None, None), p: BaseSettingsPage::get("Create a new user", admin.deref(), None, None),
u: user, u: user,
clients: clients.cloned(), clients: clients.cloned(),
providers: providers.cloned(), providers: providers.cloned(),
@@ -336,7 +346,7 @@ pub async fn edit_user(
HttpResponse::Ok().body( HttpResponse::Ok().body(
EditUserTemplate { EditUserTemplate {
_p: BaseSettingsPage::get( p: BaseSettingsPage::get(
"Edit user account", "Edit user account",
admin.deref(), admin.deref(),
match edited_account.is_none() { match edited_account.is_none() {

View File

@@ -18,6 +18,7 @@ use crate::data::provider::{Provider, ProvidersManager};
use crate::data::session_identity::{SessionIdentity, SessionStatus}; use crate::data::session_identity::{SessionIdentity, SessionStatus};
use crate::data::user::User; use crate::data::user::User;
use crate::data::webauthn_manager::WebAuthManagerReq; use crate::data::webauthn_manager::WebAuthManagerReq;
use crate::utils::string_utils;
pub struct BaseLoginPage<'a> { pub struct BaseLoginPage<'a> {
pub danger: Option<String>, pub danger: Option<String>,
@@ -30,7 +31,7 @@ pub struct BaseLoginPage<'a> {
#[derive(Template)] #[derive(Template)]
#[template(path = "login/login.html")] #[template(path = "login/login.html")]
struct LoginTemplate<'a> { struct LoginTemplate<'a> {
_p: BaseLoginPage<'a>, p: BaseLoginPage<'a>,
login: String, login: String,
providers: Vec<Provider>, providers: Vec<Provider>,
} }
@@ -38,27 +39,27 @@ struct LoginTemplate<'a> {
#[derive(Template)] #[derive(Template)]
#[template(path = "login/password_reset.html")] #[template(path = "login/password_reset.html")]
struct PasswordResetTemplate<'a> { struct PasswordResetTemplate<'a> {
_p: BaseLoginPage<'a>, p: BaseLoginPage<'a>,
min_pass_len: usize, min_pass_len: usize,
} }
#[derive(Template)] #[derive(Template)]
#[template(path = "login/choose_second_factor.html")] #[template(path = "login/choose_second_factor.html")]
struct ChooseSecondFactorTemplate<'a> { struct ChooseSecondFactorTemplate<'a> {
_p: BaseLoginPage<'a>, p: BaseLoginPage<'a>,
user: &'a User, user: &'a User,
} }
#[derive(Template)] #[derive(Template)]
#[template(path = "login/otp_input.html")] #[template(path = "login/otp_input.html")]
struct LoginWithOTPTemplate<'a> { struct LoginWithOTPTemplate<'a> {
_p: BaseLoginPage<'a>, p: BaseLoginPage<'a>,
} }
#[derive(Template)] #[derive(Template)]
#[template(path = "login/webauthn_input.html")] #[template(path = "login/webauthn_input.html")]
struct LoginWithWebauthnTemplate<'a> { struct LoginWithWebauthnTemplate<'a> {
_p: BaseLoginPage<'a>, p: BaseLoginPage<'a>,
opaque_state: String, opaque_state: String,
challenge_json: String, challenge_json: String,
} }
@@ -132,6 +133,16 @@ pub async fn login_route(
query.redirect.get_encoded() query.redirect.get_encoded()
)); ));
} }
// Check if given login is not acceptable
else if req
.as_ref()
.map(|r| string_utils::is_acceptable_login(&r.login))
== Some(false)
{
danger = Some(
"Given login could not be processed, because it has an invalid format!".to_string(),
);
}
// Try to authenticate user // Try to authenticate user
else if let Some(req) = &req { else if let Some(req) = &req {
login = req.login.clone(); login = req.login.clone();
@@ -199,7 +210,7 @@ pub async fn login_route(
HttpResponse::Ok().content_type("text/html").body( HttpResponse::Ok().content_type("text/html").body(
LoginTemplate { LoginTemplate {
_p: BaseLoginPage { p: BaseLoginPage {
page_title: "Login", page_title: "Login",
danger, danger,
success, success,
@@ -273,7 +284,7 @@ pub async fn reset_password_route(
HttpResponse::Ok().content_type("text/html").body( HttpResponse::Ok().content_type("text/html").body(
PasswordResetTemplate { PasswordResetTemplate {
_p: BaseLoginPage { p: BaseLoginPage {
page_title: "Password reset", page_title: "Password reset",
danger, danger,
success: None, success: None,
@@ -323,7 +334,7 @@ pub async fn choose_2fa_method(
HttpResponse::Ok().content_type("text/html").body( HttpResponse::Ok().content_type("text/html").body(
ChooseSecondFactorTemplate { ChooseSecondFactorTemplate {
_p: BaseLoginPage { p: BaseLoginPage {
page_title: "Two factor authentication", page_title: "Two factor authentication",
danger: None, danger: None,
success: None, success: None,
@@ -408,7 +419,7 @@ pub async fn login_with_otp(
HttpResponse::Ok().body( HttpResponse::Ok().body(
LoginWithOTPTemplate { LoginWithOTPTemplate {
_p: BaseLoginPage { p: BaseLoginPage {
danger, danger,
success: None, success: None,
page_title: "Two-Factor Auth", page_title: "Two-Factor Auth",
@@ -473,7 +484,7 @@ pub async fn login_with_webauthn(
HttpResponse::Ok().body( HttpResponse::Ok().body(
LoginWithWebauthnTemplate { LoginWithWebauthnTemplate {
_p: BaseLoginPage { p: BaseLoginPage {
danger: None, danger: None,
success: None, success: None,
page_title: "Two-Factor Auth", page_title: "Two-Factor Auth",

View File

@@ -22,14 +22,14 @@ use crate::data::session_identity::{SessionIdentity, SessionStatus};
#[derive(askama::Template)] #[derive(askama::Template)]
#[template(path = "login/prov_login_error.html")] #[template(path = "login/prov_login_error.html")]
struct ProviderLoginError<'a> { struct ProviderLoginError<'a> {
_p: BaseLoginPage<'a>, p: BaseLoginPage<'a>,
message: &'a str, message: &'a str,
} }
impl<'a> ProviderLoginError<'a> { impl<'a> ProviderLoginError<'a> {
pub fn get(message: &'a str, redirect_uri: &'a LoginRedirect) -> HttpResponse { pub fn get(message: &'a str, redirect_uri: &'a LoginRedirect) -> HttpResponse {
let body = Self { let body = Self {
_p: BaseLoginPage { p: BaseLoginPage {
danger: None, danger: None,
success: None, success: None,
page_title: "Upstream login", page_title: "Upstream login",

View File

@@ -45,14 +45,14 @@ impl<'a> BaseSettingsPage<'a> {
#[derive(Template)] #[derive(Template)]
#[template(path = "settings/account_details.html")] #[template(path = "settings/account_details.html")]
struct AccountDetailsPage<'a> { struct AccountDetailsPage<'a> {
_p: BaseSettingsPage<'a>, p: BaseSettingsPage<'a>,
remote_ip: String, remote_ip: String,
} }
#[derive(Template)] #[derive(Template)]
#[template(path = "settings/change_password.html")] #[template(path = "settings/change_password.html")]
struct ChangePasswordPage<'a> { struct ChangePasswordPage<'a> {
_p: BaseSettingsPage<'a>, p: BaseSettingsPage<'a>,
min_pwd_len: usize, min_pwd_len: usize,
} }
@@ -61,7 +61,7 @@ pub async fn account_settings_details_route(user: CurrentUser, ip: RemoteIP) ->
let user = user.into(); let user = user.into();
HttpResponse::Ok().body( HttpResponse::Ok().body(
AccountDetailsPage { AccountDetailsPage {
_p: BaseSettingsPage::get("Account details", &user, None, None), p: BaseSettingsPage::get("Account details", &user, None, None),
remote_ip: ip.0.to_string(), remote_ip: ip.0.to_string(),
} }
.render() .render()
@@ -145,7 +145,7 @@ pub async fn change_password_route(
HttpResponse::Ok().body( HttpResponse::Ok().body(
ChangePasswordPage { ChangePasswordPage {
_p: BaseSettingsPage::get("Change password", &user, danger, success), p: BaseSettingsPage::get("Change password", &user, danger, success),
min_pwd_len: MIN_PASS_LEN, min_pwd_len: MIN_PASS_LEN,
} }
.render() .render()

View File

@@ -17,14 +17,14 @@ use crate::data::webauthn_manager::WebAuthManagerReq;
#[derive(Template)] #[derive(Template)]
#[template(path = "settings/two_factors_page.html")] #[template(path = "settings/two_factors_page.html")]
struct TwoFactorsPage<'a> { struct TwoFactorsPage<'a> {
_p: BaseSettingsPage<'a>, p: BaseSettingsPage<'a>,
user: &'a User, user: &'a User,
} }
#[derive(Template)] #[derive(Template)]
#[template(path = "settings/add_2fa_totp_page.html")] #[template(path = "settings/add_2fa_totp_page.html")]
struct AddTotpPage<'a> { struct AddTotpPage<'a> {
_p: BaseSettingsPage<'a>, p: BaseSettingsPage<'a>,
qr_code: String, qr_code: String,
account_name: String, account_name: String,
secret_key: String, secret_key: String,
@@ -34,7 +34,7 @@ struct AddTotpPage<'a> {
#[derive(Template)] #[derive(Template)]
#[template(path = "settings/add_webauthn_page.html")] #[template(path = "settings/add_webauthn_page.html")]
struct AddWebauhtnPage<'a> { struct AddWebauhtnPage<'a> {
_p: BaseSettingsPage<'a>, p: BaseSettingsPage<'a>,
opaque_state: String, opaque_state: String,
challenge_json: String, challenge_json: String,
max_name_len: usize, max_name_len: usize,
@@ -44,7 +44,7 @@ struct AddWebauhtnPage<'a> {
pub async fn two_factors_route(user: CurrentUser) -> impl Responder { pub async fn two_factors_route(user: CurrentUser) -> impl Responder {
HttpResponse::Ok().body( HttpResponse::Ok().body(
TwoFactorsPage { TwoFactorsPage {
_p: BaseSettingsPage::get("Two factor auth", &user, None, None), p: BaseSettingsPage::get("Two factor auth", &user, None, None),
user: user.deref(), user: user.deref(),
} }
.render() .render()
@@ -71,7 +71,7 @@ pub async fn add_totp_factor_route(user: CurrentUser) -> impl Responder {
HttpResponse::Ok().body( HttpResponse::Ok().body(
AddTotpPage { AddTotpPage {
_p: BaseSettingsPage::get("New authenticator app", &user, None, None), p: BaseSettingsPage::get("New authenticator app", &user, None, None),
qr_code: BASE64_STANDARD.encode(qr_code), qr_code: BASE64_STANDARD.encode(qr_code),
account_name: key.account_name(&user, AppConfig::get()), account_name: key.account_name(&user, AppConfig::get()),
secret_key: key.get_secret(), secret_key: key.get_secret(),
@@ -106,7 +106,7 @@ pub async fn add_webauthn_factor_route(
HttpResponse::Ok().body( HttpResponse::Ok().body(
AddWebauhtnPage { AddWebauhtnPage {
_p: BaseSettingsPage::get("New security key", &user, None, None), p: BaseSettingsPage::get("New security key", &user, None, None),
opaque_state: registration_request.opaque_state, opaque_state: registration_request.opaque_state,
challenge_json: urlencoding::encode(&challenge_json).to_string(), challenge_json: urlencoding::encode(&challenge_json).to_string(),

View File

@@ -311,7 +311,7 @@ impl Eq for User {}
impl Default for User { impl Default for User {
fn default() -> Self { fn default() -> Self {
Self { Self {
uid: UserID("".to_string()), uid: UserID(uuid::Uuid::new_v4().to_string()),
first_name: "".to_string(), first_name: "".to_string(),
last_name: "".to_string(), last_name: "".to_string(),
username: "".to_string(), username: "".to_string(),

View File

@@ -36,9 +36,14 @@ pub fn apply_env_vars(val: &str) -> String {
val val
} }
/// Check out whether a given login is acceptable or not
pub fn is_acceptable_login(login: &str) -> bool {
mailchecker::is_valid(login) || lazy_regex::regex!("^[a-zA-Z0-9-+]+$").is_match(login)
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::utils::string_utils::apply_env_vars; use crate::utils::string_utils::{apply_env_vars, is_acceptable_login};
use std::env; use std::env;
const VAR_ONE: &str = "VAR_ONE"; const VAR_ONE: &str = "VAR_ONE";
@@ -56,4 +61,12 @@ mod test {
let src = format!("This is ${{{}}}", VAR_INVALID); let src = format!("This is ${{{}}}", VAR_INVALID);
assert_eq!(src, apply_env_vars(&src)); assert_eq!(src, apply_env_vars(&src));
} }
#[test]
fn test_is_acceptable_login() {
assert!(is_acceptable_login("admin"));
assert!(is_acceptable_login("someone@somewhere.fr"));
assert!(!is_acceptable_login("someone@somewhere.#fr"));
assert!(!is_acceptable_login("bad bad"));
}
} }

View File

@@ -8,7 +8,7 @@
<!-- No indexing --> <!-- No indexing -->
<meta name="robots" content="noindex, nofollow"> <meta name="robots" content="noindex, nofollow">
<title>{{ _p.app_name }} - {{ _p.page_title }}</title> <title>{{ p.app_name }} - {{ p.page_title }}</title>
<!-- Bootstrap core CSS --> <!-- Bootstrap core CSS -->
<link href="/assets/css/bootstrap.css" rel="stylesheet" crossorigin="anonymous"/> <link href="/assets/css/bootstrap.css" rel="stylesheet" crossorigin="anonymous"/>
@@ -43,15 +43,15 @@
<main class="form-signin"> <main class="form-signin">
<h1 class="h3 mb-3 fw-normal" style="margin-bottom: 2rem !important;">{{ _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">
{{ danger }} {{ danger }}
</div> </div>
{% endif %} {% endif %}
{% if let Some(success) = _p.success %} {% if let Some(success) = p.success %}
<div class="alert alert-success" role="alert"> <div class="alert alert-success" role="alert">
{{ success }} {{ success }}
</div> </div>

View File

@@ -5,7 +5,7 @@
<p>You need to validate a second factor to complete your login.</p> <p>You need to validate a second factor to complete your login.</p>
{% for factor in user.get_distinct_factors_types() %} {% for factor in user.get_distinct_factors_types() %}
<a class="btn btn-primary btn-lg" href="{{ factor.login_url(_p.redirect_uri) }}" style="width: 100%; display: flex;"> <a class="btn btn-primary btn-lg" href="{{ factor.login_url(p.redirect_uri) }}" style="width: 100%; display: flex;">
<img src="{{ factor.type_image() }}" alt="Factor icon" style="margin-right: 1em;" /> <img src="{{ factor.type_image() }}" alt="Factor icon" style="margin-right: 1em;" />
<div style="text-align: left;"> <div style="text-align: left;">
{{ factor.type_str() }} <br/> {{ factor.type_str() }} <br/>

View File

@@ -18,7 +18,7 @@
</style> </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">
<input name="login" type="text" required class="form-control" id="floatingName" placeholder="unsername" <input name="login" type="text" required class="form-control" id="floatingName" placeholder="unsername"
@@ -41,7 +41,7 @@
{% if !providers.is_empty() %} {% if !providers.is_empty() %}
<div id="providers"> <div id="providers">
{% for prov in providers %} {% for prov in providers %}
<a class="btn btn-secondary btn-lg provider-button" href="{{ prov.login_url(_p.redirect_uri) }}"> <a class="btn btn-secondary btn-lg provider-button" href="{{ prov.login_url(p.redirect_uri) }}">
<img src="{{ prov.logo_url() }}" alt="Provider icon"/> <img src="{{ prov.logo_url() }}" alt="Provider icon"/>
<div style="text-align: left;"> <div style="text-align: left;">
Login using {{ prov.name }} <br/> Login using {{ prov.name }} <br/>

View File

@@ -28,7 +28,7 @@
<div style="margin-top: 10px;"> <div style="margin-top: 10px;">
<a href="/2fa_auth?force_display=true&redirect={{ _p.redirect_uri.get_encoded() }}">Sign in using another factor</a><br/> <a href="/2fa_auth?force_display=true&redirect={{ p.redirect_uri.get_encoded() }}">Sign in using another factor</a><br/>
<a href="/logout">Sign out</a> <a href="/logout">Sign out</a>
</div> </div>

View File

@@ -1,6 +1,6 @@
{% extends "base_login_page.html" %} {% extends "base_login_page.html" %}
{% block content %} {% block content %}
<form action="/reset_password?redirect={{ _p.redirect_uri.get_encoded() }}" method="post" id="reset_password_form"> <form action="/reset_password?redirect={{ p.redirect_uri.get_encoded() }}" method="post" id="reset_password_form">
<div> <div>
<p>You need to configure a new password:</p> <p>You need to configure a new password:</p>

View File

@@ -7,7 +7,7 @@
<p style="margin-top: 10px; text-align: justify;">{{ message }}</p> <p style="margin-top: 10px; text-align: justify;">{{ message }}</p>
</div> </div>
<a href="/login?redirect={{ _p.redirect_uri.get_encoded() }}">Go back to login</a> <a href="/login?redirect={{ p.redirect_uri.get_encoded() }}">Go back to login</a>
{% endblock content %} {% endblock content %}

View File

@@ -12,13 +12,13 @@
</div> </div>
<div style="margin-top: 10px;"> <div style="margin-top: 10px;">
<a href="/2fa_auth?force_display=true&redirect={{ _p.redirect_uri.get_encoded() }}">Sign in using another factor</a><br/> <a href="/2fa_auth?force_display=true&redirect={{ p.redirect_uri.get_encoded() }}">Sign in using another factor</a><br/>
<a href="/logout">Sign out</a> <a href="/logout">Sign out</a>
</div> </div>
<script src="/assets/js/base64_lib.js"></script> <script src="/assets/js/base64_lib.js"></script>
<script> <script>
const REDIRECT_URI = decodeURIComponent("{{ _p.redirect_uri.get_encoded() }}"); const REDIRECT_URI = decodeURIComponent("{{ p.redirect_uri.get_encoded() }}");
const OPAQUE_STATE = "{{ opaque_state }}"; const OPAQUE_STATE = "{{ opaque_state }}";
const AUTH_CHALLENGE = JSON.parse(decodeURIComponent("{{ challenge_json }}")); const AUTH_CHALLENGE = JSON.parse(decodeURIComponent("{{ challenge_json }}"));
// Decode data // Decode data

View File

@@ -5,27 +5,27 @@
<tbody> <tbody>
<tr> <tr>
<th scope="row">User ID</th> <th scope="row">User ID</th>
<td>{{ _p.user.uid.0 }}</td> <td>{{ p.user.uid.0 }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">First name</th> <th scope="row">First name</th>
<td>{{ _p.user.first_name }}</td> <td>{{ p.user.first_name }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Last name</th> <th scope="row">Last name</th>
<td>{{ _p.user.last_name }}</td> <td>{{ p.user.last_name }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Username</th> <th scope="row">Username</th>
<td>{{ _p.user.username }}</td> <td>{{ p.user.username }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Email</th> <th scope="row">Email</th>
<td>{{ _p.user.email }}</td> <td>{{ p.user.email }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Account type</th> <th scope="row">Account type</th>
<td>{% if _p.user.admin %}Admin{% else %}Regular user{% endif %}</td> <td>{% if p.user.admin %}Admin{% else %}Regular user{% endif %}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>{{ _p.page_title }} - {{ _p.app_name }}</title> <title>{{ p.page_title }} - {{ p.app_name }}</title>
<!-- Bootstrap core CSS --> <!-- Bootstrap core CSS -->
<link href="/assets/css/bootstrap.css" rel="stylesheet" crossorigin="anonymous"/> <link href="/assets/css/bootstrap.css" rel="stylesheet" crossorigin="anonymous"/>
@@ -12,10 +12,10 @@
<body> <body>
<div class="d-flex flex-column flex-shrink-0 p-3 bg-light" style="width: 280px;"> <div class="d-flex flex-column flex-shrink-0 p-3 bg-light" style="width: 280px;">
<a href="/" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto link-dark text-decoration-none"> <a href="/" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto link-dark text-decoration-none">
<span class="fs-4">{{ _p.app_name }}</span> <span class="fs-4">{{ p.app_name }}</span>
</a> </a>
{% if _p.user.admin %} {% if p.user.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">
@@ -24,7 +24,7 @@
Account details Account details
</a> </a>
</li> </li>
{% if _p.user.allow_local_login %} {% if p.user.allow_local_login %}
<li> <li>
<a href="/settings/change_password" class="nav-link link-dark"> <a href="/settings/change_password" class="nav-link link-dark">
Change password Change password
@@ -37,7 +37,7 @@
</a> </a>
</li> </li>
{% if _p.user.admin %} {% if p.user.admin %}
<hr/> <hr/>
<li> <li>
<a href="/admin/clients" class="nav-link link-dark"> <a href="/admin/clients" class="nav-link link-dark">
@@ -61,7 +61,7 @@
<a href="#" class="d-flex align-items-center link-dark text-decoration-none dropdown-toggle" id="dropdownUser" <a href="#" class="d-flex align-items-center link-dark text-decoration-none dropdown-toggle" id="dropdownUser"
data-bs-toggle="dropdown" aria-expanded="false"> data-bs-toggle="dropdown" aria-expanded="false">
<img src="/assets/img/account.png" alt="" width="32" height="32" class="rounded-circle me-2"> <img src="/assets/img/account.png" alt="" width="32" height="32" class="rounded-circle me-2">
<strong>{{ _p.user.username }}</strong> <strong>{{ p.user.username }}</strong>
</a> </a>
<ul class="dropdown-menu text-small shadow" aria-labelledby="dropdownUser"> <ul class="dropdown-menu text-small shadow" aria-labelledby="dropdownUser">
<li><a class="dropdown-item" href="/logout">Sign out</a></li> <li><a class="dropdown-item" href="/logout">Sign out</a></li>
@@ -70,14 +70,14 @@
</div> </div>
<div class="page_body" style="flex: 1"> <div class="page_body" style="flex: 1">
{% if let Some(msg) = _p.danger_message %} {% if let Some(msg) = p.danger_message %}
<div class="alert alert-danger">{{ msg }}</div> <div class="alert alert-danger">{{ msg }}</div>
{% endif %} {% endif %}
{% if let Some(msg) = _p.success_message %} {% if let Some(msg) = p.success_message %}
<div class="alert alert-success">{{ msg }}</div> <div class="alert alert-success">{{ msg }}</div>
{% endif %} {% endif %}
<h2 class="bd-title mt-0" style="margin-bottom: 40px;">{{ _p.page_title }}</h2> <h2 class="bd-title mt-0" style="margin-bottom: 40px;">{{ p.page_title }}</h2>
{% block content %} {% block content %}
TO_REPLACE TO_REPLACE
@@ -92,8 +92,8 @@
}) })
</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>
{% endif %} {% endif %}
<script src="/assets/js/ip_location_service.js"></script> <script src="/assets/js/ip_location_service.js"></script>
</body> </body>

View File

@@ -195,7 +195,7 @@
</div> </div>
</fieldset> </fieldset>
<input type="submit" class="btn btn-primary mt-4" value="{{ _p.page_title }}"> <input type="submit" class="btn btn-primary mt-4" value="{{ p.page_title }}">
</form> </form>
<script> <script>