mirror of
https://github.com/hashicorp/vault.git
synced 2026-05-28 04:10:44 -04:00
UI: Assume default auth path if custom path is not provided (#30300)
* add default path and update base tests * finish okta, oidc-jwt and saml tests * fix test name
This commit is contained in:
parent
4c36d90281
commit
b00d986be7
7 changed files with 210 additions and 21 deletions
|
|
@ -3,6 +3,8 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
~}}
|
||||
|
||||
{{! Auth methods with standard login flows use this template. To implement any custom logic, this template should be copied into a new hbs file for that method. }}
|
||||
|
||||
<form {{on "submit" this.onSubmit}} data-test-auth-form={{@authType}}>
|
||||
|
||||
{{yield to="namespace"}}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,13 @@ export default class AuthBase extends Component<Args> {
|
|||
data[key] = formData.get(key);
|
||||
}
|
||||
|
||||
// If path is not included in the submitted form data,
|
||||
// set it as the auth type which is the default path Vault expects.
|
||||
// The "token" auth method does not support custom login paths.
|
||||
if (this.args.authType !== 'token' && !Object.keys(data).includes('path')) {
|
||||
data['path'] = this.args.authType;
|
||||
}
|
||||
|
||||
this.login.unlinked().perform(data);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,25 @@ module('Integration | Component | auth | form | base', function (hooks) {
|
|||
hooks.beforeEach(function () {
|
||||
this.authType = 'github';
|
||||
this.expectedFields = ['token'];
|
||||
this.renderComponent = () => {
|
||||
this.expectedSubmit = {
|
||||
default: { path: 'github', token: 'mysupersecuretoken' },
|
||||
custom: { path: 'custom-github', token: 'mysupersecuretoken' },
|
||||
};
|
||||
this.renderComponent = ({ yieldBlock = false } = {}) => {
|
||||
if (yieldBlock) {
|
||||
return render(hbs`
|
||||
<Auth::Form::Github
|
||||
@authType={{this.authType}}
|
||||
@cluster={{this.cluster}}
|
||||
@onError={{this.onError}}
|
||||
@onSuccess={{this.onSuccess}}
|
||||
>
|
||||
<:advancedSettings>
|
||||
<label for="path">Mount path</label>
|
||||
<input data-test-input="path" id="path" name="path" type="text" />
|
||||
</:advancedSettings>
|
||||
</Auth::Form::Github>`);
|
||||
}
|
||||
return render(hbs`
|
||||
<Auth::Form::Github
|
||||
@authType={{this.authType}}
|
||||
|
|
@ -52,7 +70,25 @@ module('Integration | Component | auth | form | base', function (hooks) {
|
|||
hooks.beforeEach(function () {
|
||||
this.authType = 'ldap';
|
||||
this.expectedFields = ['username', 'password'];
|
||||
this.renderComponent = () => {
|
||||
this.expectedSubmit = {
|
||||
default: { password: 'password', path: 'ldap', username: 'matilda' },
|
||||
custom: { password: 'password', path: 'custom-ldap', username: 'matilda' },
|
||||
};
|
||||
this.renderComponent = ({ yieldBlock = false } = {}) => {
|
||||
if (yieldBlock) {
|
||||
return render(hbs`
|
||||
<Auth::Form::Ldap
|
||||
@authType={{this.authType}}
|
||||
@cluster={{this.cluster}}
|
||||
@onError={{this.onError}}
|
||||
@onSuccess={{this.onSuccess}}
|
||||
>
|
||||
<:advancedSettings>
|
||||
<label for="path">Mount path</label>
|
||||
<input data-test-input="path" id="path" name="path" type="text" />
|
||||
</:advancedSettings>
|
||||
</Auth::Form::Ldap>`);
|
||||
}
|
||||
return render(hbs`
|
||||
<Auth::Form::Ldap
|
||||
@authType={{this.authType}}
|
||||
|
|
@ -70,7 +106,25 @@ module('Integration | Component | auth | form | base', function (hooks) {
|
|||
hooks.beforeEach(function () {
|
||||
this.authType = 'radius';
|
||||
this.expectedFields = ['username', 'password'];
|
||||
this.renderComponent = () => {
|
||||
this.expectedSubmit = {
|
||||
default: { password: 'password', path: 'radius', username: 'matilda' },
|
||||
custom: { password: 'password', path: 'custom-radius', username: 'matilda' },
|
||||
};
|
||||
this.renderComponent = ({ yieldBlock = false } = {}) => {
|
||||
if (yieldBlock) {
|
||||
return render(hbs`
|
||||
<Auth::Form::Radius
|
||||
@authType={{this.authType}}
|
||||
@cluster={{this.cluster}}
|
||||
@onError={{this.onError}}
|
||||
@onSuccess={{this.onSuccess}}
|
||||
>
|
||||
<:advancedSettings>
|
||||
<label for="path">Mount path</label>
|
||||
<input data-test-input="path" id="path" name="path" type="text" />
|
||||
</:advancedSettings>
|
||||
</Auth::Form::Radius>`);
|
||||
}
|
||||
return render(hbs`
|
||||
<Auth::Form::Radius
|
||||
@authType={{this.authType}}
|
||||
|
|
@ -88,7 +142,26 @@ module('Integration | Component | auth | form | base', function (hooks) {
|
|||
hooks.beforeEach(function () {
|
||||
this.authType = 'token';
|
||||
this.expectedFields = ['token'];
|
||||
this.renderComponent = () => {
|
||||
this.expectedSubmit = {
|
||||
default: { token: 'mytoken' },
|
||||
// token doesn't support custom paths, so just test yielding functionality
|
||||
custom: { token: 'mytoken', yield: 'yield-token' },
|
||||
};
|
||||
this.renderComponent = ({ yieldBlock = false } = {}) => {
|
||||
if (yieldBlock) {
|
||||
return render(hbs`
|
||||
<Auth::Form::Token
|
||||
@authType={{this.authType}}
|
||||
@cluster={{this.cluster}}
|
||||
@onError={{this.onError}}
|
||||
@onSuccess={{this.onSuccess}}
|
||||
>
|
||||
<:advancedSettings>
|
||||
<label for="yield">Yielded input</label>
|
||||
<input data-test-input="yield" id="yield" name="yield" type="text" />
|
||||
</:advancedSettings>
|
||||
</Auth::Form::Token>`);
|
||||
}
|
||||
return render(hbs`
|
||||
<Auth::Form::Token
|
||||
@authType={{this.authType}}
|
||||
|
|
@ -106,7 +179,25 @@ module('Integration | Component | auth | form | base', function (hooks) {
|
|||
hooks.beforeEach(function () {
|
||||
this.authType = 'userpass';
|
||||
this.expectedFields = ['username', 'password'];
|
||||
this.renderComponent = () => {
|
||||
this.expectedSubmit = {
|
||||
default: { password: 'password', path: 'userpass', username: 'matilda' },
|
||||
custom: { password: 'password', path: 'custom-userpass', username: 'matilda' },
|
||||
};
|
||||
this.renderComponent = ({ yieldBlock = false } = {}) => {
|
||||
if (yieldBlock) {
|
||||
return render(hbs`
|
||||
<Auth::Form::Userpass
|
||||
@authType={{this.authType}}
|
||||
@cluster={{this.cluster}}
|
||||
@onError={{this.onError}}
|
||||
@onSuccess={{this.onSuccess}}
|
||||
>
|
||||
<:advancedSettings>
|
||||
<label for="path">Mount path</label>
|
||||
<input data-test-input="path" id="path" name="path" type="text" />
|
||||
</:advancedSettings>
|
||||
</Auth::Form::Userpass>`);
|
||||
}
|
||||
return render(hbs`
|
||||
<Auth::Form::Userpass
|
||||
@authType={{this.authType}}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,21 @@ module('Integration | Component | auth | form | oidc-jwt', function (hooks) {
|
|||
this.cluster = { id: 1 };
|
||||
this.onError = sinon.spy();
|
||||
this.onSuccess = sinon.spy();
|
||||
this.renderComponent = () => {
|
||||
this.renderComponent = ({ yieldBlock = false } = {}) => {
|
||||
if (yieldBlock) {
|
||||
return render(hbs`
|
||||
<Auth::Form::OidcJwt
|
||||
@authType={{this.authType}}
|
||||
@cluster={{this.cluster}}
|
||||
@onError={{this.onError}}
|
||||
@onSuccess={{this.onSuccess}}
|
||||
>
|
||||
<:advancedSettings>
|
||||
<label for="path">Mount path</label>
|
||||
<input data-test-input="path" id="path" name="path" type="text" />
|
||||
</:advancedSettings>
|
||||
</Auth::Form::OidcJwt>`);
|
||||
}
|
||||
return render(hbs`
|
||||
<Auth::Form::OidcJwt
|
||||
@authType={{this.authType}}
|
||||
|
|
@ -46,6 +60,10 @@ module('Integration | Component | auth | form | oidc-jwt', function (hooks) {
|
|||
module('oidc', function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
this.authType = 'oidc';
|
||||
this.expectedSubmit = {
|
||||
default: { path: 'oidc', role: 'some-dev' },
|
||||
custom: { path: 'custom-oidc', role: 'some-dev' },
|
||||
};
|
||||
});
|
||||
|
||||
testHelper(test);
|
||||
|
|
@ -54,6 +72,10 @@ module('Integration | Component | auth | form | oidc-jwt', function (hooks) {
|
|||
module('jwt', function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
this.authType = 'jwt';
|
||||
this.expectedSubmit = {
|
||||
default: { path: 'jwt', role: 'some-dev' },
|
||||
custom: { path: 'custom-jwt', role: 'some-dev' },
|
||||
};
|
||||
});
|
||||
|
||||
testHelper(test);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,25 @@ module('Integration | Component | auth | form | okta', function (hooks) {
|
|||
this.cluster = { id: 1 };
|
||||
this.onError = sinon.spy();
|
||||
this.onSuccess = sinon.spy();
|
||||
this.renderComponent = () => {
|
||||
this.expectedSubmit = {
|
||||
default: { path: 'okta', username: 'matilda', password: 'password' },
|
||||
custom: { path: 'custom-okta', username: 'matilda', password: 'password' },
|
||||
};
|
||||
this.renderComponent = ({ yieldBlock = false } = {}) => {
|
||||
if (yieldBlock) {
|
||||
return render(hbs`
|
||||
<Auth::Form::Okta
|
||||
@authType={{this.authType}}
|
||||
@cluster={{this.cluster}}
|
||||
@onError={{this.onError}}
|
||||
@onSuccess={{this.onSuccess}}
|
||||
>
|
||||
<:advancedSettings>
|
||||
<label for="path">Mount path</label>
|
||||
<input data-test-input="path" id="path" name="path" type="text" />
|
||||
</:advancedSettings>
|
||||
</Auth::Form::Okta>`);
|
||||
}
|
||||
return render(hbs`
|
||||
<Auth::Form::Okta
|
||||
@authType={{this.authType}}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,25 @@ module('Integration | Component | auth | form | saml', function (hooks) {
|
|||
this.cluster = { id: 1 };
|
||||
this.onError = sinon.spy();
|
||||
this.onSuccess = sinon.spy();
|
||||
this.renderComponent = () => {
|
||||
this.expectedSubmit = {
|
||||
default: { path: 'saml', role: 'some-dev' },
|
||||
custom: { path: 'custom-saml', role: 'some-dev' },
|
||||
};
|
||||
this.renderComponent = ({ yieldBlock = false } = {}) => {
|
||||
if (yieldBlock) {
|
||||
return render(hbs`
|
||||
<Auth::Form::Saml
|
||||
@authType={{this.authType}}
|
||||
@cluster={{this.cluster}}
|
||||
@onError={{this.onError}}
|
||||
@onSuccess={{this.onSuccess}}
|
||||
>
|
||||
<:advancedSettings>
|
||||
<label for="path">Mount path</label>
|
||||
<input data-test-input="path" id="path" name="path" type="text" />
|
||||
</:advancedSettings>
|
||||
</Auth::Form::Saml>`);
|
||||
}
|
||||
return render(hbs`
|
||||
<Auth::Form::Saml
|
||||
@authType={{this.authType}}
|
||||
|
|
|
|||
|
|
@ -24,19 +24,6 @@ export default (test) => {
|
|||
});
|
||||
});
|
||||
|
||||
test('it submits expected form data', async function (assert) {
|
||||
await this.renderComponent();
|
||||
const { options } = AUTH_METHOD_MAP.find((m) => m.authType === this.authType);
|
||||
const { loginData } = options;
|
||||
|
||||
for (const [field, value] of Object.entries(loginData)) {
|
||||
await fillIn(GENERAL.inputByAttr(field), value);
|
||||
}
|
||||
await click(AUTH_FORM.login);
|
||||
const [actual] = this.authenticateStub.lastCall.args;
|
||||
assert.propEqual(actual.data, loginData, 'auth service "authenticate" method is called with form data');
|
||||
});
|
||||
|
||||
test('it fires onError callback', async function (assert) {
|
||||
this.authenticateStub.throws('permission denied');
|
||||
await this.renderComponent();
|
||||
|
|
@ -58,4 +45,48 @@ export default (test) => {
|
|||
const [actual] = this.onSuccess.lastCall.args;
|
||||
assert.strictEqual(actual, 'success!', 'it calls onSuccess');
|
||||
});
|
||||
|
||||
test('it submits form data with defaults', async function (assert) {
|
||||
await this.renderComponent();
|
||||
const { options } = AUTH_METHOD_MAP.find((m) => m.authType === this.authType);
|
||||
const { loginData } = options;
|
||||
|
||||
for (const [field, value] of Object.entries(loginData)) {
|
||||
await fillIn(GENERAL.inputByAttr(field), value);
|
||||
}
|
||||
await click(AUTH_FORM.login);
|
||||
const [actual] = this.authenticateStub.lastCall.args;
|
||||
assert.propEqual(
|
||||
actual.data,
|
||||
this.expectedSubmit.default,
|
||||
'auth service "authenticate" method is called with form data'
|
||||
);
|
||||
});
|
||||
|
||||
// not for testing real-world submit, that happens in acceptance tests
|
||||
// component here just yields <:advancedSettings> to test form submits data from yielded inputs
|
||||
test('it submits form data from yielded inputs', async function (assert) {
|
||||
await this.renderComponent({ yieldBlock: true });
|
||||
const { options } = AUTH_METHOD_MAP.find((m) => m.authType === this.authType);
|
||||
const { loginData } = options;
|
||||
|
||||
for (const [field, value] of Object.entries(loginData)) {
|
||||
await fillIn(GENERAL.inputByAttr(field), value);
|
||||
}
|
||||
|
||||
if (this.authType === 'token') {
|
||||
// token doesn't support custom paths, so just test yielding functionality
|
||||
await fillIn(GENERAL.inputByAttr('yield'), `yield-${this.authType}`);
|
||||
} else {
|
||||
await fillIn(GENERAL.inputByAttr('path'), `custom-${this.authType}`);
|
||||
}
|
||||
|
||||
await click(AUTH_FORM.login);
|
||||
const [actual] = this.authenticateStub.lastCall.args;
|
||||
assert.propEqual(
|
||||
actual.data,
|
||||
this.expectedSubmit.custom,
|
||||
'auth service "authenticate" method is called with yielded form data'
|
||||
);
|
||||
});
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue