From cf888fa47e749c330aa4c83e18d6fa3865dd8dfa Mon Sep 17 00:00:00 2001 From: Codex Date: Sat, 9 May 2026 22:56:19 +0300 Subject: [PATCH] =?UTF-8?q?UI=20-=20=D0=9C=D0=95=D0=96=D0=9F=D0=A0=D0=9E?= =?UTF-8?q?=D0=95=D0=9A=D0=A2=D0=9D=D0=90=D0=AF=20=D0=9A=D0=9E=D0=9C=D0=9C?= =?UTF-8?q?=D0=A3=D0=9D=D0=98=D0=9A=D0=90=D0=A6=D0=98=D0=AF:=20public=20ac?= =?UTF-8?q?cess=20=D0=B2=20Authentication=20Core?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../custom-templates/base/header_js.html | 128 ++++++++++++++++++ .../branding/nodedc-login.css | 49 +++++++ 2 files changed, 177 insertions(+) diff --git a/infra/authentik/custom-templates/base/header_js.html b/infra/authentik/custom-templates/base/header_js.html index f4b1658..612a137 100644 --- a/infra/authentik/custom-templates/base/header_js.html +++ b/infra/authentik/custom-templates/base/header_js.html @@ -81,6 +81,10 @@ ["Continue", "Продолжить"], ["Show password", "Показать пароль"], ["Hide password", "Скрыть пароль"], + ["Permission denied", "Доступ ограничен"], + ["Not you?", "Сменить пользователя"], + ["Request has been denied.", "Доступ к модулю ограничен."], + ["Go home", "Вернуться назад"], ]); function visitRoots(root, callback) { @@ -168,6 +172,113 @@ }); } + function hasPermissionDeniedScreen() { + let denied = false; + visitRoots(document, (root) => { + if (denied) return; + const text = root.textContent || ""; + denied = [ + "Permission denied", + "Доступ ограничен", + "Request has been denied.", + "Доступ к модулю ограничен.", + ].some((message) => text.includes(message)); + }); + return denied; + } + + function syncPermissionDeniedState() { + document.body?.classList.toggle("nodedc-auth-permission-denied", hasPermissionDeniedScreen()); + } + + function findAncestor(element, predicate, maxDepth = 8) { + let current = element; + for (let depth = 0; current && depth < maxDepth; depth += 1) { + if (predicate(current)) return current; + current = current.parentElement; + } + return null; + } + + function applyPermissionDeniedLayout(root) { + const deniedTitleMessages = ["Permission denied", "Доступ ограничен"]; + const deniedSubtitle = "Доступ к модулю NODE.DC ограничен."; + const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, { + acceptNode(node) { + const text = node.nodeValue?.trim() || ""; + return deniedTitleMessages.some((message) => text === message) + ? NodeFilter.FILTER_ACCEPT + : NodeFilter.FILTER_REJECT; + }, + }); + + const textNodes = []; + while (walker.nextNode()) { + textNodes.push(walker.currentNode); + } + + textNodes.forEach((node) => { + const titleElement = findAncestor( + node.parentElement, + (element) => + element.matches?.(".pf-c-title, .pf-v5-c-title, h1, h2") || + element.getAttribute?.("slot") === "title", + ); + const header = + titleElement?.closest?.(".pf-c-login__main-header") || + findAncestor(titleElement || node.parentElement, (element) => element.matches?.(".pf-c-login__main-header"), 10) || + root.querySelector?.(".pf-c-login__main-header"); + + if (titleElement) { + titleElement.style.display = "none"; + } + if (header) { + header.setAttribute("data-nodedc-permission-denied", "true"); + header.setAttribute("data-nodedc-denied-subtitle", deniedSubtitle); + } + }); + } + + function hidePermissionDeniedReason(root) { + const deniedMessages = ["Request has been denied.", "Доступ к модулю ограничен."]; + + root.querySelectorAll(".pf-c-alert, .pf-v5-c-alert, .pf-c-empty-state__body, [class*='alert']").forEach((element) => { + const text = element.textContent || ""; + if (deniedMessages.some((message) => text.includes(message))) { + element.style.display = "none"; + } + }); + + const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, { + acceptNode(node) { + const text = node.nodeValue?.trim() || ""; + return deniedMessages.some((message) => text.includes(message)) + ? NodeFilter.FILTER_ACCEPT + : NodeFilter.FILTER_REJECT; + }, + }); + + const textNodes = []; + while (walker.nextNode()) { + textNodes.push(walker.currentNode); + } + + textNodes.forEach((node) => { + let element = node.parentElement; + for (let depth = 0; element && depth < 6; depth += 1) { + const text = (element.textContent || "").trim(); + if (deniedMessages.some((message) => text === message || text.endsWith(message)) && text.length <= 96) { + element.style.display = "none"; + return; + } + element = element.parentElement; + } + if (node.parentElement) { + node.parentElement.style.display = "none"; + } + }); + } + function enhanceIdentifierField(root) { const input = root.querySelector("#ak-identifier-input"); if (!input || input.dataset.nodedcClearBound === "true") return; @@ -261,6 +372,19 @@ }); } + function ensureAccessRequestLink(root) { + const input = root.querySelector("#ak-identifier-input, #ak-stage-identification-password, #ak-stage-password-input"); + const form = input?.closest("form") || root.querySelector("form"); + if (!input || !form || form.querySelector("[data-nodedc-access-request='true']")) return; + + const link = document.createElement("a"); + link.className = "nodedc-auth-request-access"; + link.dataset.nodedcAccessRequest = "true"; + link.href = new URL("/request-access", getLauncherBaseUrl()).toString(); + link.textContent = "Запросить доступ"; + form.appendChild(link); + } + function hasAuthError(root) { const hasErrorElement = root.querySelector( ".pf-c-alert, .pf-m-error, .pf-c-helper-text__item.pf-m-error, [aria-invalid='true']" @@ -354,10 +478,14 @@ enhanceIdentifierField(root); normalizePasswordFields(root); enhanceSubmitHandoff(root); + ensureAccessRequestLink(root); translateAuthText(root); + applyPermissionDeniedLayout(root); + hidePermissionDeniedReason(root); restoreCardOnErrors(root); redirectCompletedLogout(root); }); + syncPermissionDeniedState(); } function scheduleEnhancement() { diff --git a/infra/authentik/custom-templates/branding/nodedc-login.css b/infra/authentik/custom-templates/branding/nodedc-login.css index 1c1fb82..f7a91e1 100644 --- a/infra/authentik/custom-templates/branding/nodedc-login.css +++ b/infra/authentik/custom-templates/branding/nodedc-login.css @@ -254,6 +254,25 @@ h1.pf-c-title { text-transform: none; } +.pf-c-login__main-header[data-nodedc-permission-denied="true"]::after, +body.nodedc-auth-permission-denied .pf-c-login__main-header::after { + content: attr(data-nodedc-denied-subtitle) !important; + margin-top: 0; +} + +body.nodedc-auth-permission-denied .pf-c-login__main-header::after { + content: "Доступ к модулю NODE.DC ограничен." !important; +} + +.pf-c-login__main-header[data-nodedc-permission-denied="true"] .pf-c-title, +.pf-c-login__main-header[data-nodedc-permission-denied="true"] .pf-c-title.pf-m-3xl, +.pf-c-login__main-header[data-nodedc-permission-denied="true"] h1.pf-c-title, +body.nodedc-auth-permission-denied .pf-c-login__main-header .pf-c-title, +body.nodedc-auth-permission-denied .pf-c-title.pf-m-3xl, +body.nodedc-auth-permission-denied h1.pf-c-title { + display: none !important; +} + .pf-c-login__main-body { padding: 0 !important; } @@ -457,6 +476,9 @@ input.pf-c-form-control:-webkit-autofill:focus { button.pf-m-primary, button[type="submit"], .pf-c-form .pf-c-button.pf-m-primary { + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; width: 100% !important; min-height: 3.25rem !important; margin-top: 1.25rem !important; @@ -468,7 +490,9 @@ button[type="submit"], color: var(--nodedc-auth-on-primary) !important; font-size: 0.95rem !important; font-weight: 600 !important; + line-height: 1 !important; letter-spacing: -0.02em !important; + text-align: center !important; text-transform: none !important; } @@ -479,6 +503,25 @@ button[type="submit"]:hover { color: var(--nodedc-auth-on-primary) !important; } +.nodedc-auth-request-access { + display: inline-flex !important; + justify-content: center !important; + width: 100% !important; + margin-top: -0.35rem !important; + color: var(--nodedc-auth-primary) !important; + font-size: 0.75rem !important; + line-height: 1rem !important; + font-weight: 650 !important; + letter-spacing: -0.01em !important; + text-decoration: none !important; +} + +.nodedc-auth-request-access:hover, +.nodedc-auth-request-access:focus { + color: var(--nodedc-auth-primary-hover) !important; + text-decoration: none !important; +} + .pf-c-alert, .pf-c-alert.pf-m-inline { border: 0 !important; @@ -499,6 +542,12 @@ button[type="submit"]:hover { font-weight: 600 !important; } +body.nodedc-auth-permission-denied .pf-c-alert, +body.nodedc-auth-permission-denied .pf-v5-c-alert, +body.nodedc-auth-permission-denied .pf-c-alert.pf-m-inline { + display: none !important; +} + a, .pf-c-button.pf-m-link, button.pf-m-link {