From 34567d451f080e4d1b3abb373704639b57344f73 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 11 Sep 2018 21:29:40 +0200 Subject: [PATCH] Add UI for tokens (#1656) * Add UI for tokens * Update strings * Update text * Update text --- .../ha-long-lived-access-tokens-card.js | 127 ++++++++++++++++++ src/panels/profile/ha-panel-profile.js | 32 ++++- src/panels/profile/ha-refresh-tokens-card.js | 82 +++++++++++ src/translations/en.json | 21 +++ 4 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 src/panels/profile/ha-long-lived-access-tokens-card.js create mode 100644 src/panels/profile/ha-refresh-tokens-card.js diff --git a/src/panels/profile/ha-long-lived-access-tokens-card.js b/src/panels/profile/ha-long-lived-access-tokens-card.js new file mode 100644 index 0000000000..9ada293fe9 --- /dev/null +++ b/src/panels/profile/ha-long-lived-access-tokens-card.js @@ -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` + + +
+

+ [[localize('ui.panel.profile.long_lived_access_tokens.description')]] + + [[localize('ui.panel.profile.long_lived_access_tokens.learn_auth_requests')]] + +

+ +
+ +
+ + [[localize('ui.panel.profile.long_lived_access_tokens.create')]] + +
+
+ `; + } + + 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); diff --git a/src/panels/profile/ha-panel-profile.js b/src/panels/profile/ha-panel-profile.js index e4e6d8442d..e855c583c8 100644 --- a/src/panels/profile/ha-panel-profile.js +++ b/src/panels/profile/ha-panel-profile.js @@ -15,6 +15,9 @@ import EventsMixin from '../../mixins/events-mixin.js'; import './ha-change-password-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-theme-row.js'; import './ha-push-notifications-row.js'; @@ -84,7 +87,22 @@ class HaPanelProfile extends EventsMixin(PolymerElement) { - + + + + + `; @@ -95,9 +113,21 @@ class HaPanelProfile extends EventsMixin(PolymerElement) { hass: Object, narrow: Boolean, showMenu: Boolean, + _refreshTokens: Array, }; } + connectedCallback() { + super.connectedCallback(); + this._refreshRefreshTokens(); + } + + async _refreshRefreshTokens() { + this._refreshTokens = await this.hass.callWS({ + type: 'auth/refresh_tokens' + }); + } + _handleLogOut() { this.fire('hass-logout'); } diff --git a/src/panels/profile/ha-refresh-tokens-card.js b/src/panels/profile/ha-refresh-tokens-card.js new file mode 100644 index 0000000000..3b1e1ac57d --- /dev/null +++ b/src/panels/profile/ha-refresh-tokens-card.js @@ -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` + + +
[[localize('ui.panel.profile.refresh_tokens.description')]]
+ +
+ `; + } + + 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); diff --git a/src/translations/en.json b/src/translations/en.json index fa018ab1dd..8f78aff660 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -746,6 +746,27 @@ "error_no_theme": "No themes available.", "link_promo": "Learn about themes", "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": {