Skip to content

Modernize login#5500

Merged
lippserd merged 7 commits into
mainfrom
modernize-login
May 20, 2026
Merged

Modernize login#5500
lippserd merged 7 commits into
mainfrom
modernize-login

Conversation

@jrauh01
Copy link
Copy Markdown
Contributor

@jrauh01 jrauh01 commented May 4, 2026

This PR replaces the legacy Zend-based login form and authentication controller with their ipl equivalents, and extracts the login page view script markup into a reusable widget.

Convert LoginForm to CompatForm

LoginForm now extends CompatForm, replacing init()/createElements() with __construct()/assemble() and Zend decorator arrays with inline ipl decorator objects. The CsrfCounterMeasure and FormUid traits are added.

getRedirectUrl() is renamed to createRedirectUrl() to avoid overriding CompatForm::getRedirectUrl(), using hasBeenAssembled instead of $this->created and str_contains() instead of strpos(). onSuccess() is rewritten for the CompatForm lifecycle: void return, explicit setRedirectUrl() call, Icinga::app()->getResponse() instead of $this->getResponse(), and addMessage() instead of addError().

New LoginPage widget to replace view script

Extracts the login page structure (logo, footer, social links, decorative orbs) from login.phtml into a new LoginPage widget extending HtmlDocument, so the markup is defined in one place and no longer lives in a view script. Additional login buttons provided by LoginButtonHook are now grouped in a div.login-buttons flex container, rendered only when buttons are actually present.

Convert AuthenticationController to CompatController

AuthenticationController now extends CompatController, dropping login.phtml in favour of LoginPage and addContent(). The form is built with an ON_SUBMIT handler for the redirect and an ON_REQUEST handler delegating to LoginForm::onRequest(). Uses $this->getServerRequest() instead of ServerRequest::fromGlobals().

CSS / JS fixes

  • login.less:
    • height: 100% -> 100vh on #login (now a grandchild of #layout via .content)
    • .login-buttons styled as a flex column with a row gap and top margin
    • per-li error styling, including spacing inside .login-buttons
    • .icinga-form width and control-group spacing
    • align-items: center on .remember-me-box
    • label margin-right reset for the disabled-state description
  • history.js: #layout > #login -> #layout #login (direct-child selector broke with the new nesting)
  • login-orbs.less: fixes long-standing typo #orb-notifactions -> #orb-notifications

resolve #5495

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR modernizes the login flow by migrating the legacy Zend-based login view/controller/form to ipl/compat equivalents and consolidating the login page markup into a reusable widget, with related CSS/JS adjustments.

Changes:

  • Replace the Zend LoginForm with an ipl CompatForm implementation and adapt login handling to the compat lifecycle.
  • Replace the legacy login.phtml view script with a new LoginPage widget used by a CompatController-based AuthenticationController.
  • Update login-related CSS/JS selectors and styles, including spacing for additional login buttons and a typo fix in orb IDs.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
public/js/icinga/history.js Adjusts the login selector used during onload redirect handling for the new nesting.
public/css/icinga/login.less Updates layout and spacing for the new login markup and added login buttons container.
public/css/icinga/login-orbs.less Fixes the orb ID typo to match the updated markup (orb-notifications).
library/Icinga/Web/Widget/LoginPage.php Introduces a widget encapsulating the full login page structure (form, footer, social links, orbs).
application/views/scripts/authentication/login.phtml Removes the legacy view script in favor of the widget-based rendering.
application/forms/Authentication/LoginForm.php Migrates the login form to CompatForm, adds CSRF/UID, and updates success/error handling.
application/controllers/AuthenticationController.php Migrates controller to CompatController and renders LoginPage instead of a view script.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread application/forms/Authentication/LoginForm.php
Comment thread library/Icinga/Web/Widget/LoginPage.php Outdated
Comment thread library/Icinga/Web/Widget/LoginPage.php Outdated
Comment thread library/Icinga/Web/Widget/LoginPage.php
Comment thread public/css/icinga/login.less
@jrauh01 jrauh01 force-pushed the modernize-login branch from d14271f to 9feb539 Compare May 5, 2026 12:43
Copy link
Copy Markdown
Member

