mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-02 03:27:54 +00:00
backport UI: show token expiring warning (#23762)
Co-authored-by: Chelsea Shaw <82459713+hashishaw@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
cd1d91dbf3
commit
3e8b670f18
3
changelog/23143.txt
Normal file
3
changelog/23143.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
```release-note:improvement
|
||||||
|
ui: Surface warning banner if UI has stopped auto-refreshing token
|
||||||
|
```
|
||||||
55
ui/app/components/token-expire-warning.hbs
Normal file
55
ui/app/components/token-expire-warning.hbs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
{{!
|
||||||
|
Copyright (c) HashiCorp, Inc.
|
||||||
|
SPDX-License-Identifier: BUSL-1.1
|
||||||
|
~}}
|
||||||
|
|
||||||
|
{{#if (and this.showWarning (is-after (now interval=1000) @expirationDate))}}
|
||||||
|
<Hds::Alert @type="page" @color="critical" data-test-token-expired-banner as |A|>
|
||||||
|
<A.Title>Error</A.Title>
|
||||||
|
<A.Description>
|
||||||
|
Your auth token expired on
|
||||||
|
{{date-format @expirationDate "MMMM do yyyy, h:mm:ss a"}}. You will need to re-authenticate.
|
||||||
|
</A.Description>
|
||||||
|
<A.Link::Standalone @icon="arrow-right" @iconPosition="trailing" @text="Reauthenticate" @route="vault.cluster.logout" />
|
||||||
|
</Hds::Alert>
|
||||||
|
{{else}}
|
||||||
|
<section class="section">
|
||||||
|
<div class="container is-widescreen">
|
||||||
|
{{#if (and this.showWarning @allowingExpiration this.canDismiss)}}
|
||||||
|
<Hds::Alert
|
||||||
|
@type="inline"
|
||||||
|
@color="warning"
|
||||||
|
@onDismiss={{fn (mut this.canDismiss) false}}
|
||||||
|
class="has-top-margin-s"
|
||||||
|
data-test-token-expiring-banner
|
||||||
|
as |A|
|
||||||
|
>
|
||||||
|
<A.Title>Session will expire</A.Title>
|
||||||
|
<A.Description>
|
||||||
|
We've stopped auto-renewing your token due to inactivity. It will expire in
|
||||||
|
{{date-from-now @expirationDate}}
|
||||||
|
on
|
||||||
|
{{date-format @expirationDate "MMMM do yyyy, h:mm:ss a O"}}.
|
||||||
|
</A.Description>
|
||||||
|
<A.Button
|
||||||
|
@text="Renew token"
|
||||||
|
@color="secondary"
|
||||||
|
@icon={{if this.renewToken.isRunning "loading" "reload"}}
|
||||||
|
@iconPosition="trailing"
|
||||||
|
disabled={{this.renewToken.isRunning}}
|
||||||
|
{{on "click" (perform this.renewToken)}}
|
||||||
|
data-test-renew-token-button
|
||||||
|
/>
|
||||||
|
<A.Link::Standalone
|
||||||
|
@icon="arrow-right"
|
||||||
|
@iconPosition="trailing"
|
||||||
|
@color="secondary"
|
||||||
|
@text="Log out"
|
||||||
|
@route="vault.cluster.logout"
|
||||||
|
/>
|
||||||
|
</Hds::Alert>
|
||||||
|
{{/if}}
|
||||||
|
{{yield}}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{{/if}}
|
||||||
@@ -5,9 +5,36 @@
|
|||||||
|
|
||||||
import Component from '@glimmer/component';
|
import Component from '@glimmer/component';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
|
import { tracked } from '@glimmer/tracking';
|
||||||
|
import { later } from '@ember/runloop';
|
||||||
|
import { task } from 'ember-concurrency';
|
||||||
|
|
||||||
export default class TokenExpireWarning extends Component {
|
export default class TokenExpireWarning extends Component {
|
||||||
|
@service auth;
|
||||||
@service router;
|
@service router;
|
||||||
|
@tracked canDismiss = true;
|
||||||
|
|
||||||
|
handleRenew() {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
later(() => {
|
||||||
|
this.auth
|
||||||
|
.renew()
|
||||||
|
.then(() => {
|
||||||
|
// This renewal was triggered by an explicit user action,
|
||||||
|
// so this will reset the time inactive calculation
|
||||||
|
this.auth.setLastFetch(Date.now());
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}, 200);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@task
|
||||||
|
*renewToken() {
|
||||||
|
yield this.handleRenew();
|
||||||
|
}
|
||||||
|
|
||||||
get showWarning() {
|
get showWarning() {
|
||||||
const currentRoute = this.router.currentRouteName;
|
const currentRoute = this.router.currentRouteName;
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
{{!
|
|
||||||
Copyright (c) HashiCorp, Inc.
|
|
||||||
SPDX-License-Identifier: BUSL-1.1
|
|
||||||
~}}
|
|
||||||
|
|
||||||
{{#if (and this.showWarning (is-after (now interval=1000) @expirationDate))}}
|
|
||||||
<Hds::Alert @type="page" @color="critical" as |A|>
|
|
||||||
<A.Title>Error</A.Title>
|
|
||||||
<A.Description>
|
|
||||||
Your auth token expired on
|
|
||||||
{{date-format @expirationDate "MMMM do yyyy, h:mm:ss a"}}. You will need to re-authenticate.
|
|
||||||
</A.Description>
|
|
||||||
<A.Link::Standalone @icon="arrow-right" @iconPosition="trailing" @text="Reauthenticate" @route="vault.cluster.logout" />
|
|
||||||
</Hds::Alert>
|
|
||||||
{{else}}
|
|
||||||
<section class="section">
|
|
||||||
<div class="container is-widescreen">
|
|
||||||
{{yield}}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
{{/if}}
|
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if this.auth.isActiveSession}}
|
{{#if this.auth.isActiveSession}}
|
||||||
<TokenExpireWarning @expirationDate={{this.auth.tokenExpirationDate}}>
|
<TokenExpireWarning @expirationDate={{this.auth.tokenExpirationDate}} @allowingExpiration={{this.auth.allowExpiration}}>
|
||||||
{{outlet}}
|
{{outlet}}
|
||||||
</TokenExpireWarning>
|
</TokenExpireWarning>
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ module('Integration | Component | token-expire-warning', function (hooks) {
|
|||||||
|
|
||||||
await render(hbs`<TokenExpireWarning @expirationDate={{this.expirationDate}}/>`);
|
await render(hbs`<TokenExpireWarning @expirationDate={{this.expirationDate}}/>`);
|
||||||
await waitUntil(() => find('#modal-overlays'));
|
await waitUntil(() => find('#modal-overlays'));
|
||||||
assert.dom().includesText('Your auth token expired on');
|
assert.dom('[data-test-token-expired-banner]').includesText('Your auth token expired on');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it does not render a warning when the token is not expired', async function (assert) {
|
test('it does not render a warning when the token is not expired', async function (assert) {
|
||||||
@@ -30,8 +30,45 @@ module('Integration | Component | token-expire-warning', function (hooks) {
|
|||||||
<p>Do not worry, your token has not expired.</p>
|
<p>Do not worry, your token has not expired.</p>
|
||||||
</TokenExpireWarning>
|
</TokenExpireWarning>
|
||||||
`);
|
`);
|
||||||
await waitUntil(() => find('#modal-overlays'));
|
|
||||||
assert.dom().doesNotIncludeText('Your auth token expired on');
|
assert.dom().doesNotIncludeText('Your auth token expired on');
|
||||||
assert.dom().includesText('Do not worry');
|
assert.dom().includesText('Do not worry');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('it renders a warning when the token is no longer renewing', async function (assert) {
|
||||||
|
const expirationDate = addMinutes(Date.now(), 3);
|
||||||
|
this.set('expirationDate', expirationDate);
|
||||||
|
this.set('allowingExpiration', false);
|
||||||
|
|
||||||
|
await render(
|
||||||
|
hbs`
|
||||||
|
<TokenExpireWarning @expirationDate={{this.expirationDate}} @allowingExpiration={{this.allowingExpiration}}>
|
||||||
|
<p data-test-content>This is the content</p>
|
||||||
|
</TokenExpireWarning>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
assert.dom('[data-test-token-expired-banner]').doesNotExist('Does not show token expired banner');
|
||||||
|
assert.dom('[data-test-token-expiring-banner]').doesNotExist('Does not show token expiring banner');
|
||||||
|
assert.dom('[data-test-content]').hasText('This is the content');
|
||||||
|
|
||||||
|
await this.set('allowingExpiration', true);
|
||||||
|
assert.dom('[data-test-token-expired-banner]').doesNotExist('Does not show token expired banner');
|
||||||
|
assert.dom('[data-test-token-expiring-banner]').exists('Shows token expiring banner');
|
||||||
|
assert.dom('[data-test-content]').hasText('This is the content');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Does not render a warning if no expiration date', async function (assert) {
|
||||||
|
this.set('expirationDate', null);
|
||||||
|
this.set('allowingExpiration', true);
|
||||||
|
|
||||||
|
await render(
|
||||||
|
hbs`
|
||||||
|
<TokenExpireWarning @expirationDate={{this.expirationDate}} @allowingExpiration={{this.allowingExpiration}}>
|
||||||
|
<p data-test-content>This is the content</p>
|
||||||
|
</TokenExpireWarning>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
assert.dom('[data-test-token-expired-banner]').doesNotExist('Does not show token expired banner');
|
||||||
|
assert.dom('[data-test-token-expiring-banner]').doesNotExist('Does not show token expiring banner');
|
||||||
|
assert.dom('[data-test-content]').hasText('This is the content');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user