diff --git a/Documentation/access-control.txt b/Documentation/access-control.txt index 8ff2eb6238..2f927145b5 100644 --- a/Documentation/access-control.txt +++ b/Documentation/access-control.txt @@ -1208,6 +1208,12 @@ kill command ends tasks that currently occupy the Gerrit server, usually a replication task or a user initiated task such as an upload-pack or receive-pack. +[[capability_modifyAccount]] +=== Modify Account + +Allow to link:cmd-set-account.html[modify accounts over the ssh prompt]. +This capability allows the granted group members to modify any user account +setting. [[capability_priority]] === Priority diff --git a/Documentation/cmd-set-account.txt b/Documentation/cmd-set-account.txt index 897d6889b5..a2944dda5d 100644 --- a/Documentation/cmd-set-account.txt +++ b/Documentation/cmd-set-account.txt @@ -21,7 +21,15 @@ It also allows managing email addresses, which bypasses the verification step we force within the UI. == ACCESS -Caller must be a member of the privileged 'Administrators' group. +Caller must be a member of the privileged 'Administrators' group, +or have been granted +link:access-control.html#capability_modifyAccount[the 'Modify Account' global capability]. + +To set the HTTP password for the user account (option --http-password) the +caller must be a member of the privileged 'Administrators' group, +or have been granted +link:access-control.html#capability_generateHttpPassword[the 'Generate HTTP Password' global capability] +in addition to 'Modify Account' global capability. == SCRIPTING This command is intended to be used in scripts. 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 f42811cdc7..fccf3b304f 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 @@ -38,6 +38,9 @@ public class GlobalCapability { /** Can create any account on the server. */ public static final String CREATE_ACCOUNT = "createAccount"; + /** Can modify any account on the server. */ + public static final String MODIFY_ACCOUNT = "modifyAccount"; + /** Can create any group on the server. */ public static final String CREATE_GROUP = "createGroup"; @@ -108,6 +111,7 @@ public class GlobalCapability { NAMES_ALL.add(FLUSH_CACHES); NAMES_ALL.add(GENERATE_HTTP_PASSWORD); NAMES_ALL.add(KILL_TASK); + NAMES_ALL.add(MODIFY_ACCOUNT); NAMES_ALL.add(PRIORITY); NAMES_ALL.add(QUERY_LIMIT); NAMES_ALL.add(RUN_AS); diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AddSshKey.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AddSshKey.java index 3c21d17558..58c674cf13 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AddSshKey.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AddSshKey.java @@ -63,7 +63,7 @@ public class AddSshKey implements RestModifyView { public Response apply(AccountResource rsrc, Input input) throws AuthException, BadRequestException, OrmException, IOException { if (self.get() != rsrc.getUser() - && !self.get().getCapabilities().canAdministrateServer()) { + && !self.get().getCapabilities().canModifyAccount()) { throw new AuthException("not allowed to add SSH keys"); } return apply(rsrc.getUser(), input); 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 f0f22b5dd4..631256a661 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 @@ -110,6 +110,12 @@ public class CapabilityControl { || canAdministrateServer(); } + /** @return true if the user can modify an account for another user. */ + public boolean canModifyAccount() { + return canPerform(GlobalCapability.MODIFY_ACCOUNT) + || canAdministrateServer(); + } + /** @return true if the user can view all accounts. */ public boolean canViewAllAccounts() { return canPerform(GlobalCapability.VIEW_ALL_ACCOUNTS) diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateEmail.java index 4be806723c..26beadc38c 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateEmail.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateEmail.java @@ -85,7 +85,7 @@ public class CreateEmail implements RestModifyView { ResourceNotFoundException, OrmException, EmailException, MethodNotAllowedException { if (self.get() != rsrc.getUser() - && !self.get().getCapabilities().canAdministrateServer()) { + && !self.get().getCapabilities().canModifyAccount()) { throw new AuthException("not allowed to add email address"); } @@ -98,8 +98,8 @@ public class CreateEmail implements RestModifyView { } if (input.noConfirmation - && !self.get().getCapabilities().canAdministrateServer()) { - throw new AuthException("must be administrator to use no_confirmation"); + && !self.get().getCapabilities().canModifyAccount()) { + throw new AuthException("not allowed to use no_confirmation"); } return apply(rsrc.getUser(), input); diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteActive.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteActive.java index 52ab6512aa..abdaf2394e 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteActive.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteActive.java @@ -29,7 +29,7 @@ import com.google.inject.Singleton; import java.util.Collections; -@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER) +@RequiresCapability(GlobalCapability.MODIFY_ACCOUNT) @Singleton public class DeleteActive implements RestModifyView { public static class Input { diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteEmail.java index 60485869bb..f1e02bd57b 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteEmail.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteEmail.java @@ -55,7 +55,7 @@ public class DeleteEmail implements RestModifyView throws AuthException, ResourceNotFoundException, ResourceConflictException, MethodNotAllowedException, OrmException { if (self.get() != rsrc.getUser() - && !self.get().getCapabilities().canAdministrateServer()) { + && !self.get().getCapabilities().canModifyAccount()) { throw new AuthException("not allowed to delete email address"); } return apply(rsrc.getUser(), rsrc.getEmail()); 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 1a6627743e..4ac65ecbc6 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 @@ -22,6 +22,7 @@ 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.GENERATE_HTTP_PASSWORD; import static com.google.gerrit.common.data.GlobalCapability.KILL_TASK; +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; @@ -116,6 +117,7 @@ class GetCapabilities implements RestReadView { have.put(FLUSH_CACHES, cc.canFlushCaches()); have.put(GENERATE_HTTP_PASSWORD, cc.canGenerateHttpPassword()); have.put(KILL_TASK, cc.canKillTask()); + 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()); diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetEmails.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetEmails.java index 64991e6112..bf9c9ecfe3 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetEmails.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetEmails.java @@ -40,7 +40,7 @@ public class GetEmails implements RestReadView { public List apply(AccountResource rsrc) throws AuthException, OrmException { if (self.get() != rsrc.getUser() - && !self.get().getCapabilities().canAdministrateServer()) { + && !self.get().getCapabilities().canModifyAccount()) { throw new AuthException("not allowed to list email addresses"); } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetSshKeys.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetSshKeys.java index 9266c3aad7..68464707fe 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetSshKeys.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetSshKeys.java @@ -45,7 +45,7 @@ public class GetSshKeys implements RestReadView { public List apply(AccountResource rsrc) throws AuthException, OrmException { if (self.get() != rsrc.getUser() - && !self.get().getCapabilities().canAdministrateServer()) { + && !self.get().getCapabilities().canModifyAccount()) { throw new AuthException("not allowed to get SSH keys"); } return apply(rsrc.getUser()); 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 69d16d8b23..c7a63e59a5 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 @@ -29,7 +29,7 @@ import com.google.inject.Singleton; import java.util.Collections; -@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER) +@RequiresCapability(GlobalCapability.MODIFY_ACCOUNT) @Singleton public class PutActive implements RestModifyView { public static class Input { diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutName.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutName.java index 554bae7bc4..601ee76196 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutName.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutName.java @@ -64,7 +64,7 @@ public class PutName implements RestModifyView { throws AuthException, MethodNotAllowedException, ResourceNotFoundException, OrmException { if (self.get() != rsrc.getUser() - && !self.get().getCapabilities().canAdministrateServer()) { + && !self.get().getCapabilities().canModifyAccount()) { throw new AuthException("not allowed to change name"); } return apply(rsrc.getUser(), input); 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 7ac987d57a..c49e3be0da 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 @@ -52,7 +52,7 @@ public class PutPreferred implements public Response apply(AccountResource.Email rsrc, Input input) throws AuthException, ResourceNotFoundException, OrmException { if (self.get() != rsrc.getUser() - && !self.get().getCapabilities().canAdministrateServer()) { + && !self.get().getCapabilities().canModifyAccount()) { throw new AuthException("not allowed to set preferred email address"); } return apply(rsrc.getUser(), rsrc.getEmail()); diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/SetDiffPreferences.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/SetDiffPreferences.java index 9b971e4976..08386b2588 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/SetDiffPreferences.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/SetDiffPreferences.java @@ -68,8 +68,8 @@ public class SetDiffPreferences implements RestModifyView { throws AuthException, ResourceNotFoundException, OrmException, IOException, ConfigInvalidException { if (self.get() != rsrc.getUser() - && !self.get().getCapabilities().canAdministrateServer()) { - throw new AuthException("restricted to administrator"); + && !self.get().getCapabilities().canModifyAccount()) { + throw new AuthException("restricted to members of Modify Accounts"); } if (i == null) { i = new Input(); diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/SshKeys.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/SshKeys.java index b94158fee0..b35c03e4c0 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/SshKeys.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/SshKeys.java @@ -55,7 +55,7 @@ public class SshKeys implements public AccountResource.SshKey parse(AccountResource rsrc, IdString id) throws ResourceNotFoundException, OrmException { if (self.get() != rsrc.getUser() - && !self.get().getCapabilities().canAdministrateServer()) { + && !self.get().getCapabilities().canModifyAccount()) { throw new ResourceNotFoundException(); } return parse(rsrc.getUser(), id); diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/CapabilityConstants.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/CapabilityConstants.java index 2c1632f30a..3805a0ec51 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/CapabilityConstants.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/CapabilityConstants.java @@ -31,6 +31,7 @@ public class CapabilityConstants extends TranslationBundle { public String flushCaches; public String generateHttpPassword; public String killTask; + public String modifyAccount; public String priority; public String queryLimit; public String runAs; diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/config/CapabilityConstants.properties b/gerrit-server/src/main/resources/com/google/gerrit/server/config/CapabilityConstants.properties index a1e0e1d2d5..056da87ab4 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/config/CapabilityConstants.properties +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/config/CapabilityConstants.properties @@ -7,6 +7,7 @@ emailReviewers = Email Reviewers flushCaches = Flush Caches generateHttpPassword = Generate HTTP Password killTask = Kill Task +modifyAccount = Modify Account priority = Priority queryLimit = Query Limit runAs = Run As diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java index 83d75e9856..6af58f3e7e 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java @@ -14,7 +14,9 @@ package com.google.gerrit.sshd.commands; +import com.google.gerrit.common.data.GlobalCapability; import com.google.gerrit.common.errors.EmailException; +import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.extensions.restapi.RawInput; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.RestApiException; @@ -55,6 +57,7 @@ import java.util.List; /** Set a user's account settings. **/ @CommandMetaData(name = "set-account", description = "Change an account's settings") +@RequiresCapability(GlobalCapability.MODIFY_ACCOUNT) final class SetAccountCommand extends BaseCommand { @Argument(index = 0, required = true, metaVar = "USER", usage = "full name, email-address, ssh username or account id") @@ -84,9 +87,6 @@ final class SetAccountCommand extends BaseCommand { @Option(name = "--http-password", metaVar = "PASSWORD", usage = "password for HTTP authentication for the account") private String httpPassword; - @Inject - private IdentifiedUser currentUser; - @Inject private IdentifiedUser.GenericFactory genericUserFactory; @@ -128,13 +128,6 @@ final class SetAccountCommand extends BaseCommand { startThread(new CommandRunnable() { @Override public void run() throws Exception { - if (!currentUser.getCapabilities().canAdministrateServer()) { - String msg = - String.format( - "fatal: %s does not have \"Administrator\" capability.", - currentUser.getUserName()); - throw new UnloggedFailure(1, msg); - } parseCommandLine(); validate(); setAccount();