Add PolyGerrit UI for showing service user details
This change adds another screen, that lists the account details of
a service user. This screen can be accessed by clicking on the username
in the service user list or by using the URI
/x/serviceuser/user/${account_id}.
The page currently does not allow to edit the service user's account
details. This will be added in a future change.
Change-Id: I6e7e9feeb0d1778e87688ccfaccb631d76045870
diff --git a/src/main/resources/static/gr-serviceuser-detail.html b/src/main/resources/static/gr-serviceuser-detail.html
new file mode 100644
index 0000000..0eaf7bb
--- /dev/null
+++ b/src/main/resources/static/gr-serviceuser-detail.html
@@ -0,0 +1,117 @@
+<!--
+@license
+Copyright (C) 2019 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<link rel="import"
+ href="./gr-serviceuser-ssh-panel.html">
+
+<dom-module id="gr-serviceuser-detail">
+ <template>
+ <style include="shared-styles"></style>
+ <style include="gr-subpage-styles"></style>
+ <style include="gr-form-styles"></style>
+ <style>
+ div.serviceuser-detail {
+ margin: 2em auto;
+ max-width: 50em;
+ }
+
+ h1#Title {
+ margin-bottom: 1em;
+ }
+
+ span#gr_serviceuser_activity {
+ border-radius: 1em;
+ width: 10em;
+ padding: 0.3em;
+ font-weight: bold;
+ text-align: center;
+ }
+
+ span.Active {
+ background-color: #9fcc6b;
+ }
+
+ span.Inactive {
+ background-color: #f7a1ad;
+ }
+ </style>
+ <div class="serviceuser-detail">
+ <main class="gr-form-styles read-only">
+ <div id="loading"
+ class$="[[_computeLoadingClass(_loading)]]">
+ Loading...
+ </div>
+ <div id="loadedContent"
+ class$="[[_computeLoadingClass(_loading)]]">
+ <h1 id="Title">Service User "[[_serviceUser.name]]"</h1>
+ <div id="form">
+ <fieldset>
+ <fieldset>
+ <h2 id="accountState">Account State</h2>
+ <section>
+ <span class="title">Current State</span>
+ <span id="gr_serviceuser_activity"
+ class$="value [[_active(_serviceUser)]]">
+ [[_active(_serviceUser)]]
+ </span>
+ </section>
+ </fieldset>
+ <fieldset>
+ <h2 id="userDataHeader">User Data</h2>
+ <section>
+ <span class="title">Username</span>
+ <span class="value">[[_serviceUser.username]]</span>
+ </section>
+ <section>
+ <span class="title">Full Name</span>
+ <span class="value">[[_serviceUser.name]]</span>
+ </section>
+ <section>
+ <span class="title">Email Address</span>
+ <span class="value">[[_serviceUser.email]]</span>
+ </section>
+ <section>
+ <span class="title">Owner Group</span>
+ <span class="value">[[_getOwnerGroup(_serviceUser)]]</span>
+ </section>
+ </fieldset>
+ <fieldset>
+ <h2 id="creationHeader">Creation</h2>
+ <section>
+ <span class="title">Created By</span>
+ <span class="value">[[_getCreator(_serviceUser)]]</span>
+ </section>
+ <section>
+ <span class="title">Created At</span>
+ <span class="value">[[_serviceUser.created_at]]</span>
+ </section>
+ </fieldset>
+ <fieldset>
+ <h2 id="credentialsHeader">Credentials</h2>
+ <fieldset>
+ <h3 id="SSHKeys">SSH keys</h3>
+ <gr-serviceuser-ssh-panel id="sshEditor"></gr-serviceuser-ssh-panel>
+ </fieldset>
+ </fieldset>
+ </fieldset>
+ </div>
+ </div>
+ </main>
+ </div>
+ </template>
+ <script src="gr-serviceuser-detail.js"></script>
+</dom-module>
diff --git a/src/main/resources/static/gr-serviceuser-detail.js b/src/main/resources/static/gr-serviceuser-detail.js
new file mode 100644
index 0000000..15da6af
--- /dev/null
+++ b/src/main/resources/static/gr-serviceuser-detail.js
@@ -0,0 +1,102 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function() {
+ 'use strict';
+
+ const NOT_FOUND_MESSAGE = 'Not Found';
+
+ Polymer({
+ is: 'gr-serviceuser-detail',
+ _legacyUndefinedCheck: true,
+
+ properties: {
+ _restApi: Object,
+ _serviceUserId: String,
+ _serviceUser: Object,
+ _loading: {
+ type: Boolean,
+ value: true,
+ },
+ },
+
+ behaviors: [
+ Gerrit.ListViewBehavior,
+ ],
+
+ attached() {
+ this._extractUserId();
+
+ if (!this._serviceUserId) { return; }
+
+ Promise.resolve(this._getServiceUser()).then(() => {
+ this.$.sshEditor.loadData(this._restApi, this._serviceUser);
+
+ this.fire('title-change', {title: this._serviceUser.name});
+ this._loading = false;
+ });
+ },
+
+ _computeLoadingClass(loading) {
+ return loading ? 'loading' : '';
+ },
+
+ _extractUserId() {
+ this._serviceUserId = this.baseURI.split('/').pop();
+ },
+
+ _getServiceUser() {
+ this._restApi = this.plugin.restApi(
+ '/config/server/serviceuser~serviceusers/');
+ return this._restApi.get(this._serviceUserId)
+ .then(serviceUser => {
+ if (!serviceUser) {
+ this._serviceUser = {};
+ return;
+ }
+ this._serviceUser = serviceUser;
+ });
+ },
+
+ _active(serviceUser) {
+ if (!serviceUser) {
+ return NOT_FOUND_MESSAGE;
+ }
+
+ return serviceUser.inactive === true ? 'Inactive' : 'Active';
+ },
+
+ _getCreator(serviceUser) {
+ if (!serviceUser || !serviceUser.created_by) {
+ return NOT_FOUND_MESSAGE;
+ }
+
+ if (serviceUser.created_by.username != undefined) {
+ return serviceUser.created_by.username;
+ }
+
+ if (serviceUser.created_by._account_id != -1) {
+ return serviceUser.created_by._account_id;
+ }
+
+ return NOT_FOUND_MESSAGE;
+ },
+
+ _getOwnerGroup(serviceUser) {
+ return serviceUser && serviceUser.owner
+ ? serviceUser.owner.name
+ : NOT_FOUND_MESSAGE;
+ },
+ });
+})();
diff --git a/src/main/resources/static/gr-serviceuser-list.html b/src/main/resources/static/gr-serviceuser-list.html
index 3ff745c..5557202 100644
--- a/src/main/resources/static/gr-serviceuser-list.html
+++ b/src/main/resources/static/gr-serviceuser-list.html
@@ -46,7 +46,9 @@
<template is="dom-repeat"
items="[[_serviceUsers]]">
<tr class="table">
- <td class="name">[[item.username]]</td>
+ <td class="name">
+ <a href$="[[_computeServiceUserUrl(item._account_id)]]">[[item.username]]</a>
+ </td>
<td class="fullName">[[item.name]]</td>
<td class="email">[[item.email]]</td>
<td class="owner">[[_getOwnerGroup(item)]]</td>
diff --git a/src/main/resources/static/gr-serviceuser-list.js b/src/main/resources/static/gr-serviceuser-list.js
index 1a6272a..b7ef602 100644
--- a/src/main/resources/static/gr-serviceuser-list.js
+++ b/src/main/resources/static/gr-serviceuser-list.js
@@ -83,5 +83,9 @@
_getOwnerGroup(item) {
return item && item.owner ? item.owner.name : NOT_FOUND_MESSAGE;
},
+
+ _computeServiceUserUrl(id) {
+ return `${this.plugin.screenUrl()}/user/${id}`;
+ },
});
})();
diff --git a/src/main/resources/static/gr-serviceuser-ssh-panel.html b/src/main/resources/static/gr-serviceuser-ssh-panel.html
new file mode 100644
index 0000000..c63ab5c
--- /dev/null
+++ b/src/main/resources/static/gr-serviceuser-ssh-panel.html
@@ -0,0 +1,107 @@
+<!--
+@license
+Copyright (C) 2019 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<dom-module id="gr-serviceuser-ssh-panel">
+ <template>
+ <style include="shared-styles"></style>
+ <style include="gr-form-styles">
+ .statusHeader {
+ width: 4em;
+ }
+
+ .keyHeader {
+ width: 7.5em;
+ }
+
+ #viewKeyOverlay {
+ padding: 2em;
+ width: 50em;
+ }
+
+ .publicKey {
+ font-family: var(--monospace-font-family);
+ overflow-x: scroll;
+ overflow-wrap: break-word;
+ width: 30em;
+ }
+
+ .closeButton {
+ bottom: 2em;
+ position: absolute;
+ right: 2em;
+ }
+
+ #existing {
+ margin-bottom: 1em;
+ }
+
+ #existing .commentColumn {
+ min-width: 27em;
+ width: auto;
+ }
+ </style>
+ <div class="gr-form-styles">
+ <fieldset id="existing">
+ <table>
+ <thead>
+ <tr>
+ <th class="commentColumn">Comment</th>
+ <th class="statusHeader">Status</th>
+ <th class="keyHeader">Public key</th>
+ </tr>
+ </thead>
+ <tbody>
+ <template is="dom-repeat"
+ items="[[_keys]]"
+ as="key">
+ <tr>
+ <td class="commentColumn">[[key.comment]]</td>
+ <td>[[_getStatusLabel(key.valid)]]</td>
+ <td>
+ <gr-button link
+ on-tap="_showKey"
+ data-index$="[[index]]"
+ link>Click to View</gr-button>
+ </td>
+ </tr>
+ </template>
+ </tbody>
+ </table>
+ <gr-overlay id="viewKeyOverlay"
+ with-backdrop>
+ <fieldset>
+ <section>
+ <span class="title">Algorithm</span>
+ <span class="value">[[_keyToView.algorithm]]</span>
+ </section>
+ <section>
+ <span class="title">Public key</span>
+ <span class="value publicKey">[[_keyToView.encoded_key]]</span>
+ </section>
+ <section>
+ <span class="title">Comment</span>
+ <span class="value">[[_keyToView.comment]]</span>
+ </section>
+ </fieldset>
+ <gr-button class="closeButton"
+ on-tap="_closeOverlay">Close</gr-button>
+ </gr-overlay>
+ </fieldset>
+ </div>
+ </template>
+ <script src="gr-serviceuser-ssh-panel.js"></script>
+</dom-module>
diff --git a/src/main/resources/static/gr-serviceuser-ssh-panel.js b/src/main/resources/static/gr-serviceuser-ssh-panel.js
new file mode 100644
index 0000000..4ee0b36
--- /dev/null
+++ b/src/main/resources/static/gr-serviceuser-ssh-panel.js
@@ -0,0 +1,61 @@
+/**
+ * @license
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function() {
+ 'use strict';
+
+ Polymer({
+ is: 'gr-serviceuser-ssh-panel',
+ _legacyUndefinedCheck: true,
+
+ properties: {
+ _restApi: Object,
+ _serviceUser: Object,
+ _keys: Array,
+ /** @type {?} */
+ _keyToView: Object,
+ },
+
+ loadData(restApi, serviceUser) {
+ this._restApi = restApi;
+ this._serviceUser = serviceUser;
+ return this._restApi.get(`${this._serviceUser._account_id}/sshkeys`)
+ .then(keys => {
+ if (!keys) {
+ this._keys = [];
+ return;
+ }
+ this._keys = keys;
+ });
+ },
+
+ _getStatusLabel(isValid) {
+ return isValid ? 'Valid' : 'Invalid';
+ },
+
+ _showKey(e) {
+ const el = Polymer.dom(e).localTarget;
+ const index = parseInt(el.getAttribute('data-index'), 10);
+ this._keyToView = this._keys[index];
+ this.$.viewKeyOverlay.open();
+ },
+
+ _closeOverlay() {
+ this.$.viewKeyOverlay.close();
+ },
+ });
+})();
diff --git a/src/main/resources/static/gr-serviceuser.html b/src/main/resources/static/gr-serviceuser.html
index 982ba43..deb277e 100644
--- a/src/main/resources/static/gr-serviceuser.html
+++ b/src/main/resources/static/gr-serviceuser.html
@@ -18,6 +18,8 @@
<link rel="import"
href="./gr-serviceuser-list.html">
+<link rel="import"
+ href="./gr-serviceuser-detail.html">
<dom-module id="gr-serviceuser">
<script>
@@ -28,6 +30,7 @@
&& (capabilities.administrateServer
|| capabilities['serviceuser-createServiceUser'])) {
plugin.screen('list', 'gr-serviceuser-list');
+ plugin.screen('user', 'gr-serviceuser-detail');
}
plugin.admin()
.addMenuLink(