PolyGerrit: Add Identities to settings screen
The identities section shows the user what is linked to your id -- external id (ldap, oauth and more), and linked emails. Bug: Issue 4315 Change-Id: Idd271609bc8ae02a935f60b398375afe9bfae613
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
|
||||
const DETAIL_TYPES = {
|
||||
BRANCHES: 'branches',
|
||||
ID: 'id',
|
||||
TAGS: 'tags',
|
||||
};
|
||||
|
||||
@@ -56,6 +57,8 @@
|
||||
return 'Branch';
|
||||
} else if (detailType === DETAIL_TYPES.TAGS) {
|
||||
return 'Tag';
|
||||
} else if (detailType === DETAIL_TYPES.ID) {
|
||||
return 'ID';
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@@ -0,0 +1,84 @@
|
||||
<!--
|
||||
Copyright (C) 2017 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="../../../bower_components/polymer/polymer.html">
|
||||
<link rel="import" href="../../../styles/shared-styles.html">
|
||||
<link rel="import" href="../../../styles/gr-form-styles.html">
|
||||
<link rel="import" href="../../admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.html">
|
||||
<link rel="import" href="../../shared/gr-button/gr-button.html">
|
||||
<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
|
||||
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
||||
|
||||
<dom-module id="gr-identities">
|
||||
<template>
|
||||
<style include="shared-styles"></style>
|
||||
<style include="gr-form-styles">
|
||||
td {
|
||||
width: 5em;
|
||||
}
|
||||
.deleteButton {
|
||||
float: right;
|
||||
}
|
||||
.deleteButton:not(.show) {
|
||||
display: none;
|
||||
}
|
||||
.statusColumn {
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
<div class="gr-form-styles">
|
||||
<table id="identities">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="statusHeader">Status</th>
|
||||
<th class="emailAddressHeader">Email Address</th>
|
||||
<th class="identityHeader">Identity</th>
|
||||
<th class="deleteHeader"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template is="dom-repeat" items="[[_identities]]" filter="filterIdentities">
|
||||
<tr>
|
||||
<td class$="statusColumn">
|
||||
[[_computeIsTrusted(item.trusted)]]
|
||||
</td>
|
||||
<td>[[item.email_address]]</td>
|
||||
<td>[[_computeIdentity(item.identity)]]</td>
|
||||
<td>
|
||||
<gr-button
|
||||
link
|
||||
class$="deleteButton [[_computeHideDeleteClass(item.can_delete)]]"
|
||||
on-tap="_handleDeleteItem">
|
||||
Delete
|
||||
</gr-button>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<gr-overlay id="overlay" with-backdrop>
|
||||
<gr-confirm-delete-item-dialog
|
||||
class="confirmDialog"
|
||||
on-confirm="_handleDeleteItemConfirm"
|
||||
on-cancel="_handleConfirmDialogCancel"
|
||||
item="[[_idName]]"
|
||||
item-type="id"></gr-confirm-delete-item-dialog>
|
||||
</gr-overlay>
|
||||
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
|
||||
</template>
|
||||
<script src="gr-identities.js"></script>
|
||||
</dom-module>
|
@@ -0,0 +1,64 @@
|
||||
// Copyright (C) 2017 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-identities',
|
||||
|
||||
properties: {
|
||||
_identities: Object,
|
||||
_idName: String,
|
||||
},
|
||||
|
||||
loadData() {
|
||||
return this.$.restAPI.getExternalIds().then(id => {
|
||||
this._identities = id;
|
||||
});
|
||||
},
|
||||
|
||||
_computeIdentity(id) {
|
||||
return id && id.startsWith('mailto:') ? '' : id;
|
||||
},
|
||||
|
||||
_computeHideDeleteClass(canDelete) {
|
||||
return canDelete ? 'show' : '';
|
||||
},
|
||||
|
||||
_handleDeleteItemConfirm() {
|
||||
this.$.overlay.close();
|
||||
return this.$.restAPI.deleteAccountIdentity([this._idName])
|
||||
.then(() => { this.loadData(); });
|
||||
},
|
||||
|
||||
_handleConfirmDialogCancel() {
|
||||
this.$.overlay.close();
|
||||
},
|
||||
|
||||
_handleDeleteItem(e) {
|
||||
const name = e.model.get('item.identity');
|
||||
if (!name) { return; }
|
||||
this._idName = name;
|
||||
this.$.overlay.open();
|
||||
},
|
||||
|
||||
_computeIsTrusted(item) {
|
||||
return item ? '' : 'Untrusted';
|
||||
},
|
||||
|
||||
filterIdentities(item) {
|
||||
return !item.identity.startsWith('username:');
|
||||
},
|
||||
});
|
||||
})();
|
@@ -0,0 +1,125 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright (C) 2017 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.
|
||||
-->
|
||||
|
||||
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
|
||||
<title>gr-identities</title>
|
||||
|
||||
<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
|
||||
<script src="../../../bower_components/web-component-tester/browser.js"></script>
|
||||
<link rel="import" href="../../../test/common-test-setup.html"/>
|
||||
<link rel="import" href="gr-identities.html">
|
||||
|
||||
<script>void(0);</script>
|
||||
|
||||
<test-fixture id="basic">
|
||||
<template>
|
||||
<gr-identities></gr-identities>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<script>
|
||||
suite('gr-identities tests', () => {
|
||||
let element;
|
||||
let sandbox;
|
||||
const ids = [
|
||||
{
|
||||
identity: 'username:john',
|
||||
email_address: 'john.doe@example.com',
|
||||
trusted: true,
|
||||
}, {
|
||||
identity: 'gerrit:gerrit',
|
||||
email_address: 'gerrit@example.com',
|
||||
}, {
|
||||
identity: 'mailto:gerrit2@example.com',
|
||||
email_address: 'gerrit2@example.com',
|
||||
trusted: true,
|
||||
can_delete: true,
|
||||
},
|
||||
];
|
||||
|
||||
setup(done => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
stub('gr-rest-api-interface', {
|
||||
getExternalIds() { return Promise.resolve(ids); },
|
||||
});
|
||||
|
||||
element = fixture('basic');
|
||||
|
||||
element.loadData().then(() => { flush(done); });
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
test('renders', () => {
|
||||
const rows = Polymer.dom(element.root).querySelectorAll('tbody tr');
|
||||
|
||||
assert.equal(rows.length, 2);
|
||||
|
||||
const nameCells = rows.map(row =>
|
||||
row.querySelectorAll('td')[2].textContent
|
||||
);
|
||||
|
||||
assert.equal(nameCells[0], 'gerrit:gerrit');
|
||||
assert.equal(nameCells[1], '');
|
||||
});
|
||||
|
||||
test('renders email', () => {
|
||||
const rows = Polymer.dom(element.root).querySelectorAll('tbody tr');
|
||||
|
||||
assert.equal(rows.length, 2);
|
||||
|
||||
const nameCells = rows.map(row =>
|
||||
row.querySelectorAll('td')[1].textContent
|
||||
);
|
||||
|
||||
assert.equal(nameCells[0], 'gerrit@example.com');
|
||||
assert.equal(nameCells[1], 'gerrit2@example.com');
|
||||
});
|
||||
|
||||
test('_computeIdentity', () => {
|
||||
assert.equal(
|
||||
element._computeIdentity(ids[0].identity), 'username:john');
|
||||
assert.equal(element._computeIdentity(ids[2].identity), '');
|
||||
});
|
||||
|
||||
test('filterIdentities', () => {
|
||||
assert.isFalse(element.filterIdentities(ids[0]));
|
||||
|
||||
assert.isTrue(element.filterIdentities(ids[1]));
|
||||
});
|
||||
|
||||
test('delete id', done => {
|
||||
element._idName = 'mailto:gerrit2@example.com';
|
||||
const loadDataStub = sandbox.stub(element, 'loadData');
|
||||
element._handleDeleteItemConfirm().then(() => {
|
||||
assert.isTrue(loadDataStub.called);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('_handleDeleteItem opens modal', () => {
|
||||
const deleteBtn =
|
||||
Polymer.dom(element.root).querySelector('.deleteButton');
|
||||
const deleteItem = sandbox.stub(element, '_handleDeleteItem');
|
||||
MockInteractions.tap(deleteBtn);
|
||||
assert.isTrue(deleteItem.called);
|
||||
});
|
||||
});
|
||||
</script>
|
@@ -32,6 +32,7 @@ limitations under the License.
|
||||
<link rel="import" href="../gr-email-editor/gr-email-editor.html">
|
||||
<link rel="import" href="../gr-group-list/gr-group-list.html">
|
||||
<link rel="import" href="../gr-http-password/gr-http-password.html">
|
||||
<link rel="import" href="../gr-identities/gr-identities.html">
|
||||
<link rel="import" href="../gr-menu-editor/gr-menu-editor.html">
|
||||
<link rel="import" href="../gr-ssh-editor/gr-ssh-editor.html">
|
||||
<link rel="import" href="../gr-watched-projects-editor/gr-watched-projects-editor.html">
|
||||
@@ -71,6 +72,7 @@ limitations under the License.
|
||||
SSH Keys
|
||||
</a></li>
|
||||
<li><a href="#Groups">Groups</a></li>
|
||||
<li><a href="#Identities">Identities</a></li>
|
||||
<template is="dom-if" if="[[_serverConfig.auth.use_contributor_agreements]]">
|
||||
<li>
|
||||
<a href="#Agreements">Agreements</a>
|
||||
@@ -399,6 +401,10 @@ limitations under the License.
|
||||
<fieldset>
|
||||
<gr-group-list id="groupList"></gr-group-list>
|
||||
</fieldset>
|
||||
<h2 id="Identities">Identities</h2>
|
||||
<fieldset>
|
||||
<gr-identities id="identities"></gr-identities>
|
||||
</fieldset>
|
||||
<template is="dom-if" if="[[_serverConfig.auth.use_contributor_agreements]]">
|
||||
<h2 id="Agreements">Agreements</h2>
|
||||
<fieldset>
|
||||
|
@@ -145,6 +145,7 @@
|
||||
this.$.watchedProjectsEditor.loadData(),
|
||||
this.$.groupList.loadData(),
|
||||
this.$.httpPass.loadData(),
|
||||
this.$.identities.loadData(),
|
||||
];
|
||||
|
||||
promises.push(this.$.restAPI.getPreferences().then(prefs => {
|
||||
|
@@ -486,6 +486,15 @@
|
||||
});
|
||||
},
|
||||
|
||||
getExternalIds() {
|
||||
return this.fetchJSON('/accounts/self/external.ids');
|
||||
},
|
||||
|
||||
deleteAccountIdentity(id) {
|
||||
return this.send('POST', '/accounts/self/external.ids:delete', id)
|
||||
.then(response => this.getResponseObject(response));
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} userId the ID of the user usch as an email address.
|
||||
* @return {!Promise<!Object>}
|
||||
|
@@ -120,6 +120,7 @@ limitations under the License.
|
||||
'settings/gr-email-editor/gr-email-editor_test.html',
|
||||
'settings/gr-group-list/gr-group-list_test.html',
|
||||
'settings/gr-http-password/gr-http-password_test.html',
|
||||
'settings/gr-identities/gr-identities_test.html',
|
||||
'settings/gr-menu-editor/gr-menu-editor_test.html',
|
||||
'settings/gr-registration-dialog/gr-registration-dialog_test.html',
|
||||
'settings/gr-settings-view/gr-settings-view_test.html',
|
||||
|
Reference in New Issue
Block a user