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`.
|
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]]
|
||||||
List Account Emails
|
List Account Emails
|
||||||
~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~
|
||||||
@@ -965,6 +1044,24 @@ Only Gerrit administrators are allowed to add email addresses without
|
|||||||
confirmation.
|
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]]
|
[[query-limit-info]]
|
||||||
QueryLimitInfo
|
QueryLimitInfo
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
@@ -47,16 +47,6 @@ public interface AccountSecurity extends RemoteJsonService {
|
|||||||
@SignInRequired
|
@SignInRequired
|
||||||
void changeUserName(String newName, AsyncCallback<VoidResult> callback);
|
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
|
@SignInRequired
|
||||||
void myExternalIds(AsyncCallback<List<AccountExternalId>> callback);
|
void myExternalIds(AsyncCallback<List<AccountExternalId>> callback);
|
||||||
|
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
package com.google.gerrit.client.account;
|
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.NativeString;
|
||||||
import com.google.gerrit.client.rpc.RestApi;
|
import com.google.gerrit.client.rpc.RestApi;
|
||||||
import com.google.gwt.core.client.JavaScriptObject;
|
import com.google.gwt.core.client.JavaScriptObject;
|
||||||
@@ -31,4 +32,29 @@ public class AccountApi {
|
|||||||
new RestApi("/accounts/").id(account).view("emails").id(email)
|
new RestApi("/accounts/").id(account).view("emails").id(email)
|
||||||
.ifNoneMatch().put(in, cb);
|
.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 static com.google.gerrit.reviewdb.client.AccountExternalId.SCHEME_USERNAME;
|
||||||
|
|
||||||
import com.google.gerrit.client.Gerrit;
|
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.GerritCallback;
|
||||||
|
import com.google.gerrit.client.rpc.NativeString;
|
||||||
import com.google.gerrit.client.rpc.ScreenLoadCallback;
|
import com.google.gerrit.client.rpc.ScreenLoadCallback;
|
||||||
import com.google.gerrit.reviewdb.client.AccountExternalId;
|
import com.google.gerrit.reviewdb.client.AccountExternalId;
|
||||||
import com.google.gwt.event.dom.client.ClickEvent;
|
import com.google.gwt.event.dom.client.ClickEvent;
|
||||||
@@ -152,10 +154,12 @@ public class MyPasswordScreen extends SettingsScreen {
|
|||||||
private void doGeneratePassword() {
|
private void doGeneratePassword() {
|
||||||
if (id != null) {
|
if (id != null) {
|
||||||
enableUI(false);
|
enableUI(false);
|
||||||
Util.ACCOUNT_SEC.generatePassword(id.getKey(),
|
AccountApi.generateHttpPassword("self",
|
||||||
new GerritCallback<AccountExternalId>() {
|
new GerritCallback<NativeString>() {
|
||||||
public void onSuccess(final AccountExternalId result) {
|
@Override
|
||||||
display(result);
|
public void onSuccess(NativeString newPassword) {
|
||||||
|
id.setPassword(newPassword.asString());
|
||||||
|
display(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -169,10 +173,12 @@ public class MyPasswordScreen extends SettingsScreen {
|
|||||||
private void doClearPassword() {
|
private void doClearPassword() {
|
||||||
if (id != null) {
|
if (id != null) {
|
||||||
enableUI(false);
|
enableUI(false);
|
||||||
Util.ACCOUNT_SEC.clearPassword(id.getKey(),
|
AccountApi.clearHttpPassword("self",
|
||||||
new GerritCallback<AccountExternalId>() {
|
new GerritCallback<VoidResult>() {
|
||||||
public void onSuccess(final AccountExternalId result) {
|
@Override
|
||||||
display(result);
|
public void onSuccess(VoidResult result) {
|
||||||
|
id.setPassword(null);
|
||||||
|
display(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -29,8 +29,6 @@ import com.google.gerrit.httpd.rpc.UiRpcModule;
|
|||||||
import com.google.gerrit.lifecycle.LifecycleModule;
|
import com.google.gerrit.lifecycle.LifecycleModule;
|
||||||
import com.google.gerrit.server.CmdLineParserModule;
|
import com.google.gerrit.server.CmdLineParserModule;
|
||||||
import com.google.gerrit.server.RemotePeer;
|
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.AuthConfig;
|
||||||
import com.google.gerrit.server.config.CanonicalWebUrl;
|
import com.google.gerrit.server.config.CanonicalWebUrl;
|
||||||
import com.google.gerrit.server.config.FactoryModule;
|
import com.google.gerrit.server.config.FactoryModule;
|
||||||
@@ -130,10 +128,8 @@ public class WebModule extends FactoryModule {
|
|||||||
bind(GerritConfig.class).toProvider(GerritConfigProvider.class);
|
bind(GerritConfig.class).toProvider(GerritConfigProvider.class);
|
||||||
DynamicSet.setOf(binder(), WebUiPlugin.class);
|
DynamicSet.setOf(binder(), WebUiPlugin.class);
|
||||||
|
|
||||||
factory(ClearPassword.Factory.class);
|
|
||||||
install(new AsyncReceiveCommits.Module());
|
install(new AsyncReceiveCommits.Module());
|
||||||
install(new CmdLineParserModule());
|
install(new CmdLineParserModule());
|
||||||
factory(GeneratePassword.Factory.class);
|
|
||||||
|
|
||||||
bind(SocketAddress.class).annotatedWith(RemotePeer.class).toProvider(
|
bind(SocketAddress.class).annotatedWith(RemotePeer.class).toProvider(
|
||||||
HttpRemotePeerProvider.class).in(RequestScoped.class);
|
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.AccountException;
|
||||||
import com.google.gerrit.server.account.AccountManager;
|
import com.google.gerrit.server.account.AccountManager;
|
||||||
import com.google.gerrit.server.account.ChangeUserName;
|
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.GroupCache;
|
||||||
import com.google.gerrit.server.account.Realm;
|
import com.google.gerrit.server.account.Realm;
|
||||||
import com.google.gerrit.server.contact.ContactStore;
|
import com.google.gerrit.server.contact.ContactStore;
|
||||||
@@ -70,8 +68,6 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
|
|||||||
private final AccountManager accountManager;
|
private final AccountManager accountManager;
|
||||||
private final boolean useContactInfo;
|
private final boolean useContactInfo;
|
||||||
|
|
||||||
private final ClearPassword.Factory clearPasswordFactory;
|
|
||||||
private final GeneratePassword.Factory generatePasswordFactory;
|
|
||||||
private final ChangeUserName.CurrentUser changeUserNameFactory;
|
private final ChangeUserName.CurrentUser changeUserNameFactory;
|
||||||
private final DeleteExternalIds.Factory deleteExternalIdsFactory;
|
private final DeleteExternalIds.Factory deleteExternalIdsFactory;
|
||||||
private final ExternalIdDetailFactory.Factory externalIdDetailFactory;
|
private final ExternalIdDetailFactory.Factory externalIdDetailFactory;
|
||||||
@@ -86,8 +82,6 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
|
|||||||
final EmailTokenVerifier etv, final ProjectCache pc,
|
final EmailTokenVerifier etv, final ProjectCache pc,
|
||||||
final SshKeyCache skc, final AccountByEmailCache abec,
|
final SshKeyCache skc, final AccountByEmailCache abec,
|
||||||
final AccountCache uac, final AccountManager am,
|
final AccountCache uac, final AccountManager am,
|
||||||
final ClearPassword.Factory clearPasswordFactory,
|
|
||||||
final GeneratePassword.Factory generatePasswordFactory,
|
|
||||||
final ChangeUserName.CurrentUser changeUserNameFactory,
|
final ChangeUserName.CurrentUser changeUserNameFactory,
|
||||||
final DeleteExternalIds.Factory deleteExternalIdsFactory,
|
final DeleteExternalIds.Factory deleteExternalIdsFactory,
|
||||||
final ExternalIdDetailFactory.Factory externalIdDetailFactory,
|
final ExternalIdDetailFactory.Factory externalIdDetailFactory,
|
||||||
@@ -105,8 +99,6 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
|
|||||||
|
|
||||||
useContactInfo = contactStore != null && contactStore.isEnabled();
|
useContactInfo = contactStore != null && contactStore.isEnabled();
|
||||||
|
|
||||||
this.clearPasswordFactory = clearPasswordFactory;
|
|
||||||
this.generatePasswordFactory = generatePasswordFactory;
|
|
||||||
this.changeUserNameFactory = changeUserNameFactory;
|
this.changeUserNameFactory = changeUserNameFactory;
|
||||||
this.deleteExternalIdsFactory = deleteExternalIdsFactory;
|
this.deleteExternalIdsFactory = deleteExternalIdsFactory;
|
||||||
this.externalIdDetailFactory = externalIdDetailFactory;
|
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) {
|
public void myExternalIds(AsyncCallback<List<AccountExternalId>> callback) {
|
||||||
externalIdDetailFactory.create().to(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);
|
put(EMAIL_KIND).to(PutEmail.class);
|
||||||
delete(EMAIL_KIND).to(DeleteEmail.class);
|
delete(EMAIL_KIND).to(DeleteEmail.class);
|
||||||
put(EMAIL_KIND, "preferred").to(PutPreferred.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").to(GetAvatar.class);
|
||||||
get(ACCOUNT_KIND, "avatar.change.url").to(GetAvatarChangeUrl.class);
|
get(ACCOUNT_KIND, "avatar.change.url").to(GetAvatarChangeUrl.class);
|
||||||
child(ACCOUNT_KIND, "capabilities").to(Capabilities.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