UI: Prioritize direct link when multiple mounts are visible (#12142) (#12156)

* override auth form with direct link

* add changelog

Co-authored-by: claire bontempo <68122737+hellobontempo@users.noreply.github.com>
This commit is contained in:
Vault Automation 2026-02-03 17:50:14 -05:00 committed by GitHub
parent caf642b7d2
commit 4e78a0bfc5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 30 additions and 16 deletions

3
changelog/_12142.txt Normal file
View file

@ -0,0 +1,3 @@
```release-note:bug
ui: Fixes login form so `?with=<path>` query param correctly displays only the specified mount when multiple mounts of the same auth type are configured with `listing_visibility="unauth"`
```

View file

@ -58,17 +58,6 @@ import type { Task } from 'ember-concurrency';
* 🔀 Multiple visible mounts:
* Path dropdown is shown.
*
* @example
* <Auth::Page
* @cluster={{this.model.clusterModel}}
* @directLinkData={{this.model.directLinkData}}
* @loginSettings={{this.model.loginSettings}}
* @namespaceQueryParam={{this.namespaceQueryParam}}
* @oidcProviderQueryParam={{this.oidcProvider}}
* @loginAndTransition={{this.loginAndTransition}}
* @onNamespaceUpdate={{perform this.updateNamespace}}
* @visibleAuthMounts={{this.model.visibleAuthMounts}}
* />
*
* @param {object} cluster - the ember data cluster model. contains information such as cluster id, name and boolean for if the cluster is in standby
* @param {object} directLinkData - mount data built from the "with" query param. If param is a mount path and maps to a visible mount, the login form defaults to this mount. Otherwise the form preselects the passed auth type.
@ -161,10 +150,14 @@ export default class AuthPage extends Component<Args> {
get directLinkViews() {
const { directLinkData } = this.args;
// If "path" key exists we know the "with" query param references a mount with listing_visibility="unauth"
// Treat it as a preferred method and hide all other tabs.
// If "path" key exists then the "with" query param references a specific mount with listing_visibility="unauth".
// Show only this mount and hide any others for this auth type as well as any tabs for different auth types.
if (directLinkData?.path) {
const tabData = this.filterVisibleMountsByType([directLinkData.type]);
const mounts = this.mountsByType(directLinkData.type);
const selectedMount = mounts?.find((m) => m.path === directLinkData?.path);
const tabData: UnauthMountsByType = {
[directLinkData.type]: selectedMount ? [selectedMount] : null,
};
const defaultView = this.constructViews(FormView.TABS, tabData);
const alternateView = this.constructViews(FormView.DROPDOWN, null);
@ -263,11 +256,16 @@ export default class AuthPage extends Component<Args> {
const tabs: UnauthMountsByType = {};
for (const type of authTypes) {
// adds visible mounts for each type, if they exist
tabs[type] = this.visibleMountsByType?.[type] || null;
tabs[type] = this.mountsByType(type);
}
return tabs;
}
private mountsByType(type: string) {
// Return null and not an empty array to distinguish between "dropdown mode" and "tabs with no mounts" in downstream components
return this.visibleMountsByType?.[type] || null;
}
private constructViews(view: FormView, tabData: UnauthMountsByType | null) {
return { view, tabData };
}

View file

@ -102,7 +102,6 @@ export default class AuthRoute extends Route {
const { default_auth_type, backup_auth_types } = response.data;
return {
defaultType: default_auth_type,
// TODO WIP backend PR consistently return empty array when no backup_auth_types
backupTypes: backup_auth_types?.length ? backup_auth_types : null,
};
}

View file

@ -132,6 +132,20 @@ module('Integration | Component | auth | page | listing visibility', function (h
assert.dom(GENERAL.backButton).doesNotExist();
});
test('it treats direct link as only mount when multiple mounts are tuned with listing_visibility="unauth"', async function (assert) {
this.directLinkData = { path: 'userpass2/', type: 'userpass' };
await this.renderComponent();
assert.dom(AUTH_FORM.authForm('userpass')).exists;
assert.dom(AUTH_FORM.tabBtn('userpass')).hasText('Userpass', 'it renders tab for type');
assert.dom(AUTH_FORM.tabs).exists({ count: 1 }, 'only one tab renders');
assert.dom(GENERAL.inputByAttr('path')).hasAttribute('type', 'hidden');
assert.dom(GENERAL.inputByAttr('path')).hasValue('userpass2/');
assert.dom(GENERAL.button('Sign in with other methods')).exists('"Sign in with other methods" renders');
assert.dom(GENERAL.selectByAttr('auth type')).doesNotExist();
assert.dom(AUTH_FORM.advancedSettings).doesNotExist();
assert.dom(GENERAL.backButton).doesNotExist();
});
test('it prioritizes auth type from canceled mfa instead of direct link for path', async function (assert) {
assert.expect(1);
this.directLinkData = this.directLinkIsVisibleMount; // type is "oidc"