Show and allow modification of exclusive bit in gr-permission
Bug: Issue 8034 Change-Id: Ia66c8308c242e7f5a09dc796cbd02944a3b02aa6
This commit is contained in:
@@ -16,6 +16,7 @@ limitations under the License.
|
|||||||
|
|
||||||
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
||||||
<link rel="import" href="../../../behaviors/gr-access-behavior/gr-access-behavior.html">
|
<link rel="import" href="../../../behaviors/gr-access-behavior/gr-access-behavior.html">
|
||||||
|
<link rel="import" href="../../../bower_components/paper-toggle-button/paper-toggle-button.html">
|
||||||
<link rel="import" href="../../../styles/gr-form-styles.html">
|
<link rel="import" href="../../../styles/gr-form-styles.html">
|
||||||
<link rel="import" href="../../../styles/gr-menu-page-styles.html">
|
<link rel="import" href="../../../styles/gr-menu-page-styles.html">
|
||||||
<link rel="import" href="../../../styles/shared-styles.html">
|
<link rel="import" href="../../../styles/shared-styles.html">
|
||||||
@@ -52,8 +53,13 @@ limitations under the License.
|
|||||||
#removeBtn {
|
#removeBtn {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
.right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
.editing #removeBtn {
|
.editing #removeBtn {
|
||||||
display: block;
|
display: block;
|
||||||
|
margin-left: 1.5em;
|
||||||
}
|
}
|
||||||
.editing #addRule {
|
.editing #addRule {
|
||||||
display: block;
|
display: block;
|
||||||
@@ -82,10 +88,17 @@ limitations under the License.
|
|||||||
<div id="mainContainer">
|
<div id="mainContainer">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<span class="title">[[name]]</span>
|
<span class="title">[[name]]</span>
|
||||||
|
<div class="right">
|
||||||
|
<paper-toggle-button
|
||||||
|
id="exclusiveToggle"
|
||||||
|
checked="{{permission.value.exclusive}}"
|
||||||
|
on-change="_handleValueChange"
|
||||||
|
disabled$="[[!editing]]"></paper-toggle-button>Exclusive
|
||||||
<gr-button
|
<gr-button
|
||||||
link
|
link
|
||||||
id="removeBtn"
|
id="removeBtn"
|
||||||
on-tap="_handleRemovePermission">Remove</gr-button>
|
on-tap="_handleRemovePermission">Remove</gr-button>
|
||||||
|
</div>
|
||||||
</div><!-- end header -->
|
</div><!-- end header -->
|
||||||
<div class="rules">
|
<div class="rules">
|
||||||
<template
|
<template
|
||||||
|
|||||||
@@ -58,6 +58,7 @@
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
|
_originalExclusiveValue: Boolean,
|
||||||
},
|
},
|
||||||
|
|
||||||
behaviors: [
|
behaviors: [
|
||||||
@@ -68,6 +69,26 @@
|
|||||||
'_handleRulesChanged(_rules.splices)',
|
'_handleRulesChanged(_rules.splices)',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
listeners: {
|
||||||
|
'access-saved': '_handleAccessSaved',
|
||||||
|
},
|
||||||
|
|
||||||
|
ready() {
|
||||||
|
this._setupValues();
|
||||||
|
},
|
||||||
|
|
||||||
|
_setupValues() {
|
||||||
|
if (!this.permission) { return; }
|
||||||
|
this._originalExclusiveValue = !!this.permission.value.exclusive;
|
||||||
|
Polymer.dom.flush();
|
||||||
|
},
|
||||||
|
|
||||||
|
_handleAccessSaved() {
|
||||||
|
// Set a new 'original' value to keep track of after the value has been
|
||||||
|
// saved.
|
||||||
|
this._setupValues();
|
||||||
|
},
|
||||||
|
|
||||||
_handleEditingChanged(editing, editingOld) {
|
_handleEditingChanged(editing, editingOld) {
|
||||||
// Ignore when editing gets set initially.
|
// Ignore when editing gets set initially.
|
||||||
if (!editingOld) { return; }
|
if (!editingOld) { return; }
|
||||||
@@ -76,9 +97,19 @@
|
|||||||
this._deleted = false;
|
this._deleted = false;
|
||||||
this._groupFilter = '';
|
this._groupFilter = '';
|
||||||
this._rules = this._rules.filter(rule => !rule.value.added);
|
this._rules = this._rules.filter(rule => !rule.value.added);
|
||||||
|
|
||||||
|
// Restore exclusive bit to original.
|
||||||
|
this.set(['permission', 'value', 'exclusive'],
|
||||||
|
this._originalExclusiveValue);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_handleValueChange() {
|
||||||
|
this.permission.value.modified = true;
|
||||||
|
// Allows overall access page to know a change has been made.
|
||||||
|
this.dispatchEvent(new CustomEvent('access-modified', {bubbles: true}));
|
||||||
|
},
|
||||||
|
|
||||||
_handleRemovePermission() {
|
_handleRemovePermission() {
|
||||||
this._deleted = true;
|
this._deleted = true;
|
||||||
this.permission.value.deleted = true;
|
this.permission.value.deleted = true;
|
||||||
|
|||||||
@@ -288,6 +288,7 @@ limitations under the License.
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
element._setupValues();
|
||||||
flushAsynchronousOperations();
|
flushAsynchronousOperations();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -326,6 +327,32 @@ limitations under the License.
|
|||||||
assert.isFalse(element.$.permission.classList.contains('deleted'));
|
assert.isFalse(element.$.permission.classList.contains('deleted'));
|
||||||
assert.isFalse(element._deleted);
|
assert.isFalse(element._deleted);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('modify a permission', () => {
|
||||||
|
element.editing = true;
|
||||||
|
element.name = 'Priority';
|
||||||
|
element.section = 'refs/*';
|
||||||
|
|
||||||
|
assert.isFalse(element._originalExclusiveValue);
|
||||||
|
assert.isNotOk(element.permission.value.modified);
|
||||||
|
MockInteractions.tap(element.$.exclusiveToggle);
|
||||||
|
flushAsynchronousOperations();
|
||||||
|
assert.isTrue(element.permission.value.exclusive);
|
||||||
|
assert.isTrue(element.permission.value.modified);
|
||||||
|
assert.isFalse(element._originalExclusiveValue);
|
||||||
|
element.editing = false;
|
||||||
|
assert.isFalse(element.permission.value.exclusive);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('_handleValueChange', () => {
|
||||||
|
const modifiedHandler = sandbox.stub();
|
||||||
|
element.permission = {value: {rules: {}}};
|
||||||
|
element.addEventListener('access-modified', modifiedHandler);
|
||||||
|
assert.isNotOk(element.permission.value.modified);
|
||||||
|
element._handleValueChange();
|
||||||
|
assert.isTrue(element.permission.value.modified);
|
||||||
|
assert.isTrue(modifiedHandler.called);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -76,30 +76,6 @@ limitations under the License.
|
|||||||
'Code-Review': {},
|
'Code-Review': {},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const repoAccessInput = {
|
|
||||||
add: {
|
|
||||||
'refs/*': {
|
|
||||||
permissions: {
|
|
||||||
owner: {
|
|
||||||
rules: {
|
|
||||||
123: {action: 'DENY', modified: true},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
remove: {
|
|
||||||
'refs/*': {
|
|
||||||
permissions: {
|
|
||||||
owner: {
|
|
||||||
rules: {
|
|
||||||
123: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
setup(() => {
|
setup(() => {
|
||||||
sandbox = sinon.sandbox.create();
|
sandbox = sinon.sandbox.create();
|
||||||
element = fixture('basic');
|
element = fixture('basic');
|
||||||
@@ -126,14 +102,13 @@ limitations under the License.
|
|||||||
name: 'Create Account',
|
name: 'Create Account',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const accessStub = sandbox.stub(element.$.restAPI,
|
const accessStub = sandbox.stub(element.$.restAPI,
|
||||||
'getRepoAccessRights');
|
'getRepoAccessRights');
|
||||||
|
|
||||||
|
accessStub.withArgs('New Repo').returns(
|
||||||
accessStub.withArgs('New Repo').returns(Promise.resolve(accessRes));
|
Promise.resolve(JSON.parse(JSON.stringify(accessRes))));
|
||||||
accessStub.withArgs('Another New Repo')
|
accessStub.withArgs('Another New Repo')
|
||||||
.returns(Promise.resolve(accessRes2));
|
.returns(Promise.resolve(JSON.parse(JSON.stringify(accessRes2))));
|
||||||
const capabilitiesStub = sandbox.stub(element.$.restAPI,
|
const capabilitiesStub = sandbox.stub(element.$.restAPI,
|
||||||
'getCapabilities');
|
'getCapabilities');
|
||||||
capabilitiesStub.returns(Promise.resolve(capabilitiesRes));
|
capabilitiesStub.returns(Promise.resolve(capabilitiesRes));
|
||||||
@@ -168,26 +143,13 @@ limitations under the License.
|
|||||||
name: 'Access Database',
|
name: 'Access Database',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const accessRes = {
|
|
||||||
local: {
|
|
||||||
GLOBAL_CAPABILITIES: {
|
|
||||||
permissions: {
|
|
||||||
accessDatabase: {
|
|
||||||
rules: {
|
|
||||||
123: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const repoRes = {
|
const repoRes = {
|
||||||
labels: {
|
labels: {
|
||||||
'Code-Review': {},
|
'Code-Review': {},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const accessStub = sandbox.stub(element.$.restAPI,
|
const accessStub = sandbox.stub(element.$.restAPI, 'getRepoAccessRights')
|
||||||
'getRepoAccessRights').returns(Promise.resolve(accessRes));
|
.returns(Promise.resolve(JSON.parse(JSON.stringify(accessRes2))));
|
||||||
const capabilitiesStub = sandbox.stub(element.$.restAPI,
|
const capabilitiesStub = sandbox.stub(element.$.restAPI,
|
||||||
'getCapabilities').returns(Promise.resolve(capabilitiesRes));
|
'getCapabilities').returns(Promise.resolve(capabilitiesRes));
|
||||||
const repoStub = sandbox.stub(element.$.restAPI, 'getRepo').returns(
|
const repoStub = sandbox.stub(element.$.restAPI, 'getRepo').returns(
|
||||||
@@ -228,7 +190,8 @@ limitations under the License.
|
|||||||
|
|
||||||
suite('with defined sections', () => {
|
suite('with defined sections', () => {
|
||||||
setup(() => {
|
setup(() => {
|
||||||
element._sections = element.toSortedArray(accessRes.local);
|
element._sections =
|
||||||
|
element.toSortedArray(JSON.parse(JSON.stringify(accessRes.local)));
|
||||||
flushAsynchronousOperations();
|
flushAsynchronousOperations();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -268,7 +231,7 @@ limitations under the License.
|
|||||||
element._local = JSON.parse(JSON.stringify(accessRes.local));
|
element._local = JSON.parse(JSON.stringify(accessRes.local));
|
||||||
assert.deepEqual(element._computeAddAndRemove(), {add: {}, remove: {}});
|
assert.deepEqual(element._computeAddAndRemove(), {add: {}, remove: {}});
|
||||||
element._local['refs/*'].permissions.owner.rules[123].deleted = true;
|
element._local['refs/*'].permissions.owner.rules[123].deleted = true;
|
||||||
const expectedInput = {
|
let expectedInput = {
|
||||||
add: {},
|
add: {},
|
||||||
remove: {
|
remove: {
|
||||||
'refs/*': {
|
'refs/*': {
|
||||||
@@ -285,19 +248,26 @@ limitations under the License.
|
|||||||
assert.deepEqual(element._computeAddAndRemove(), expectedInput);
|
assert.deepEqual(element._computeAddAndRemove(), expectedInput);
|
||||||
delete element._local['refs/*'].permissions.owner.rules[123].deleted;
|
delete element._local['refs/*'].permissions.owner.rules[123].deleted;
|
||||||
element._local['refs/*'].permissions.owner.rules[123].modified = true;
|
element._local['refs/*'].permissions.owner.rules[123].modified = true;
|
||||||
assert.deepEqual(element._computeAddAndRemove(), repoAccessInput);
|
expectedInput = {
|
||||||
});
|
add: {
|
||||||
|
'refs/*': {
|
||||||
test('_computeAddAndRemove permissions', () => {
|
permissions: {
|
||||||
element._local = JSON.parse(JSON.stringify(accessRes.local));
|
owner: {
|
||||||
assert.deepEqual(element._computeAddAndRemove(), {add: {}, remove: {}});
|
rules: {
|
||||||
element._local['refs/*'].permissions.owner.deleted = true;
|
123: {action: 'DENY', modified: true},
|
||||||
const expectedInput = {
|
},
|
||||||
add: {},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
remove: {
|
remove: {
|
||||||
'refs/*': {
|
'refs/*': {
|
||||||
permissions: {
|
permissions: {
|
||||||
owner: {rules: {}},
|
owner: {
|
||||||
|
rules: {
|
||||||
|
123: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -309,7 +279,7 @@ limitations under the License.
|
|||||||
element._local = JSON.parse(JSON.stringify(accessRes.local));
|
element._local = JSON.parse(JSON.stringify(accessRes.local));
|
||||||
assert.deepEqual(element._computeAddAndRemove(), {add: {}, remove: {}});
|
assert.deepEqual(element._computeAddAndRemove(), {add: {}, remove: {}});
|
||||||
element._local['refs/*'].permissions.owner.deleted = true;
|
element._local['refs/*'].permissions.owner.deleted = true;
|
||||||
const expectedInput = {
|
let expectedInput = {
|
||||||
add: {},
|
add: {},
|
||||||
remove: {
|
remove: {
|
||||||
'refs/*': {
|
'refs/*': {
|
||||||
@@ -320,6 +290,31 @@ limitations under the License.
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
assert.deepEqual(element._computeAddAndRemove(), expectedInput);
|
assert.deepEqual(element._computeAddAndRemove(), expectedInput);
|
||||||
|
delete element._local['refs/*'].permissions.owner.deleted;
|
||||||
|
element._local['refs/*'].permissions.owner.modified = true;
|
||||||
|
expectedInput = {
|
||||||
|
add: {
|
||||||
|
'refs/*': {
|
||||||
|
permissions: {
|
||||||
|
owner: {
|
||||||
|
modified: true,
|
||||||
|
rules: {
|
||||||
|
234: {action: 'ALLOW'},
|
||||||
|
123: {action: 'DENY'},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
remove: {
|
||||||
|
'refs/*': {
|
||||||
|
permissions: {
|
||||||
|
owner: {rules: {}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
assert.deepEqual(element._computeAddAndRemove(), expectedInput);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('_computeAddAndRemove combinations', () => {
|
test('_computeAddAndRemove combinations', () => {
|
||||||
@@ -368,11 +363,65 @@ limitations under the License.
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
assert.deepEqual(element._computeAddAndRemove(), expectedInput);
|
assert.deepEqual(element._computeAddAndRemove(), expectedInput);
|
||||||
|
// Modify both permissions with an exclusive bit. Owner is still
|
||||||
|
// deleted.
|
||||||
|
element._local['refs/*'].permissions.owner.exclusive = true;
|
||||||
|
element._local['refs/*'].permissions.owner.modified = true;
|
||||||
|
element._local['refs/*'].permissions.read.exclusive = true;
|
||||||
|
element._local['refs/*'].permissions.read.modified = true;
|
||||||
|
expectedInput = {
|
||||||
|
add: {
|
||||||
|
'refs/*': {
|
||||||
|
permissions: {
|
||||||
|
read: {
|
||||||
|
exclusive: true,
|
||||||
|
modified: true,
|
||||||
|
rules: {
|
||||||
|
234: {action: 'ALLOW'},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
remove: {
|
||||||
|
'refs/*': {
|
||||||
|
permissions: {
|
||||||
|
owner: {rules: {}},
|
||||||
|
read: {rules: {}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
assert.deepEqual(element._computeAddAndRemove(), expectedInput);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('_handleSaveForReview', done => {
|
test('_handleSaveForReview', done => {
|
||||||
sandbox.stub(element.$.restAPI, 'getRepoAccessRights')
|
const repoAccessInput = {
|
||||||
.returns(Promise.resolve(accessRes));
|
add: {
|
||||||
|
'refs/*': {
|
||||||
|
permissions: {
|
||||||
|
owner: {
|
||||||
|
rules: {
|
||||||
|
123: {action: 'DENY', modified: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
remove: {
|
||||||
|
'refs/*': {
|
||||||
|
permissions: {
|
||||||
|
owner: {
|
||||||
|
rules: {
|
||||||
|
123: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
sandbox.stub(element.$.restAPI, 'getRepoAccessRights').returns(
|
||||||
|
Promise.resolve(JSON.parse(JSON.stringify(accessRes))));
|
||||||
sandbox.stub(element.$.restAPI, 'getRepo')
|
sandbox.stub(element.$.restAPI, 'getRepo')
|
||||||
.returns(Promise.resolve({}));
|
.returns(Promise.resolve({}));
|
||||||
sandbox.stub(Gerrit.Nav, 'navigateToChange');
|
sandbox.stub(Gerrit.Nav, 'navigateToChange');
|
||||||
@@ -381,8 +430,7 @@ limitations under the License.
|
|||||||
.returns(Promise.resolve({_number: 1}));
|
.returns(Promise.resolve({_number: 1}));
|
||||||
|
|
||||||
element.repo = 'test-repo';
|
element.repo = 'test-repo';
|
||||||
sandbox.stub(element, '_computeAddAndRemove')
|
sandbox.stub(element, '_computeAddAndRemove').returns(repoAccessInput);
|
||||||
.returns(repoAccessInput);
|
|
||||||
|
|
||||||
element._handleSaveForReview().then(() => {
|
element._handleSaveForReview().then(() => {
|
||||||
assert.isTrue(saveForReviewStub.called);
|
assert.isTrue(saveForReviewStub.called);
|
||||||
|
|||||||
@@ -62,10 +62,6 @@ limitations under the License.
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
paper-toggle-button {
|
|
||||||
--paper-toggle-button-checked-bar-color: var(--color-link);
|
|
||||||
--paper-toggle-button-checked-button-color: var(--color-link);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<h3>Messages</h3>
|
<h3>Messages</h3>
|
||||||
|
|||||||
@@ -96,6 +96,10 @@ limitations under the License.
|
|||||||
.separator.transparent {
|
.separator.transparent {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
paper-toggle-button {
|
||||||
|
--paper-toggle-button-checked-bar-color: var(--color-link);
|
||||||
|
--paper-toggle-button-checked-button-color: var(--color-link);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</template>
|
</template>
|
||||||
</dom-module>
|
</dom-module>
|
||||||
|
|||||||
Reference in New Issue
Block a user