From 2be1160f0536412cdb7125171e990e77e99c9376 Mon Sep 17 00:00:00 2001 From: lincoln Date: Mon, 5 Jul 2010 10:53:25 -0300 Subject: [PATCH] Block off commands on a server for certain user groups. This feature adds two new options to gerrit.config file: upload and receive with the allowGroup attribute, that restrict to some specific groups the ability to run upload/receive commands on the server. [sp: All bugs are mine, I refactored the code a bit from the original] Change-Id: Ibd31bd11234e429f8b0201bbb03099f737281f21 Signed-off-by: Shawn O. Pearce --- Documentation/config-gerrit.txt | 43 +++++++++++++++ .../google/gerrit/httpd/ProjectServlet.java | 12 ++++- .../google/gerrit/common/CollectionsUtil.java | 43 +++++++++++++++ .../server/config/GerritGlobalModule.java | 6 +-- .../server/config/GitReceivePackGroups.java | 37 +++++++++++++ .../config/GitReceivePackGroupsProvider.java | 42 +++++++++++++++ .../server/config/GitUploadPackGroups.java | 37 +++++++++++++ .../config/GitUploadPackGroupsProvider.java | 42 +++++++++++++++ .../server/config/GroupSetProvider.java | 50 +++++++++++++++++ .../config/ProjectCreatorGroupsProvider.java | 28 ++-------- .../config/ProjectOwnerGroupsProvider.java | 28 +++------- .../server/project/AccessControlModule.java | 54 +++++++++++++++++++ .../gerrit/server/project/ProjectControl.java | 26 ++++++++- .../gerrit/server/project/ProjectState.java | 5 +- .../gerrit/server/project/RefControlTest.java | 9 ++-- .../gerrit/sshd/commands/CreateProject.java | 24 +-------- .../google/gerrit/sshd/commands/Receive.java | 4 ++ .../google/gerrit/sshd/commands/Upload.java | 4 ++ 18 files changed, 416 insertions(+), 78 deletions(-) create mode 100644 gerrit-server/src/main/java/com/google/gerrit/common/CollectionsUtil.java create mode 100644 gerrit-server/src/main/java/com/google/gerrit/server/config/GitReceivePackGroups.java create mode 100644 gerrit-server/src/main/java/com/google/gerrit/server/config/GitReceivePackGroupsProvider.java create mode 100644 gerrit-server/src/main/java/com/google/gerrit/server/config/GitUploadPackGroups.java create mode 100644 gerrit-server/src/main/java/com/google/gerrit/server/config/GitUploadPackGroupsProvider.java create mode 100644 gerrit-server/src/main/java/com/google/gerrit/server/config/GroupSetProvider.java create mode 100644 gerrit-server/src/main/java/com/google/gerrit/server/project/AccessControlModule.java diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt index ec4750e442..2f9b8835ee 100644 --- a/Documentation/config-gerrit.txt +++ b/Documentation/config-gerrit.txt @@ -1341,6 +1341,27 @@ auto-detected and one thread per CPU is used, per client request. By default, 1. +[[receive]]Section receive +~~~~~~~~~~~~~~~~~~~~~~~~~~ +Sets the group of users allowed to execute 'receive-pack' on the +server, 'receive-pack' is what runs on the server during a user's +push or repo upload command. + +---- +[receive] + allowGroup = GROUP_ALLOWED_TO_EXECUTE + allowGroup = YET_ANOTHER_GROUP_ALLOWED_TO_EXECUTE +---- + +[[receive.allowGroup]]receive.allowGroup:: ++ +Name of the groups of users that are allowed to execute +'receive-pack' on the server. One or more groups can be set. ++ +If no groups are added, any user will be allowed to execute +'receive-pack' on the server. + + [[repository]]Section repository ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Repositories in this sense are the same as projects. @@ -1676,6 +1697,28 @@ reasonable timeout value. + Defaults to 0 seconds, wait indefinitely. + +[[upload]]Section upload +~~~~~~~~~~~~~~~~~~~~~~~~~~ +Sets the group of users allowed to execute 'upload-pack' on the +server, 'upload-pack' is what runs on the server during a user's +fetch, clone or repo sync command. + +---- +[upload] + allowGroup = GROUP_ALLOWED_TO_EXECUTE + allowGroup = YET_ANOTHER_GROUP_ALLOWED_TO_EXECUTE +---- + +[[upload.allowGroup]]upload.allowGroup:: ++ +Name of the groups of users that are allowed to execute 'upload-pack' +on the server. One or more groups can be set. ++ +If no groups are added, any user will be allowed to execute +'upload-pack' on the server. + + [[user]] Section user ~~~~~~~~~~~~~~~~~~~~~ diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ProjectServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ProjectServlet.java index 6a7fb06c82..09e2cce8ef 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ProjectServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ProjectServlet.java @@ -187,10 +187,14 @@ public class ProjectServlet extends GitServlet { @Override public UploadPack create(HttpServletRequest req, Repository repo) - throws ServiceNotEnabledException { + throws ServiceNotEnabledException, ServiceNotAuthorizedException { + ProjectControl pc = getProjectControl(req); + if (!pc.canRunUploadPack()) { + throw new ServiceNotAuthorizedException(); + } + // The Resolver above already checked READ access for us. // - ProjectControl pc = getProjectControl(req); UploadPack up = new UploadPack(repo); up.setPackConfig(packConfig); if (!pc.allRefsAreVisible()) { @@ -212,6 +216,10 @@ public class ProjectServlet extends GitServlet { public ReceivePack create(HttpServletRequest req, Repository db) throws ServiceNotEnabledException, ServiceNotAuthorizedException { final ProjectControl pc = getProjectControl(req); + if (!pc.canRunReceivePack()) { + throw new ServiceNotAuthorizedException(); + } + if (pc.getCurrentUser() instanceof IdentifiedUser) { final IdentifiedUser user = (IdentifiedUser) pc.getCurrentUser(); final ReceiveCommits rc = factory.create(pc, db); diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/CollectionsUtil.java b/gerrit-server/src/main/java/com/google/gerrit/common/CollectionsUtil.java new file mode 100644 index 0000000000..6e635cb2c2 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/common/CollectionsUtil.java @@ -0,0 +1,43 @@ +// 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.common; + +import java.util.Collection; + +/** Utilities for manipulating Collections . */ +public class CollectionsUtil { + /** + * Checks if any of the elements in the first collection can be found in the + * second collection. + * + * @param findAnyOfThese which elements to look for. + * @param inThisCollection where to look for them. + * @param type of the elements in question. + * @return {@code true} if any of the elements in {@code findAnyOfThese} can + * be found in {@code inThisCollection}, {@code false} otherwise. + */ + public static boolean isAnyIncludedIn(Collection findAnyOfThese, + Collection inThisCollection) { + for (E findThisItem : findAnyOfThese) { + if (inThisCollection.contains(findThisItem)) { + return true; + } + } + return false; + } + + private CollectionsUtil() { + } +} diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java index 232c09d7f2..c42d6539a0 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java @@ -56,6 +56,7 @@ import com.google.gerrit.server.mail.FromAddressGeneratorProvider; import com.google.gerrit.server.mail.SmtpEmailSender; import com.google.gerrit.server.patch.PatchListCacheImpl; import com.google.gerrit.server.patch.PatchSetInfoFactory; +import com.google.gerrit.server.project.AccessControlModule; import com.google.gerrit.server.project.ChangeControl; import com.google.gerrit.server.project.ProjectCacheImpl; import com.google.gerrit.server.project.ProjectControl; @@ -136,10 +137,6 @@ public class GerritGlobalModule extends FactoryModule { bind(Project.NameKey.class).annotatedWith(WildProjectName.class) .toProvider(WildProjectNameProvider.class).in(SINGLETON); - bind(new TypeLiteral>(){}).annotatedWith(ProjectCreatorGroups.class) - .toProvider(ProjectCreatorGroupsProvider.class).in(SINGLETON); - bind(new TypeLiteral>(){}).annotatedWith(ProjectOwnerGroups.class) - .toProvider(ProjectOwnerGroupsProvider.class).in(SINGLETON); bind(ApprovalTypes.class).toProvider(ApprovalTypesProvider.class).in( SINGLETON); bind(EmailExpander.class).toProvider(EmailExpanderProvider.class).in( @@ -156,6 +153,7 @@ public class GerritGlobalModule extends FactoryModule { install(GroupCacheImpl.module()); install(PatchListCacheImpl.module()); install(ProjectCacheImpl.module()); + install(new AccessControlModule()); factory(AccountInfoCacheFactory.Factory.class); factory(ProjectState.Factory.class); diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GitReceivePackGroups.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GitReceivePackGroups.java new file mode 100644 index 0000000000..35ea9e6c2f --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GitReceivePackGroups.java @@ -0,0 +1,37 @@ +// 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 static java.lang.annotation.RetentionPolicy.RUNTIME; + +import com.google.inject.BindingAnnotation; + +import java.lang.annotation.Retention; + +/** + * Used to populate the groups of users that are allowed to run + * receive-pack on the server. + * + * Gerrit.config example: + * + *
+ * [receive]
+ *     allowGroup = RECEIVE_GROUP_ALLOWED
+ * 
+ */ +@Retention(RUNTIME) +@BindingAnnotation +public @interface GitReceivePackGroups { +} diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GitReceivePackGroupsProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GitReceivePackGroupsProvider.java new file mode 100644 index 0000000000..9af6d629b6 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GitReceivePackGroupsProvider.java @@ -0,0 +1,42 @@ +// 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.AccountGroup; +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; +import java.util.HashSet; + +public class GitReceivePackGroupsProvider extends GroupSetProvider { + @Inject + public GitReceivePackGroupsProvider(@GerritServerConfig Config config, + AuthConfig authConfig, SchemaFactory db) { + super(config, db, "receive", null, "allowGroup"); + + // If no group was set, default to "registered users" + // + if (groupIds.isEmpty()) { + HashSet all = new HashSet(); + all.addAll(authConfig.getRegisteredGroups()); + all.removeAll(authConfig.getAnonymousGroups()); + groupIds = Collections.unmodifiableSet(all); + } + } +} diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GitUploadPackGroups.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GitUploadPackGroups.java new file mode 100644 index 0000000000..fa8ccb7244 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GitUploadPackGroups.java @@ -0,0 +1,37 @@ +// 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 static java.lang.annotation.RetentionPolicy.RUNTIME; + +import com.google.inject.BindingAnnotation; + +import java.lang.annotation.Retention; + +/** + * Used to populate the groups of users that are allowed to run + * upload-pack on the server. + * + * Gerrit.config example: + * + *
+ * [upload]
+ *     allowGroup = UPLOAD_GROUP_ALLOWED
+ * 
+ */ +@Retention(RUNTIME) +@BindingAnnotation +public @interface GitUploadPackGroups { +} diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GitUploadPackGroupsProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GitUploadPackGroupsProvider.java new file mode 100644 index 0000000000..bfb09a51bc --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GitUploadPackGroupsProvider.java @@ -0,0 +1,42 @@ +// 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.AccountGroup; +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; +import java.util.HashSet; + +public class GitUploadPackGroupsProvider extends GroupSetProvider { + @Inject + public GitUploadPackGroupsProvider(@GerritServerConfig Config config, + AuthConfig authConfig, SchemaFactory db) { + super(config, db, "upload", null, "allowGroup"); + + // If no group was set, default to "registered users" and "anonymous" + // + if (groupIds.isEmpty()) { + HashSet all = new HashSet(); + all.addAll(authConfig.getRegisteredGroups()); + all.addAll(authConfig.getAnonymousGroups()); + groupIds = Collections.unmodifiableSet(all); + } + } +} diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GroupSetProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GroupSetProvider.java new file mode 100644 index 0000000000..373fdb56e1 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GroupSetProvider.java @@ -0,0 +1,50 @@ +// 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 static com.google.gerrit.server.config.ConfigUtil.groupsFor; +import static java.util.Collections.unmodifiableSet; + +import com.google.gerrit.reviewdb.AccountGroup; +import com.google.gerrit.reviewdb.ReviewDb; +import com.google.gwtorm.client.SchemaFactory; +import com.google.inject.Inject; +import com.google.inject.Provider; + +import org.eclipse.jgit.lib.Config; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Set; + +public abstract class GroupSetProvider implements + Provider> { + private static final Logger log = + LoggerFactory.getLogger(GroupSetProvider.class); + + protected Set groupIds; + + @Inject + protected GroupSetProvider(@GerritServerConfig Config config, + SchemaFactory db, String section, String subsection, String name) { + String[] groupNames = config.getStringList(section, subsection, name); + groupIds = unmodifiableSet(groupsFor(db, groupNames, log)); + } + + @Override + public Set get() { + return groupIds; + } +} diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectCreatorGroupsProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectCreatorGroupsProvider.java index 616e691149..381c914238 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectCreatorGroupsProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectCreatorGroupsProvider.java @@ -14,19 +14,14 @@ package com.google.gerrit.server.config; -import com.google.gerrit.reviewdb.AccountGroup; import com.google.gerrit.reviewdb.ReviewDb; import com.google.gerrit.reviewdb.SystemConfig; import com.google.gwtorm.client.SchemaFactory; import com.google.inject.Inject; -import com.google.inject.Provider; import org.eclipse.jgit.lib.Config; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.Collections; -import java.util.Set; /** * Provider of the group(s) which are allowed to create new projects. Currently @@ -39,27 +34,14 @@ import java.util.Set; * createGroup = Administrators * */ -public class ProjectCreatorGroupsProvider implements - Provider> { - private static final Logger log = - LoggerFactory.getLogger(ProjectCreatorGroupsProvider.class); - - private final Set groupIds; - +public class ProjectCreatorGroupsProvider extends GroupSetProvider { @Inject - ProjectCreatorGroupsProvider(@GerritServerConfig final Config config, - SchemaFactory db, final SystemConfig systemConfig) { - String[] names = config.getStringList("repository", "*", "createGroup"); - Set createGroups = ConfigUtil.groupsFor(db, names, log); + public ProjectCreatorGroupsProvider(@GerritServerConfig final Config config, + final SystemConfig systemConfig, final SchemaFactory db) { + super(config, db, "repository", "*", "createGroup"); - if (createGroups.isEmpty()) { + if (groupIds.isEmpty()) { groupIds = Collections.singleton(systemConfig.adminGroupId); - } else { - groupIds = createGroups; } } - - public Set get() { - return groupIds; - } } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java index 30234c0bfb..c457d734a6 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java @@ -18,11 +18,8 @@ import com.google.gerrit.reviewdb.AccountGroup; import com.google.gerrit.reviewdb.ReviewDb; import com.google.gwtorm.client.SchemaFactory; import com.google.inject.Inject; -import com.google.inject.Provider; import org.eclipse.jgit.lib.Config; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.Set; @@ -37,28 +34,15 @@ import java.util.Set; * ownerGroup = Administrators * */ -public class ProjectOwnerGroupsProvider implements - Provider> { - private static final Logger log = - LoggerFactory.getLogger(ProjectOwnerGroupsProvider.class); - - private final Set groupIds; - +public class ProjectOwnerGroupsProvider extends GroupSetProvider { @Inject - ProjectOwnerGroupsProvider(@GerritServerConfig final Config config, - SchemaFactory db, - @ProjectCreatorGroups Set creatorGroups) { - String[] names = config.getStringList("repository", "*", "ownerGroup"); - Set ownerGroups = ConfigUtil.groupsFor(db, names, log); + public ProjectOwnerGroupsProvider( + @ProjectCreatorGroups final Set creatorGroups, + @GerritServerConfig final Config config, final SchemaFactory db) { + super(config, db, "repository", "*", "ownerGroup"); - if (ownerGroups.isEmpty()) { + if (groupIds.isEmpty()) { groupIds = creatorGroups; - } else { - groupIds = ownerGroups; } } - - public Set get() { - return groupIds; - } } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/AccessControlModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/AccessControlModule.java new file mode 100644 index 0000000000..1e2e7f4382 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/AccessControlModule.java @@ -0,0 +1,54 @@ +// 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.project; + +import static com.google.inject.Scopes.SINGLETON; + +import com.google.gerrit.reviewdb.AccountGroup; +import com.google.gerrit.server.config.FactoryModule; +import com.google.gerrit.server.config.GitReceivePackGroups; +import com.google.gerrit.server.config.GitReceivePackGroupsProvider; +import com.google.gerrit.server.config.GitUploadPackGroups; +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.ProjectOwnerGroupsProvider; +import com.google.inject.TypeLiteral; + +import java.util.Set; + +public class AccessControlModule extends FactoryModule { + @Override + protected void configure() { + bind(new TypeLiteral>() {}) // + .annotatedWith(ProjectCreatorGroups.class) // + .toProvider(ProjectCreatorGroupsProvider.class).in(SINGLETON); + + bind(new TypeLiteral>() {}) // + .annotatedWith(ProjectOwnerGroups.class) // + .toProvider(ProjectOwnerGroupsProvider.class).in(SINGLETON); + + bind(new TypeLiteral>() {}) // + .annotatedWith(GitUploadPackGroups.class) // + .toProvider(GitUploadPackGroupsProvider.class).in(SINGLETON); + + bind(new TypeLiteral>() {}) // + .annotatedWith(GitReceivePackGroups.class) // + .toProvider(GitReceivePackGroupsProvider.class).in(SINGLETON); + + factory(ProjectControl.AssistedFactory.class); + } +} diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java index ff788dd089..0539c85e97 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java @@ -14,6 +14,7 @@ package com.google.gerrit.server.project; +import static com.google.gerrit.common.CollectionsUtil.*; import com.google.gerrit.reviewdb.AccountGroup; import com.google.gerrit.reviewdb.ApprovalCategory; import com.google.gerrit.reviewdb.Branch; @@ -22,8 +23,11 @@ import com.google.gerrit.reviewdb.Project; import com.google.gerrit.reviewdb.RefRight; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.ReplicationUser; +import com.google.gerrit.server.config.GitReceivePackGroups; +import com.google.gerrit.server.config.GitUploadPackGroups; import com.google.inject.Inject; import com.google.inject.Provider; +import com.google.inject.assistedinject.Assisted; import java.util.HashSet; import java.util.Set; @@ -93,10 +97,22 @@ public class ProjectControl { } } + interface AssistedFactory { + ProjectControl create(CurrentUser who, ProjectState ps); + } + + private final Set uploadGroups; + private final Set receiveGroups; + private final CurrentUser user; private final ProjectState state; - ProjectControl(final CurrentUser who, final ProjectState ps) { + @Inject + ProjectControl(@GitUploadPackGroups Set uploadGroups, + @GitReceivePackGroups Set receiveGroups, + @Assisted CurrentUser who, @Assisted ProjectState ps) { + this.uploadGroups = uploadGroups; + this.receiveGroups = receiveGroups; user = who; state = ps; } @@ -230,4 +246,12 @@ public class ProjectControl { } return all; } + + public boolean canRunUploadPack() { + return isAnyIncludedIn(uploadGroups, user.getEffectiveGroups()); + } + + public boolean canRunReceivePack() { + return isAnyIncludedIn(receiveGroups, user.getEffectiveGroups()); + } } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java index 330fc6efa8..e52d25b716 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java @@ -40,6 +40,7 @@ public class ProjectState { private final AnonymousUser anonymousUser; private final Project.NameKey wildProject; private final ProjectCache projectCache; + private final ProjectControl.AssistedFactory projectControlFactory; private final Project project; private final Collection localRights; @@ -51,11 +52,13 @@ public class ProjectState { protected ProjectState(final AnonymousUser anonymousUser, final ProjectCache projectCache, @WildProjectName final Project.NameKey wildProject, + final ProjectControl.AssistedFactory projectControlFactory, @Assisted final Project project, @Assisted final Collection rights) { this.anonymousUser = anonymousUser; this.projectCache = projectCache; this.wildProject = wildProject; + this.projectControlFactory = projectControlFactory; this.project = project; this.localRights = rights; @@ -160,7 +163,7 @@ public class ProjectState { } public ProjectControl controlFor(final CurrentUser user) { - return new ProjectControl(user, this); + return projectControlFactory.create(user, this); } private static Collection filter(Collection all, diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java index 8c26705226..7c26b39e77 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java @@ -217,15 +217,18 @@ public class RefControlTest extends TestCase { } private ProjectControl user(AccountGroup.Id... memberOf) { - return new ProjectControl(new MockUser(memberOf), newProjectState()); + return new ProjectControl(Collections. emptySet(), + Collections. emptySet(), new MockUser(memberOf), + newProjectState()); } private ProjectState newProjectState() { ProjectCache projectCache = null; Project.NameKey wildProject = null; + ProjectControl.AssistedFactory projectControlFactory = null; ProjectState ps = - new ProjectState(anonymousUser, projectCache, wildProject, new Project( - projectNameKey), local); + new ProjectState(anonymousUser, projectCache, wildProject, + projectControlFactory, new Project(projectNameKey), local); ps.setInheritedRights(inherited); return ps; } diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProject.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProject.java index 6d027d71f2..40c7c5d745 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProject.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProject.java @@ -14,6 +14,7 @@ package com.google.gerrit.sshd.commands; +import com.google.gerrit.common.CollectionsUtil; import com.google.gerrit.reviewdb.AccountGroup; import com.google.gerrit.reviewdb.ApprovalCategory; import com.google.gerrit.reviewdb.Project; @@ -38,7 +39,6 @@ import org.kohsuke.args4j.Option; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -126,26 +126,6 @@ final class CreateProject extends BaseCommand { }); } - /** - * Checks if any of the elements in the first collection can be found in the - * second collection. - * - * @param findAnyOfThese which elements to look for. - * @param inThisCollection where to look for them. - * @param type of the elements in question. - * @return {@code true} if any of the elements in {@code findAnyOfThese} can - * be found in {@code inThisCollection}, {@code false} otherwise. - */ - private static boolean isAnyIncludedIn(Collection findAnyOfThese, - Collection inThisCollection) { - for (E findThisItem : findAnyOfThese) { - if (inThisCollection.contains(findThisItem)) { - return true; - } - } - return false; - } - private void createProject() throws OrmException { final Project.NameKey newProjectNameKey = new Project.NameKey(projectName); @@ -179,7 +159,7 @@ final class CreateProject extends BaseCommand { projectName.substring(0, projectName.length() - ".git".length()); } - if (!isAnyIncludedIn(currentUser.getEffectiveGroups(), projectCreatorGroups)) { + if (!CollectionsUtil.isAnyIncludedIn(currentUser.getEffectiveGroups(), projectCreatorGroups)) { throw new Failure(1, "fatal: Not permitted to create " + projectName); } diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Receive.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Receive.java index b601f42492..027cfd6df1 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Receive.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Receive.java @@ -58,6 +58,10 @@ final class Receive extends AbstractGitCommand { @Override protected void runImpl() throws IOException, Failure { + if (!projectControl.canRunReceivePack()) { + throw new Failure(1, "fatal: receive-pack not permitted on this server"); + } + final ReceiveCommits receive = factory.create(projectControl, repo); ReceiveCommits.Capable r = receive.canUpload(); diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Upload.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Upload.java index c3be7c7e68..bcc9d19d32 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Upload.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Upload.java @@ -36,6 +36,10 @@ final class Upload extends AbstractGitCommand { @Override protected void runImpl() throws IOException, Failure { + if (!projectControl.canRunUploadPack()) { + throw new Failure(1, "fatal: upload-pack not permitted on this server"); + } + final UploadPack up = new UploadPack(repo); if (!projectControl.allRefsAreVisible()) { up.setRefFilter(new VisibleRefFilter(repo, projectControl, db.get()));