Allow multiple groups to own a project
Project ownership is now managed as an access right rather than as part of the project entity's basic properties. This permits more than one group to be granted the Owner access value, thus allowing more than group to manage the properties of the project. Bug: GERRIT-247 Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
@@ -44,13 +44,11 @@ for a project named `project`.
|
||||
(project_id
|
||||
,use_contributor_agreements
|
||||
,submit_type
|
||||
,owner_group_id
|
||||
,name)
|
||||
VALUES
|
||||
(nextval('project_id')
|
||||
,'Y'
|
||||
,'M'
|
||||
,(SELECT admin_group_id FROM system_config)
|
||||
,'new/project');
|
||||
|
||||
INSERT INTO branches
|
||||
|
||||
@@ -37,10 +37,6 @@ public interface ProjectAdminService extends RemoteJsonService {
|
||||
void changeProjectSettings(Project update,
|
||||
AsyncCallback<ProjectDetail> callback);
|
||||
|
||||
@SignInRequired
|
||||
void changeProjectOwner(Project.Id projectId, String newOwnerName,
|
||||
AsyncCallback<VoidResult> callback);
|
||||
|
||||
@SignInRequired
|
||||
void deleteRight(Set<ProjectRight.Key> ids, AsyncCallback<VoidResult> callback);
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@ public class ProjectDetail {
|
||||
throws OrmException {
|
||||
project = g.getProject();
|
||||
groups = new HashMap<AccountGroup.Id, AccountGroup>();
|
||||
wantGroup(project.getOwnerGroupId());
|
||||
|
||||
rights = new ArrayList<ProjectRight>();
|
||||
for (final ProjectRight p : g.getRights()) {
|
||||
|
||||
@@ -44,11 +44,6 @@ public class ProjectInfoPanel extends Composite {
|
||||
private Project.Id projectId;
|
||||
private Project project;
|
||||
|
||||
private Panel ownerPanel;
|
||||
private NpTextBox ownerTxtBox;
|
||||
private SuggestBox ownerTxt;
|
||||
private Button changeOwner;
|
||||
|
||||
private Panel submitTypePanel;
|
||||
private ListBox submitType;
|
||||
|
||||
@@ -69,7 +64,6 @@ public class ProjectInfoPanel extends Composite {
|
||||
});
|
||||
|
||||
final FlowPanel body = new FlowPanel();
|
||||
initOwner(body);
|
||||
initDescription(body);
|
||||
initSubmitType(body);
|
||||
initAgreements(body);
|
||||
@@ -82,7 +76,6 @@ public class ProjectInfoPanel extends Composite {
|
||||
@Override
|
||||
protected void onLoad() {
|
||||
enableForm(false);
|
||||
changeOwner.setEnabled(false);
|
||||
saveProject.setEnabled(false);
|
||||
super.onLoad();
|
||||
refresh();
|
||||
@@ -93,7 +86,6 @@ public class ProjectInfoPanel extends Composite {
|
||||
new GerritCallback<ProjectDetail>() {
|
||||
public void onSuccess(final ProjectDetail result) {
|
||||
enableForm(true);
|
||||
changeOwner.setEnabled(false);
|
||||
saveProject.setEnabled(false);
|
||||
display(result);
|
||||
}
|
||||
@@ -102,42 +94,11 @@ public class ProjectInfoPanel extends Composite {
|
||||
|
||||
private void enableForm(final boolean on) {
|
||||
submitType.setEnabled(on);
|
||||
ownerTxtBox.setEnabled(on);
|
||||
descTxt.setEnabled(on);
|
||||
useContributorAgreements.setEnabled(on);
|
||||
useSignedOffBy.setEnabled(on);
|
||||
}
|
||||
|
||||
private void initOwner(final Panel body) {
|
||||
ownerPanel = new VerticalPanel();
|
||||
ownerPanel.add(new SmallHeading(Util.C.headingOwner()));
|
||||
|
||||
ownerTxtBox = new NpTextBox();
|
||||
ownerTxtBox.setVisibleLength(60);
|
||||
ownerTxt = new SuggestBox(new AccountGroupSuggestOracle(), ownerTxtBox);
|
||||
ownerPanel.add(ownerTxt);
|
||||
|
||||
changeOwner = new Button(Util.C.buttonChangeGroupOwner());
|
||||
changeOwner.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(final ClickEvent event) {
|
||||
final String newOwner = ownerTxt.getText().trim();
|
||||
if (newOwner.length() > 0) {
|
||||
Util.PROJECT_SVC.changeProjectOwner(projectId, newOwner,
|
||||
new GerritCallback<VoidResult>() {
|
||||
public void onSuccess(final VoidResult result) {
|
||||
changeOwner.setEnabled(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
ownerPanel.add(changeOwner);
|
||||
body.add(ownerPanel);
|
||||
|
||||
new TextSaveButtonListener(ownerTxtBox, changeOwner);
|
||||
}
|
||||
|
||||
private void initDescription(final Panel body) {
|
||||
final VerticalPanel vp = new VerticalPanel();
|
||||
vp.add(new SmallHeading(Util.C.headingDescription()));
|
||||
@@ -206,15 +167,8 @@ public class ProjectInfoPanel extends Composite {
|
||||
|
||||
void display(final ProjectDetail result) {
|
||||
project = result.project;
|
||||
final AccountGroup owner = result.groups.get(project.getOwnerGroupId());
|
||||
if (owner != null) {
|
||||
ownerTxt.setText(owner.getName());
|
||||
} else {
|
||||
ownerTxt.setText(Util.M.deletedGroup(project.getOwnerGroupId().get()));
|
||||
}
|
||||
|
||||
final boolean isall = ProjectRight.WILD_PROJECT.equals(project.getId());
|
||||
ownerPanel.setVisible(!isall);
|
||||
submitTypePanel.setVisible(!isall);
|
||||
agreementsPanel.setVisible(!isall);
|
||||
useContributorAgreements.setVisible(Common.getGerritConfig()
|
||||
@@ -236,7 +190,6 @@ public class ProjectInfoPanel extends Composite {
|
||||
}
|
||||
|
||||
enableForm(false);
|
||||
changeOwner.setEnabled(false);
|
||||
saveProject.setEnabled(false);
|
||||
|
||||
Util.PROJECT_SVC.changeProjectSettings(project,
|
||||
|
||||
@@ -65,11 +65,11 @@ public class ProjectRightsPanel extends Composite {
|
||||
private SuggestBox nameTxt;
|
||||
|
||||
public ProjectRightsPanel(final Project.Id toShow) {
|
||||
projectId = toShow;
|
||||
|
||||
final FlowPanel body = new FlowPanel();
|
||||
initRights(body);
|
||||
initWidget(body);
|
||||
|
||||
projectId = toShow;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -119,6 +119,15 @@ public class ProjectRightsPanel extends Composite {
|
||||
}
|
||||
for (final ApprovalType at : Common.getGerritConfig().getActionTypes()) {
|
||||
final ApprovalCategory c = at.getCategory();
|
||||
if (ProjectRight.WILD_PROJECT.equals(projectId)
|
||||
&& ApprovalCategory.OWN.equals(c.getId())) {
|
||||
// Giving out control of the WILD_PROJECT to other groups beyond
|
||||
// Administrators is dangerous. Having control over WILD_PROJECT
|
||||
// is about the same as having Administrator access as users are
|
||||
// able to affect grants in all projects on the system.
|
||||
//
|
||||
continue;
|
||||
}
|
||||
catBox.addItem(c.getName(), c.getId().get());
|
||||
}
|
||||
if (catBox.getItemCount() > 0) {
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.google.gerrit.client.data.ChangeDetail;
|
||||
import com.google.gerrit.client.data.PatchSetDetail;
|
||||
import com.google.gerrit.client.data.ProjectCache;
|
||||
import com.google.gerrit.client.reviewdb.Account;
|
||||
import com.google.gerrit.client.reviewdb.ApprovalCategory;
|
||||
import com.google.gerrit.client.reviewdb.Change;
|
||||
import com.google.gerrit.client.reviewdb.PatchSet;
|
||||
import com.google.gerrit.client.reviewdb.Project;
|
||||
@@ -69,8 +70,7 @@ public class ChangeDetailServiceImpl extends BaseServiceImplementation
|
||||
me.equals(change.getOwner())
|
||||
|| me.equals(patch.getUploader())
|
||||
|| Common.getGroupCache().isAdministrator(me)
|
||||
|| Common.getGroupCache().isInGroup(me,
|
||||
proj.getOwnerGroupId());
|
||||
|| canPerform(me, projEnt, ApprovalCategory.OWN, (short) 1);
|
||||
}
|
||||
final ChangeDetail d = new ChangeDetail();
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
package com.google.gerrit.client.data;
|
||||
|
||||
import com.google.gerrit.client.reviewdb.AccountGroup;
|
||||
import com.google.gerrit.client.reviewdb.ApprovalCategory;
|
||||
import com.google.gerrit.client.reviewdb.Project;
|
||||
import com.google.gerrit.client.reviewdb.ProjectRight;
|
||||
import com.google.gerrit.client.reviewdb.ReviewDb;
|
||||
@@ -22,8 +24,10 @@ import com.google.gwtorm.client.OrmException;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/** Cache of project information, including access rights. */
|
||||
@SuppressWarnings("serial")
|
||||
@@ -177,12 +181,22 @@ public class ProjectCache {
|
||||
public static class Entry {
|
||||
private final Project project;
|
||||
private final Collection<ProjectRight> rights;
|
||||
private final Set<AccountGroup.Id> owners;
|
||||
|
||||
protected Entry(final ReviewDb db, final Project p) throws OrmException {
|
||||
project = p;
|
||||
rights =
|
||||
Collections.unmodifiableCollection(db.projectRights().byProject(
|
||||
project.getId()).toList());
|
||||
|
||||
final HashSet<AccountGroup.Id> groups = new HashSet<AccountGroup.Id>();
|
||||
for (final ProjectRight right : rights) {
|
||||
if (ApprovalCategory.OWN.equals(right.getApprovalCategoryId())
|
||||
&& right.getMaxValue() > 0) {
|
||||
groups.add(right.getAccountGroupId());
|
||||
}
|
||||
}
|
||||
owners = Collections.unmodifiableSet(groups);
|
||||
}
|
||||
|
||||
public Project getProject() {
|
||||
@@ -192,5 +206,9 @@ public class ProjectCache {
|
||||
public Collection<ProjectRight> getRights() {
|
||||
return rights;
|
||||
}
|
||||
|
||||
public Set<AccountGroup.Id> getOwners() {
|
||||
return owners;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,10 @@ public final class ApprovalCategory {
|
||||
public static final ApprovalCategory.Id READ =
|
||||
new ApprovalCategory.Id("READ");
|
||||
|
||||
/** Id of the special "Own" category; manages a project. */
|
||||
public static final ApprovalCategory.Id OWN =
|
||||
new ApprovalCategory.Id("OWN");
|
||||
|
||||
/** Id of the special "Push Annotated Tag" action (and category). */
|
||||
public static final ApprovalCategory.Id PUSH_TAG =
|
||||
new ApprovalCategory.Id("pTAG");
|
||||
@@ -67,6 +71,14 @@ public final class ApprovalCategory {
|
||||
protected void set(String newValue) {
|
||||
id = newValue;
|
||||
}
|
||||
|
||||
/** True if the right can inherit from {@link ProjectRight#WILD_PROJECT}. */
|
||||
public boolean canInheritFromWildProject() {
|
||||
if (OWN.equals(this)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/** Internal short unique identifier for this category. */
|
||||
|
||||
@@ -123,9 +123,6 @@ public final class Project {
|
||||
@Column(length = Integer.MAX_VALUE, notNull = false)
|
||||
protected String description;
|
||||
|
||||
@Column
|
||||
protected AccountGroup.Id ownerGroupId;
|
||||
|
||||
@Column
|
||||
protected boolean useContributorAgreements;
|
||||
|
||||
@@ -165,14 +162,6 @@ public final class Project {
|
||||
description = d;
|
||||
}
|
||||
|
||||
public AccountGroup.Id getOwnerGroupId() {
|
||||
return ownerGroupId;
|
||||
}
|
||||
|
||||
public void setOwnerGroupId(final AccountGroup.Id id) {
|
||||
ownerGroupId = id;
|
||||
}
|
||||
|
||||
public boolean isUseContributorAgreements() {
|
||||
return useContributorAgreements;
|
||||
}
|
||||
|
||||
@@ -31,9 +31,6 @@ public interface ProjectAccess extends Access<Project, Project.NameKey> {
|
||||
@Query("ORDER BY name")
|
||||
ResultSet<Project> all() throws OrmException;
|
||||
|
||||
@Query("WHERE ownerGroupId = ?")
|
||||
ResultSet<Project> ownedByGroup(AccountGroup.Id groupId) throws OrmException;
|
||||
|
||||
@Query("WHERE name.name >= ? AND name.name <= ? ORDER BY name LIMIT ?")
|
||||
ResultSet<Project> suggestByName(String nameA, String nameB, int limit)
|
||||
throws OrmException;
|
||||
|
||||
@@ -28,10 +28,7 @@ public interface ProjectRightAccess extends
|
||||
@Query("WHERE key.projectId = ?")
|
||||
ResultSet<ProjectRight> byProject(Project.Id id) throws OrmException;
|
||||
|
||||
@Query("WHERE key.groupId = ?")
|
||||
ResultSet<ProjectRight> byGroup(AccountGroup.Id id) throws OrmException;
|
||||
|
||||
@Query("WHERE key.categoryId = ?")
|
||||
ResultSet<ProjectRight> byApprovalCategory(ApprovalCategory.Id id)
|
||||
throws OrmException;
|
||||
@Query("WHERE key.categoryId = ? AND key.groupId = ?")
|
||||
ResultSet<ProjectRight> byCategoryGroup(ApprovalCategory.Id cat,
|
||||
AccountGroup.Id group) throws OrmException;
|
||||
}
|
||||
|
||||
@@ -97,27 +97,25 @@ public class BaseServiceImplementation {
|
||||
|
||||
public static boolean canRead(final Account.Id who,
|
||||
final Project.NameKey projectKey) {
|
||||
final ProjectCache.Entry e = Common.getProjectCache().get(projectKey);
|
||||
return canRead(who, e);
|
||||
return canRead(who, Common.getProjectCache().get(projectKey));
|
||||
}
|
||||
|
||||
public static boolean canRead(final Account.Id who, final ProjectCache.Entry e) {
|
||||
if (e == null) {
|
||||
// Unexpected, a project disappearing. But claim its not available.
|
||||
//
|
||||
return false;
|
||||
return canPerform(who, e, ApprovalCategory.READ, (short) 1);
|
||||
}
|
||||
final Set<AccountGroup.Id> myGroups = Common.getGroupCache().getEffectiveGroups(who);
|
||||
return canPerform(myGroups, e, ApprovalCategory.READ, (short) 1, true);
|
||||
|
||||
public static boolean canPerform(final Account.Id who,
|
||||
final ProjectCache.Entry e, final ApprovalCategory.Id actionId,
|
||||
final short requireValue) {
|
||||
Set<AccountGroup.Id> groups = Common.getGroupCache().getEffectiveGroups(who);
|
||||
return canPerform(groups, e, actionId, requireValue);
|
||||
}
|
||||
|
||||
public static boolean canPerform(final Set<AccountGroup.Id> myGroups,
|
||||
final ProjectCache.Entry e, final ApprovalCategory.Id actionId,
|
||||
final short requireValue, final boolean assumeOwner) {
|
||||
if (assumeOwner && myGroups.contains(e.getProject().getOwnerGroupId())) {
|
||||
// Ownership implies full access.
|
||||
//
|
||||
return true;
|
||||
final short requireValue) {
|
||||
if (e == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int val = Integer.MIN_VALUE;
|
||||
@@ -138,7 +136,7 @@ public class BaseServiceImplementation {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (val == Integer.MIN_VALUE) {
|
||||
if (val == Integer.MIN_VALUE && actionId.canInheritFromWildProject()) {
|
||||
for (final ProjectRight pr : Common.getProjectCache().getWildcardRights()) {
|
||||
if (actionId.equals(pr.getApprovalCategoryId())
|
||||
&& myGroups.contains(pr.getAccountGroupId())) {
|
||||
|
||||
@@ -473,7 +473,6 @@ public class GerritServer {
|
||||
new Project(new Project.NameKey("-- All Projects --"),
|
||||
ProjectRight.WILD_PROJECT);
|
||||
proj.setDescription("Rights inherited by all other projects");
|
||||
proj.setOwnerGroupId(sConfig.adminGroupId);
|
||||
proj.setUseContributorAgreements(false);
|
||||
c.projects().insert(Collections.singleton(proj));
|
||||
}
|
||||
@@ -519,6 +518,21 @@ public class GerritServer {
|
||||
c.projectRights().insert(Collections.singleton(approve));
|
||||
}
|
||||
|
||||
private void initOwnerCategory(final ReviewDb c) throws OrmException {
|
||||
final Transaction txn = c.beginTransaction();
|
||||
final ApprovalCategory cat;
|
||||
final ArrayList<ApprovalCategoryValue> vals;
|
||||
|
||||
cat = new ApprovalCategory(ApprovalCategory.OWN, "Owner");
|
||||
cat.setPosition((short) -1);
|
||||
cat.setFunctionName(NoOpFunction.NAME);
|
||||
vals = new ArrayList<ApprovalCategoryValue>();
|
||||
vals.add(value(cat, 1, "Administer All Settings"));
|
||||
c.approvalCategories().insert(Collections.singleton(cat), txn);
|
||||
c.approvalCategoryValues().insert(vals, txn);
|
||||
txn.commit();
|
||||
}
|
||||
|
||||
private void initReadCategory(final ReviewDb c) throws OrmException {
|
||||
final Transaction txn = c.beginTransaction();
|
||||
final ApprovalCategory cat;
|
||||
@@ -631,13 +645,14 @@ public class GerritServer {
|
||||
|
||||
initSystemConfig(c);
|
||||
sConfig = c.systemConfig().get(new SystemConfig.Key());
|
||||
initWildCardProject(c);
|
||||
initOwnerCategory(c);
|
||||
initReadCategory(c);
|
||||
initVerifiedCategory(c);
|
||||
initCodeReviewCategory(c);
|
||||
initSubmitCategory(c);
|
||||
initPushTagCategory(c);
|
||||
initPushUpdateBranchCategory(c);
|
||||
initWildCardProject(c);
|
||||
}
|
||||
|
||||
if (sVer.versionNbr == 2) {
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.google.gerrit.client.reviewdb.Branch;
|
||||
import com.google.gerrit.client.reviewdb.Project;
|
||||
import com.google.gerrit.client.reviewdb.ProjectRight;
|
||||
import com.google.gerrit.client.reviewdb.ReviewDb;
|
||||
import com.google.gerrit.client.reviewdb.AccountGroup.Id;
|
||||
import com.google.gerrit.client.rpc.BaseServiceImplementation;
|
||||
import com.google.gerrit.client.rpc.Common;
|
||||
import com.google.gerrit.client.rpc.InvalidNameException;
|
||||
@@ -74,17 +75,12 @@ public class ProjectAdminServiceImpl extends BaseServiceImplementation
|
||||
public void ownedProjects(final AsyncCallback<List<Project>> callback) {
|
||||
run(callback, new Action<List<Project>>() {
|
||||
public List<Project> run(ReviewDb db) throws OrmException {
|
||||
final List<Project> result;
|
||||
if (Common.getGroupCache().isAdministrator(Common.getAccountId())) {
|
||||
result = db.projects().all().toList();
|
||||
} else {
|
||||
result = myOwnedProjects(db);
|
||||
final List<Project> result = myOwnedProjects(db);
|
||||
Collections.sort(result, new Comparator<Project>() {
|
||||
public int compare(final Project a, final Project b) {
|
||||
return a.getName().compareTo(b.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
@@ -153,52 +149,16 @@ public class ProjectAdminServiceImpl extends BaseServiceImplementation
|
||||
});
|
||||
}
|
||||
|
||||
public void changeProjectOwner(final Project.Id projectId,
|
||||
final String newOwnerName, final AsyncCallback<VoidResult> callback) {
|
||||
run(callback, new Action<VoidResult>() {
|
||||
public VoidResult run(final ReviewDb db) throws OrmException, Failure {
|
||||
assertAmProjectOwner(db, projectId);
|
||||
final Project project = db.projects().get(projectId);
|
||||
if (project == null) {
|
||||
throw new Failure(new NoSuchEntityException());
|
||||
}
|
||||
if (ProjectRight.WILD_PROJECT.equals(projectId)) {
|
||||
// This is *not* a good idea to change away from administrators.
|
||||
//
|
||||
throw new Failure(new NoSuchEntityException());
|
||||
}
|
||||
|
||||
final AccountGroup owner =
|
||||
db.accountGroups().get(new AccountGroup.NameKey(newOwnerName));
|
||||
if (owner == null) {
|
||||
throw new Failure(new NoSuchEntityException());
|
||||
}
|
||||
|
||||
project.setOwnerGroupId(owner.getId());
|
||||
db.projects().update(Collections.singleton(project));
|
||||
Common.getProjectCache().invalidate(project);
|
||||
return VoidResult.INSTANCE;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void deleteRight(final Set<ProjectRight.Key> keys,
|
||||
final AsyncCallback<VoidResult> callback) {
|
||||
run(callback, new Action<VoidResult>() {
|
||||
public VoidResult run(final ReviewDb db) throws OrmException, Failure {
|
||||
final Set<Project.Id> owned = ids(myOwnedProjects(db));
|
||||
Boolean amAdmin = null;
|
||||
for (final ProjectRight.Key k : keys) {
|
||||
if (!owned.contains(k.getProjectId())) {
|
||||
if (amAdmin == null) {
|
||||
amAdmin =
|
||||
Common.getGroupCache().isAdministrator(Common.getAccountId());
|
||||
}
|
||||
if (!amAdmin) {
|
||||
throw new Failure(new NoSuchEntityException());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (final ProjectRight.Key k : keys) {
|
||||
final ProjectRight m = db.projectRights().get(k);
|
||||
if (m != null) {
|
||||
@@ -215,6 +175,17 @@ public class ProjectAdminServiceImpl extends BaseServiceImplementation
|
||||
final ApprovalCategory.Id categoryId, final String groupName,
|
||||
final short amin, final short amax,
|
||||
final AsyncCallback<ProjectDetail> callback) {
|
||||
if (ProjectRight.WILD_PROJECT.equals(projectId)
|
||||
&& ApprovalCategory.OWN.equals(categoryId)) {
|
||||
// Giving out control of the WILD_PROJECT to other groups beyond
|
||||
// Administrators is dangerous. Having control over WILD_PROJECT
|
||||
// is about the same as having Administrator access as users are
|
||||
// able to affect grants in all projects on the system.
|
||||
//
|
||||
callback.onFailure(new NoSuchEntityException());
|
||||
return;
|
||||
}
|
||||
|
||||
final short min, max;
|
||||
if (amin <= amax) {
|
||||
min = amin;
|
||||
@@ -295,7 +266,6 @@ public class ProjectAdminServiceImpl extends BaseServiceImplementation
|
||||
public Set<Branch.NameKey> run(ReviewDb db) throws OrmException, Failure {
|
||||
final Set<Branch.NameKey> deleted = new HashSet<Branch.NameKey>();
|
||||
final Set<Project.Id> owned = ids(myOwnedProjects(db));
|
||||
Boolean amAdmin = null;
|
||||
for (final Branch.NameKey k : ids) {
|
||||
final ProjectCache.Entry e;
|
||||
|
||||
@@ -304,15 +274,9 @@ public class ProjectAdminServiceImpl extends BaseServiceImplementation
|
||||
throw new Failure(new NoSuchEntityException());
|
||||
}
|
||||
if (!owned.contains(e.getProject().getId())) {
|
||||
if (amAdmin == null) {
|
||||
amAdmin =
|
||||
Common.getGroupCache().isAdministrator(Common.getAccountId());
|
||||
}
|
||||
if (!amAdmin) {
|
||||
throw new Failure(new NoSuchEntityException());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (final Branch.NameKey k : ids) {
|
||||
final Branch m = db.branches().get(k);
|
||||
if (m == null) {
|
||||
@@ -475,25 +439,44 @@ public class ProjectAdminServiceImpl extends BaseServiceImplementation
|
||||
if (p == null) {
|
||||
throw new Failure(new NoSuchEntityException());
|
||||
}
|
||||
final Account.Id me = Common.getAccountId();
|
||||
if (!Common.getGroupCache().isInGroup(me, p.getProject().getOwnerGroupId())
|
||||
&& !Common.getGroupCache().isAdministrator(me)) {
|
||||
if (Common.getGroupCache().isAdministrator(Common.getAccountId())) {
|
||||
return;
|
||||
}
|
||||
final Set<Id> myGroups = myGroups();
|
||||
if (!canPerform(myGroups, p, ApprovalCategory.OWN, (short) 1)) {
|
||||
throw new Failure(new NoSuchEntityException());
|
||||
}
|
||||
}
|
||||
|
||||
private List<Project> myOwnedProjects(final ReviewDb db) throws OrmException {
|
||||
final Account.Id me = Common.getAccountId();
|
||||
if (Common.getGroupCache().isAdministrator(Common.getAccountId())) {
|
||||
return db.projects().all().toList();
|
||||
}
|
||||
|
||||
final Set<AccountGroup.Id> myGroups = myGroups();
|
||||
final HashSet<Project.Id> projects = new HashSet<Project.Id>();
|
||||
for (final AccountGroup.Id groupId : myGroups) {
|
||||
for (final ProjectRight r : db.projectRights().byCategoryGroup(
|
||||
ApprovalCategory.OWN, groupId)) {
|
||||
projects.add(r.getProjectId());
|
||||
}
|
||||
}
|
||||
|
||||
final ProjectCache projectCache = Common.getProjectCache();
|
||||
final List<Project> own = new ArrayList<Project>();
|
||||
for (final AccountGroup.Id groupId : Common.getGroupCache()
|
||||
.getEffectiveGroups(me)) {
|
||||
for (final Project g : db.projects().ownedByGroup(groupId)) {
|
||||
own.add(g);
|
||||
for (Project.Id id : projects) {
|
||||
final ProjectCache.Entry cacheEntry = projectCache.get(id);
|
||||
if (canPerform(myGroups, cacheEntry, ApprovalCategory.OWN, (short) 1)) {
|
||||
own.add(cacheEntry.getProject());
|
||||
}
|
||||
}
|
||||
return own;
|
||||
}
|
||||
|
||||
private Set<Id> myGroups() {
|
||||
return Common.getGroupCache().getEffectiveGroups(Common.getAccountId());
|
||||
}
|
||||
|
||||
private static Set<Project.Id> ids(final Collection<Project> projectList) {
|
||||
final HashSet<Project.Id> r = new HashSet<Project.Id>();
|
||||
for (final Project project : projectList) {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package com.google.gerrit.server.mail;
|
||||
|
||||
import com.google.gerrit.client.reviewdb.Account;
|
||||
import com.google.gerrit.client.reviewdb.AccountGroup;
|
||||
import com.google.gerrit.client.reviewdb.AccountGroupMember;
|
||||
import com.google.gerrit.client.reviewdb.AccountProjectWatch;
|
||||
import com.google.gerrit.client.reviewdb.Change;
|
||||
@@ -48,10 +49,11 @@ public class CreateChangeSender extends NewChangeSender {
|
||||
// Try to mark interested owners with a TO and not a BCC line.
|
||||
//
|
||||
final Set<Account.Id> owners = new HashSet<Account.Id>();
|
||||
for (AccountGroupMember m : db.accountGroupMembers().byGroup(
|
||||
project.getOwnerGroupId())) {
|
||||
for (AccountGroup.Id g : getProjectOwners()) {
|
||||
for (AccountGroupMember m : db.accountGroupMembers().byGroup(g)) {
|
||||
owners.add(m.getAccountId());
|
||||
}
|
||||
}
|
||||
|
||||
// BCC anyone who has interest in this project's changes
|
||||
//
|
||||
|
||||
@@ -16,6 +16,7 @@ package com.google.gerrit.server.mail;
|
||||
|
||||
import com.google.gerrit.client.data.ProjectCache;
|
||||
import com.google.gerrit.client.reviewdb.Account;
|
||||
import com.google.gerrit.client.reviewdb.AccountGroup;
|
||||
import com.google.gerrit.client.reviewdb.AccountProjectWatch;
|
||||
import com.google.gerrit.client.reviewdb.Change;
|
||||
import com.google.gerrit.client.reviewdb.ChangeApproval;
|
||||
@@ -44,12 +45,14 @@ import java.net.URL;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
@@ -515,6 +518,14 @@ public abstract class OutgoingEmail {
|
||||
return r != null ? r.getProject() : null;
|
||||
}
|
||||
|
||||
/** Get the groups which own the project. */
|
||||
protected Set<AccountGroup.Id> getProjectOwners() {
|
||||
final ProjectCache.Entry r;
|
||||
|
||||
r = Common.getProjectCache().get(change.getDest().getParentKey());
|
||||
return r != null ? r.getOwners() : Collections.<AccountGroup.Id> emptySet();
|
||||
}
|
||||
|
||||
/** Schedule this message for delivery to the listed accounts. */
|
||||
protected void add(final RecipientType rt, final Collection<Account.Id> list) {
|
||||
for (final Account.Id id : list) {
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
package com.google.gerrit.server.patch;
|
||||
|
||||
import com.google.gerrit.client.Gerrit;
|
||||
import com.google.gerrit.client.data.ApprovalType;
|
||||
import com.google.gerrit.client.data.PatchScript;
|
||||
import com.google.gerrit.client.data.PatchScriptSettings;
|
||||
@@ -408,7 +407,7 @@ public class PatchDetailServiceImpl extends BaseServiceImplementation implements
|
||||
|
||||
if (!me.equals(change.getOwner()) && !me.equals(patch.getUploader())
|
||||
&& !Common.getGroupCache().isAdministrator(me)
|
||||
&& !Common.getGroupCache().isInGroup(me, proj.getOwnerGroupId())) {
|
||||
&& !canPerform(me, projEnt, ApprovalCategory.OWN, (short) 1)) {
|
||||
// The user doesn't have permission to abandon the change
|
||||
throw new Failure(new NoSuchEntityException());
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ abstract class AbstractCommand implements Command, SessionAware {
|
||||
protected boolean canPerform(final ProjectCache.Entry project,
|
||||
final ApprovalCategory.Id actionId, final short val) {
|
||||
return BaseServiceImplementation.canPerform(getGroups(), project, actionId,
|
||||
val, false);
|
||||
val);
|
||||
}
|
||||
|
||||
protected void assertIsAdministrator() throws Failure {
|
||||
|
||||
@@ -160,21 +160,14 @@ ON patch_set_ancestors (ancestor_revision);
|
||||
-- *********************************************************************
|
||||
-- ProjectAccess
|
||||
-- @PrimaryKey covers: all, suggestByName
|
||||
-- covers: ownedByGroup
|
||||
CREATE INDEX projects_ownedByGroup
|
||||
ON projects (owner_group_id);
|
||||
|
||||
|
||||
-- *********************************************************************
|
||||
-- ProjectRightAccess
|
||||
-- @PrimaryKey covers: byProject
|
||||
-- covers: byGroup
|
||||
CREATE INDEX project_rights_byGroup
|
||||
ON project_rights (group_id);
|
||||
|
||||
-- covers: byApprovalCategory
|
||||
CREATE INDEX project_rights_byCat
|
||||
ON project_rights (category_id);
|
||||
-- covers: byCategoryGroup
|
||||
CREATE INDEX project_rights_byCatGroup
|
||||
ON project_rights (category_id, group_id);
|
||||
|
||||
|
||||
-- *********************************************************************
|
||||
|
||||
@@ -186,20 +186,14 @@ ON patch_set_ancestors (ancestor_revision);
|
||||
-- ProjectAccess
|
||||
-- @PrimaryKey covers: all, suggestByName
|
||||
-- covers: ownedByGroup
|
||||
CREATE INDEX projects_ownedByGroup
|
||||
ON projects (owner_group_id);
|
||||
|
||||
|
||||
-- *********************************************************************
|
||||
-- ProjectRightAccess
|
||||
-- @PrimaryKey covers: byProject
|
||||
-- covers: byGroup
|
||||
CREATE INDEX project_rights_byGroup
|
||||
ON project_rights (group_id);
|
||||
|
||||
-- covers: byApprovalCategory
|
||||
CREATE INDEX project_rights_byCat
|
||||
ON project_rights (category_id);
|
||||
-- covers: byCategoryGroup
|
||||
CREATE INDEX project_rights_byCatGroup
|
||||
ON project_rights (category_id, group_id);
|
||||
|
||||
|
||||
-- *********************************************************************
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
-- Upgrade: schema_version 14 to 15
|
||||
--
|
||||
ALTER TABLE patch_comments ADD parent_uuid VARCHAR(40);
|
||||
|
||||
CREATE TABLE account_patch_reviews
|
||||
(account_id INTEGER NOT NULL DEFAULT(0),
|
||||
change_id INTEGER NOT NULL DEFAULT(0),
|
||||
patch_set_id INTEGER NOT NULL DEFAULT(0),
|
||||
file_name VARCHAR(255) NOT NULL DEFAULT(''),
|
||||
PRIMARY KEY (account_id, change_id, patch_set_id, file_name)
|
||||
);
|
||||
|
||||
UPDATE schema_version SET version_nbr = 15;
|
||||
36
src/main/webapp/WEB-INF/sql/upgrade014_015_part1_mysql.sql
Normal file
36
src/main/webapp/WEB-INF/sql/upgrade014_015_part1_mysql.sql
Normal file
@@ -0,0 +1,36 @@
|
||||
-- Upgrade: schema_version 14 to 15
|
||||
--
|
||||
ALTER TABLE patch_comments ADD parent_uuid VARCHAR(40);
|
||||
|
||||
CREATE TABLE account_patch_reviews
|
||||
(account_id INTEGER NOT NULL DEFAULT(0),
|
||||
change_id INTEGER NOT NULL DEFAULT(0),
|
||||
patch_set_id INTEGER NOT NULL DEFAULT(0),
|
||||
file_name VARCHAR(255) NOT NULL DEFAULT(''),
|
||||
PRIMARY KEY (account_id, change_id, patch_set_id, file_name)
|
||||
);
|
||||
|
||||
INSERT INTO approval_categories
|
||||
(name, position, function_name, category_id)
|
||||
VALUES
|
||||
('Owner', -1, 'NoOp', 'OWN');
|
||||
|
||||
INSERT INTO approval_category_values
|
||||
(category_id, value, name)
|
||||
VALUES
|
||||
('OWN', 1, 'Administer All Settings');
|
||||
|
||||
INSERT INTO project_rights
|
||||
(project_id, category_id, group_id, min_value, max_value)
|
||||
SELECT p.project_id, 'OWN', p.owner_group_id, 0, 1
|
||||
FROM projects p
|
||||
AND p.project_id <> 0;
|
||||
|
||||
DROP INDEX projects_ownedByGroup ON projects;
|
||||
DROP INDEX project_rights_byCat ON project_rights;
|
||||
DROP INDEX project_rights_byGroup ON project_rights;
|
||||
|
||||
CREATE INDEX project_rights_byCatGroup
|
||||
ON project_rights (category_id, group_id);
|
||||
|
||||
UPDATE schema_version SET version_nbr = 15;
|
||||
@@ -0,0 +1,38 @@
|
||||
-- Upgrade: schema_version 14 to 15
|
||||
--
|
||||
ALTER TABLE patch_comments ADD parent_uuid VARCHAR(40);
|
||||
|
||||
CREATE TABLE account_patch_reviews
|
||||
(account_id INTEGER NOT NULL DEFAULT(0),
|
||||
change_id INTEGER NOT NULL DEFAULT(0),
|
||||
patch_set_id INTEGER NOT NULL DEFAULT(0),
|
||||
file_name VARCHAR(255) NOT NULL DEFAULT(''),
|
||||
PRIMARY KEY (account_id, change_id, patch_set_id, file_name)
|
||||
);
|
||||
|
||||
ALTER TABLE account_patch_reviews OWNER TO gerrit2;
|
||||
|
||||
INSERT INTO approval_categories
|
||||
(name, position, function_name, category_id)
|
||||
VALUES
|
||||
('Owner', -1, 'NoOp', 'OWN');
|
||||
|
||||
INSERT INTO approval_category_values
|
||||
(category_id, value, name)
|
||||
VALUES
|
||||
('OWN', 1, 'Administer All Settings');
|
||||
|
||||
INSERT INTO project_rights
|
||||
(project_id, category_id, group_id, min_value, max_value)
|
||||
SELECT p.project_id, 'OWN', p.owner_group_id, 1, 1
|
||||
FROM projects p
|
||||
AND p.project_id <> 0;
|
||||
|
||||
DROP INDEX projects_ownedByGroup;
|
||||
DROP INDEX project_rights_byCat;
|
||||
DROP INDEX project_rights_byGroup;
|
||||
|
||||
CREATE INDEX project_rights_byCatGroup
|
||||
ON project_rights (category_id, group_id);
|
||||
|
||||
UPDATE schema_version SET version_nbr = 15;
|
||||
1
src/main/webapp/WEB-INF/sql/upgrade014_015_part2.sql
Normal file
1
src/main/webapp/WEB-INF/sql/upgrade014_015_part2.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE projects DROP COLUMN owner_group_id;
|
||||
@@ -1,15 +0,0 @@
|
||||
-- Upgrade: schema_version 14 to 15
|
||||
--
|
||||
ALTER TABLE patch_comments ADD parent_uuid VARCHAR(40);
|
||||
|
||||
CREATE TABLE account_patch_reviews
|
||||
(account_id INTEGER NOT NULL DEFAULT(0),
|
||||
change_id INTEGER NOT NULL DEFAULT(0),
|
||||
patch_set_id INTEGER NOT NULL DEFAULT(0),
|
||||
file_name VARCHAR(255) NOT NULL DEFAULT(''),
|
||||
PRIMARY KEY (account_id, change_id, patch_set_id, file_name)
|
||||
);
|
||||
|
||||
ALTER TABLE account_patch_reviews OWNER TO gerrit2;
|
||||
|
||||
UPDATE schema_version SET version_nbr = 15;
|
||||
Reference in New Issue
Block a user