Add REST endpoint to confirm emails
Use the new REST endpoint in the UI instead of the old
AccountSecurity.validateEmail(...) RPC.
AccountSecurity.validateEmail(...) is removed since it is no longer
used.
Change-Id: I561224e9d9ea31875df2bba838ee53f77f24c55b
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
diff --git a/Documentation/rest-api-config.txt b/Documentation/rest-api-config.txt
index 9ba8264..c641f94 100644
--- a/Documentation/rest-api-config.txt
+++ b/Documentation/rest-api-config.txt
@@ -126,6 +126,32 @@
}
----
+[[confirm-email]]
+=== Confirm Email
+--
+'PUT /config/server/email.confirm'
+--
+
+Confirms that the user owns an email address.
+
+The email token must be provided in the request body inside
+an link:#email-confirmation-input[EmailConfirmationInput] entity.
+
+.Request
+----
+ PUT /config/server/email.confirm HTTP/1.0
+ Content-Type: application/json; charset=UTF-8
+
+ {
+ "token": "Enim+QNbAo6TV8Hur8WwoUypI6apG7qBPvF+bw==$MTAwMDAwNDp0ZXN0QHRlc3QuZGU="
+ }
+----
+
+The response is "`204 No Content`".
+
+If the token is invalid or if it's the token of another user the
+request fails and the response is "`422 Unprocessable Entity`".
+
[[list-caches]]
=== List Caches
@@ -1132,6 +1158,18 @@
authentication.
|=================================
+[[email-confirmation-input]]
+=== EmailConfirmationInput
+The `EmailConfirmationInput` entity contains information for confirming
+an email address.
+
+[options="header",cols="1,6"]
+|=======================
+|Field Name |Description
+|`token` |
+The token that was sent by mail to a newly registered email address.
+|=======================
+
[[entries-info]]
=== EntriesInfo
The `EntriesInfo` entity contains information about the entries in a
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/ConfirmEmailIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/ConfirmEmailIT.java
new file mode 100644
index 0000000..9d8320ad
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/ConfirmEmailIT.java
@@ -0,0 +1,66 @@
+// Copyright (C) 2015 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.acceptance.rest.config;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.server.config.ConfirmEmail;
+import com.google.gerrit.server.mail.EmailTokenVerifier;
+import com.google.gerrit.testutil.ConfigSuite;
+import com.google.gwtjsonrpc.server.SignedToken;
+import com.google.inject.Inject;
+
+import org.apache.http.HttpStatus;
+import org.eclipse.jgit.lib.Config;
+import org.junit.Test;
+
+public class ConfirmEmailIT extends AbstractDaemonTest {
+ @ConfigSuite.Default
+ public static Config defaultConfig() {
+ Config cfg = new Config();
+ cfg.setString("auth", null, "registerEmailPrivateKey",
+ SignedToken.generateRandomKey());
+ return cfg;
+ }
+
+ @Inject
+ private EmailTokenVerifier emailTokenVerifier;
+
+ @Test
+ public void confirm() throws Exception {
+ ConfirmEmail.Input in = new ConfirmEmail.Input();
+ in.token = emailTokenVerifier.encode(admin.getId(), "new.mail@example.com");
+ RestResponse r = adminSession.put("/config/server/email.confirm", in);
+ assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT);
+ }
+
+ @Test
+ public void confirmForOtherUser_UnprocessableEntity() throws Exception {
+ ConfirmEmail.Input in = new ConfirmEmail.Input();
+ in.token = emailTokenVerifier.encode(user.getId(), "new.mail@example.com");
+ RestResponse r = adminSession.put("/config/server/email.confirm", in);
+ assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_UNPROCESSABLE_ENTITY);
+ }
+
+ @Test
+ public void confirmInvalidToken_UnprocessableEntity() throws Exception {
+ ConfirmEmail.Input in = new ConfirmEmail.Input();
+ in.token = "invalidToken";
+ RestResponse r = adminSession.put("/config/server/email.confirm", in);
+ assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_UNPROCESSABLE_ENTITY);
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountSecurity.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountSecurity.java
index 0a1f454..0ca0207 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountSecurity.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountSecurity.java
@@ -47,8 +47,4 @@
@SignInRequired
void enterAgreement(String agreementName,
AsyncCallback<VoidResult> callback);
-
- @Audit
- @SignInRequired
- void validateEmail(String token, AsyncCallback<VoidResult> callback);
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ValidateEmailScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ValidateEmailScreen.java
index 1164efc..b79723b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ValidateEmailScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ValidateEmailScreen.java
@@ -15,10 +15,11 @@
package com.google.gerrit.client.account;
import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.VoidResult;
+import com.google.gerrit.client.config.ConfigServerApi;
import com.google.gerrit.client.rpc.ScreenLoadCallback;
import com.google.gerrit.client.ui.AccountScreen;
import com.google.gerrit.common.PageLinks;
-import com.google.gwtjsonrpc.common.VoidResult;
public class ValidateEmailScreen extends AccountScreen {
private final String magicToken;
@@ -36,7 +37,7 @@
@Override
protected void onLoad() {
super.onLoad();
- Util.ACCOUNT_SEC.validateEmail(magicToken,
+ ConfigServerApi.confirmEmail(magicToken,
new ScreenLoadCallback<VoidResult>(this) {
@Override
protected void preDisplay(final VoidResult result) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ConfigServerApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ConfigServerApi.java
index ae5cad2..6e65ccd 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ConfigServerApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ConfigServerApi.java
@@ -14,11 +14,13 @@
package com.google.gerrit.client.config;
+import com.google.gerrit.client.VoidResult;
import com.google.gerrit.client.info.AccountPreferencesInfo;
import com.google.gerrit.client.info.ServerInfo;
import com.google.gerrit.client.info.TopMenuList;
import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.client.rpc.RestApi;
+import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.user.client.rpc.AsyncCallback;
/**
@@ -42,4 +44,21 @@
public static void serverInfo(AsyncCallback<ServerInfo> cb) {
new RestApi("/config/server/info").get(cb);
}
+
+ public static void confirmEmail(String token, AsyncCallback<VoidResult> cb) {
+ EmailConfirmationInput input = EmailConfirmationInput.create();
+ input.setToken(token);
+ new RestApi("/config/server/email.confirm").put(input, cb);
+ }
+
+ private static class EmailConfirmationInput extends JavaScriptObject {
+ final native void setToken(String t) /*-{ this.t = t; }-*/;
+
+ static EmailConfirmationInput create() {
+ return createObject().cast();
+ }
+
+ protected EmailConfirmationInput() {
+ }
+ }
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java
index ce235c4..55586b0 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java
@@ -34,12 +34,9 @@
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountByEmailCache;
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.GroupCache;
import com.google.gerrit.server.account.Realm;
import com.google.gerrit.server.contact.ContactStore;
-import com.google.gerrit.server.mail.EmailTokenVerifier;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gwtjsonrpc.common.AsyncCallback;
import com.google.gwtjsonrpc.common.VoidResult;
@@ -57,10 +54,8 @@
private final Realm realm;
private final ProjectCache projectCache;
private final Provider<IdentifiedUser> user;
- private final EmailTokenVerifier emailTokenVerifier;
private final AccountByEmailCache byEmailCache;
private final AccountCache accountCache;
- private final AccountManager accountManager;
private final boolean useContactInfo;
private final DeleteExternalIds.Factory deleteExternalIdsFactory;
@@ -74,9 +69,8 @@
AccountSecurityImpl(final Provider<ReviewDb> schema,
final Provider<CurrentUser> currentUser, final ContactStore cs,
final Realm r, final Provider<IdentifiedUser> u,
- final EmailTokenVerifier etv, final ProjectCache pc,
+ final ProjectCache pc,
final AccountByEmailCache abec, final AccountCache uac,
- final AccountManager am,
final DeleteExternalIds.Factory deleteExternalIdsFactory,
final ExternalIdDetailFactory.Factory externalIdDetailFactory,
final ChangeHooks hooks, final GroupCache groupCache,
@@ -85,11 +79,9 @@
contactStore = cs;
realm = r;
user = u;
- emailTokenVerifier = etv;
projectCache = pc;
byEmailCache = abec;
accountCache = uac;
- accountManager = am;
this.auditService = auditService;
useContactInfo = contactStore != null && contactStore.isEnabled();
@@ -201,22 +193,4 @@
}
});
}
-
- @Override
- public void validateEmail(final String tokenString,
- final AsyncCallback<VoidResult> callback) {
- try {
- EmailTokenVerifier.ParsedToken token = emailTokenVerifier.decode(tokenString);
- Account.Id currentUser = user.get().getAccountId();
- if (currentUser.equals(token.getAccountId())) {
- accountManager.link(currentUser, token.toAuthRequest());
- callback.onSuccess(VoidResult.INSTANCE);
- } else {
- throw new EmailTokenVerifier.InvalidTokenException();
- }
- } catch (EmailTokenVerifier.InvalidTokenException | OrmException
- | AccountException e) {
- callback.onFailure(e);
- }
- }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfirmEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfirmEmail.java
new file mode 100644
index 0000000..789af9d
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfirmEmail.java
@@ -0,0 +1,80 @@
+// Copyright (C) 2015 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.config;
+
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.DefaultInput;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountException;
+import com.google.gerrit.server.account.AccountManager;
+import com.google.gerrit.server.config.ConfirmEmail.Input;
+import com.google.gerrit.server.mail.EmailTokenVerifier;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+@Singleton
+public class ConfirmEmail implements RestModifyView<ConfigResource, Input> {
+ public static class Input {
+ @DefaultInput
+ public String token;
+ }
+
+ private final Provider<CurrentUser> self;
+ private final EmailTokenVerifier emailTokenVerifier;
+ private final AccountManager accountManager;
+
+ @Inject
+ public ConfirmEmail(Provider<CurrentUser> self,
+ EmailTokenVerifier emailTokenVerifier,
+ AccountManager accountManager) {
+ this.self = self;
+ this.emailTokenVerifier = emailTokenVerifier;
+ this.accountManager = accountManager;
+ }
+
+ @Override
+ public Response<?> apply(ConfigResource rsrc, Input input)
+ throws AuthException, UnprocessableEntityException, AccountException,
+ OrmException {
+ CurrentUser user = self.get();
+ if (!user.isIdentifiedUser()) {
+ throw new AuthException("Authentication required");
+ }
+
+ if (input == null) {
+ input = new Input();
+ }
+
+ try {
+ EmailTokenVerifier.ParsedToken token = emailTokenVerifier.decode(input.token);
+ Account.Id accId = ((IdentifiedUser)user).getAccountId();
+ if (accId.equals(token.getAccountId())) {
+ accountManager.link(accId, token.toAuthRequest());
+ return Response.none();
+ } else {
+ throw new UnprocessableEntityException("invalid token");
+ }
+ } catch (EmailTokenVerifier.InvalidTokenException e) {
+ throw new UnprocessableEntityException("invalid token");
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/Module.java
index 31fdc1e..e909f17 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/Module.java
@@ -38,5 +38,6 @@
get(CONFIG_KIND, "info").to(GetServerInfo.class);
get(CONFIG_KIND, "preferences").to(GetPreferences.class);
put(CONFIG_KIND, "preferences").to(SetPreferences.class);
+ put(CONFIG_KIND, "email.confirm").to(ConfirmEmail.class);
}
}