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 e66257e33b..eabffebee8 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 @@ -1876,6 +1876,67 @@ public class AccountIT extends AbstractDaemonTest { assertThat(info.name).isEqualTo("Something Else"); } + @Test + public void userCanGenerateNewHttpPassword() throws Exception { + String newPassword = gApi.accounts().self().generateHttpPassword(); + assertThat(newPassword).isNotNull(); + } + + @Test + public void adminCanGenerateNewHttpPasswordForUser() throws Exception { + setApiUser(admin); + String newPassword = gApi.accounts().id(user.username).generateHttpPassword(); + assertThat(newPassword).isNotNull(); + } + + @Test + public void userCannotGenerateNewHttpPasswordForOtherUser() throws Exception { + setApiUser(user); + exception.expect(AuthException.class); + gApi.accounts().id(admin.username).generateHttpPassword(); + } + + @Test + public void userCannotExplicitlySetHttpPassword() throws Exception { + setApiUser(user); + exception.expect(AuthException.class); + gApi.accounts().self().setHttpPassword("my-new-password"); + } + + @Test + public void userCannotExplicitlySetHttpPasswordForOtherUser() throws Exception { + setApiUser(user); + exception.expect(AuthException.class); + gApi.accounts().id(admin.username).setHttpPassword("my-new-password"); + } + + @Test + public void userCanRemoveHttpPassword() throws Exception { + setApiUser(user); + assertThat(gApi.accounts().self().setHttpPassword(null)).isNull(); + } + + @Test + public void userCannotRemoveHttpPasswordForOtherUser() throws Exception { + setApiUser(user); + exception.expect(AuthException.class); + gApi.accounts().id(admin.username).setHttpPassword(null); + } + + @Test + public void adminCanExplicitlySetHttpPasswordForUser() throws Exception { + setApiUser(admin); + String httpPassword = "new-password-for-user"; + assertThat(gApi.accounts().id(user.username).setHttpPassword(httpPassword)) + .isEqualTo(httpPassword); + } + + @Test + public void adminCanRemoveHttpPasswordForUser() throws Exception { + setApiUser(admin); + assertThat(gApi.accounts().id(user.username).setHttpPassword(null)).isNull(); + } + private void assertGroups(String user, List expected) throws Exception { List actual = gApi.accounts().id(user).getGroups().stream().map(g -> g.name).collect(toList()); diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/accounts/AccountApi.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/accounts/AccountApi.java index 1526cc6e8c..3b2963ad2c 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/accounts/AccountApi.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/accounts/AccountApi.java @@ -104,6 +104,23 @@ public interface AccountApi { void setName(String name) throws RestApiException; + /** + * Generate a new HTTP password. + * + * @return the generated password. + */ + String generateHttpPassword() throws RestApiException; + + /** + * Set a new HTTP password. + * + *

May only be invoked by administrators. + * + * @param httpPassword the new password, {@code null} to remove the password. + * @return the new password, {@code null} if the password was removed. + */ + String setHttpPassword(String httpPassword) throws RestApiException; + /** * A default implementation which allows source compatibility when adding new methods to the * interface. @@ -286,5 +303,15 @@ public interface AccountApi { public void setName(String name) throws RestApiException { throw new NotImplementedException(); } + + @Override + public String generateHttpPassword() throws RestApiException { + throw new NotImplementedException(); + } + + @Override + public String setHttpPassword(String httpPassword) throws RestApiException { + throw new NotImplementedException(); + } } } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java index d7952f84de..933ff5be4b 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java @@ -63,6 +63,7 @@ import com.google.gerrit.server.account.Index; import com.google.gerrit.server.account.PostWatchedProjects; import com.google.gerrit.server.account.PutActive; import com.google.gerrit.server.account.PutAgreement; +import com.google.gerrit.server.account.PutHttpPassword; import com.google.gerrit.server.account.PutName; import com.google.gerrit.server.account.PutStatus; import com.google.gerrit.server.account.SetDiffPreferences; @@ -122,6 +123,7 @@ public class AccountApiImpl implements AccountApi { private final PutStatus putStatus; private final GetGroups getGroups; private final PutName putName; + private final PutHttpPassword putHttpPassword; @Inject AccountApiImpl( @@ -161,6 +163,7 @@ public class AccountApiImpl implements AccountApi { PutStatus putStatus, GetGroups getGroups, PutName putName, + PutHttpPassword putPassword, @Assisted AccountResource account) { this.account = account; this.accountLoaderFactory = ailf; @@ -199,6 +202,7 @@ public class AccountApiImpl implements AccountApi { this.putStatus = putStatus; this.getGroups = getGroups; this.putName = putName; + this.putHttpPassword = putPassword; } @Override @@ -528,4 +532,31 @@ public class AccountApiImpl implements AccountApi { throw asRestApiException("Cannot set account name", e); } } + + @Override + public String generateHttpPassword() throws RestApiException { + PutHttpPassword.Input input = new PutHttpPassword.Input(); + input.generate = true; + try { + // Response should never be 'none' for a generated password, but + // let's make sure. + Response result = putHttpPassword.apply(account, input); + return result.isNone() ? null : result.value(); + } catch (Exception e) { + throw asRestApiException("Cannot generate HTTP password", e); + } + } + + @Override + public String setHttpPassword(String password) throws RestApiException { + PutHttpPassword.Input input = new PutHttpPassword.Input(); + input.generate = false; + input.httpPassword = password; + try { + Response result = putHttpPassword.apply(account, input); + return result.isNone() ? null : result.value(); + } catch (Exception e) { + throw asRestApiException("Cannot generate HTTP password", e); + } + } }