mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-10 02:46:38 +00:00
Add UI for tokens (#1656)
* Add UI for tokens * Update strings * Update text * Update text
This commit is contained in:
parent
494e3dc62c
commit
34567d451f
127
src/panels/profile/ha-long-lived-access-tokens-card.js
Normal file
127
src/panels/profile/ha-long-lived-access-tokens-card.js
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import '@polymer/paper-button/paper-button.js';
|
||||||
|
|
||||||
|
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
|
||||||
|
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
|
||||||
|
import EventsMixin from '../../mixins/events-mixin.js';
|
||||||
|
import LocalizeMixin from '../../mixins/localize-mixin.js';
|
||||||
|
import formatDateTime from '../../common/datetime/format_date_time.js';
|
||||||
|
|
||||||
|
import '../../resources/ha-style.js';
|
||||||
|
|
||||||
|
import './ha-settings-row.js';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @appliesMixin EventsMixin
|
||||||
|
* @appliesMixin LocalizeMixin
|
||||||
|
*/
|
||||||
|
class HaLongLivedTokens extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||||
|
static get template() {
|
||||||
|
return html`
|
||||||
|
<style include="ha-style">
|
||||||
|
paper-card {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.card-content {
|
||||||
|
margin: -1em 0;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
paper-icon-button {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<paper-card heading="[[localize('ui.panel.profile.long_lived_access_tokens.header')]]">
|
||||||
|
<div class="card-content">
|
||||||
|
<p>
|
||||||
|
[[localize('ui.panel.profile.long_lived_access_tokens.description')]]
|
||||||
|
<a href='https://developers.home-assistant.io/docs/en/auth_api.html#making-authenticated-requests' target='_blank'>
|
||||||
|
[[localize('ui.panel.profile.long_lived_access_tokens.learn_auth_requests')]]
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<template is='dom-if' if='[[!_tokens.length]]'>
|
||||||
|
<p>[[localize('ui.panel.profile.long_lived_access_tokens.empty_state')]]</p>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<template is='dom-repeat' items='[[_tokens]]'>
|
||||||
|
<ha-settings-row>
|
||||||
|
<span slot='heading'>[[item.client_name]]</span>
|
||||||
|
<span slot='description'>[[_formatCreatedAt(item.created_at)]]</span>
|
||||||
|
<paper-icon-button icon="hass:delete" on-click='_handleDelete'></paper-icon-button>
|
||||||
|
</ha-settings-row>
|
||||||
|
</template>
|
||||||
|
<div class='card-actions'>
|
||||||
|
<paper-button on-click='_handleCreate'>
|
||||||
|
[[localize('ui.panel.profile.long_lived_access_tokens.create')]]
|
||||||
|
</paper-button>
|
||||||
|
</div>
|
||||||
|
</paper-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
hass: Object,
|
||||||
|
refreshTokens: Array,
|
||||||
|
_tokens: {
|
||||||
|
type: Array,
|
||||||
|
computed: '_computeTokens(refreshTokens)'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_computeTokens(refreshTokens) {
|
||||||
|
return refreshTokens.filter(tkn => tkn.type === 'long_lived_access_token').reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
_formatTitle(name) {
|
||||||
|
return this.localize(
|
||||||
|
'ui.panel.profile.long_lived_access_tokens.token_title',
|
||||||
|
'name', name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_formatCreatedAt(created) {
|
||||||
|
return this.localize(
|
||||||
|
'ui.panel.profile.long_lived_access_tokens.created_at',
|
||||||
|
'date', formatDateTime(new Date(created))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _handleCreate() {
|
||||||
|
const name = prompt(this.localize('ui.panel.profile.long_lived_access_tokens.prompt_name'));
|
||||||
|
if (!name) return;
|
||||||
|
try {
|
||||||
|
const token = await this.hass.callWS({
|
||||||
|
type: 'auth/long_lived_access_token',
|
||||||
|
lifespan: 3650,
|
||||||
|
client_name: name,
|
||||||
|
});
|
||||||
|
prompt(this.localize('ui.panel.profile.long_lived_access_tokens.prompt_copy_token'), token);
|
||||||
|
this.fire('hass-refresh-tokens');
|
||||||
|
} catch (err) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
console.error(err);
|
||||||
|
alert(this.localize('ui.panel.profile.long_lived_access_tokens.create_failed'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async _handleDelete(ev) {
|
||||||
|
if (!confirm(this.localize('ui.panel.profile.long_lived_access_tokens.confirm_delete', 'name', ev.model.item.client_name))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await this.hass.callWS({
|
||||||
|
type: 'auth/delete_refresh_token',
|
||||||
|
refresh_token_id: ev.model.item.id,
|
||||||
|
});
|
||||||
|
this.fire('hass-refresh-tokens');
|
||||||
|
} catch (err) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
console.error(err);
|
||||||
|
alert(this.localize('ui.panel.profile.long_lived_access_tokens.delete_failed'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('ha-long-lived-access-tokens-card', HaLongLivedTokens);
|
@ -15,6 +15,9 @@ import EventsMixin from '../../mixins/events-mixin.js';
|
|||||||
|
|
||||||
import './ha-change-password-card.js';
|
import './ha-change-password-card.js';
|
||||||
import './ha-mfa-modules-card.js';
|
import './ha-mfa-modules-card.js';
|
||||||
|
import './ha-refresh-tokens-card.js';
|
||||||
|
import './ha-long-lived-access-tokens-card.js';
|
||||||
|
|
||||||
import './ha-pick-language-row.js';
|
import './ha-pick-language-row.js';
|
||||||
import './ha-pick-theme-row.js';
|
import './ha-pick-theme-row.js';
|
||||||
import './ha-push-notifications-row.js';
|
import './ha-push-notifications-row.js';
|
||||||
@ -84,7 +87,22 @@ class HaPanelProfile extends EventsMixin(PolymerElement) {
|
|||||||
<ha-change-password-card hass="[[hass]]"></ha-change-password-card>
|
<ha-change-password-card hass="[[hass]]"></ha-change-password-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<ha-mfa-modules-card hass='[[hass]]' mfa-modules='[[hass.user.mfa_modules]]'></ha-mfa-modules-card>
|
<ha-mfa-modules-card
|
||||||
|
hass='[[hass]]'
|
||||||
|
mfa-modules='[[hass.user.mfa_modules]]'
|
||||||
|
></ha-mfa-modules-card>
|
||||||
|
|
||||||
|
<ha-refresh-tokens-card
|
||||||
|
hass='[[hass]]'
|
||||||
|
refresh-tokens='[[_refreshTokens]]'
|
||||||
|
on-hass-refresh-tokens='_refreshRefreshTokens'
|
||||||
|
></ha-refresh-tokens-card>
|
||||||
|
|
||||||
|
<ha-long-lived-access-tokens-card
|
||||||
|
hass='[[hass]]'
|
||||||
|
refresh-tokens='[[_refreshTokens]]'
|
||||||
|
on-hass-refresh-tokens='_refreshRefreshTokens'
|
||||||
|
></ha-long-lived-access-tokens-card>
|
||||||
</div>
|
</div>
|
||||||
</app-header-layout>
|
</app-header-layout>
|
||||||
`;
|
`;
|
||||||
@ -95,9 +113,21 @@ class HaPanelProfile extends EventsMixin(PolymerElement) {
|
|||||||
hass: Object,
|
hass: Object,
|
||||||
narrow: Boolean,
|
narrow: Boolean,
|
||||||
showMenu: Boolean,
|
showMenu: Boolean,
|
||||||
|
_refreshTokens: Array,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
this._refreshRefreshTokens();
|
||||||
|
}
|
||||||
|
|
||||||
|
async _refreshRefreshTokens() {
|
||||||
|
this._refreshTokens = await this.hass.callWS({
|
||||||
|
type: 'auth/refresh_tokens'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_handleLogOut() {
|
_handleLogOut() {
|
||||||
this.fire('hass-logout');
|
this.fire('hass-logout');
|
||||||
}
|
}
|
||||||
|
82
src/panels/profile/ha-refresh-tokens-card.js
Normal file
82
src/panels/profile/ha-refresh-tokens-card.js
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import '@polymer/paper-icon-button/paper-icon-button.js';
|
||||||
|
|
||||||
|
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
|
||||||
|
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
|
||||||
|
import EventsMixin from '../../mixins/events-mixin.js';
|
||||||
|
import LocalizeMixin from '../../mixins/localize-mixin.js';
|
||||||
|
import formatDateTime from '../../common/datetime/format_date_time.js';
|
||||||
|
|
||||||
|
import './ha-settings-row.js';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @appliesMixin EventsMixin
|
||||||
|
* @appliesMixin LocalizeMixin
|
||||||
|
*/
|
||||||
|
class HaRefreshTokens extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||||
|
static get template() {
|
||||||
|
return html`
|
||||||
|
<style>
|
||||||
|
paper-card {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
paper-icon-button {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<paper-card heading="[[localize('ui.panel.profile.refresh_tokens.header')]]">
|
||||||
|
<div class="card-content">[[localize('ui.panel.profile.refresh_tokens.description')]]</div>
|
||||||
|
<template is='dom-repeat' items='[[_computeTokens(refreshTokens)]]'>
|
||||||
|
<ha-settings-row>
|
||||||
|
<span slot='heading'>[[_formatTitle(item.client_id)]]</span>
|
||||||
|
<span slot='description'>[[_formatCreatedAt(item.created_at)]]</span>
|
||||||
|
<paper-icon-button icon="hass:delete" on-click='_handleDelete'></paper-icon-button>
|
||||||
|
</ha-settings-row>
|
||||||
|
</template>
|
||||||
|
</paper-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
hass: Object,
|
||||||
|
refreshTokens: Array,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_computeTokens(refreshTokens) {
|
||||||
|
return refreshTokens.filter(tkn => tkn.type === 'normal').reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
_formatTitle(clientId) {
|
||||||
|
return this.localize(
|
||||||
|
'ui.panel.profile.refresh_tokens.token_title',
|
||||||
|
'clientId', clientId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_formatCreatedAt(created) {
|
||||||
|
return this.localize(
|
||||||
|
'ui.panel.profile.refresh_tokens.created_at',
|
||||||
|
'date', formatDateTime(new Date(created))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _handleDelete(ev) {
|
||||||
|
if (!confirm(this.localize('ui.panel.profile.refresh_tokens.confirm_delete', 'name', ev.model.item.client_id))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await this.hass.callWS({
|
||||||
|
type: 'auth/delete_refresh_token',
|
||||||
|
refresh_token_id: ev.model.item.id,
|
||||||
|
});
|
||||||
|
this.fire('hass-refresh-tokens');
|
||||||
|
} catch (err) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
console.error(err);
|
||||||
|
alert(this.localize('ui.panel.profile.refresh_tokens.delete_failed'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('ha-refresh-tokens-card', HaRefreshTokens);
|
@ -746,6 +746,27 @@
|
|||||||
"error_no_theme": "No themes available.",
|
"error_no_theme": "No themes available.",
|
||||||
"link_promo": "Learn about themes",
|
"link_promo": "Learn about themes",
|
||||||
"dropdown_label": "Theme"
|
"dropdown_label": "Theme"
|
||||||
|
},
|
||||||
|
"refresh_tokens": {
|
||||||
|
"header": "Refresh Tokens",
|
||||||
|
"description": "Each refresh token represents a login session. Refresh tokens will be automatically removed when you click log out. Below a list of refresh tokens that are currently active for your account.",
|
||||||
|
"token_title": "Refresh token for {clientId}",
|
||||||
|
"created_at": "Created at {date}",
|
||||||
|
"confirm_delete": "Are you sure you want to delete the refresh token for {name}?",
|
||||||
|
"delete_failed": "Failed to delete the refresh token."
|
||||||
|
},
|
||||||
|
"long_lived_access_tokens": {
|
||||||
|
"header": "Long-Lived Access Tokens",
|
||||||
|
"description": "Create long-lived access tokens to allow your scripts to interact with your Home Assistant instance. Each token will be valid for 10 years from creation. The following long-lived access tokens are currently active.",
|
||||||
|
"learn_auth_requests": "Learn how to make authenticated requests.",
|
||||||
|
"created_at": "Created at {date}",
|
||||||
|
"confirm_delete": "Are you sure you want to delete the access token for {name}?",
|
||||||
|
"delete_failed": "Failed to delete the access token.",
|
||||||
|
"create": "Create Token",
|
||||||
|
"create_failed": "Failed to create the access token.",
|
||||||
|
"prompt_name": "Name?",
|
||||||
|
"prompt_copy_token": "Copy your access token. It will not be shown again.",
|
||||||
|
"empty_state": "You have no long-lived access tokens yet."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"shopping-list": {
|
"shopping-list": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user