ldpa commit 2
This commit is contained in:
@@ -100,4 +100,5 @@ AUTHORIZATION_TYPE=local
|
||||
# LDAP_USERNAME_ATTRIBUTE=sAMAccountName
|
||||
# LDAP_USER_FILTER=(uid={username})
|
||||
# LDAP_USERNAME_ATTRIBUTE=uid
|
||||
# email auth: LDAP_EMAIL_ATTRIBUTE=mail
|
||||
|
||||
|
||||
@@ -1,27 +1,15 @@
|
||||
dn: ou=people,dc=example,dc=org
|
||||
dn: ou=people,dc=organization,dc=local
|
||||
objectClass: organizationalUnit
|
||||
ou: people
|
||||
|
||||
dn: uid=mateusz,ou=people,dc=example,dc=org
|
||||
dn: uid=admin,ou=people,dc=organization,dc=local
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: organizationalPerson
|
||||
objectClass: person
|
||||
objectClass: top
|
||||
uid: mateusz
|
||||
cn: Mateusz Testowy
|
||||
sn: Testowy
|
||||
displayName: Mateusz Testowy
|
||||
mail: mateusz@example.org
|
||||
userPassword: test1234
|
||||
|
||||
dn: uid=anna,ou=people,dc=example,dc=org
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: organizationalPerson
|
||||
objectClass: person
|
||||
objectClass: top
|
||||
uid: anna
|
||||
cn: Anna Testowa
|
||||
sn: Testowa
|
||||
displayName: Anna Testowa
|
||||
mail: anna@example.org
|
||||
userPassword: test1234
|
||||
uid: admin
|
||||
cn: Organization Administrator
|
||||
sn: Administrator
|
||||
displayName: Organization Administrator
|
||||
mail: admin@organization.local
|
||||
userPassword: test1234!
|
||||
@@ -10,7 +10,7 @@ services:
|
||||
LDAP_CONFIG_PASSWORD: config
|
||||
LDAP_TLS: "false"
|
||||
ports:
|
||||
- "12389:389"
|
||||
- "10.87.2.6:389:389"
|
||||
volumes:
|
||||
- ./bootstrap:/container/service/slapd/assets/config/bootstrap/ldif/custom:ro
|
||||
command: --copy-service
|
||||
@@ -23,6 +23,6 @@ services:
|
||||
PHPLDAPADMIN_LDAP_HOSTS: ldap
|
||||
PHPLDAPADMIN_HTTPS: "false"
|
||||
ports:
|
||||
- "12390:80"
|
||||
- "10.87.2.6:8088:80"
|
||||
depends_on:
|
||||
- ldap
|
||||
|
||||
@@ -180,6 +180,7 @@ async fn home(State(state): State<SharedState>) -> Response {
|
||||
include_str!("../static/home.html"),
|
||||
&state.asset_version,
|
||||
state.registration_enabled,
|
||||
state.ldap.is_some(),
|
||||
&state.frontend_log_level,
|
||||
state.upload_max_size_bytes,
|
||||
"home",
|
||||
@@ -195,6 +196,7 @@ async fn pad(State(state): State<SharedState>, Path(slug): Path<String>) -> Resp
|
||||
&html,
|
||||
&state.asset_version,
|
||||
state.registration_enabled,
|
||||
state.ldap.is_some(),
|
||||
&state.frontend_log_level,
|
||||
state.upload_max_size_bytes,
|
||||
"pad",
|
||||
@@ -222,6 +224,7 @@ async fn public_page(State(state): State<SharedState>, Path(token): Path<String>
|
||||
include_str!("../static/public.html"),
|
||||
&state.asset_version,
|
||||
state.registration_enabled,
|
||||
state.ldap.is_some(),
|
||||
&state.frontend_log_level,
|
||||
state.upload_max_size_bytes,
|
||||
"public",
|
||||
@@ -254,6 +257,7 @@ async fn workspace(
|
||||
&html,
|
||||
&state.asset_version,
|
||||
state.registration_enabled,
|
||||
state.ldap.is_some(),
|
||||
&state.frontend_log_level,
|
||||
state.upload_max_size_bytes,
|
||||
"workspace",
|
||||
@@ -308,6 +312,7 @@ async fn note(
|
||||
&html,
|
||||
&state.asset_version,
|
||||
state.registration_enabled,
|
||||
state.ldap.is_some(),
|
||||
&state.frontend_log_level,
|
||||
state.upload_max_size_bytes,
|
||||
"note",
|
||||
|
||||
+5
-3
@@ -27,12 +27,13 @@ pub fn render_html(
|
||||
template: &str,
|
||||
asset_version: &str,
|
||||
registration_enabled: bool,
|
||||
external_auth: bool,
|
||||
frontend_log_level: &str,
|
||||
upload_max_size_bytes: usize,
|
||||
entrypoint: &str,
|
||||
) -> Response {
|
||||
let urls = AssetUrls::new(asset_version);
|
||||
let frontend_config = frontend_config(frontend_log_level, upload_max_size_bytes);
|
||||
let frontend_config = frontend_config(frontend_log_level, upload_max_size_bytes, external_auth);
|
||||
let html = template
|
||||
.replace("__APP_STYLESHEET__", &urls.stylesheet("styles"))
|
||||
.replace("__APP_IMPORT_MAP__", &urls.import_map())
|
||||
@@ -59,11 +60,12 @@ pub fn stylesheet_tag(asset_version: &str, name: &str) -> String {
|
||||
AssetUrls::new(asset_version).stylesheet(name)
|
||||
}
|
||||
|
||||
fn frontend_config(frontend_log_level: &str, upload_max_size_bytes: usize) -> String {
|
||||
fn frontend_config(frontend_log_level: &str, upload_max_size_bytes: usize, external_auth: bool) -> String {
|
||||
format!(
|
||||
r#"<script>window.__RUSTPAD_CONFIG__=Object.freeze({{frontendLogLevel:"{}",uploadMaxSizeBytes:{}}});</script>"#,
|
||||
r#"<script>window.__RUSTPAD_CONFIG__=Object.freeze({{frontendLogLevel:"{}",uploadMaxSizeBytes:{},externalAuth:{}}});</script>"#,
|
||||
escape_js_string(frontend_log_level),
|
||||
upload_max_size_bytes,
|
||||
external_auth,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+15
-2
@@ -53,13 +53,26 @@ pub async fn authenticate(
|
||||
config.email_attribute.as_str(),
|
||||
config.display_name_attribute.as_str(),
|
||||
];
|
||||
let (entries, _) = ldap
|
||||
.search(&config.base_dn, Scope::Subtree, &filter, attributes)
|
||||
let (mut entries, _) = ldap
|
||||
.search(&config.base_dn, Scope::Subtree, &filter, attributes.clone())
|
||||
.await
|
||||
.map_err(|error| format!("LDAP search failed: {error}"))?
|
||||
.success()
|
||||
.map_err(|error| format!("LDAP search rejected: {error}"))?;
|
||||
|
||||
// Allow users to sign in with their directory e-mail even when the configured
|
||||
// primary filter searches by uid/sAMAccountName only.
|
||||
if entries.is_empty() && login.trim().contains('@') {
|
||||
let email_filter = format!("({}={})", config.email_attribute, escaped);
|
||||
let (email_entries, _) = ldap
|
||||
.search(&config.base_dn, Scope::Subtree, &email_filter, attributes)
|
||||
.await
|
||||
.map_err(|error| format!("LDAP e-mail search failed: {error}"))?
|
||||
.success()
|
||||
.map_err(|error| format!("LDAP e-mail search rejected: {error}"))?;
|
||||
entries = email_entries;
|
||||
}
|
||||
|
||||
if entries.len() != 1 {
|
||||
let _ = ldap.unbind().await;
|
||||
return Ok(None);
|
||||
|
||||
+12
-2
@@ -24,6 +24,7 @@ export function bindIdentityDialog({ dialog, onIdentity, initialMode = "login" }
|
||||
const resetButton = dialog.querySelector("#show-reset");
|
||||
const backButton = dialog.querySelector("#reset-back");
|
||||
const registrationEnabled = document.body.dataset.registrationEnabled === "true";
|
||||
const externalAuth = window.__RUSTPAD_CONFIG__?.externalAuth === true;
|
||||
let mode = initialMode;
|
||||
|
||||
const setMode = (nextMode) => {
|
||||
@@ -47,9 +48,12 @@ export function bindIdentityDialog({ dialog, onIdentity, initialMode = "login" }
|
||||
submit.textContent = resetting ? "Send reset link" : registering ? "Create account" : "Log in";
|
||||
switchMode.hidden = resetting || !registrationEnabled;
|
||||
switchMode.textContent = registering ? "Already registered? Log in" : "Create an account";
|
||||
resetButton.hidden = resetting || registering;
|
||||
resetButton.hidden = resetting || registering || externalAuth;
|
||||
backButton.hidden = !resetting;
|
||||
const loginMode = mode === "login";
|
||||
email.type = externalAuth && loginMode ? "text" : "email";
|
||||
if (externalAuth && loginMode) password.removeAttribute("minlength");
|
||||
else password.minLength = 8;
|
||||
form.autocomplete = loginMode ? "on" : "off";
|
||||
nickname.autocomplete = "off";
|
||||
nickname.dataset.bwignore = "true";
|
||||
@@ -143,6 +147,7 @@ function bindLegacyIdentityDialog({ dialog, onIdentity }) {
|
||||
const backButton = dialog.querySelector("#auth-back");
|
||||
const logoutButton = dialog.querySelector("#logout-account");
|
||||
const registrationEnabled = document.body.dataset.registrationEnabled === "true";
|
||||
const externalAuth = window.__RUSTPAD_CONFIG__?.externalAuth === true;
|
||||
let mode = "login";
|
||||
|
||||
const updateActions = () => {
|
||||
@@ -170,6 +175,9 @@ function bindLegacyIdentityDialog({ dialog, onIdentity }) {
|
||||
modeTitle.textContent = mode === "register" ? "Register nickname" : "Log in";
|
||||
authSubmit.textContent = mode === "register" ? "Register and continue" : "Log in and continue";
|
||||
const loginMode = mode === "login";
|
||||
email.type = externalAuth && loginMode ? "text" : "email";
|
||||
if (externalAuth && loginMode) password.removeAttribute("minlength");
|
||||
else password.minLength = 8;
|
||||
form.autocomplete = loginMode ? "on" : "off";
|
||||
nickname.autocomplete = "off";
|
||||
nickname.dataset.bwignore = "true";
|
||||
@@ -194,7 +202,9 @@ function bindLegacyIdentityDialog({ dialog, onIdentity }) {
|
||||
dialog.addEventListener("close", collapse);
|
||||
dialog.addEventListener("cancel", collapse);
|
||||
|
||||
dialog.querySelector("#show-reset")?.addEventListener("click", async () => {
|
||||
const legacyResetButton = dialog.querySelector("#show-reset");
|
||||
if (legacyResetButton) legacyResetButton.hidden = externalAuth;
|
||||
legacyResetButton?.addEventListener("click", async () => {
|
||||
const value = email.value.trim() || await askInput({
|
||||
title: "Reset password",
|
||||
message: "Enter the e-mail address assigned to your local account.",
|
||||
|
||||
Reference in New Issue
Block a user