Merge branch 'stable-3.2' into stable-3.3
* stable-3.2:
Format build file using buildifier
Fix "Authentication required" in serviceuser plugin
Log internal errors when validating service user
Migrate UI to Polymer 3
Change-Id: I2e36fcb6fb7f72867f1fcc245dea9d31b0abe3a1
diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/.eslintignore
diff --git a/.eslintrc.json b/.eslintrc.json
index 3a9c4b9..b586e29 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,79 +1,166 @@
{
- "extends": ["eslint:recommended", "google"],
- "parserOptions": {
- "ecmaVersion": 8
- },
- "env": {
- "browser": true,
- "es6": true
- },
- "globals": {
- "__dirname": false,
- "app": false,
- "page": false,
- "Polymer": false,
- "process": false,
- "require": false,
- "Gerrit": false,
- "Promise": false,
- "assert": false,
- "test": false,
- "flushAsynchronousOperations": false
- },
- "rules": {
- "arrow-parens": ["error", "as-needed"],
- "block-spacing": ["error", "always"],
- "brace-style": ["error", "1tbs", { "allowSingleLine": true }],
- "camelcase": "off",
- "comma-dangle": ["error", "always-multiline"],
- "eol-last": "off",
- "indent": "off",
- "indent-legacy": ["error", 2, {
- "MemberExpression": 2,
- "FunctionDeclaration": {"body": 1, "parameters": 2},
- "FunctionExpression": {"body": 1, "parameters": 2},
- "CallExpression": {"arguments": 2},
- "ArrayExpression": 1,
- "ObjectExpression": 1,
- "SwitchCase": 1
- }],
- "keyword-spacing": ["error", { "after": true, "before": true }],
- "max-len": [
- "error",
- 80,
- 2,
- {"ignoreComments": true}
- ],
- "new-cap": ["error", { "capIsNewExceptions": ["Polymer"] }],
- "no-console": "off",
- "no-restricted-syntax": [
- "error",
- {
- "selector": "ExpressionStatement > CallExpression > MemberExpression[object.name='test'][property.name='only']",
- "message": "Remove test.only."
- },
- {
- "selector": "ExpressionStatement > CallExpression > MemberExpression[object.name='suite'][property.name='only']",
- "message": "Remove suite.only."
- }
- ],
- "no-undef": "off",
- "no-useless-escape": "off",
- "no-var": "error",
- "object-shorthand": ["error", "always"],
- "prefer-arrow-callback": "error",
- "prefer-const": "error",
- "prefer-spread": "error",
- "quote-props": ["error", "consistent-as-needed"],
- "require-jsdoc": "off",
- "semi": [2, "always"],
- "template-curly-spacing": "error",
- "valid-jsdoc": "off"
- },
- "plugins": [
- "html"
+ "extends": ["eslint:recommended", "google"],
+ "parserOptions": {
+ "ecmaVersion": 8,
+ "sourceType": "module"
+ },
+ "env": {
+ "browser": true,
+ "es6": true
+ },
+ "globals": {
+ "__dirname": false,
+ "app": false,
+ "page": false,
+ "Polymer": false,
+ "process": false,
+ "require": false,
+ "Gerrit": false,
+ "Promise": false,
+ "assert": false,
+ "test": false,
+ "flushAsynchronousOperations": false
+ },
+ "rules": {
+ "arrow-parens": ["error", "as-needed"],
+ "block-spacing": ["error", "always"],
+ "brace-style": ["error", "1tbs", { "allowSingleLine": true }],
+ "camelcase": "off",
+ "comma-dangle": ["error", {
+ "arrays": "always-multiline",
+ "objects": "always-multiline",
+ "imports": "always-multiline",
+ "exports": "always-multiline",
+ "functions": "never"
+ }],
+ "eol-last": "off",
+ "indent": ["error", 2, {
+ "MemberExpression": 2,
+ "FunctionDeclaration": {"body": 1, "parameters": 2},
+ "FunctionExpression": {"body": 1, "parameters": 2},
+ "CallExpression": {"arguments": 2 },
+ "ArrayExpression": 1,
+ "ObjectExpression": 1,
+ "SwitchCase": 1
+ }],
+ "keyword-spacing": ["error", { "after": true, "before": true }],
+ "lines-between-class-members": ["error", "always"],
+ "max-len": [
+ "error",
+ 80,
+ 2,
+ {
+ "ignoreComments": true,
+ "ignorePattern": "^import .*;$"
+ }
],
- "settings": {
- "html/report-bad-indent": "error"
+ "new-cap": ["error", { "capIsNewExceptions": ["Polymer", "LegacyElementMixin", "GestureEventListeners", "LegacyDataMixin"] }],
+ "no-console": "off",
+ "no-multiple-empty-lines": [ "error", { "max": 1 } ],
+ "no-prototype-builtins": "off",
+ "no-redeclare": "off",
+ "no-restricted-syntax": [
+ "error",
+ {
+ "selector": "ExpressionStatement > CallExpression > MemberExpression[object.name='test'][property.name='only']",
+ "message": "Remove test.only."
+ },
+ {
+ "selector": "ExpressionStatement > CallExpression > MemberExpression[object.name='suite'][property.name='only']",
+ "message": "Remove suite.only."
+ }
+ ],
+ "no-undef": "off",
+ "no-useless-escape": "off",
+ "no-var": "error",
+ "object-shorthand": ["error", "always"],
+ "padding-line-between-statements": [
+ "error",
+ {
+ "blankLine": "always",
+ "prev": "class",
+ "next": "*"
+ },
+ {
+ "blankLine": "always",
+ "prev": "*",
+ "next": "class"
+ }
+ ],
+ "prefer-arrow-callback": "error",
+ "prefer-const": "error",
+ "prefer-spread": "error",
+ "quote-props": ["error", "consistent-as-needed"],
+ "require-jsdoc": "off",
+ "semi": [2, "always"],
+ "template-curly-spacing": "error",
+ "valid-jsdoc": "off",
+ "require-jsdoc": 0,
+ "valid-jsdoc": 0,
+ "jsdoc/check-alignment": 2,
+ "jsdoc/check-examples": 0,
+ "jsdoc/check-indentation": 0,
+ "jsdoc/check-param-names": 0,
+ "jsdoc/check-syntax": 0,
+ "jsdoc/check-tag-names": 0,
+ "jsdoc/check-types": 0,
+ "jsdoc/implements-on-classes": 2,
+ "jsdoc/match-description": 0,
+ "jsdoc/newline-after-description": 2,
+ "jsdoc/no-types": 0,
+ "jsdoc/no-undefined-types": 0,
+ "jsdoc/require-description": 0,
+ "jsdoc/require-description-complete-sentence": 0,
+ "jsdoc/require-example": 0,
+ "jsdoc/require-hyphen-before-param-description": 0,
+ "jsdoc/require-jsdoc": 0,
+ "jsdoc/require-param": 0,
+ "jsdoc/require-param-description": 0,
+ "jsdoc/require-param-name": 2,
+ "jsdoc/require-param-type": 2,
+ "jsdoc/require-returns": 0,
+ "jsdoc/require-returns-check": 0,
+ "jsdoc/require-returns-description": 0,
+ "jsdoc/require-returns-type": 2,
+ "jsdoc/valid-types": 2,
+ "jsdoc/require-file-overview": ["error", {
+ "tags": {
+ "license": {
+ "mustExist": true,
+ "preventDuplicates": true
+ }
+ }
+ }],
+ "import/named": 2,
+ "import/no-unresolved": 2,
+ "import/no-self-import": 2,
+ // The no-cycle rule is slow, because it doesn't cache dependencies.
+ // Disable it.
+ "import/no-cycle": 0,
+ "import/no-useless-path-segments": 2,
+ "import/no-unused-modules": 2,
+ "import/no-default-export": 2
+ },
+ "plugins": [
+ "html",
+ "jsdoc",
+ "import"
+ ],
+ "settings": {
+ "html/report-bad-indent": "error"
+ },
+ "overrides": [
+ {
+ "files": ["*_html.js", "*-styles.js"],
+ "rules": {
+ "max-len": "off"
+ }
+ },
+ {
+ "files": ["*.html"],
+ "rules": {
+ "jsdoc/require-file-overview": "off"
+ }
}
- }
+ ]
+}
diff --git a/BUILD b/BUILD
index fcad0e5..81b7851 100644
--- a/BUILD
+++ b/BUILD
@@ -1,3 +1,7 @@
+load("@npm//@bazel/rollup:index.bzl", "rollup_bundle")
+load("//tools/bzl:js.bzl", "polygerrit_plugin")
+load("//tools/bzl:genrule2.bzl", "genrule2")
+load("//tools/js:eslint.bzl", "eslint")
load("//tools/bzl:junit.bzl", "junit_tests")
load("//tools/bzl:plugin.bzl", "PLUGIN_DEPS", "PLUGIN_TEST_DEPS", "gerrit_plugin")
@@ -10,6 +14,7 @@
"Gerrit-HttpModule: com.googlesource.gerrit.plugins.serviceuser.HttpModule",
"Gerrit-SshModule: com.googlesource.gerrit.plugins.serviceuser.SshModule",
],
+ resource_jars = [":gr-serviceuser-static"],
resources = glob(["src/main/resources/**/*"]),
)
@@ -24,3 +29,54 @@
":serviceuser__plugin",
],
)
+
+genrule2(
+ name = "gr-serviceuser-static",
+ srcs = [":gr-serviceuser"],
+ outs = ["gr-serviceuser-static.jar"],
+ cmd = " && ".join([
+ "mkdir $$TMP/static",
+ "cp -r $(locations :gr-serviceuser) $$TMP/static",
+ "cd $$TMP",
+ "zip -Drq $$ROOT/$@ -g .",
+ ]),
+)
+
+rollup_bundle(
+ name = "serviceuser-bundle",
+ srcs = glob(["gr-serviceuser/*.js"]),
+ entry_point = "gr-serviceuser/gr-serviceuser.js",
+ format = "iife",
+ rollup_bin = "//tools/node_tools:rollup-bin",
+ sourcemap = "hidden",
+ deps = [
+ "@tools_npm//rollup-plugin-node-resolve",
+ ],
+)
+
+polygerrit_plugin(
+ name = "gr-serviceuser",
+ app = "serviceuser-bundle.js",
+ plugin_name = "serviceuser",
+)
+
+# Define the eslinter for the plugin
+# The eslint macro creates 2 rules: lint_test and lint_bin
+eslint(
+ name = "lint",
+ srcs = glob([
+ "gr-serviceuser/**/*.js",
+ ]),
+ config = ".eslintrc.json",
+ data = [],
+ extensions = [
+ ".js",
+ ],
+ ignore = ".eslintignore",
+ plugins = [
+ "@npm//eslint-config-google",
+ "@npm//eslint-plugin-html",
+ "@npm//eslint-plugin-import",
+ "@npm//eslint-plugin-jsdoc",
+ ],
+)
diff --git a/gr-serviceuser/gr-serviceuser-create.js b/gr-serviceuser/gr-serviceuser-create.js
new file mode 100644
index 0000000..4ca98d9
--- /dev/null
+++ b/gr-serviceuser/gr-serviceuser-create.js
@@ -0,0 +1,171 @@
+/**
+ * @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.
+ */
+
+import {htmlTemplate} from './gr-serviceuser-create_html.js';
+
+export class GrServiceUserCreate extends Polymer.GestureEventListeners(
+ Polymer.Element) {
+ /** @returns {?} template for this component */
+ static get template() { return htmlTemplate; }
+
+ /** @returns {string} name of the component */
+ static get is() { return 'gr-serviceuser-create'; }
+
+ /**
+ * Defines properties of the component
+ *
+ * @returns {?}
+ */
+ static get properties() {
+ return {
+ _infoMessageEnabled: {
+ type: Boolean,
+ value: false,
+ },
+ _infoMessage: String,
+ _successMessageEnabled: {
+ type: Boolean,
+ value: false,
+ },
+ _successMessage: String,
+ _newUsername: String,
+ _emailEnabled: {
+ type: Boolean,
+ value: false,
+ },
+ _newEmail: String,
+ _newKey: String,
+ _dataValid: {
+ type: Boolean,
+ value: false,
+ },
+ _isAdding: {
+ type: Boolean,
+ value: false,
+ },
+ _enableButton: {
+ type: Boolean,
+ value: false,
+ },
+ _accountId: String,
+ };
+ }
+
+ connectedCallback() {
+ super.connectedCallback();
+ this._getConfig();
+ }
+
+ _forwardToDetails() {
+ page.show(
+ this.plugin.screenUrl()
+ + '/user/'
+ + this._accountId);
+ }
+
+ _getConfig() {
+ return this.plugin.restApi('/config/server/serviceuser~config/').get('')
+ .then(config => {
+ if (!config) {
+ return;
+ }
+
+ if (config.info && config.info != '') {
+ this._infoMessageEnabled = true;
+ this._infoMessage = config.info;
+ this.$.infoMessage.innerHTML = this._infoMessage;
+ }
+
+ if (config.on_success && config.on_success != '') {
+ this._successMessageEnabled = true;
+ this._successMessage = config.on_success;
+ this.$.successMessage.innerHTML = this._successMessage;
+ }
+
+ this._emailEnabled = config.allow_email;
+ });
+ }
+
+ _validateData() {
+ this._dataValid = this._validateName(this._newUsername)
+ && this._validateEmail(this._newEmail)
+ && this._validateKey(this._newKey);
+ this._computeButtonEnabled();
+ }
+
+ _validateName(username) {
+ if (username && username.trim().length > 0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ _validateEmail(email) {
+ if (!email || email.trim().length == 0 || email.includes('@')) {
+ return true;
+ }
+
+ return false;
+ }
+
+ _validateKey(key) {
+ if (!key || !key.trim()) {
+ return false;
+ }
+
+ return true;
+ }
+
+ _computeButtonEnabled() {
+ this._enableButton = this._dataValid && !this._isAdding;
+ }
+
+ _handleCreateServiceUser() {
+ this._isAdding = true;
+ this._computeButtonEnabled();
+ const body = {
+ ssh_key: this._newKey.trim(),
+ email: this._newEmail ? this._newEmail.trim() : null,
+ };
+ return this.plugin.restApi('/a/config/server/serviceuser~serviceusers/')
+ .post(this._newUsername, body)
+ .then(response => {
+ this._accountId = response._account_id;
+ if (this._successMessage) {
+ this.$.successDialogOverlay.open();
+ } else {
+ this._forwardToDetails();
+ }
+ }).catch(response => {
+ this.dispatchEvent(
+ new CustomEvent(
+ 'show-error',
+ {
+ detail: {message: response},
+ bubbles: true,
+ composed: true,
+ }
+ )
+ );
+ this._isAdding = false;
+ this._computeButtonEnabled();
+ });
+ }
+}
+
+customElements.define(GrServiceUserCreate.is, GrServiceUserCreate);
diff --git a/src/main/resources/static/gr-serviceuser-create.html b/gr-serviceuser/gr-serviceuser-create_html.js
similarity index 74%
rename from src/main/resources/static/gr-serviceuser-create.html
rename to gr-serviceuser/gr-serviceuser-create_html.js
index 2e70471..86f2473 100644
--- a/src/main/resources/static/gr-serviceuser-create.html
+++ b/gr-serviceuser/gr-serviceuser-create_html.js
@@ -1,22 +1,21 @@
-<!--
-@license
-Copyright (C) 2019 The Android Open Source Project
+/**
+ * @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.
+ */
-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-create">
- <template>
+export const htmlTemplate = Polymer.html`
<style include="shared-styles"></style>
<style include="gr-subpage-styles"></style>
<style include="gr-form-styles"></style>
@@ -28,7 +27,7 @@
</style>
<main class="gr-form-styles read-only">
<div class="topHeader">
- <h2>Create Service User</h2>
+ <h1 class="heading-1">Create Service User</h1>
</div>
<fieldset id="infoMessage"
hidden$="[[!_infoMessageEnabled]]">
@@ -88,6 +87,4 @@
</gr-dialog>
</gr-overlay>
</main>
- </template>
- <script src="gr-serviceuser-create.js"></script>
-</dom-module>
+`;
diff --git a/gr-serviceuser/gr-serviceuser-detail.js b/gr-serviceuser/gr-serviceuser-detail.js
new file mode 100644
index 0000000..fa8fd32
--- /dev/null
+++ b/gr-serviceuser/gr-serviceuser-detail.js
@@ -0,0 +1,373 @@
+/**
+ * @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.
+ */
+
+import './gr-serviceuser-ssh-panel.js';
+import './gr-serviceuser-http-password.js';
+import {htmlTemplate} from './gr-serviceuser-detail_html.js';
+
+const NOT_FOUND_MESSAGE = 'Not Found';
+
+export class GrServiceUserDetail extends Polymer.GestureEventListeners(
+ Polymer.Element) {
+ /** @returns {?} template for this component */
+ static get template() { return htmlTemplate; }
+
+ /** @returns {string} name of the component */
+ static get is() { return 'gr-serviceuser-detail'; }
+
+ /**
+ * Defines properties of the component
+ *
+ * @returns {?}
+ */
+ static get properties() {
+ return {
+ _restApi: Object,
+ _serviceUserId: String,
+ _serviceUser: Object,
+ _loading: {
+ type: Boolean,
+ value: true,
+ },
+ _statusButtonText: {
+ type: String,
+ value: 'Activate',
+ },
+ _prefsChanged: {
+ type: Boolean,
+ value: false,
+ },
+ _changingPrefs: {
+ type: Boolean,
+ value: false,
+ },
+ _isAdmin: {
+ type: Boolean,
+ value: false,
+ },
+ _allowEmail: {
+ type: Boolean,
+ value: false,
+ },
+ _allowOwner: {
+ type: Boolean,
+ value: false,
+ },
+ _allowHttpPassword: {
+ type: Boolean,
+ value: false,
+ },
+ _newFullName: String,
+ _newEmail: String,
+ _availableOwners: Array,
+ _newOwner: String,
+ _ownerChangeWarning: String,
+ _query: {
+ type: Function,
+ value() {
+ return this._getGroupSuggestions.bind(this);
+ },
+ },
+ };
+ }
+
+ static get behaviors() {
+ return [
+ Gerrit.ListViewBehavior,
+ ];
+ }
+
+ connectedCallback() {
+ super.connectedCallback();
+ this._extractUserId();
+ this._loadServiceUser();
+ }
+
+ _loadServiceUser() {
+ if (!this._serviceUserId) { return; }
+
+ const promises = [];
+
+ promises.push(this._getPluginConfig());
+ promises.push(this._getServiceUser());
+
+ Promise.all(promises).then(() => {
+ this.$.sshEditor.loadData(this._restApi, this._serviceUser);
+ this.$.httpPass.loadData(this._restApi, this._serviceUser);
+
+ this.dispatchEvent(
+ new CustomEvent(
+ 'title-change',
+ {
+ detail: {title: this._serviceUser.name},
+ bubbles: true,
+ composed: true,
+ }
+ )
+ );
+ this._computeStatusButtonText();
+ this._loading = false;
+ this._newFullName = this._serviceUser.name;
+ this._newEmail = this._serviceUser.email;
+ });
+ }
+
+ _computeLoadingClass(loading) {
+ return loading ? 'loading' : '';
+ }
+
+ _extractUserId() {
+ this._serviceUserId = this.baseURI.split('/').pop();
+ }
+
+ _getPermissions() {
+ return this.plugin.restApi().getAccountCapabilities(['administrateServer'])
+ .then(capabilities => {
+ this._isAdmin = capabilities && capabilities.administrateServer;
+ });
+ }
+
+ _getPluginConfig() {
+ return Promise.resolve(this._getPermissions()).then(() => {
+ this.plugin.restApi('/config/server/serviceuser~config/').get('')
+ .then(config => {
+ if (!config) {
+ return;
+ }
+ this._allowEmail = config.allow_email || this._isAdmin;
+ this._allowOwner = config.allow_owner || this._isAdmin;
+ this._allowHttpPassword = config.allow_http_password
+ || this._isAdmin;
+ });
+ });
+ }
+
+ _getServiceUser() {
+ this._restApi = this.plugin.restApi(
+ '/a/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';
+ }
+
+ _computeStatusButtonText() {
+ if (!this._serviceUser) {
+ return;
+ }
+
+ this._statusButtonText = this._serviceUser.inactive === true
+ ? 'Activate'
+ : 'Deactivate';
+ }
+
+ _toggleStatus() {
+ if (this._serviceUser.inactive === true) {
+ this._restApi.put(`${this._serviceUser._account_id}/active`)
+ .then(() => {
+ this._loadServiceUser();
+ });
+ } else {
+ this._restApi.delete(`${this._serviceUser._account_id}/active`)
+ .then(() => {
+ this._loadServiceUser();
+ });
+ }
+ }
+
+ _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;
+ }
+
+ _isEmailValid(email) {
+ if (!email) {
+ return false;
+ }
+ return email.includes('@');
+ }
+
+ _getGroupSuggestions(input) {
+ let query;
+ if (!input || input === this._getOwnerGroup(this._serviceUser)) {
+ query = '';
+ } else {
+ query = `?suggest=${input}`;
+ }
+
+ return this.plugin.restApi('/a/groups/').get(query)
+ .then(response => {
+ const groups = [];
+ for (const key in response) {
+ if (!response.hasOwnProperty(key)) { continue; }
+ groups.push({
+ name: key,
+ value: decodeURIComponent(response[key].id),
+ });
+ }
+ this._availableOwners = groups;
+ return groups;
+ });
+ }
+
+ _isOwnerValid(owner) {
+ if (!owner) {
+ return false;
+ }
+
+ return this._getOwnerName(owner);
+ }
+
+ _isNewOwner() {
+ return this._getOwnerName(this._newOwner)
+ === this._getOwnerGroup(this._serviceUser);
+ }
+
+ _getOwnerName(id) {
+ return this._availableOwners.find(o => { return o.value === id; }).name;
+ }
+
+ _computeOwnerWarning() {
+ let message = 'If ';
+ message += this._getOwnerGroup(this._serviceUser) != NOT_FOUND_MESSAGE
+ ? 'the owner group is changed' : 'an owner group is set';
+ message += ' only members of the ';
+ message += this._getOwnerGroup(this._serviceUser) != NOT_FOUND_MESSAGE
+ ? 'new ' : '';
+ message += 'owner group can see and administrate the service user.';
+ message += this._getOwnerGroup(this._serviceUser) != NOT_FOUND_MESSAGE
+ ? '' : ' The creator of the service user can no'
+ + ' longer see and administrate the service user if she/he'
+ + ' is not member of the owner group.';
+ this._ownerChangeWarning = message;
+ }
+
+ _computePrefsChanged() {
+ if (this.loading || this._changingPrefs) {
+ return;
+ }
+
+ if (!this._newOwner && !this._newEmail && !this._newFullName) {
+ this._prefsChanged = false;
+ return;
+ }
+
+ if (this._newEmail && !this._isEmailValid(this._newEmail)) {
+ this._prefsChanged = false;
+ return;
+ }
+
+ if (this._newOwner
+ && (this._isNewOwner() || !this._isOwnerValid(this._newOwner))) {
+ this._prefsChanged = false;
+ return;
+ }
+
+ if (this._newOwner) {
+ this._computeOwnerWarning();
+ }
+
+ this._prefsChanged = true;
+ }
+
+ _applyNewFullName() {
+ return this._restApi
+ .put(`${this._serviceUser._account_id}/name`,
+ {name: this._newFullName})
+ .then(() => {
+ this.$.serviceUserFullNameInput.value = '';
+ });
+ }
+
+ _applyNewEmail(email) {
+ if (!this._isEmailValid(email)) {
+ return;
+ }
+ return this._restApi
+ .put(`${this._serviceUser._account_id}/email`, {email})
+ .then(() => {
+ this.$.serviceUserEmailInput.value = '';
+ });
+ }
+
+ _applyNewOwner(owner) {
+ if (this._isNewOwner() || !this._isOwnerValid(this._newOwner)) {
+ return;
+ }
+ return this._restApi
+ .put(`${this._serviceUser._account_id}/owner`, {group: owner})
+ .then(() => {
+ this.$.serviceUserOwnerInput.text = this._getOwnerGroup(
+ this._serviceUser);
+ });
+ }
+
+ _handleSavePreferences() {
+ const promises = [];
+ this._changingPrefs = true;
+
+ if (this._newFullName) {
+ promises.push(this._applyNewFullName());
+ }
+
+ if (this._newEmail) {
+ promises.push(this._applyNewEmail(this._newEmail));
+ }
+
+ if (this._newOwner) {
+ promises.push(this._applyNewOwner(this._newOwner));
+ }
+
+ Promise.all(promises).then(() => {
+ this._changingPrefs = false;
+ this._prefsChanged = false;
+ this._ownerChangeWarning = '';
+ this._loadServiceUser();
+ });
+ }
+}
+
+customElements.define(GrServiceUserDetail.is, GrServiceUserDetail);
diff --git a/src/main/resources/static/gr-serviceuser-detail.html b/gr-serviceuser/gr-serviceuser-detail_html.js
similarity index 81%
rename from src/main/resources/static/gr-serviceuser-detail.html
rename to gr-serviceuser/gr-serviceuser-detail_html.js
index 41e4e8d..0fe87f3 100644
--- a/src/main/resources/static/gr-serviceuser-detail.html
+++ b/gr-serviceuser/gr-serviceuser-detail_html.js
@@ -1,25 +1,21 @@
-<!--
-@license
-Copyright (C) 2019 The Android Open Source Project
+/**
+ * @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.
+ */
-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">
-<link rel="import" href="./gr-serviceuser-http-password.html">
-
-<dom-module id="gr-serviceuser-detail">
- <template>
+export const htmlTemplate = Polymer.html`
<style include="shared-styles"></style>
<style include="gr-subpage-styles"></style>
<style include="gr-form-styles"></style>
@@ -70,11 +66,11 @@
</div>
<div id="loadedContent"
class$="[[_computeLoadingClass(_loading)]]">
- <h1 id="Title">Service User "[[_serviceUser.name]]"</h1>
+ <h1 id="Title" class="heading-1">Service User "[[_serviceUser.name]]"</h1>
<div id="form">
<fieldset>
<fieldset>
- <h2 id="accountState">Account State</h2>
+ <h2 id="accountState" class="heading-2">Account State</h2>
<section>
<span class="title">Current State</span>
<span id="gr_serviceuser_activity"
@@ -86,7 +82,7 @@
[[_statusButtonText]]</gr-button>
</fieldset>
<fieldset>
- <h2 id="userDataHeader">User Data</h2>
+ <h2 id="userDataHeader" class="heading-2">User Data</h2>
<section>
<span class="title">Username</span>
<span class="value">[[_serviceUser.username]]</span>
@@ -133,7 +129,7 @@
</gr-button>
</fieldset>
<fieldset>
- <h2 id="creationHeader">Creation</h2>
+ <h2 id="creationHeader" class="heading-2">Creation</h2>
<section>
<span class="title">Created By</span>
<span class="value">[[_getCreator(_serviceUser)]]</span>
@@ -145,7 +141,7 @@
</fieldset>
<fieldset>
<fieldset>
- <h2 id="credentialsHeader">Credentials</h2>
+ <h2 id="credentialsHeader" class="heading-2">Credentials</h2>
</fieldset>
<fieldset hidden$="[[!_allowHttpPassword]]">
<h3 id="HTTPCredentials">HTTP Credentials</h3>
@@ -164,6 +160,4 @@
</div>
</main>
</div>
- </template>
- <script src="gr-serviceuser-detail.js"></script>
-</dom-module>
+`;
diff --git a/gr-serviceuser/gr-serviceuser-http-password.js b/gr-serviceuser/gr-serviceuser-http-password.js
new file mode 100644
index 0000000..396eef7
--- /dev/null
+++ b/gr-serviceuser/gr-serviceuser-http-password.js
@@ -0,0 +1,71 @@
+/**
+ * @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.
+ */
+
+import {htmlTemplate} from './gr-serviceuser-http-password_html.js';
+
+class GrServiceUserHttpPassword extends Polymer.GestureEventListeners(
+ Polymer.Element) {
+ /** @returns {?} template for this component */
+ static get template() { return htmlTemplate; }
+
+ /** @returns {string} name of the component */
+ static get is() { return 'gr-serviceuser-http-password'; }
+
+ /**
+ * Defines properties of the component
+ *
+ * @returns {?}
+ */
+ static get properties() {
+ return {
+ _restApi: Object,
+ _serviceUser: Object,
+ _generatedPassword: String,
+ _passwordUrl: String,
+ };
+ }
+
+ loadData(restApi, serviceUser) {
+ this._restApi = restApi;
+ this._serviceUser = serviceUser;
+ }
+
+ _handleGenerateTap() {
+ this._generatedPassword = 'Generating...';
+ this.$.generatedPasswordOverlay.open();
+ this._restApi
+ .put(`${this._serviceUser._account_id}/password.http`,
+ {generate: true})
+ .then(newPassword => {
+ this._generatedPassword = newPassword;
+ });
+ }
+
+ _closeOverlay() {
+ this.$.generatedPasswordOverlay.close();
+ }
+
+ _generatedPasswordOverlayClosed() {
+ this._generatedPassword = '';
+ }
+
+ _handleDelete() {
+ this._restApi.delete(`${this._serviceUser._account_id}/password.http`);
+ }
+}
+
+customElements.define(GrServiceUserHttpPassword.is, GrServiceUserHttpPassword);
diff --git a/src/main/resources/static/gr-serviceuser-http-password.html b/gr-serviceuser/gr-serviceuser-http-password_html.js
similarity index 69%
rename from src/main/resources/static/gr-serviceuser-http-password.html
rename to gr-serviceuser/gr-serviceuser-http-password_html.js
index 8cebbe5..b48855c 100644
--- a/src/main/resources/static/gr-serviceuser-http-password.html
+++ b/gr-serviceuser/gr-serviceuser-http-password_html.js
@@ -1,22 +1,21 @@
-<!--
-@license
-Copyright (C) 2019 The Android Open Source Project
+/**
+ * @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.
+ */
-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-http-password">
- <template>
+export const htmlTemplate = Polymer.html`
<style include="shared-styles">
.password {
font-family: var(--monospace-font-family);
@@ -76,6 +75,4 @@
on-click="_closeOverlay">Close</gr-button>
</div>
</gr-overlay>
- </template>
- <script src="gr-serviceuser-http-password.js"></script>
-</dom-module>
+`;
diff --git a/gr-serviceuser/gr-serviceuser-list.js b/gr-serviceuser/gr-serviceuser-list.js
new file mode 100644
index 0000000..a3c40c0
--- /dev/null
+++ b/gr-serviceuser/gr-serviceuser-list.js
@@ -0,0 +1,119 @@
+/**
+ * @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.
+ */
+
+import {htmlTemplate} from './gr-serviceuser-list_html.js';
+
+const NOT_FOUND_MESSAGE = 'Not Found';
+
+export class GrServiceUserList extends Polymer.GestureEventListeners(
+ Polymer.Element) {
+ /** @returns {?} template for this component */
+ static get template() { return htmlTemplate; }
+
+ /** @returns {string} name of the component */
+ static get is() { return 'gr-serviceuser-list'; }
+
+ /**
+ * Defines properties of the component
+ *
+ * @returns {?}
+ */
+ static get properties() {
+ return {
+ _serviceUsers: Array,
+ _loading: {
+ type: Boolean,
+ value: true,
+ },
+ };
+ }
+
+ static get behaviors() {
+ return [
+ Gerrit.ListViewBehavior,
+ ];
+ }
+
+ connectedCallback() {
+ super.connectedCallback();
+ this.dispatchEvent(
+ new CustomEvent(
+ 'title-change',
+ {detail: {title: 'Service Users'}, bubbles: true, composed: true}));
+ this._getServiceUsers();
+ }
+
+ _getServiceUsers() {
+ return this.plugin.restApi('/a/config/server/serviceuser~serviceusers/')
+ .get('')
+ .then(serviceUsers => {
+ if (!serviceUsers) {
+ this._serviceUsers = [];
+ return;
+ }
+ this._serviceUsers = Object.keys(serviceUsers)
+ .map(key => {
+ const serviceUser = serviceUsers[key];
+ serviceUser.username = key;
+ return serviceUser;
+ });
+ this._loading = false;
+ });
+ }
+
+ _computeLoadingClass(loading) {
+ return loading ? 'loading' : '';
+ }
+
+ _active(item) {
+ if (!item) {
+ return NOT_FOUND_MESSAGE;
+ }
+
+ return item.inactive === true ? 'Inactive' : 'Active';
+ }
+
+ _getCreator(item) {
+ if (!item || !item.created_by) {
+ return NOT_FOUND_MESSAGE;
+ }
+
+ if (item.created_by.username != undefined) {
+ return item.created_by.username;
+ }
+
+ if (item.created_by._account_id != -1) {
+ return item.created_by._account_id;
+ }
+
+ return NOT_FOUND_MESSAGE;
+ }
+
+ _getOwnerGroup(item) {
+ return item && item.owner ? item.owner.name : NOT_FOUND_MESSAGE;
+ }
+
+ _computeServiceUserUrl(id) {
+ return `${this.plugin.screenUrl()}/user/${id}`;
+ }
+
+ _createNewServiceUser() {
+ page.show(this.plugin.screenUrl() + '/create');
+ }
+}
+
+customElements.define(GrServiceUserList.is, GrServiceUserList);
diff --git a/src/main/resources/static/gr-serviceuser-list.html b/gr-serviceuser/gr-serviceuser-list_html.js
similarity index 67%
rename from src/main/resources/static/gr-serviceuser-list.html
rename to gr-serviceuser/gr-serviceuser-list_html.js
index e927d63..ae0ff2f 100644
--- a/src/main/resources/static/gr-serviceuser-list.html
+++ b/gr-serviceuser/gr-serviceuser-list_html.js
@@ -1,22 +1,21 @@
-<!--
-@license
-Copyright (C) 2019 The Android Open Source Project
+/**
+ * @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.
+ */
-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-list">
- <template>
+export const htmlTemplate = Polymer.html`
<style include="shared-styles"></style>
<style include="gr-table-styles"></style>
<style>
@@ -33,7 +32,7 @@
}
</style>
<div class="topHeader">
- <h2>Service Users</h2>
+ <h1 class="heading-1">Service Users</h1>
</div>
<div id="topContainer">
<div></div>
@@ -59,10 +58,10 @@
<th class="accountState topHeader">Account State</th>
</tr>
<tr id="loading"
- class$="loadingMsg [[computeLoadingClass(_loading)]]">
+ class$="loadingMsg [[_computeLoadingClass(_loading)]]">
<td>Loading...</td>
</tr>
- <tbody class$="[[computeLoadingClass(_loading)]]">
+ <tbody class$="[[_computeLoadingClass(_loading)]]">
<template is="dom-repeat"
items="[[_serviceUsers]]">
<tr class="table">
@@ -79,6 +78,4 @@
</template>
</tbody>
</table>
- </template>
- <script src="gr-serviceuser-list.js"></script>
-</dom-module>
+`;
diff --git a/gr-serviceuser/gr-serviceuser-ssh-panel.js b/gr-serviceuser/gr-serviceuser-ssh-panel.js
new file mode 100644
index 0000000..a2db9c0
--- /dev/null
+++ b/gr-serviceuser/gr-serviceuser-ssh-panel.js
@@ -0,0 +1,113 @@
+/**
+ * @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.
+ */
+
+import {htmlTemplate} from './gr-serviceuser-ssh-panel_html.js';
+
+class GrServiceUserSshPanel extends Polymer.GestureEventListeners(
+ Polymer.Element) {
+ /** @returns {?} template for this component */
+ static get template() { return htmlTemplate; }
+
+ /** @returns {string} name of the component */
+ static get is() { return 'gr-serviceuser-ssh-panel'; }
+
+ /**
+ * Defines properties of the component
+ *
+ * @returns {?}
+ */
+ static get properties() {
+ return {
+ _restApi: Object,
+ _serviceUser: Object,
+ _keys: Array,
+ /** @type {?} */
+ _keyToView: Object,
+ _newKey: {
+ type: String,
+ value: '',
+ },
+ _keysToRemove: {
+ type: Array,
+ value() { return []; },
+ },
+ };
+ }
+
+ 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();
+ }
+
+ _handleDeleteKey(e) {
+ const el = Polymer.dom(e).localTarget;
+ const index = parseInt(el.getAttribute('data-index'), 10);
+ this.push('_keysToRemove', this._keys[index]);
+
+ const promises = this._keysToRemove.map(key => {
+ this._restApi.delete(
+ `${this._serviceUser._account_id}/sshkeys/${key.seq}`);
+ });
+
+ return Promise.all(promises).then(() => {
+ this.splice('_keys', index, 1);
+ this._keysToRemove = [];
+ });
+ }
+
+ _handleAddKey() {
+ this.$.addButton.disabled = true;
+ this.$.newKey.disabled = true;
+ return this._restApi.post(`${this._serviceUser._account_id}/sshkeys`,
+ this._newKey.trim(), null, 'plain/text')
+ .then(key => {
+ this.push('_keys', key);
+ }).catch(() => {
+ this.$.addButton.disabled = false;
+ this.$.newKey.disabled = false;
+ });
+ }
+
+ _computeAddButtonDisabled(newKey) {
+ return !newKey.length;
+ }
+}
+
+customElements.define(GrServiceUserSshPanel.is, GrServiceUserSshPanel);
diff --git a/src/main/resources/static/gr-serviceuser-ssh-panel.html b/gr-serviceuser/gr-serviceuser-ssh-panel_html.js
similarity index 81%
rename from src/main/resources/static/gr-serviceuser-ssh-panel.html
rename to gr-serviceuser/gr-serviceuser-ssh-panel_html.js
index 2906a10..a9fb7d9 100644
--- a/src/main/resources/static/gr-serviceuser-ssh-panel.html
+++ b/gr-serviceuser/gr-serviceuser-ssh-panel_html.js
@@ -1,22 +1,21 @@
-<!--
-@license
-Copyright (C) 2019 The Android Open Source Project
+/**
+ * @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.
+ */
-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>
+export const htmlTemplate = Polymer.html`
<style include="shared-styles"></style>
<style include="gr-form-styles">
.statusHeader {
@@ -127,6 +126,4 @@
</fieldset>
</div>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
- </template>
- <script src="gr-serviceuser-ssh-panel.js"></script>
-</dom-module>
+`;
diff --git a/gr-serviceuser/gr-serviceuser.js b/gr-serviceuser/gr-serviceuser.js
new file mode 100644
index 0000000..c6624a6
--- /dev/null
+++ b/gr-serviceuser/gr-serviceuser.js
@@ -0,0 +1,41 @@
+/**
+ * @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.
+ */
+
+import {GrServiceUserList} from './gr-serviceuser-list.js';
+import {GrServiceUserDetail} from './gr-serviceuser-detail.js';
+import {GrServiceUserCreate} from './gr-serviceuser-create.js';
+
+Gerrit.install(plugin => {
+ plugin.restApi()
+ .getAccountCapabilities([
+ 'administrateServer',
+ 'serviceuser-createServiceUser'])
+ .then(capabilities => {
+ if (capabilities
+ && (capabilities.administrateServer
+ || capabilities['serviceuser-createServiceUser'])) {
+ plugin.screen('list', GrServiceUserList.is);
+ plugin.screen('user', GrServiceUserDetail.is);
+ plugin.screen('create', GrServiceUserCreate.is);
+ }
+ plugin.admin()
+ .addMenuLink(
+ 'Service Users',
+ '/x/serviceuser/list',
+ 'serviceuser-createServiceUser');
+ });
+});
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..aa7780e
--- /dev/null
+++ b/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "serviceuser",
+ "description": "Serviceuser plugin",
+ "browser": true,
+ "scripts": {
+ "safe_bazelisk": "if which bazelisk >/dev/null; then bazel_bin=bazelisk; else bazel_bin=bazel; fi && $bazel_bin",
+ "eslint": "npm run safe_bazelisk test :lint_test",
+ "eslintfix": "npm run safe_bazelisk run :lint_bin -- -- --fix $(pwd)"
+ },
+ "devDependencies": {},
+ "license": "Apache-2.0",
+ "private": true
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/GetOwner.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/GetOwner.java
index bf7227b..396b1dd 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/GetOwner.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/GetOwner.java
@@ -27,6 +27,8 @@
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.restapi.group.GroupJson;
import com.google.gerrit.server.restapi.group.GroupsCollection;
+import com.google.gerrit.server.util.ManualRequestContext;
+import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -36,23 +38,31 @@
private final GroupsCollection groups;
private final GroupJson json;
private final StorageCache storageCache;
+ private final OneOffRequestContext requestContext;
@Inject
- GetOwner(GroupsCollection groups, GroupJson json, StorageCache storageCache) {
+ GetOwner(
+ GroupsCollection groups,
+ GroupJson json,
+ StorageCache storageCache,
+ OneOffRequestContext requestContext) {
this.groups = groups;
this.json = json;
this.storageCache = storageCache;
+ this.requestContext = requestContext;
}
@Override
public Response<GroupInfo> apply(ServiceUserResource rsrc)
throws IOException, RestApiException, PermissionBackendException {
- String owner =
- storageCache.get().getString(USER, rsrc.getUser().getUserName().get(), KEY_OWNER);
- if (owner != null) {
- GroupDescription.Basic group =
- groups.parse(TopLevelResource.INSTANCE, IdString.fromDecoded(owner)).getGroup();
- return Response.<GroupInfo>ok(json.format(group));
+ try (ManualRequestContext ctx = requestContext.open()) {
+ String owner =
+ storageCache.get().getString(USER, rsrc.getUser().getUserName().get(), KEY_OWNER);
+ if (owner != null) {
+ GroupDescription.Basic group =
+ groups.parse(TopLevelResource.INSTANCE, IdString.fromDecoded(owner)).getGroup();
+ return Response.<GroupInfo>ok(json.format(group));
+ }
}
return Response.none();
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/HttpModule.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/HttpModule.java
index a99987b..650eba6 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/HttpModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/HttpModule.java
@@ -23,6 +23,6 @@
@Override
protected void configure() {
DynamicSet.bind(binder(), WebUiPlugin.class)
- .toInstance(new JavaScriptPlugin("gr-serviceuser.html"));
+ .toInstance(new JavaScriptPlugin("serviceuser.js"));
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/ValidateServiceUserCommits.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/ValidateServiceUserCommits.java
index b576b1c..6d4783e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/ValidateServiceUserCommits.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/ValidateServiceUserCommits.java
@@ -29,9 +29,12 @@
import java.util.List;
import java.util.Optional;
import org.eclipse.jgit.lib.PersonIdent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
@Singleton
class ValidateServiceUserCommits implements CommitValidationListener {
+ private static final Logger log = LoggerFactory.getLogger(ValidateServiceUserCommits.class);
private final ServiceUserResolver serviceUserResolver;
private final AccountCache accountCache;
@@ -71,6 +74,7 @@
}
}
} catch (RestApiException e) {
+ log.error(e.getMessage(), e);
throw new CommitValidationException(
"Internal error while checking for service user commits.", e);
}
diff --git a/src/main/resources/static/gr-serviceuser-create.js b/src/main/resources/static/gr-serviceuser-create.js
deleted file mode 100644
index df5d9a6..0000000
--- a/src/main/resources/static/gr-serviceuser-create.js
+++ /dev/null
@@ -1,150 +0,0 @@
-/**
- * @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-create',
- _legacyUndefinedCheck: true,
-
- properties: {
- _infoMessageEnabled: {
- type: Boolean,
- value: false,
- },
- _infoMessage: String,
- _successMessageEnabled: {
- type: Boolean,
- value: false,
- },
- _successMessage: String,
- _newUsername: String,
- _emailEnabled: {
- type: Boolean,
- value: false,
- },
- _newEmail: String,
- _newKey: String,
- _dataValid: {
- type: Boolean,
- value: false,
- },
- _isAdding: {
- type: Boolean,
- value: false,
- },
- _enableButton: {
- type: Boolean,
- value: false,
- },
- _accountId: String,
- },
-
- attached() {
- this._getConfig();
- },
-
- _forwardToDetails() {
- page.show(
- this.plugin.screenUrl()
- + '/user/'
- + this._accountId);
- },
-
- _getConfig() {
- return this.plugin.restApi('/config/server/serviceuser~config/').get('')
- .then(config => {
- if (!config) {
- return;
- }
-
- if (config.info && config.info != '') {
- this._infoMessageEnabled = true;
- this._infoMessage = config.info;
- this.$.infoMessage.innerHTML = this._infoMessage;
- }
-
- if (config.on_success && config.on_success != '') {
- this._successMessageEnabled = true;
- this._successMessage = config.on_success;
- this.$.successMessage.innerHTML = this._successMessage;
- }
-
- this._emailEnabled = config.allow_email;
- });
- },
-
- _validateData() {
- this._dataValid = this._validateName(this._newUsername)
- && this._validateEmail(this._newEmail)
- && this._validateKey(this._newKey);
- this._computeButtonEnabled();
- },
-
- _validateName(username) {
- if (username && username.trim().length > 0) {
- return true;
- }
-
- return false;
- },
-
- _validateEmail(email) {
- if (!email || email.trim().length == 0 || email.includes('@')) {
- return true;
- }
-
- return false;
- },
-
- _validateKey(key) {
- if (!key || !key.trim()) {
- return false;
- }
-
- return true;
- },
-
- _computeButtonEnabled() {
- this._enableButton = this._dataValid && !this._isAdding;
- },
-
- _handleCreateServiceUser() {
- this._isAdding = true;
- this._computeButtonEnabled();
- const body = {
- ssh_key: this._newKey.trim(),
- email: this._newEmail ? this._newEmail.trim() : null,
- };
- return this.plugin.restApi('/config/server/serviceuser~serviceusers/')
- .post(this._newUsername, body)
- .then(response => {
- this._accountId = response._account_id;
- if (this._successMessage) {
- this.$.successDialogOverlay.open();
- } else {
- this._forwardToDetails();
- }
- }).catch(response => {
- this.fire('show-error', {message: response});
- this._isAdding = false;
- this._computeButtonEnabled();
- });
- },
- });
-})();
diff --git a/src/main/resources/static/gr-serviceuser-detail.js b/src/main/resources/static/gr-serviceuser-detail.js
deleted file mode 100644
index 95aded2..0000000
--- a/src/main/resources/static/gr-serviceuser-detail.js
+++ /dev/null
@@ -1,345 +0,0 @@
-// 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,
- },
- _statusButtonText: {
- type: String,
- value: 'Activate',
- },
- _prefsChanged: {
- type: Boolean,
- value: false,
- },
- _changingPrefs: {
- type: Boolean,
- value: false,
- },
- _isAdmin: {
- type: Boolean,
- value: false,
- },
- _allowEmail: {
- type: Boolean,
- value: false,
- },
- _allowOwner: {
- type: Boolean,
- value: false,
- },
- _allowHttpPassword: {
- type: Boolean,
- value: false,
- },
- _newFullName: String,
- _newEmail: String,
- _availableOwners: Array,
- _newOwner: String,
- _ownerChangeWarning: String,
- _query: {
- type: Function,
- value() {
- return this._getGroupSuggestions.bind(this);
- },
- },
- },
-
- behaviors: [
- Gerrit.ListViewBehavior,
- ],
-
- attached() {
- this._extractUserId();
- this._loadServiceUser();
- },
-
- _loadServiceUser() {
- if (!this._serviceUserId) { return; }
-
- const promises = [];
-
- promises.push(this._getPluginConfig());
- promises.push(this._getServiceUser());
-
- Promise.all(promises).then(() => {
- this.$.sshEditor.loadData(this._restApi, this._serviceUser);
- this.$.httpPass.loadData(this._restApi, this._serviceUser);
-
- this.fire('title-change', {title: this._serviceUser.name});
- this._computeStatusButtonText();
- this._loading = false;
- this._newFullName = this._serviceUser.name;
- this._newEmail = this._serviceUser.email;
- });
- },
-
- _computeLoadingClass(loading) {
- return loading ? 'loading' : '';
- },
-
- _extractUserId() {
- this._serviceUserId = this.baseURI.split('/').pop();
- },
-
- _getPermissions() {
- return this.plugin.restApi('/accounts/self/capabilities/').get('')
- .then(capabilities => {
- this._isAdmin = capabilities && capabilities.administrateServer;
- });
- },
-
- _getPluginConfig() {
- return Promise.resolve(this._getPermissions()).then(() => {
- this.plugin.restApi('/config/server/serviceuser~config/').get('')
- .then(config => {
- if (!config) {
- return;
- }
- this._allowEmail = config.allow_email || this._isAdmin;
- this._allowOwner = config.allow_owner || this._isAdmin;
- this._allowHttpPassword = config.allow_http_password
- || this._isAdmin;
- });
- });
- },
-
- _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';
- },
-
- _computeStatusButtonText() {
- if (!this._serviceUser) {
- return;
- }
-
- this._statusButtonText = this._serviceUser.inactive === true
- ? 'Activate'
- : 'Deactivate';
- },
-
- _toggleStatus() {
- if (this._serviceUser.inactive === true) {
- this._restApi.put(`${this._serviceUser._account_id}/active`)
- .then(() => {
- this._loadServiceUser();
- });
- } else {
- this._restApi.delete(`${this._serviceUser._account_id}/active`)
- .then(() => {
- this._loadServiceUser();
- });
- }
- },
-
- _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;
- },
-
- _isEmailValid(email) {
- if (!email) {
- return false;
- }
- return email.includes('@');
- },
-
- _getGroupSuggestions(input) {
- let query;
- if (!input || input === this._getOwnerGroup(this._serviceUser)) {
- query = '';
- } else {
- query = `?suggest=${input}`;
- }
-
- return this.plugin.restApi('/a/groups/').get(query)
- .then(response => {
- const groups = [];
- for (const key in response) {
- if (!response.hasOwnProperty(key)) { continue; }
- groups.push({
- name: key,
- value: decodeURIComponent(response[key].id),
- });
- }
- this._availableOwners = groups;
- return groups;
- });
- },
-
- _isOwnerValid(owner) {
- if (!owner) {
- return false;
- }
-
- return this._getOwnerName(owner);
- },
-
- _isNewOwner() {
- return this._getOwnerName(this._newOwner)
- === this._getOwnerGroup(this._serviceUser);
- },
-
- _getOwnerName(id) {
- return this._availableOwners.find(o => { return o.value === id; }).name;
- },
-
- _computeOwnerWarning() {
- let message = 'If ';
- message += this._getOwnerGroup(this._serviceUser) != NOT_FOUND_MESSAGE
- ? 'the owner group is changed' : 'an owner group is set';
- message += ' only members of the ';
- message += this._getOwnerGroup(this._serviceUser) != NOT_FOUND_MESSAGE
- ? 'new ' : '';
- message += 'owner group can see and administrate the service user.';
- message += this._getOwnerGroup(this._serviceUser) != NOT_FOUND_MESSAGE
- ? '' : ' The creator of the service user can no'
- + ' longer see and administrate the service user if she/he'
- + ' is not member of the owner group.';
- this._ownerChangeWarning = message;
- },
-
- _computePrefsChanged() {
- if (this.loading || this._changingPrefs) {
- return;
- }
-
- if (!this._newOwner && !this._newEmail && !this._newFullName) {
- this._prefsChanged = false;
- return;
- }
-
- if (this._newEmail && !this._isEmailValid(this._newEmail)) {
- this._prefsChanged = false;
- return;
- }
-
- if (this._newOwner
- && (this._isNewOwner() || !this._isOwnerValid(this._newOwner))) {
- this._prefsChanged = false;
- return;
- }
-
- if (this._newOwner) {
- this._computeOwnerWarning();
- }
-
- this._prefsChanged = true;
- },
-
- _applyNewFullName() {
- return this._restApi
- .put(`${this._serviceUser._account_id}/name`,
- {name: this._newFullName})
- .then(() => {
- this.$.serviceUserFullNameInput.value = '';
- });
- },
-
- _applyNewEmail(email) {
- if (!this._isEmailValid(email)) {
- return;
- }
- return this._restApi
- .put(`${this._serviceUser._account_id}/email`, {email})
- .then(() => {
- this.$.serviceUserEmailInput.value = '';
- });
- },
-
- _applyNewOwner(owner) {
- if (this._isNewOwner() || !this._isOwnerValid(this._newOwner)) {
- return;
- }
- return this._restApi
- .put(`${this._serviceUser._account_id}/owner`, {group: owner})
- .then(() => {
- this.$.serviceUserOwnerInput.text = this._getOwnerGroup(
- this._serviceUser);
- });
- },
-
- _handleSavePreferences() {
- const promises = [];
- this._changingPrefs = true;
-
- if (this._newFullName) {
- promises.push(this._applyNewFullName());
- }
-
- if (this._newEmail) {
- promises.push(this._applyNewEmail(this._newEmail));
- }
-
- if (this._newOwner) {
- promises.push(this._applyNewOwner(this._newOwner));
- }
-
- Promise.all(promises).then(() => {
- this._changingPrefs = false;
- this._prefsChanged = false;
- this._ownerChangeWarning = '';
- this._loadServiceUser();
- });
- },
- });
-})();
diff --git a/src/main/resources/static/gr-serviceuser-http-password.js b/src/main/resources/static/gr-serviceuser-http-password.js
deleted file mode 100644
index 87e1543..0000000
--- a/src/main/resources/static/gr-serviceuser-http-password.js
+++ /dev/null
@@ -1,60 +0,0 @@
-/**
- * @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.
- */
-
-(function() {
- 'use strict';
-
- Polymer({
- is: 'gr-serviceuser-http-password',
- _legacyUndefinedCheck: true,
-
- properties: {
- _restApi: Object,
- _serviceUser: Object,
- _generatedPassword: String,
- _passwordUrl: String,
- },
-
- loadData(restApi, serviceUser) {
- this._restApi = restApi;
- this._serviceUser = serviceUser;
- },
-
- _handleGenerateTap() {
- this._generatedPassword = 'Generating...';
- this.$.generatedPasswordOverlay.open();
- this._restApi
- .put(`${this._serviceUser._account_id}/password.http`,
- {generate: true})
- .then(newPassword => {
- this._generatedPassword = newPassword;
- });
- },
-
- _closeOverlay() {
- this.$.generatedPasswordOverlay.close();
- },
-
- _generatedPasswordOverlayClosed() {
- this._generatedPassword = '';
- },
-
- _handleDelete() {
- this._restApi.delete(`${this._serviceUser._account_id}/password.http`);
- },
- });
-})();
diff --git a/src/main/resources/static/gr-serviceuser-list.js b/src/main/resources/static/gr-serviceuser-list.js
deleted file mode 100644
index 8f35dd4..0000000
--- a/src/main/resources/static/gr-serviceuser-list.js
+++ /dev/null
@@ -1,95 +0,0 @@
-// 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-list',
- _legacyUndefinedCheck: true,
-
- properties: {
- _serviceUsers: Array,
- _loading: {
- type: Boolean,
- value: true,
- },
- },
-
- behaviors: [
- Gerrit.ListViewBehavior,
- ],
-
- attached() {
- this.fire('title-change', {title: 'Service Users'});
- this._getServiceUsers();
- },
-
- _getServiceUsers() {
- return this.plugin.restApi('/config/server/serviceuser~serviceusers/')
- .get('')
- .then(serviceUsers => {
- if (!serviceUsers) {
- this._serviceUsers = [];
- return;
- }
- this._serviceUsers = Object.keys(serviceUsers)
- .map(key => {
- const serviceUser = serviceUsers[key];
- serviceUser.username = key;
- return serviceUser;
- });
- this._loading = false;
- });
- },
-
- _active(item) {
- if (!item) {
- return NOT_FOUND_MESSAGE;
- }
-
- return item.inactive === true ? 'Inactive' : 'Active';
- },
-
- _getCreator(item) {
- if (!item || !item.created_by) {
- return NOT_FOUND_MESSAGE;
- }
-
- if (item.created_by.username != undefined) {
- return item.created_by.username;
- }
-
- if (item.created_by._account_id != -1) {
- return item.created_by._account_id;
- }
-
- return NOT_FOUND_MESSAGE;
- },
-
- _getOwnerGroup(item) {
- return item && item.owner ? item.owner.name : NOT_FOUND_MESSAGE;
- },
-
- _computeServiceUserUrl(id) {
- return `${this.plugin.screenUrl()}/user/${id}`;
- },
-
- _createNewServiceUser() {
- page.show(this.plugin.screenUrl() + '/create');
- },
- });
-})();
diff --git a/src/main/resources/static/gr-serviceuser-ssh-panel.js b/src/main/resources/static/gr-serviceuser-ssh-panel.js
deleted file mode 100644
index e1e8dd0..0000000
--- a/src/main/resources/static/gr-serviceuser-ssh-panel.js
+++ /dev/null
@@ -1,101 +0,0 @@
-/**
- * @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,
- _newKey: {
- type: String,
- value: '',
- },
- _keysToRemove: {
- type: Array,
- value() { return []; },
- },
- },
-
- 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();
- },
-
- _handleDeleteKey(e) {
- const el = Polymer.dom(e).localTarget;
- const index = parseInt(el.getAttribute('data-index'), 10);
- this.push('_keysToRemove', this._keys[index]);
-
- const promises = this._keysToRemove.map(key => {
- this._restApi.delete(`${this._serviceUser._account_id}/sshkeys/${key.seq}`);
- });
-
- return Promise.all(promises).then(() => {
- this.splice('_keys', index, 1);
- this._keysToRemove = [];
- });
- },
-
- _handleAddKey() {
- this.$.addButton.disabled = true;
- this.$.newKey.disabled = true;
- return this._restApi.post(`${this._serviceUser._account_id}/sshkeys`,
- this._newKey.trim(), null, 'plain/text')
- .then(key => {
- this.push('_keys', key);
- }).catch(() => {
- this.$.addButton.disabled = false;
- this.$.newKey.disabled = false;
- });
- },
-
- _computeAddButtonDisabled(newKey) {
- return !newKey.length;
- },
- });
-})();
diff --git a/src/main/resources/static/gr-serviceuser.html b/src/main/resources/static/gr-serviceuser.html
deleted file mode 100644
index 92714a2..0000000
--- a/src/main/resources/static/gr-serviceuser.html
+++ /dev/null
@@ -1,46 +0,0 @@
-<!--
-@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-list.html">
-<link rel="import"
- href="./gr-serviceuser-detail.html">
-<link rel="import"
- href="./gr-serviceuser-create.html">
-
-<dom-module id="gr-serviceuser">
- <script>
- Gerrit.install(plugin => {
- plugin.restApi('/accounts/self/capabilities/').get('')
- .then(capabilities => {
- if (capabilities
- && (capabilities.administrateServer
- || capabilities['serviceuser-createServiceUser'])) {
- plugin.screen('list', 'gr-serviceuser-list');
- plugin.screen('user', 'gr-serviceuser-detail');
- plugin.screen('create', 'gr-serviceuser-create');
- }
- plugin.admin()
- .addMenuLink(
- 'Service Users',
- '/x/serviceuser/list',
- 'serviceuser-createServiceUser');
- });
- });
- </script>
-</dom-module>