Merge "SSH-command : set-account"
This commit is contained in:
commit
7dabe97976
@ -96,6 +96,9 @@ review. See link:user-upload.html#push_create[Creating Changes].
|
||||
link:cmd-create-account.html[gerrit create-account]::
|
||||
Create a new batch/role account.
|
||||
|
||||
link:cmd-set-account.html[gerrit set-account]::
|
||||
Change an account's settings.
|
||||
|
||||
link:cmd-create-group.html[gerrit create-group]::
|
||||
Create a new account group.
|
||||
|
||||
|
92
Documentation/cmd-set-account.txt
Normal file
92
Documentation/cmd-set-account.txt
Normal file
@ -0,0 +1,92 @@
|
||||
gerrit set-account
|
||||
==================
|
||||
|
||||
NAME
|
||||
----
|
||||
gerrit set-account - Change an account's settings.
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
set-account [--full-name <FULLNAME>] [--active|--inactive] \
|
||||
[--add-email <EMAIL>] [--delete-email <EMAIL> | ALL] \
|
||||
[--add-ssh-key - | <KEY>] \
|
||||
[--delete-ssh-key - | <KEY> | ALL] <USER>
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
Modifies a given user's settings. This command can be useful to
|
||||
deactivate an account or add/delete ssh keys without going through
|
||||
the UI.
|
||||
|
||||
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.
|
||||
|
||||
SCRIPTING
|
||||
---------
|
||||
This command is intended to be used in scripts.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
<USER>::
|
||||
Required; Full name, email-address, SSH username or account id.
|
||||
|
||||
--full-name::
|
||||
Display name of the user account.
|
||||
+
|
||||
Names containing spaces should be quoted in single quotes (').
|
||||
This most likely requires double quoting the value, for example
|
||||
`--full-name "'A description string'"`.
|
||||
|
||||
--active::
|
||||
Set the account state to be active.
|
||||
|
||||
--inactive::
|
||||
Set the account state to be inactive. This prevents the
|
||||
user from logging in.
|
||||
|
||||
--add-email::
|
||||
Add another email to the user's account. This doesn't
|
||||
trigger the mail validation and adds the email directly
|
||||
to the user's account.
|
||||
May be supplied more than once to add multiple emails to
|
||||
an account in a single command execution.
|
||||
|
||||
--delete-email::
|
||||
Delete an email from this user's account if it exists.
|
||||
If the email provided is 'ALL', all associated emails are
|
||||
deleted from this account.
|
||||
Maybe supplied more than once to remove multiple emails
|
||||
from an account in a single command execution.
|
||||
|
||||
--add-ssh-key::
|
||||
Content of the public SSH key to add to the account's
|
||||
keyring. If `-` the key is read from stdin, rather than
|
||||
from the command line.
|
||||
May be supplied more than once to add multiple SSH keys
|
||||
in a single command execution.
|
||||
|
||||
--delete-ssh-key::
|
||||
Content of the public SSH key to remove from the account's
|
||||
keyring or the comment associated with this key.
|
||||
If `-` the key is read from stdin, rather than from the
|
||||
command line. If the key provided is 'ALL', all
|
||||
associated SSH keys are removed from this account.
|
||||
May be supplied more than once to delete multiple SSH
|
||||
keys in a single command execution.
|
||||
|
||||
EXAMPLES
|
||||
--------
|
||||
Add an email and SSH key to `watcher`'s account:
|
||||
|
||||
====
|
||||
$ cat ~/.ssh/id_watcher.pub | ssh -p 29418 review.example.com gerrit set-account --add-ssh-key - --add-email mail@example.com watcher
|
||||
====
|
||||
|
||||
GERRIT
|
||||
------
|
||||
Part of link:index.html[Gerrit Code Review]
|
@ -36,5 +36,6 @@ public class MasterCommandModule extends CommandModule {
|
||||
command(gerrit, "replicate").to(Replicate.class);
|
||||
command(gerrit, "set-project-parent").to(AdminSetParent.class);
|
||||
command(gerrit, "review").to(ReviewCommand.class);
|
||||
command(gerrit, "set-account").to(SetAccountCommand.class);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,273 @@
|
||||
// Copyright (C) 2012 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.sshd.commands;
|
||||
|
||||
|
||||
import com.google.gerrit.common.errors.InvalidSshKeyException;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.Account.FieldName;
|
||||
import com.google.gerrit.reviewdb.client.AccountExternalId;
|
||||
import com.google.gerrit.reviewdb.client.AccountSshKey;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.account.AccountCache;
|
||||
import com.google.gerrit.server.account.AccountException;
|
||||
import com.google.gerrit.server.account.AccountManager;
|
||||
import com.google.gerrit.server.account.AuthRequest;
|
||||
import com.google.gerrit.server.account.Realm;
|
||||
import com.google.gerrit.server.ssh.SshKeyCache;
|
||||
import com.google.gerrit.sshd.BaseCommand;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.gwtorm.server.ResultSet;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import org.apache.sshd.server.Environment;
|
||||
import org.kohsuke.args4j.Argument;
|
||||
import org.kohsuke.args4j.Option;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/** Set a user's account settings. **/
|
||||
final class SetAccountCommand extends BaseCommand {
|
||||
|
||||
@Argument(index = 0, required = true, metaVar = "USER", usage = "full name, email-address, ssh username or account id")
|
||||
private Account.Id id;
|
||||
|
||||
@Option(name = "--full-name", metaVar = "NAME", usage = "display name of the account")
|
||||
private String fullName;
|
||||
|
||||
@Option(name = "--active", usage = "set account's state to active")
|
||||
private boolean active;
|
||||
|
||||
@Option(name = "--inactive", usage = "set account's state to inactive")
|
||||
private boolean inactive;
|
||||
|
||||
@Option(name = "--add-email", multiValued = true, metaVar = "EMAIL", usage = "email addresses to add to the account")
|
||||
private List<String> addEmails = new ArrayList<String>();
|
||||
|
||||
@Option(name = "--delete-email", multiValued = true, metaVar = "EMAIL", usage = "email addresses to delete from the account")
|
||||
private List<String> deleteEmails = new ArrayList<String>();
|
||||
|
||||
@Option(name = "--add-ssh-key", multiValued = true, metaVar = "-|KEY", usage = "public keys to add to the account")
|
||||
private List<String> addSshKeys = new ArrayList<String>();
|
||||
|
||||
@Option(name = "--delete-ssh-key", multiValued = true, metaVar = "-|KEY", usage = "public keys to delete from the account")
|
||||
private List<String> deleteSshKeys = new ArrayList<String>();
|
||||
|
||||
@Inject
|
||||
private IdentifiedUser currentUser;
|
||||
|
||||
@Inject
|
||||
private ReviewDb db;
|
||||
|
||||
@Inject
|
||||
private AccountManager manager;
|
||||
|
||||
@Inject
|
||||
private SshKeyCache sshKeyCache;
|
||||
|
||||
@Inject
|
||||
private AccountCache byIdCache;
|
||||
|
||||
@Inject
|
||||
private Realm realm;
|
||||
|
||||
@Override
|
||||
public void start(final Environment env) {
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void validate() throws UnloggedFailure {
|
||||
if (active && inactive) {
|
||||
throw new UnloggedFailure(1,
|
||||
"--active and --inactive options are mutually exclusive.");
|
||||
}
|
||||
if (addSshKeys.contains("-") && deleteSshKeys.contains("-")) {
|
||||
throw new UnloggedFailure(1, "Only one option may use the stdin");
|
||||
}
|
||||
if (deleteSshKeys.contains("ALL")) {
|
||||
deleteSshKeys = Collections.singletonList("ALL");
|
||||
}
|
||||
if (deleteEmails.contains("ALL")) {
|
||||
deleteEmails = Collections.singletonList("ALL");
|
||||
}
|
||||
}
|
||||
|
||||
private void setAccount() throws OrmException, IOException, UnloggedFailure {
|
||||
|
||||
final Account account = db.accounts().get(id);
|
||||
boolean accountUpdated = false;
|
||||
boolean sshKeysUpdated = false;
|
||||
|
||||
for (String email : addEmails) {
|
||||
link(id, email);
|
||||
}
|
||||
|
||||
for (String email : deleteEmails) {
|
||||
deleteMail(id, email);
|
||||
}
|
||||
|
||||
if (fullName != null) {
|
||||
if (realm.allowsEdit(FieldName.FULL_NAME)) {
|
||||
account.setFullName(fullName);
|
||||
} else {
|
||||
throw new UnloggedFailure(1, "The realm doesn't allow editing names");
|
||||
}
|
||||
}
|
||||
|
||||
if (active) {
|
||||
accountUpdated = true;
|
||||
account.setActive(true);
|
||||
} else if (inactive) {
|
||||
accountUpdated = true;
|
||||
account.setActive(false);
|
||||
}
|
||||
|
||||
addSshKeys = readSshKey(addSshKeys);
|
||||
if (!addSshKeys.isEmpty()) {
|
||||
sshKeysUpdated = true;
|
||||
addSshKeys(addSshKeys, account);
|
||||
}
|
||||
|
||||
deleteSshKeys = readSshKey(deleteSshKeys);
|
||||
if (!deleteSshKeys.isEmpty()) {
|
||||
sshKeysUpdated = true;
|
||||
deleteSshKeys(deleteSshKeys, account);
|
||||
}
|
||||
|
||||
if (accountUpdated) {
|
||||
db.accounts().update(Collections.singleton(account));
|
||||
byIdCache.evict(id);
|
||||
}
|
||||
|
||||
if (sshKeysUpdated) {
|
||||
sshKeyCache.evict(account.getUserName());
|
||||
}
|
||||
|
||||
db.close();
|
||||
}
|
||||
|
||||
private void addSshKeys(final List<String> keys, final Account account)
|
||||
throws OrmException, UnloggedFailure {
|
||||
List<AccountSshKey> accountKeys = new ArrayList<AccountSshKey>();
|
||||
int seq = db.accountSshKeys().byAccount(account.getId()).toList().size();
|
||||
for (String key : keys) {
|
||||
try {
|
||||
seq++;
|
||||
AccountSshKey accountSshKey = sshKeyCache.create(
|
||||
new AccountSshKey.Id(account.getId(), seq), key.trim());
|
||||
accountKeys.add(accountSshKey);
|
||||
} catch (InvalidSshKeyException e) {
|
||||
throw new UnloggedFailure(1, "fatal: invalid ssh key");
|
||||
}
|
||||
}
|
||||
db.accountSshKeys().insert(accountKeys);
|
||||
}
|
||||
|
||||
private void deleteSshKeys(final List<String> keys, final Account account)
|
||||
throws OrmException {
|
||||
ResultSet<AccountSshKey> allKeys = db.accountSshKeys().byAccount(account.getId());
|
||||
if (keys.contains("ALL")) {
|
||||
db.accountSshKeys().delete(allKeys);
|
||||
} else {
|
||||
List<AccountSshKey> accountKeys = new ArrayList<AccountSshKey>();
|
||||
for (String key : keys) {
|
||||
for (AccountSshKey accountSshKey : allKeys) {
|
||||
if (key.trim().equals(accountSshKey.getSshPublicKey())
|
||||
|| accountSshKey.getComment().trim().equals(key)) {
|
||||
accountKeys.add(accountSshKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
db.accountSshKeys().delete(accountKeys);
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteMail(Account.Id id, final String mailAddress)
|
||||
throws UnloggedFailure, OrmException {
|
||||
if (mailAddress.equals("ALL")) {
|
||||
ResultSet<AccountExternalId> ids = db.accountExternalIds().byAccount(id);
|
||||
for (AccountExternalId extId : ids) {
|
||||
if (extId.isScheme(AccountExternalId.SCHEME_MAILTO)) {
|
||||
unlink(id, extId.getEmailAddress());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
AccountExternalId.Key key = new AccountExternalId.Key(
|
||||
AccountExternalId.SCHEME_MAILTO, mailAddress);
|
||||
AccountExternalId extId = db.accountExternalIds().get(key);
|
||||
if (extId != null) {
|
||||
unlink(id, mailAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void unlink(Account.Id id, final String mailAddress)
|
||||
throws UnloggedFailure {
|
||||
try {
|
||||
manager.unlink(id, AuthRequest.forEmail(mailAddress));
|
||||
} catch (AccountException ex) {
|
||||
throw die(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void link(Account.Id id, final String mailAddress)
|
||||
throws UnloggedFailure {
|
||||
try {
|
||||
manager.link(id, AuthRequest.forEmail(mailAddress));
|
||||
} catch (AccountException ex) {
|
||||
throw die(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> readSshKey(final List<String> sshKeys)
|
||||
throws UnsupportedEncodingException, IOException {
|
||||
if (!sshKeys.isEmpty()) {
|
||||
String sshKey = "";
|
||||
int idx = sshKeys.indexOf("-");
|
||||
if (idx >= 0) {
|
||||
sshKey = "";
|
||||
BufferedReader br =
|
||||
new BufferedReader(new InputStreamReader(in, "UTF-8"));
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
sshKey += line + "\n";
|
||||
}
|
||||
sshKeys.set(idx, sshKey);
|
||||
}
|
||||
}
|
||||
return sshKeys;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user