Allow owner users to change password of any user (#6698)

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
Bram Kragten 2020-08-28 14:30:42 +02:00 committed by GitHub
parent dc5b92030f
commit 7a4c9b128c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 117 additions and 23 deletions

View File

@ -44,3 +44,14 @@ export const createAuthForUser = async (
username, username,
password, password,
}); });
export const adminChangePassword = async (
hass: HomeAssistant,
userId: string,
password: string
) =>
hass.callWS<void>({
type: "config/auth_provider/homeassistant/admin_change_password",
user_id: userId,
password,
});

View File

@ -1,20 +1,21 @@
import "@material/mwc-button"; import "@material/mwc-button";
import "@polymer/paper-input/paper-input"; import "@polymer/paper-input/paper-input";
import "../../../components/ha-circular-progress";
import { import {
css, css,
CSSResult, CSSResult,
customElement, customElement,
html, html,
internalProperty,
LitElement, LitElement,
property, property,
internalProperty,
PropertyValues, PropertyValues,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
import "../../../components/ha-circular-progress";
import "../../../components/ha-dialog"; import "../../../components/ha-dialog";
import "../../../components/ha-switch";
import "../../../components/ha-formfield"; import "../../../components/ha-formfield";
import "../../../components/ha-switch";
import { createAuthForUser } from "../../../data/auth"; import { createAuthForUser } from "../../../data/auth";
import { import {
createUser, createUser,
@ -27,7 +28,6 @@ import { PolymerChangedEvent } from "../../../polymer-types";
import { haStyleDialog } from "../../../resources/styles"; import { haStyleDialog } from "../../../resources/styles";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { AddUserDialogParams } from "./show-dialog-add-user"; import { AddUserDialogParams } from "./show-dialog-add-user";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
@customElement("dialog-add-user") @customElement("dialog-add-user")
export class DialogAddUser extends LitElement { export class DialogAddUser extends LitElement {
@ -46,6 +46,8 @@ export class DialogAddUser extends LitElement {
@internalProperty() private _password?: string; @internalProperty() private _password?: string;
@internalProperty() private _passwordConfirm?: string;
@internalProperty() private _isAdmin?: boolean; @internalProperty() private _isAdmin?: boolean;
public showDialog(params: AddUserDialogParams) { public showDialog(params: AddUserDialogParams) {
@ -53,6 +55,7 @@ export class DialogAddUser extends LitElement {
this._name = ""; this._name = "";
this._username = ""; this._username = "";
this._password = ""; this._password = "";
this._passwordConfirm = "";
this._isAdmin = false; this._isAdmin = false;
this._error = undefined; this._error = undefined;
this._loading = false; this._loading = false;
@ -83,17 +86,20 @@ export class DialogAddUser extends LitElement {
${this._error ? html` <div class="error">${this._error}</div> ` : ""} ${this._error ? html` <div class="error">${this._error}</div> ` : ""}
<paper-input <paper-input
class="name" class="name"
name="name"
.label=${this.hass.localize("ui.panel.config.users.add_user.name")} .label=${this.hass.localize("ui.panel.config.users.add_user.name")}
.value=${this._name} .value=${this._name}
required required
auto-validate auto-validate
autocapitalize="on" autocapitalize="on"
.errorMessage=${this.hass.localize("ui.common.error_required")} .errorMessage=${this.hass.localize("ui.common.error_required")}
@value-changed=${this._nameChanged} @value-changed=${this._handleValueChanged}
@blur=${this._maybePopulateUsername} @blur=${this._maybePopulateUsername}
></paper-input> ></paper-input>
<paper-input <paper-input
class="username" class="username"
name="username"
.label=${this.hass.localize( .label=${this.hass.localize(
"ui.panel.config.users.add_user.username" "ui.panel.config.users.add_user.username"
)} )}
@ -101,20 +107,40 @@ export class DialogAddUser extends LitElement {
required required
auto-validate auto-validate
autocapitalize="none" autocapitalize="none"
@value-changed=${this._usernameChanged} @value-changed=${this._handleValueChanged}
.errorMessage=${this.hass.localize("ui.common.error_required")} .errorMessage=${this.hass.localize("ui.common.error_required")}
></paper-input> ></paper-input>
<paper-input <paper-input
.label=${this.hass.localize( .label=${this.hass.localize(
"ui.panel.config.users.add_user.password" "ui.panel.config.users.add_user.password"
)} )}
type="password" type="password"
name="password"
.value=${this._password} .value=${this._password}
required required
auto-validate auto-validate
@value-changed=${this._passwordChanged} @value-changed=${this._handleValueChanged}
.errorMessage=${this.hass.localize("ui.common.error_required")} .errorMessage=${this.hass.localize("ui.common.error_required")}
></paper-input> ></paper-input>
<paper-input
label="${this.hass.localize(
"ui.panel.config.users.add_user.password_confirm"
)}"
name="passwordConfirm"
.value=${this._passwordConfirm}
@value-changed=${this._handleValueChanged}
required
type="password"
.invalid=${this._password !== "" &&
this._passwordConfirm !== "" &&
this._passwordConfirm !== this._password}
.errorMessage="${this.hass.localize(
"ui.panel.config.users.add_user.password_not_match"
)}"
></paper-input>
<ha-formfield <ha-formfield
.label=${this.hass.localize("ui.panel.config.users.editor.admin")} .label=${this.hass.localize("ui.panel.config.users.editor.admin")}
.dir=${computeRTLDirection(this.hass)} .dir=${computeRTLDirection(this.hass)}
@ -147,7 +173,10 @@ export class DialogAddUser extends LitElement {
: html` : html`
<mwc-button <mwc-button
slot="primaryAction" slot="primaryAction"
.disabled=${!this._name || !this._username || !this._password} .disabled=${!this._name ||
!this._username ||
!this._password ||
this._password !== this._passwordConfirm}
@click=${this._createUser} @click=${this._createUser}
> >
${this.hass.localize("ui.panel.config.users.add_user.create")} ${this.hass.localize("ui.panel.config.users.add_user.create")}
@ -173,19 +202,10 @@ export class DialogAddUser extends LitElement {
} }
} }
private _nameChanged(ev: PolymerChangedEvent<string>) { private _handleValueChanged(ev: PolymerChangedEvent<string>): void {
this._error = undefined; this._error = undefined;
this._name = ev.detail.value; const name = (ev.target as any).name;
} this[`_${name}`] = ev.detail.value;
private _usernameChanged(ev: PolymerChangedEvent<string>) {
this._error = undefined;
this._username = ev.detail.value;
}
private _passwordChanged(ev: PolymerChangedEvent<string>) {
this._error = undefined;
this._password = ev.detail.value;
} }
private async _adminChanged(ev): Promise<void> { private async _adminChanged(ev): Promise<void> {

View File

@ -6,23 +6,28 @@ import {
CSSResult, CSSResult,
customElement, customElement,
html, html,
internalProperty,
LitElement, LitElement,
property, property,
internalProperty,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
import { createCloseHeading } from "../../../components/ha-dialog"; import { createCloseHeading } from "../../../components/ha-dialog";
import "../../../components/ha-switch";
import "../../../components/ha-formfield"; import "../../../components/ha-formfield";
import "../../../components/ha-switch";
import { adminChangePassword } from "../../../data/auth";
import { import {
SYSTEM_GROUP_ID_ADMIN, SYSTEM_GROUP_ID_ADMIN,
SYSTEM_GROUP_ID_USER, SYSTEM_GROUP_ID_USER,
} from "../../../data/user"; } from "../../../data/user";
import {
showAlertDialog,
showPromptDialog,
} from "../../../dialogs/generic/show-dialog-box";
import { PolymerChangedEvent } from "../../../polymer-types"; import { PolymerChangedEvent } from "../../../polymer-types";
import { haStyleDialog } from "../../../resources/styles"; import { haStyleDialog } from "../../../resources/styles";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { UserDetailDialogParams } from "./show-dialog-user-detail"; import { UserDetailDialogParams } from "./show-dialog-user-detail";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
@customElement("dialog-user-detail") @customElement("dialog-user-detail")
class DialogUserDetail extends LitElement { class DialogUserDetail extends LitElement {
@ -141,7 +146,15 @@ class DialogUserDetail extends LitElement {
</paper-tooltip> </paper-tooltip>
` `
: ""} : ""}
${!user.system_generated && this.hass.user?.is_owner
? html`<mwc-button @click=${this._changePassword}>
${this.hass.localize(
"ui.panel.config.users.editor.change_password"
)}
</mwc-button>`
: ""}
</div> </div>
<div slot="primaryAction"> <div slot="primaryAction">
<mwc-button <mwc-button
@click=${this._updateEntry} @click=${this._updateEntry}
@ -202,6 +215,52 @@ class DialogUserDetail extends LitElement {
} }
} }
private async _changePassword() {
const credential = this._params?.entry.credentials.find(
(cred) => cred.type === "homeassistant"
);
if (!credential) {
showAlertDialog(this, {
title: "No Home Assistant credentials found.",
});
return;
}
const newPassword = await showPromptDialog(this, {
title: this.hass.localize("ui.panel.config.users.editor.change_password"),
inputType: "password",
inputLabel: this.hass.localize(
"ui.panel.config.users.editor.new_password"
),
});
if (!newPassword) {
return;
}
const confirmPassword = await showPromptDialog(this, {
title: this.hass.localize("ui.panel.config.users.editor.change_password"),
inputType: "password",
inputLabel: this.hass.localize(
"ui.panel.config.users.add_user.password_confirm"
),
});
if (!confirmPassword) {
return;
}
if (newPassword !== confirmPassword) {
showAlertDialog(this, {
title: this.hass.localize(
"ui.panel.config.users.add_user.password_not_match"
),
});
return;
}
await adminChangePassword(this.hass, this._params!.entry.id, newPassword);
showAlertDialog(this, {
title: this.hass.localize(
"ui.panel.config.users.add_user.password_changed"
),
});
}
private _close(): void { private _close(): void {
this._params = undefined; this._params = undefined;
} }

View File

@ -1654,6 +1654,8 @@
"caption": "View user", "caption": "View user",
"name": "Name", "name": "Name",
"change_password": "Change password", "change_password": "Change password",
"new_password": "New Password",
"password_changed": "The password is changed!",
"activate_user": "Activate user", "activate_user": "Activate user",
"deactivate_user": "Deactivate user", "deactivate_user": "Deactivate user",
"delete_user": "Delete user", "delete_user": "Delete user",
@ -1674,6 +1676,8 @@
"name": "Name", "name": "Name",
"username": "Username", "username": "Username",
"password": "Password", "password": "Password",
"password_confirm": "Confirm Password",
"password_not_match": "Passwords don't match",
"create": "Create" "create": "Create"
} }
}, },