[fix](trx-frontend-http): block Guest password changes
CI / lint (pull_request) Successful in 2m25s
CI / test (pull_request) Successful in 9m5s
CI / frontend (pull_request) Successful in 5m23s
CI / reuse (pull_request) Successful in 5s
CI / lint (push) Successful in 2m24s
CI / test (push) Successful in 9m11s
CI / frontend (push) Successful in 5m22s
CI / reuse (push) Successful in 5s
CI / lint (pull_request) Successful in 2m25s
CI / test (pull_request) Successful in 9m5s
CI / frontend (pull_request) Successful in 5m23s
CI / reuse (pull_request) Successful in 5s
CI / lint (push) Successful in 2m24s
CI / test (push) Successful in 9m11s
CI / frontend (push) Successful in 5m22s
CI / reuse (push) Successful in 5s
This commit was merged in pull request #65.
This commit is contained in:
@@ -6097,6 +6097,14 @@ async function refreshUserManagement() {
|
||||
password.className = "auth-input";
|
||||
password.minLength = 8;
|
||||
password.maxLength = 1024;
|
||||
const syncPasswordAccess = () => {
|
||||
const guestActive = guestInput?.checked === true;
|
||||
password.disabled = guestActive;
|
||||
password.title = guestActive ? "Password changes are unavailable while the Guest role is active" : "";
|
||||
if (guestActive) password.value = "";
|
||||
};
|
||||
roleInputs.forEach(({ input }) => input.addEventListener("change", syncPasswordAccess));
|
||||
syncPasswordAccess();
|
||||
const save = document.createElement("button");
|
||||
save.type = "button";
|
||||
save.textContent = "Save";
|
||||
|
||||
@@ -5173,6 +5173,14 @@ async function refreshUserManagement() {
|
||||
}
|
||||
const password = document.createElement("input");
|
||||
password.type = "password"; password.placeholder = "New password (8+ characters)"; password.autocomplete = "new-password"; password.className = "auth-input"; password.minLength = 8; password.maxLength = 1024;
|
||||
const syncPasswordAccess = () => {
|
||||
const guestActive = guestInput?.checked === true;
|
||||
password.disabled = guestActive;
|
||||
password.title = guestActive ? "Password changes are unavailable while the Guest role is active" : "";
|
||||
if (guestActive) password.value = "";
|
||||
};
|
||||
roleInputs.forEach(({ input }) => input.addEventListener("change", syncPasswordAccess));
|
||||
syncPasswordAccess();
|
||||
const save = document.createElement("button"); save.type = "button"; save.textContent = "Save";
|
||||
save.addEventListener("click", async () => {
|
||||
const changes: { roles?: AuthRole[]; password?: string; enabled?: boolean } = {
|
||||
|
||||
@@ -18,6 +18,7 @@ const fixture = await startWebFixture({
|
||||
},
|
||||
users: [
|
||||
{ username: "admin", roles: ALL_ROLES, enabled: true },
|
||||
{ username: "guest", roles: ["guest"], enabled: true },
|
||||
{ username: "listener", roles: ["read"], enabled: false },
|
||||
],
|
||||
});
|
||||
@@ -41,6 +42,7 @@ try {
|
||||
const rows = [...document.querySelectorAll("#user-list > .sch-row")];
|
||||
const rowFor = (username) => rows.find((row) => row.querySelector("strong")?.textContent.startsWith(username));
|
||||
const admin = rowFor("admin");
|
||||
const guest = rowFor("guest");
|
||||
const listener = rowFor("listener");
|
||||
const role = (row, value) => row?.querySelector(`input[value="${value}"]`);
|
||||
return {
|
||||
@@ -49,6 +51,7 @@ try {
|
||||
adminRoleLocked: role(admin, "administrator")?.disabled,
|
||||
adminGuestLocked: role(admin, "guest")?.disabled,
|
||||
adminRemoveLocked: admin?.querySelector("button.danger")?.disabled,
|
||||
guestPasswordLocked: guest?.querySelector('input[type="password"]')?.disabled,
|
||||
listenerEnabled: listener?.querySelector('input[type="checkbox"]')?.checked,
|
||||
listenerRead: role(listener, "read")?.checked,
|
||||
};
|
||||
@@ -58,8 +61,16 @@ try {
|
||||
assert.equal(state.adminRoleLocked, true);
|
||||
assert.equal(state.adminGuestLocked, true);
|
||||
assert.equal(state.adminRemoveLocked, true);
|
||||
assert.equal(state.guestPasswordLocked, true);
|
||||
assert.equal(state.listenerEnabled, false);
|
||||
assert.equal(state.listenerRead, true);
|
||||
|
||||
const listenerRow = page.locator("#user-list > .sch-row").filter({ hasText: "listener" });
|
||||
const listenerPassword = listenerRow.locator('input[type="password"]');
|
||||
await listenerRow.locator('input[value="guest"]').check();
|
||||
assert.equal(await listenerPassword.isDisabled(), true);
|
||||
await listenerRow.locator('input[value="read"]').check();
|
||||
assert.equal(await listenerPassword.isDisabled(), false);
|
||||
assert.deepEqual(runtimeErrors, []);
|
||||
} finally {
|
||||
await browser.close();
|
||||
|
||||
@@ -514,6 +514,10 @@ impl UserStore {
|
||||
roles: Option<AuthRoles>,
|
||||
enabled: Option<bool>,
|
||||
) -> Result<(), UserStoreError> {
|
||||
let roles = roles
|
||||
.map(validate_roles)
|
||||
.transpose()
|
||||
.map_err(UserStoreError::Invalid)?;
|
||||
if let Some(value) = password {
|
||||
validate_password(value).map_err(UserStoreError::Invalid)?;
|
||||
}
|
||||
@@ -526,6 +530,16 @@ impl UserStore {
|
||||
.get(username)
|
||||
.cloned()
|
||||
.ok_or(UserStoreError::NotFound)?;
|
||||
if password.is_some()
|
||||
&& roles
|
||||
.as_ref()
|
||||
.unwrap_or(&old.roles)
|
||||
.contains(&AuthRole::Guest)
|
||||
{
|
||||
return Err(UserStoreError::Invalid(
|
||||
"password changes are not available while the guest role is active".to_string(),
|
||||
));
|
||||
}
|
||||
let remains_enabled_administrator = enabled.unwrap_or(old.enabled)
|
||||
&& roles
|
||||
.as_ref()
|
||||
@@ -544,7 +558,7 @@ impl UserStore {
|
||||
record.password_hash = hash;
|
||||
}
|
||||
if let Some(value) = roles {
|
||||
record.roles = validate_roles(value).map_err(UserStoreError::Invalid)?;
|
||||
record.roles = value;
|
||||
}
|
||||
if let Some(value) = enabled {
|
||||
record.enabled = value;
|
||||
@@ -1666,6 +1680,16 @@ mod tests {
|
||||
assert_eq!(response.status(), actix_web::http::StatusCode::OK);
|
||||
let admin_cookie = response.response().cookies().next().unwrap().to_string();
|
||||
|
||||
let request = aw_test::TestRequest::patch()
|
||||
.uri("/auth/users/guest")
|
||||
.insert_header((actix_web::http::header::COOKIE, admin_cookie.clone()))
|
||||
.set_json(serde_json::json!({"password":"changed-guest-password"}))
|
||||
.to_request();
|
||||
assert_eq!(
|
||||
aw_test::call_service(&app, request).await.status(),
|
||||
actix_web::http::StatusCode::BAD_REQUEST
|
||||
);
|
||||
|
||||
let request = aw_test::TestRequest::post()
|
||||
.uri("/auth/users")
|
||||
.insert_header((actix_web::http::header::COOKIE, admin_cookie.clone()))
|
||||
|
||||
Reference in New Issue
Block a user