@lippserd lippserd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace LoginButtonForm::ON_SUCCESS with ON_SUBMIT
(AuthenticationController.php:131)

ON_SUCCESS is a deprecated alias for ON_SUBMIT in ipl-html. It still
works, but it is inconsistent with how the main login form's event is wired
on the lines just above it.


Verify or remove the $this->controls = new HtmlDocument() workaround
(AuthenticationController.php:144-145)

The comment says this suppresses the tab bar, but setTitle() does not add
any tabs to the controls object. Controls::isEmpty() would therefore
already return true without this replacement. If runtime testing confirms
the tab bar does not appear without it, remove the assignment. If it is
needed, fix the PHPStan type violation (Controls property assigned a
HtmlDocument) and update the comment to name the framework path that
populates controls on the login page. The type mismatch is a new PHPStan
error not yet in the baseline.


Add an Errors decorator to the username element
(LoginForm.php, assemble())

The password and rememberme elements both include an Errors decorator
so their validation messages render. username has only RenderElement and
ControlGroup, so a server-side required-field error on that field is
silently discarded. HTML5 validation can be disabled by the browser or
bypassed entirely with curl or similar tools, which means the server must
handle and display validation errors for every field. Add an Errors
decorator to username to match the other fields.


Add rel="noopener noreferrer" to the social link anchors
(LoginPage.php, assembleSocialLinks())

Both the Facebook and GitHub links open in a new tab (target="_blank")
without this attribute. Without it, the opened page can access
window.opener and navigate the login page away (tabnabbing). Add
'rel' => 'noopener noreferrer' to the attributes of both anchors.


Add a summary line to the LoginPage constructor docblock
(LoginPage.php:49)

The constructor docblock contains only @param tags. The PHPDoc convention
requires a summary line as the first line; for constructors it reads
Create a new LoginPage.


Add docblocks to onSuccess() and onRequest()
(LoginForm.php:135,197)

Both methods are non-trivial override points with side effects: onSuccess()
calls setRerenderLayout() and sets the redirect URL; onRequest() detects
external-only authentication and surfaces a warning. Neither has a docblock.


Simplify the error margin shorthand
(login.less:59)

margin: 1em 0 1em 0; has identical top and bottom values and identical
left and right values. Write it as margin: 1em 0;.

@jrauh01 jrauh01 force-pushed the modernize-login branch 2 times, most recently from 164fde5 to 4897909 Compare May 12, 2026 08:08
@jrauh01
Copy link
Copy Markdown
Contributor Author

jrauh01 commented May 12, 2026

@lippserd I have fully implemented your requested list. Additionally I rebased on main and changed the following:

LoginForm.php:

  • REDIRECT_URL constant made public
  • Summary line and @throws HttpBadRequestException explanation added to createRedirectUrl() docblock
  • Docblock added to __construct()
  • Superfluous inline comment before onError() call removed
  • ControlGroup decorator arrays replaced with typed HtmlTagDecorator instances
  • Unused Database trait removed
  • Placeholder exception message 'nope' replaced with 'Redirect to an external host is not allowed'

AuthenticationController.php:

  • $_ parameter in the ON_REQUEST callback typed as ServerRequestInterface

LoginPage.php:

  • Constructor property promotion applied
  • empty($loginButtons) check replaced with strict $loginButtons !== []

@jrauh01 jrauh01 requested a review from lippserd May 12, 2026 08:40
@jrauh01 jrauh01 force-pushed the modernize-login branch 2 times, most recently from b3bb326 to 28e5e85 Compare May 18, 2026 08:12
@jrauh01
Copy link
Copy Markdown
Contributor Author

jrauh01 commented May 18, 2026

Rebased on main and squashed the commits. Ready to merge now.

