Support to get/set/delete/generate an HTTP password via REST
The HTTP password of a user can now be retrieved by GET on /accounts/<account-id>/password.http. By PUT on /accounts/<account-id>/password.http a new HTTP password can be generated or set. Directly setting an HTTP password is only allowed for Gerrit administrators. By DELETE on /accounts/<account-id>/password.http the HTTP password can be cleared. The WebUI is adapted to use the new REST endpoints to generate/clear the HTTP password. The old RPCs for this are deleted. Change-Id: I1424f1d4b45e5409095b51f05c9c6afe26e66800 Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
This commit is contained in:

committed by
Edwin Kempin

parent
12c35d3ed8
commit
d9cdf5eea4
@@ -222,6 +222,85 @@ Sets the account state to inactive.
|
||||
|
||||
If the account was already inactive the response is `404 Not Found`.
|
||||
|
||||
[[get-http-password]]
|
||||
Get HTTP Password
|
||||
~~~~~~~~~~~~~~~~~
|
||||
[verse]
|
||||
'GET /accounts/link:#account-id[\{account-id\}]/password.http'
|
||||
|
||||
Retrieves the HTTP password of an account.
|
||||
|
||||
.Request
|
||||
----
|
||||
GET /accounts/john.doe@example.com/password.http HTTP/1.0
|
||||
----
|
||||
|
||||
.Response
|
||||
----
|
||||
HTTP/1.1 200 OK
|
||||
Content-Disposition: attachment
|
||||
Content-Type: application/json;charset=UTF-8
|
||||
|
||||
)]}'
|
||||
"ETxgpih8xrNs"
|
||||
----
|
||||
|
||||
If the account does not have an HTTP password the response is `404 Not Found`.
|
||||
|
||||
[[set-http-password]]
|
||||
Set/Generate HTTP Password
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
[verse]
|
||||
'PUT /accounts/link:#account-id[\{account-id\}]/password.http'
|
||||
|
||||
Sets/Generates the HTTP password of an account.
|
||||
|
||||
The options for setting/generating the HTTP password must be provided
|
||||
in the request body inside a link:#http-password-input[
|
||||
HttpPasswordInput] entity.
|
||||
|
||||
.Request
|
||||
----
|
||||
PUT /accounts/self/password.http HTTP/1.0
|
||||
Content-Type: application/json;charset=UTF-8
|
||||
|
||||
{
|
||||
"generate": true
|
||||
}
|
||||
----
|
||||
|
||||
As response the new HTTP password is returned.
|
||||
|
||||
.Response
|
||||
----
|
||||
HTTP/1.1 200 OK
|
||||
Content-Disposition: attachment
|
||||
Content-Type: application/json;charset=UTF-8
|
||||
|
||||
)]}'
|
||||
"ETxgpih8xrNs"
|
||||
----
|
||||
|
||||
If the HTTP password was deleted the response is "`204 No Content`".
|
||||
|
||||
[[delete-http-password]]
|
||||
Delete HTTP Password
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
[verse]
|
||||
'DELETE /accounts/link:#account-id[\{account-id\}]/password.http'
|
||||
|
||||
Deletes the HTTP password of an account.
|
||||
|
||||
.Request
|
||||
----
|
||||
DELETE /accounts/self/password.http HTTP/1.0
|
||||
----
|
||||
|
||||
.Response
|
||||
----
|
||||
HTTP/1.1 204 No Content
|
||||
----
|
||||
|
||||
[[list-account-emails]]
|
||||
List Account Emails
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
@@ -965,6 +1044,24 @@ Only Gerrit administrators are allowed to add email addresses without
|
||||
confirmation.
|
||||
|==============================
|
||||
|
||||
[[http-password-input]]
|
||||
HttpPasswordInput
|
||||
~~~~~~~~~~~~~~~~~
|
||||
The `HttpPasswordInput` entity contains information for setting/generating
|
||||
an HTTP password.
|
||||
|
||||
[options="header",width="50%",cols="1,^1,5"]
|
||||
|============================
|
||||
|Field Name ||Description
|
||||
|`generate` |`false` if not set|
|
||||
Whether a new HTTP password should be generated
|
||||
|`http_password`|optional|
|
||||
The new HTTP password. Only Gerrit administrators may set the HTTP
|
||||
password directly. +
|
||||
If empty or not set and `generate` is false or not set, the HTTP
|
||||
password is deleted.
|
||||
|============================
|
||||
|
||||
[[query-limit-info]]
|
||||
QueryLimitInfo
|
||||
~~~~~~~~~~~~~~
|
||||
|
@@ -47,16 +47,6 @@ public interface AccountSecurity extends RemoteJsonService {
|
||||
@SignInRequired
|
||||
void changeUserName(String newName, AsyncCallback<VoidResult> callback);
|
||||
|
||||
@Audit
|
||||
@SignInRequired
|
||||
void generatePassword(AccountExternalId.Key key,
|
||||
AsyncCallback<AccountExternalId> callback);
|
||||
|
||||
@Audit
|
||||
@SignInRequired
|
||||
void clearPassword(AccountExternalId.Key key,
|
||||
AsyncCallback<AccountExternalId> gerritCallback);
|
||||
|
||||
@SignInRequired
|
||||
void myExternalIds(AsyncCallback<List<AccountExternalId>> callback);
|
||||
|
||||
|
@@ -14,6 +14,7 @@
|
||||
|
||||
package com.google.gerrit.client.account;
|
||||
|
||||
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;
|
||||
@@ -31,4 +32,29 @@ public class AccountApi {
|
||||
new RestApi("/accounts/").id(account).view("emails").id(email)
|
||||
.ifNoneMatch().put(in, cb);
|
||||
}
|
||||
|
||||
/** Generate a new HTTP password */
|
||||
public static void generateHttpPassword(String account,
|
||||
AsyncCallback<NativeString> cb) {
|
||||
HttpPasswordInput in = HttpPasswordInput.create();
|
||||
in.generate(true);
|
||||
new RestApi("/accounts/").id(account).view("password.http").put(in, cb);
|
||||
}
|
||||
|
||||
/** Clear HTTP password */
|
||||
public static void clearHttpPassword(String account,
|
||||
AsyncCallback<VoidResult> cb) {
|
||||
new RestApi("/accounts/").id(account).view("password.http").delete(cb);
|
||||
}
|
||||
|
||||
private static class HttpPasswordInput extends JavaScriptObject {
|
||||
final native void generate(boolean g) /*-{ if(g)this.generate=g; }-*/;
|
||||
|
||||
static HttpPasswordInput create() {
|
||||
return createObject().cast();
|
||||
}
|
||||
|
||||
protected HttpPasswordInput() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -17,7 +17,9 @@ package com.google.gerrit.client.account;
|
||||
import static com.google.gerrit.reviewdb.client.AccountExternalId.SCHEME_USERNAME;
|
||||
|
||||
import com.google.gerrit.client.Gerrit;
|
||||
import com.google.gerrit.client.VoidResult;
|
||||
import com.google.gerrit.client.rpc.GerritCallback;
|
||||
import com.google.gerrit.client.rpc.NativeString;
|
||||
import com.google.gerrit.client.rpc.ScreenLoadCallback;
|
||||
import com.google.gerrit.reviewdb.client.AccountExternalId;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
@@ -152,10 +154,12 @@ public class MyPasswordScreen extends SettingsScreen {
|
||||
private void doGeneratePassword() {
|
||||
if (id != null) {
|
||||
enableUI(false);
|
||||
Util.ACCOUNT_SEC.generatePassword(id.getKey(),
|
||||
new GerritCallback<AccountExternalId>() {
|
||||
public void onSuccess(final AccountExternalId result) {
|
||||
display(result);
|
||||
AccountApi.generateHttpPassword("self",
|
||||
new GerritCallback<NativeString>() {
|
||||
@Override
|
||||
public void onSuccess(NativeString newPassword) {
|
||||
id.setPassword(newPassword.asString());
|
||||
display(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -169,10 +173,12 @@ public class MyPasswordScreen extends SettingsScreen {
|
||||
private void doClearPassword() {
|
||||
if (id != null) {
|
||||
enableUI(false);
|
||||
Util.ACCOUNT_SEC.clearPassword(id.getKey(),
|
||||
new GerritCallback<AccountExternalId>() {
|
||||
public void onSuccess(final AccountExternalId result) {
|
||||
display(result);
|
||||
AccountApi.clearHttpPassword("self",
|
||||
new GerritCallback<VoidResult>() {
|
||||
@Override
|
||||
public void onSuccess(VoidResult result) {
|
||||
id.setPassword(null);
|
||||
display(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -29,8 +29,6 @@ import com.google.gerrit.httpd.rpc.UiRpcModule;
|
||||
import com.google.gerrit.lifecycle.LifecycleModule;
|
||||
import com.google.gerrit.server.CmdLineParserModule;
|
||||
import com.google.gerrit.server.RemotePeer;
|
||||
import com.google.gerrit.server.account.ClearPassword;
|
||||
import com.google.gerrit.server.account.GeneratePassword;
|
||||
import com.google.gerrit.server.config.AuthConfig;
|
||||
import com.google.gerrit.server.config.CanonicalWebUrl;
|
||||
import com.google.gerrit.server.config.FactoryModule;
|
||||
@@ -130,10 +128,8 @@ public class WebModule extends FactoryModule {
|
||||
bind(GerritConfig.class).toProvider(GerritConfigProvider.class);
|
||||
DynamicSet.setOf(binder(), WebUiPlugin.class);
|
||||
|
||||
factory(ClearPassword.Factory.class);
|
||||
install(new AsyncReceiveCommits.Module());
|
||||
install(new CmdLineParserModule());
|
||||
factory(GeneratePassword.Factory.class);
|
||||
|
||||
bind(SocketAddress.class).annotatedWith(RemotePeer.class).toProvider(
|
||||
HttpRemotePeerProvider.class).in(RequestScoped.class);
|
||||
|
@@ -39,8 +39,6 @@ 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.ChangeUserName;
|
||||
import com.google.gerrit.server.account.ClearPassword;
|
||||
import com.google.gerrit.server.account.GeneratePassword;
|
||||
import com.google.gerrit.server.account.GroupCache;
|
||||
import com.google.gerrit.server.account.Realm;
|
||||
import com.google.gerrit.server.contact.ContactStore;
|
||||
@@ -70,8 +68,6 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
|
||||
private final AccountManager accountManager;
|
||||
private final boolean useContactInfo;
|
||||
|
||||
private final ClearPassword.Factory clearPasswordFactory;
|
||||
private final GeneratePassword.Factory generatePasswordFactory;
|
||||
private final ChangeUserName.CurrentUser changeUserNameFactory;
|
||||
private final DeleteExternalIds.Factory deleteExternalIdsFactory;
|
||||
private final ExternalIdDetailFactory.Factory externalIdDetailFactory;
|
||||
@@ -86,8 +82,6 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
|
||||
final EmailTokenVerifier etv, final ProjectCache pc,
|
||||
final SshKeyCache skc, final AccountByEmailCache abec,
|
||||
final AccountCache uac, final AccountManager am,
|
||||
final ClearPassword.Factory clearPasswordFactory,
|
||||
final GeneratePassword.Factory generatePasswordFactory,
|
||||
final ChangeUserName.CurrentUser changeUserNameFactory,
|
||||
final DeleteExternalIds.Factory deleteExternalIdsFactory,
|
||||
final ExternalIdDetailFactory.Factory externalIdDetailFactory,
|
||||
@@ -105,8 +99,6 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
|
||||
|
||||
useContactInfo = contactStore != null && contactStore.isEnabled();
|
||||
|
||||
this.clearPasswordFactory = clearPasswordFactory;
|
||||
this.generatePasswordFactory = generatePasswordFactory;
|
||||
this.changeUserNameFactory = changeUserNameFactory;
|
||||
this.deleteExternalIdsFactory = deleteExternalIdsFactory;
|
||||
this.externalIdDetailFactory = externalIdDetailFactory;
|
||||
@@ -179,18 +171,6 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generatePassword(AccountExternalId.Key key,
|
||||
AsyncCallback<AccountExternalId> callback) {
|
||||
Handler.wrap(generatePasswordFactory.create(key)).to(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearPassword(AccountExternalId.Key key,
|
||||
AsyncCallback<AccountExternalId> callback) {
|
||||
Handler.wrap(clearPasswordFactory.create(key)).to(callback);
|
||||
}
|
||||
|
||||
public void myExternalIds(AsyncCallback<List<AccountExternalId>> callback) {
|
||||
externalIdDetailFactory.create().to(callback);
|
||||
}
|
||||
|
@@ -1,63 +0,0 @@
|
||||
// Copyright (C) 2010 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.common.errors.NoSuchEntityException;
|
||||
import com.google.gerrit.reviewdb.client.AccountExternalId;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/** Operation to clear a password for an account. */
|
||||
public class ClearPassword implements Callable<AccountExternalId> {
|
||||
public interface Factory {
|
||||
ClearPassword create(AccountExternalId.Key forUser);
|
||||
}
|
||||
|
||||
private final AccountCache accountCache;
|
||||
private final ReviewDb db;
|
||||
private final IdentifiedUser user;
|
||||
|
||||
private final AccountExternalId.Key forUser;
|
||||
|
||||
@Inject
|
||||
ClearPassword(final AccountCache accountCache, final ReviewDb db,
|
||||
final IdentifiedUser user,
|
||||
|
||||
@Assisted AccountExternalId.Key forUser) {
|
||||
this.accountCache = accountCache;
|
||||
this.db = db;
|
||||
this.user = user;
|
||||
|
||||
this.forUser = forUser;
|
||||
}
|
||||
|
||||
public AccountExternalId call() throws OrmException, NoSuchEntityException {
|
||||
AccountExternalId id = db.accountExternalIds().get(forUser);
|
||||
if (id == null || !user.getAccountId().equals(id.getAccountId())) {
|
||||
throw new NoSuchEntityException();
|
||||
}
|
||||
|
||||
id.setPassword(null);
|
||||
db.accountExternalIds().update(Collections.singleton(id));
|
||||
accountCache.evict(user.getAccountId());
|
||||
return id;
|
||||
}
|
||||
}
|
@@ -1,93 +0,0 @@
|
||||
// Copyright (C) 2010 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.common.errors.NoSuchEntityException;
|
||||
import com.google.gerrit.reviewdb.client.AccountExternalId;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/** Operation to generate a password for an account. */
|
||||
public class GeneratePassword implements Callable<AccountExternalId> {
|
||||
private static final int LEN = 12;
|
||||
private static final SecureRandom rng;
|
||||
|
||||
static {
|
||||
try {
|
||||
rng = SecureRandom.getInstance("SHA1PRNG");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("Cannot create RNG for password generator", e);
|
||||
}
|
||||
}
|
||||
|
||||
public interface Factory {
|
||||
GeneratePassword create(AccountExternalId.Key forUser);
|
||||
}
|
||||
|
||||
private final AccountCache accountCache;
|
||||
private final ReviewDb db;
|
||||
private final IdentifiedUser user;
|
||||
|
||||
private final AccountExternalId.Key forUser;
|
||||
|
||||
@Inject
|
||||
GeneratePassword(final AccountCache accountCache, final ReviewDb db,
|
||||
final IdentifiedUser user,
|
||||
|
||||
@Assisted AccountExternalId.Key forUser) {
|
||||
this.accountCache = accountCache;
|
||||
this.db = db;
|
||||
this.user = user;
|
||||
|
||||
this.forUser = forUser;
|
||||
}
|
||||
|
||||
public AccountExternalId call() throws OrmException, NoSuchEntityException {
|
||||
AccountExternalId id = db.accountExternalIds().get(forUser);
|
||||
if (id == null || !user.getAccountId().equals(id.getAccountId())) {
|
||||
throw new NoSuchEntityException();
|
||||
}
|
||||
|
||||
id.setPassword(generate());
|
||||
db.accountExternalIds().update(Collections.singleton(id));
|
||||
accountCache.evict(user.getAccountId());
|
||||
return id;
|
||||
}
|
||||
|
||||
private String generate() {
|
||||
byte[] rand = new byte[LEN];
|
||||
rng.nextBytes(rand);
|
||||
|
||||
byte[] enc = Base64.encodeBase64(rand, false);
|
||||
StringBuilder r = new StringBuilder(LEN);
|
||||
for (int i = 0; i < LEN; i++) {
|
||||
if (enc[i] == '=') {
|
||||
break;
|
||||
}
|
||||
r.append((char) enc[i]);
|
||||
}
|
||||
return r.toString();
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
// 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.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
|
||||
public class GetHttpPassword implements RestReadView<AccountResource> {
|
||||
|
||||
private final Provider<CurrentUser> self;
|
||||
|
||||
@Inject
|
||||
GetHttpPassword(Provider<CurrentUser> self) {
|
||||
this.self = self;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(AccountResource rsrc) throws AuthException,
|
||||
ResourceNotFoundException, OrmException {
|
||||
if (self.get() != rsrc.getUser()
|
||||
&& !self.get().getCapabilities().canAdministrateServer()) {
|
||||
throw new AuthException("not allowed to get http password");
|
||||
}
|
||||
AccountState s = rsrc.getUser().state();
|
||||
if (s.getUserName() == null) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
String p = s.getPassword(s.getUserName());
|
||||
if (p == null) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
return p;
|
||||
}
|
||||
}
|
@@ -45,6 +45,9 @@ public class Module extends RestApiModule {
|
||||
put(EMAIL_KIND).to(PutEmail.class);
|
||||
delete(EMAIL_KIND).to(DeleteEmail.class);
|
||||
put(EMAIL_KIND, "preferred").to(PutPreferred.class);
|
||||
get(ACCOUNT_KIND, "password.http").to(GetHttpPassword.class);
|
||||
put(ACCOUNT_KIND, "password.http").to(PutHttpPassword.class);
|
||||
delete(ACCOUNT_KIND, "password.http").to(PutHttpPassword.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,123 @@
|
||||
// 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 static com.google.gerrit.reviewdb.client.AccountExternalId.SCHEME_USERNAME;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
import com.google.gerrit.extensions.restapi.Response;
|
||||
import com.google.gerrit.extensions.restapi.RestModifyView;
|
||||
import com.google.gerrit.reviewdb.client.AccountExternalId;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.account.PutHttpPassword.Input;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Collections;
|
||||
|
||||
public class PutHttpPassword implements RestModifyView<AccountResource, Input> {
|
||||
static class Input {
|
||||
String httpPassword;
|
||||
boolean generate;
|
||||
}
|
||||
|
||||
private static final int LEN = 12;
|
||||
private static final SecureRandom rng;
|
||||
|
||||
static {
|
||||
try {
|
||||
rng = SecureRandom.getInstance("SHA1PRNG");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("Cannot create RNG for password generator", e);
|
||||
}
|
||||
}
|
||||
|
||||
private final Provider<CurrentUser> self;
|
||||
private final Provider<ReviewDb> dbProvider;
|
||||
private final AccountCache accountCache;
|
||||
|
||||
@Inject
|
||||
PutHttpPassword(Provider<CurrentUser> self, Provider<ReviewDb> dbProvider,
|
||||
AccountCache accountCache) {
|
||||
this.self = self;
|
||||
this.dbProvider = dbProvider;
|
||||
this.accountCache = accountCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<String> apply(AccountResource rsrc, Input input) throws AuthException,
|
||||
ResourceNotFoundException, ResourceConflictException, OrmException {
|
||||
if (self.get() != rsrc.getUser()
|
||||
&& !self.get().getCapabilities().canAdministrateServer()) {
|
||||
throw new AuthException("not allowed to set HTTP password");
|
||||
}
|
||||
if (input == null) {
|
||||
input = new Input();
|
||||
}
|
||||
if (rsrc.getUser().getUserName() == null) {
|
||||
throw new ResourceConflictException("username must be set");
|
||||
}
|
||||
AccountExternalId.Key key =
|
||||
new AccountExternalId.Key(SCHEME_USERNAME, rsrc.getUser().getUserName());
|
||||
AccountExternalId id = dbProvider.get().accountExternalIds().get(key);
|
||||
if (id == null) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
String newPassword;
|
||||
if (input.generate) {
|
||||
newPassword = generate();
|
||||
} else {
|
||||
if (!Strings.isNullOrEmpty(input.httpPassword)
|
||||
&& !self.get().getCapabilities().canAdministrateServer()) {
|
||||
throw new AuthException("not allowed to set HTTP password directly, "
|
||||
+ "need to be Gerrit administrator");
|
||||
}
|
||||
newPassword = Strings.emptyToNull(input.httpPassword);
|
||||
}
|
||||
|
||||
id.setPassword(newPassword);
|
||||
dbProvider.get().accountExternalIds().update(Collections.singleton(id));
|
||||
accountCache.evict(rsrc.getUser().getAccountId());
|
||||
|
||||
return Strings.isNullOrEmpty(newPassword)
|
||||
? Response.<String>none()
|
||||
: Response.ok(newPassword);
|
||||
}
|
||||
|
||||
private String generate() {
|
||||
byte[] rand = new byte[LEN];
|
||||
rng.nextBytes(rand);
|
||||
|
||||
byte[] enc = Base64.encodeBase64(rand, false);
|
||||
StringBuilder r = new StringBuilder(LEN);
|
||||
for (int i = 0; i < LEN; i++) {
|
||||
if (enc[i] == '=') {
|
||||
break;
|
||||
}
|
||||
r.append((char) enc[i]);
|
||||
}
|
||||
return r.toString();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user