Merge changes Ib3f0849f,I9d12ed46,I38520cff

* changes:
  Factor out Contributor Agreements from ProjectControl
  Add ProjectPermissions for upload and receive pack, migrate callers
  Add ProjectPermission.READ_NO_CONFIG
This commit is contained in:
David Pursehouse
2017-10-01 15:31:51 +00:00
committed by Gerrit Code Review
18 changed files with 338 additions and 143 deletions

View File

@@ -19,6 +19,7 @@ import com.google.common.collect.Lists;
import com.google.gerrit.acceptance.InProcessProtocol.Context; import com.google.gerrit.acceptance.InProcessProtocol.Context;
import com.google.gerrit.common.data.Capable; import com.google.gerrit.common.data.Capable;
import com.google.gerrit.extensions.registration.DynamicSet; import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.reviewdb.server.ReviewDb;
@@ -34,8 +35,13 @@ import com.google.gerrit.server.git.TransferConfig;
import com.google.gerrit.server.git.VisibleRefFilter; import com.google.gerrit.server.git.VisibleRefFilter;
import com.google.gerrit.server.git.receive.AsyncReceiveCommits; import com.google.gerrit.server.git.receive.AsyncReceiveCommits;
import com.google.gerrit.server.git.validators.UploadValidators; import com.google.gerrit.server.git.validators.UploadValidators;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.project.NoSuchProjectException; import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectControl; import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.util.RequestContext; import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.RequestScopePropagator; import com.google.gerrit.server.util.RequestScopePropagator;
import com.google.gerrit.server.util.ThreadLocalRequestContext; import com.google.gerrit.server.util.ThreadLocalRequestContext;
@@ -203,29 +209,32 @@ class InProcessProtocol extends TestProtocol<Context> {
private static class Upload implements UploadPackFactory<Context> { private static class Upload implements UploadPackFactory<Context> {
private final Provider<CurrentUser> userProvider; private final Provider<CurrentUser> userProvider;
private final ProjectControl.GenericFactory projectControlFactory;
private final VisibleRefFilter.Factory refFilterFactory; private final VisibleRefFilter.Factory refFilterFactory;
private final TransferConfig transferConfig; private final TransferConfig transferConfig;
private final DynamicSet<PreUploadHook> preUploadHooks; private final DynamicSet<PreUploadHook> preUploadHooks;
private final UploadValidators.Factory uploadValidatorsFactory; private final UploadValidators.Factory uploadValidatorsFactory;
private final ThreadLocalRequestContext threadContext; private final ThreadLocalRequestContext threadContext;
private final ProjectCache projectCache;
private final PermissionBackend permissionBackend;
@Inject @Inject
Upload( Upload(
Provider<CurrentUser> userProvider, Provider<CurrentUser> userProvider,
ProjectControl.GenericFactory projectControlFactory,
VisibleRefFilter.Factory refFilterFactory, VisibleRefFilter.Factory refFilterFactory,
TransferConfig transferConfig, TransferConfig transferConfig,
DynamicSet<PreUploadHook> preUploadHooks, DynamicSet<PreUploadHook> preUploadHooks,
UploadValidators.Factory uploadValidatorsFactory, UploadValidators.Factory uploadValidatorsFactory,
ThreadLocalRequestContext threadContext) { ThreadLocalRequestContext threadContext,
ProjectCache projectCache,
PermissionBackend permissionBackend) {
this.userProvider = userProvider; this.userProvider = userProvider;
this.projectControlFactory = projectControlFactory;
this.refFilterFactory = refFilterFactory; this.refFilterFactory = refFilterFactory;
this.transferConfig = transferConfig; this.transferConfig = transferConfig;
this.preUploadHooks = preUploadHooks; this.preUploadHooks = preUploadHooks;
this.uploadValidatorsFactory = uploadValidatorsFactory; this.uploadValidatorsFactory = uploadValidatorsFactory;
this.threadContext = threadContext; this.threadContext = threadContext;
this.projectCache = projectCache;
this.permissionBackend = permissionBackend;
} }
@Override @Override
@@ -236,23 +245,35 @@ class InProcessProtocol extends TestProtocol<Context> {
// its original context anyway. // its original context anyway.
threadContext.setContext(req); threadContext.setContext(req);
current.set(req); current.set(req);
try {
ProjectControl ctl = projectControlFactory.controlFor(req.project, userProvider.get());
if (!ctl.canRunUploadPack()) {
throw new ServiceNotAuthorizedException();
}
UploadPack up = new UploadPack(repo); try {
up.setPackConfig(transferConfig.getPackConfig()); permissionBackend
up.setTimeout(transferConfig.getTimeout()); .user(userProvider)
up.setAdvertiseRefsHook(refFilterFactory.create(ctl.getProjectState(), repo)); .project(req.project)
List<PreUploadHook> hooks = Lists.newArrayList(preUploadHooks); .check(ProjectPermission.RUN_UPLOAD_PACK);
hooks.add(uploadValidatorsFactory.create(ctl.getProject(), repo, "localhost-test")); } catch (AuthException e) {
up.setPreUploadHook(PreUploadHookChain.newChain(hooks)); throw new ServiceNotAuthorizedException();
return up; } catch (PermissionBackendException e) {
} catch (NoSuchProjectException | IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
ProjectState projectState;
try {
projectState = projectCache.checkedGet(req.project);
} catch (IOException e) {
throw new RuntimeException(e);
}
if (projectState == null) {
throw new RuntimeException("can't load project state for " + req.project.get());
}
UploadPack up = new UploadPack(repo);
up.setPackConfig(transferConfig.getPackConfig());
up.setTimeout(transferConfig.getTimeout());
up.setAdvertiseRefsHook(refFilterFactory.create(projectState, repo));
List<PreUploadHook> hooks = Lists.newArrayList(preUploadHooks);
hooks.add(uploadValidatorsFactory.create(projectState.getProject(), repo, "localhost-test"));
up.setPreUploadHook(PreUploadHookChain.newChain(hooks));
return up;
} }
} }
@@ -264,6 +285,7 @@ class InProcessProtocol extends TestProtocol<Context> {
private final DynamicSet<ReceivePackInitializer> receivePackInitializers; private final DynamicSet<ReceivePackInitializer> receivePackInitializers;
private final DynamicSet<PostReceiveHook> postReceiveHooks; private final DynamicSet<PostReceiveHook> postReceiveHooks;
private final ThreadLocalRequestContext threadContext; private final ThreadLocalRequestContext threadContext;
private final PermissionBackend permissionBackend;
@Inject @Inject
Receive( Receive(
@@ -273,7 +295,8 @@ class InProcessProtocol extends TestProtocol<Context> {
TransferConfig config, TransferConfig config,
DynamicSet<ReceivePackInitializer> receivePackInitializers, DynamicSet<ReceivePackInitializer> receivePackInitializers,
DynamicSet<PostReceiveHook> postReceiveHooks, DynamicSet<PostReceiveHook> postReceiveHooks,
ThreadLocalRequestContext threadContext) { ThreadLocalRequestContext threadContext,
PermissionBackend permissionBackend) {
this.userProvider = userProvider; this.userProvider = userProvider;
this.projectControlFactory = projectControlFactory; this.projectControlFactory = projectControlFactory;
this.factory = factory; this.factory = factory;
@@ -281,6 +304,7 @@ class InProcessProtocol extends TestProtocol<Context> {
this.receivePackInitializers = receivePackInitializers; this.receivePackInitializers = receivePackInitializers;
this.postReceiveHooks = postReceiveHooks; this.postReceiveHooks = postReceiveHooks;
this.threadContext = threadContext; this.threadContext = threadContext;
this.permissionBackend = permissionBackend;
} }
@Override @Override
@@ -291,12 +315,18 @@ class InProcessProtocol extends TestProtocol<Context> {
// its original context anyway. // its original context anyway.
threadContext.setContext(req); threadContext.setContext(req);
current.set(req); current.set(req);
try {
permissionBackend
.user(userProvider)
.project(req.project)
.check(ProjectPermission.RUN_RECEIVE_PACK);
} catch (AuthException e) {
throw new ServiceNotAuthorizedException();
} catch (PermissionBackendException e) {
throw new RuntimeException(e);
}
try { try {
ProjectControl ctl = projectControlFactory.controlFor(req.project, userProvider.get()); ProjectControl ctl = projectControlFactory.controlFor(req.project, userProvider.get());
if (!ctl.canRunReceivePack()) {
throw new ServiceNotAuthorizedException();
}
AsyncReceiveCommits arc = factory.create(ctl, db, null, ImmutableSetMultimap.of()); AsyncReceiveCommits arc = factory.create(ctl, db, null, ImmutableSetMultimap.of());
ReceivePack rp = arc.getReceivePack(); ReceivePack rp = arc.getReceivePack();

View File

@@ -234,13 +234,16 @@ public class GitOverHttpServlet extends GitServlet {
static class UploadFilter implements Filter { static class UploadFilter implements Filter {
private final VisibleRefFilter.Factory refFilterFactory; private final VisibleRefFilter.Factory refFilterFactory;
private final UploadValidators.Factory uploadValidatorsFactory; private final UploadValidators.Factory uploadValidatorsFactory;
private final PermissionBackend permissionBackend;
@Inject @Inject
UploadFilter( UploadFilter(
VisibleRefFilter.Factory refFilterFactory, VisibleRefFilter.Factory refFilterFactory,
UploadValidators.Factory uploadValidatorsFactory) { UploadValidators.Factory uploadValidatorsFactory,
PermissionBackend permissionBackend) {
this.refFilterFactory = refFilterFactory; this.refFilterFactory = refFilterFactory;
this.uploadValidatorsFactory = uploadValidatorsFactory; this.uploadValidatorsFactory = uploadValidatorsFactory;
this.permissionBackend = permissionBackend;
} }
@Override @Override
@@ -251,13 +254,20 @@ public class GitOverHttpServlet extends GitServlet {
ProjectControl pc = (ProjectControl) request.getAttribute(ATT_CONTROL); ProjectControl pc = (ProjectControl) request.getAttribute(ATT_CONTROL);
UploadPack up = (UploadPack) request.getAttribute(ServletUtils.ATTRIBUTE_HANDLER); UploadPack up = (UploadPack) request.getAttribute(ServletUtils.ATTRIBUTE_HANDLER);
if (!pc.canRunUploadPack()) { try {
permissionBackend
.user(pc.getUser())
.project(pc.getProject().getNameKey())
.check(ProjectPermission.RUN_UPLOAD_PACK);
} catch (AuthException e) {
GitSmartHttpTools.sendError( GitSmartHttpTools.sendError(
(HttpServletRequest) request, (HttpServletRequest) request,
(HttpServletResponse) response, (HttpServletResponse) response,
HttpServletResponse.SC_FORBIDDEN, HttpServletResponse.SC_FORBIDDEN,
"upload-pack not permitted on this server"); "upload-pack not permitted on this server");
return; return;
} catch (PermissionBackendException e) {
throw new ServletException(e);
} }
// We use getRemoteHost() here instead of getRemoteAddr() because REMOTE_ADDR // We use getRemoteHost() here instead of getRemoteAddr() because REMOTE_ADDR
// may have been overridden by a proxy server -- we'll try to avoid this. // may have been overridden by a proxy server -- we'll try to avoid this.
@@ -312,10 +322,14 @@ public class GitOverHttpServlet extends GitServlet {
static class ReceiveFilter implements Filter { static class ReceiveFilter implements Filter {
private final Cache<AdvertisedObjectsCacheKey, Set<ObjectId>> cache; private final Cache<AdvertisedObjectsCacheKey, Set<ObjectId>> cache;
private final PermissionBackend permissionBackend;
@Inject @Inject
ReceiveFilter(@Named(ID_CACHE) Cache<AdvertisedObjectsCacheKey, Set<ObjectId>> cache) { ReceiveFilter(
@Named(ID_CACHE) Cache<AdvertisedObjectsCacheKey, Set<ObjectId>> cache,
PermissionBackend permissionBackend) {
this.cache = cache; this.cache = cache;
this.permissionBackend = permissionBackend;
} }
@Override @Override
@@ -329,13 +343,20 @@ public class GitOverHttpServlet extends GitServlet {
ProjectControl pc = (ProjectControl) request.getAttribute(ATT_CONTROL); ProjectControl pc = (ProjectControl) request.getAttribute(ATT_CONTROL);
Project.NameKey projectName = pc.getProject().getNameKey(); Project.NameKey projectName = pc.getProject().getNameKey();
if (!pc.canRunReceivePack()) { try {
permissionBackend
.user(pc.getUser())
.project(pc.getProject().getNameKey())
.check(ProjectPermission.RUN_RECEIVE_PACK);
} catch (AuthException e) {
GitSmartHttpTools.sendError( GitSmartHttpTools.sendError(
(HttpServletRequest) request, (HttpServletRequest) request,
(HttpServletResponse) response, (HttpServletResponse) response,
HttpServletResponse.SC_FORBIDDEN, HttpServletResponse.SC_FORBIDDEN,
"receive-pack not permitted on this server"); "receive-pack not permitted on this server");
return; return;
} catch (PermissionBackendException e) {
throw new RuntimeException(e);
} }
Capable s = arc.canUpload(); Capable s = arc.canUpload();

View File

@@ -25,6 +25,7 @@ import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
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.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ContributorAgreementsChecker;
import com.google.gerrit.server.project.NoSuchProjectException; 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.ProjectControl; import com.google.gerrit.server.project.ProjectControl;
@@ -62,6 +63,7 @@ class ChangeProjectAccess extends ProjectAccessHandler<ProjectAccess> {
AllProjectsName allProjects, AllProjectsName allProjects,
Provider<SetParent> setParent, Provider<SetParent> setParent,
GitReferenceUpdated gitRefUpdated, GitReferenceUpdated gitRefUpdated,
ContributorAgreementsChecker contributorAgreements,
@Assisted("projectName") Project.NameKey projectName, @Assisted("projectName") Project.NameKey projectName,
@Nullable @Assisted ObjectId base, @Nullable @Assisted ObjectId base,
@Assisted List<AccessSection> sectionList, @Assisted List<AccessSection> sectionList,
@@ -78,6 +80,7 @@ class ChangeProjectAccess extends ProjectAccessHandler<ProjectAccess> {
sectionList, sectionList,
parentProjectName, parentProjectName,
message, message,
contributorAgreements,
true); true);
this.projectAccessFactory = projectAccessFactory; this.projectAccessFactory = projectAccessFactory;
this.projectCache = projectCache; this.projectCache = projectCache;

View File

@@ -18,7 +18,6 @@ import static com.google.gerrit.common.ProjectAccessUtil.mergeSections;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.google.gerrit.common.data.AccessSection; import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.Capable;
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;
import com.google.gerrit.common.data.PermissionRule; import com.google.gerrit.common.data.PermissionRule;
@@ -37,6 +36,7 @@ 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.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ContributorAgreementsChecker;
import com.google.gerrit.server.project.NoSuchProjectException; import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectControl; import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.RefPattern; import com.google.gerrit.server.project.RefPattern;
@@ -58,6 +58,7 @@ public abstract class ProjectAccessHandler<T> extends Handler<T> {
private final MetaDataUpdate.User metaDataUpdateFactory; private final MetaDataUpdate.User metaDataUpdateFactory;
private final AllProjectsName allProjects; private final AllProjectsName allProjects;
private final Provider<SetParent> setParent; private final Provider<SetParent> setParent;
private final ContributorAgreementsChecker contributorAgreements;
protected final Project.NameKey projectName; protected final Project.NameKey projectName;
protected final ObjectId base; protected final ObjectId base;
@@ -77,6 +78,7 @@ public abstract class ProjectAccessHandler<T> extends Handler<T> {
List<AccessSection> sectionList, List<AccessSection> sectionList,
Project.NameKey parentProjectName, Project.NameKey parentProjectName,
String message, String message,
ContributorAgreementsChecker contributorAgreements,
boolean checkIfOwner) { boolean checkIfOwner) {
this.projectControlFactory = projectControlFactory; this.projectControlFactory = projectControlFactory;
this.groupBackend = groupBackend; this.groupBackend = groupBackend;
@@ -89,6 +91,7 @@ public abstract class ProjectAccessHandler<T> extends Handler<T> {
this.sectionList = sectionList; this.sectionList = sectionList;
this.parentProjectName = parentProjectName; this.parentProjectName = parentProjectName;
this.message = message; this.message = message;
this.contributorAgreements = contributorAgreements;
this.checkIfOwner = checkIfOwner; this.checkIfOwner = checkIfOwner;
} }
@@ -99,9 +102,10 @@ public abstract class ProjectAccessHandler<T> extends Handler<T> {
PermissionDeniedException, PermissionBackendException { PermissionDeniedException, PermissionBackendException {
final ProjectControl projectControl = projectControlFactory.controlFor(projectName); final ProjectControl projectControl = projectControlFactory.controlFor(projectName);
Capable r = projectControl.canPushToAtLeastOneRef(); try {
if (r != Capable.OK) { contributorAgreements.check(projectName, projectControl.getUser());
throw new PermissionDeniedException(r.getMessage()); } catch (AuthException e) {
throw new PermissionDeniedException(e.getMessage());
} }
try (MetaDataUpdate md = metaDataUpdateFactory.create(projectName)) { try (MetaDataUpdate md = metaDataUpdateFactory.create(projectName)) {

View File

@@ -43,6 +43,7 @@ import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gerrit.server.permissions.PermissionBackend; import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.RefPermission; import com.google.gerrit.server.permissions.RefPermission;
import com.google.gerrit.server.project.ContributorAgreementsChecker;
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;
import com.google.gerrit.server.project.SetParent; import com.google.gerrit.server.project.SetParent;
@@ -94,6 +95,7 @@ public class ReviewProjectAccess extends ProjectAccessHandler<Change.Id> {
BatchUpdate.Factory updateFactory, BatchUpdate.Factory updateFactory,
Provider<SetParent> setParent, Provider<SetParent> setParent,
Sequences seq, Sequences seq,
ContributorAgreementsChecker contributorAgreements,
@Assisted("projectName") Project.NameKey projectName, @Assisted("projectName") Project.NameKey projectName,
@Nullable @Assisted ObjectId base, @Nullable @Assisted ObjectId base,
@Assisted List<AccessSection> sectionList, @Assisted List<AccessSection> sectionList,
@@ -110,6 +112,7 @@ public class ReviewProjectAccess extends ProjectAccessHandler<Change.Id> {
sectionList, sectionList,
parentProjectName, parentProjectName,
message, message,
contributorAgreements,
false); false);
this.db = db; this.db = db;
this.permissionBackend = permissionBackend; this.permissionBackend = permissionBackend;

View File

@@ -31,10 +31,10 @@ import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission; import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.permissions.RefPermission; import com.google.gerrit.server.permissions.RefPermission;
import com.google.gerrit.server.project.ContributorAgreementsChecker;
import com.google.gerrit.server.project.InvalidChangeOperationException; import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.NoSuchChangeException; import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.NoSuchProjectException; import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.update.BatchUpdate; import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.RetryHelper; import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView; import com.google.gerrit.server.update.RetryingRestModifyView;
@@ -54,7 +54,7 @@ public class CherryPick
private final Provider<CurrentUser> user; private final Provider<CurrentUser> user;
private final CherryPickChange cherryPickChange; private final CherryPickChange cherryPickChange;
private final ChangeJson.Factory json; private final ChangeJson.Factory json;
private final ProjectControl.GenericFactory projectControlFactory; private final ContributorAgreementsChecker contributorAgreements;
@Inject @Inject
CherryPick( CherryPick(
@@ -63,13 +63,13 @@ public class CherryPick
RetryHelper retryHelper, RetryHelper retryHelper,
CherryPickChange cherryPickChange, CherryPickChange cherryPickChange,
ChangeJson.Factory json, ChangeJson.Factory json,
ProjectControl.GenericFactory projectControlFactory) { ContributorAgreementsChecker contributorAgreements) {
super(retryHelper); super(retryHelper);
this.permissionBackend = permissionBackend; this.permissionBackend = permissionBackend;
this.user = user; this.user = user;
this.cherryPickChange = cherryPickChange; this.cherryPickChange = cherryPickChange;
this.json = json; this.json = json;
this.projectControlFactory = projectControlFactory; this.contributorAgreements = contributorAgreements;
} }
@Override @Override
@@ -85,7 +85,8 @@ public class CherryPick
} }
String refName = RefNames.fullName(input.destination); String refName = RefNames.fullName(input.destination);
CreateChange.checkValidCLA(projectControlFactory.controlFor(rsrc.getProject(), rsrc.getUser())); contributorAgreements.check(rsrc.getProject(), rsrc.getUser());
permissionBackend permissionBackend
.user(user) .user(user)
.project(rsrc.getChange().getProject()) .project(rsrc.getChange().getProject())

View File

@@ -30,6 +30,7 @@ import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.RefPermission; import com.google.gerrit.server.permissions.RefPermission;
import com.google.gerrit.server.project.CommitResource; import com.google.gerrit.server.project.CommitResource;
import com.google.gerrit.server.project.ContributorAgreementsChecker;
import com.google.gerrit.server.project.InvalidChangeOperationException; import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.NoSuchProjectException; import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.update.BatchUpdate; import com.google.gerrit.server.update.BatchUpdate;
@@ -51,6 +52,7 @@ public class CherryPickCommit
private final Provider<CurrentUser> user; private final Provider<CurrentUser> user;
private final CherryPickChange cherryPickChange; private final CherryPickChange cherryPickChange;
private final ChangeJson.Factory json; private final ChangeJson.Factory json;
private final ContributorAgreementsChecker contributorAgreements;
@Inject @Inject
CherryPickCommit( CherryPickCommit(
@@ -58,12 +60,14 @@ public class CherryPickCommit
Provider<CurrentUser> user, Provider<CurrentUser> user,
CherryPickChange cherryPickChange, CherryPickChange cherryPickChange,
ChangeJson.Factory json, ChangeJson.Factory json,
PermissionBackend permissionBackend) { PermissionBackend permissionBackend,
ContributorAgreementsChecker contributorAgreements) {
super(retryHelper); super(retryHelper);
this.permissionBackend = permissionBackend; this.permissionBackend = permissionBackend;
this.user = user; this.user = user;
this.cherryPickChange = cherryPickChange; this.cherryPickChange = cherryPickChange;
this.json = json; this.json = json;
this.contributorAgreements = contributorAgreements;
} }
@Override @Override
@@ -83,7 +87,7 @@ public class CherryPickCommit
} }
String refName = RefNames.fullName(destination); String refName = RefNames.fullName(destination);
CreateChange.checkValidCLA(rsrc.getProjectState().controlFor(user.get())); contributorAgreements.check(projectName, user.get());
permissionBackend permissionBackend
.user(user) .user(user)
.project(projectName) .project(projectName)

View File

@@ -20,14 +20,12 @@ import com.google.common.base.MoreObjects;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.gerrit.common.TimeUtil; import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.common.data.Capable;
import com.google.gerrit.extensions.client.ChangeStatus; import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo; import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
import com.google.gerrit.extensions.client.SubmitType; import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeInput; import com.google.gerrit.extensions.common.ChangeInput;
import com.google.gerrit.extensions.common.MergeInput; import com.google.gerrit.extensions.common.MergeInput;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.Response; import com.google.gerrit.extensions.restapi.Response;
@@ -57,8 +55,8 @@ import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.RefPermission; import com.google.gerrit.server.permissions.RefPermission;
import com.google.gerrit.server.project.CommitsCollection; import com.google.gerrit.server.project.CommitsCollection;
import com.google.gerrit.server.project.ContributorAgreementsChecker;
import com.google.gerrit.server.project.InvalidChangeOperationException; import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.ProjectResource; import com.google.gerrit.server.project.ProjectResource;
import com.google.gerrit.server.project.ProjectState; import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.ProjectsCollection; import com.google.gerrit.server.project.ProjectsCollection;
@@ -110,6 +108,7 @@ public class CreateChange
private final MergeUtil.Factory mergeUtilFactory; private final MergeUtil.Factory mergeUtilFactory;
private final SubmitType submitType; private final SubmitType submitType;
private final NotifyUtil notifyUtil; private final NotifyUtil notifyUtil;
private final ContributorAgreementsChecker contributorAgreements;
@Inject @Inject
CreateChange( CreateChange(
@@ -130,7 +129,8 @@ public class CreateChange
PatchSetUtil psUtil, PatchSetUtil psUtil,
@GerritServerConfig Config config, @GerritServerConfig Config config,
MergeUtil.Factory mergeUtilFactory, MergeUtil.Factory mergeUtilFactory,
NotifyUtil notifyUtil) { NotifyUtil notifyUtil,
ContributorAgreementsChecker contributorAgreements) {
super(retryHelper); super(retryHelper);
this.anonymousCowardName = anonymousCowardName; this.anonymousCowardName = anonymousCowardName;
this.db = db; this.db = db;
@@ -149,6 +149,7 @@ public class CreateChange
this.submitType = config.getEnum("project", null, "submitType", SubmitType.MERGE_IF_NECESSARY); this.submitType = config.getEnum("project", null, "submitType", SubmitType.MERGE_IF_NECESSARY);
this.mergeUtilFactory = mergeUtilFactory; this.mergeUtilFactory = mergeUtilFactory;
this.notifyUtil = notifyUtil; this.notifyUtil = notifyUtil;
this.contributorAgreements = contributorAgreements;
} }
@Override @Override
@@ -175,7 +176,7 @@ public class CreateChange
} }
ProjectResource rsrc = projectsCollection.parse(input.project); ProjectResource rsrc = projectsCollection.parse(input.project);
checkValidCLA(rsrc.getControl()); contributorAgreements.check(rsrc.getNameKey(), rsrc.getUser());
Project.NameKey project = rsrc.getNameKey(); Project.NameKey project = rsrc.getNameKey();
String refName = RefNames.fullName(input.branch); String refName = RefNames.fullName(input.branch);
@@ -341,11 +342,4 @@ public class CreateChange
private static ObjectId emptyTreeId(ObjectInserter inserter) throws IOException { private static ObjectId emptyTreeId(ObjectInserter inserter) throws IOException {
return inserter.insert(new TreeFormatter()); return inserter.insert(new TreeFormatter());
} }
static void checkValidCLA(ProjectControl ctl) throws AuthException {
Capable capable = ctl.canPushToAtLeastOneRef();
if (capable != Capable.OK) {
throw new AuthException(capable.getMessage());
}
}
} }

View File

@@ -26,8 +26,8 @@ import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestView; import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.server.edit.ChangeEdit; import com.google.gerrit.server.edit.ChangeEdit;
import com.google.gerrit.server.edit.ChangeEditUtil; import com.google.gerrit.server.edit.ChangeEditUtil;
import com.google.gerrit.server.project.ContributorAgreementsChecker;
import com.google.gerrit.server.project.NoSuchProjectException; import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.update.BatchUpdate; import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.RetryHelper; import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView; import com.google.gerrit.server.update.RetryingRestModifyView;
@@ -76,18 +76,18 @@ public class PublishChangeEdit
private final ChangeEditUtil editUtil; private final ChangeEditUtil editUtil;
private final NotifyUtil notifyUtil; private final NotifyUtil notifyUtil;
private final ProjectControl.GenericFactory projectControlFactory; private final ContributorAgreementsChecker contributorAgreementsChecker;
@Inject @Inject
Publish( Publish(
RetryHelper retryHelper, RetryHelper retryHelper,
ChangeEditUtil editUtil, ChangeEditUtil editUtil,
NotifyUtil notifyUtil, NotifyUtil notifyUtil,
ProjectControl.GenericFactory projectControlFactory) { ContributorAgreementsChecker contributorAgreementsChecker) {
super(retryHelper); super(retryHelper);
this.editUtil = editUtil; this.editUtil = editUtil;
this.notifyUtil = notifyUtil; this.notifyUtil = notifyUtil;
this.projectControlFactory = projectControlFactory; this.contributorAgreementsChecker = contributorAgreementsChecker;
} }
@Override @Override
@@ -95,8 +95,7 @@ public class PublishChangeEdit
BatchUpdate.Factory updateFactory, ChangeResource rsrc, PublishChangeEditInput in) BatchUpdate.Factory updateFactory, ChangeResource rsrc, PublishChangeEditInput in)
throws IOException, OrmException, RestApiException, UpdateException, ConfigInvalidException, throws IOException, OrmException, RestApiException, UpdateException, ConfigInvalidException,
NoSuchProjectException { NoSuchProjectException {
CreateChange.checkValidCLA( contributorAgreementsChecker.check(rsrc.getProject(), rsrc.getUser());
projectControlFactory.controlFor(rsrc.getProject(), rsrc.getUser()));
Optional<ChangeEdit> edit = editUtil.byChange(rsrc.getNotes(), rsrc.getUser()); Optional<ChangeEdit> edit = editUtil.byChange(rsrc.getNotes(), rsrc.getUser());
if (!edit.isPresent()) { if (!edit.isPresent()) {
throw new ResourceConflictException( throw new ResourceConflictException(

View File

@@ -46,9 +46,9 @@ import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ReviewerStateInternal; import com.google.gerrit.server.notedb.ReviewerStateInternal;
import com.google.gerrit.server.permissions.PermissionBackend; import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ContributorAgreementsChecker;
import com.google.gerrit.server.project.NoSuchChangeException; import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.NoSuchProjectException; import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.update.BatchUpdate; import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.BatchUpdateOp; import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext; import com.google.gerrit.server.update.ChangeContext;
@@ -95,7 +95,7 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
private final PersonIdent serverIdent; private final PersonIdent serverIdent;
private final ApprovalsUtil approvalsUtil; private final ApprovalsUtil approvalsUtil;
private final ChangeReverted changeReverted; private final ChangeReverted changeReverted;
private final ProjectControl.GenericFactory projectControlFactory; private final ContributorAgreementsChecker contributorAgreements;
@Inject @Inject
Revert( Revert(
@@ -112,7 +112,7 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
@GerritPersonIdent PersonIdent serverIdent, @GerritPersonIdent PersonIdent serverIdent,
ApprovalsUtil approvalsUtil, ApprovalsUtil approvalsUtil,
ChangeReverted changeReverted, ChangeReverted changeReverted,
ProjectControl.GenericFactory projectControlFactory) { ContributorAgreementsChecker contributorAgreements) {
super(retryHelper); super(retryHelper);
this.db = db; this.db = db;
this.permissionBackend = permissionBackend; this.permissionBackend = permissionBackend;
@@ -126,7 +126,7 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
this.serverIdent = serverIdent; this.serverIdent = serverIdent;
this.approvalsUtil = approvalsUtil; this.approvalsUtil = approvalsUtil;
this.changeReverted = changeReverted; this.changeReverted = changeReverted;
this.projectControlFactory = projectControlFactory; this.contributorAgreements = contributorAgreements;
} }
@Override @Override
@@ -139,7 +139,7 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
throw new ResourceConflictException("change is " + ChangeUtil.status(change)); throw new ResourceConflictException("change is " + ChangeUtil.status(change));
} }
CreateChange.checkValidCLA(projectControlFactory.controlFor(rsrc.getProject(), rsrc.getUser())); contributorAgreements.check(rsrc.getProject(), rsrc.getUser());
permissionBackend.user(rsrc.getUser()).ref(change.getDest()).check(CREATE_CHANGE); permissionBackend.user(rsrc.getUser()).ref(change.getDest()).check(CREATE_CHANGE);
Change.Id revertId = Change.Id revertId =

View File

@@ -20,7 +20,6 @@ import static com.google.gerrit.reviewdb.client.RefNames.REFS_CONFIG;
import static com.google.gerrit.reviewdb.client.RefNames.REFS_USERS_SELF; import static com.google.gerrit.reviewdb.client.RefNames.REFS_USERS_SELF;
import static java.util.stream.Collectors.toMap; import static java.util.stream.Collectors.toMap;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.Nullable; import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
@@ -37,6 +36,7 @@ import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.GlobalPermission; import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend; import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.permissions.RefPermission; import com.google.gerrit.server.permissions.RefPermission;
import com.google.gerrit.server.project.ProjectControl; import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.ProjectState; import com.google.gerrit.server.project.ProjectState;
@@ -118,15 +118,18 @@ public class VisibleRefFilter extends AbstractAdvertiseRefsHook {
refs = addUsersSelfSymref(refs); refs = addUsersSelfSymref(refs);
} }
projectCtl = projectState.controlFor(user.get()); PermissionBackend.WithUser withUser = permissionBackend.user(user);
if (projectCtl.allRefsAreVisible(ImmutableSet.of(REFS_CONFIG))) { PermissionBackend.ForProject forProject = withUser.project(projectState.getNameKey());
if (checkProjectPermission(forProject, ProjectPermission.READ)) {
return refs;
} else if (checkProjectPermission(forProject, ProjectPermission.READ_NO_CONFIG)) {
return fastHideRefsMetaConfig(refs); return fastHideRefsMetaConfig(refs);
} }
Account.Id userId; Account.Id userId;
boolean viewMetadata; boolean viewMetadata;
if (user.get().isIdentifiedUser()) { if (user.get().isIdentifiedUser()) {
viewMetadata = permissionBackend.user(user).testOrFalse(GlobalPermission.ACCESS_DATABASE); viewMetadata = withUser.testOrFalse(GlobalPermission.ACCESS_DATABASE);
IdentifiedUser u = user.get().asIdentifiedUser(); IdentifiedUser u = user.get().asIdentifiedUser();
userId = u.getAccountId(); userId = u.getAccountId();
userEditPrefix = RefNames.refsEditPrefix(userId); userEditPrefix = RefNames.refsEditPrefix(userId);
@@ -138,6 +141,7 @@ public class VisibleRefFilter extends AbstractAdvertiseRefsHook {
Map<String, Ref> result = new HashMap<>(); Map<String, Ref> result = new HashMap<>();
List<Ref> deferredTags = new ArrayList<>(); List<Ref> deferredTags = new ArrayList<>();
projectCtl = projectState.controlFor(user.get());
for (Ref ref : refs.values()) { for (Ref ref : refs.values()) {
String name = ref.getName(); String name = ref.getName();
Change.Id changeId; Change.Id changeId;
@@ -336,4 +340,21 @@ public class VisibleRefFilter extends AbstractAdvertiseRefsHook {
return false; return false;
} }
} }
private boolean checkProjectPermission(
PermissionBackend.ForProject forProject, ProjectPermission perm) {
try {
forProject.check(perm);
} catch (AuthException e) {
return false;
} catch (PermissionBackendException e) {
log.error(
String.format(
"Can't check permission for user %s on project %s",
user.get(), projectState.getName()),
e);
return false;
}
return true;
}
} }

View File

@@ -32,6 +32,7 @@ import com.google.gerrit.server.notedb.ReviewerStateInternal;
import com.google.gerrit.server.permissions.PermissionBackend; import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission; import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.project.ContributorAgreementsChecker;
import com.google.gerrit.server.project.ProjectControl; import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.ProjectState; import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.InternalChangeQuery; import com.google.gerrit.server.query.change.InternalChangeQuery;
@@ -45,6 +46,7 @@ import com.google.inject.Singleton;
import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.FactoryModuleBuilder; import com.google.inject.assistedinject.FactoryModuleBuilder;
import com.google.inject.name.Named; import com.google.inject.name.Named;
import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@@ -167,6 +169,7 @@ public class AsyncReceiveCommits implements PreReceiveHook {
private final ExecutorService executor; private final ExecutorService executor;
private final RequestScopePropagator scopePropagator; private final RequestScopePropagator scopePropagator;
private final ReceiveConfig receiveConfig; private final ReceiveConfig receiveConfig;
private final ContributorAgreementsChecker contributorAgreements;
private final long timeoutMillis; private final long timeoutMillis;
private final ProjectControl projectControl; private final ProjectControl projectControl;
private final Repository repo; private final Repository repo;
@@ -185,6 +188,7 @@ public class AsyncReceiveCommits implements PreReceiveHook {
ReceiveConfig receiveConfig, ReceiveConfig receiveConfig,
TransferConfig transferConfig, TransferConfig transferConfig,
Provider<LazyPostReceiveHookChain> lazyPostReceive, Provider<LazyPostReceiveHookChain> lazyPostReceive,
ContributorAgreementsChecker contributorAgreements,
@Named(TIMEOUT_NAME) long timeoutMillis, @Named(TIMEOUT_NAME) long timeoutMillis,
@Assisted ProjectControl projectControl, @Assisted ProjectControl projectControl,
@Assisted Repository repo, @Assisted Repository repo,
@@ -195,6 +199,7 @@ public class AsyncReceiveCommits implements PreReceiveHook {
this.executor = executor; this.executor = executor;
this.scopePropagator = scopePropagator; this.scopePropagator = scopePropagator;
this.receiveConfig = receiveConfig; this.receiveConfig = receiveConfig;
this.contributorAgreements = contributorAgreements;
this.timeoutMillis = timeoutMillis; this.timeoutMillis = timeoutMillis;
this.projectControl = projectControl; this.projectControl = projectControl;
this.repo = repo; this.repo = repo;
@@ -235,15 +240,23 @@ public class AsyncReceiveCommits implements PreReceiveHook {
} }
/** Determine if the user can upload commits. */ /** Determine if the user can upload commits. */
public Capable canUpload() { public Capable canUpload() throws IOException {
Capable result = projectControl.canPushToAtLeastOneRef(); Capable result = projectControl.canPushToAtLeastOneRef();
if (result != Capable.OK) { if (result != Capable.OK) {
return result; return result;
} }
if (receiveConfig.checkMagicRefs) {
result = MagicBranch.checkMagicBranchRefs(repo, projectControl.getProject()); try {
contributorAgreements.check(
projectControl.getProject().getNameKey(), projectControl.getUser());
} catch (AuthException e) {
return new Capable(e.getMessage());
} }
return result;
if (receiveConfig.checkMagicRefs) {
return MagicBranch.checkMagicBranchRefs(repo, projectControl.getProject());
}
return Capable.OK;
} }
@Override @Override

View File

@@ -34,6 +34,14 @@ public enum ProjectPermission {
*/ */
READ(Permission.READ), READ(Permission.READ),
/**
* Can read all non-config references in the repository.
*
* <p>This is the same as {@code READ} but does not check if they user can see refs/meta/config.
* Therefore, callers should check {@code READ} before excluding config refs in a short-circuit.
*/
READ_NO_CONFIG,
/** /**
* Can create at least one reference in the project. * Can create at least one reference in the project.
* *
@@ -62,7 +70,13 @@ public enum ProjectPermission {
* .check(RefPermission.CREATE_CHANGE); * .check(RefPermission.CREATE_CHANGE);
* </pre> * </pre>
*/ */
CREATE_CHANGE; CREATE_CHANGE,
/** Can run receive pack. */
RUN_RECEIVE_PACK,
/** Can run upload pack. */
RUN_UPLOAD_PACK;
private final String name; private final String name;

View File

@@ -0,0 +1,108 @@
// Copyright (C) 2017 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 com.google.gerrit.common.Nullable;
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.common.data.ContributorAgreement;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.common.data.PermissionRule.Action;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.AccountGroup.UUID;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.project.ProjectControl.Metrics;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Singleton
public class ContributorAgreementsChecker {
private final String canonicalWebUrl;
private final ProjectCache projectCache;
private final Metrics metrics;
@Inject
ContributorAgreementsChecker(
@CanonicalWebUrl @Nullable String canonicalWebUrl,
ProjectCache projectCache,
Metrics metrics) {
this.canonicalWebUrl = canonicalWebUrl;
this.projectCache = projectCache;
this.metrics = metrics;
}
/**
* Checks if the user has signed a contributor agreement for the project.
*
* @throws AuthException if the user has not signed a contributor agreement for the project
* @throws IOException if project states could not be loaded
*/
public void check(Project.NameKey project, CurrentUser user) throws IOException, AuthException {
metrics.claCheckCount.increment();
ProjectState projectState = projectCache.checkedGet(project);
if (projectState == null) {
throw new IOException("Can't load All-Projects");
}
if (!projectState.isUseContributorAgreements()) {
return;
}
if (!user.isIdentifiedUser()) {
throw new AuthException("Must be logged in to verify Contributor Agreement");
}
IdentifiedUser iUser = user.asIdentifiedUser();
Collection<ContributorAgreement> contributorAgreements =
projectCache.getAllProjects().getConfig().getContributorAgreements();
List<UUID> okGroupIds = new ArrayList<>();
for (ContributorAgreement ca : contributorAgreements) {
List<AccountGroup.UUID> groupIds;
groupIds = okGroupIds;
for (PermissionRule rule : ca.getAccepted()) {
if ((rule.getAction() == Action.ALLOW)
&& (rule.getGroup() != null)
&& (rule.getGroup().getUUID() != null)) {
groupIds.add(new AccountGroup.UUID(rule.getGroup().getUUID().get()));
}
}
}
if (!iUser.getEffectiveGroups().containsAnyOf(okGroupIds)) {
final StringBuilder msg = new StringBuilder();
msg.append("A Contributor Agreement must be completed before uploading");
if (canonicalWebUrl != null) {
msg.append(":\n\n ");
msg.append(canonicalWebUrl);
msg.append("#");
msg.append(PageLinks.SETTINGS_AGREEMENTS);
msg.append("\n");
} else {
msg.append(".");
}
throw new AuthException(msg.toString());
}
}
}

View File

@@ -16,16 +16,13 @@ package com.google.gerrit.server.project;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.common.data.AccessSection; import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.Capable; import com.google.gerrit.common.data.Capable;
import com.google.gerrit.common.data.ContributorAgreement;
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.common.data.PermissionRule.Action;
import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.metrics.Counter0; import com.google.gerrit.metrics.Counter0;
import com.google.gerrit.metrics.Description; import com.google.gerrit.metrics.Description;
@@ -34,11 +31,10 @@ import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Branch; import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.GroupMembership; import com.google.gerrit.server.account.GroupMembership;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.GitReceivePackGroups; import com.google.gerrit.server.config.GitReceivePackGroups;
import com.google.gerrit.server.config.GitUploadPackGroups; import com.google.gerrit.server.config.GitUploadPackGroups;
import com.google.gerrit.server.group.SystemGroupBackend; import com.google.gerrit.server.group.SystemGroupBackend;
@@ -58,7 +54,6 @@ import com.google.inject.Provider;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.Assisted;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.EnumSet; import java.util.EnumSet;
@@ -129,15 +124,12 @@ public class ProjectControl {
private final Set<AccountGroup.UUID> uploadGroups; private final Set<AccountGroup.UUID> uploadGroups;
private final Set<AccountGroup.UUID> receiveGroups; private final Set<AccountGroup.UUID> receiveGroups;
private final String canonicalWebUrl;
private final PermissionBackend.WithUser perm; private final PermissionBackend.WithUser perm;
private final CurrentUser user; private final CurrentUser user;
private final ProjectState state; private final ProjectState state;
private final CommitsCollection commits; private final CommitsCollection commits;
private final ChangeControl.Factory changeControlFactory; private final ChangeControl.Factory changeControlFactory;
private final PermissionCollection.Factory permissionFilter; private final PermissionCollection.Factory permissionFilter;
private final Collection<ContributorAgreement> contributorAgreements;
private final Metrics metrics;
private List<SectionMatcher> allSections; private List<SectionMatcher> allSections;
private Map<String, RefControl> refControls; private Map<String, RefControl> refControls;
@@ -151,19 +143,14 @@ public class ProjectControl {
PermissionCollection.Factory permissionFilter, PermissionCollection.Factory permissionFilter,
CommitsCollection commits, CommitsCollection commits,
ChangeControl.Factory changeControlFactory, ChangeControl.Factory changeControlFactory,
@CanonicalWebUrl @Nullable String canonicalWebUrl,
PermissionBackend permissionBackend, PermissionBackend permissionBackend,
@Assisted CurrentUser who, @Assisted CurrentUser who,
@Assisted ProjectState ps, @Assisted ProjectState ps) {
Metrics metrics) {
this.changeControlFactory = changeControlFactory; this.changeControlFactory = changeControlFactory;
this.uploadGroups = uploadGroups; this.uploadGroups = uploadGroups;
this.receiveGroups = receiveGroups; this.receiveGroups = receiveGroups;
this.permissionFilter = permissionFilter; this.permissionFilter = permissionFilter;
this.commits = commits; this.commits = commits;
this.contributorAgreements = pc.getAllProjects().getConfig().getContributorAgreements();
this.canonicalWebUrl = canonicalWebUrl;
this.metrics = metrics;
this.perm = permissionBackend.user(who); this.perm = permissionBackend.user(who);
user = who; user = who;
state = ps; state = ps;
@@ -214,31 +201,31 @@ public class ProjectControl {
return state.getProject(); return state.getProject();
} }
public boolean allRefsAreVisible(Set<String> ignore) {
// TODO(hiesel) Hide refs/changes and replace this method by a proper READ check of all refs
return user.isInternalUser() || canPerformOnAllRefs(Permission.READ, ignore);
}
/** Is this user a project owner? */ /** Is this user a project owner? */
public boolean isOwner() { public boolean isOwner() {
return (isDeclaredOwner() && !controlForRef("refs/*").isBlocked(Permission.OWNER)) || isAdmin(); return (isDeclaredOwner() && !controlForRef("refs/*").isBlocked(Permission.OWNER)) || isAdmin();
} }
/** @return {@code Capable.OK} if the user can upload to at least one reference */ /**
* @return {@code Capable.OK} if the user can upload to at least one reference. Does not check
* Contributor Agreements.
*/
public Capable canPushToAtLeastOneRef() { public Capable canPushToAtLeastOneRef() {
if (!canPerformOnAnyRef(Permission.PUSH) if (!canPerformOnAnyRef(Permission.PUSH)
&& !canPerformOnAnyRef(Permission.CREATE_TAG) && !canPerformOnAnyRef(Permission.CREATE_TAG)
&& !isOwner()) { && !isOwner()) {
return new Capable("Upload denied for project '" + state.getName() + "'"); return new Capable("Upload denied for project '" + state.getName() + "'");
} }
if (state.isUseContributorAgreements()) {
return verifyActiveContributorAgreement();
}
return Capable.OK; return Capable.OK;
} }
/** Does this user have ownership on at least one reference name? */
public boolean isOwnerAnyRef() {
return canPerformOnAnyRef(Permission.OWNER) || isAdmin();
}
/** Can the user run upload pack? */ /** Can the user run upload pack? */
public boolean canRunUploadPack() { private boolean canRunUploadPack() {
for (AccountGroup.UUID group : uploadGroups) { for (AccountGroup.UUID group : uploadGroups) {
if (match(group)) { if (match(group)) {
return true; return true;
@@ -248,7 +235,7 @@ public class ProjectControl {
} }
/** Can the user run receive pack? */ /** Can the user run receive pack? */
public boolean canRunReceivePack() { private boolean canRunReceivePack() {
for (AccountGroup.UUID group : receiveGroups) { for (AccountGroup.UUID group : receiveGroups) {
if (match(group)) { if (match(group)) {
return true; return true;
@@ -257,6 +244,10 @@ public class ProjectControl {
return false; return false;
} }
private boolean allRefsAreVisible(Set<String> ignore) {
return user.isInternalUser() || canPerformOnAllRefs(Permission.READ, ignore);
}
/** Returns whether the project is hidden. */ /** Returns whether the project is hidden. */
private boolean isHidden() { private boolean isHidden() {
return getProject().getState().equals(com.google.gerrit.extensions.client.ProjectState.HIDDEN); return getProject().getState().equals(com.google.gerrit.extensions.client.ProjectState.HIDDEN);
@@ -296,46 +287,6 @@ public class ProjectControl {
return declaredOwner; return declaredOwner;
} }
private Capable verifyActiveContributorAgreement() {
metrics.claCheckCount.increment();
if (!(user.isIdentifiedUser())) {
return new Capable("Must be logged in to verify Contributor Agreement");
}
final IdentifiedUser iUser = user.asIdentifiedUser();
List<AccountGroup.UUID> okGroupIds = new ArrayList<>();
for (ContributorAgreement ca : contributorAgreements) {
List<AccountGroup.UUID> groupIds;
groupIds = okGroupIds;
for (PermissionRule rule : ca.getAccepted()) {
if ((rule.getAction() == Action.ALLOW)
&& (rule.getGroup() != null)
&& (rule.getGroup().getUUID() != null)) {
groupIds.add(new AccountGroup.UUID(rule.getGroup().getUUID().get()));
}
}
}
if (iUser.getEffectiveGroups().containsAnyOf(okGroupIds)) {
return Capable.OK;
}
final StringBuilder msg = new StringBuilder();
msg.append("A Contributor Agreement must be completed before uploading");
if (canonicalWebUrl != null) {
msg.append(":\n\n ");
msg.append(canonicalWebUrl);
msg.append("#");
msg.append(PageLinks.SETTINGS_AGREEMENTS);
msg.append("\n");
} else {
msg.append(".");
}
msg.append("\n");
return new Capable(msg.toString());
}
private boolean canPerformOnAnyRef(String permissionName) { private boolean canPerformOnAnyRef(String permissionName) {
for (SectionMatcher matcher : access()) { for (SectionMatcher matcher : access()) {
AccessSection section = matcher.section; AccessSection section = matcher.section;
@@ -513,10 +464,18 @@ public class ProjectControl {
case READ: case READ:
return !isHidden() && allRefsAreVisible(Collections.emptySet()); return !isHidden() && allRefsAreVisible(Collections.emptySet());
case READ_NO_CONFIG:
return !isHidden() && allRefsAreVisible(ImmutableSet.of(RefNames.REFS_CONFIG));
case CREATE_REF: case CREATE_REF:
return canAddRefs(); return canAddRefs();
case CREATE_CHANGE: case CREATE_CHANGE:
return canCreateChanges(); return canCreateChanges();
case RUN_RECEIVE_PACK:
return canRunReceivePack();
case RUN_UPLOAD_PACK:
return canRunUploadPack();
} }
throw new PermissionBackendException(perm + " unsupported"); throw new PermissionBackendException(perm + " unsupported");
} }

View File

@@ -207,7 +207,6 @@ public class RefControlTest {
@Inject private SingleVersionListener singleVersionListener; @Inject private SingleVersionListener singleVersionListener;
@Inject private InMemoryDatabase schemaFactory; @Inject private InMemoryDatabase schemaFactory;
@Inject private ThreadLocalRequestContext requestContext; @Inject private ThreadLocalRequestContext requestContext;
@Inject private ProjectControl.Metrics metrics;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
@@ -875,11 +874,9 @@ public class RefControlTest {
sectionSorter, sectionSorter,
null, // commitsCollection null, // commitsCollection
changeControlFactory, changeControlFactory,
"http://localhost", // canonicalWebUrl
permissionBackend, permissionBackend,
new MockUser(name, memberOf), new MockUser(name, memberOf),
newProjectState(local), newProjectState(local));
metrics);
} }
private ProjectState newProjectState(ProjectConfig local) { private ProjectState newProjectState(ProjectConfig local) {

View File

@@ -17,11 +17,15 @@ package com.google.gerrit.sshd.commands;
import com.google.common.collect.MultimapBuilder; import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.SetMultimap; import com.google.common.collect.SetMultimap;
import com.google.gerrit.common.data.Capable; import com.google.gerrit.common.data.Capable;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.git.VisibleRefFilter; import com.google.gerrit.server.git.VisibleRefFilter;
import com.google.gerrit.server.git.receive.AsyncReceiveCommits; import com.google.gerrit.server.git.receive.AsyncReceiveCommits;
import com.google.gerrit.server.notedb.ReviewerStateInternal; import com.google.gerrit.server.notedb.ReviewerStateInternal;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.sshd.AbstractGitCommand; import com.google.gerrit.sshd.AbstractGitCommand;
import com.google.gerrit.sshd.CommandMetaData; import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshSession; import com.google.gerrit.sshd.SshSession;
@@ -51,6 +55,7 @@ final class Receive extends AbstractGitCommand {
@Inject private AsyncReceiveCommits.Factory factory; @Inject private AsyncReceiveCommits.Factory factory;
@Inject private IdentifiedUser currentUser; @Inject private IdentifiedUser currentUser;
@Inject private SshSession session; @Inject private SshSession session;
@Inject private PermissionBackend permissionBackend;
private final SetMultimap<ReviewerStateInternal, Account.Id> reviewers = private final SetMultimap<ReviewerStateInternal, Account.Id> reviewers =
MultimapBuilder.hashKeys(2).hashSetValues().build(); MultimapBuilder.hashKeys(2).hashSetValues().build();
@@ -77,8 +82,15 @@ final class Receive extends AbstractGitCommand {
@Override @Override
protected void runImpl() throws IOException, Failure { protected void runImpl() throws IOException, Failure {
if (!projectControl.canRunReceivePack()) { try {
permissionBackend
.user(currentUser)
.project(project.getNameKey())
.check(ProjectPermission.RUN_RECEIVE_PACK);
} catch (AuthException e) {
throw new Failure(1, "fatal: receive-pack not permitted on this server"); throw new Failure(1, "fatal: receive-pack not permitted on this server");
} catch (PermissionBackendException e) {
throw new Failure(1, "fatal: unable to check permissions " + e);
} }
AsyncReceiveCommits arc = factory.create(projectControl, repo, null, reviewers); AsyncReceiveCommits arc = factory.create(projectControl, repo, null, reviewers);

View File

@@ -16,10 +16,14 @@ package com.google.gerrit.sshd.commands;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.gerrit.extensions.registration.DynamicSet; import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.server.git.TransferConfig; import com.google.gerrit.server.git.TransferConfig;
import com.google.gerrit.server.git.VisibleRefFilter; import com.google.gerrit.server.git.VisibleRefFilter;
import com.google.gerrit.server.git.validators.UploadValidationException; import com.google.gerrit.server.git.validators.UploadValidationException;
import com.google.gerrit.server.git.validators.UploadValidators; import com.google.gerrit.server.git.validators.UploadValidators;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.sshd.AbstractGitCommand; import com.google.gerrit.sshd.AbstractGitCommand;
import com.google.gerrit.sshd.SshSession; import com.google.gerrit.sshd.SshSession;
import com.google.inject.Inject; import com.google.inject.Inject;
@@ -39,11 +43,19 @@ final class Upload extends AbstractGitCommand {
@Inject private DynamicSet<PostUploadHook> postUploadHooks; @Inject private DynamicSet<PostUploadHook> postUploadHooks;
@Inject private UploadValidators.Factory uploadValidatorsFactory; @Inject private UploadValidators.Factory uploadValidatorsFactory;
@Inject private SshSession session; @Inject private SshSession session;
@Inject private PermissionBackend permissionBackend;
@Override @Override
protected void runImpl() throws IOException, Failure { protected void runImpl() throws IOException, Failure {
if (!projectControl.canRunUploadPack()) { try {
permissionBackend
.user(projectControl.getUser())
.project(projectControl.getProject().getNameKey())
.check(ProjectPermission.RUN_UPLOAD_PACK);
} catch (AuthException e) {
throw new Failure(1, "fatal: upload-pack not permitted on this server"); throw new Failure(1, "fatal: upload-pack not permitted on this server");
} catch (PermissionBackendException e) {
throw new Failure(1, "fatal: unable to check permissions " + e);
} }
final UploadPack up = new UploadPack(repo); final UploadPack up = new UploadPack(repo);