Make administrator, create-project a global capability

This gets rid of the special entries in system_config and
gerrit.config related to who the Administrators group is,
or which groups are permitted to create new projects on
this server.

An interesting side effect of this change is admins can
now actually remove the blessed Administrators group and
run the server entirely without it. Fine grained rules
can be used for most permissions, and direct access to
the All-Projects.git repository can be used for cases
where the "Administrate Site" override power is needed.

Another benefit is the 'Create Project' capability is
now dynamic, and can be modified at runtime without a
server restart.

Bug: issue 742
Change-Id: I44702010a4a521fd67d986d5b20411002c9481dd
This commit is contained in:
Shawn O. Pearce
2011-06-16 16:59:59 -07:00
parent c7e736a157
commit 897d9218ac
51 changed files with 511 additions and 410 deletions

View File

@@ -37,11 +37,8 @@ on the remote system to create the empty repository.
ACCESS ACCESS
------ ------
Caller must be a member of any of the groups defined by Caller must be a member of the privileged 'Administrators' group,
`repository.*.createGroup` in gerrit.config. or have been granted the 'Create Project' global capability.
If there is no such declaration, caller is required to be a member
of the privileged 'Administrators' group.
SCRIPTING SCRIPTING
--------- ---------
@@ -70,9 +67,7 @@ OPTIONS
Several groups can be specified on the command line. Several groups can be specified on the command line.
+ +
Defaults to what is specified by `repository.*.ownerGroup` Defaults to what is specified by `repository.*.ownerGroup`
in gerrit.config. If no such declaration(s) exist, in gerrit.config.
`repository.*.createGroup` will be used. If they don't exist,
`Administrators` will be used.
--parent:: --parent::
-p:: -p::

View File