jrauh01 added 7 commits May 18, 2026 12:14
Replace the legacy Zend-based `LoginForm` with an ipl `CompatForm`-based form:

- Add `CsrfCounterMeasure` and `FormUid` traits
- Replace `init()`/`createElements()` with `__construct()´/`assemble()` using
  inline ipl decorator objects instead of Zend decorator arrays
- Replace `getRedirectUrl()` with `createRedirectUrl()` because
  `getRedirectUrl()` would override `ipl\Html\Form::getRedirectUrl()`
    - use `hasBeenAssembled()` instead of `$this->created`
    - use `str_contains()` instead of `strpos()`
- Rewrite `onSuccess()` for `CompatForm`:
    - change visibility from `public` to `protected`
    - void return type
    - use `Icinga::app()->getResponse()` instead of `$this->getResponse()`
    - use `addMessage()` instead of `addError()`
    - call `setRedirectUrl()` explicitly
Extract the login page structure (logo, footer, social links, decorative orbs)
from `login.phtml` into a reusable ipl-html widget. Extending `HtmlDocument`
rather than `BaseHtmlElement` lets it emit `#login` and the seven `.orb` sibling
divs without a wrapping root tag.

Both the login action and the upcoming 2FA challenge action can now share the
same visual scaffolding without duplicating markup and without using view
scripts anymore.
Convert `AuthenticationController` from the legacy Zend Controller to
`CompatController`, dropping `login.phtml` in favour of the new `LoginPage`
widget and `addContent()`. The view variable assignments are replaced by
`setTitle()` and `addContent(new LoginPage(...))`. Improve `httpBadRequest()`
message for external redirect attempts.

In `CompatController`, `$this->controls` is the tab bar area rendered
above the page content. When no tabs are added it still emits an empty
`<div class="controls">` wrapper. Setting `$this->view->compact = true`
suppresses that wrapper entirely, keeping the login page markup clean.

Handle the redirect on success in an `ON_SUBMIT` event handler. Delegate
the external-backend-only check to `LoginForm::onRequest()` via `ON_REQUEST`.
Use `$this->getServerRequest()` instead of `ServerRequest::fromGlobals()`.

Two structural fixes required by the changed DOM nesting:
- `login.less`: height `100%` -> `100vh` (`#login` is now inside `.content`
  which has no explicit height, so percentage inheritance breaks)
- `history.js`: `#layout > #login` -> `#layout #login` (direct-child selector
  breaks because `#login` is now a grandchild of `#layout` through `.content`)
Adjust login.less to match the HTML structure emitted by CompatForm:

- Add width: unset and margin on `.icinga-form` inside `.login-form` so the ipl
  form does not overflow its container and control groups have spacing
- Restyle `.errors` at the li level rather than the container level, giving each
  error message its own red box with margin between them
- Remove the padding from `.errors`/`.form-errors` that was set at the container
  level (now on each li)
- Change `.remember-me-box` align-items from `flex-start` to `center` so the
  toggle switch and label sit at the same baseline

Also fix a pre-existing typo in `login-orbs.less`: `#orb-notifactions` ->
`#orb-notifications`
Ensure `.errors` inside `.login-buttons` has no extra padding and that
the last error item retains a bottom margin, matching the layout used
for the primary login form errors.
Prevents the linked page from accessing `window.opener` and stops the
referring URL from being sent in the `Referer` header.
@jrauh01 jrauh01 force-pushed the modernize-login branch from 28e5e85 to 059073a Compare May 18, 2026 10:14
@lippserd lippserd merged commit 0b266cd into main May 20, 2026
13 checks passed
@lippserd lippserd deleted the modernize-login branch May 20, 2026 07:02
@lippserd lippserd added this to the 2.14.0 milestone May 20, 2026
@moreamazingnick
Copy link
Copy Markdown
Contributor

@lippserd Do you have a releasedate for icingaweb2 2.14.0, I think this might brake my oidc implementation
Best Regards
Nicolas

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LoginButtons via LoginButtonHook could use some margin

4 participants