Merge "RetryHelper.Options: Rename caller to actionName"
diff --git a/java/com/google/gerrit/server/restapi/account/GetAgreements.java b/java/com/google/gerrit/server/restapi/account/GetAgreements.java
index 5feca66..572b489 100644
--- a/java/com/google/gerrit/server/restapi/account/GetAgreements.java
+++ b/java/com/google/gerrit/server/restapi/account/GetAgreements.java
@@ -29,6 +29,8 @@
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.config.GerritServerConfig;
+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.server.project.ProjectCache;
 import com.google.gerrit.server.restapi.config.AgreementJson;
@@ -48,17 +50,20 @@
   private final ProjectCache projectCache;
   private final AgreementJson agreementJson;
   private final boolean agreementsEnabled;
+  private final PermissionBackend permissionBackend;
 
   @Inject
   GetAgreements(
       Provider<CurrentUser> self,
       ProjectCache projectCache,
       AgreementJson agreementJson,
+      PermissionBackend permissionBackend,
       @GerritServerConfig Config config) {
     this.self = self;
     this.projectCache = projectCache;
     this.agreementJson = agreementJson;
     this.agreementsEnabled = config.getBoolean("auth", "contributorAgreements", false);
+    this.permissionBackend = permissionBackend;
   }
 
   @Override
@@ -74,7 +79,11 @@
 
     IdentifiedUser user = self.get().asIdentifiedUser();
     if (user != resource.getUser()) {
-      throw new AuthException("not allowed to get contributor agreements");
+      try {
+        permissionBackend.user(user).check(GlobalPermission.ADMINISTRATE_SERVER);
+      } catch (AuthException e) {
+        throw new AuthException("not allowed to get contributor agreements", e);
+      }
     }
 
     List<AgreementInfo> results = new ArrayList<>();
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java
index 3bb0338..11ca391 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java
@@ -178,6 +178,18 @@
   }
 
   @Test
+  public void listAgreementPermission() throws Exception {
+    assume().that(isContributorAgreementsEnabled()).isTrue();
+    requestScopeOperations.setApiUser(admin.id());
+    // Allowed.
+    gApi.accounts().id(user.id().get()).listAgreements();
+    requestScopeOperations.setApiUser(user.id());
+
+    // Not allowed.
+    assertThrows(AuthException.class, () -> gApi.accounts().id(admin.id().get()).listAgreements());
+  }
+
+  @Test
   public void signAgreementAsOtherUser() throws Exception {
     assume().that(isContributorAgreementsEnabled()).isTrue();
     assertThat(gApi.accounts().self().get().name).isNotEqualTo("admin");
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js
index 5eb8b09..c6743e8 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js
@@ -560,6 +560,10 @@
         isStartOfRange ? 'startOfRange' : '');
     const shaNode = this._createElement('span', 'sha');
     shaNode.innerText = commit.id.substr(0, 7);
+    shaNode.onclick = function() {
+      location.href = '/q/' + shaNode.innerText;
+    };
+
     blameNode.appendChild(shaNode);
     blameNode.append(` on ${date} by ${commit.author}`);
     return blameNode;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
index 3b64f55..92c2c37 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
@@ -273,6 +273,8 @@
       }
       td.blame .sha {
         font-family: var(--monospace-font-family);
+        color: var(--link-color);
+        cursor: pointer;
       }
       .full-width td.blame {
         overflow: hidden;