Replication Security
Replication now makes use of the standard security system in Gerrit. This is done by tying to Gerrit groups via replication.config.
This commit is contained in:
@@ -45,6 +45,8 @@ different hosts:
|
||||
push = +refs/heads/*
|
||||
push = +refs/tags/*
|
||||
threads = 3
|
||||
authGroup = Public Mirror Group
|
||||
authGroup = Second Public Mirror Group
|
||||
====
|
||||
|
||||
To manually trigger replication at runtime, see
|
||||
@@ -151,6 +153,18 @@ parallel pushing.
|
||||
+
|
||||
By default, 1 thread.
|
||||
|
||||
[[remote.name.authGroup]]remote.<name>.authGroup:
|
||||
+
|
||||
Specifies the name of a group that the remote should use to access
|
||||
the repositories. Multiple authGroups may be specified within a
|
||||
single remote block to signify a wider access right. In the project
|
||||
administration web interface the read access can be specified for
|
||||
this group to control if a project should be replicated or not to the
|
||||
remote.
|
||||
+
|
||||
By default, replicates without group control, i.e replicates
|
||||
everything to all remotes.
|
||||
|
||||
|
||||
[[ssh_config]]File `~/.ssh/config`
|
||||
----------------------------------
|
||||
|
||||
@@ -14,8 +14,16 @@
|
||||
|
||||
package com.google.gerrit.git;
|
||||
|
||||
import com.google.gerrit.client.reviewdb.AccountGroup;
|
||||
import com.google.gerrit.client.reviewdb.Project;
|
||||
import com.google.gerrit.client.reviewdb.ReviewDb;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.ReplicationUser;
|
||||
import com.google.gerrit.server.config.SitePath;
|
||||
import com.google.gerrit.server.project.NoSuchProjectException;
|
||||
import com.google.gerrit.server.project.ProjectControl;
|
||||
import com.google.gwtorm.client.OrmException;
|
||||
import com.google.gwtorm.client.SchemaFactory;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
@@ -47,9 +55,11 @@ import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/** Manages automatic replication to remote repositories. */
|
||||
@@ -72,12 +82,18 @@ public class PushReplication implements ReplicationQueue {
|
||||
private final Injector injector;
|
||||
private final WorkQueue workQueue;
|
||||
private final List<ReplicationConfig> configs;
|
||||
private final SchemaFactory<ReviewDb> database;
|
||||
private final ReplicationUser.Factory replicationUserFactory;
|
||||
|
||||
@Inject
|
||||
PushReplication(final Injector i, final WorkQueue wq,
|
||||
@SitePath final File sitePath) throws ConfigInvalidException, IOException {
|
||||
@SitePath final File sitePath, final ReplicationUser.Factory ruf,
|
||||
final SchemaFactory<ReviewDb> db) throws ConfigInvalidException,
|
||||
IOException {
|
||||
injector = i;
|
||||
workQueue = wq;
|
||||
database = db;
|
||||
replicationUserFactory = ruf;
|
||||
configs = allConfigs(sitePath);
|
||||
}
|
||||
|
||||
@@ -152,7 +168,8 @@ public class PushReplication implements ReplicationQueue {
|
||||
c.addPushRefSpec(spec);
|
||||
}
|
||||
|
||||
r.add(new ReplicationConfig(injector, workQueue, c, cfg));
|
||||
r.add(new ReplicationConfig(injector, workQueue, c, cfg, database,
|
||||
replicationUserFactory));
|
||||
}
|
||||
return Collections.unmodifiableList(r);
|
||||
}
|
||||
@@ -285,9 +302,13 @@ public class PushReplication implements ReplicationQueue {
|
||||
private final WorkQueue.Executor pool;
|
||||
private final Map<URIish, PushOp> pending = new HashMap<URIish, PushOp>();
|
||||
private final PushOp.Factory opFactory;
|
||||
private final ProjectControl.Factory projectControlFactory;
|
||||
private final boolean authEnabled;
|
||||
|
||||
ReplicationConfig(final Injector injector, final WorkQueue workQueue,
|
||||
final RemoteConfig rc, final Config cfg) {
|
||||
final RemoteConfig rc, final Config cfg, SchemaFactory<ReviewDb> db,
|
||||
final ReplicationUser.Factory replicationUserFactory) {
|
||||
|
||||
remote = rc;
|
||||
delay = Math.max(0, getInt(rc, cfg, "replicationdelay", 15));
|
||||
|
||||
@@ -295,6 +316,22 @@ public class PushReplication implements ReplicationQueue {
|
||||
final String poolName = "ReplicateTo-" + rc.getName();
|
||||
pool = workQueue.createQueue(poolSize, poolName);
|
||||
|
||||
String[] authGroupNames =
|
||||
cfg.getStringList("remote", rc.getName(), "authGroup");
|
||||
authEnabled = authGroupNames.length > 0;
|
||||
Set<AccountGroup.Id> authGroups = groupsFor(db, authGroupNames);
|
||||
|
||||
final ReplicationUser remoteUser =
|
||||
replicationUserFactory.create(authGroups);
|
||||
|
||||
projectControlFactory =
|
||||
injector.createChildInjector(new AbstractModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(CurrentUser.class).toInstance(remoteUser);
|
||||
}
|
||||
}).getInstance(ProjectControl.Factory.class);
|
||||
|
||||
opFactory = injector.createChildInjector(new AbstractModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
@@ -307,6 +344,31 @@ public class PushReplication implements ReplicationQueue {
|
||||
}).getInstance(PushOp.Factory.class);
|
||||
}
|
||||
|
||||
private static Set<AccountGroup.Id> groupsFor(
|
||||
SchemaFactory<ReviewDb> dbfactory, String[] groupNames) {
|
||||
final Set<AccountGroup.Id> result = new HashSet<AccountGroup.Id>();
|
||||
try {
|
||||
final ReviewDb db = dbfactory.open();
|
||||
try {
|
||||
for (String name : groupNames) {
|
||||
AccountGroup group =
|
||||
db.accountGroups().get(new AccountGroup.NameKey(name));
|
||||
if (group == null) {
|
||||
log.warn("Group \"" + name + "\" not in database,"
|
||||
+ " removing from authGroup");
|
||||
} else {
|
||||
result.add(group.getId());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
} catch (OrmException e) {
|
||||
log.error("Database error: " + e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private int getInt(final RemoteConfig rc, final Config cfg,
|
||||
final String name, final int defValue) {
|
||||
return cfg.getInt("remote", rc.getName(), name, defValue);
|
||||
@@ -314,6 +376,16 @@ public class PushReplication implements ReplicationQueue {
|
||||
|
||||
void schedule(final Project.NameKey project, final String ref,
|
||||
final URIish uri) {
|
||||
try {
|
||||
if (authEnabled
|
||||
&& !projectControlFactory.controlFor(project).isVisible()) {
|
||||
return;
|
||||
}
|
||||
} catch (NoSuchProjectException e1) {
|
||||
log.error("Internal error: project " + project
|
||||
+ " not found during replication");
|
||||
return;
|
||||
}
|
||||
synchronized (pending) {
|
||||
PushOp e = pending.get(uri);
|
||||
if (e == null) {
|
||||
|
||||
@@ -23,5 +23,8 @@ public enum AccessPath {
|
||||
WEB,
|
||||
|
||||
/** Access through an SSH command, e.g. git fetch or push. */
|
||||
SSH;
|
||||
SSH,
|
||||
|
||||
/** Access through replication */
|
||||
REPLICATION;
|
||||
}
|
||||
|
||||
56
src/main/java/com/google/gerrit/server/ReplicationUser.java
Normal file
56
src/main/java/com/google/gerrit/server/ReplicationUser.java
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright (C) 2009 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;
|
||||
|
||||
import com.google.gerrit.client.reviewdb.AccountGroup;
|
||||
import com.google.gerrit.client.reviewdb.Change;
|
||||
import com.google.gerrit.server.config.AuthConfig;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class ReplicationUser extends CurrentUser {
|
||||
public interface Factory {
|
||||
ReplicationUser create(@Assisted Set<AccountGroup.Id> authGroups);
|
||||
}
|
||||
|
||||
private Set<AccountGroup.Id> effectiveGroups;
|
||||
|
||||
@Inject
|
||||
protected ReplicationUser(AuthConfig authConfig,
|
||||
@Assisted Set<AccountGroup.Id> authGroups) {
|
||||
super(AccessPath.REPLICATION, authConfig);
|
||||
effectiveGroups = new HashSet<AccountGroup.Id>(authGroups);
|
||||
|
||||
if (effectiveGroups.isEmpty()) {
|
||||
effectiveGroups.addAll(authConfig.getRegisteredGroups());
|
||||
}
|
||||
|
||||
effectiveGroups = Collections.unmodifiableSet(effectiveGroups);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<AccountGroup.Id> getEffectiveGroups() {
|
||||
return Collections.unmodifiableSet(effectiveGroups);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Change.Id> getStarredChanges() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ import com.google.gerrit.server.GerritPersonIdent;
|
||||
import com.google.gerrit.server.GerritPersonIdentProvider;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.MimeUtilFileTypeRegistry;
|
||||
import com.google.gerrit.server.ReplicationUser;
|
||||
import com.google.gerrit.server.account.AccountByEmailCacheImpl;
|
||||
import com.google.gerrit.server.account.AccountCacheImpl;
|
||||
import com.google.gerrit.server.account.AccountInfoCacheFactory;
|
||||
@@ -45,7 +46,6 @@ import com.google.gerrit.server.account.Realm;
|
||||
import com.google.gerrit.server.cache.CachePool;
|
||||
import com.google.gerrit.server.ldap.LdapModule;
|
||||
import com.google.gerrit.server.mail.AbandonedSender;
|
||||
import com.google.gerrit.server.mail.AddReviewerSender;
|
||||
import com.google.gerrit.server.mail.CommentSender;
|
||||
import com.google.gerrit.server.mail.EmailSender;
|
||||
import com.google.gerrit.server.mail.FromAddressGenerator;
|
||||
@@ -160,5 +160,6 @@ public class GerritGlobalModule extends FactoryModule {
|
||||
factory(MergedSender.Factory.class);
|
||||
factory(MergeFailSender.Factory.class);
|
||||
factory(RegisterNewEmailSender.Factory.class);
|
||||
factory(ReplicationUser.Factory.class);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user