Merge "Support adding an SSH key via REST"
This commit is contained in:
@@ -509,6 +509,44 @@ describes the SSH key.
|
||||
}
|
||||
----
|
||||
|
||||
[[add-ssh-key]]
|
||||
Add SSH Key
|
||||
~~~~~~~~~~~
|
||||
[verse]
|
||||
'POST /accounts/link:#account-id[\{account-id\}]/sshkeys'
|
||||
|
||||
Adds an SSH key for a user.
|
||||
|
||||
The SSH public key must be provided as raw content in the request body.
|
||||
|
||||
.Request
|
||||
----
|
||||
POST /accounts/self/sshkeys HTTP/1.0
|
||||
Content-Type: plain/text
|
||||
|
||||
AAAAB3NzaC1yc2EAAAABIwAAAQEA0T...YImydZAw\u003d\u003d
|
||||
----
|
||||
|
||||
As response an link:#ssh-key-info[SshKeyInfo] entity is returned that
|
||||
describes the new SSH key.
|
||||
|
||||
.Response
|
||||
----
|
||||
HTTP/1.1 200 OK
|
||||
Content-Disposition: attachment
|
||||
Content-Type: application/json;charset=UTF-8
|
||||
|
||||
)]}'
|
||||
{
|
||||
"seq": 2,
|
||||
"ssh_public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0T...YImydZAw\u003d\u003d john.doe@example.com",
|
||||
"encoded_key": "AAAAB3NzaC1yc2EAAAABIwAAAQEA0T...YImydZAw\u003d\u003d",
|
||||
"algorithm": "ssh-rsa",
|
||||
"comment": "john.doe@example.com",
|
||||
"valid": true
|
||||
}
|
||||
----
|
||||
|
||||
[[list-account-capabilities]]
|
||||
List Account Capabilities
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -31,10 +31,6 @@ import java.util.Set;
|
||||
|
||||
@RpcImpl(version = Version.V2_0)
|
||||
public interface AccountSecurity extends RemoteJsonService {
|
||||
@Audit
|
||||
@SignInRequired
|
||||
void addSshKey(String keyText, AsyncCallback<AccountSshKey> callback);
|
||||
|
||||
@Audit
|
||||
@SignInRequired
|
||||
void deleteSshKeys(Set<AccountSshKey.Id> ids,
|
||||
|
||||
@@ -40,6 +40,13 @@ public class AccountApi {
|
||||
new RestApi("/accounts/").id(account).view("sshkeys").get(cb);
|
||||
}
|
||||
|
||||
/** Add a new SSH keys */
|
||||
public static void addSshKey(String account, String sshPublicKey,
|
||||
AsyncCallback<SshKeyInfo> cb) {
|
||||
new RestApi("/accounts/").id(account).view("sshkeys")
|
||||
.post(sshPublicKey, cb);
|
||||
}
|
||||
|
||||
/** Generate a new HTTP password */
|
||||
public static void generateHttpPassword(String account,
|
||||
AsyncCallback<NativeString> cb) {
|
||||
|
||||
@@ -24,18 +24,6 @@ public class SshKeyInfo extends JavaScriptObject {
|
||||
public final native String comment() /*-{ return this.comment; }-*/;
|
||||
public final native boolean isValid() /*-{ return this['valid'] ? true : false; }-*/;
|
||||
|
||||
public static native SshKeyInfo create(int seq, String sshPublicKey,
|
||||
String encodedKey, String algorithm, String comment, boolean valid) /*-{
|
||||
return {
|
||||
'seq': seq,
|
||||
'ssh_public_key': sshPublicKey,
|
||||
'encoded_key': encodedKey,
|
||||
'algorithm': algorithm,
|
||||
'comment': comment,
|
||||
'valid': valid
|
||||
};
|
||||
}-*/;
|
||||
|
||||
protected SshKeyInfo() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,13 +159,11 @@ class SshPanel extends Composite {
|
||||
final String txt = addTxt.getText();
|
||||
if (txt != null && txt.length() > 0) {
|
||||
addNew.setEnabled(false);
|
||||
Util.ACCOUNT_SEC.addSshKey(txt, new GerritCallback<AccountSshKey>() {
|
||||
public void onSuccess(final AccountSshKey k) {
|
||||
AccountApi.addSshKey("self", txt, new GerritCallback<SshKeyInfo>() {
|
||||
public void onSuccess(final SshKeyInfo k) {
|
||||
addNew.setEnabled(true);
|
||||
addTxt.setText("");
|
||||
keys.addOneKey(SshKeyInfo.create(k.getKey().get(),
|
||||
k.getSshPublicKey(), k.getEncodedKey(), k.getAlgorithm(),
|
||||
k.getComment(), k.isValid()));
|
||||
keys.addOneKey(k);
|
||||
if (!keys.isVisible()) {
|
||||
showAddKeyBlock(false);
|
||||
setKeyTableVisible(true);
|
||||
|
||||
@@ -304,6 +304,11 @@ public class RestApi {
|
||||
sendJSON(POST, content, cb);
|
||||
}
|
||||
|
||||
public <T extends JavaScriptObject> void post(String content,
|
||||
AsyncCallback<T> cb) {
|
||||
sendRaw(POST, content, cb);
|
||||
}
|
||||
|
||||
public <T extends JavaScriptObject> void put(AsyncCallback<T> cb) {
|
||||
send(PUT, cb);
|
||||
}
|
||||
@@ -329,6 +334,19 @@ public class RestApi {
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends JavaScriptObject> void sendRaw(Method method, String body,
|
||||
AsyncCallback<T> cb) {
|
||||
HttpCallback<T> httpCallback = new HttpCallback<T>(cb);
|
||||
try {
|
||||
RpcStatus.INSTANCE.onRpcStart();
|
||||
RequestBuilder req = request(method);
|
||||
req.setHeader("Content-Type", TEXT_TYPE);
|
||||
req.sendRequest(body, httpCallback);
|
||||
} catch (RequestException e) {
|
||||
httpCallback.onError(null, e);
|
||||
}
|
||||
}
|
||||
|
||||
private RequestBuilder request(Method method) {
|
||||
RequestBuilder req = new RequestBuilder(method, url());
|
||||
if (ifNoneMatch != null) {
|
||||
|
||||
@@ -19,7 +19,6 @@ import com.google.gerrit.common.ChangeHooks;
|
||||
import com.google.gerrit.common.data.AccountSecurity;
|
||||
import com.google.gerrit.common.data.ContributorAgreement;
|
||||
import com.google.gerrit.common.errors.ContactInformationStoreException;
|
||||
import com.google.gerrit.common.errors.InvalidSshKeyException;
|
||||
import com.google.gerrit.common.errors.NoSuchEntityException;
|
||||
import com.google.gerrit.common.errors.PermissionDeniedException;
|
||||
import com.google.gerrit.httpd.rpc.BaseServiceImplementation;
|
||||
@@ -106,29 +105,6 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
|
||||
this.groupCache = groupCache;
|
||||
}
|
||||
|
||||
public void addSshKey(final String keyText,
|
||||
final AsyncCallback<AccountSshKey> callback) {
|
||||
run(callback, new Action<AccountSshKey>() {
|
||||
public AccountSshKey run(final ReviewDb db) throws OrmException, Failure {
|
||||
int max = 0;
|
||||
final Account.Id me = user.get().getAccountId();
|
||||
for (final AccountSshKey k : db.accountSshKeys().byAccount(me)) {
|
||||
max = Math.max(max, k.getKey().get());
|
||||
}
|
||||
|
||||
final AccountSshKey key;
|
||||
try {
|
||||
key = sshKeyCache.create(new AccountSshKey.Id(me, max + 1), keyText);
|
||||
} catch (InvalidSshKeyException e) {
|
||||
throw new Failure(e);
|
||||
}
|
||||
db.accountSshKeys().insert(Collections.singleton(key));
|
||||
uncacheSshKeys();
|
||||
return key;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void deleteSshKeys(final Set<AccountSshKey.Id> ids,
|
||||
final AsyncCallback<VoidResult> callback) {
|
||||
run(callback, new Action<VoidResult>() {
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
// Copyright (C) 2013 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.account;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.common.io.InputSupplier;
|
||||
import com.google.gerrit.common.errors.InvalidSshKeyException;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
|
||||
import com.google.gerrit.extensions.restapi.RawInput;
|
||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.Response;
|
||||
import com.google.gerrit.extensions.restapi.RestModifyView;
|
||||
import com.google.gerrit.reviewdb.client.AccountSshKey;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.account.AddSshKey.Input;
|
||||
import com.google.gerrit.server.account.GetSshKeys.SshKeyInfo;
|
||||
import com.google.gerrit.server.ssh.SshKeyCache;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
|
||||
public class AddSshKey implements RestModifyView<AccountResource, Input> {
|
||||
static class Input {
|
||||
RawInput raw;
|
||||
}
|
||||
|
||||
private final Provider<CurrentUser> self;
|
||||
private final Provider<ReviewDb> dbProvider;
|
||||
private final SshKeyCache sshKeyCache;
|
||||
|
||||
@Inject
|
||||
AddSshKey(Provider<CurrentUser> self, Provider<ReviewDb> dbProvider,
|
||||
SshKeyCache sshKeyCache) {
|
||||
this.self = self;
|
||||
this.dbProvider = dbProvider;
|
||||
this.sshKeyCache = sshKeyCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<SshKeyInfo> apply(AccountResource rsrc, Input input)
|
||||
throws AuthException, MethodNotAllowedException, BadRequestException,
|
||||
ResourceConflictException, OrmException, IOException {
|
||||
if (self.get() != rsrc.getUser()
|
||||
&& !self.get().getCapabilities().canAdministrateServer()) {
|
||||
throw new AuthException("not allowed to add SSH keys");
|
||||
}
|
||||
if (input == null) {
|
||||
input = new Input();
|
||||
}
|
||||
if (input.raw == null) {
|
||||
throw new BadRequestException("SSH public key missing");
|
||||
}
|
||||
|
||||
int max = 0;
|
||||
for (AccountSshKey k : dbProvider.get().accountSshKeys()
|
||||
.byAccount(rsrc.getUser().getAccountId())) {
|
||||
max = Math.max(max, k.getKey().get());
|
||||
}
|
||||
|
||||
final InputStream in = input.raw.getInputStream();
|
||||
String sshPublicKey =
|
||||
CharStreams.toString(CharStreams.newReaderSupplier(
|
||||
new InputSupplier<InputStream>() {
|
||||
@Override
|
||||
public InputStream getInput() {
|
||||
return in;
|
||||
}
|
||||
}, Charsets.UTF_8));
|
||||
try {
|
||||
AccountSshKey sshKey =
|
||||
sshKeyCache.create(new AccountSshKey.Id(
|
||||
rsrc.getUser().getAccountId(), max + 1), sshPublicKey);
|
||||
dbProvider.get().accountSshKeys().insert(Collections.singleton(sshKey));
|
||||
sshKeyCache.evict(rsrc.getUser().getUserName());
|
||||
return Response.<SshKeyInfo>created(new SshKeyInfo(sshKey));
|
||||
} catch (InvalidSshKeyException e) {
|
||||
throw new BadRequestException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,7 @@ public class Module extends RestApiModule {
|
||||
put(ACCOUNT_KIND, "password.http").to(PutHttpPassword.class);
|
||||
delete(ACCOUNT_KIND, "password.http").to(PutHttpPassword.class);
|
||||
child(ACCOUNT_KIND, "sshkeys").to(SshKeys.class);
|
||||
post(ACCOUNT_KIND, "sshkeys").to(AddSshKey.class);
|
||||
get(SSH_KEY_KIND).to(GetSshKey.class);
|
||||
get(ACCOUNT_KIND, "avatar").to(GetAvatar.class);
|
||||
get(ACCOUNT_KIND, "avatar.change.url").to(GetAvatarChangeUrl.class);
|
||||
|
||||
Reference in New Issue
Block a user