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

@@ -29,6 +29,8 @@ import com.google.gerrit.reviewdb.SystemConfig;
import com.google.gerrit.rules.PrologEnvironment;
import com.google.gerrit.server.AccessPath;
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.GerritServerConfig;
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.Guice;
import com.google.inject.Injector;
import com.google.inject.assistedinject.FactoryProvider;
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 parent;
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 AuthConfig authConfig;
private final CapabilityControl.Factory capabilityControlFactory;
public RefControlTest() {
systemConfig = SystemConfig.create();
systemConfig.adminGroupUUID = admin;
systemConfig.batchUsersGroupUUID = anonymous;
try {
byte[] bin = "abcdefghijklmnopqrstuvwxyz".getBytes("UTF-8");
@@ -217,6 +224,37 @@ public class RefControlTest extends TestCase {
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() {
@Override
protected void configure() {
@@ -224,11 +262,18 @@ public class RefControlTest extends TestCase {
.annotatedWith(GerritServerConfig.class) //
.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(AuthConfig.class);
}
});
authConfig = injector.getInstance(AuthConfig.class);
capabilityControlFactory = injector.getInstance(CapabilityControl.Factory.class);
}
@Override
@@ -287,42 +332,14 @@ public class RefControlTest extends TestCase {
}
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;
GitRepositoryManager mgr = null;
Project.NameKey wildProject = new Project.NameKey("All-Projects");
ProjectControl.AssistedFactory projectControlFactory = null;
all.put(local.getProject().getNameKey(), new ProjectState(
projectCache, wildProject, projectControlFactory,
projectCache, allProjectsName, projectControlFactory,
envFactory, mgr, local));
all.put(parent.getProject().getNameKey(), new ProjectState(
projectCache, wildProject, projectControlFactory,
projectCache, allProjectsName, projectControlFactory,
envFactory, mgr, parent));
return all.get(local.getProject().getNameKey());
}
@@ -331,7 +348,9 @@ public class RefControlTest extends TestCase {
private final Set<AccountGroup.UUID> groups;
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.add(registered);
groups.add(anonymous);
@@ -351,10 +370,5 @@ public class RefControlTest extends TestCase {
public Collection<AccountProjectWatch> getNotificationFilters() {
return Collections.emptySet();
}
@Override
public boolean isAdministrator() {
return false;
}
}
}

View File

@@ -14,7 +14,6 @@
package com.google.gerrit.server.schema;
import com.google.gerrit.reviewdb.AccountGroup;
import com.google.gerrit.reviewdb.ApprovalCategory;
import com.google.gerrit.reviewdb.ApprovalCategoryValue;
import com.google.gerrit.reviewdb.ReviewDb;
@@ -71,9 +70,6 @@ public class SchemaCreatorTest extends TestCase {
db.assertSchemaVersion();
final SystemConfig config = db.getSystemConfig();
assertNotNull(config);
assertNotNull(config.adminGroupId);
assertNotNull(config.anonymousGroupId);
assertNotNull(config.registeredGroupId);
// By default sitePath is set to the current working directory.
//
@@ -95,58 +91,10 @@ public class SchemaCreatorTest extends TestCase {
final SystemConfig act = db.getSystemConfig();
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.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()
throws OrmException {
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.server.GerritPersonIdent;
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.SitePaths;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -80,6 +81,9 @@ public class SchemaUpdaterTest extends TestCase {
.annotatedWith(GerritPersonIdent.class) //
.toProvider(GerritPersonIdentProvider.class);
bind(AllProjectsName.class)
.toInstance(new AllProjectsName("All-Projects"));
bind(GitRepositoryManager.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.server.GerritPersonIdent;
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.SitePath;
import com.google.gerrit.server.config.SystemConfigProvider;
@@ -144,7 +145,11 @@ public class InMemoryDatabase implements SchemaFactory<ReviewDb> {
final ReviewDb c = open();
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);
} catch (IOException e) {
throw new OrmException("Cannot create in-memory database", e);