Merge "Only limit width of prose text in message list"
diff --git a/Documentation/cmd-index.txt b/Documentation/cmd-index.txt
index 62f7fd2..eed2eb4 100644
--- a/Documentation/cmd-index.txt
+++ b/Documentation/cmd-index.txt
@@ -82,6 +82,9 @@
link:cmd-set-head.html[gerrit set-head]::
Change the HEAD reference of a project.
+link:cmd-set-project.html[gerrit set-project]::
+ Change a project's settings.
+
link:cmd-set-reviewers.html[gerrit set-reviewers]::
Add or remove reviewers on a change.
@@ -172,9 +175,6 @@
link:cmd-set-members.html[gerrit set-members]::
Set group members.
-link:cmd-set-project.html[gerrit set-project]::
- Change a project's settings.
-
link:cmd-set-project-parent.html[gerrit set-project-parent]::
Change the project permissions are inherited from.
diff --git a/Documentation/dev-contributing.txt b/Documentation/dev-contributing.txt
index 823424e..405b1d7 100644
--- a/Documentation/dev-contributing.txt
+++ b/Documentation/dev-contributing.txt
@@ -154,8 +154,8 @@
To format Java source code, Gerrit uses the
link:https://github.com/google/google-java-format[`google-java-format`]
tool (version 1.3), and to format Bazel BUILD and WORKSPACE files the
-link:https://github.com/bazelbuild/buildifier[`buildifier`] tool. These
-tools automatically apply format according to the style guides; this
+link:https://github.com/bazelbuild/buildifier[`buildifier`] tool (version 0.4.5).
+These tools automatically apply format according to the style guides; this
streamlines code review by reducing the need for time-consuming, tedious,
and contentious discussions about trivial issues like whitespace.
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index ade1287..42b1014 100644
--- a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -494,12 +494,22 @@
protected TestRepository<InMemoryRepository> cloneProject(
Project.NameKey p, TestAccount testAccount) throws Exception {
+ return GitUtil.cloneProject(p, registerRepoConnection(p, testAccount));
+ }
+
+ /**
+ * Register a repository connection over the test protocol.
+ *
+ * @return a URI string that can be used to connect to this repository for both fetch and push.
+ */
+ protected String registerRepoConnection(Project.NameKey p, TestAccount testAccount)
+ throws Exception {
InProcessProtocol.Context ctx =
new InProcessProtocol.Context(
reviewDbProvider, identifiedUserFactory, testAccount.getId(), p);
Repository repo = repoManager.openRepository(p);
toClose.add(repo);
- return GitUtil.cloneProject(p, inProcessProtocol.register(ctx, repo).toString());
+ return inProcessProtocol.register(ctx, repo).toString();
}
protected void afterTest() throws Exception {
diff --git a/gerrit-acceptance-tests/BUILD b/gerrit-acceptance-tests/BUILD
index 9fb5e67..c593783 100644
--- a/gerrit-acceptance-tests/BUILD
+++ b/gerrit-acceptance-tests/BUILD
@@ -3,8 +3,8 @@
java_library(
name = "lib",
testonly = 1,
- resources = RESOURCES,
srcs = ["src/test/java/com/google/gerrit/acceptance/Dummy.java"],
+ resources = RESOURCES,
visibility = ["//visibility:public"],
exports = [
"//gerrit-acceptance-framework:lib",
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index 2e98e0c..ad8140e 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -17,6 +17,7 @@
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assert_;
+import static com.google.gerrit.acceptance.GitUtil.deleteRef;
import static com.google.gerrit.acceptance.GitUtil.fetch;
import static com.google.gerrit.gpg.PublicKeyStore.REFS_GPG_KEYS;
import static com.google.gerrit.gpg.PublicKeyStore.keyToString;
@@ -44,6 +45,7 @@
import com.google.gerrit.acceptance.Sandboxed;
import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.UseSsh;
+import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.accounts.EmailInput;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
@@ -99,6 +101,8 @@
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.PushCertificateIdent;
+import org.eclipse.jgit.transport.PushResult;
+import org.eclipse.jgit.transport.RemoteRefUpdate;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -683,6 +687,50 @@
}
@Test
+ @Sandboxed
+ public void cannotDeleteUserBranch() throws Exception {
+ grant(
+ Permission.DELETE,
+ allUsers,
+ RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}",
+ true,
+ REGISTERED_USERS);
+
+ TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
+ String userRef = RefNames.refsUsers(admin.id);
+ PushResult r = deleteRef(allUsersRepo, userRef);
+ RemoteRefUpdate refUpdate = r.getRemoteUpdate(userRef);
+ assertThat(refUpdate.getStatus()).isEqualTo(RemoteRefUpdate.Status.REJECTED_OTHER_REASON);
+ assertThat(refUpdate.getMessage()).contains("Not allowed to delete user branch.");
+
+ try (Repository repo = repoManager.openRepository(allUsers)) {
+ assertThat(repo.exactRef(userRef)).isNotNull();
+ }
+ }
+
+ @Test
+ @Sandboxed
+ public void deleteUserBranchWithAccessDatabaseCapability() throws Exception {
+ allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ grant(
+ Permission.DELETE,
+ allUsers,
+ RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}",
+ true,
+ REGISTERED_USERS);
+
+ TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
+ String userRef = RefNames.refsUsers(admin.id);
+ PushResult r = deleteRef(allUsersRepo, userRef);
+ RemoteRefUpdate refUpdate = r.getRemoteUpdate(userRef);
+ assertThat(refUpdate.getStatus()).isEqualTo(RemoteRefUpdate.Status.OK);
+
+ try (Repository repo = repoManager.openRepository(allUsers)) {
+ assertThat(repo.exactRef(userRef)).isNull();
+ }
+ }
+
+ @Test
public void addGpgKey() throws Exception {
TestKey key = validKeyWithoutExpiration();
String id = key.getKeyIdString();
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java
index 82eae1b..b221ec5 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java
@@ -314,7 +314,7 @@
in.label("Code-Review", 1);
exception.expect(UnprocessableEntityException.class);
- exception.expectMessage("on_behalf_of account " + user.id + " cannot see destination ref");
+ exception.expectMessage("on_behalf_of account " + user.id + " cannot see change");
revision.review(in);
}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GlobalCapability.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GlobalCapability.java
index 4c9b64a..6fd0e77 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GlobalCapability.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GlobalCapability.java
@@ -15,6 +15,7 @@
package com.google.gerrit.common.data;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -115,6 +116,9 @@
private static final List<String> NAMES_ALL;
private static final List<String> NAMES_LC;
+ private static final String[] RANGE_NAMES = {
+ QUERY_LIMIT, BATCH_CHANGES_LIMIT,
+ };
static {
NAMES_ALL = new ArrayList<>();
@@ -158,7 +162,16 @@
/** @return true if the capability should have a range attached. */
public static boolean hasRange(String varName) {
- return QUERY_LIMIT.equalsIgnoreCase(varName) || BATCH_CHANGES_LIMIT.equalsIgnoreCase(varName);
+ for (String n : RANGE_NAMES) {
+ if (n.equalsIgnoreCase(varName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static List<String> getRangeNames() {
+ return Collections.unmodifiableList(Arrays.asList(RANGE_NAMES));
}
/** @return the valid range for the capability if it has one, otherwise null. */
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RunAsFilter.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RunAsFilter.java
index 4862a70..47850c4 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RunAsFilter.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RunAsFilter.java
@@ -19,11 +19,15 @@
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import com.google.gerrit.extensions.registration.DynamicItem;
+import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountResolver;
import com.google.gerrit.server.config.AuthConfig;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -57,6 +61,7 @@
private final Provider<ReviewDb> db;
private final boolean enabled;
private final DynamicItem<WebSession> session;
+ private final PermissionBackend permissionBackend;
private final AccountResolver accountResolver;
@Inject
@@ -64,10 +69,12 @@
Provider<ReviewDb> db,
AuthConfig config,
DynamicItem<WebSession> session,
+ PermissionBackend permissionBackend,
AccountResolver accountResolver) {
this.db = db;
this.enabled = config.isRunAsEnabled();
this.session = session;
+ this.permissionBackend = permissionBackend;
this.accountResolver = accountResolver;
}
@@ -85,12 +92,20 @@
}
CurrentUser self = session.get().getUser();
- if (!self.getCapabilities().canRunAs()
+ try {
+ if (!self.isIdentifiedUser()) {
// Always disallow for anonymous users, even if permitted by the ACL,
// because that would be crazy.
- || !self.isIdentifiedUser()) {
+ throw new AuthException("denied");
+ }
+ permissionBackend.user(self).check(GlobalPermission.RUN_AS);
+ } catch (AuthException e) {
replyError(req, res, SC_FORBIDDEN, "not permitted to use " + RUN_AS, null);
return;
+ } catch (PermissionBackendException e) {
+ log.warn("cannot check runAs", e);
+ replyError(req, res, SC_INTERNAL_SERVER_ERROR, RUN_AS + " unavailable", null);
+ return;
}
Account target;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
index 1f7848d..8ba0978 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
@@ -38,6 +38,7 @@
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.group.SystemGroupBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.RefControl;
@@ -174,7 +175,11 @@
AddReviewerInput input = new AddReviewerInput();
input.reviewer = projectOwners;
reviewersProvider.get().apply(rsrc, input);
- } catch (IOException | OrmException | RestApiException | UpdateException e) {
+ } catch (IOException
+ | OrmException
+ | RestApiException
+ | UpdateException
+ | PermissionBackendException e) {
// one of the owner groups is not visible to the user and this it why it
// can't be added as reviewer
}
@@ -193,7 +198,11 @@
AddReviewerInput input = new AddReviewerInput();
input.reviewer = r.getGroup().getUUID().get();
reviewersProvider.get().apply(rsrc, input);
- } catch (IOException | OrmException | RestApiException | UpdateException e) {
+ } catch (IOException
+ | OrmException
+ | RestApiException
+ | UpdateException
+ | PermissionBackendException e) {
// ignore
}
}
diff --git a/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/index.html.soy b/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/index.html.soy
index 7b828b6..4d8c43b 100644
--- a/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/index.html.soy
+++ b/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/index.html.soy
@@ -27,6 +27,8 @@
<meta name="description" content="Gerrit Code Review">{\n}
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">{\n}
+ <script>window.CANONICAL_PATH = '{$canonicalPath}';</script>{\n}
+
// SourceCodePro fonts are used in styles/fonts.css
// @see https://github.com/w3c/preload/issues/32 regarding crossorigin
<link rel="preload" href="{$staticResourcePath}/fonts/SourceCodePro-Regular.woff2" as="font" type="font/woff2" crossorigin>{\n}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/metrics/dropwizard/GetMetric.java b/gerrit-server/src/main/java/com/google/gerrit/metrics/dropwizard/GetMetric.java
index 52e35c3..f0ae97e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/metrics/dropwizard/GetMetric.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/metrics/dropwizard/GetMetric.java
@@ -17,10 +17,14 @@
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.inject.Inject;
import org.kohsuke.args4j.Option;
class GetMetric implements RestReadView<MetricResource> {
+ private final PermissionBackend permissionBackend;
private final CurrentUser user;
private final DropWizardMetricMaker metrics;
@@ -28,16 +32,16 @@
boolean dataOnly;
@Inject
- GetMetric(CurrentUser user, DropWizardMetricMaker metrics) {
+ GetMetric(PermissionBackend permissionBackend, CurrentUser user, DropWizardMetricMaker metrics) {
+ this.permissionBackend = permissionBackend;
this.user = user;
this.metrics = metrics;
}
@Override
- public MetricJson apply(MetricResource resource) throws AuthException {
- if (!user.getCapabilities().canViewCaches()) {
- throw new AuthException("restricted to viewCaches");
- }
+ public MetricJson apply(MetricResource resource)
+ throws AuthException, PermissionBackendException {
+ permissionBackend.user(user).check(GlobalPermission.VIEW_CACHES);
return new MetricJson(
resource.getMetric(), metrics.getAnnotations(resource.getName()), dataOnly);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/metrics/dropwizard/ListMetrics.java b/gerrit-server/src/main/java/com/google/gerrit/metrics/dropwizard/ListMetrics.java
index 891f4ac..59f6b97 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/metrics/dropwizard/ListMetrics.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/metrics/dropwizard/ListMetrics.java
@@ -19,6 +19,9 @@
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.ConfigResource;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.inject.Inject;
import java.util.ArrayList;
import java.util.List;
@@ -28,6 +31,7 @@
import org.kohsuke.args4j.Option;
class ListMetrics implements RestReadView<ConfigResource> {
+ private final PermissionBackend permissionBackend;
private final CurrentUser user;
private final DropWizardMetricMaker metrics;
@@ -43,16 +47,17 @@
List<String> query = new ArrayList<>();
@Inject
- ListMetrics(CurrentUser user, DropWizardMetricMaker metrics) {
+ ListMetrics(
+ PermissionBackend permissionBackend, CurrentUser user, DropWizardMetricMaker metrics) {
+ this.permissionBackend = permissionBackend;
this.user = user;
this.metrics = metrics;
}
@Override
- public Map<String, MetricJson> apply(ConfigResource resource) throws AuthException {
- if (!user.getCapabilities().canViewCaches()) {
- throw new AuthException("restricted to viewCaches");
- }
+ public Map<String, MetricJson> apply(ConfigResource resource)
+ throws AuthException, PermissionBackendException {
+ permissionBackend.user(user).check(GlobalPermission.VIEW_CACHES);
SortedMap<String, MetricJson> out = new TreeMap<>();
List<String> prefixes = new ArrayList<>(query.size());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/metrics/dropwizard/MetricsCollection.java b/gerrit-server/src/main/java/com/google/gerrit/metrics/dropwizard/MetricsCollection.java
index 2686f1f..6abf17c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/metrics/dropwizard/MetricsCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/metrics/dropwizard/MetricsCollection.java
@@ -23,6 +23,9 @@
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.ConfigResource;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -31,6 +34,7 @@
class MetricsCollection implements ChildCollection<ConfigResource, MetricResource> {
private final DynamicMap<RestView<MetricResource>> views;
private final Provider<ListMetrics> list;
+ private final PermissionBackend permissionBackend;
private final Provider<CurrentUser> user;
private final DropWizardMetricMaker metrics;
@@ -38,10 +42,12 @@
MetricsCollection(
DynamicMap<RestView<MetricResource>> views,
Provider<ListMetrics> list,
+ PermissionBackend permissionBackend,
Provider<CurrentUser> user,
DropWizardMetricMaker metrics) {
this.views = views;
this.list = list;
+ this.permissionBackend = permissionBackend;
this.user = user;
this.metrics = metrics;
}
@@ -58,10 +64,8 @@
@Override
public MetricResource parse(ConfigResource parent, IdString id)
- throws ResourceNotFoundException, AuthException {
- if (!user.get().getCapabilities().canViewCaches()) {
- throw new AuthException("restricted to viewCaches");
- }
+ throws ResourceNotFoundException, AuthException, PermissionBackendException {
+ permissionBackend.user(user).check(GlobalPermission.VIEW_CACHES);
Metric metric = metrics.getMetric(id.get());
if (metric == null) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/rules/PrologEnvironment.java b/gerrit-server/src/main/java/com/google/gerrit/rules/PrologEnvironment.java
index 9538121a4..23c59f5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/rules/PrologEnvironment.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/rules/PrologEnvironment.java
@@ -20,6 +20,7 @@
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
+import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.project.ProjectCache;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -166,6 +167,7 @@
}
private final ProjectCache projectCache;
+ private final PermissionBackend permissionBackend;
private final GitRepositoryManager repositoryManager;
private final PatchListCache patchListCache;
private final PatchSetInfoFactory patchSetInfoFactory;
@@ -177,6 +179,7 @@
@Inject
Args(
ProjectCache projectCache,
+ PermissionBackend permissionBackend,
GitRepositoryManager repositoryManager,
PatchListCache patchListCache,
PatchSetInfoFactory patchSetInfoFactory,
@@ -184,6 +187,7 @@
Provider<AnonymousUser> anonymousUser,
@GerritServerConfig Config config) {
this.projectCache = projectCache;
+ this.permissionBackend = permissionBackend;
this.repositoryManager = repositoryManager;
this.patchListCache = patchListCache;
this.patchSetInfoFactory = patchSetInfoFactory;
@@ -213,6 +217,10 @@
return projectCache;
}
+ public PermissionBackend getPermissionBackend() {
+ return permissionBackend;
+ }
+
public GitRepositoryManager getGitRepositoryManager() {
return repositoryManager;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java b/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java
index 98ec569..e35171b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java
@@ -33,6 +33,7 @@
import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
+import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gwtorm.server.OrmException;
@@ -130,6 +131,15 @@
}
};
+ public static final StoredValue<PermissionBackend> PERMISSION_BACKEND =
+ new StoredValue<PermissionBackend>() {
+ @Override
+ protected PermissionBackend createValue(Prolog engine) {
+ PrologEnvironment env = (PrologEnvironment) engine.control;
+ return env.getArgs().getPermissionBackend();
+ }
+ };
+
public static final StoredValue<AnonymousUser> ANONYMOUS_USER =
new StoredValue<AnonymousUser>() {
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/CapabilityControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/CapabilityControl.java
index 12dee31..f678379 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/CapabilityControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/CapabilityControl.java
@@ -26,6 +26,8 @@
import com.google.gerrit.server.PeerDaemonUser;
import com.google.gerrit.server.git.QueueProvider;
import com.google.gerrit.server.group.SystemGroupBackend;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -75,21 +77,6 @@
return canAdministrateServer;
}
- /** @return true if the user can create an account for another user. */
- public boolean canCreateAccount() {
- return canPerform(GlobalCapability.CREATE_ACCOUNT) || canAdministrateServer();
- }
-
- /** @return true if the user can create a group. */
- public boolean canCreateGroup() {
- return canPerform(GlobalCapability.CREATE_GROUP) || canAdministrateServer();
- }
-
- /** @return true if the user can create a project. */
- public boolean canCreateProject() {
- return canPerform(GlobalCapability.CREATE_PROJECT) || canAdministrateServer();
- }
-
/** @return true if the user can email reviewers. */
public boolean canEmailReviewers() {
if (canEmailReviewers == null) {
@@ -100,11 +87,6 @@
return canEmailReviewers;
}
- /** @return true if the user can kill any running task. */
- public boolean canKillTask() {
- return canPerform(GlobalCapability.KILL_TASK) || canMaintainServer();
- }
-
/** @return true if the user can modify an account for another user. */
public boolean canModifyAccount() {
return canPerform(GlobalCapability.MODIFY_ACCOUNT) || canAdministrateServer();
@@ -115,31 +97,11 @@
return canPerform(GlobalCapability.VIEW_ALL_ACCOUNTS) || canAdministrateServer();
}
- /** @return true if the user can view the server caches. */
- public boolean canViewCaches() {
- return canPerform(GlobalCapability.VIEW_CACHES) || canMaintainServer();
- }
-
- /** @return true if the user can flush the server's caches. */
- public boolean canFlushCaches() {
- return canPerform(GlobalCapability.FLUSH_CACHES) || canMaintainServer();
- }
-
/** @return true if the user can perform basic server maintenance. */
public boolean canMaintainServer() {
return canPerform(GlobalCapability.MAINTAIN_SERVER) || canAdministrateServer();
}
- /** @return true if the user can view open connections. */
- public boolean canViewConnections() {
- return canPerform(GlobalCapability.VIEW_CONNECTIONS) || canAdministrateServer();
- }
-
- /** @return true if the user can view the installed plugins. */
- public boolean canViewPlugins() {
- return canPerform(GlobalCapability.VIEW_PLUGINS) || canAdministrateServer();
- }
-
/** @return true if the user can view the entire queue. */
public boolean canViewQueue() {
return canPerform(GlobalCapability.VIEW_QUEUE) || canMaintainServer();
@@ -147,22 +109,11 @@
/** @return true if the user can access the database (with gsql). */
public boolean canAccessDatabase() {
- return canPerform(GlobalCapability.ACCESS_DATABASE);
- }
-
- /** @return true if the user can stream Gerrit events. */
- public boolean canStreamEvents() {
- return canPerform(GlobalCapability.STREAM_EVENTS) || canAdministrateServer();
- }
-
- /** @return true if the user can run the Git garbage collection. */
- public boolean canRunGC() {
- return canPerform(GlobalCapability.RUN_GC) || canMaintainServer();
- }
-
- /** @return true if the user can impersonate another user. */
- public boolean canRunAs() {
- return canPerform(GlobalCapability.RUN_AS);
+ try {
+ return doCanForDefaultPermissionBackend(GlobalPermission.ACCESS_DATABASE);
+ } catch (PermissionBackendException e) {
+ return false;
+ }
}
/** @return which priority queue the user's tasks should be submitted to. */
@@ -278,4 +229,42 @@
private static boolean match(GroupMembership groups, PermissionRule rule) {
return groups.contains(rule.getGroup().getUUID());
}
+
+ /** Do not use unless inside DefaultPermissionBackend. */
+ public boolean doCanForDefaultPermissionBackend(GlobalPermission perm)
+ throws PermissionBackendException {
+ switch (perm) {
+ case ADMINISTRATE_SERVER:
+ return canAdministrateServer();
+ case EMAIL_REVIEWERS:
+ return canEmailReviewers();
+ case MAINTAIN_SERVER:
+ return canMaintainServer();
+ case MODIFY_ACCOUNT:
+ return canModifyAccount();
+ case VIEW_ALL_ACCOUNTS:
+ return canViewAllAccounts();
+ case VIEW_QUEUE:
+ return canViewQueue();
+
+ case FLUSH_CACHES:
+ case KILL_TASK:
+ case RUN_GC:
+ case VIEW_CACHES:
+ return canPerform(perm.permissionName()) || canMaintainServer();
+
+ case CREATE_ACCOUNT:
+ case CREATE_GROUP:
+ case CREATE_PROJECT:
+ case STREAM_EVENTS:
+ case VIEW_CONNECTIONS:
+ case VIEW_PLUGINS:
+ return canPerform(perm.permissionName()) || canAdministrateServer();
+
+ case ACCESS_DATABASE:
+ case RUN_AS:
+ return canPerform(perm.permissionName());
+ }
+ throw new PermissionBackendException(perm + " unsupported");
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetCapabilities.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetCapabilities.java
index 876807d..1268ef2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetCapabilities.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetCapabilities.java
@@ -14,23 +14,7 @@
package com.google.gerrit.server.account;
-import static com.google.gerrit.common.data.GlobalCapability.ACCESS_DATABASE;
-import static com.google.gerrit.common.data.GlobalCapability.CREATE_ACCOUNT;
-import static com.google.gerrit.common.data.GlobalCapability.CREATE_GROUP;
-import static com.google.gerrit.common.data.GlobalCapability.CREATE_PROJECT;
-import static com.google.gerrit.common.data.GlobalCapability.EMAIL_REVIEWERS;
-import static com.google.gerrit.common.data.GlobalCapability.FLUSH_CACHES;
-import static com.google.gerrit.common.data.GlobalCapability.KILL_TASK;
-import static com.google.gerrit.common.data.GlobalCapability.MAINTAIN_SERVER;
-import static com.google.gerrit.common.data.GlobalCapability.MODIFY_ACCOUNT;
import static com.google.gerrit.common.data.GlobalCapability.PRIORITY;
-import static com.google.gerrit.common.data.GlobalCapability.RUN_GC;
-import static com.google.gerrit.common.data.GlobalCapability.STREAM_EVENTS;
-import static com.google.gerrit.common.data.GlobalCapability.VIEW_ALL_ACCOUNTS;
-import static com.google.gerrit.common.data.GlobalCapability.VIEW_CACHES;
-import static com.google.gerrit.common.data.GlobalCapability.VIEW_CONNECTIONS;
-import static com.google.gerrit.common.data.GlobalCapability.VIEW_PLUGINS;
-import static com.google.gerrit.common.data.GlobalCapability.VIEW_QUEUE;
import com.google.common.collect.Iterables;
import com.google.gerrit.common.data.GlobalCapability;
@@ -45,12 +29,15 @@
import com.google.gerrit.server.OutputFormat;
import com.google.gerrit.server.account.AccountResource.Capability;
import com.google.gerrit.server.git.QueueProvider;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gson.reflect.TypeToken;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
+import java.util.EnumSet;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
@@ -67,34 +54,72 @@
private Set<String> query;
+ private final PermissionBackend permissionBackend;
private final Provider<CurrentUser> self;
private final DynamicMap<CapabilityDefinition> pluginCapabilities;
@Inject
- GetCapabilities(Provider<CurrentUser> self, DynamicMap<CapabilityDefinition> pluginCapabilities) {
+ GetCapabilities(
+ PermissionBackend permissionBackend,
+ Provider<CurrentUser> self,
+ DynamicMap<CapabilityDefinition> pluginCapabilities) {
+ this.permissionBackend = permissionBackend;
this.self = self;
this.pluginCapabilities = pluginCapabilities;
}
@Override
- public Object apply(AccountResource resource) throws AuthException {
- if (self.get() != resource.getUser() && !self.get().getCapabilities().canAdministrateServer()) {
- throw new AuthException("restricted to administrator");
+ public Object apply(AccountResource rsrc) throws AuthException, PermissionBackendException {
+ PermissionBackend.WithUser perm = permissionBackend.user(self);
+ if (self.get() != rsrc.getUser()) {
+ perm.check(GlobalPermission.ADMINISTRATE_SERVER);
+ perm = permissionBackend.user(rsrc.getUser());
}
- CapabilityControl cc = resource.getUser().getCapabilities();
Map<String, Object> have = new LinkedHashMap<>();
- for (String name : GlobalCapability.getAllNames()) {
- if (want(name)) {
- if (GlobalCapability.hasRange(name)) {
- if (cc.hasExplicitRange(name)) {
- have.put(name, new Range(cc.getRange(name)));
- }
- } else if (!name.equals(PRIORITY) && cc.canPerform(name)) {
- have.put(name, true);
+ for (GlobalPermission p : testGlobalPermissions(perm)) {
+ have.put(p.permissionName(), true);
+ }
+ addRanges(have, rsrc);
+ addPluginCapabilities(have, rsrc);
+ addPriority(have, rsrc);
+
+ return OutputFormat.JSON
+ .newGson()
+ .toJsonTree(have, new TypeToken<Map<String, Object>>() {}.getType());
+ }
+
+ private Set<GlobalPermission> testGlobalPermissions(PermissionBackend.WithUser perm)
+ throws PermissionBackendException {
+ EnumSet<GlobalPermission> toTest;
+ if (query != null) {
+ toTest = EnumSet.noneOf(GlobalPermission.class);
+ for (GlobalPermission p : GlobalPermission.values()) {
+ if (want(p.permissionName())) {
+ toTest.add(p);
}
}
+ } else {
+ toTest = EnumSet.allOf(GlobalPermission.class);
}
+ return perm.test(toTest);
+ }
+
+ private boolean want(String name) {
+ return query == null || query.contains(name.toLowerCase());
+ }
+
+ private void addRanges(Map<String, Object> have, AccountResource rsrc) {
+ CapabilityControl cc = rsrc.getUser().getCapabilities();
+ for (String name : GlobalCapability.getRangeNames()) {
+ if (want(name) && cc.hasExplicitRange(name)) {
+ have.put(name, new Range(cc.getRange(name)));
+ }
+ }
+ }
+
+ private void addPluginCapabilities(Map<String, Object> have, AccountResource rsrc) {
+ CapabilityControl cc = rsrc.getUser().getCapabilities();
for (String pluginName : pluginCapabilities.plugins()) {
for (String capability : pluginCapabilities.byPlugin(pluginName).keySet()) {
String name = String.format("%s-%s", pluginName, capability);
@@ -103,47 +128,14 @@
}
}
}
+ }
- have.put(ACCESS_DATABASE, cc.canAccessDatabase());
- have.put(CREATE_ACCOUNT, cc.canCreateAccount());
- have.put(CREATE_GROUP, cc.canCreateGroup());
- have.put(CREATE_PROJECT, cc.canCreateProject());
- have.put(EMAIL_REVIEWERS, cc.canEmailReviewers());
- have.put(FLUSH_CACHES, cc.canFlushCaches());
- have.put(KILL_TASK, cc.canKillTask());
- have.put(MAINTAIN_SERVER, cc.canMaintainServer());
- have.put(MODIFY_ACCOUNT, cc.canModifyAccount());
- have.put(RUN_GC, cc.canRunGC());
- have.put(STREAM_EVENTS, cc.canStreamEvents());
- have.put(VIEW_ALL_ACCOUNTS, cc.canViewAllAccounts());
- have.put(VIEW_CACHES, cc.canViewCaches());
- have.put(VIEW_CONNECTIONS, cc.canViewConnections());
- have.put(VIEW_PLUGINS, cc.canViewPlugins());
- have.put(VIEW_QUEUE, cc.canViewQueue());
-
- QueueProvider.QueueType queue = cc.getQueueType();
+ private void addPriority(Map<String, Object> have, AccountResource rsrc) {
+ QueueProvider.QueueType queue = rsrc.getUser().getCapabilities().getQueueType();
if (queue != QueueProvider.QueueType.INTERACTIVE
|| (query != null && query.contains(PRIORITY))) {
have.put(PRIORITY, queue);
}
-
- Iterator<Map.Entry<String, Object>> itr = have.entrySet().iterator();
- while (itr.hasNext()) {
- Map.Entry<String, Object> e = itr.next();
- if (!want(e.getKey())) {
- itr.remove();
- } else if (e.getValue() instanceof Boolean && !((Boolean) e.getValue())) {
- itr.remove();
- }
- }
-
- return OutputFormat.JSON
- .newGson()
- .toJsonTree(have, new TypeToken<Map<String, Object>>() {}.getType());
- }
-
- private boolean want(String name) {
- return query == null || query.contains(name.toLowerCase());
}
private static class Range {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutActive.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutActive.java
index 32c5345..4c525c2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutActive.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutActive.java
@@ -28,7 +28,6 @@
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
-import java.util.Collections;
import java.util.concurrent.atomic.AtomicBoolean;
@RequiresCapability(GlobalCapability.MODIFY_ACCOUNT)
@@ -69,7 +68,6 @@
if (a == null) {
throw new ResourceNotFoundException("account not found");
}
- dbProvider.get().accounts().update(Collections.singleton(a));
byIdCache.evict(a.getId());
return alreadyActive.get() ? Response.ok("") : Response.created("");
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutPreferred.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutPreferred.java
index ec60fb3..fb87e1e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutPreferred.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutPreferred.java
@@ -29,7 +29,6 @@
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
-import java.util.Collections;
import java.util.concurrent.atomic.AtomicBoolean;
@Singleton
@@ -79,7 +78,6 @@
if (a == null) {
throw new ResourceNotFoundException("account not found");
}
- dbProvider.get().accounts().update(Collections.singleton(a));
byIdCache.evict(a.getId());
return alreadyPreferred.get() ? Response.ok("") : Response.created("");
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
index 4698a80..d534c5a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
@@ -416,7 +416,7 @@
public void addReviewer(AddReviewerInput in) throws RestApiException {
try {
postReviewers.apply(change, in);
- } catch (OrmException | IOException | UpdateException e) {
+ } catch (OrmException | IOException | UpdateException | PermissionBackendException e) {
throw new RestApiException("Cannot add change reviewer", e);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
index 2af7b90..d934f6c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
@@ -214,7 +214,7 @@
public void review(ReviewInput in) throws RestApiException {
try {
review.apply(revision, in);
- } catch (OrmException | UpdateException | IOException e) {
+ } catch (OrmException | UpdateException | IOException | PermissionBackendException e) {
throw new RestApiException("Cannot post review", e);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
index 190f59b..c86714a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
@@ -59,8 +59,6 @@
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
import com.google.gerrit.common.data.LabelValue;
-import com.google.gerrit.common.data.Permission;
-import com.google.gerrit.common.data.PermissionRange;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.common.data.SubmitTypeRecord;
import com.google.gerrit.extensions.api.changes.FixInput;
@@ -104,7 +102,6 @@
import com.google.gerrit.server.api.accounts.AccountInfoComparator;
import com.google.gerrit.server.api.accounts.GpgApiAdapter;
import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.LabelNormalizer;
import com.google.gerrit.server.git.MergeUtil;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.index.change.ChangeIndexCollection;
@@ -112,6 +109,9 @@
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ReviewerStateInternal;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
+import com.google.gerrit.server.permissions.LabelPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.SubmitRuleOptions;
@@ -186,9 +186,9 @@
}
private final Provider<ReviewDb> db;
- private final LabelNormalizer labelNormalizer;
private final Provider<CurrentUser> userProvider;
private final AnonymousUser anonymous;
+ private final PermissionBackend permissionBackend;
private final GitRepositoryManager repoManager;
private final ProjectCache projectCache;
private final MergeUtil.Factory mergeUtilFactory;
@@ -217,9 +217,9 @@
@Inject
ChangeJson(
Provider<ReviewDb> db,
- LabelNormalizer ln,
Provider<CurrentUser> user,
AnonymousUser au,
+ PermissionBackend permissionBackend,
GitRepositoryManager repoManager,
ProjectCache projectCache,
MergeUtil.Factory mergeUtilFactory,
@@ -241,10 +241,10 @@
ApprovalsUtil approvalsUtil,
@Assisted Iterable<ListChangesOption> options) {
this.db = db;
- this.labelNormalizer = ln;
this.userProvider = user;
this.anonymous = au;
this.changeDataFactory = cdf;
+ this.permissionBackend = permissionBackend;
this.repoManager = repoManager;
this.userFactory = uf;
this.projectCache = projectCache;
@@ -316,6 +316,7 @@
| GpgException
| OrmException
| IOException
+ | PermissionBackendException
| RuntimeException e) {
if (!has(CHECK)) {
Throwables.throwIfInstanceOf(e, OrmException.class);
@@ -393,6 +394,7 @@
| GpgException
| OrmException
| IOException
+ | PermissionBackendException
| RuntimeException e) {
if (has(CHECK)) {
i = checkOnly(cd);
@@ -450,7 +452,8 @@
}
private ChangeInfo toChangeInfo(ChangeData cd, Optional<PatchSet.Id> limitToPsId)
- throws PatchListNotAvailableException, GpgException, OrmException, IOException {
+ throws PatchListNotAvailableException, GpgException, OrmException, IOException,
+ PermissionBackendException {
ChangeInfo out = new ChangeInfo();
CurrentUser user = userProvider.get();
ChangeControl ctl = cd.changeControl().forUser(user);
@@ -466,6 +469,7 @@
}
}
+ PermissionBackend.ForChange perm = permissionBackend.user(user).database(db).change(cd);
Change in = cd.change();
out.project = in.getProject().get();
out.branch = in.getDest().getShortName();
@@ -514,16 +518,17 @@
out.reviewed = cd.reviewedBy().contains(accountId) ? true : null;
}
- out.labels = labelsFor(ctl, cd, has(LABELS), has(DETAILED_LABELS));
+ out.labels = labelsFor(perm, ctl, cd, has(LABELS), has(DETAILED_LABELS));
out.submitted = getSubmittedOn(cd);
if (out.labels != null && has(DETAILED_LABELS)) {
// If limited to specific patch sets but not the current patch set, don't
// list permitted labels, since users can't vote on those patch sets.
- if (!limitToPsId.isPresent() || limitToPsId.get().equals(in.currentPatchSetId())) {
+ if (user.isIdentifiedUser()
+ && (!limitToPsId.isPresent() || limitToPsId.get().equals(in.currentPatchSetId()))) {
out.permittedLabels =
cd.change().getStatus() != Change.Status.ABANDONED
- ? permittedLabels(ctl, cd)
+ ? permittedLabels(perm, cd)
: ImmutableMap.of();
}
@@ -603,7 +608,12 @@
}
private Map<String, LabelInfo> labelsFor(
- ChangeControl ctl, ChangeData cd, boolean standard, boolean detailed) throws OrmException {
+ PermissionBackend.ForChange perm,
+ ChangeControl ctl,
+ ChangeData cd,
+ boolean standard,
+ boolean detailed)
+ throws OrmException, PermissionBackendException {
if (!standard && !detailed) {
return null;
}
@@ -612,20 +622,24 @@
return null;
}
- LabelTypes labelTypes = ctl.getLabelTypes();
+ LabelTypes labelTypes = cd.getLabelTypes();
Map<String, LabelWithStatus> withStatus =
cd.change().getStatus().isOpen()
- ? labelsForOpenChange(ctl, cd, labelTypes, standard, detailed)
- : labelsForClosedChange(ctl, cd, labelTypes, standard, detailed);
+ ? labelsForOpenChange(perm, cd, labelTypes, standard, detailed)
+ : labelsForClosedChange(perm, cd, labelTypes, standard, detailed);
return ImmutableMap.copyOf(Maps.transformValues(withStatus, LabelWithStatus::label));
}
private Map<String, LabelWithStatus> labelsForOpenChange(
- ChangeControl ctl, ChangeData cd, LabelTypes labelTypes, boolean standard, boolean detailed)
- throws OrmException {
+ PermissionBackend.ForChange perm,
+ ChangeData cd,
+ LabelTypes labelTypes,
+ boolean standard,
+ boolean detailed)
+ throws OrmException, PermissionBackendException {
Map<String, LabelWithStatus> labels = initLabels(cd, labelTypes, standard);
if (detailed) {
- setAllApprovals(ctl, cd, labels);
+ setAllApprovals(perm, cd, labels);
}
for (Map.Entry<String, LabelWithStatus> e : labels.entrySet()) {
LabelType type = labelTypes.byLabel(e.getKey());
@@ -712,8 +726,8 @@
}
private void setAllApprovals(
- ChangeControl baseCtrl, ChangeData cd, Map<String, LabelWithStatus> labels)
- throws OrmException {
+ PermissionBackend.ForChange basePerm, ChangeData cd, Map<String, LabelWithStatus> labels)
+ throws OrmException, PermissionBackendException {
Change.Status status = cd.change().getStatus();
checkState(status.isOpen(), "should not call setAllApprovals on %s change", status);
@@ -727,17 +741,17 @@
}
Table<Account.Id, String, PatchSetApproval> current =
- HashBasedTable.create(allUsers.size(), baseCtrl.getLabelTypes().getLabelTypes().size());
+ HashBasedTable.create(allUsers.size(), cd.getLabelTypes().getLabelTypes().size());
for (PatchSetApproval psa : cd.currentApprovals()) {
current.put(psa.getAccountId(), psa.getLabel(), psa);
}
+ LabelTypes labelTypes = cd.getLabelTypes();
for (Account.Id accountId : allUsers) {
- IdentifiedUser user = userFactory.create(accountId);
- ChangeControl ctl = baseCtrl.forUser(user);
- Map<String, VotingRangeInfo> pvr = getPermittedVotingRanges(permittedLabels(ctl, cd));
+ PermissionBackend.ForChange perm = basePerm.user(userFactory.create(accountId));
+ Map<String, VotingRangeInfo> pvr = getPermittedVotingRanges(permittedLabels(perm, cd));
for (Map.Entry<String, LabelWithStatus> e : labels.entrySet()) {
- LabelType lt = ctl.getLabelTypes().byLabel(e.getKey());
+ LabelType lt = labelTypes.byLabel(e.getKey());
if (lt == null) {
// Ignore submit record for undefined label; likely the submit rule
// author didn't intend for the label to show up in the table.
@@ -754,7 +768,7 @@
// This may be a dummy approval that was inserted when the reviewer
// was added. Explicitly check whether the user can vote on this
// label.
- value = labelNormalizer.canVote(ctl, lt, accountId) ? 0 : null;
+ value = perm.test(new LabelPermission(lt)) ? 0 : null;
}
tag = psa.getTag();
date = psa.getGranted();
@@ -765,7 +779,7 @@
// Either the user cannot vote on this label, or they were added as a
// reviewer but have not responded yet. Explicitly check whether the
// user can vote on this label.
- value = labelNormalizer.canVote(ctl, lt, accountId) ? 0 : null;
+ value = perm.test(new LabelPermission(lt)) ? 0 : null;
}
addApproval(
e.getValue().label(), approvalInfo(accountId, value, permittedVotingRange, tag, date));
@@ -813,12 +827,12 @@
}
private Map<String, LabelWithStatus> labelsForClosedChange(
- ChangeControl baseCtrl,
+ PermissionBackend.ForChange basePerm,
ChangeData cd,
LabelTypes labelTypes,
boolean standard,
boolean detailed)
- throws OrmException {
+ throws OrmException, PermissionBackendException {
Set<Account.Id> allUsers = new HashSet<>();
if (detailed) {
// Users expect to see all reviewers on closed changes, even if they
@@ -886,8 +900,8 @@
Map<String, ApprovalInfo> byLabel = Maps.newHashMapWithExpectedSize(labels.size());
Map<String, VotingRangeInfo> pvr = Collections.emptyMap();
if (detailed) {
- ChangeControl ctl = baseCtrl.forUser(userFactory.create(accountId));
- pvr = getPermittedVotingRanges(permittedLabels(ctl, cd));
+ PermissionBackend.ForChange perm = basePerm.user(userFactory.create(accountId));
+ pvr = getPermittedVotingRanges(permittedLabels(perm, cd));
for (Map.Entry<String, LabelWithStatus> entry : labels.entrySet()) {
ApprovalInfo ai = approvalInfo(accountId, 0, null, null, null);
byLabel.put(entry.getKey(), ai);
@@ -961,15 +975,25 @@
}
}
- private Map<String, Collection<String>> permittedLabels(ChangeControl ctl, ChangeData cd)
- throws OrmException {
- if (ctl == null || !ctl.getUser().isIdentifiedUser()) {
- return null;
+ private Map<String, Collection<String>> permittedLabels(
+ PermissionBackend.ForChange perm, ChangeData cd)
+ throws OrmException, PermissionBackendException {
+ boolean isMerged = cd.change().getStatus() == Change.Status.MERGED;
+ LabelTypes labelTypes = cd.getLabelTypes();
+ Map<String, LabelType> toCheck = new HashMap<>();
+ for (SubmitRecord rec : submitRecords(cd)) {
+ if (rec.labels != null) {
+ for (SubmitRecord.Label r : rec.labels) {
+ LabelType type = labelTypes.byLabel(r.label);
+ if (type != null && (!isMerged || type.allowPostSubmit())) {
+ toCheck.put(type.getName(), type);
+ }
+ }
+ }
}
Map<String, Short> labels = null;
- boolean isMerged = ctl.getChange().getStatus() == Change.Status.MERGED;
- LabelTypes labelTypes = ctl.getLabelTypes();
+ Set<LabelPermission.WithValue> can = perm.testLabels(toCheck.values());
SetMultimap<String, String> permitted = LinkedHashMultimap.create();
for (SubmitRecord rec : submitRecords(cd)) {
if (rec.labels == null) {
@@ -980,12 +1004,12 @@
if (type == null || (isMerged && !type.allowPostSubmit())) {
continue;
}
- PermissionRange range = ctl.getRange(Permission.forLabel(r.label));
+
for (LabelValue v : type.getValues()) {
- boolean ok = range.contains(v.getValue());
+ boolean ok = can.contains(new LabelPermission.WithValue(type, v));
if (isMerged) {
if (labels == null) {
- labels = currentLabels(ctl);
+ labels = currentLabels(perm, cd);
}
short prev = labels.getOrDefault(type.getName(), (short) 0);
ok &= v.getValue() >= prev;
@@ -996,6 +1020,7 @@
}
}
}
+
List<String> toClear = Lists.newArrayListWithCapacity(permitted.keySet().size());
for (Map.Entry<String, Collection<String>> e : permitted.asMap().entrySet()) {
if (isOnlyZero(e.getValue())) {
@@ -1008,11 +1033,14 @@
return permitted.asMap();
}
- private Map<String, Short> currentLabels(ChangeControl ctl) throws OrmException {
+ private Map<String, Short> currentLabels(PermissionBackend.ForChange perm, ChangeData cd)
+ throws OrmException {
+ IdentifiedUser user = perm.user().asIdentifiedUser();
+ ChangeControl ctl = cd.changeControl().forUser(user);
Map<String, Short> result = new HashMap<>();
for (PatchSetApproval psa :
approvalsUtil.byPatchSetUser(
- db.get(), ctl, ctl.getChange().currentPatchSetId(), ctl.getUser().getAccountId())) {
+ db.get(), ctl, cd.change().currentPatchSetId(), user.getAccountId())) {
result.put(psa.getLabel(), psa.getValue());
}
return result;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetReviewer.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetReviewer.java
index aa0b339..db9af1d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetReviewer.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetReviewer.java
@@ -16,6 +16,7 @@
import com.google.gerrit.extensions.api.changes.ReviewerInfo;
import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -31,7 +32,8 @@
}
@Override
- public List<ReviewerInfo> apply(ReviewerResource rsrc) throws OrmException {
+ public List<ReviewerInfo> apply(ReviewerResource rsrc)
+ throws OrmException, PermissionBackendException {
return json.format(rsrc);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListReviewers.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListReviewers.java
index 1dba58c..ba2a10b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListReviewers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListReviewers.java
@@ -20,6 +20,7 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.mail.Address;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -48,7 +49,8 @@
}
@Override
- public List<ReviewerInfo> apply(ChangeResource rsrc) throws OrmException {
+ public List<ReviewerInfo> apply(ChangeResource rsrc)
+ throws OrmException, PermissionBackendException {
Map<String, ReviewerResource> reviewers = new LinkedHashMap<>();
ReviewDb db = dbProvider.get();
for (Account.Id accountId : approvalsUtil.getReviewers(db, rsrc.getNotes()).all()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListRevisionReviewers.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListRevisionReviewers.java
index 5aaee56..6d9dc79 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListRevisionReviewers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListRevisionReviewers.java
@@ -21,6 +21,7 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.mail.Address;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -50,7 +51,7 @@
@Override
public List<ReviewerInfo> apply(RevisionResource rsrc)
- throws OrmException, MethodNotAllowedException {
+ throws OrmException, MethodNotAllowedException, PermissionBackendException {
if (!rsrc.isCurrent()) {
throw new MethodNotAllowedException("Cannot list reviewers on non-current patch set");
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
index 81ab39e..6365fd9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
@@ -18,6 +18,7 @@
import static com.google.common.base.Preconditions.checkState;
import static com.google.gerrit.server.CommentsUtil.setCommentRevId;
import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
+import static com.google.gerrit.server.permissions.LabelPermission.ForUser.ON_BEHALF_OF;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.joining;
@@ -39,8 +40,6 @@
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
-import com.google.gerrit.common.data.Permission;
-import com.google.gerrit.common.data.PermissionRange;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.AddReviewerResult;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
@@ -83,6 +82,7 @@
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CommentsUtil;
+import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.OutputFormat;
import com.google.gerrit.server.PatchSetUtil;
@@ -95,6 +95,10 @@
import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.patch.PatchListCache;
+import com.google.gerrit.server.permissions.ChangePermission;
+import com.google.gerrit.server.permissions.LabelPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.update.BatchUpdate;
@@ -188,12 +192,14 @@
@Override
public Response<ReviewResult> apply(RevisionResource revision, ReviewInput input)
- throws RestApiException, UpdateException, OrmException, IOException {
+ throws RestApiException, UpdateException, OrmException, IOException,
+ PermissionBackendException {
return apply(revision, input, TimeUtil.nowTs());
}
public Response<ReviewResult> apply(RevisionResource revision, ReviewInput input, Timestamp ts)
- throws RestApiException, UpdateException, OrmException, IOException {
+ throws RestApiException, UpdateException, OrmException, IOException,
+ PermissionBackendException {
// Respect timestamp, but truncate at change created-on time.
ts = Ordering.natural().max(ts, revision.getChange().getCreatedOn());
if (revision.getEdit().isPresent()) {
@@ -348,7 +354,8 @@
}
private RevisionResource onBehalfOf(RevisionResource rev, ReviewInput in)
- throws BadRequestException, AuthException, UnprocessableEntityException, OrmException {
+ throws BadRequestException, AuthException, UnprocessableEntityException, OrmException,
+ PermissionBackendException {
if (in.labels == null || in.labels.isEmpty()) {
throw new AuthException(
String.format("label required to post review on behalf of \"%s\"", in.onBehalfOf));
@@ -360,11 +367,13 @@
throw new AuthException("not allowed to modify other user's drafts");
}
- ChangeControl caller = rev.getControl();
+ CurrentUser caller = rev.getUser();
+ PermissionBackend.ForChange perm = rev.permissions().database(db);
+ LabelTypes labelTypes = rev.getControl().getLabelTypes();
Iterator<Map.Entry<String, Short>> itr = in.labels.entrySet().iterator();
while (itr.hasNext()) {
Map.Entry<String, Short> ent = itr.next();
- LabelType type = caller.getLabelTypes().byLabel(ent.getKey());
+ LabelType type = labelTypes.byLabel(ent.getKey());
if (type == null && in.strictLabels) {
throw new BadRequestException(
String.format("label \"%s\" is not a configured label", ent.getKey()));
@@ -373,16 +382,15 @@
continue;
}
- if (caller.getUser().isInternalUser()) {
- continue;
- }
-
- PermissionRange r = caller.getRange(Permission.forLabelAs(type.getName()));
- if (r == null || r.isEmpty() || !r.contains(ent.getValue())) {
- throw new AuthException(
- String.format(
- "not permitted to modify label \"%s\" on behalf of \"%s\"",
- ent.getKey(), in.onBehalfOf));
+ if (!caller.isInternalUser()) {
+ try {
+ perm.check(new LabelPermission.WithValue(ON_BEHALF_OF, type, ent.getValue()));
+ } catch (AuthException e) {
+ throw new AuthException(
+ String.format(
+ "not permitted to modify label \"%s\" on behalf of \"%s\"",
+ type.getName(), in.onBehalfOf));
+ }
}
}
if (in.labels.isEmpty()) {
@@ -390,25 +398,26 @@
String.format("label required to post review on behalf of \"%s\"", in.onBehalfOf));
}
- ChangeControl target =
- caller.forUser(accounts.parseOnBehalfOf(caller.getUser(), in.onBehalfOf));
- if (!target.getRefControl().isVisible()) {
+ IdentifiedUser reviewer = accounts.parseOnBehalfOf(caller, in.onBehalfOf);
+ try {
+ perm.user(reviewer).check(ChangePermission.READ);
+ } catch (AuthException e) {
throw new UnprocessableEntityException(
- String.format(
- "on_behalf_of account %s cannot see destination ref",
- target.getUser().getAccountId()));
+ String.format("on_behalf_of account %s cannot see change", reviewer.getAccountId()));
}
- return new RevisionResource(changes.parse(target), rev.getPatchSet());
+
+ ChangeControl ctl = rev.getControl().forUser(reviewer);
+ return new RevisionResource(changes.parse(ctl), rev.getPatchSet());
}
- private void checkLabels(RevisionResource revision, boolean strict, Map<String, Short> labels)
- throws BadRequestException, AuthException {
- ChangeControl ctl = revision.getControl();
+ private void checkLabels(RevisionResource rsrc, boolean strict, Map<String, Short> labels)
+ throws BadRequestException, AuthException, PermissionBackendException {
+ LabelTypes types = rsrc.getControl().getLabelTypes();
+ PermissionBackend.ForChange perm = rsrc.permissions();
Iterator<Map.Entry<String, Short>> itr = labels.entrySet().iterator();
while (itr.hasNext()) {
Map.Entry<String, Short> ent = itr.next();
-
- LabelType lt = revision.getControl().getLabelTypes().byLabel(ent.getKey());
+ LabelType lt = types.byLabel(ent.getKey());
if (lt == null) {
if (strict) {
throw new BadRequestException(
@@ -433,18 +442,15 @@
continue;
}
- String name = lt.getName();
- PermissionRange range = ctl.getRange(Permission.forLabel(name));
- if (range == null || !range.contains(ent.getValue())) {
+ short val = ent.getValue();
+ try {
+ perm.check(new LabelPermission.WithValue(lt, val));
+ } catch (AuthException e) {
if (strict) {
throw new AuthException(
- String.format(
- "Applying label \"%s\": %d is restricted", ent.getKey(), ent.getValue()));
- } else if (range == null || range.isEmpty()) {
- ent.setValue((short) 0);
- } else {
- ent.setValue((short) range.squash(ent.getValue()));
+ String.format("Applying label \"%s\": %d is restricted", lt.getName(), val));
}
+ ent.setValue(perm.squashThenCheck(lt, val));
}
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
index b7fe986..cc759fd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
@@ -54,6 +54,8 @@
import com.google.gerrit.server.mail.Address;
import com.google.gerrit.server.mail.send.OutgoingEmailValidator;
import com.google.gerrit.server.notedb.NotesMigration;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache;
@@ -79,6 +81,7 @@
private final AccountsCollection accounts;
private final ReviewerResource.Factory reviewerFactory;
+ private final PermissionBackend permissionBackend;
private final GroupsCollection groupsCollection;
private final GroupMembers.Factory groupMembersFactory;
@@ -98,6 +101,7 @@
PostReviewers(
AccountsCollection accounts,
ReviewerResource.Factory reviewerFactory,
+ PermissionBackend permissionBackend,
GroupsCollection groupsCollection,
GroupMembers.Factory groupMembersFactory,
AccountLoader.Factory accountLoaderFactory,
@@ -113,6 +117,7 @@
PostReviewersOp.Factory postReviewersOpFactory) {
this.accounts = accounts;
this.reviewerFactory = reviewerFactory;
+ this.permissionBackend = permissionBackend;
this.groupsCollection = groupsCollection;
this.groupMembersFactory = groupMembersFactory;
this.accountLoaderFactory = accountLoaderFactory;
@@ -130,7 +135,8 @@
@Override
public AddReviewerResult apply(ChangeResource rsrc, AddReviewerInput input)
- throws IOException, OrmException, RestApiException, UpdateException {
+ throws IOException, OrmException, RestApiException, UpdateException,
+ PermissionBackendException {
if (input.reviewer == null) {
throw new BadRequestException("missing reviewer field");
}
@@ -398,14 +404,17 @@
rsrc, this.reviewers, this.reviewersByEmail, state, notify, accountsToNotify);
}
- void gatherResults() throws OrmException {
+ void gatherResults() throws OrmException, PermissionBackendException {
// Generate result details and fill AccountLoader. This occurs outside
// the Op because the accounts are in a different table.
PostReviewersOp.Result opResult = op.getResult();
if (migration.readChanges() && state == CC) {
result.ccs = Lists.newArrayListWithCapacity(opResult.addedCCs().size());
for (Account.Id accountId : opResult.addedCCs()) {
- result.ccs.add(json.format(new ReviewerInfo(accountId.get()), reviewers.get(accountId)));
+ ChangeControl ctl = reviewers.get(accountId);
+ PermissionBackend.ForChange perm =
+ permissionBackend.user(ctl.getUser()).database(dbProvider).change(ctl.getNotes());
+ result.ccs.add(json.format(new ReviewerInfo(accountId.get()), perm, ctl));
}
accountLoaderFactory.create(true).fill(result.ccs);
for (Address a : reviewersByEmail) {
@@ -415,11 +424,12 @@
result.reviewers = Lists.newArrayListWithCapacity(opResult.addedReviewers().size());
for (PatchSetApproval psa : opResult.addedReviewers()) {
// New reviewers have value 0, don't bother normalizing.
+ ChangeControl ctl = reviewers.get(psa.getAccountId());
+ PermissionBackend.ForChange perm =
+ permissionBackend.user(ctl.getUser()).database(dbProvider).change(ctl.getNotes());
result.reviewers.add(
json.format(
- new ReviewerInfo(psa.getAccountId().get()),
- reviewers.get(psa.getAccountId()),
- ImmutableList.of(psa)));
+ new ReviewerInfo(psa.getAccountId().get()), perm, ctl, ImmutableList.of(psa)));
}
accountLoaderFactory.create(true).fill(result.reviewers);
for (Address a : reviewersByEmail) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerJson.java
index ac7f15e..d6ef2a7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerJson.java
@@ -20,7 +20,6 @@
import com.google.common.collect.Lists;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
-import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRange;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.extensions.api.changes.ReviewerInfo;
@@ -30,6 +29,9 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.account.AccountLoader;
+import com.google.gerrit.server.permissions.LabelPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.SubmitRuleEvaluator;
import com.google.gerrit.server.query.change.ChangeData;
@@ -44,6 +46,7 @@
@Singleton
public class ReviewerJson {
private final Provider<ReviewDb> db;
+ private final PermissionBackend permissionBackend;
private final ChangeData.Factory changeDataFactory;
private final ApprovalsUtil approvalsUtil;
private final AccountLoader.Factory accountLoaderFactory;
@@ -51,22 +54,29 @@
@Inject
ReviewerJson(
Provider<ReviewDb> db,
+ PermissionBackend permissionBackend,
ChangeData.Factory changeDataFactory,
ApprovalsUtil approvalsUtil,
AccountLoader.Factory accountLoaderFactory) {
this.db = db;
+ this.permissionBackend = permissionBackend;
this.changeDataFactory = changeDataFactory;
this.approvalsUtil = approvalsUtil;
this.accountLoaderFactory = accountLoaderFactory;
}
- public List<ReviewerInfo> format(Collection<ReviewerResource> rsrcs) throws OrmException {
+ public List<ReviewerInfo> format(Collection<ReviewerResource> rsrcs)
+ throws OrmException, PermissionBackendException {
List<ReviewerInfo> infos = Lists.newArrayListWithCapacity(rsrcs.size());
AccountLoader loader = accountLoaderFactory.create(true);
for (ReviewerResource rsrc : rsrcs) {
ReviewerInfo info =
format(
new ReviewerInfo(rsrc.getReviewerUser().getAccountId().get()),
+ permissionBackend
+ .user(rsrc.getReviewerUser())
+ .database(db)
+ .change(rsrc.getChangeResource().getNotes()),
rsrc.getReviewerControl());
loader.put(info);
infos.add(info);
@@ -75,22 +85,29 @@
return infos;
}
- public List<ReviewerInfo> format(ReviewerResource rsrc) throws OrmException {
+ public List<ReviewerInfo> format(ReviewerResource rsrc)
+ throws OrmException, PermissionBackendException {
return format(ImmutableList.<ReviewerResource>of(rsrc));
}
- public ReviewerInfo format(ReviewerInfo out, ChangeControl ctl) throws OrmException {
+ public ReviewerInfo format(ReviewerInfo out, PermissionBackend.ForChange perm, ChangeControl ctl)
+ throws OrmException, PermissionBackendException {
PatchSet.Id psId = ctl.getChange().currentPatchSetId();
return format(
out,
+ perm,
ctl,
approvalsUtil.byPatchSetUser(db.get(), ctl, psId, new Account.Id(out._accountId)));
}
public ReviewerInfo format(
- ReviewerInfo out, ChangeControl ctl, Iterable<PatchSetApproval> approvals)
- throws OrmException {
- LabelTypes labelTypes = ctl.getLabelTypes();
+ ReviewerInfo out,
+ PermissionBackend.ForChange perm,
+ ChangeControl ctl,
+ Iterable<PatchSetApproval> approvals)
+ throws OrmException, PermissionBackendException {
+ ChangeData cd = changeDataFactory.create(db.get(), ctl);
+ LabelTypes labelTypes = cd.getLabelTypes();
// Don't use Maps.newTreeMap(Comparator) due to OpenJDK bug 100167.
out.approvals = new TreeMap<>(labelTypes.nameComparator());
@@ -107,7 +124,6 @@
// Add dummy approvals for all permitted labels for the user even if they
// do not exist in the DB.
- ChangeData cd = changeDataFactory.create(db.get(), ctl);
PatchSet ps = cd.currentPatchSet();
if (ps != null) {
for (SubmitRecord rec :
@@ -117,8 +133,10 @@
}
for (SubmitRecord.Label label : rec.labels) {
String name = label.label;
+ LabelType type = labelTypes.byLabel(name);
if (!out.approvals.containsKey(name)
- && !ctl.getRange(Permission.forLabel(name)).isEmpty()) {
+ && type != null
+ && perm.test(new LabelPermission(type))) {
out.approvals.put(name, formatValue((short) 0));
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/CachesCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/CachesCollection.java
index f002f8d..1e88842 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/CachesCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/CachesCollection.java
@@ -27,8 +27,10 @@
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -40,6 +42,7 @@
private final DynamicMap<RestView<CacheResource>> views;
private final Provider<ListCaches> list;
+ private final PermissionBackend permissionBackend;
private final Provider<CurrentUser> self;
private final DynamicMap<Cache<?, ?>> cacheMap;
private final PostCaches postCaches;
@@ -48,11 +51,13 @@
CachesCollection(
DynamicMap<RestView<CacheResource>> views,
Provider<ListCaches> list,
+ PermissionBackend permissionBackend,
Provider<CurrentUser> self,
DynamicMap<Cache<?, ?>> cacheMap,
PostCaches postCaches) {
this.views = views;
this.list = list;
+ this.permissionBackend = permissionBackend;
this.self = self;
this.cacheMap = cacheMap;
this.postCaches = postCaches;
@@ -65,15 +70,8 @@
@Override
public CacheResource parse(ConfigResource parent, IdString id)
- throws AuthException, ResourceNotFoundException {
- CurrentUser user = self.get();
- if (user instanceof AnonymousUser) {
- throw new AuthException("Authentication required");
- } else if (!user.isIdentifiedUser()) {
- throw new ResourceNotFoundException();
- } else if (!user.getCapabilities().canViewCaches()) {
- throw new AuthException("not allowed to view caches");
- }
+ throws AuthException, ResourceNotFoundException, PermissionBackendException {
+ permissionBackend.user(self).check(GlobalPermission.VIEW_CACHES);
String cacheName = id.get();
String pluginName = "gerrit";
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/LabelNormalizer.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/LabelNormalizer.java
index 6a05d22..323f352 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/LabelNormalizer.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/LabelNormalizer.java
@@ -142,16 +142,6 @@
return Result.create(unchanged, updated, deleted);
}
- /**
- * @param ctl change control (for any user).
- * @param lt label type.
- * @param id account ID.
- * @return whether the given account ID has any permissions to vote on this label for this change.
- */
- public boolean canVote(ChangeControl ctl, LabelType lt, Account.Id id) {
- return !getRange(ctl, lt, id).isEmpty();
- }
-
private PatchSetApproval copy(PatchSetApproval src) {
return new PatchSetApproval(src.getPatchSetId(), src);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/RefOperationValidators.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/RefOperationValidators.java
index 80792c3..0c4eb89 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/RefOperationValidators.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/RefOperationValidators.java
@@ -14,10 +14,13 @@
package com.google.gerrit.server.git.validators;
import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.events.RefReceivedEvent;
import com.google.gerrit.server.validators.ValidationException;
import com.google.inject.Inject;
@@ -42,15 +45,18 @@
update.getExpectedOldObjectId(), update.getNewObjectId(), update.getName(), type);
}
- private final RefReceivedEvent event;
+ private final AllUsersName allUsersName;
private final DynamicSet<RefOperationValidationListener> refOperationValidationListeners;
+ private final RefReceivedEvent event;
@Inject
RefOperationValidators(
+ AllUsersName allUsersName,
DynamicSet<RefOperationValidationListener> refOperationValidationListeners,
@Assisted Project project,
@Assisted IdentifiedUser user,
@Assisted ReceiveCommand cmd) {
+ this.allUsersName = allUsersName;
this.refOperationValidationListeners = refOperationValidationListeners;
event = new RefReceivedEvent();
event.command = cmd;
@@ -59,11 +65,13 @@
}
public List<ValidationMessage> validateForRefOperation() throws RefOperationValidationException {
-
List<ValidationMessage> messages = new ArrayList<>();
boolean withException = false;
+ List<RefOperationValidationListener> listeners = new ArrayList<>();
+ listeners.add(new DisallowDeletionOfUserBranches(allUsersName));
+ refOperationValidationListeners.forEach(l -> listeners.add(l));
try {
- for (RefOperationValidationListener listener : refOperationValidationListeners) {
+ for (RefOperationValidationListener listener : listeners) {
messages.addAll(listener.onRefOperation(event));
}
} catch (ValidationException e) {
@@ -95,4 +103,26 @@
return input.isError();
}
}
+
+ private static class DisallowDeletionOfUserBranches implements RefOperationValidationListener {
+ private final AllUsersName allUsersName;
+
+ DisallowDeletionOfUserBranches(AllUsersName allUsersName) {
+ this.allUsersName = allUsersName;
+ }
+
+ @Override
+ public List<ValidationMessage> onRefOperation(RefReceivedEvent refEvent)
+ throws ValidationException {
+ if (refEvent.project.getNameKey().equals(allUsersName)
+ && (refEvent.command.getRefName().startsWith(RefNames.REFS_USERS)
+ && !refEvent.command.getRefName().equals(RefNames.REFS_USERS_DEFAULT))
+ && refEvent.command.getType().equals(ReceiveCommand.Type.DELETE)) {
+ if (!refEvent.user.getCapabilities().canAccessDatabase()) {
+ throw new ValidationException("Not allowed to delete user branch.");
+ }
+ }
+ return ImmutableList.of();
+ }
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/MergedSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/MergedSender.java
index 47115af..4d48990 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/MergedSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/MergedSender.java
@@ -41,7 +41,7 @@
public MergedSender(EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id)
throws OrmException {
super(ea, "merged", newChangeData(ea, project, id));
- labelTypes = changeData.changeControl().getLabelTypes();
+ labelTypes = changeData.getLabelTypes();
}
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/permissions/FailedPermissionBackend.java b/gerrit-server/src/main/java/com/google/gerrit/server/permissions/FailedPermissionBackend.java
index 4945879..24f5164 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/permissions/FailedPermissionBackend.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/permissions/FailedPermissionBackend.java
@@ -164,5 +164,10 @@
throws PermissionBackendException {
throw new PermissionBackendException(message, cause);
}
+
+ @Override
+ public CurrentUser user() {
+ throw new UnsupportedOperationException("FailedPermissionBackend is not scoped to user");
+ }
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/permissions/GlobalPermission.java b/gerrit-server/src/main/java/com/google/gerrit/server/permissions/GlobalPermission.java
new file mode 100644
index 0000000..575a08b
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/permissions/GlobalPermission.java
@@ -0,0 +1,54 @@
+// 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.permissions;
+
+import com.google.gerrit.common.data.GlobalCapability;
+import java.util.Locale;
+
+public enum GlobalPermission {
+ ACCESS_DATABASE(GlobalCapability.ACCESS_DATABASE),
+ ADMINISTRATE_SERVER(GlobalCapability.ADMINISTRATE_SERVER),
+ CREATE_ACCOUNT(GlobalCapability.CREATE_ACCOUNT),
+ CREATE_GROUP(GlobalCapability.CREATE_GROUP),
+ CREATE_PROJECT(GlobalCapability.CREATE_PROJECT),
+ EMAIL_REVIEWERS(GlobalCapability.EMAIL_REVIEWERS),
+ FLUSH_CACHES(GlobalCapability.FLUSH_CACHES),
+ KILL_TASK(GlobalCapability.KILL_TASK),
+ MAINTAIN_SERVER(GlobalCapability.MAINTAIN_SERVER),
+ MODIFY_ACCOUNT(GlobalCapability.MODIFY_ACCOUNT),
+ RUN_AS(GlobalCapability.RUN_AS),
+ RUN_GC(GlobalCapability.RUN_GC),
+ STREAM_EVENTS(GlobalCapability.STREAM_EVENTS),
+ VIEW_ALL_ACCOUNTS(GlobalCapability.VIEW_ALL_ACCOUNTS),
+ VIEW_CACHES(GlobalCapability.VIEW_CACHES),
+ VIEW_CONNECTIONS(GlobalCapability.VIEW_CONNECTIONS),
+ VIEW_PLUGINS(GlobalCapability.VIEW_PLUGINS),
+ VIEW_QUEUE(GlobalCapability.VIEW_QUEUE);
+
+ private final String name;
+
+ GlobalPermission(String name) {
+ this.name = name;
+ }
+
+ /** @return name used in {@code project.config} permissions. */
+ public String permissionName() {
+ return name;
+ }
+
+ public String describeForException() {
+ return toString().toLowerCase(Locale.US).replace('_', ' ');
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/permissions/LabelPermission.java b/gerrit-server/src/main/java/com/google/gerrit/server/permissions/LabelPermission.java
index 61f7330..747c997 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/permissions/LabelPermission.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/permissions/LabelPermission.java
@@ -15,25 +15,69 @@
package com.google.gerrit.server.permissions;
import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.gerrit.server.permissions.LabelPermission.ForUser.ON_BEHALF_OF;
+import static com.google.gerrit.server.permissions.LabelPermission.ForUser.SELF;
import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.common.data.LabelValue;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.server.util.LabelVote;
import java.util.Optional;
/** Permission representing a label. */
public class LabelPermission implements ChangePermissionOrLabel {
+ public enum ForUser {
+ SELF,
+ ON_BEHALF_OF;
+ }
+
+ private final ForUser forUser;
private final String name;
/**
* Construct a reference to a label permission.
*
+ * @param type type description of the label.
+ */
+ public LabelPermission(LabelType type) {
+ this(SELF, type);
+ }
+
+ /**
+ * Construct a reference to a label permission.
+ *
+ * @param forUser {@code SELF} (default) or {@code ON_BEHALF_OF} for labelAs behavior.
+ * @param type type description of the label.
+ */
+ public LabelPermission(ForUser forUser, LabelType type) {
+ this(forUser, type.getName());
+ }
+
+ /**
+ * Construct a reference to a label permission.
+ *
* @param name name of the label, e.g. {@code "Code-Review"} or {@code "Verified"}.
*/
public LabelPermission(String name) {
+ this(SELF, name);
+ }
+
+ /**
+ * Construct a reference to a label permission.
+ *
+ * @param forUser {@code SELF} (default) or {@code ON_BEHALF_OF} for labelAs behavior.
+ * @param name name of the label, e.g. {@code "Code-Review"} or {@code "Verified"}.
+ */
+ public LabelPermission(ForUser forUser, String name) {
+ this.forUser = checkNotNull(forUser, "ForUser");
this.name = LabelType.checkName(name);
}
+ /** @return {@code SELF} or {@code ON_BEHALF_OF} (or labelAs). */
+ public ForUser forUser() {
+ return forUser;
+ }
+
/** @return name of the label, e.g. {@code "Code-Review"}. */
public String label() {
return name;
@@ -42,12 +86,21 @@
/** @return name used in {@code project.config} permissions. */
@Override
public Optional<String> permissionName() {
- return Optional.of(Permission.forLabel(label()));
+ switch (forUser) {
+ case SELF:
+ return Optional.of(Permission.forLabel(name));
+ case ON_BEHALF_OF:
+ return Optional.of(Permission.forLabelAs(name));
+ }
+ return Optional.empty();
}
@Override
public String describeForException() {
- return "label " + label();
+ if (forUser == ON_BEHALF_OF) {
+ return "labelAs " + name;
+ }
+ return "label " + name;
}
@Override
@@ -57,26 +110,87 @@
@Override
public boolean equals(Object other) {
- return other instanceof LabelPermission && name.equals(((LabelPermission) other).name);
+ if (other instanceof LabelPermission) {
+ LabelPermission b = (LabelPermission) other;
+ return forUser == b.forUser && name.equals(b.name);
+ }
+ return false;
}
@Override
public String toString() {
+ if (forUser == ON_BEHALF_OF) {
+ return "LabelAs[" + name + ']';
+ }
return "Label[" + name + ']';
}
/** A {@link LabelPermission} at a specific value. */
public static class WithValue implements ChangePermissionOrLabel {
+ private final ForUser forUser;
private final LabelVote label;
/**
* Construct a reference to a label at a specific value.
*
+ * @param type description of the label.
+ * @param value numeric score assigned to the label.
+ */
+ public WithValue(LabelType type, LabelValue value) {
+ this(SELF, type, value);
+ }
+
+ /**
+ * Construct a reference to a label at a specific value.
+ *
+ * @param type description of the label.
+ * @param value numeric score assigned to the label.
+ */
+ public WithValue(LabelType type, short value) {
+ this(SELF, type.getName(), value);
+ }
+
+ /**
+ * Construct a reference to a label at a specific value.
+ *
+ * @param forUser {@code SELF} (default) or {@code ON_BEHALF_OF} for labelAs behavior.
+ * @param type description of the label.
+ * @param value numeric score assigned to the label.
+ */
+ public WithValue(ForUser forUser, LabelType type, LabelValue value) {
+ this(forUser, type.getName(), value.getValue());
+ }
+
+ /**
+ * Construct a reference to a label at a specific value.
+ *
+ * @param forUser {@code SELF} (default) or {@code ON_BEHALF_OF} for labelAs behavior.
+ * @param type description of the label.
+ * @param value numeric score assigned to the label.
+ */
+ public WithValue(ForUser forUser, LabelType type, short value) {
+ this(forUser, type.getName(), value);
+ }
+
+ /**
+ * Construct a reference to a label at a specific value.
+ *
* @param name name of the label, e.g. {@code "Code-Review"} or {@code "Verified"}.
* @param value numeric score assigned to the label.
*/
public WithValue(String name, short value) {
- this(LabelVote.create(name, value));
+ this(SELF, name, value);
+ }
+
+ /**
+ * Construct a reference to a label at a specific value.
+ *
+ * @param forUser {@code SELF} (default) or {@code ON_BEHALF_OF} for labelAs behavior.
+ * @param name name of the label, e.g. {@code "Code-Review"} or {@code "Verified"}.
+ * @param value numeric score assigned to the label.
+ */
+ public WithValue(ForUser forUser, String name, short value) {
+ this(forUser, LabelVote.create(name, value));
}
/**
@@ -85,9 +199,25 @@
* @param label label name and vote.
*/
public WithValue(LabelVote label) {
+ this(SELF, label);
+ }
+
+ /**
+ * Construct a reference to a label at a specific value.
+ *
+ * @param forUser {@code SELF} (default) or {@code ON_BEHALF_OF} for labelAs behavior.
+ * @param label label name and vote.
+ */
+ public WithValue(ForUser forUser, LabelVote label) {
+ this.forUser = checkNotNull(forUser, "ForUser");
this.label = checkNotNull(label, "LabelVote");
}
+ /** @return {@code SELF} or {@code ON_BEHALF_OF} (or labelAs). */
+ public ForUser forUser() {
+ return forUser;
+ }
+
/** @return name of the label, e.g. {@code "Code-Review"}. */
public String label() {
return label.label();
@@ -101,11 +231,20 @@
/** @return name used in {@code project.config} permissions. */
@Override
public Optional<String> permissionName() {
- return Optional.of(Permission.forLabel(label()));
+ switch (forUser) {
+ case SELF:
+ return Optional.of(Permission.forLabel(label()));
+ case ON_BEHALF_OF:
+ return Optional.of(Permission.forLabelAs(label()));
+ }
+ return Optional.empty();
}
@Override
public String describeForException() {
+ if (forUser == ON_BEHALF_OF) {
+ return "labelAs " + label.formatWithEquals();
+ }
return "label " + label.formatWithEquals();
}
@@ -116,11 +255,18 @@
@Override
public boolean equals(Object other) {
- return other instanceof WithValue && label.equals(((WithValue) other).label);
+ if (other instanceof WithValue) {
+ WithValue b = (WithValue) other;
+ return forUser == b.forUser && label.equals(b.label);
+ }
+ return false;
}
@Override
public String toString() {
+ if (forUser == ON_BEHALF_OF) {
+ return "LabelAs[" + label.format() + ']';
+ }
return "Label[" + label.format() + ']';
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/permissions/PermissionBackend.java b/gerrit-server/src/main/java/com/google/gerrit/server/permissions/PermissionBackend.java
index 9e5350b..b3a858c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/permissions/PermissionBackend.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/permissions/PermissionBackend.java
@@ -15,7 +15,9 @@
package com.google.gerrit.server.permissions;
import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.stream.Collectors.toSet;
+import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Project;
@@ -34,10 +36,7 @@
import org.slf4j.LoggerFactory;
/**
- * Checks authorization to perform an action on project, ref, or change.
- *
- * <p>{@code PermissionBackend} should be a singleton for the server, acting as a factory for
- * lightweight request instances.
+ * Checks authorization to perform an action on a project, reference, or change.
*
* <p>{@code check} methods should be used during action handlers to verify the user is allowed to
* exercise the specified permission. For convenience in implementation {@code check} methods throw
@@ -48,6 +47,13 @@
* permission. This is suitable for configuring UI button state, but should not be relied upon to
* guard handlers before making state changes.
*
+ * <p>{@code PermissionBackend} is a singleton for the server, acting as a factory for lightweight
+ * request instances. Implementation classes may cache supporting data inside of {@link WithUser},
+ * {@link ForProject}, {@link ForRef}, and {@link ForChange} instances, in addition to storing
+ * within {@link CurrentUser} using a {@link com.google.gerrit.server.CurrentUser.PropertyKey}.
+ * {@link GlobalPermission} caching for {@link WithUser} may best cached inside {@link CurrentUser}
+ * as {@link WithUser} instances are frequently created.
+ *
* <p>Example use:
*
* <pre>
@@ -126,6 +132,27 @@
public ForChange change(ChangeNotes notes) {
return ref(notes.getChange().getDest()).change(notes);
}
+
+ /** Verify scoped user can {@code perm}, throwing if denied. */
+ public abstract void check(GlobalPermission perm)
+ throws AuthException, PermissionBackendException;
+
+ /** Filter {@code permSet} to permissions scoped user might be able to perform. */
+ public abstract Set<GlobalPermission> test(Collection<GlobalPermission> permSet)
+ throws PermissionBackendException;
+
+ public boolean test(GlobalPermission perm) throws PermissionBackendException {
+ return test(EnumSet.of(perm)).contains(perm);
+ }
+
+ public boolean testOrFalse(GlobalPermission perm) {
+ try {
+ return test(perm);
+ } catch (PermissionBackendException e) {
+ logger.warn("Cannot test " + perm + "; assuming false", e);
+ return false;
+ }
+ }
}
/** PermissionBackend scoped to a user and project. */
@@ -174,6 +201,9 @@
/** PermissionBackend scoped to a user, project, reference and change. */
public abstract static class ForChange extends AcceptsReviewDb<ForChange> {
+ /** @return user this instance is scoped to. */
+ public abstract CurrentUser user();
+
/** @return new instance rescoped to same change, but different {@code user}. */
public abstract ForChange user(CurrentUser user);
@@ -207,5 +237,97 @@
return false;
}
}
+
+ /**
+ * Test which values of a label the user may be able to set.
+ *
+ * @param label definition of the label to test values of.
+ * @return set containing values the user may be able to use; may be empty if none.
+ * @throws PermissionBackendException if failure consulting backend configuration.
+ */
+ public Set<LabelPermission.WithValue> test(LabelType label) throws PermissionBackendException {
+ return test(valuesOf(checkNotNull(label, "LabelType")));
+ }
+
+ /**
+ * Test which values of a group of labels the user may be able to set.
+ *
+ * @param types definition of the labels to test values of.
+ * @return set containing values the user may be able to use; may be empty if none.
+ * @throws PermissionBackendException if failure consulting backend configuration.
+ */
+ public Set<LabelPermission.WithValue> testLabels(Collection<LabelType> types)
+ throws PermissionBackendException {
+ checkNotNull(types, "LabelType");
+ return test(types.stream().flatMap((t) -> valuesOf(t).stream()).collect(toSet()));
+ }
+
+ private static Set<LabelPermission.WithValue> valuesOf(LabelType label) {
+ return label
+ .getValues()
+ .stream()
+ .map((v) -> new LabelPermission.WithValue(label, v))
+ .collect(toSet());
+ }
+
+ /**
+ * Squash a label value to the nearest allowed value.
+ *
+ * <p>For multi-valued labels like Code-Review with values -2..+2 a user may try to use +2, but
+ * only have permission for the -1..+1 range. The caller should have already tried:
+ *
+ * <pre>
+ * check(new LabelPermission.WithValue("Code-Review", 2));
+ * </pre>
+ *
+ * and caught {@link AuthException}. {@code squashThenCheck} will use {@link #test(LabelType)}
+ * to determine potential values of Code-Review the user can use, and select the nearest value
+ * along the same sign, e.g. -1 for -2 and +1 for +2.
+ *
+ * @param label definition of the label to test values of.
+ * @param val previously denied value the user attempted.
+ * @return nearest allowed value, or {@code 0} if no value was allowed.
+ * @throws PermissionBackendException backend cannot run test or check.
+ */
+ public short squashThenCheck(LabelType label, short val) throws PermissionBackendException {
+ short s = squashByTest(label, val);
+ if (s == 0 || s == val) {
+ return 0;
+ }
+ try {
+ check(new LabelPermission.WithValue(label, s));
+ return s;
+ } catch (AuthException e) {
+ return 0;
+ }
+ }
+
+ /**
+ * Squash a label value to the nearest allowed value using only test methods.
+ *
+ * <p>Tests all possible values and selects the closet available to {@code val} while matching
+ * the sign of {@code val}. Unlike {@code #squashThenCheck(LabelType, short)} this method only
+ * uses {@code test} methods and should not be used in contexts like a review handler without
+ * checking the resulting score.
+ *
+ * @param label definition of the label to test values of.
+ * @param val previously denied value the user attempted.
+ * @return nearest likely allowed value, or {@code 0} if no value was identified.
+ * @throws PermissionBackendException backend cannot run test.
+ */
+ public short squashByTest(LabelType label, short val) throws PermissionBackendException {
+ return nearest(test(label), val);
+ }
+
+ private static short nearest(Iterable<LabelPermission.WithValue> possible, short wanted) {
+ short s = 0;
+ for (LabelPermission.WithValue v : possible) {
+ if ((wanted < 0 && v.value() < 0 && wanted <= v.value() && v.value() < s)
+ || (wanted > 0 && v.value() > 0 && wanted >= v.value() && v.value() > s)) {
+ s = v.value();
+ }
+ }
+ return s;
+ }
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
index af89d94..3e5eba3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
@@ -16,6 +16,7 @@
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
+import static com.google.gerrit.server.permissions.LabelPermission.ForUser.ON_BEHALF_OF;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -527,8 +528,13 @@
}
@Override
+ public CurrentUser user() {
+ return getUser();
+ }
+
+ @Override
public ForChange user(CurrentUser user) {
- return getUser().equals(user) ? this : forUser(user).asForChange(cd, db);
+ return user().equals(user) ? this : forUser(user).asForChange(cd, db);
}
@Override
@@ -603,7 +609,11 @@
}
private boolean can(LabelPermission.WithValue perm) {
- return label(perm.permissionName().get()).contains(perm.value());
+ PermissionRange r = label(perm.permissionName().get());
+ if (perm.forUser() == ON_BEHALF_OF && r.isEmpty()) {
+ return false;
+ }
+ return r.contains(perm.value());
}
private PermissionRange label(String permission) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/DefaultPermissionBackend.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/DefaultPermissionBackend.java
index 51fe493..a8f9efa 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/DefaultPermissionBackend.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/DefaultPermissionBackend.java
@@ -16,13 +16,19 @@
import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.permissions.FailedPermissionBackend;
+import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.Set;
@Singleton
class DefaultPermissionBackend extends PermissionBackend {
@@ -57,5 +63,28 @@
return FailedPermissionBackend.project("unavailable", e);
}
}
+
+ @Override
+ public void check(GlobalPermission perm) throws AuthException, PermissionBackendException {
+ if (!can(perm)) {
+ throw new AuthException(perm.describeForException() + " not permitted");
+ }
+ }
+
+ @Override
+ public Set<GlobalPermission> test(Collection<GlobalPermission> permSet)
+ throws PermissionBackendException {
+ EnumSet<GlobalPermission> ok = EnumSet.noneOf(GlobalPermission.class);
+ for (GlobalPermission perm : permSet) {
+ if (can(perm)) {
+ ok.add(perm);
+ }
+ }
+ return ok;
+ }
+
+ private boolean can(GlobalPermission perm) throws PermissionBackendException {
+ return user.getCapabilities().doCanForDefaultPermissionBackend(perm);
+ }
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
index 1dbe5cd..64ef091 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -33,6 +33,7 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.common.data.LabelTypes;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.common.data.SubmitTypeRecord;
import com.google.gerrit.reviewdb.client.Account;
@@ -757,6 +758,10 @@
return change;
}
+ public LabelTypes getLabelTypes() {
+ return changeControl.getLabelTypes();
+ }
+
public ChangeNotes notes() throws OrmException {
if (notes == null) {
if (!lazyLoad) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index 6b66c41..1ae54ee 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -66,6 +66,7 @@
import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.notedb.ReviewerStateInternal;
import com.google.gerrit.server.patch.PatchListCache;
+import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.ListChildProjects;
import com.google.gerrit.server.project.ProjectCache;
@@ -182,6 +183,7 @@
final AccountResolver accountResolver;
final AllProjectsName allProjectsName;
final AllUsersName allUsersName;
+ final PermissionBackend permissionBackend;
final CapabilityControl.Factory capabilityControlFactory;
final ChangeControl.GenericFactory changeControlGenericFactory;
final ChangeData.Factory changeDataFactory;
@@ -221,6 +223,7 @@
DynamicMap<ChangeHasOperandFactory> hasOperands,
IdentifiedUser.GenericFactory userFactory,
Provider<CurrentUser> self,
+ PermissionBackend permissionBackend,
CapabilityControl.Factory capabilityControlFactory,
ChangeControl.GenericFactory changeControlGenericFactory,
ChangeNotes.Factory notesFactory,
@@ -253,6 +256,7 @@
hasOperands,
userFactory,
self,
+ permissionBackend,
capabilityControlFactory,
changeControlGenericFactory,
notesFactory,
@@ -287,6 +291,7 @@
DynamicMap<ChangeHasOperandFactory> hasOperands,
IdentifiedUser.GenericFactory userFactory,
Provider<CurrentUser> self,
+ PermissionBackend permissionBackend,
CapabilityControl.Factory capabilityControlFactory,
ChangeControl.GenericFactory changeControlGenericFactory,
ChangeNotes.Factory notesFactory,
@@ -317,6 +322,7 @@
this.opFactories = opFactories;
this.userFactory = userFactory;
this.self = self;
+ this.permissionBackend = permissionBackend;
this.capabilityControlFactory = capabilityControlFactory;
this.notesFactory = notesFactory;
this.changeControlGenericFactory = changeControlGenericFactory;
@@ -353,6 +359,7 @@
hasOperands,
userFactory,
Providers.of(otherUser),
+ permissionBackend,
capabilityControlFactory,
changeControlGenericFactory,
notesFactory,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
index bb251cb..a5814fe 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
@@ -16,7 +16,6 @@
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
-import com.google.gerrit.common.data.Permission;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Change;
@@ -24,8 +23,9 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.permissions.ChangePermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.gwtorm.server.OrmException;
@@ -33,7 +33,7 @@
class EqualsLabelPredicate extends ChangeIndexPredicate {
private final ProjectCache projectCache;
- private final ChangeControl.GenericFactory ccFactory;
+ private final PermissionBackend permissionBackend;
private final IdentifiedUser.GenericFactory userFactory;
private final Provider<ReviewDb> dbProvider;
private final String label;
@@ -43,7 +43,7 @@
EqualsLabelPredicate(LabelPredicate.Args args, String label, int expVal, Account.Id account) {
super(ChangeField.LABEL, ChangeField.formatLabel(label, expVal, account));
- this.ccFactory = args.ccFactory;
+ this.permissionBackend = args.permissionBackend;
this.projectCache = args.projectCache;
this.userFactory = args.userFactory;
this.dbProvider = args.dbProvider;
@@ -78,7 +78,7 @@
for (PatchSetApproval p : object.currentApprovals()) {
if (labelType.matches(p)) {
hasVote = true;
- if (match(c, p.getValue(), p.getAccountId(), labelType)) {
+ if (match(object, p.getValue(), p.getAccountId(), labelType)) {
return true;
}
}
@@ -104,40 +104,28 @@
return null;
}
- private boolean match(Change change, int value, Account.Id approver, LabelType type)
- throws OrmException {
- int psVal = value;
- if (psVal == expVal) {
- // Double check the value is still permitted for the user.
- //
- IdentifiedUser reviewer = userFactory.create(approver);
- try {
- ChangeControl cc = ccFactory.controlFor(dbProvider.get(), change, reviewer);
- if (!cc.isVisible(dbProvider.get())) {
- // The user can't see the change anymore.
- //
- return false;
- }
- psVal = cc.getRange(Permission.forLabel(type.getName())).squash(psVal);
- } catch (NoSuchChangeException e) {
- // The project has disappeared.
- //
- return false;
- }
-
- if (account != null && !account.equals(approver)) {
- return false;
- }
-
- if (group != null && !reviewer.getEffectiveGroups().contains(group)) {
- return false;
- }
-
- if (psVal == expVal) {
- return true;
- }
+ private boolean match(ChangeData cd, short value, Account.Id approver, LabelType type) {
+ if (value != expVal) {
+ return false;
}
- return false;
+
+ if (account != null && !account.equals(approver)) {
+ return false;
+ }
+
+ IdentifiedUser reviewer = userFactory.create(approver);
+ if (group != null && !reviewer.getEffectiveGroups().contains(group)) {
+ return false;
+ }
+
+ // Double check the value is still permitted for the user.
+ try {
+ PermissionBackend.ForChange perm =
+ permissionBackend.user(reviewer).database(dbProvider).change(cd);
+ return perm.test(ChangePermission.READ) && expVal == perm.squashByTest(type, value);
+ } catch (PermissionBackendException e) {
+ return false;
+ }
}
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LabelPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LabelPredicate.java
index 9fdbcef..3fe6a6f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LabelPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LabelPredicate.java
@@ -19,6 +19,7 @@
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.query.OrPredicate;
@@ -36,6 +37,7 @@
static class Args {
final ProjectCache projectCache;
+ final PermissionBackend permissionBackend;
final ChangeControl.GenericFactory ccFactory;
final IdentifiedUser.GenericFactory userFactory;
final Provider<ReviewDb> dbProvider;
@@ -45,6 +47,7 @@
private Args(
ProjectCache projectCache,
+ PermissionBackend permissionBackend,
ChangeControl.GenericFactory ccFactory,
IdentifiedUser.GenericFactory userFactory,
Provider<ReviewDb> dbProvider,
@@ -52,6 +55,7 @@
Set<Account.Id> accounts,
AccountGroup.UUID group) {
this.projectCache = projectCache;
+ this.permissionBackend = permissionBackend;
this.ccFactory = ccFactory;
this.userFactory = userFactory;
this.dbProvider = dbProvider;
@@ -84,6 +88,7 @@
predicates(
new Args(
a.projectCache,
+ a.permissionBackend,
a.changeControlGenericFactory,
a.userFactory,
a.db,
diff --git a/gerrit-server/src/main/java/gerrit/PRED__check_user_label_3.java b/gerrit-server/src/main/java/gerrit/PRED__check_user_label_3.java
new file mode 100644
index 0000000..b2b9890
--- /dev/null
+++ b/gerrit-server/src/main/java/gerrit/PRED__check_user_label_3.java
@@ -0,0 +1,104 @@
+// 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 gerrit;
+
+import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.rules.StoredValues;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.permissions.LabelPermission;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.googlecode.prolog_cafe.exceptions.IllegalTypeException;
+import com.googlecode.prolog_cafe.exceptions.PInstantiationException;
+import com.googlecode.prolog_cafe.exceptions.PrologException;
+import com.googlecode.prolog_cafe.exceptions.SystemException;
+import com.googlecode.prolog_cafe.lang.IntegerTerm;
+import com.googlecode.prolog_cafe.lang.JavaObjectTerm;
+import com.googlecode.prolog_cafe.lang.Operation;
+import com.googlecode.prolog_cafe.lang.Predicate;
+import com.googlecode.prolog_cafe.lang.Prolog;
+import com.googlecode.prolog_cafe.lang.SymbolTerm;
+import com.googlecode.prolog_cafe.lang.Term;
+import com.googlecode.prolog_cafe.lang.VariableTerm;
+
+/**
+ * Checks user can set label to val.
+ *
+ * <pre>
+ * '_check_user_label'(+Label, +CurrentUser, +Val)
+ * </pre>
+ */
+class PRED__check_user_label_3 extends Predicate.P3 {
+ PRED__check_user_label_3(Term a1, Term a2, Term a3, Operation n) {
+ arg1 = a1;
+ arg2 = a2;
+ arg3 = a3;
+ cont = n;
+ }
+
+ @Override
+ public Operation exec(Prolog engine) throws PrologException {
+ engine.setB0();
+ Term a1 = arg1.dereference();
+ Term a2 = arg2.dereference();
+ Term a3 = arg3.dereference();
+
+ if (a1 instanceof VariableTerm) {
+ throw new PInstantiationException(this, 1);
+ }
+ if (!(a1 instanceof SymbolTerm)) {
+ throw new IllegalTypeException(this, 1, "atom", a1);
+ }
+ String label = a1.name();
+
+ if (a2 instanceof VariableTerm) {
+ throw new PInstantiationException(this, 2);
+ }
+ if (!(a2 instanceof JavaObjectTerm) || !a2.convertible(CurrentUser.class)) {
+ throw new IllegalTypeException(this, 2, "CurrentUser)", a2);
+ }
+ CurrentUser user = (CurrentUser) ((JavaObjectTerm) a2).object();
+
+ if (a3 instanceof VariableTerm) {
+ throw new PInstantiationException(this, 3);
+ }
+ if (!(a3 instanceof IntegerTerm)) {
+ throw new IllegalTypeException(this, 3, "integer", a3);
+ }
+ short val = (short) ((IntegerTerm) a3).intValue();
+
+ ChangeData cd = StoredValues.CHANGE_DATA.get(engine);
+ LabelType type = cd.getLabelTypes().byLabel(label);
+ if (type == null) {
+ return engine.fail();
+ }
+
+ try {
+ StoredValues.PERMISSION_BACKEND
+ .get(engine)
+ .user(user)
+ .change(cd)
+ .check(new LabelPermission.WithValue(type, val));
+ return cont;
+ } catch (AuthException err) {
+ return engine.fail();
+ } catch (PermissionBackendException err) {
+ SystemException se = new SystemException(err.getMessage());
+ se.initCause(err);
+ throw se;
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/gerrit/PRED__load_commit_labels_1.java b/gerrit-server/src/main/java/gerrit/PRED__load_commit_labels_1.java
index 8b5a33d..5a3d656 100644
--- a/gerrit-server/src/main/java/gerrit/PRED__load_commit_labels_1.java
+++ b/gerrit-server/src/main/java/gerrit/PRED__load_commit_labels_1.java
@@ -38,7 +38,7 @@
Term listHead = Prolog.Nil;
try {
ChangeData cd = StoredValues.CHANGE_DATA.get(engine);
- LabelTypes types = StoredValues.CHANGE_CONTROL.get(engine).getLabelTypes();
+ LabelTypes types = cd.getLabelTypes();
for (PatchSetApproval a : cd.currentApprovals()) {
LabelType t = types.byLabel(a.getLabelId());
diff --git a/gerrit-server/src/main/java/gerrit/PRED__user_label_range_4.java b/gerrit-server/src/main/java/gerrit/PRED__user_label_range_4.java
index d06664e..5c61007 100644
--- a/gerrit-server/src/main/java/gerrit/PRED__user_label_range_4.java
+++ b/gerrit-server/src/main/java/gerrit/PRED__user_label_range_4.java
@@ -14,14 +14,16 @@
package gerrit;
-import com.google.gerrit.common.data.Permission;
-import com.google.gerrit.common.data.PermissionRange;
+import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.rules.StoredValues;
import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.permissions.LabelPermission;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.query.change.ChangeData;
import com.googlecode.prolog_cafe.exceptions.IllegalTypeException;
import com.googlecode.prolog_cafe.exceptions.PInstantiationException;
import com.googlecode.prolog_cafe.exceptions.PrologException;
+import com.googlecode.prolog_cafe.exceptions.SystemException;
import com.googlecode.prolog_cafe.lang.IntegerTerm;
import com.googlecode.prolog_cafe.lang.JavaObjectTerm;
import com.googlecode.prolog_cafe.lang.Operation;
@@ -30,12 +32,13 @@
import com.googlecode.prolog_cafe.lang.SymbolTerm;
import com.googlecode.prolog_cafe.lang.Term;
import com.googlecode.prolog_cafe.lang.VariableTerm;
+import java.util.Set;
/**
* Resolves the valid range for a label on a CurrentUser.
*
* <pre>
- * '$user_label_range'(+Label, +CurrentUser, -Min, -Max)
+ * '_user_label_range'(+Label, +CurrentUser, -Min, -Max)
* </pre>
*/
class PRED__user_label_range_4 extends Predicate.P4 {
@@ -71,20 +74,33 @@
}
CurrentUser user = (CurrentUser) ((JavaObjectTerm) a2).object();
- ChangeControl ctl = StoredValues.CHANGE_CONTROL.get(engine).forUser(user);
- PermissionRange range = ctl.getRange(Permission.LABEL + label);
- if (range == null) {
+ ChangeData cd = StoredValues.CHANGE_DATA.get(engine);
+ LabelType type = cd.getLabelTypes().byLabel(label);
+ if (type == null) {
return engine.fail();
}
- IntegerTerm min = new IntegerTerm(range.getMin());
- IntegerTerm max = new IntegerTerm(range.getMax());
+ Set<LabelPermission.WithValue> can;
+ try {
+ can = StoredValues.PERMISSION_BACKEND.get(engine).user(user).change(cd).test(type);
+ } catch (PermissionBackendException err) {
+ SystemException se = new SystemException(err.getMessage());
+ se.initCause(err);
+ throw se;
+ }
- if (!a3.unify(min, engine.trail)) {
+ int min = 0;
+ int max = 0;
+ for (LabelPermission.WithValue v : can) {
+ min = Math.min(min, v.value());
+ max = Math.max(max, v.value());
+ }
+
+ if (!a3.unify(new IntegerTerm(min), engine.trail)) {
return engine.fail();
}
- if (!a4.unify(max, engine.trail)) {
+ if (!a4.unify(new IntegerTerm(max), engine.trail)) {
return engine.fail();
}
diff --git a/gerrit-server/src/main/java/gerrit/PRED_get_legacy_label_types_1.java b/gerrit-server/src/main/java/gerrit/PRED_get_legacy_label_types_1.java
index ea3fb17..33d63c4 100644
--- a/gerrit-server/src/main/java/gerrit/PRED_get_legacy_label_types_1.java
+++ b/gerrit-server/src/main/java/gerrit/PRED_get_legacy_label_types_1.java
@@ -51,7 +51,7 @@
public Operation exec(Prolog engine) throws PrologException {
engine.setB0();
Term a1 = arg1.dereference();
- List<LabelType> list = StoredValues.CHANGE_CONTROL.get(engine).getLabelTypes().getLabelTypes();
+ List<LabelType> list = StoredValues.CHANGE_DATA.get(engine).getLabelTypes().getLabelTypes();
Term head = Prolog.Nil;
for (int idx = list.size() - 1; 0 <= idx; idx--) {
head = new ListTerm(export(list.get(idx)), head);
diff --git a/gerrit-server/src/main/prolog/gerrit_common.pl b/gerrit-server/src/main/prolog/gerrit_common.pl
index 59c926f..4671e0d 100644
--- a/gerrit-server/src/main/prolog/gerrit_common.pl
+++ b/gerrit-server/src/main/prolog/gerrit_common.pl
@@ -92,6 +92,27 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
+%% check_user_label/3:
+%%
+%% Check Who can set Label to Val.
+%%
+check_user_label(Label, Who, Val) :-
+ hash_get(commit_labels, '$fast_range', true), !,
+ atom(Label),
+ assume_range_from_label(Label, Who, Min, Max),
+ Min @=< Val, Val @=< Max.
+check_user_label(Label, Who, Val) :-
+ Who = user(_), !,
+ atom(Label),
+ current_user(Who, User),
+ '_check_user_label'(Label, User, Val).
+check_user_label(Label, test_user(Name), Val) :-
+ clause(user:test_grant(Label, test_user(Name), range(Min, Max)), _),
+ Min @=< Val, Val @=< Max
+ .
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
%% user_label_range/4:
%%
%% Lookup the range allowed to be used.
@@ -319,8 +340,7 @@
%%
check_label_range_permission(Label, ExpValue, ok(Who)) :-
commit_label(label(Label, ExpValue), Who),
- user_label_range(Label, Who, Min, Max),
- Min @=< ExpValue, ExpValue @=< Max
+ check_user_label(Label, Who, ExpValue)
.
%TODO Uncomment this clause when group suggesting is possible.
%check_label_range_permission(Label, ExpValue, ask(Group)) :-
diff --git a/gerrit-server/src/test/java/com/google/gerrit/rules/GerritCommonTest.java b/gerrit-server/src/test/java/com/google/gerrit/rules/GerritCommonTest.java
index fa4a951..3dfcaec 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/rules/GerritCommonTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/rules/GerritCommonTest.java
@@ -17,8 +17,8 @@
import static org.easymock.EasyMock.expect;
import com.google.gerrit.common.data.LabelTypes;
-import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.Util;
+import com.google.gerrit.server.query.change.ChangeData;
import com.google.inject.AbstractModule;
import com.googlecode.prolog_cafe.exceptions.CompileException;
import com.googlecode.prolog_cafe.exceptions.ReductionLimitException;
@@ -47,7 +47,8 @@
cfg.setInt("rules", null, "reductionLimit", 1300);
cfg.setInt("rules", null, "compileReductionLimit", (int) 1e6);
bind(PrologEnvironment.Args.class)
- .toInstance(new PrologEnvironment.Args(null, null, null, null, null, null, cfg));
+ .toInstance(
+ new PrologEnvironment.Args(null, null, null, null, null, null, null, cfg));
}
});
}
@@ -55,10 +56,10 @@
@Override
protected void setUpEnvironment(PrologEnvironment env) {
LabelTypes labelTypes = new LabelTypes(Arrays.asList(Util.codeReview(), Util.verified()));
- ChangeControl ctl = EasyMock.createMock(ChangeControl.class);
- expect(ctl.getLabelTypes()).andStubReturn(labelTypes);
- EasyMock.replay(ctl);
- env.set(StoredValues.CHANGE_CONTROL, ctl);
+ ChangeData cd = EasyMock.createMock(ChangeData.class);
+ expect(cd.getLabelTypes()).andStubReturn(labelTypes);
+ EasyMock.replay(cd);
+ env.set(StoredValues.CHANGE_DATA, cd);
}
@Test
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/index/change/FakeQueryBuilder.java b/gerrit-server/src/test/java/com/google/gerrit/server/index/change/FakeQueryBuilder.java
index 6fda100..3bbd335 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/index/change/FakeQueryBuilder.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/index/change/FakeQueryBuilder.java
@@ -27,8 +27,8 @@
new FakeQueryBuilder.Definition<>(FakeQueryBuilder.class),
new ChangeQueryBuilder.Arguments(
null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, indexes, null, null, null, null, null, null,
- null, null, null));
+ null, null, null, null, null, null, null, null, indexes, null, null, null, null, null,
+ null, null, null, null));
}
@Operator
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SuExec.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SuExec.java
index 53a98eb..54371c1 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SuExec.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SuExec.java
@@ -18,11 +18,15 @@
import com.google.common.base.Throwables;
import com.google.common.util.concurrent.Atomics;
+import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PeerDaemonUser;
import com.google.gerrit.server.config.AuthConfig;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.sshd.SshScope.Context;
import com.google.inject.Inject;
import java.io.IOException;
@@ -45,6 +49,7 @@
public final class SuExec extends BaseCommand {
private final SshScope sshScope;
private final DispatchCommandProvider dispatcher;
+ private final PermissionBackend permissionBackend;
private boolean enableRunAs;
private CurrentUser caller;
@@ -67,6 +72,7 @@
SuExec(
final SshScope sshScope,
@CommandName(Commands.ROOT) final DispatchCommandProvider dispatcher,
+ PermissionBackend permissionBackend,
final CurrentUser caller,
final SshSession session,
final IdentifiedUser.GenericFactory userFactory,
@@ -74,6 +80,7 @@
AuthConfig config) {
this.sshScope = sshScope;
this.dispatcher = dispatcher;
+ this.permissionBackend = permissionBackend;
this.caller = caller;
this.session = session;
this.userFactory = userFactory;
@@ -115,8 +122,14 @@
// OK.
} else if (!enableRunAs) {
throw die("suexec disabled by auth.enableRunAs = false");
- } else if (!caller.getCapabilities().canRunAs()) {
- throw die("suexec not permitted");
+ } else {
+ try {
+ permissionBackend.user(caller).check(GlobalPermission.RUN_AS);
+ } catch (AuthException e) {
+ throw die("suexec not permitted");
+ } catch (PermissionBackendException e) {
+ throw die("suexec not available: " + e);
+ }
}
}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminQueryShell.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminQueryShell.java
index 42c7578..ef1cd81 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminQueryShell.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminQueryShell.java
@@ -15,9 +15,12 @@
package com.google.gerrit.sshd.commands;
import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.common.errors.PermissionDeniedException;
import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.sshd.AdminHighPriorityCommand;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
@@ -29,8 +32,8 @@
@RequiresCapability(GlobalCapability.ACCESS_DATABASE)
@CommandMetaData(name = "gsql", description = "Administrative interface to active database")
final class AdminQueryShell extends SshCommand {
+ @Inject private PermissionBackend permissionBackend;
@Inject private QueryShell.Factory factory;
-
@Inject private IdentifiedUser currentUser;
@Option(name = "--format", usage = "Set output format")
@@ -42,9 +45,11 @@
@Override
protected void run() throws Failure {
try {
- checkPermission();
- } catch (PermissionDeniedException err) {
+ permissionBackend.user(currentUser).check(GlobalPermission.ACCESS_DATABASE);
+ } catch (AuthException err) {
throw die(err.getMessage());
+ } catch (PermissionBackendException e) {
+ throw new Failure(1, "unavailable", e);
}
QueryShell shell = factory.create(in, out);
@@ -55,22 +60,4 @@
shell.run();
}
}
-
- /**
- * Assert that the current user is permitted to perform raw queries.
- *
- * <p>As the @RequireCapability guards at various entry points of internal commands implicitly add
- * administrators (which we want to avoid), we also check permissions within QueryShell and grant
- * access only to those who can access the database, regardless of whether they are administrators
- * or not.
- *
- * @throws PermissionDeniedException
- */
- private void checkPermission() throws PermissionDeniedException {
- if (!currentUser.getCapabilities().canAccessDatabase()) {
- throw new PermissionDeniedException(
- String.format(
- "%s does not have \"Access Database\" capability.", currentUser.getUserName()));
- }
- }
}
diff --git a/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior.html b/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior.html
new file mode 100644
index 0000000..2ec8538
--- /dev/null
+++ b/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior.html
@@ -0,0 +1,37 @@
+<!--
+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.
+-->
+
+<script>
+(function(window) {
+ 'use strict';
+
+ /** @polymerBehavior Gerrit.BaseUrlBehavior */
+ var BaseUrlBehavior = {
+ getBaseUrl: function() {
+ return window.CANONICAL_PATH || '';
+ },
+
+ computeGwtUrl: function(path) {
+ var base = this.getBaseUrl();
+ var clientPath = path.substring(base.length);
+ return base + '/?polygerrit=0#' + clientPath;
+ },
+ };
+
+ window.Gerrit = window.Gerrit || {};
+ window.Gerrit.BaseUrlBehavior = BaseUrlBehavior;
+})(window);
+</script>
diff --git a/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html b/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html
new file mode 100644
index 0000000..1e277bc
--- /dev/null
+++ b/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<!--
+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.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>keyboard-shortcut-behavior</title>
+
+<script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
+<script src="../../bower_components/web-component-tester/browser.js"></script>
+
+<link rel="import" href="../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<script>
+ window.CANONICAL_PATH = '/r';
+</script>
+<link rel="import" href="base-url-behavior.html">
+
+<test-fixture id="basic">
+ <template>
+ <test-element></test-element>
+ </template>
+</test-fixture>
+
+<test-fixture id="within-overlay">
+ <template>
+ <gr-overlay>
+ <test-element></test-element>
+ </gr-overlay>
+ </template>
+</test-fixture>
+
+<script>
+ suite('base-url-behavior tests', function() {
+ var element;
+ var overlay;
+
+ suiteSetup(function() {
+ // Define a Polymer element that uses this behavior.
+ Polymer({
+ is: 'test-element',
+ behaviors: [
+ Gerrit.BaseUrlBehavior,
+ ],
+ });
+ });
+
+ setup(function() {
+ element = fixture('basic');
+ overlay = fixture('within-overlay');
+ });
+
+ test('getBaseUrl', function() {
+ assert.deepEqual(element.getBaseUrl(), '/r');
+ });
+
+ test('computeGwtUrl', function() {
+ assert.deepEqual(
+ element.computeGwtUrl('/r/c/1/'),
+ '/r/?polygerrit=0#/c/1/'
+ );
+ });
+ });
+</script>
\ No newline at end of file
diff --git a/polygerrit-ui/app/behaviors/rest-client-behavior.html b/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior.html
similarity index 90%
rename from polygerrit-ui/app/behaviors/rest-client-behavior.html
rename to polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior.html
index b7cf467..f71fe8f 100644
--- a/polygerrit-ui/app/behaviors/rest-client-behavior.html
+++ b/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior.html
@@ -13,7 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../bower_components/polymer/polymer.html">
+<link rel="import" href="../../bower_components/polymer/polymer.html">
+<link rel="import" href="../base-url-behavior/base-url-behavior.html">
<script>
(function(window) {
'use strict';
@@ -99,7 +100,7 @@
},
changeBaseURL: function(changeNum, patchNum) {
- var v = '/changes/' + changeNum;
+ var v = this.getBaseUrl() + '/changes/' + changeNum;
if (patchNum) {
v += '/revisions/' + patchNum;
}
@@ -107,7 +108,7 @@
},
changePath: function(changeNum) {
- return '/c/' + changeNum;
+ return this.getBaseUrl() + '/c/' + changeNum;
},
changeIsOpen: function(status) {
@@ -134,6 +135,9 @@
};
window.Gerrit = window.Gerrit || {};
- window.Gerrit.RESTClientBehavior = RESTClientBehavior;
+ window.Gerrit.RESTClientBehavior = [
+ Gerrit.BaseUrlBehavior,
+ RESTClientBehavior
+ ];
})(window);
</script>
diff --git a/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html b/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html
new file mode 100644
index 0000000..2b3e858
--- /dev/null
+++ b/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<!--
+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.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>keyboard-shortcut-behavior</title>
+
+<script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
+<script src="../../bower_components/web-component-tester/browser.js"></script>
+<script>
+ window.CANONICAL_PATH = '/r';
+</script>
+
+<link rel="import" href="../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../base-url-behavior/base-url-behavior.html">
+<link rel="import" href="rest-client-behavior.html">
+
+<test-fixture id="basic">
+ <template>
+ <test-element></test-element>
+ </template>
+</test-fixture>
+
+<test-fixture id="within-overlay">
+ <template>
+ <gr-overlay>
+ <test-element></test-element>
+ </gr-overlay>
+ </template>
+</test-fixture>
+
+<script>
+ suite('rest-client-behavior tests', function() {
+ var element;
+ var overlay;
+
+ suiteSetup(function() {
+ // Define a Polymer element that uses this behavior.
+ Polymer({
+ is: 'test-element',
+ behaviors: [
+ Gerrit.BaseUrlBehavior,
+ Gerrit.RESTClientBehavior,
+ ],
+ });
+ });
+
+ setup(function() {
+ element = fixture('basic');
+ overlay = fixture('within-overlay');
+ });
+
+ test('changeBaseURL', function() {
+ assert.deepEqual(
+ element.changeBaseURL('1', '1'),
+ '/r/changes/1/revisions/1'
+ );
+ });
+
+ test('changePath', function() {
+ assert.deepEqual(element.changePath('1'), '/r/c/1');
+ });
+ });
+</script>
\ No newline at end of file
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html
index 8e2b192..d50e0b3 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html
@@ -13,9 +13,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../behaviors/gr-change-table-behavior/gr-change-table-behavior.html">
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior.html">
-<link rel="import" href="../../../behaviors/rest-client-behavior.html">
+<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../styles/gr-change-list-styles.html">
<link rel="import" href="../../shared/gr-account-link/gr-account-link.html">
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js
index 280de86..566dfe0 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js
@@ -34,6 +34,7 @@
},
behaviors: [
+ Gerrit.BaseUrlBehavior,
Gerrit.ChangeTableBehavior,
Gerrit.RESTClientBehavior,
Gerrit.URLEncodingBehavior,
@@ -41,7 +42,7 @@
_computeChangeURL: function(changeNum) {
if (!changeNum) { return ''; }
- return '/c/' + changeNum + '/';
+ return this.getBaseUrl() + '/c/' + changeNum + '/';
},
_computeLabelTitle: function(change, labelName) {
@@ -101,7 +102,7 @@
},
_computeProjectURL: function(project) {
- return '/q/status:open+project:' +
+ return this.getBaseUrl() + '/q/status:open+project:' +
this.encodeURL(project, false);
},
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.html b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.html
index c9a8d64..8a95fa8 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.html
@@ -14,10 +14,10 @@
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/gr-change-table-behavior/gr-change-table-behavior.html">
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
-<link rel="import" href="../../../behaviors/rest-client-behavior.html">
+<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
+<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../styles/gr-change-list-styles.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../gr-change-list-item/gr-change-list-item.html">
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
index 05306ae..71ccb04 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
@@ -14,9 +14,9 @@
limitations under the License.
-->
+<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
-<link rel="import" href="../../../behaviors/rest-client-behavior.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../../shared/gr-dropdown/gr-dropdown.html">
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html
index 5f11219..4ecd4b7 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html
@@ -14,9 +14,10 @@
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../behaviors/rest-client-behavior.html">
+<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior.html">
+<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
+<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-account-chip/gr-account-chip.html">
<link rel="import" href="../../shared/gr-label/gr-label.html">
<link rel="import" href="../../shared/gr-date-formatter/gr-date-formatter.html">
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
index 6caed12..834069e 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
@@ -47,6 +47,7 @@
},
behaviors: [
+ Gerrit.BaseUrlBehavior,
Gerrit.RESTClientBehavior,
Gerrit.URLEncodingBehavior,
],
@@ -234,7 +235,8 @@
},
_computeProjectURL: function(project) {
- return '/q/status:open+project:' + this.encodeURL(project, false);
+ return this.getBaseUrl() + '/q/status:open+project:' +
+ this.encodeURL(project, false);
},
_computeBranchURL: function(project, branch) {
@@ -244,15 +246,16 @@
} else {
status = this.change.status.toLowerCase();
}
- return '/q/project:' + this.encodeURL(project, false) +
+ return this.getBaseUrl() + '/q/project:' +
+ this.encodeURL(project, false) +
' branch:' + this.encodeURL(branch, false) +
- ' status:' + this.encodeURL(status, false);
+ ' status:' + this.encodeURL(status, false);
},
_computeTopicHref: function(topic) {
var encodedTopic = encodeURIComponent('\"' + topic + '\"');
- return '/q/topic:' + encodeURIComponent(encodedTopic) +
- '+(status:open OR status:merged)';
+ return this.getBaseUrl() + '/q/topic:' +
+ encodeURIComponent(encodedTopic) + '+(status:open OR status:merged)';
},
_handleTopicRemoved: function() {
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
index 22f742d..cec9487 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
@@ -14,10 +14,11 @@
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
-<link rel="import" href="../../../behaviors/rest-client-behavior.html">
+<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
+<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-account-link/gr-account-link.html">
<link rel="import" href="../../shared/gr-select/gr-select.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
index 8a317ab..8ce7a83 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
@@ -162,6 +162,7 @@
},
behaviors: [
+ Gerrit.BaseUrlBehavior,
Gerrit.KeyboardShortcutBehavior,
Gerrit.PatchSetBehavior,
Gerrit.RESTClientBehavior,
@@ -588,7 +589,7 @@
},
_computeChangePermalink: function(changeNum) {
- return '/' + changeNum;
+ return this.getBaseUrl() + '/' + changeNum;
},
_computeChangeStatus: function(change, patchNum) {
diff --git a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.html b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.html
index 13daffd..0e640a7 100644
--- a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.html
+++ b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.html
@@ -14,6 +14,7 @@
limitations under the License.
-->
+<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../behaviors/gr-path-list-behavior/gr-path-list-behavior.html">
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-formatted-text/gr-formatted-text.html">
diff --git a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.js b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.js
index ecd4dcc..f1fb0fd 100644
--- a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.js
+++ b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.js
@@ -16,7 +16,11 @@
Polymer({
is: 'gr-comment-list',
- behaviors: [Gerrit.PathListBehavior],
+
+ behaviors: [
+ Gerrit.BaseUrlBehavior,
+ Gerrit.PathListBehavior,
+ ],
properties: {
changeNum: Number,
@@ -31,7 +35,8 @@
},
_computeFileDiffURL: function(file, changeNum, patchNum) {
- return '/c/' + changeNum + '/' + patchNum + '/' + file;
+ return this.getBaseUrl() + '/c/' + changeNum +
+ '/' + patchNum + '/' + file;
},
_isOnParent: function(comment) {
diff --git a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.html b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.html
index bcd9053..0e97d36 100644
--- a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.html
@@ -14,9 +14,9 @@
limitations under the License.
-->
+<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
-<link rel="import" href="../../../behaviors/rest-client-behavior.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
index 75d86f4..b20c82c 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
@@ -14,6 +14,7 @@
limitations under the License.
-->
+<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior.html">
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
index d06a695..73dd969 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
@@ -111,6 +111,7 @@
},
behaviors: [
+ Gerrit.BaseUrlBehavior,
Gerrit.KeyboardShortcutBehavior,
Gerrit.PatchSetBehavior,
Gerrit.URLEncodingBehavior,
@@ -604,7 +605,7 @@
},
_computeDiffURL: function(changeNum, patchRange, path) {
- return this.encodeURL('/c/' + changeNum + '/' +
+ return this.encodeURL(this.getBaseUrl() + '/c/' + changeNum + '/' +
this._patchRangeStr(patchRange) + '/' + path, true);
},
diff --git a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.html b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.html
index e7d7cf9..c9d5cd8 100644
--- a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.html
+++ b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.html
@@ -14,8 +14,9 @@
limitations under the License.
-->
+<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
+<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../behaviors/rest-client-behavior.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<dom-module id="gr-related-changes-list">
diff --git a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js
index 5a9642e..8501b20 100644
--- a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js
+++ b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js
@@ -62,6 +62,7 @@
},
behaviors: [
+ Gerrit.BaseUrlBehavior,
Gerrit.RESTClientBehavior,
],
@@ -160,7 +161,7 @@
},
_computeChangeURL: function(changeNum, patchNum) {
- var urlStr = '/c/' + changeNum;
+ var urlStr = this.getBaseUrl() + '/c/' + changeNum;
if (patchNum != null) {
urlStr += '/' + patchNum;
}
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
index f9ec01c..842de22 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
@@ -15,7 +15,7 @@
-->
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
-<link rel="import" href="../../../behaviors/rest-client-behavior.html">
+<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
<link rel="import" href="../../../bower_components/iron-selector/iron-selector.html">
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
index dc66bb6..1e6596b 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
@@ -14,6 +14,7 @@
limitations under the License.
-->
+<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
index c9abac2..487074e 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
@@ -96,6 +96,10 @@
},
},
+ behaviors: [
+ Gerrit.BaseUrlBehavior,
+ ],
+
observers: [
'_accountLoaded(_account)',
],
@@ -114,14 +118,23 @@
},
_handleLocationChange: function(e) {
- this._loginURL = '/login/' + encodeURIComponent(
- window.location.pathname +
- window.location.search +
- window.location.hash);
+ if (this.getBaseUrl()) {
+ // Strip the canonical path from the path since needing canonical in
+ // the path is uneeded and breaks the url.
+ this._loginURL = this.getBaseUrl() + '/login/' + encodeURIComponent(
+ '/' + window.location.pathname.substring(this.getBaseUrl().length) +
+ window.location.search +
+ window.location.hash);
+ } else {
+ this._loginURL = '/login/' + encodeURIComponent(
+ window.location.pathname +
+ window.location.search +
+ window.location.hash);
+ }
},
_computeRelativeURL: function(path) {
- return '//' + window.location.host + path;
+ return '//' + window.location.host + this.getBaseUrl() + path;
},
_computeLinks: function(defaultLinks, userLinks, adminLinks) {
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html
index d10567c..d5d33ab 100644
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html
+++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html
@@ -14,6 +14,7 @@
limitations under the License.
-->
+<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
index 780f3b9..1f96014 100644
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
+++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
@@ -79,6 +79,10 @@
},
},
+ behaviors: [
+ Gerrit.BaseUrlBehavior,
+ ],
+
get performanceTiming() {
return window.performance.timing;
},
@@ -154,11 +158,11 @@
var page = '';
var pathname = this._getPathname();
if (pathname.indexOf('/q/') === 0) {
- page = '/q/';
+ page = this.getBaseUrl() + '/q/';
} else if (pathname.match(CHANGE_VIEW_REGEX)) { // change view
- page = '/c/';
+ page = this.getBaseUrl() + '/c/';
} else if (pathname.match(DIFF_VIEW_REGEX)) { // diff view
- page = '/c//COMMIT_MSG';
+ page = this.getBaseUrl() + '/c//COMMIT_MSG';
} else {
// Ignore other page changes.
return;
@@ -172,7 +176,7 @@
},
_getPathname: function() {
- return window.location.pathname;
+ return '/' + window.location.pathname.substring(this.getBaseUrl().length);
},
/**
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.html b/polygerrit-ui/app/elements/core/gr-router/gr-router.html
index bd79419..5a494b1 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.html
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.html
@@ -13,6 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../gr-reporting/gr-reporting.html">
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.js b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
index eb650ca..1e3481f 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
@@ -33,6 +33,11 @@
getReporting().pageLoaded();
};
+ var base = window.Gerrit.BaseUrlBehavior.getBaseUrl();
+ if (base) {
+ page.base(base);
+ }
+
window.addEventListener('WebComponentsReady', function() {
getReporting().timeEnd('WebComponentsReady');
});
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html
index 9ec52d2..1204170 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html
@@ -14,11 +14,12 @@
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/iron-dropdown/iron-dropdown.html">
+<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior.html">
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
-<link rel="import" href="../../../behaviors/rest-client-behavior.html">
+<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
+<link rel="import" href="../../../bower_components/iron-dropdown/iron-dropdown.html">
+<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
index b795d64..fcbbe23 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
@@ -106,6 +106,7 @@
},
behaviors: [
+ Gerrit.BaseUrlBehavior,
Gerrit.KeyboardShortcutBehavior,
Gerrit.RESTClientBehavior,
Gerrit.URLEncodingBehavior,
@@ -502,8 +503,8 @@
},
_getDiffURL: function(changeNum, patchRange, path) {
- return '/c/' + changeNum + '/' + this._patchRangeStr(patchRange) + '/' +
- this.encodeURL(path, true);
+ return this.getBaseUrl() + '/c/' + changeNum + '/' +
+ this._patchRangeStr(patchRange) + '/' + this.encodeURL(path, true);
},
_computeDiffURL: function(changeNum, patchRangeRecord, path) {
@@ -528,7 +529,7 @@
},
_getChangePath: function(changeNum, patchRange, revisions) {
- var base = '/c/' + changeNum + '/';
+ var base = this.getBaseUrl() + '/c/' + changeNum + '/';
// The change may not have loaded yet, making revisions unavailable.
if (!revisions) {
diff --git a/polygerrit-ui/app/elements/gr-app.html b/polygerrit-ui/app/elements/gr-app.html
index 6811397..bc88b14 100644
--- a/polygerrit-ui/app/elements/gr-app.html
+++ b/polygerrit-ui/app/elements/gr-app.html
@@ -14,8 +14,9 @@
limitations under the License.
-->
-<link rel="import" href="../bower_components/polymer/polymer.html">
+<link rel="import" href="../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
+<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../styles/app-theme.html">
<link rel="import" href="./plugins/gr-plugin-host/gr-plugin-host.html">
@@ -151,7 +152,7 @@
rel="noopener" target="_blank">Send feedback</a>
<template is="dom-if" if="[[_computeShowGwtUiLink(_serverConfig)]]">
|
- <a id="gwtLink" href$="/?polygerrit=0#[[_path]]" rel="external">Old UI</a>
+ <a id="gwtLink" href$="[[computeGwtUrl(_path)]]" rel="external">Old UI</a>
</template>
| Press “?” for keyboard shortcuts
</div>
diff --git a/polygerrit-ui/app/elements/gr-app.js b/polygerrit-ui/app/elements/gr-app.js
index c24e2aa..acf38c7 100644
--- a/polygerrit-ui/app/elements/gr-app.js
+++ b/polygerrit-ui/app/elements/gr-app.js
@@ -71,6 +71,7 @@
],
behaviors: [
+ Gerrit.BaseUrlBehavior,
Gerrit.KeyboardShortcutBehavior,
],
diff --git a/polygerrit-ui/app/elements/gr-app_test.html b/polygerrit-ui/app/elements/gr-app_test.html
index 5aacc77..2eb835d 100644
--- a/polygerrit-ui/app/elements/gr-app_test.html
+++ b/polygerrit-ui/app/elements/gr-app_test.html
@@ -72,8 +72,10 @@
element._path = '/test/path';
flush(function() {
var gwtLink = element.$$('#gwtLink');
- assert.equal(gwtLink.href,
- 'http://' + location.host + '/?polygerrit=0#/test/path');
+ assert.equal(
+ gwtLink.href,
+ 'http://' + location.host + element.getBaseUrl() + '/?polygerrit=0#/test/path'
+ );
done();
});
});
@@ -89,8 +91,11 @@
flush(function() {
var gwtLink = element.$$('#gwtLink');
- assert.equal(gwtLink.href,
- 'http://' + location.host + '/?polygerrit=0#/c/1/1/testfile.txt@2');
+ assert.equal(
+ gwtLink.href,
+ 'http://' + location.host + element.getBaseUrl() +
+ '/?polygerrit=0#/c/1/1/testfile.txt@2'
+ );
done();
});
});
diff --git a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.html b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.html
index 8d89692..20b6e3f 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.html
+++ b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.html
@@ -14,6 +14,7 @@
limitations under the License.
-->
+<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../gr-account-label/gr-account-label.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.js b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.js
index 3ff4ace..69beb78 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.js
@@ -25,10 +25,14 @@
},
},
+ behaviors: [
+ Gerrit.BaseUrlBehavior,
+ ],
+
_computeOwnerLink: function(account) {
if (!account) { return; }
var accountID = account.email || account._account_id;
- return '/q/owner:' + encodeURIComponent(accountID);
+ return this.getBaseUrl() + '/q/owner:' + encodeURIComponent(accountID);
},
_computeShowEmail: function(account) {
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.html b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.html
index db07de4..0a555cf 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.html
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.html
@@ -14,6 +14,7 @@
limitations under the License.
-->
+<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../bower_components/iron-dropdown/iron-dropdown.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js
index 9b87f03..e84357f 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js
@@ -56,6 +56,10 @@
_hasAvatars: String,
},
+ behaviors: [
+ Gerrit.BaseUrlBehavior,
+ ],
+
attached: function() {
this.$.restAPI.getConfig().then(function(cfg) {
this._hasAvatars = !!(cfg && cfg.plugin && cfg.plugin.has_avatars);
@@ -79,7 +83,7 @@
},
_computeURLHelper: function(host, path) {
- return '//' + host + path;
+ return '//' + host + this.getBaseUrl() + path;
},
_computeRelativeURL: function(path) {
diff --git a/polygerrit-ui/app/elements/shared/gr-placeholder/gr-placeholder.html b/polygerrit-ui/app/elements/shared/gr-placeholder/gr-placeholder.html
index 2e624cc..2c8be31a 100644
--- a/polygerrit-ui/app/elements/shared/gr-placeholder/gr-placeholder.html
+++ b/polygerrit-ui/app/elements/shared/gr-placeholder/gr-placeholder.html
@@ -14,6 +14,7 @@
limitations under the License.
-->
+<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<dom-module id="gr-placeholder">
@@ -44,7 +45,7 @@
<h1>[[title]]</h1>
<section>
This page is not yet implemented in PolyGerrit. View it in the
- <a id="gwtLink" href$="/?polygerrit=0#[[path]]" rel="external">
+ <a id="gwtLink" href$="[[computeGwtUrl(path)]]" rel="external">
Old UI</a>
</section>
</main>
diff --git a/polygerrit-ui/app/elements/shared/gr-placeholder/gr-placeholder.js b/polygerrit-ui/app/elements/shared/gr-placeholder/gr-placeholder.js
index 0ea2979..9b60061 100644
--- a/polygerrit-ui/app/elements/shared/gr-placeholder/gr-placeholder.js
+++ b/polygerrit-ui/app/elements/shared/gr-placeholder/gr-placeholder.js
@@ -21,5 +21,9 @@
path: String,
title: String,
},
+
+ behaviors: [
+ Gerrit.BaseUrlBehavior,
+ ],
});
})();
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html
index 07ff632..1e5fdaa 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html
@@ -14,6 +14,7 @@
limitations under the License.
-->
+<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../behaviors/gr-path-list-behavior/gr-path-list-behavior.html">
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<script src="../../../bower_components/es6-promise/dist/es6-promise.min.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index 7cb29a1..0e3019a 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -82,7 +82,11 @@
Polymer({
is: 'gr-rest-api-interface',
- behaviors: [Gerrit.PathListBehavior],
+
+ behaviors: [
+ Gerrit.BaseUrlBehavior,
+ Gerrit.PathListBehavior,
+ ],
/**
* Fired when an server error occurs.
@@ -152,7 +156,7 @@
},
_urlWithParams: function(url, opt_params) {
- if (!opt_params) { return url; }
+ if (!opt_params) { return this.getBaseUrl() + url; }
var params = [];
for (var p in opt_params) {
@@ -167,7 +171,7 @@
encodeURIComponent(values[i]));
}
}
- return url + '?' + params.join('&');
+ return this.getBaseUrl() + url + '?' + params.join('&');
},
getResponseObject: function(response) {
@@ -694,7 +698,7 @@
}
options.body = opt_body;
}
- return fetch(url, options).then(function(response) {
+ return fetch(this.getBaseUrl() + url, options).then(function(response) {
if (!response.ok) {
if (opt_errFn) {
opt_errFn.call(opt_ctx || null, response);
@@ -907,7 +911,7 @@
},
_fetchB64File: function(url) {
- return fetch(url, {credentials: 'same-origin'}).then(function(response) {
+ return fetch(this.getBaseUrl() + url, {credentials: 'same-origin'}).then(function(response) {
var type = response.headers.get('X-FYI-Content-Type');
return response.text()
.then(function(text) {
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index ca50bbc..3770d4d 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -114,6 +114,8 @@
// Behaviors tests.
[
+ 'base-url-behavior/base-url-behavior_test.html',
+ 'rest-client-behavior/rest-client-behavior_test.html',
'gr-change-table-behavior/gr-change-table-behavior_test.html',
'gr-patch-set-behavior/gr-patch-set-behavior_test.html',
'gr-path-list-behavior/gr-path-list-behavior_test.html',