Merge branch 'stable-3.1' into stable-3.2
* stable-3.1:
Restore existing Base64 transcoder for private key
Remove unused annotation @ChangeUpdateExecutor
Change-Id: I55f0668b1131b945ae3c7325a9b6b27b3ad9b51c
diff --git a/java/com/google/gerrit/server/BUILD b/java/com/google/gerrit/server/BUILD
index ef3f415..1c46ed6 100644
--- a/java/com/google/gerrit/server/BUILD
+++ b/java/com/google/gerrit/server/BUILD
@@ -107,6 +107,7 @@
"//lib/auto:auto-value-annotations",
"//lib/bouncycastle:bcpkix-neverlink",
"//lib/bouncycastle:bcprov-neverlink",
+ "//lib/commons:codec",
"//lib/commons:compress",
"//lib/commons:dbcp",
"//lib/commons:lang",
diff --git a/java/com/google/gerrit/server/config/ChangeUpdateExecutor.java b/java/com/google/gerrit/server/config/ChangeUpdateExecutor.java
deleted file mode 100644
index 4c9e5f0..0000000
--- a/java/com/google/gerrit/server/config/ChangeUpdateExecutor.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (C) 2012 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 static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.gerrit.server.update.BatchUpdate;
-import com.google.inject.BindingAnnotation;
-import java.lang.annotation.Retention;
-
-/**
- * Marker on the global {@link ListeningExecutorService} used by asynchronous {@link BatchUpdate}s.
- */
-@Retention(RUNTIME)
-@BindingAnnotation
-public @interface ChangeUpdateExecutor {}
diff --git a/java/com/google/gerrit/server/config/SysExecutorModule.java b/java/com/google/gerrit/server/config/SysExecutorModule.java
index 6c729d9..e7f4540 100644
--- a/java/com/google/gerrit/server/config/SysExecutorModule.java
+++ b/java/com/google/gerrit/server/config/SysExecutorModule.java
@@ -14,19 +14,13 @@
package com.google.gerrit.server.config;
-import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.gerrit.server.FanOutExecutor;
import com.google.gerrit.server.git.WorkQueue;
-import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;
-import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.lib.Config;
/**
@@ -73,28 +67,4 @@
}
return queues.createQueue(poolSize, "FanOut");
}
-
- @Provides
- @Singleton
- @ChangeUpdateExecutor
- public ListeningExecutorService createChangeUpdateExecutor(@GerritServerConfig Config config) {
- int poolSize = config.getInt("receive", null, "changeUpdateThreads", 1);
- if (poolSize <= 1) {
- return MoreExecutors.newDirectExecutorService();
- }
- return MoreExecutors.listeningDecorator(
- new LoggingContextAwareExecutorService(
- MoreExecutors.getExitingExecutorService(
- new ThreadPoolExecutor(
- 1,
- poolSize,
- 10,
- TimeUnit.MINUTES,
- new ArrayBlockingQueue<>(poolSize),
- new ThreadFactoryBuilder()
- .setNameFormat("ChangeUpdate-%d")
- .setDaemon(true)
- .build(),
- new ThreadPoolExecutor.CallerRunsPolicy()))));
- }
}
diff --git a/java/com/google/gerrit/server/mail/SignedToken.java b/java/com/google/gerrit/server/mail/SignedToken.java
index a010e30..7dcac1a 100644
--- a/java/com/google/gerrit/server/mail/SignedToken.java
+++ b/java/com/google/gerrit/server/mail/SignedToken.java
@@ -22,6 +22,7 @@
import javax.crypto.Mac;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.SecretKeySpec;
+import org.apache.commons.codec.binary.Base64;
/**
* Utility function to compute and verify XSRF tokens.
@@ -46,7 +47,7 @@
public static String generateRandomKey() {
final byte[] r = new byte[26];
new SecureRandom().nextBytes(r);
- return encodeBase64(r);
+ return encodeBase64PrivateKey(r);
}
private final int maxAge;
@@ -63,7 +64,7 @@
*/
public SignedToken(final int age, final String keyBase64) throws XsrfException {
maxAge = age > 5 ? age / 5 : age;
- key = new SecretKeySpec(decodeBase64(keyBase64), MAC_ALG);
+ key = new SecretKeySpec(decodeBase64PrivateKey(keyBase64), MAC_ALG);
rng = new SecureRandom();
tokenLength = 2 * INT_SZ + newMac().getMacLength();
}
@@ -169,6 +170,14 @@
return (int) (System.currentTimeMillis() / 5000L);
}
+ private static byte[] decodeBase64PrivateKey(final String privateKeyBase64String) {
+ return Base64.decodeBase64(toBytes(privateKeyBase64String));
+ }
+
+ private static String encodeBase64PrivateKey(final byte[] buf) {
+ return toString(Base64.encodeBase64(buf));
+ }
+
private static byte[] decodeBase64(final String s) {
return BaseEncoding.base64Url().decode(s);
}
@@ -208,4 +217,12 @@
}
return r;
}
+
+ private static String toString(final byte[] b) {
+ final StringBuilder r = new StringBuilder(b.length);
+ for (int i = 0; i < b.length; i++) {
+ r.append((char) b[i]);
+ }
+ return r.toString();
+ }
}
diff --git a/java/com/google/gerrit/testing/InMemoryModule.java b/java/com/google/gerrit/testing/InMemoryModule.java
index af9d8c3..baccbf9 100644
--- a/java/com/google/gerrit/testing/InMemoryModule.java
+++ b/java/com/google/gerrit/testing/InMemoryModule.java
@@ -18,7 +18,6 @@
import static com.google.inject.Scopes.SINGLETON;
import com.google.common.base.Strings;
-import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperationsImpl;
@@ -48,7 +47,6 @@
import com.google.gerrit.server.config.AnonymousCowardNameProvider;
import com.google.gerrit.server.config.CanonicalWebUrlModule;
import com.google.gerrit.server.config.CanonicalWebUrlProvider;
-import com.google.gerrit.server.config.ChangeUpdateExecutor;
import com.google.gerrit.server.config.DefaultUrlFormatter;
import com.google.gerrit.server.config.GerritGlobalModule;
import com.google.gerrit.server.config.GerritInstanceNameModule;
@@ -186,9 +184,6 @@
bind(GitRepositoryManager.class).to(InMemoryRepositoryManager.class);
bind(InMemoryRepositoryManager.class).in(SINGLETON);
bind(TrackingFooters.class).toProvider(TrackingFootersProvider.class).in(SINGLETON);
- bind(ListeningExecutorService.class)
- .annotatedWith(ChangeUpdateExecutor.class)
- .toInstance(MoreExecutors.newDirectExecutorService());
bind(SecureStore.class).to(DefaultSecureStore.class);
install(new InMemorySchemaModule());
diff --git a/javatests/com/google/gerrit/server/mail/SignedTokenTest.java b/javatests/com/google/gerrit/server/mail/SignedTokenTest.java
index cfb551f..41d8d69 100644
--- a/javatests/com/google/gerrit/server/mail/SignedTokenTest.java
+++ b/javatests/com/google/gerrit/server/mail/SignedTokenTest.java
@@ -18,20 +18,14 @@
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import java.util.Random;
+import java.util.regex.Pattern;
import org.junit.Before;
import org.junit.Test;
public class SignedTokenTest {
- private static final String REGISTER_EMAIL_PRIVATE_KEY =
- "R2Vycml0JTIwcmVnaXN0ZXJFbWFpbFByaXZhdGVLZXk=";
- private static final String URL_SAFE_REGISTER_EMAIL_PRIVATE_KEY =
- REGISTER_EMAIL_PRIVATE_KEY.replaceFirst("R2", "_-");
- private static final String URL_UNSAFE_REGISTER_EMAIL_PRIVATE_KEY_WITH_PLUS =
- REGISTER_EMAIL_PRIVATE_KEY.replaceFirst("R", "+");
- private static final String URL_UNSAFE_REGISTER_EMAIL_PRIVATE_KEY_WITH_SLASH =
- REGISTER_EMAIL_PRIVATE_KEY.replaceFirst("R", "/");
-
+ private static final Pattern URL_UNSAFE_CHARS = Pattern.compile("(\\+|/)");
+ private static final String REGISTER_EMAIL_PRIVATE_KEY = "TGMv3/bTC42jUKQndTQrXyHhHYMP0t69i/4=";
private static final int maxAge = 5;
private static final String TEXT = "This is a text";
private static final String FORGED_TEXT = "This is a forged text";
@@ -44,29 +38,23 @@
signedToken = new SignedToken(maxAge, REGISTER_EMAIL_PRIVATE_KEY);
}
- /**
- * Test new token: the key is a normal BASE64 string without index of '62'(+ or _) or '63'(/ or -)
- */
+ /** Test new token: the key is a normal BASE64 string that can be used for URL safely */
@Test
public void newTokenKeyDoesNotContainUnsafeChar() throws Exception {
- new SignedToken(maxAge, REGISTER_EMAIL_PRIVATE_KEY);
- }
-
- /** Test new token: the key is an URL safe BASE64 string with indexes of '62'(_) and '63'(-) */
- @Test
- public void newTokenWithUrlSafeBase64() throws Exception {
- new SignedToken(maxAge, URL_SAFE_REGISTER_EMAIL_PRIVATE_KEY);
+ assertThat(signedToken.newToken(TEXT)).doesNotContainMatch(URL_UNSAFE_CHARS);
}
/** Test new token: the key is an URL unsafe BASE64 string with index of '62'(+) */
@Test
public void newTokenWithUrlUnsafeBase64Plus() throws Exception {
- IllegalArgumentException thrown =
- assertThrows(
- IllegalArgumentException.class,
- () -> new SignedToken(maxAge, URL_UNSAFE_REGISTER_EMAIL_PRIVATE_KEY_WITH_PLUS));
+ String token = "+" + signedToken.newToken(TEXT);
+ CheckTokenException thrown =
+ assertThrows(CheckTokenException.class, () -> signedToken.checkToken(token, TEXT));
+
+ assertThat(thrown).hasMessageThat().contains("decoding failed");
assertThat(thrown)
+ .hasCauseThat()
.hasMessageThat()
.isEqualTo(
"com.google.common.io.BaseEncoding$DecodingException: Unrecognized character: +");
@@ -75,12 +63,14 @@
/** Test new token: the key is an URL unsafe BASE64 string with '63'(/) */
@Test
public void newTokenWithUrlUnsafeBase64Slash() throws Exception {
- IllegalArgumentException thrown =
- assertThrows(
- IllegalArgumentException.class,
- () -> new SignedToken(maxAge, URL_UNSAFE_REGISTER_EMAIL_PRIVATE_KEY_WITH_SLASH));
+ String token = "/" + signedToken.newToken(TEXT);
+ CheckTokenException thrown =
+ assertThrows(CheckTokenException.class, () -> signedToken.checkToken(token, TEXT));
+
+ assertThat(thrown).hasMessageThat().contains("decoding failed");
assertThat(thrown)
+ .hasCauseThat()
.hasMessageThat()
.isEqualTo(
"com.google.common.io.BaseEncoding$DecodingException: Unrecognized character: /");