Support listing the SSH keys of an account via REST
By GET on /accounts/<account-id>/sshkeys it is now possible to retrieve the SSH keys of an account. The WebUI is adapted to use the new REST endpoint to retrieve the SSH keys. The old RPC for this is deleted. Change-Id: Iea83e77acbc1bb28c2bea0b5a3972b78acb22d62 Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
This commit is contained in:
committed by
Edwin Kempin
parent
d9cdf5eea4
commit
ebccb84a19
@@ -441,6 +441,41 @@ Sets an email address as preferred email address for an account.
|
||||
If the email address was already the preferred email address of the
|
||||
account the response is "`200 OK`".
|
||||
|
||||
[[list-ssh-keys]]
|
||||
List SSH Keys
|
||||
~~~~~~~~~~~~~
|
||||
[verse]
|
||||
'GET /accounts/link:#account-id[\{account-id\}]/sshkeys'
|
||||
|
||||
Returns the SSH keys of an account.
|
||||
|
||||
.Request
|
||||
----
|
||||
GET /accounts/self/sshkeys HTTP/1.0
|
||||
----
|
||||
|
||||
As response the SSH keys of the account are returned as a list of
|
||||
link:#ssh-key-info[SshKeyInfo] entities.
|
||||
|
||||
.Response
|
||||
----
|
||||
HTTP/1.1 200 OK
|
||||
Content-Disposition: attachment
|
||||
Content-Type: application/json;charset=UTF-8
|
||||
|
||||
)]}'
|
||||
[
|
||||
{
|
||||
"seq": 1,
|
||||
"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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -1075,6 +1110,23 @@ link:access-control.html#capability_queryLimit[Query Limit] of a user.
|
||||
|`max` |Upper limit.
|
||||
|================================
|
||||
|
||||
[[ssh-key-info]]
|
||||
SshKeyInfo
|
||||
~~~~~~~~~~
|
||||
The `SshKeyInfo` entity contains information about an SSH key of a
|
||||
user.
|
||||
|
||||
[options="header",width="50%",cols="1,^1,5"]
|
||||
|=============================
|
||||
|Field Name ||Description
|
||||
|`seq` ||The sequence number of the SSH key.
|
||||
|`ssh_public_key`||The complete public SSH key.
|
||||
|`encoded_key` ||The encoded key.
|
||||
|`algorithm` ||The algorithm of the SSH key.
|
||||
|`comment` |optional|The comment of the SSH key.
|
||||
|`valid` ||Whether the SSH key is valid.
|
||||
|=============================
|
||||
|
||||
|
||||
GERRIT
|
||||
------
|
||||
|
||||
@@ -31,9 +31,6 @@ import java.util.Set;
|
||||
|
||||
@RpcImpl(version = Version.V2_0)
|
||||
public interface AccountSecurity extends RemoteJsonService {
|
||||
@SignInRequired
|
||||
void mySshKeys(AsyncCallback<List<AccountSshKey>> callback);
|
||||
|
||||
@Audit
|
||||
@SignInRequired
|
||||
void addSshKey(String keyText, AsyncCallback<AccountSshKey> callback);
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.google.gerrit.client.VoidResult;
|
||||
import com.google.gerrit.client.rpc.NativeString;
|
||||
import com.google.gerrit.client.rpc.RestApi;
|
||||
import com.google.gwt.core.client.JavaScriptObject;
|
||||
import com.google.gwt.core.client.JsArray;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
|
||||
/**
|
||||
@@ -33,6 +34,12 @@ public class AccountApi {
|
||||
.ifNoneMatch().put(in, cb);
|
||||
}
|
||||
|
||||
/** Retrieve SSH keys */
|
||||
public static void getSshKeys(String account,
|
||||
AsyncCallback<JsArray<SshKeyInfo>> cb) {
|
||||
new RestApi("/accounts/").id(account).view("sshkeys").get(cb);
|
||||
}
|
||||
|
||||
/** Generate a new HTTP password */
|
||||
public static void generateHttpPassword(String account,
|
||||
AsyncCallback<NativeString> cb) {
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
// 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.client.account;
|
||||
|
||||
import com.google.gwt.core.client.JavaScriptObject;
|
||||
|
||||
public class SshKeyInfo extends JavaScriptObject {
|
||||
public final native int seq() /*-{ return this.seq || 0; }-*/;
|
||||
public final native String sshPublicKey() /*-{ return this.ssh_public_key; }-*/;
|
||||
public final native String encodedKey() /*-{ return this.encoded_key; }-*/;
|
||||
public final native String algorithm() /*-{ return this.algorithm; }-*/;
|
||||
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() {
|
||||
}
|
||||
}
|
||||
@@ -17,12 +17,14 @@ package com.google.gerrit.client.account;
|
||||
import com.google.gerrit.client.ErrorDialog;
|
||||
import com.google.gerrit.client.Gerrit;
|
||||
import com.google.gerrit.client.rpc.GerritCallback;
|
||||
import com.google.gerrit.client.rpc.Natives;
|
||||
import com.google.gerrit.client.ui.ComplexDisclosurePanel;
|
||||
import com.google.gerrit.client.ui.FancyFlexTable;
|
||||
import com.google.gerrit.client.ui.SmallHeading;
|
||||
import com.google.gerrit.common.data.SshHostKey;
|
||||
import com.google.gerrit.common.errors.InvalidSshKeyException;
|
||||
import com.google.gerrit.reviewdb.client.AccountSshKey;
|
||||
import com.google.gwt.core.client.JsArray;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.dom.client.ClickHandler;
|
||||
import com.google.gwt.event.logical.shared.ValueChangeEvent;
|
||||
@@ -158,10 +160,12 @@ class SshPanel extends Composite {
|
||||
if (txt != null && txt.length() > 0) {
|
||||
addNew.setEnabled(false);
|
||||
Util.ACCOUNT_SEC.addSshKey(txt, new GerritCallback<AccountSshKey>() {
|
||||
public void onSuccess(final AccountSshKey result) {
|
||||
public void onSuccess(final AccountSshKey k) {
|
||||
addNew.setEnabled(true);
|
||||
addTxt.setText("");
|
||||
keys.addOneKey(result);
|
||||
keys.addOneKey(SshKeyInfo.create(k.getKey().get(),
|
||||
k.getSshPublicKey(), k.getEncodedKey(), k.getAlgorithm(),
|
||||
k.getComment(), k.isValid()));
|
||||
if (!keys.isVisible()) {
|
||||
showAddKeyBlock(false);
|
||||
setKeyTableVisible(true);
|
||||
@@ -196,10 +200,11 @@ class SshPanel extends Composite {
|
||||
protected void onLoad() {
|
||||
super.onLoad();
|
||||
|
||||
Util.ACCOUNT_SEC.mySshKeys(new GerritCallback<List<AccountSshKey>>() {
|
||||
public void onSuccess(final List<AccountSshKey> result) {
|
||||
keys.display(result);
|
||||
if (result.isEmpty() && keys.isVisible()) {
|
||||
AccountApi.getSshKeys("self", new GerritCallback<JsArray<SshKeyInfo>>() {
|
||||
@Override
|
||||
public void onSuccess(JsArray<SshKeyInfo> result) {
|
||||
keys.display(Natives.asList(result));
|
||||
if (result.length() == 0 && keys.isVisible()) {
|
||||
showAddKeyBlock(true);
|
||||
}
|
||||
if (++loadCount == 2) {
|
||||
@@ -229,7 +234,7 @@ class SshPanel extends Composite {
|
||||
addKeyBlock.setVisible(show);
|
||||
}
|
||||
|
||||
private class SshKeyTable extends FancyFlexTable<AccountSshKey> {
|
||||
private class SshKeyTable extends FancyFlexTable<SshKeyInfo> {
|
||||
private ValueChangeHandler<Boolean> updateDeleteHandler;
|
||||
|
||||
SshKeyTable() {
|
||||
@@ -257,9 +262,9 @@ class SshPanel extends Composite {
|
||||
void deleteChecked() {
|
||||
final HashSet<AccountSshKey.Id> ids = new HashSet<AccountSshKey.Id>();
|
||||
for (int row = 1; row < table.getRowCount(); row++) {
|
||||
final AccountSshKey k = getRowItem(row);
|
||||
final SshKeyInfo k = getRowItem(row);
|
||||
if (k != null && ((CheckBox) table.getWidget(row, 1)).getValue()) {
|
||||
ids.add(k.getKey());
|
||||
ids.add(new AccountSshKey.Id(Gerrit.getUserAccount().getId(), k.seq()));
|
||||
}
|
||||
}
|
||||
if (ids.isEmpty()) {
|
||||
@@ -268,15 +273,17 @@ class SshPanel extends Composite {
|
||||
Util.ACCOUNT_SEC.deleteSshKeys(ids, new GerritCallback<VoidResult>() {
|
||||
public void onSuccess(final VoidResult result) {
|
||||
for (int row = 1; row < table.getRowCount();) {
|
||||
final AccountSshKey k = getRowItem(row);
|
||||
if (k != null && ids.contains(k.getKey())) {
|
||||
final SshKeyInfo k = getRowItem(row);
|
||||
if (k != null
|
||||
&& ids.contains(new AccountSshKey.Id(Gerrit.getUserAccount()
|
||||
.getId(), k.seq()))) {
|
||||
table.removeRow(row);
|
||||
} else {
|
||||
row++;
|
||||
}
|
||||
}
|
||||
if (table.getRowCount() == 1) {
|
||||
display(Collections.<AccountSshKey> emptyList());
|
||||
display(Collections.<SshKeyInfo> emptyList());
|
||||
} else {
|
||||
updateDeleteButton();
|
||||
}
|
||||
@@ -285,14 +292,14 @@ class SshPanel extends Composite {
|
||||
}
|
||||
}
|
||||
|
||||
void display(final List<AccountSshKey> result) {
|
||||
void display(final List<SshKeyInfo> result) {
|
||||
if (result.isEmpty()) {
|
||||
setKeyTableVisible(false);
|
||||
showAddKeyBlock(true);
|
||||
} else {
|
||||
while (1 < table.getRowCount())
|
||||
table.removeRow(table.getRowCount() - 1);
|
||||
for (final AccountSshKey k : result) {
|
||||
for (final SshKeyInfo k : result) {
|
||||
addOneKey(k);
|
||||
}
|
||||
setKeyTableVisible(true);
|
||||
@@ -300,7 +307,7 @@ class SshPanel extends Composite {
|
||||
}
|
||||
}
|
||||
|
||||
void addOneKey(final AccountSshKey k) {
|
||||
void addOneKey(final SshKeyInfo k) {
|
||||
final FlexCellFormatter fmt = table.getFlexCellFormatter();
|
||||
final int row = table.getRowCount();
|
||||
table.insertRow(row);
|
||||
@@ -318,13 +325,13 @@ class SshPanel extends Composite {
|
||||
table.setText(row, 2, Util.C.sshKeyInvalid());
|
||||
fmt.addStyleName(row, 2, Gerrit.RESOURCES.css().sshKeyPanelInvalid());
|
||||
}
|
||||
table.setText(row, 3, k.getAlgorithm());
|
||||
table.setText(row, 3, k.algorithm());
|
||||
|
||||
CopyableLabel keyLabel = new CopyableLabel(k.getSshPublicKey());
|
||||
keyLabel.setPreviewText(elide(k.getEncodedKey(), 40));
|
||||
CopyableLabel keyLabel = new CopyableLabel(k.sshPublicKey());
|
||||
keyLabel.setPreviewText(elide(k.encodedKey(), 40));
|
||||
table.setWidget(row, 4, keyLabel);
|
||||
|
||||
table.setText(row, 5, k.getComment());
|
||||
table.setText(row, 5, k.comment());
|
||||
|
||||
fmt.addStyleName(row, 1, Gerrit.RESOURCES.css().iconCell());
|
||||
fmt.addStyleName(row, 4, Gerrit.RESOURCES.css().sshKeyPanelEncodedKey());
|
||||
|
||||
@@ -106,15 +106,6 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
|
||||
this.groupCache = groupCache;
|
||||
}
|
||||
|
||||
public void mySshKeys(final AsyncCallback<List<AccountSshKey>> callback) {
|
||||
run(callback, new Action<List<AccountSshKey>>() {
|
||||
public List<AccountSshKey> run(ReviewDb db) throws OrmException {
|
||||
IdentifiedUser u = user.get();
|
||||
return db.accountSshKeys().byAccount(u.getAccountId()).toList();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void addSshKey(final String keyText,
|
||||
final AsyncCallback<AccountSshKey> callback) {
|
||||
run(callback, new Action<AccountSshKey>() {
|
||||
|
||||
@@ -29,6 +29,9 @@ public class AccountResource implements RestResource {
|
||||
public static final TypeLiteral<RestView<Email>> EMAIL_KIND =
|
||||
new TypeLiteral<RestView<Email>>() {};
|
||||
|
||||
public static final TypeLiteral<RestView<SshKey>> SSH_KEY_KIND =
|
||||
new TypeLiteral<RestView<SshKey>>() {};
|
||||
|
||||
private final IdentifiedUser user;
|
||||
|
||||
public AccountResource(IdentifiedUser user) {
|
||||
@@ -73,4 +76,11 @@ public class AccountResource implements RestResource {
|
||||
return email;
|
||||
}
|
||||
}
|
||||
|
||||
static class SshKey extends AccountResource {
|
||||
|
||||
public SshKey(IdentifiedUser user) {
|
||||
super(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
// 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.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||
import com.google.gerrit.reviewdb.client.AccountSshKey;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GetSshKeys implements RestReadView<AccountResource> {
|
||||
|
||||
private final Provider<CurrentUser> self;
|
||||
private final Provider<ReviewDb> dbProvider;
|
||||
|
||||
@Inject
|
||||
GetSshKeys(Provider<CurrentUser> self, Provider<ReviewDb> dbProvider) {
|
||||
this.self = self;
|
||||
this.dbProvider = dbProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SshKeyInfo> apply(AccountResource rsrc) throws AuthException,
|
||||
OrmException {
|
||||
if (self.get() != rsrc.getUser()
|
||||
&& !self.get().getCapabilities().canAdministrateServer()) {
|
||||
throw new AuthException("not allowed to get SSH keys");
|
||||
}
|
||||
|
||||
List<SshKeyInfo> sshKeys = Lists.newArrayList();
|
||||
for (AccountSshKey sshKey : dbProvider.get().accountSshKeys()
|
||||
.byAccount(rsrc.getUser().getAccountId()).toList()) {
|
||||
SshKeyInfo info = new SshKeyInfo();
|
||||
info.seq = sshKey.getKey().get();
|
||||
info.sshPublicKey = sshKey.getSshPublicKey();
|
||||
info.encodedKey = sshKey.getEncodedKey();
|
||||
info.algorithm = sshKey.getAlgorithm();
|
||||
info.comment = Strings.emptyToNull(sshKey.getComment());
|
||||
info.valid = sshKey.isValid();
|
||||
sshKeys.add(info);
|
||||
}
|
||||
return sshKeys;
|
||||
}
|
||||
|
||||
public static class SshKeyInfo {
|
||||
public int seq;
|
||||
public String sshPublicKey;
|
||||
public String encodedKey;
|
||||
public String algorithm;
|
||||
public String comment;
|
||||
public boolean valid;
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ package com.google.gerrit.server.account;
|
||||
import static com.google.gerrit.server.account.AccountResource.ACCOUNT_KIND;
|
||||
import static com.google.gerrit.server.account.AccountResource.CAPABILITY_KIND;
|
||||
import static com.google.gerrit.server.account.AccountResource.EMAIL_KIND;
|
||||
import static com.google.gerrit.server.account.AccountResource.SSH_KEY_KIND;
|
||||
|
||||
import com.google.gerrit.extensions.registration.DynamicMap;
|
||||
import com.google.gerrit.extensions.restapi.RestApiModule;
|
||||
@@ -30,6 +31,7 @@ public class Module extends RestApiModule {
|
||||
|
||||
DynamicMap.mapOf(binder(), ACCOUNT_KIND);
|
||||
DynamicMap.mapOf(binder(), EMAIL_KIND);
|
||||
DynamicMap.mapOf(binder(), SSH_KEY_KIND);
|
||||
DynamicMap.mapOf(binder(), CAPABILITY_KIND);
|
||||
|
||||
put(ACCOUNT_KIND).to(PutAccount.class);
|
||||
@@ -48,6 +50,7 @@ public class Module extends RestApiModule {
|
||||
get(ACCOUNT_KIND, "password.http").to(GetHttpPassword.class);
|
||||
put(ACCOUNT_KIND, "password.http").to(PutHttpPassword.class);
|
||||
delete(ACCOUNT_KIND, "password.http").to(PutHttpPassword.class);
|
||||
child(ACCOUNT_KIND, "sshkeys").to(SshKeys.class);
|
||||
get(ACCOUNT_KIND, "avatar").to(GetAvatar.class);
|
||||
get(ACCOUNT_KIND, "avatar.change.url").to(GetAvatarChangeUrl.class);
|
||||
child(ACCOUNT_KIND, "capabilities").to(Capabilities.class);
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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.gerrit.extensions.registration.DynamicMap;
|
||||
import com.google.gerrit.extensions.restapi.ChildCollection;
|
||||
import com.google.gerrit.extensions.restapi.IdString;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
import com.google.gerrit.extensions.restapi.RestView;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
|
||||
public class SshKeys implements
|
||||
ChildCollection<AccountResource, AccountResource.SshKey> {
|
||||
private final DynamicMap<RestView<AccountResource.SshKey>> views;
|
||||
private final Provider<GetSshKeys> list;
|
||||
|
||||
@Inject
|
||||
SshKeys(DynamicMap<RestView<AccountResource.SshKey>> views,
|
||||
Provider<GetSshKeys> list) {
|
||||
this.views = views;
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestView<AccountResource> list() {
|
||||
return list.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccountResource.SshKey parse(AccountResource parent, IdString id)
|
||||
throws ResourceNotFoundException {
|
||||
throw new ResourceNotFoundException(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicMap<RestView<AccountResource.SshKey>> views() {
|
||||
return views;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user