@@ -900,6 +900,15 @@ the path string `"$\{basePath}/$\{project_name}.git"`.
+ +
If relative, the path is resolved relative to `'$site_path'`. If relative, the path is resolved relative to `'$site_path'`.
[[gerrit.allProjects]]gerrit.allProjects::
+
Name of the permissions-only project defining global server
access controls and settings. These are inherited into every
other project managed by the running server. The name is
relative to `gerrit.basePath`.
+
Defaults to `All-Projects` if not set.
[[gerrit.canonicalWebUrl]]gerrit.canonicalWebUrl:: [[gerrit.canonicalWebUrl]]gerrit.canonicalWebUrl::
+ +
The default URL for Gerrit to be accessed through. The default URL for Gerrit to be accessed through.
@@ -1498,15 +1507,11 @@ If no groups are added, any user will be allowed to execute
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Repositories in this sense are the same as projects. Repositories in this sense are the same as projects.
In the following example configuration the `Administrators` and the In the following example configuration `Registered Users` is set
`Registered Users` groups are set to be the ones to be allowed to to be the default owner of new projects.
create projects matching `*` (any project). `Registered Users` is
set to be the default owner of new projects.
---- ----
[repository "*"] [repository "*"]
createGroup = Administrators
createGroup = Registered Users
ownerGroup = Registered Users ownerGroup = Registered Users
---- ----
@@ -1514,24 +1519,11 @@ set to be the default owner of new projects.
Currently only the repository name `*` is supported. Currently only the repository name `*` is supported.
This is a wildcard designating all repositories. This is a wildcard designating all repositories.
[[repository.name.createGroup]]repository.<name>.createGroup::
+
A name of a group which exists in the database. Zero, one or many
groups are allowed. Each on its own line. Groups which don't exist
in the database are ignored.
+
If no groups are declared (or only non-existing ones), the default
value `Administrators` is used.
[[repository.name.ownerGroup]]repository.<name>.ownerGroup:: [[repository.name.ownerGroup]]repository.<name>.ownerGroup::
+ +
A name of a group which exists in the database. Zero, one or many A name of a group which exists in the database. Zero, one or many
groups are allowed. Each on its own line. Groups which don't exist groups are allowed. Each on its own line. Groups which don't exist
in the database are ignored. in the database are ignored.
+
If no groups are declared (or only non-existing ones), it defaults
to whatever is declared by `repository.<name>.createGroup` (including
any fallback to `Administrators`.)
[[sendemail]]Section sendemail [[sendemail]]Section sendemail
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -19,12 +19,26 @@ import java.util.List;
/** Server wide capabilities. Represented as {@link Permission} objects. */ /** Server wide capabilities. Represented as {@link Permission} objects. */
public class GlobalCapability { public class GlobalCapability {
/** Can create any group on the server. */ /**
public static final String CREATE_GROUP = "createGroup"; * Denotes the server's administrators.
* <p>
* This is similar to UNIX root, or Windows SYSTEM account. Any user that
* has this capability can perform almost any other action, or can grant
* themselves the power to perform any other action on the site. Most of
* the other capabilities and permissions fall-back to the predicate
* "OR user has capablity ADMINISTRATE_SERVER".
*/
public static final String ADMINISTRATE_SERVER = "administrateServer";
/** Can create any account on the server. */ /** Can create any account on the server. */
public static final String CREATE_ACCOUNT = "createAccount"; public static final String CREATE_ACCOUNT = "createAccount";
/** Can create any group on the server. */
public static final String CREATE_GROUP = "createGroup";
/** Can create any project on the server. */
public static final String CREATE_PROJECT = "createProject";
/** Can flush any cache except the active web_sessions cache. */ /** Can flush any cache except the active web_sessions cache. */
public static final String FLUSH_CACHES = "flushCaches"; public static final String FLUSH_CACHES = "flushCaches";
@@ -50,8 +64,10 @@ public class GlobalCapability {
static { static {
NAMES_LC = new ArrayList<String>(); NAMES_LC = new ArrayList<String>();
NAMES_LC.add(CREATE_GROUP.toLowerCase()); NAMES_LC.add(ADMINISTRATE_SERVER.toLowerCase());
NAMES_LC.add(CREATE_ACCOUNT.toLowerCase()); NAMES_LC.add(CREATE_ACCOUNT.toLowerCase());
NAMES_LC.add(CREATE_GROUP.toLowerCase());
NAMES_LC.add(CREATE_PROJECT.toLowerCase());
NAMES_LC.add(FLUSH_CACHES.toLowerCase()); NAMES_LC.add(FLUSH_CACHES.toLowerCase());
NAMES_LC.add(KILL_TASK.toLowerCase()); NAMES_LC.add(KILL_TASK.toLowerCase());
NAMES_LC.add(QUERY_LIMIT.toLowerCase()); NAMES_LC.add(QUERY_LIMIT.toLowerCase());

View File

@@ -33,6 +33,7 @@ public class Permission implements Comparable<Permission> {
public static final String SUBMIT = "submit"; public static final String SUBMIT = "submit";
private static final List<String> NAMES_LC; private static final List<String> NAMES_LC;
private static final int labelIndex;
static { static {
NAMES_LC = new ArrayList<String>(); NAMES_LC = new ArrayList<String>();
@@ -47,6 +48,8 @@ public class Permission implements Comparable<Permission> {
NAMES_LC.add(PUSH_TAG.toLowerCase()); NAMES_LC.add(PUSH_TAG.toLowerCase());
NAMES_LC.add(LABEL.toLowerCase()); NAMES_LC.add(LABEL.toLowerCase());
NAMES_LC.add(SUBMIT.toLowerCase()); NAMES_LC.add(SUBMIT.toLowerCase());
labelIndex = NAMES_LC.indexOf(Permission.LABEL);
} }
/** @return true if the name is recognized as a permission name. */ /** @return true if the name is recognized as a permission name. */
@@ -189,13 +192,18 @@ public class Permission implements Comparable<Permission> {
@Override @Override
public int compareTo(Permission b) { public int compareTo(Permission b) {
int cmp = index(this) - index(b); int cmp = index(this) - index(b);
if (cmp == 0) getName().compareTo(b.getName()); if (cmp == 0) {
cmp = getName().compareTo(b.getName());
}
return cmp; return cmp;
} }
private static int index(Permission a) { private static int index(Permission a) {
String lc = a.isLabel() ? Permission.LABEL : a.getName().toLowerCase(); if (a.isLabel()) {
int index = NAMES_LC.indexOf(lc); return labelIndex;
}
int index = NAMES_LC.indexOf(a.getName().toLowerCase());
return 0 <= index ? index : NAMES_LC.size(); return 0 <= index ? index : NAMES_LC.size();
} }
} }

View File

@@ -45,6 +45,7 @@ import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.ValueListBox; import com.google.gwt.user.client.ui.ValueListBox;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
public class AccessSectionEditor extends Composite implements public class AccessSectionEditor extends Composite implements
@@ -176,6 +177,8 @@ public class AccessSectionEditor extends Composite implements
@Override @Override
public void setValue(AccessSection value) { public void setValue(AccessSection value) {
Collections.sort(value.getPermissions());
this.value = value; this.value = value;
this.readOnly = !editing || !projectAccess.isOwnerOf(value); this.readOnly = !editing || !projectAccess.isOwnerOf(value);

View File

@@ -119,8 +119,10 @@ errorsMustBeFixed = Errors must be fixed before committing changes.
# Capability Names # Capability Names
capabilityNames = \ capabilityNames = \
administrateServer, \
createAccount, \ createAccount, \
createGroup, \ createGroup, \
createProject, \
flushCaches, \ flushCaches, \
killTask, \ killTask, \
queryLimit, \ queryLimit, \
@@ -128,8 +130,10 @@ capabilityNames = \
viewCaches, \ viewCaches, \
viewConnections, \ viewConnections, \
viewQueue viewQueue
administrateServer = Administrate Server
createAccount = Create Account createAccount = Create Account
createGroup = Create Group createGroup = Create Group
createProject = Create Project
flushCaches = Flush Caches flushCaches = Flush Caches
killTask = Kill Task killTask = Kill Task
queryLimit = Query Limit queryLimit = Query Limit

View File

@@ -18,12 +18,11 @@ import com.google.gerrit.common.data.ApprovalTypes;
import com.google.gerrit.common.data.GerritConfig; import com.google.gerrit.common.data.GerritConfig;
import com.google.gerrit.common.data.GitwebLink; import com.google.gerrit.common.data.GitwebLink;
import com.google.gerrit.reviewdb.Account; import com.google.gerrit.reviewdb.Account;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.server.account.Realm; import com.google.gerrit.server.account.Realm;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AuthConfig; import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.DownloadSchemeConfig; import com.google.gerrit.server.config.DownloadSchemeConfig;
import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.WildProjectName;
import com.google.gerrit.server.contact.ContactStore; import com.google.gerrit.server.contact.ContactStore;
import com.google.gerrit.server.mail.EmailSender; import com.google.gerrit.server.mail.EmailSender;
import com.google.gerrit.server.ssh.SshInfo; import com.google.gerrit.server.ssh.SshInfo;
@@ -50,7 +49,7 @@ class GerritConfigProvider implements Provider<GerritConfig> {
private final AuthConfig authConfig; private final AuthConfig authConfig;
private final DownloadSchemeConfig schemeConfig; private final DownloadSchemeConfig schemeConfig;
private final GitWebConfig gitWebConfig; private final GitWebConfig gitWebConfig;
private final Project.NameKey wildProject; private final AllProjectsName wildProject;
private final SshInfo sshInfo; private final SshInfo sshInfo;
private final ApprovalTypes approvalTypes; private final ApprovalTypes approvalTypes;
@@ -61,7 +60,7 @@ class GerritConfigProvider implements Provider<GerritConfig> {
@Inject @Inject
GerritConfigProvider(final Realm r, @GerritServerConfig final Config gsc, GerritConfigProvider(final Realm r, @GerritServerConfig final Config gsc,
final AuthConfig ac, final GitWebConfig gwc, final AuthConfig ac, final GitWebConfig gwc,
@WildProjectName final Project.NameKey wp, final SshInfo si, final AllProjectsName wp, final SshInfo si,
final ApprovalTypes at, final ContactStore cs, final ServletContext sc, final ApprovalTypes at, final ContactStore cs, final ServletContext sc,
final DownloadSchemeConfig dc) { final DownloadSchemeConfig dc) {
realm = r; realm = r;

View File

@@ -298,7 +298,7 @@ public class ChangeListServiceImpl extends BaseServiceImplementation implements
int maxLimit = currentUser.get().getCapabilities() int maxLimit = currentUser.get().getCapabilities()
.getRange(GlobalCapability.QUERY_LIMIT) .getRange(GlobalCapability.QUERY_LIMIT)
.getMax(); .getMax();
if (maxLimit == 0) { if (maxLimit <= 0) {
throw new InvalidQueryException("Search Disabled"); throw new InvalidQueryException("Search Disabled");
} }
return 0 < pageSize && pageSize <= maxLimit ? pageSize : maxLimit; return 0 < pageSize && pageSize <= maxLimit ? pageSize : maxLimit;

View File

@@ -94,7 +94,7 @@ class GroupAdminServiceImpl extends BaseServiceImplementation implements
public GroupList run(ReviewDb db) throws OrmException { public GroupList run(ReviewDb db) throws OrmException {
final IdentifiedUser user = identifiedUser.get(); final IdentifiedUser user = identifiedUser.get();
final List<AccountGroup> list; final List<AccountGroup> list;
if (user.isAdministrator()) { if (user.getCapabilities().canAdministrateServer()) {
list = db.accountGroups().all().toList(); list = db.accountGroups().all().toList();
} else { } else {
list = new ArrayList<AccountGroup>(); list = new ArrayList<AccountGroup>();

View File

@@ -24,7 +24,7 @@ import com.google.gerrit.reviewdb.AccountGroup;
import com.google.gerrit.reviewdb.Project; import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.server.account.GroupCache; import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.account.GroupControl; import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.config.WildProjectName; import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.git.MetaDataUpdate; import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.ProjectConfig; import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.project.NoSuchProjectException; import com.google.gerrit.server.project.NoSuchProjectException;
@@ -54,7 +54,7 @@ class ProjectAccessFactory extends Handler<ProjectAccess> {
private final ProjectControl.Factory projectControlFactory; private final ProjectControl.Factory projectControlFactory;
private final GroupControl.Factory groupControlFactory; private final GroupControl.Factory groupControlFactory;
private final MetaDataUpdate.Server metaDataUpdateFactory; private final MetaDataUpdate.Server metaDataUpdateFactory;
private final Project.NameKey wildProject; private final AllProjectsName allProjectsName;
private final Project.NameKey projectName; private final Project.NameKey projectName;
private ProjectControl pc; private ProjectControl pc;
@@ -65,8 +65,7 @@ class ProjectAccessFactory extends Handler<ProjectAccess> {
final ProjectControl.Factory projectControlFactory, final ProjectControl.Factory projectControlFactory,
final GroupControl.Factory groupControlFactory, final GroupControl.Factory groupControlFactory,
final MetaDataUpdate.Server metaDataUpdateFactory, final MetaDataUpdate.Server metaDataUpdateFactory,
@WildProjectName final Project.NameKey wildProject, final AllProjectsName allProjectsName,
@Assisted final Project.NameKey name) { @Assisted final Project.NameKey name) {
this.groupCache = groupCache; this.groupCache = groupCache;
@@ -74,7 +73,7 @@ class ProjectAccessFactory extends Handler<ProjectAccess> {
this.projectControlFactory = projectControlFactory; this.projectControlFactory = projectControlFactory;
this.groupControlFactory = groupControlFactory; this.groupControlFactory = groupControlFactory;
this.metaDataUpdateFactory = metaDataUpdateFactory; this.metaDataUpdateFactory = metaDataUpdateFactory;
this.wildProject = wildProject; this.allProjectsName = allProjectsName;
this.projectName = name; this.projectName = name;
} }
@@ -180,7 +179,7 @@ class ProjectAccessFactory extends Handler<ProjectAccess> {
final ProjectAccess detail = new ProjectAccess(); final ProjectAccess detail = new ProjectAccess();
detail.setRevision(config.getRevision().name()); detail.setRevision(config.getRevision().name());
if (projectName.equals(wildProject)) { if (projectName.equals(allProjectsName)) {
if (pc.isOwner()) { if (pc.isOwner()) {
ownerOf.add(AccessSection.GLOBAL_CAPABILITIES); ownerOf.add(AccessSection.GLOBAL_CAPABILITIES);
} }
@@ -190,7 +189,7 @@ class ProjectAccessFactory extends Handler<ProjectAccess> {
detail.setInheritsFrom(config.getProject().getParent()); detail.setInheritsFrom(config.getProject().getParent());
} else { } else {
detail.setInheritsFrom(wildProject); detail.setInheritsFrom(allProjectsName);
} }
detail.setLocal(local); detail.setLocal(local);

View File

@@ -49,6 +49,19 @@ public final class Project {
return get().compareTo(other.get()); return get().compareTo(other.get());
} }
@Override
public int hashCode() {
return get().hashCode();
}
@Override
public boolean equals(Object b) {
if (b instanceof NameKey) {
return get().equals(((NameKey) b).get());
}
return false;
}
/** Parse a Project.NameKey out of a string representation. */ /** Parse a Project.NameKey out of a string representation. */
public static NameKey parse(final String str) { public static NameKey parse(final String str) {
final NameKey r = new NameKey(); final NameKey r = new NameKey();

View File

@@ -62,32 +62,32 @@ public final class SystemConfig {
@Column(id = 3, notNull = false) @Column(id = 3, notNull = false)
public transient String sitePath; public transient String sitePath;
/** Identity of the administration group; those with full access. */
@Column(id = 4)
public AccountGroup.Id adminGroupId;
@Column(id = 10)
public AccountGroup.UUID adminGroupUUID;
/** Identity of the anonymous group, which permits anyone. */
@Column(id = 5)
public AccountGroup.Id anonymousGroupId;
/** Identity of the registered users group, which permits anyone. */
@Column(id = 6)
public AccountGroup.Id registeredGroupId;
/** Identity of the project */
@Column(id = 7)
public Project.NameKey wildProjectName;
/** Identity of the batch users group */
@Column(id = 8) @Column(id = 8)
public AccountGroup.Id batchUsersGroupId; public AccountGroup.Id batchUsersGroupId;
@Column(id = 11) @Column(id = 11)
public AccountGroup.UUID batchUsersGroupUUID; public AccountGroup.UUID batchUsersGroupUUID;
/** Identity of the owner group, which permits any project owner. */
@Column(id = 9) // DO NOT LOOK BELOW THIS LINE. These fields have all been deleted,
// but survive to support schema upgrade code.
/** DEPRECATED DO NOT USE */
@Column(id = 4, notNull = false)
public AccountGroup.Id adminGroupId;
/** DEPRECATED DO NOT USE */
@Column(id = 10, notNull = false)
public AccountGroup.UUID adminGroupUUID;
/** DEPRECATED DO NOT USE */
@Column(id = 5, notNull = false)
public AccountGroup.Id anonymousGroupId;
/** DEPRECATED DO NOT USE */
@Column(id = 6, notNull = false)
public AccountGroup.Id registeredGroupId;
/** DEPRECATED DO NOT USE */
@Column(id = 7, notNull = false)
public Project.NameKey wildProjectName;
/** DEPRECATED DO NOT USE */
@Column(id = 9, notNull = false)
public AccountGroup.Id ownerGroupId; public AccountGroup.Id ownerGroupId;
protected SystemConfig() { protected SystemConfig() {

View File

@@ -77,10 +77,6 @@ public abstract class CurrentUser {
return getEffectiveGroups().contains(authConfig.getBatchUsersGroup()); return getEffectiveGroups().contains(authConfig.getBatchUsersGroup());
} }
public boolean isAdministrator() {
return getEffectiveGroups().contains(authConfig.getAdministratorsGroup());
}
/** Capabilities available to this user account. */ /** Capabilities available to this user account. */
public CapabilityControl getCapabilities() { public CapabilityControl getCapabilities() {
CapabilityControl ctl = capabilities; CapabilityControl ctl = capabilities;

View File

@@ -25,7 +25,6 @@ import com.google.inject.assistedinject.Assisted;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
/** Identity of a peer daemon process that isn't this JVM. */ /** Identity of a peer daemon process that isn't this JVM. */
@@ -37,23 +36,18 @@ public class PeerDaemonUser extends CurrentUser {
PeerDaemonUser create(@Assisted SocketAddress peer); PeerDaemonUser create(@Assisted SocketAddress peer);
} }
private final Set<AccountGroup.UUID> effectiveGroups;
private final SocketAddress peer; private final SocketAddress peer;
@Inject @Inject
protected PeerDaemonUser(CapabilityControl.Factory capabilityControlFactory, protected PeerDaemonUser(CapabilityControl.Factory capabilityControlFactory,
AuthConfig authConfig, @Assisted SocketAddress peer) { AuthConfig authConfig, @Assisted SocketAddress peer) {
super(capabilityControlFactory, AccessPath.SSH_COMMAND, authConfig); super(capabilityControlFactory, AccessPath.SSH_COMMAND, authConfig);
final HashSet<AccountGroup.UUID> g = new HashSet<AccountGroup.UUID>();
g.add(authConfig.getAdministratorsGroup());
this.effectiveGroups = Collections.unmodifiableSet(g);
this.peer = peer; this.peer = peer;
} }
@Override @Override
public Set<AccountGroup.UUID> getEffectiveGroups() { public Set<AccountGroup.UUID> getEffectiveGroups() {
return effectiveGroups; return Collections.emptySet();
} }
@Override @Override

View File

@@ -15,6 +15,9 @@
package com.google.gerrit.server.account; package com.google.gerrit.server.account;
import com.google.gerrit.common.auth.openid.OpenIdUrls; import com.google.gerrit.common.auth.openid.OpenIdUrls;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.errors.InvalidUserNameException; import com.google.gerrit.common.errors.InvalidUserNameException;
import com.google.gerrit.common.errors.NameAlreadyUsedException; import com.google.gerrit.common.errors.NameAlreadyUsedException;
import com.google.gerrit.reviewdb.Account; import com.google.gerrit.reviewdb.Account;
@@ -25,6 +28,7 @@ import com.google.gerrit.reviewdb.AccountGroupMemberAudit;
import com.google.gerrit.reviewdb.ReviewDb; import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.AuthConfig; import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gwtorm.client.OrmException; import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.SchemaFactory; import com.google.gwtorm.client.SchemaFactory;
import com.google.inject.Inject; import com.google.inject.Inject;
@@ -51,6 +55,7 @@ public class AccountManager {
private final Realm realm; private final Realm realm;
private final IdentifiedUser.GenericFactory userFactory; private final IdentifiedUser.GenericFactory userFactory;
private final ChangeUserName.Factory changeUserNameFactory; private final ChangeUserName.Factory changeUserNameFactory;
private final ProjectCache projectCache;
private final AtomicBoolean firstAccount; private final AtomicBoolean firstAccount;
@Inject @Inject
@@ -58,7 +63,8 @@ public class AccountManager {
final AccountCache byIdCache, final AccountByEmailCache byEmailCache, final AccountCache byIdCache, final AccountByEmailCache byEmailCache,
final AuthConfig authConfig, final Realm accountMapper, final AuthConfig authConfig, final Realm accountMapper,
final IdentifiedUser.GenericFactory userFactory, final IdentifiedUser.GenericFactory userFactory,
final ChangeUserName.Factory changeUserNameFactory) throws OrmException { final ChangeUserName.Factory changeUserNameFactory,
final ProjectCache projectCache) throws OrmException {
this.schema = schema; this.schema = schema;
this.byIdCache = byIdCache; this.byIdCache = byIdCache;
this.byEmailCache = byEmailCache; this.byEmailCache = byEmailCache;
@@ -66,6 +72,7 @@ public class AccountManager {
this.realm = accountMapper; this.realm = accountMapper;
this.userFactory = userFactory; this.userFactory = userFactory;
this.changeUserNameFactory = changeUserNameFactory; this.changeUserNameFactory = changeUserNameFactory;
this.projectCache = projectCache;
firstAccount = new AtomicBoolean(); firstAccount = new AtomicBoolean();
final ReviewDb db = schema.open(); final ReviewDb db = schema.open();
@@ -275,11 +282,16 @@ public class AccountManager {
// is going to be the site's administrator and just make them that // is going to be the site's administrator and just make them that
// to bootstrap the authentication database. // to bootstrap the authentication database.
// //
final AccountGroup.UUID uuid = authConfig.getAdministratorsGroup(); Permission admin = projectCache.getAllProjects()
.getConfig()
.getAccessSection(AccessSection.GLOBAL_CAPABILITIES)
.getPermission(GlobalCapability.ADMINISTRATE_SERVER);
final AccountGroup.UUID uuid = admin.getRules().get(0).getGroup().getUUID();
final AccountGroup g = db.accountGroups().byUUID(uuid).iterator().next(); final AccountGroup g = db.accountGroups().byUUID(uuid).iterator().next();
final AccountGroup.Id admin = g.getId(); final AccountGroup.Id adminId = g.getId();
final AccountGroupMember m = final AccountGroupMember m =
new AccountGroupMember(new AccountGroupMember.Key(newId, admin)); new AccountGroupMember(new AccountGroupMember.Key(newId, adminId));
db.accountGroupMembersAudit().insert( db.accountGroupMembersAudit().insert(
Collections.singleton(new AccountGroupMemberAudit(m, newId))); Collections.singleton(new AccountGroupMemberAudit(m, newId)));
db.accountGroupMembers().insert(Collections.singleton(m)); db.accountGroupMembers().insert(Collections.singleton(m));

View File

@@ -21,10 +21,8 @@ import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRange; import com.google.gerrit.common.data.PermissionRange;
import com.google.gerrit.common.data.PermissionRule; import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.reviewdb.AccountGroup; import com.google.gerrit.reviewdb.AccountGroup;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.WildProjectName; import com.google.gerrit.server.PeerDaemonUser;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState; import com.google.gerrit.server.project.ProjectState;
import com.google.inject.Inject; import com.google.inject.Inject;
@@ -47,15 +45,11 @@ public class CapabilityControl {
private final CurrentUser user; private final CurrentUser user;
private Map<String, List<PermissionRule>> permissions; private Map<String, List<PermissionRule>> permissions;
private Boolean canAdministrateServer;
@Inject @Inject
CapabilityControl( CapabilityControl(ProjectCache projectCache, @Assisted CurrentUser currentUser) {
@WildProjectName Project.NameKey wp, state = projectCache.getAllProjects();
ProjectCache projectCache,
@Assisted CurrentUser currentUser) throws NoSuchProjectException {
state = projectCache.get(wp);
if (state == null) {
throw new NoSuchProjectException(wp);
}
user = currentUser; user = currentUser;
} }
@@ -64,44 +58,67 @@ public class CapabilityControl {
return user; return user;
} }
/** @return true if the user can administer this server. */
public boolean canAdministrateServer() {
if (canAdministrateServer == null) {
canAdministrateServer = user instanceof PeerDaemonUser
|| canPerform(GlobalCapability.ADMINISTRATE_SERVER);
}
return canAdministrateServer;
}
/** @return true if the user can create an account for another user. */ /** @return true if the user can create an account for another user. */
public boolean canCreateAccount() { public boolean canCreateAccount() {
return canPerform(GlobalCapability.CREATE_ACCOUNT) || user.isAdministrator(); return canPerform(GlobalCapability.CREATE_ACCOUNT)
|| canAdministrateServer();
} }
/** @return true if the user can create a group. */ /** @return true if the user can create a group. */
public boolean canCreateGroup() { public boolean canCreateGroup() {
return canPerform(GlobalCapability.CREATE_GROUP) || user.isAdministrator(); return canPerform(GlobalCapability.CREATE_GROUP)
|| canAdministrateServer();
}
/** @return true if the user can create a group. */
public boolean canCreateProject() {
return canPerform(GlobalCapability.CREATE_PROJECT)
|| canAdministrateServer();
} }
/** @return true if the user can kill any running task. */ /** @return true if the user can kill any running task. */
public boolean canKillTask() { public boolean canKillTask() {
return canPerform(GlobalCapability.KILL_TASK) || user.isAdministrator(); return canPerform(GlobalCapability.KILL_TASK)
|| canAdministrateServer();
} }
/** @return true if the user can view the server caches. */ /** @return true if the user can view the server caches. */
public boolean canViewCaches() { public boolean canViewCaches() {
return canPerform(GlobalCapability.VIEW_CACHES) || user.isAdministrator(); return canPerform(GlobalCapability.VIEW_CACHES)
|| canAdministrateServer();
} }
/** @return true if the user can flush the server's caches. */ /** @return true if the user can flush the server's caches. */
public boolean canFlushCaches() { public boolean canFlushCaches() {
return canPerform(GlobalCapability.FLUSH_CACHES) || user.isAdministrator(); return canPerform(GlobalCapability.FLUSH_CACHES)
|| canAdministrateServer();
} }
/** @return true if the user can view open connections. */ /** @return true if the user can view open connections. */
public boolean canViewConnections() { public boolean canViewConnections() {
return canPerform(GlobalCapability.VIEW_CONNECTIONS) || user.isAdministrator(); return canPerform(GlobalCapability.VIEW_CONNECTIONS)
|| canAdministrateServer();
} }
/** @return true if the user can view the entire queue. */ /** @return true if the user can view the entire queue. */
public boolean canViewQueue() { public boolean canViewQueue() {
return canPerform(GlobalCapability.VIEW_QUEUE) || user.isAdministrator(); return canPerform(GlobalCapability.VIEW_QUEUE)
|| canAdministrateServer();
} }
/** @return true if the user can force replication to any configured destination. */ /** @return true if the user can force replication to any configured destination. */
public boolean canStartReplication() { public boolean canStartReplication() {
return canPerform(GlobalCapability.START_REPLICATION) || user.isAdministrator(); return canPerform(GlobalCapability.START_REPLICATION)
|| canAdministrateServer();
} }
/** True if the user has this permission. Works only for non labels. */ /** True if the user has this permission. Works only for non labels. */

View File

@@ -97,7 +97,7 @@ public class GroupControl {
AccountGroup g = groupCache.get(group.getOwnerGroupId()); AccountGroup g = groupCache.get(group.getOwnerGroupId());
AccountGroup.UUID ownerUUID = g != null ? g.getGroupUUID() : null; AccountGroup.UUID ownerUUID = g != null ? g.getGroupUUID() : null;
isOwner = getCurrentUser().getEffectiveGroups().contains(ownerUUID) isOwner = getCurrentUser().getEffectiveGroups().contains(ownerUUID)
|| getCurrentUser().isAdministrator(); || getCurrentUser().getCapabilities().canAdministrateServer();
} }
return isOwner; return isOwner;
} }

View File

@@ -15,19 +15,11 @@
package com.google.gerrit.server.config; package com.google.gerrit.server.config;
import com.google.gerrit.reviewdb.Project; import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.SystemConfig;
import com.google.inject.Inject;
import com.google.inject.Provider;
public class WildProjectNameProvider implements Provider<Project.NameKey> { /** Special name of the project that all projects derive from. */
private final Project.NameKey name; @SuppressWarnings("serial")
public class AllProjectsName extends Project.NameKey {
@Inject public AllProjectsName(String name) {
WildProjectNameProvider(final SystemConfig config) { super(name);
name = config.wildProjectName;
}
public Project.NameKey get() {
return name;
} }
} }

View File

@@ -14,20 +14,26 @@
package com.google.gerrit.server.config; package com.google.gerrit.server.config;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.gerrit.reviewdb.Project; import org.eclipse.jgit.lib.Config;
import com.google.inject.BindingAnnotation;
import java.lang.annotation.Retention; public class AllProjectsNameProvider implements Provider<AllProjectsName> {
public static final String DEFAULT = "All-Projects";
/** private final AllProjectsName name;
* Marker on a {@link Project.NameKey} for the current wildcard project.
* <p> @Inject
* This is the name of the project whose rights inherit into every other project AllProjectsNameProvider(@GerritServerConfig Config cfg) {
* in the system. String n = cfg.getString("gerrit", null, "allProjects");
*/ if (n == null || n.isEmpty()) {
@Retention(RUNTIME) n = DEFAULT;
@BindingAnnotation }
public @interface WildProjectName { name = new AllProjectsName(n);
}
public AllProjectsName get() {
return name;
}
} }

View File

@@ -43,7 +43,6 @@ public class AuthConfig {
private final boolean cookieSecure; private final boolean cookieSecure;
private final SignedToken emailReg; private final SignedToken emailReg;
private final AccountGroup.UUID administratorGroup;
private final AccountGroup.UUID batchUsersGroup; private final AccountGroup.UUID batchUsersGroup;
private final boolean allowGoogleAccountUpgrade; private final boolean allowGoogleAccountUpgrade;
@@ -60,7 +59,6 @@ public class AuthConfig {
cookieSecure = cfg.getBoolean("auth", "cookiesecure", false); cookieSecure = cfg.getBoolean("auth", "cookiesecure", false);
emailReg = new SignedToken(5 * 24 * 60 * 60, s.registerEmailPrivateKey); emailReg = new SignedToken(5 * 24 * 60 * 60, s.registerEmailPrivateKey);
administratorGroup = s.adminGroupUUID;
batchUsersGroup = s.batchUsersGroupUUID; batchUsersGroup = s.batchUsersGroupUUID;
if (authType == AuthType.OPENID) { if (authType == AuthType.OPENID) {
@@ -117,11 +115,6 @@ public class AuthConfig {
return allowGoogleAccountUpgrade; return allowGoogleAccountUpgrade;
} }
/** Identity of the magic group with full powers. */
public AccountGroup.UUID getAdministratorsGroup() {
return administratorGroup;
}
/** Identity of the group whose service is degraded to lower priority. */ /** Identity of the group whose service is degraded to lower priority. */
public AccountGroup.UUID getBatchUsersGroup() { public AccountGroup.UUID getBatchUsersGroup() {
return batchUsersGroup; return batchUsersGroup;

View File

@@ -133,8 +133,6 @@ public class GerritGlobalModule extends FactoryModule {
break; break;
} }
bind(Project.NameKey.class).annotatedWith(WildProjectName.class)
.toProvider(WildProjectNameProvider.class).in(SINGLETON);
bind(ApprovalTypes.class).toProvider(ApprovalTypesProvider.class).in( bind(ApprovalTypes.class).toProvider(ApprovalTypesProvider.class).in(
SINGLETON); SINGLETON);
bind(EmailExpander.class).toProvider(EmailExpanderProvider.class).in( bind(EmailExpander.class).toProvider(EmailExpanderProvider.class).in(

View File

@@ -1,30 +0,0 @@
// Copyright (C) 2010 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.
package com.google.gerrit.server.config;
import com.google.inject.BindingAnnotation;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Marker on a {@code Set&lt;AccountGroup.Id>} for the configured groups with
* permission to create projects.
*/
@Retention(RUNTIME)
@BindingAnnotation
public @interface ProjectCreatorGroups {
}

View File

@@ -1,46 +0,0 @@
// Copyright (C) 2010 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.
package com.google.gerrit.server.config;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gwtorm.client.SchemaFactory;
import com.google.inject.Inject;
import org.eclipse.jgit.lib.Config;
import java.util.Collections;
/**
* Provider of the group(s) which are allowed to create new projects. Currently
* only supports {@code createGroup} declarations in the {@code "*"} repository,
* like so:
*
* <pre>
* [repository &quot;*&quot;]
* createGroup = Registered Users
* createGroup = Administrators
* </pre>
*/
public class ProjectCreatorGroupsProvider extends GroupSetProvider {
@Inject
public ProjectCreatorGroupsProvider(@GerritServerConfig final Config config,
final AuthConfig authConfig, final SchemaFactory<ReviewDb> db) {
super(config, db, "repository", "*", "createGroup");
if (groupIds.isEmpty()) {
groupIds = Collections.singleton(authConfig.getAdministratorsGroup());
}
}
}

View File

@@ -14,15 +14,12 @@
package com.google.gerrit.server.config; package com.google.gerrit.server.config;
import com.google.gerrit.reviewdb.AccountGroup;
import com.google.gerrit.reviewdb.ReviewDb; import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gwtorm.client.SchemaFactory; import com.google.gwtorm.client.SchemaFactory;
import com.google.inject.Inject; import com.google.inject.Inject;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
import java.util.Set;
/** /**
* Provider of the group(s) which should become owners of a newly created * Provider of the group(s) which should become owners of a newly created
* project. Currently only supports {@code ownerGroup} declarations in the * project. Currently only supports {@code ownerGroup} declarations in the
@@ -37,12 +34,7 @@ import java.util.Set;
public class ProjectOwnerGroupsProvider extends GroupSetProvider { public class ProjectOwnerGroupsProvider extends GroupSetProvider {
@Inject @Inject
public ProjectOwnerGroupsProvider( public ProjectOwnerGroupsProvider(
@ProjectCreatorGroups final Set<AccountGroup.UUID> creatorGroups,
@GerritServerConfig final Config config, final SchemaFactory<ReviewDb> db) { @GerritServerConfig final Config config, final SchemaFactory<ReviewDb> db) {
super(config, db, "repository", "*", "ownerGroup"); super(config, db, "repository", "*", "ownerGroup");
if (groupIds.isEmpty()) {
groupIds = creatorGroups;
}
} }
} }

View File

@@ -336,7 +336,7 @@ public abstract class ChangeEmail extends OutgoingEmail {
} }
for (AccountProjectWatch w : args.db.get().accountProjectWatches() for (AccountProjectWatch w : args.db.get().accountProjectWatches()
.byProject(args.wildProject)) { .byProject(args.allProjectsName)) {
if (!projectWatchers.contains(w.getAccountId())) { if (!projectWatchers.contains(w.getAccountId())) {
add(matching, w); add(matching, w);
} }

View File

@@ -14,15 +14,14 @@
package com.google.gerrit.server.mail; package com.google.gerrit.server.mail;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.ReviewDb; import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.IdentifiedUser.GenericFactory; import com.google.gerrit.server.IdentifiedUser.GenericFactory;
import com.google.gerrit.server.account.AccountCache; import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.GroupCache; import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.CanonicalWebUrl; import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.SitePaths; import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.config.WildProjectName;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.patch.PatchListCache; import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.patch.PatchSetInfoFactory; import com.google.gerrit.server.patch.PatchSetInfoFactory;
@@ -45,7 +44,7 @@ class EmailArguments {
final PatchSetInfoFactory patchSetInfoFactory; final PatchSetInfoFactory patchSetInfoFactory;
final IdentifiedUser.GenericFactory identifiedUserFactory; final IdentifiedUser.GenericFactory identifiedUserFactory;
final Provider<String> urlProvider; final Provider<String> urlProvider;
final Project.NameKey wildProject; final AllProjectsName allProjectsName;
final ChangeQueryBuilder.Factory queryBuilder; final ChangeQueryBuilder.Factory queryBuilder;
final Provider<ChangeQueryRewriter> queryRewriter; final Provider<ChangeQueryRewriter> queryRewriter;
@@ -59,7 +58,7 @@ class EmailArguments {
EmailSender emailSender, PatchSetInfoFactory patchSetInfoFactory, EmailSender emailSender, PatchSetInfoFactory patchSetInfoFactory,
GenericFactory identifiedUserFactory, GenericFactory identifiedUserFactory,
@CanonicalWebUrl @Nullable Provider<String> urlProvider, @CanonicalWebUrl @Nullable Provider<String> urlProvider,
@WildProjectName Project.NameKey wildProject, AllProjectsName allProjectsName,
ChangeQueryBuilder.Factory queryBuilder, ChangeQueryBuilder.Factory queryBuilder,
Provider<ChangeQueryRewriter> queryRewriter, Provider<ReviewDb> db, Provider<ChangeQueryRewriter> queryRewriter, Provider<ReviewDb> db,
SitePaths site) { SitePaths site) {
@@ -73,7 +72,7 @@ class EmailArguments {
this.patchSetInfoFactory = patchSetInfoFactory; this.patchSetInfoFactory = patchSetInfoFactory;
this.identifiedUserFactory = identifiedUserFactory; this.identifiedUserFactory = identifiedUserFactory;
this.urlProvider = urlProvider; this.urlProvider = urlProvider;
this.wildProject = wildProject; this.allProjectsName = allProjectsName;
this.queryBuilder = queryBuilder; this.queryBuilder = queryBuilder;
this.queryRewriter = queryRewriter; this.queryRewriter = queryRewriter;
this.db = db; this.db = db;

View File

@@ -22,8 +22,6 @@ import com.google.gerrit.server.config.GitReceivePackGroups;
import com.google.gerrit.server.config.GitReceivePackGroupsProvider; import com.google.gerrit.server.config.GitReceivePackGroupsProvider;
import com.google.gerrit.server.config.GitUploadPackGroups; import com.google.gerrit.server.config.GitUploadPackGroups;
import com.google.gerrit.server.config.GitUploadPackGroupsProvider; import com.google.gerrit.server.config.GitUploadPackGroupsProvider;
import com.google.gerrit.server.config.ProjectCreatorGroups;
import com.google.gerrit.server.config.ProjectCreatorGroupsProvider;
import com.google.gerrit.server.config.ProjectOwnerGroups; import com.google.gerrit.server.config.ProjectOwnerGroups;
import com.google.gerrit.server.config.ProjectOwnerGroupsProvider; import com.google.gerrit.server.config.ProjectOwnerGroupsProvider;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
@@ -33,10 +31,6 @@ import java.util.Set;
public class AccessControlModule extends FactoryModule { public class AccessControlModule extends FactoryModule {
@Override @Override
protected void configure() { protected void configure() {
bind(new TypeLiteral<Set<AccountGroup.UUID>>() {}) //
.annotatedWith(ProjectCreatorGroups.class) //
.toProvider(ProjectCreatorGroupsProvider.class).in(SINGLETON);
bind(new TypeLiteral<Set<AccountGroup.UUID>>() {}) // bind(new TypeLiteral<Set<AccountGroup.UUID>>() {}) //
.annotatedWith(ProjectOwnerGroups.class) // .annotatedWith(ProjectOwnerGroups.class) //
.toProvider(ProjectOwnerGroupsProvider.class).in(SINGLETON); .toProvider(ProjectOwnerGroupsProvider.class).in(SINGLETON);

View File

@@ -161,7 +161,7 @@ public class ChangeControl {
return isOwner() // owner (aka creator) of the change can abandon return isOwner() // owner (aka creator) of the change can abandon
|| getRefControl().isOwner() // branch owner can abandon || getRefControl().isOwner() // branch owner can abandon
|| getProjectControl().isOwner() // project owner can abandon || getProjectControl().isOwner() // project owner can abandon
|| getCurrentUser().isAdministrator() // site administers are god || getCurrentUser().getCapabilities().canAdministrateServer() // site administers are god
; ;
} }
@@ -216,7 +216,7 @@ public class ChangeControl {
// //
if (getRefControl().isOwner() // branch owner if (getRefControl().isOwner() // branch owner
|| getProjectControl().isOwner() // project owner || getProjectControl().isOwner() // project owner
|| getCurrentUser().isAdministrator()) { || getCurrentUser().getCapabilities().canAdministrateServer()) {
return true; return true;
} }
} }

View File

@@ -18,6 +18,9 @@ import com.google.gerrit.reviewdb.Project;
/** Cache of project information, including access rights. */ /** Cache of project information, including access rights. */
public interface ProjectCache { public interface ProjectCache {
/** @return the parent state for all projects on this server. */
public ProjectState getAllProjects();
/** /**
* Get the cached data for a project by its unique name. * Get the cached data for a project by its unique name.
* *

View File

@@ -18,6 +18,7 @@ import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.server.cache.Cache; import com.google.gerrit.server.cache.Cache;
import com.google.gerrit.server.cache.CacheModule; import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.cache.EntryCreator; import com.google.gerrit.server.cache.EntryCreator;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.ConfigUtil; import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
@@ -66,6 +67,7 @@ public class ProjectCacheImpl implements ProjectCache {
}; };
} }
private final AllProjectsName allProjectsName;
private final Cache<Project.NameKey, ProjectState> byName; private final Cache<Project.NameKey, ProjectState> byName;
private final Cache<ListKey,SortedSet<Project.NameKey>> list; private final Cache<ListKey,SortedSet<Project.NameKey>> list;
private final Lock listLock; private final Lock listLock;
@@ -73,9 +75,11 @@ public class ProjectCacheImpl implements ProjectCache {
@Inject @Inject
ProjectCacheImpl( ProjectCacheImpl(
final AllProjectsName allProjectsName,
@Named(CACHE_NAME) final Cache<Project.NameKey, ProjectState> byName, @Named(CACHE_NAME) final Cache<Project.NameKey, ProjectState> byName,
@Named(CACHE_LIST) final Cache<ListKey, SortedSet<Project.NameKey>> list, @Named(CACHE_LIST) final Cache<ListKey, SortedSet<Project.NameKey>> list,
@GerritServerConfig final Config serverConfig) { @GerritServerConfig final Config serverConfig) {
this.allProjectsName = allProjectsName;
this.byName = byName; this.byName = byName;
this.list = list; this.list = list;
this.listLock = new ReentrantLock(true /* fair */); this.listLock = new ReentrantLock(true /* fair */);
@@ -102,6 +106,17 @@ public class ProjectCacheImpl implements ProjectCache {
} }
} }
@Override
public ProjectState getAllProjects() {
ProjectState state = get(allProjectsName);
if (state == null) {
// This should never occur, the server must have this
// project to process anything.
throw new IllegalStateException("Missing project " + allProjectsName);
}
return state;
}
/** /**
* Get the cached data for a project by its unique name. * Get the cached data for a project by its unique name.
* *

View File

@@ -180,13 +180,13 @@ public class ProjectControl {
/** Is this user a project owner? Ownership does not imply {@link #isVisible()} */ /** Is this user a project owner? Ownership does not imply {@link #isVisible()} */
public boolean isOwner() { public boolean isOwner() {
return controlForRef(AccessSection.ALL).isOwner() return controlForRef(AccessSection.ALL).isOwner()
|| getCurrentUser().isAdministrator(); || getCurrentUser().getCapabilities().canAdministrateServer();
} }
/** Does this user have ownership on at least one reference name? */ /** Does this user have ownership on at least one reference name? */
public boolean isOwnerAnyRef() { public boolean isOwnerAnyRef() {
return canPerformOnAnyRef(Permission.OWNER) return canPerformOnAnyRef(Permission.OWNER)
|| getCurrentUser().isAdministrator(); || getCurrentUser().getCapabilities().canAdministrateServer();
} }
/** @return true if the user can upload to at least one reference */ /** @return true if the user can upload to at least one reference */

View File

@@ -22,7 +22,7 @@ import com.google.gerrit.reviewdb.AccountGroup;
import com.google.gerrit.reviewdb.Project; import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.rules.PrologEnvironment; import com.google.gerrit.rules.PrologEnvironment;
import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.WildProjectName; import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.ProjectConfig; import com.google.gerrit.server.git.ProjectConfig;
import com.google.inject.Inject; import com.google.inject.Inject;
@@ -52,7 +52,7 @@ public class ProjectState {
ProjectState create(ProjectConfig config); ProjectState create(ProjectConfig config);
} }
private final Project.NameKey wildProject; private final boolean isAllProjects;
private final ProjectCache projectCache; private final ProjectCache projectCache;
private final ProjectControl.AssistedFactory projectControlFactory; private final ProjectControl.AssistedFactory projectControlFactory;
private final PrologEnvironment.Factory envFactory; private final PrologEnvironment.Factory envFactory;
@@ -67,13 +67,13 @@ public class ProjectState {
@Inject @Inject
protected ProjectState( protected ProjectState(
final ProjectCache projectCache, final ProjectCache projectCache,
@WildProjectName final Project.NameKey wildProject, final AllProjectsName allProjectsName,
final ProjectControl.AssistedFactory projectControlFactory, final ProjectControl.AssistedFactory projectControlFactory,
final PrologEnvironment.Factory envFactory, final PrologEnvironment.Factory envFactory,
final GitRepositoryManager gitMgr, final GitRepositoryManager gitMgr,
@Assisted final ProjectConfig config) { @Assisted final ProjectConfig config) {
this.projectCache = projectCache; this.projectCache = projectCache;
this.wildProject = wildProject; this.isAllProjects = config.getProject().getNameKey().equals(allProjectsName);
this.projectControlFactory = projectControlFactory; this.projectControlFactory = projectControlFactory;
this.envFactory = envFactory; this.envFactory = envFactory;
this.gitMgr = gitMgr; this.gitMgr = gitMgr;
@@ -160,7 +160,7 @@ public class ProjectState {
/** Get the rights this project inherits. */ /** Get the rights this project inherits. */
public Collection<AccessSection> getInheritedAccessSections() { public Collection<AccessSection> getInheritedAccessSections() {
if (isWildProject()) { if (isAllProjects) {
return Collections.emptyList(); return Collections.emptyList();
} }
@@ -178,12 +178,9 @@ public class ProjectState {
} }
} }
// Wild project is the parent, or the root of the tree // The root of the tree is the special "All-Projects" case.
if (parent == null) { if (parent == null) {
ProjectState s = projectCache.get(wildProject); inherited.addAll(projectCache.getAllProjects().getLocalAccessSections());
if (s != null) {
inherited.addAll(s.getLocalAccessSections());
}
} }
return inherited; return inherited;
@@ -205,7 +202,7 @@ public class ProjectState {
*/ */
public Set<AccountGroup.UUID> getOwners() { public Set<AccountGroup.UUID> getOwners() {
Project.NameKey parentName = getProject().getParent(); Project.NameKey parentName = getProject().getParent();
if (!localOwners.isEmpty() || parentName == null || isWildProject()) { if (!localOwners.isEmpty() || parentName == null || isAllProjects) {
return localOwners; return localOwners;
} }
@@ -247,8 +244,4 @@ public class ProjectState {
public ProjectControl controlFor(final CurrentUser user) { public ProjectControl controlFor(final CurrentUser user) {
return projectControlFactory.create(user, this); return projectControlFactory.create(user, this);
} }
private boolean isWildProject() {
return wildProject.equals(getProject().getNameKey());
}
} }

View File

@@ -106,7 +106,7 @@ public class RefControl {
// calls us to find out if there is ownership of all references in // calls us to find out if there is ownership of all references in
// order to determine project level ownership. // order to determine project level ownership.
// //
owner = getCurrentUser().isAdministrator(); owner = getCurrentUser().getCapabilities().canAdministrateServer();
} else { } else {
owner = getProjectControl().isOwner(); owner = getProjectControl().isOwner();

View File

@@ -24,8 +24,9 @@ import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountResolver; import com.google.gerrit.server.account.AccountResolver;
import com.google.gerrit.server.account.CapabilityControl;
import com.google.gerrit.server.account.GroupCache; import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.config.WildProjectName; import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.patch.PatchListCache; import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.project.ChangeControl; import com.google.gerrit.server.project.ChangeControl;
@@ -98,12 +99,13 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
final Provider<ReviewDb> dbProvider; final Provider<ReviewDb> dbProvider;
final Provider<ChangeQueryRewriter> rewriter; final Provider<ChangeQueryRewriter> rewriter;
final IdentifiedUser.GenericFactory userFactory; final IdentifiedUser.GenericFactory userFactory;
final CapabilityControl.Factory capabilityControlFactory;
final ChangeControl.Factory changeControlFactory; final ChangeControl.Factory changeControlFactory;
final ChangeControl.GenericFactory changeControlGenericFactory; final ChangeControl.GenericFactory changeControlGenericFactory;
final AccountResolver accountResolver; final AccountResolver accountResolver;
final GroupCache groupCache; final GroupCache groupCache;
final ApprovalTypes approvalTypes; final ApprovalTypes approvalTypes;
final Project.NameKey wildProjectName; final AllProjectsName allProjectsName;
final PatchListCache patchListCache; final PatchListCache patchListCache;
final GitRepositoryManager repoManager; final GitRepositoryManager repoManager;
final ProjectCache projectCache; final ProjectCache projectCache;
@@ -112,23 +114,25 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
Arguments(Provider<ReviewDb> dbProvider, Arguments(Provider<ReviewDb> dbProvider,
Provider<ChangeQueryRewriter> rewriter, Provider<ChangeQueryRewriter> rewriter,
IdentifiedUser.GenericFactory userFactory, IdentifiedUser.GenericFactory userFactory,
CapabilityControl.Factory capabilityControlFactory,
ChangeControl.Factory changeControlFactory, ChangeControl.Factory changeControlFactory,
ChangeControl.GenericFactory changeControlGenericFactory, ChangeControl.GenericFactory changeControlGenericFactory,
AccountResolver accountResolver, GroupCache groupCache, AccountResolver accountResolver, GroupCache groupCache,
ApprovalTypes approvalTypes, ApprovalTypes approvalTypes,
@WildProjectName Project.NameKey wildProjectName, AllProjectsName allProjectsName,
PatchListCache patchListCache, PatchListCache patchListCache,
GitRepositoryManager repoManager, GitRepositoryManager repoManager,
ProjectCache projectCache) { ProjectCache projectCache) {
this.dbProvider = dbProvider; this.dbProvider = dbProvider;
this.rewriter = rewriter; this.rewriter = rewriter;
this.userFactory = userFactory; this.userFactory = userFactory;
this.capabilityControlFactory = capabilityControlFactory;
this.changeControlFactory = changeControlFactory; this.changeControlFactory = changeControlFactory;
this.changeControlGenericFactory = changeControlGenericFactory; this.changeControlGenericFactory = changeControlGenericFactory;
this.accountResolver = accountResolver; this.accountResolver = accountResolver;
this.groupCache = groupCache; this.groupCache = groupCache;
this.approvalTypes = approvalTypes; this.approvalTypes = approvalTypes;
this.wildProjectName = wildProjectName; this.allProjectsName = allProjectsName;
this.patchListCache = patchListCache; this.patchListCache = patchListCache;
this.repoManager = repoManager; this.repoManager = repoManager;
this.projectCache = projectCache; this.projectCache = projectCache;
@@ -341,7 +345,8 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
// //
AccountGroup g = args.groupCache.get(new AccountGroup.NameKey(who)); AccountGroup g = args.groupCache.get(new AccountGroup.NameKey(who));
if (g != null) { if (g != null) {
return visibleto(new SingleGroupUser(g.getGroupUUID())); return visibleto(new SingleGroupUser(args.capabilityControlFactory,
g.getGroupUUID()));
} }
Collection<AccountGroup> matches = Collection<AccountGroup> matches =
@@ -351,7 +356,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
for (AccountGroup group : matches) { for (AccountGroup group : matches) {
ids.add(group.getGroupUUID()); ids.add(group.getGroupUUID());
} }
return visibleto(new SingleGroupUser(ids)); return visibleto(new SingleGroupUser(args.capabilityControlFactory, ids));
} }
throw error("No user or group matches \"" + who + "\"."); throw error("No user or group matches \"" + who + "\".");

View File

@@ -39,7 +39,7 @@ public class ChangeQueryRewriter extends QueryRewriter<ChangeData> {
new InvalidProvider<ReviewDb>(), // new InvalidProvider<ReviewDb>(), //
new InvalidProvider<ChangeQueryRewriter>(), // new InvalidProvider<ChangeQueryRewriter>(), //
null, null, null, null, null, null, null, // null, null, null, null, null, null, null, //
null, null, null), null)); null, null, null, null), null));
private final Provider<ReviewDb> dbProvider; private final Provider<ReviewDb> dbProvider;

View File

@@ -79,7 +79,7 @@ class IsWatchedByPredicate extends OperatorPredicate<ChangeData> {
Project.NameKey project = change.getDest().getParentKey(); Project.NameKey project = change.getDest().getParentKey();
List<Predicate<ChangeData>> list = rules.get(project); List<Predicate<ChangeData>> list = rules.get(project);
if (list == null) { if (list == null) {
list = rules.get(args.wildProjectName); list = rules.get(args.allProjectsName);
} }
if (list != null) { if (list != null) {
for (Predicate<ChangeData> p : list) { for (Predicate<ChangeData> p : list) {

View File

@@ -19,6 +19,7 @@ import com.google.gerrit.reviewdb.AccountProjectWatch;
import com.google.gerrit.reviewdb.Change; import com.google.gerrit.reviewdb.Change;
import com.google.gerrit.server.AccessPath; import com.google.gerrit.server.AccessPath;
import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.CapabilityControl;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@@ -27,12 +28,14 @@ import java.util.Set;
final class SingleGroupUser extends CurrentUser { final class SingleGroupUser extends CurrentUser {
private final Set<AccountGroup.UUID> groups; private final Set<AccountGroup.UUID> groups;
SingleGroupUser(AccountGroup.UUID groupId) { SingleGroupUser(CapabilityControl.Factory capabilityControlFactory,
this(Collections.singleton(groupId)); AccountGroup.UUID groupId) {
this(capabilityControlFactory, Collections.singleton(groupId));
} }
SingleGroupUser(Set<AccountGroup.UUID> groups) { SingleGroupUser(CapabilityControl.Factory capabilityControlFactory,
super(null, AccessPath.UNKNOWN, null); Set<AccountGroup.UUID> groups) {
super(capabilityControlFactory, AccessPath.UNKNOWN, null);
this.groups = groups; this.groups = groups;
} }
@@ -55,9 +58,4 @@ final class SingleGroupUser extends CurrentUser {
public boolean isBatchUser() { public boolean isBatchUser() {
return false; return false;
} }
@Override
public boolean isAdministrator() {
return false;
}
} }

View File

@@ -16,6 +16,7 @@ package com.google.gerrit.server.schema;
import com.google.gerrit.common.Version; import com.google.gerrit.common.Version;
import com.google.gerrit.common.data.AccessSection; import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.Permission; import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRule; import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.reviewdb.AccountGroup; import com.google.gerrit.reviewdb.AccountGroup;
@@ -28,6 +29,7 @@ import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.reviewdb.SystemConfig; import com.google.gerrit.reviewdb.SystemConfig;
import com.google.gerrit.server.GerritPersonIdent; import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.account.GroupUUID; import com.google.gerrit.server.account.GroupUUID;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.SitePath; import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.config.SitePaths; import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
@@ -56,13 +58,11 @@ import java.util.Collections;
/** Creates the current database schema and populates initial code rows. */ /** Creates the current database schema and populates initial code rows. */
public class SchemaCreator { public class SchemaCreator {
private static final Project.NameKey DEFAULT_WILD_NAME =
new Project.NameKey("All-Projects");
private final @SitePath private final @SitePath
File site_path; File site_path;
private final GitRepositoryManager mgr; private final GitRepositoryManager mgr;
private final AllProjectsName allProjectsName;
private final PersonIdent serverUser; private final PersonIdent serverUser;
private final int versionNbr; private final int versionNbr;
@@ -76,17 +76,22 @@ public class SchemaCreator {
private AccountGroup owners; private AccountGroup owners;
@Inject @Inject
public SchemaCreator(final SitePaths site, public SchemaCreator(SitePaths site,
@Current final SchemaVersion version, final GitRepositoryManager mgr, @Current SchemaVersion version,
@GerritPersonIdent final PersonIdent au) { GitRepositoryManager mgr,
this(site.site_path, version, mgr, au); AllProjectsName allProjectsName,
@GerritPersonIdent PersonIdent au) {
this(site.site_path, version, mgr, allProjectsName, au);
} }
public SchemaCreator(final @SitePath File site, public SchemaCreator(@SitePath File site,
@Current final SchemaVersion version, final GitRepositoryManager gitMgr, @Current SchemaVersion version,
final @GerritPersonIdent PersonIdent au) { GitRepositoryManager gitMgr,
AllProjectsName ap,
@GerritPersonIdent PersonIdent au) {
site_path = site; site_path = site;
mgr = gitMgr; mgr = gitMgr;
allProjectsName = ap;
serverUser = au; serverUser = au;
versionNbr = version.getVersionNbr(); versionNbr = version.getVersionNbr();
index_generic = new ScriptRunner("index_generic.sql"); index_generic = new ScriptRunner("index_generic.sql");
@@ -189,18 +194,9 @@ public class SchemaCreator {
final SystemConfig s = SystemConfig.create(); final SystemConfig s = SystemConfig.create();
s.registerEmailPrivateKey = SignedToken.generateRandomKey(); s.registerEmailPrivateKey = SignedToken.generateRandomKey();
s.adminGroupId = admin.getId();
s.adminGroupUUID = admin.getGroupUUID();
s.anonymousGroupId = anonymous.getId();
s.registeredGroupId = registered.getId();
s.batchUsersGroupId = batchUsers.getId(); s.batchUsersGroupId = batchUsers.getId();
s.batchUsersGroupUUID = batchUsers.getGroupUUID(); s.batchUsersGroupUUID = batchUsers.getGroupUUID();
s.ownerGroupId = owners.getId();
s.wildProjectName = DEFAULT_WILD_NAME;
try { try {
s.sitePath = site_path.getCanonicalPath(); s.sitePath = site_path.getCanonicalPath();
} catch (IOException e) { } catch (IOException e) {
@@ -213,20 +209,19 @@ public class SchemaCreator {
private void initWildCardProject() throws IOException, ConfigInvalidException { private void initWildCardProject() throws IOException, ConfigInvalidException {
Repository git; Repository git;
try { try {
git = mgr.openRepository(DEFAULT_WILD_NAME); git = mgr.openRepository(allProjectsName);
} catch (RepositoryNotFoundException notFound) { } catch (RepositoryNotFoundException notFound) {
// A repository may be missing if this project existed only to store // A repository may be missing if this project existed only to store
// inheritable permissions. For example 'All-Projects'. // inheritable permissions. For example 'All-Projects'.
try { try {
git = mgr.createRepository(DEFAULT_WILD_NAME); git = mgr.createRepository(allProjectsName);
} catch (RepositoryNotFoundException err) { } catch (RepositoryNotFoundException err) {
final String name = DEFAULT_WILD_NAME.get(); final String name = allProjectsName.get();
throw new IOException("Cannot create repository " + name, err); throw new IOException("Cannot create repository " + name, err);
} }
} }
try { try {
MetaDataUpdate md = MetaDataUpdate md = new MetaDataUpdate(new NoReplication(), allProjectsName, git);
new MetaDataUpdate(new NoReplication(), DEFAULT_WILD_NAME, git);
md.getCommitBuilder().setAuthor(serverUser); md.getCommitBuilder().setAuthor(serverUser);
md.getCommitBuilder().setCommitter(serverUser); md.getCommitBuilder().setCommitter(serverUser);
@@ -235,10 +230,14 @@ public class SchemaCreator {
p.setDescription("Rights inherited by all other projects"); p.setDescription("Rights inherited by all other projects");
p.setUseContributorAgreements(false); p.setUseContributorAgreements(false);
AccessSection cap = config.getAccessSection(AccessSection.GLOBAL_CAPABILITIES, true);
AccessSection all = config.getAccessSection(AccessSection.ALL, true); AccessSection all = config.getAccessSection(AccessSection.ALL, true);
AccessSection heads = config.getAccessSection(AccessSection.HEADS, true); AccessSection heads = config.getAccessSection(AccessSection.HEADS, true);
AccessSection meta = config.getAccessSection(GitRepositoryManager.REF_CONFIG, true); AccessSection meta = config.getAccessSection(GitRepositoryManager.REF_CONFIG, true);
cap.getPermission(GlobalCapability.ADMINISTRATE_SERVER, true)
.add(rule(config, admin));
PermissionRule review = rule(config, registered); PermissionRule review = rule(config, registered);
review.setRange(-1, 1); review.setRange(-1, 1);
heads.getPermission(Permission.LABEL + "Code-Review", true).add(review); heads.getPermission(Permission.LABEL + "Code-Review", true).add(review);
@@ -259,7 +258,7 @@ public class SchemaCreator {
md.setMessage("Initialized Gerrit Code Review " + Version.getVersion()); md.setMessage("Initialized Gerrit Code Review " + Version.getVersion());
if (!config.commit(md)) { if (!config.commit(md)) {
throw new IOException("Cannot create " + DEFAULT_WILD_NAME.get()); throw new IOException("Cannot create " + allProjectsName.get());
} }
} finally { } finally {
git.close(); git.close();

View File

@@ -14,9 +14,13 @@
package com.google.gerrit.server.schema; package com.google.gerrit.server.schema;
import static com.google.inject.Scopes.SINGLETON;
import com.google.gerrit.lifecycle.LifecycleModule; import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.server.GerritPersonIdent; import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.GerritPersonIdentProvider; import com.google.gerrit.server.GerritPersonIdentProvider;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AllProjectsNameProvider;
import com.google.gerrit.server.config.FactoryModule; import com.google.gerrit.server.config.FactoryModule;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.LocalDiskRepositoryManager; import com.google.gerrit.server.git.LocalDiskRepositoryManager;
@@ -29,8 +33,12 @@ public class SchemaModule extends FactoryModule {
protected void configure() { protected void configure() {
install(new SchemaVersion.Module()); install(new SchemaVersion.Module());
bind(PersonIdent.class).annotatedWith(GerritPersonIdent.class).toProvider( bind(PersonIdent.class).annotatedWith(GerritPersonIdent.class)
GerritPersonIdentProvider.class); .toProvider(GerritPersonIdentProvider.class);
bind(AllProjectsName.class)
.toProvider(AllProjectsNameProvider.class)
.in(SINGLETON);
bind(GitRepositoryManager.class).to(LocalDiskRepositoryManager.class); bind(GitRepositoryManager.class).to(LocalDiskRepositoryManager.class);
install(new LifecycleModule() { install(new LifecycleModule() {

View File

@@ -32,7 +32,7 @@ import java.util.List;
/** A version of the database schema. */ /** A version of the database schema. */
public abstract class SchemaVersion { public abstract class SchemaVersion {
/** The current schema version. */ /** The current schema version. */
private static final Class<? extends SchemaVersion> C = Schema_56.class; private static final Class<? extends SchemaVersion> C = Schema_57.class;
public static class Module extends AbstractModule { public static class Module extends AbstractModule {
@Override @Override

View File

@@ -0,0 +1,165 @@
// Copyright (C) 2011 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.
package com.google.gerrit.server.schema;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.reviewdb.AccountGroup;
import com.google.gerrit.reviewdb.AccountGroupName;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.reviewdb.SystemConfig;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.config.AllProjectsNameProvider;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.LocalDiskRepositoryManager;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.NoReplication;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.util.FS;
import java.io.IOException;
import java.util.Collections;
public class Schema_57 extends SchemaVersion {
private final SitePaths site;
private final LocalDiskRepositoryManager mgr;
private final PersonIdent serverUser;
@Inject
Schema_57(Provider<Schema_56> prior, SitePaths site,
LocalDiskRepositoryManager mgr, @GerritPersonIdent PersonIdent serverUser) {
super(prior);
this.site = site;
this.mgr = mgr;
this.serverUser = serverUser;
}
@Override
protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
SystemConfig sc = db.systemConfig().get(new SystemConfig.Key());
Project.NameKey allProjects = sc.wildProjectName;
FileBasedConfig cfg = new FileBasedConfig(site.gerrit_config, FS.DETECTED);
boolean cfgDirty = false;
try {
cfg.load();
} catch (ConfigInvalidException err) {
throw new OrmException("Cannot read " + site.gerrit_config, err);
} catch (IOException err) {
throw new OrmException("Cannot read " + site.gerrit_config, err);
}
if (!allProjects.get().equals(AllProjectsNameProvider.DEFAULT)) {
ui.message("Setting gerrit.allProjects = " + allProjects.get());
cfg.setString("gerrit", null, "allProjects", allProjects.get());
cfgDirty = true;
}
try {
Repository git = mgr.openRepository(allProjects);
try {
MetaDataUpdate md = new MetaDataUpdate(new NoReplication(), allProjects, git);
md.getCommitBuilder().setAuthor(serverUser);
md.getCommitBuilder().setCommitter(serverUser);
ProjectConfig config = ProjectConfig.read(md);
AccessSection cap = config.getAccessSection(AccessSection.GLOBAL_CAPABILITIES, true);
// Move the Administrators group reference to All-Projects.
cap.getPermission(GlobalCapability.ADMINISTRATE_SERVER, true)
.add(new PermissionRule(config.resolve(db.accountGroups().get(sc.adminGroupId))));
// Move the repository.*.createGroup to Create Project.
String[] createGroupList = cfg.getStringList("repository", "*", "createGroup");
for (String name : createGroupList) {
AccountGroup.NameKey key = new AccountGroup.NameKey(name);
AccountGroupName groupName = db.accountGroupNames().get(key);
if (groupName == null) {
continue;
}
AccountGroup group = db.accountGroups().get(groupName.getId());
if (group == null) {
continue;
}
cap.getPermission(GlobalCapability.CREATE_PROJECT, true)
.add(new PermissionRule(config.resolve(group)));
}
if (createGroupList.length != 0) {
ui.message("Moved repository.*.createGroup to 'Create Project' capability");
cfg.unset("repository", "*", "createGroup");
cfgDirty = true;
}
AccountGroup batch = db.accountGroups().get(sc.batchUsersGroupId);
if (db.accountGroupMembers().byGroup(sc.batchUsersGroupId).toList().isEmpty() &&
db.accountGroupIncludes().byGroup(sc.batchUsersGroupId).toList().isEmpty()) {
// If the batch user group is not used, delete it.
//
if (batch != null) {
db.accountGroups().delete(Collections.singleton(batch));
AccountGroupName name = db.accountGroupNames().get(batch.getNameKey());
if (name != null) {
db.accountGroupNames().delete(Collections.singleton(name));
}
}
} else {
// FIXME Assign low priority to this group.
}
md.setMessage("Upgrade to Gerrit Code Review schema 57\n");
if (!config.commit(md)) {
throw new OrmException("Cannot update " + allProjects);
}
} finally {
git.close();
}
} catch (ConfigInvalidException err) {
throw new OrmException("Cannot read " + allProjects, err);
} catch (IOException err) {
throw new OrmException("Cannot update " + allProjects, err);
}
if (cfgDirty) {
try {
cfg.save();
} catch (IOException err) {
throw new OrmException("Cannot update " + site.gerrit_config, err);
}
}
// We cannot set the columns to NULL, so use 0 and a DELETED tag.
sc.adminGroupId = new AccountGroup.Id(0);
sc.adminGroupUUID = new AccountGroup.UUID("DELETED");
sc.anonymousGroupId = new AccountGroup.Id(0);
sc.registeredGroupId = new AccountGroup.Id(0);
sc.wildProjectName = new Project.NameKey("DELETED");
sc.ownerGroupId = new AccountGroup.Id(0);
db.systemConfig().update(Collections.singleton(sc));
}
}

View File

@@ -29,6 +29,8 @@ import com.google.gerrit.reviewdb.SystemConfig;
import com.google.gerrit.rules.PrologEnvironment; import com.google.gerrit.rules.PrologEnvironment;
import com.google.gerrit.server.AccessPath; import com.google.gerrit.server.AccessPath;
import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.CapabilityControl;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AuthConfig; import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
@@ -36,6 +38,7 @@ import com.google.gerrit.server.git.ProjectConfig;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Guice; import com.google.inject.Guice;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.assistedinject.FactoryProvider;
import junit.framework.TestCase; import junit.framework.TestCase;
@@ -194,6 +197,10 @@ public class RefControlTest extends TestCase {
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
private final Map<Project.NameKey, ProjectState> all;
private final AllProjectsName allProjectsName = new AllProjectsName("parent");
private final ProjectCache projectCache;
private ProjectConfig local; private ProjectConfig local;
private ProjectConfig parent; private ProjectConfig parent;
private final AccountGroup.UUID admin = new AccountGroup.UUID("test.admin"); private final AccountGroup.UUID admin = new AccountGroup.UUID("test.admin");
@@ -205,10 +212,10 @@ public class RefControlTest extends TestCase {
private final SystemConfig systemConfig; private final SystemConfig systemConfig;
private final AuthConfig authConfig; private final AuthConfig authConfig;
private final CapabilityControl.Factory capabilityControlFactory;
public RefControlTest() { public RefControlTest() {
systemConfig = SystemConfig.create(); systemConfig = SystemConfig.create();
systemConfig.adminGroupUUID = admin;
systemConfig.batchUsersGroupUUID = anonymous; systemConfig.batchUsersGroupUUID = anonymous;
try { try {
byte[] bin = "abcdefghijklmnopqrstuvwxyz".getBytes("UTF-8"); byte[] bin = "abcdefghijklmnopqrstuvwxyz".getBytes("UTF-8");
@@ -217,6 +224,37 @@ public class RefControlTest extends TestCase {
throw new RuntimeException("Cannot encode key", err); throw new RuntimeException("Cannot encode key", err);
} }
all = new HashMap<Project.NameKey, ProjectState>();
projectCache = new ProjectCache() {
@Override
public ProjectState getAllProjects() {
return get(allProjectsName);
}
@Override
public ProjectState get(Project.NameKey projectName) {
return all.get(projectName);
}
@Override
public void evict(Project p) {
}
@Override
public Iterable<Project.NameKey> all() {
return Collections.emptySet();
}
@Override
public Iterable<Project.NameKey> byName(String prefix) {
return Collections.emptySet();
}
@Override
public void onCreateProject(Project.NameKey newProjectName) {
}
};
Injector injector = Guice.createInjector(new AbstractModule() { Injector injector = Guice.createInjector(new AbstractModule() {
@Override @Override
protected void configure() { protected void configure() {
@@ -224,11 +262,18 @@ public class RefControlTest extends TestCase {
.annotatedWith(GerritServerConfig.class) // .annotatedWith(GerritServerConfig.class) //
.toInstance(new Config()); .toInstance(new Config());
bind(CapabilityControl.Factory.class)
.toProvider(FactoryProvider.newFactory(
CapabilityControl.Factory.class,
CapabilityControl.class));
bind(ProjectCache.class).toInstance(projectCache);
bind(SystemConfig.class).toInstance(systemConfig); bind(SystemConfig.class).toInstance(systemConfig);
bind(AuthConfig.class); bind(AuthConfig.class);
} }
}); });
authConfig = injector.getInstance(AuthConfig.class); authConfig = injector.getInstance(AuthConfig.class);
capabilityControlFactory = injector.getInstance(CapabilityControl.Factory.class);
} }
@Override @Override
@@ -287,42 +332,14 @@ public class RefControlTest extends TestCase {
} }
private ProjectState newProjectState() { private ProjectState newProjectState() {
final Map<Project.NameKey, ProjectState> all =
new HashMap<Project.NameKey, ProjectState>();
final ProjectCache projectCache = new ProjectCache() {
@Override
public ProjectState get(Project.NameKey projectName) {
return all.get(projectName);
}
@Override
public void evict(Project p) {
}
@Override
public Iterable<Project.NameKey> all() {
return Collections.emptySet();
}
@Override
public Iterable<Project.NameKey> byName(String prefix) {
return Collections.emptySet();
}
@Override
public void onCreateProject(Project.NameKey newProjectName) {
}
};
PrologEnvironment.Factory envFactory = null; PrologEnvironment.Factory envFactory = null;
GitRepositoryManager mgr = null; GitRepositoryManager mgr = null;
Project.NameKey wildProject = new Project.NameKey("All-Projects");
ProjectControl.AssistedFactory projectControlFactory = null; ProjectControl.AssistedFactory projectControlFactory = null;
all.put(local.getProject().getNameKey(), new ProjectState( all.put(local.getProject().getNameKey(), new ProjectState(
projectCache, wildProject, projectControlFactory, projectCache, allProjectsName, projectControlFactory,
envFactory, mgr, local)); envFactory, mgr, local));
all.put(parent.getProject().getNameKey(), new ProjectState( all.put(parent.getProject().getNameKey(), new ProjectState(
projectCache, wildProject, projectControlFactory, projectCache, allProjectsName, projectControlFactory,
envFactory, mgr, parent)); envFactory, mgr, parent));
return all.get(local.getProject().getNameKey()); return all.get(local.getProject().getNameKey());
} }
@@ -331,7 +348,9 @@ public class RefControlTest extends TestCase {
private final Set<AccountGroup.UUID> groups; private final Set<AccountGroup.UUID> groups;
MockUser(AccountGroup.UUID[] groupId) { MockUser(AccountGroup.UUID[] groupId) {
super(null, AccessPath.UNKNOWN, RefControlTest.this.authConfig); super(RefControlTest.this.capabilityControlFactory,
AccessPath.UNKNOWN,
RefControlTest.this.authConfig);
groups = new HashSet<AccountGroup.UUID>(Arrays.asList(groupId)); groups = new HashSet<AccountGroup.UUID>(Arrays.asList(groupId));
groups.add(registered); groups.add(registered);
groups.add(anonymous); groups.add(anonymous);
@@ -351,10 +370,5 @@ public class RefControlTest extends TestCase {
public Collection<AccountProjectWatch> getNotificationFilters() { public Collection<AccountProjectWatch> getNotificationFilters() {
return Collections.emptySet(); return Collections.emptySet();
} }
@Override
public boolean isAdministrator() {
return false;
}
} }
} }

View File

@@ -14,7 +14,6 @@
package com.google.gerrit.server.schema; package com.google.gerrit.server.schema;
import com.google.gerrit.reviewdb.AccountGroup;
import com.google.gerrit.reviewdb.ApprovalCategory; import com.google.gerrit.reviewdb.ApprovalCategory;
import com.google.gerrit.reviewdb.ApprovalCategoryValue; import com.google.gerrit.reviewdb.ApprovalCategoryValue;
import com.google.gerrit.reviewdb.ReviewDb; import com.google.gerrit.reviewdb.ReviewDb;
@@ -71,9 +70,6 @@ public class SchemaCreatorTest extends TestCase {
db.assertSchemaVersion(); db.assertSchemaVersion();
final SystemConfig config = db.getSystemConfig(); final SystemConfig config = db.getSystemConfig();
assertNotNull(config); assertNotNull(config);
assertNotNull(config.adminGroupId);
assertNotNull(config.anonymousGroupId);
assertNotNull(config.registeredGroupId);
// By default sitePath is set to the current working directory. // By default sitePath is set to the current working directory.
// //
@@ -95,58 +91,10 @@ public class SchemaCreatorTest extends TestCase {
final SystemConfig act = db.getSystemConfig(); final SystemConfig act = db.getSystemConfig();
assertNotSame(exp, act); assertNotSame(exp, act);
assertEquals(exp.adminGroupId, act.adminGroupId);
assertEquals(exp.anonymousGroupId, act.anonymousGroupId);
assertEquals(exp.registeredGroupId, act.registeredGroupId);
assertEquals(exp.sitePath, act.sitePath); assertEquals(exp.sitePath, act.sitePath);
assertEquals(exp.registerEmailPrivateKey, act.registerEmailPrivateKey); assertEquals(exp.registerEmailPrivateKey, act.registerEmailPrivateKey);
} }
public void testCreateSchema_Group_Administrators() throws OrmException {
db.create();
final SystemConfig config = db.getSystemConfig();
final ReviewDb c = db.open();
try {
final AccountGroup admin = c.accountGroups().get(config.adminGroupId);
assertNotNull(admin);
assertEquals(config.adminGroupId, admin.getId());
assertEquals("Administrators", admin.getName());
assertSame(AccountGroup.Type.INTERNAL, admin.getType());
} finally {
c.close();
}
}
public void testCreateSchema_Group_AnonymousUsers() throws OrmException {
db.create();
final SystemConfig config = db.getSystemConfig();
final ReviewDb c = db.open();
try {
final AccountGroup anon = c.accountGroups().get(config.anonymousGroupId);
assertNotNull(anon);
assertEquals(config.anonymousGroupId, anon.getId());
assertEquals("Anonymous Users", anon.getName());
assertSame(AccountGroup.Type.SYSTEM, anon.getType());
} finally {
c.close();
}
}
public void testCreateSchema_Group_RegisteredUsers() throws OrmException {
db.create();
final SystemConfig config = db.getSystemConfig();
final ReviewDb c = db.open();
try {
final AccountGroup reg = c.accountGroups().get(config.registeredGroupId);
assertNotNull(reg);
assertEquals(config.registeredGroupId, reg.getId());
assertEquals("Registered Users", reg.getName());
assertSame(AccountGroup.Type.SYSTEM, reg.getType());
} finally {
c.close();
}
}
public void testCreateSchema_ApprovalCategory_CodeReview() public void testCreateSchema_ApprovalCategory_CodeReview()
throws OrmException { throws OrmException {
final ReviewDb c = db.create().open(); final ReviewDb c = db.create().open();

View File

@@ -18,6 +18,7 @@ import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.reviewdb.SystemConfig; import com.google.gerrit.reviewdb.SystemConfig;
import com.google.gerrit.server.GerritPersonIdent; import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.GerritPersonIdentProvider; import com.google.gerrit.server.GerritPersonIdentProvider;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths; import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
@@ -80,6 +81,9 @@ public class SchemaUpdaterTest extends TestCase {
.annotatedWith(GerritPersonIdent.class) // .annotatedWith(GerritPersonIdent.class) //
.toProvider(GerritPersonIdentProvider.class); .toProvider(GerritPersonIdentProvider.class);
bind(AllProjectsName.class)
.toInstance(new AllProjectsName("All-Projects"));
bind(GitRepositoryManager.class) // bind(GitRepositoryManager.class) //
.to(LocalDiskRepositoryManager.class); .to(LocalDiskRepositoryManager.class);
} }

View File

@@ -19,6 +19,7 @@ import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.reviewdb.SystemConfig; import com.google.gerrit.reviewdb.SystemConfig;
import com.google.gerrit.server.GerritPersonIdent; import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.GerritPersonIdentProvider; import com.google.gerrit.server.GerritPersonIdentProvider;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePath; import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.config.SystemConfigProvider; import com.google.gerrit.server.config.SystemConfigProvider;
@@ -144,7 +145,11 @@ public class InMemoryDatabase implements SchemaFactory<ReviewDb> {
final ReviewDb c = open(); final ReviewDb c = open();
try { try {
try { try {
new SchemaCreator(new File("."), schemaVersion, null, new SchemaCreator(
new File("."),
schemaVersion,
null,
new AllProjectsName("Test-Projects"),
new PersonIdent("name", "email@site")).create(c); new PersonIdent("name", "email@site")).create(c);
} catch (IOException e) { } catch (IOException e) {
throw new OrmException("Cannot create in-memory database", e); throw new OrmException("Cannot create in-memory database", e);

View File

@@ -240,7 +240,8 @@ public abstract class BaseCommand implements Command {
protected synchronized void startThread(final CommandRunnable thunk) { protected synchronized void startThread(final CommandRunnable thunk) {
final TaskThunk tt = new TaskThunk(thunk); final TaskThunk tt = new TaskThunk(thunk);
if (isAdminCommand()||(isAdminHighPriorityCommand() && userProvider.get().isAdministrator())) { if (isAdminCommand() || (isAdminHighPriorityCommand()
&& userProvider.get().getCapabilities().canAdministrateServer())) {
// Admin commands should not block the main work threads (there // Admin commands should not block the main work threads (there
// might be an interactive shell there), nor should they wait // might be an interactive shell there), nor should they wait
// for the main work threads. // for the main work threads.

View File

@@ -71,7 +71,8 @@ final class DispatchCommand extends BaseCommand {
final Command cmd = p.get(); final Command cmd = p.get();
if (isAdminCommand(cmd) && !currentUser.get().isAdministrator()) { if (isAdminCommand(cmd)
&& !currentUser.get().getCapabilities().canAdministrateServer()) {
final String msg = "fatal: Not a Gerrit administrator"; final String msg = "fatal: Not a Gerrit administrator";
throw new UnloggedFailure(BaseCommand.STATUS_NOT_ADMIN, msg); throw new UnloggedFailure(BaseCommand.STATUS_NOT_ADMIN, msg);
} }

View File

@@ -15,7 +15,7 @@
package com.google.gerrit.sshd.commands; package com.google.gerrit.sshd.commands;
import com.google.gerrit.reviewdb.Project; import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.server.config.WildProjectName; import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.git.MetaDataUpdate; import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.ProjectConfig; import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectCache;
@@ -52,8 +52,7 @@ final class AdminSetParent extends BaseCommand {
private MetaDataUpdate.User metaDataUpdateFactory; private MetaDataUpdate.User metaDataUpdateFactory;
@Inject @Inject
@WildProjectName private AllProjectsName allProjectsName;
private Project.NameKey wildProject;
@Override @Override
public void start(final Environment env) { public void start(final Environment env) {
@@ -71,7 +70,7 @@ final class AdminSetParent extends BaseCommand {
final Set<Project.NameKey> grandParents = new HashSet<Project.NameKey>(); final Set<Project.NameKey> grandParents = new HashSet<Project.NameKey>();
Project.NameKey newParentKey; Project.NameKey newParentKey;
grandParents.add(wildProject); grandParents.add(allProjectsName);
if (newParent != null) { if (newParent != null) {
newParentKey = newParent.getProject().getNameKey(); newParentKey = newParent.getProject().getNameKey();
@@ -98,7 +97,7 @@ final class AdminSetParent extends BaseCommand {
final Project.NameKey key = pc.getProject().getNameKey(); final Project.NameKey key = pc.getProject().getNameKey();
final String name = pc.getProject().getName(); final String name = pc.getProject().getName();
if (wildProject.equals(key)) { if (allProjectsName.equals(key)) {
// Don't allow the wild card project to have a parent. // Don't allow the wild card project to have a parent.
// //
err.append("error: Cannot set parent of '" + name + "'\n"); err.append("error: Cannot set parent of '" + name + "'\n");
@@ -109,7 +108,7 @@ final class AdminSetParent extends BaseCommand {
// Try to avoid creating a cycle in the parent pointers. // Try to avoid creating a cycle in the parent pointers.
// //
err.append("error: Cycle exists between '" + name + "' and '" err.append("error: Cycle exists between '" + name + "' and '"
+ (newParentKey != null ? newParentKey.get() : wildProject.get()) + (newParentKey != null ? newParentKey.get() : allProjectsName.get())
+ "'\n"); + "'\n");
continue; continue;
} }

View File

@@ -14,7 +14,6 @@
package com.google.gerrit.sshd.commands; package com.google.gerrit.sshd.commands;
import com.google.gerrit.common.CollectionsUtil;
import com.google.gerrit.common.data.AccessSection; import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.GroupReference; import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.Permission; import com.google.gerrit.common.data.Permission;
@@ -25,7 +24,6 @@ import com.google.gerrit.reviewdb.Project.SubmitType;
import com.google.gerrit.server.GerritPersonIdent; import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.GroupCache; import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.config.ProjectCreatorGroups;
import com.google.gerrit.server.config.ProjectOwnerGroups; import com.google.gerrit.server.config.ProjectOwnerGroups;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MetaDataUpdate; import com.google.gerrit.server.git.MetaDataUpdate;
@@ -125,10 +123,6 @@ final class CreateProject extends BaseCommand {
@Inject @Inject
private GroupCache groupCache; private GroupCache groupCache;
@Inject
@ProjectCreatorGroups
private Set<AccountGroup.UUID> projectCreatorGroups;
@Inject @Inject
@ProjectOwnerGroups @ProjectOwnerGroups
private Set<AccountGroup.UUID> projectOwnerGroups; private Set<AccountGroup.UUID> projectOwnerGroups;
@@ -153,6 +147,13 @@ final class CreateProject extends BaseCommand {
startThread(new CommandRunnable() { startThread(new CommandRunnable() {
@Override @Override
public void run() throws Exception { public void run() throws Exception {
if (!currentUser.getCapabilities().canCreateProject()) {
String msg = String.format(
"fatal: %s does not have \"Create Project\" capability.",
currentUser.getUserName());
throw new UnloggedFailure(BaseCommand.STATUS_NOT_ADMIN, msg);
}
parseCommandLine(); parseCommandLine();
validateParameters(); validateParameters();
@@ -279,10 +280,6 @@ final class CreateProject extends BaseCommand {
projectName.length() - Constants.DOT_GIT_EXT.length()); projectName.length() - Constants.DOT_GIT_EXT.length());
} }
if (!CollectionsUtil.isAnyIncludedIn(currentUser.getEffectiveGroups(), projectCreatorGroups)) {
throw new Failure(1, "fatal: Not permitted to create " + projectName);
}
if (ownerIds != null && !ownerIds.isEmpty()) { if (ownerIds != null && !ownerIds.isEmpty()) {
ownerIds = ownerIds =
new ArrayList<AccountGroup.UUID>(new HashSet<AccountGroup.UUID>(ownerIds)); new ArrayList<AccountGroup.UUID>(new HashSet<AccountGroup.UUID>(ownerIds));

View File

@@ -65,7 +65,8 @@ final class FlushCaches extends CacheCommand {
} }
private void flush() throws Failure { private void flush() throws Failure {
if (caches.contains(WEB_SESSIONS) && !currentUser.isAdministrator()) { if (caches.contains(WEB_SESSIONS)
&& !currentUser.getCapabilities().canAdministrateServer()) {
String msg = String.format( String msg = String.format(
"fatal: only site administrators can flush %s", "fatal: only site administrators can flush %s",
WEB_SESSIONS); WEB_SESSIONS);

View File

@@ -16,7 +16,7 @@ package com.google.gerrit.sshd.commands;
import com.google.gerrit.reviewdb.Project; import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.WildProjectName; import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectControl; import com.google.gerrit.server.project.ProjectControl;
@@ -84,8 +84,7 @@ final class ListProjects extends BaseCommand {
private GitRepositoryManager repoManager; private GitRepositoryManager repoManager;
@Inject @Inject
@WildProjectName private AllProjectsName allProjectsName;
private Project.NameKey allProjectsName;
@Option(name = "--show-branch", aliases = {"-b"}, multiValued = true, @Option(name = "--show-branch", aliases = {"-b"}, multiValued = true,
usage = "displays the sha of each project in the specified branch") usage = "displays the sha of each project in the specified branch")