Merge branch 'stable-3.1' into stable-3.2

* stable-3.1:
  Bump Bazel version to 3.1.0
  SignedToken: Use URL-safe encoding
  Align duct-tape and jna versions with testcontainers version
  Upgrade testcontainers to 1.14.1
  Bump MySQL Connector/J version to 5.1.48
  GerritSimulation: Support change creation/deletion

Change-Id: Ib3e8b17acd1df1774896da6795a7790a52653102
This commit is contained in:
David Pursehouse
2020-04-25 21:10:43 +09:00
19 changed files with 499 additions and 34 deletions

View File

@@ -1 +1 @@
3.0.0
3.1.0

View File

@@ -0,0 +1,5 @@
{
"project": "${project}",
"branch": "master",
"subject": "Change"
}

View File

@@ -0,0 +1,6 @@
[
{
"url": "http://HOSTNAME:HTTP_PORT/a/changes/",
"project": "_PROJECT"
}
]

View File

@@ -0,0 +1,3 @@
{
"create_empty_commit": "true"
}

View File

@@ -0,0 +1,6 @@
[
{
"url": "http://HOSTNAME:HTTP_PORT/a/changes/",
"number": "_NUMBER"
}
]

View File

@@ -21,7 +21,7 @@ import io.gatling.core.structure.ScenarioBuilder
import scala.concurrent.duration._
class CloneUsingBothProtocols extends GitSimulation {
private val data: FileBasedFeederBuilder[Any]#F#F = jsonFile(resource).convert(url).queue
private val data: FileBasedFeederBuilder[Any]#F#F = jsonFile(resource).convert(keys).queue
private val default: String = name
override def replaceOverride(in: String): String = {

View File

@@ -0,0 +1,60 @@
// Copyright (C) 2020 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.scenarios
import io.gatling.core.Predef.{atOnceUsers, _}
import io.gatling.core.feeder.FileBasedFeederBuilder
import io.gatling.core.structure.ScenarioBuilder
import io.gatling.http.Predef._
import scala.concurrent.duration._
class CreateChange extends GerritSimulation {
private val data: FileBasedFeederBuilder[Any]#F#F = jsonFile(resource).convert(keys).queue
private val default: String = name
private val numberKey = "_number"
val test: ScenarioBuilder = scenario(unique)
.feed(data)
.exec(httpRequest
.body(ElFileBody(body)).asJson
.check(regex("\"" + numberKey + "\":(\\d+),").saveAs(numberKey)))
.exec(session => {
deleteChange.number = Some(session(numberKey).as[Int])
session
})
private val createProject = new CreateProject(default)
private val deleteProject = new DeleteProject(default)
private val deleteChange = new DeleteChange
setUp(
createProject.test.inject(
atOnceUsers(1)
),
test.inject(
nothingFor(2 seconds),
atOnceUsers(1)
),
deleteChange.test.inject(
nothingFor(6 seconds),
atOnceUsers(1)
),
deleteProject.test.inject(
nothingFor(8 seconds),
atOnceUsers(1)
),
).protocols(httpProtocol)
}

View File

@@ -19,7 +19,7 @@ import io.gatling.core.feeder.FileBasedFeederBuilder
import io.gatling.core.structure.ScenarioBuilder
class CreateProject extends ProjectSimulation {
private val data: FileBasedFeederBuilder[Any]#F#F = jsonFile(resource).convert(url).queue
private val data: FileBasedFeederBuilder[Any]#F#F = jsonFile(resource).convert(keys).queue
def this(default: String) {
this()
@@ -28,7 +28,7 @@ class CreateProject extends ProjectSimulation {
val test: ScenarioBuilder = scenario(unique)
.feed(data)
.exec(httpRequest)
.exec(httpRequest.body(RawFileBody(body)).asJson)
setUp(
test.inject(

View File

@@ -0,0 +1,42 @@
// Copyright (C) 2020 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.scenarios
import io.gatling.core.Predef.{atOnceUsers, _}
import io.gatling.core.feeder.FileBasedFeederBuilder
import io.gatling.core.structure.ScenarioBuilder
import io.gatling.http.Predef.http
class DeleteChange extends GerritSimulation {
private val data: FileBasedFeederBuilder[Any]#F#F = jsonFile(resource).convert(keys).queue
var number: Option[Int] = None
val test: ScenarioBuilder = scenario(unique)
.feed(data)
.exec(session => {
if (number.nonEmpty) {
session.set("number", number.get)
} else {
session
}
})
.exec(http(unique).delete("${url}${number}"))
setUp(
test.inject(
atOnceUsers(1)
),
).protocols(httpProtocol)
}

View File

@@ -19,7 +19,7 @@ import io.gatling.core.feeder.FileBasedFeederBuilder
import io.gatling.core.structure.ScenarioBuilder
class DeleteProject extends ProjectSimulation {
private val data: FileBasedFeederBuilder[Any]#F#F = jsonFile(resource).convert(url).queue
private val data: FileBasedFeederBuilder[Any]#F#F = jsonFile(resource).convert(keys).queue
def this(default: String) {
this()

View File

@@ -26,7 +26,9 @@ class GerritSimulation extends Simulation {
private val pack: String = this.getClass.getPackage.getName
private val path: String = pack.replaceAllLiterally(".", "/")
protected val name: String = this.getClass.getSimpleName
protected val resource: String = s"data/$path/$name.json"
private val pathName: String = s"data/$path/$name"
protected val resource: String = s"$pathName.json"
protected val body: String = s"$pathName-body.json"
protected val unique: String = name + "-" + this.hashCode()
protected val httpRequest: HttpRequestBuilder = http(unique).post("${url}")
@@ -34,12 +36,22 @@ class GerritSimulation extends Simulation {
conf.httpConfiguration.userName,
conf.httpConfiguration.password)
protected val url: PartialFunction[(String, Any), Any] = {
protected val keys: PartialFunction[(String, Any), Any] = {
case ("url", url) =>
var in = replaceOverride(url.toString)
in = replaceProperty("hostname", "localhost", in)
in = replaceProperty("http_port", 8080, in)
replaceProperty("ssh_port", 29418, in)
case ("number", number) =>
val precedes = replaceKeyWith("_number", 0, number.toString)
replaceProperty("number", 1, precedes)
case ("project", project) =>
val precedes = replaceKeyWith("_project", name, project.toString)
replaceProperty("project", precedes)
}
private def replaceProperty(term: String, in: String): String = {
replaceProperty(term, term, in)
}
protected def replaceProperty(term: String, default: Any, in: String): String = {
@@ -66,7 +78,7 @@ class GerritSimulation extends Simulation {
* Meant to be optionally overridden by plugins or other extensions.
* Such potential overriding methods, such as the example below,
* typically return resulting call(s) to [[replaceProperty()]].
* This is usually similar to how [[url]] is implemented above.
* This is usually similar to how [[keys]] is implemented above.
*
* <pre>
* override def replaceOverride(in: String): String = {

View File

@@ -21,7 +21,7 @@ import io.gatling.core.structure.ScenarioBuilder
import scala.concurrent.duration._
class ReplayRecordsFromFeeder extends GitSimulation {
private val data: FileBasedFeederBuilder[Any]#F#F = jsonFile(resource).convert(url).circular
private val data: FileBasedFeederBuilder[Any]#F#F = jsonFile(resource).convert(keys).circular
private val default: String = name
override def replaceOverride(in: String): String = {

View File

@@ -0,0 +1,28 @@
// Copyright 2020 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.mail;
/** Indicates the SignedToken is invalid */
public class CheckTokenException extends Exception {
private static final long serialVersionUID = 1L;
CheckTokenException(final String message) {
super(message);
}
CheckTokenException(final String message, final Throwable why) {
super(message, why);
}
}

View File

@@ -88,24 +88,29 @@ public class SignedToken {
}
/**
* Validate a returned token.
* Validate a returned token. If the token is valid then return a {@link ValidToken}, else will
* throw {@link XsrfException} when it's an unexpected token overflow or {@link
* CheckTokenException} when it's an illegal token string format.
*
* @param tokenString a token string previously created by this class.
* @param text text that must have been used during {@link #newToken(String)} in order for the
* token to be valid. If null the text will be taken from the token string itself.
* @return true if the token is valid; false if the token is null, the empty string, has expired,
* does not match the text supplied, or is a forged token.
* @return the token which is valid.
* @throws XsrfException the JVM doesn't support the necessary algorithms to generate a token.
* XSRF services are simply not available.
* @throws CheckTokenException throws when token is null, the empty string, has expired, does not
* match the text supplied, or is a forged token.
*/
ValidToken checkToken(final String tokenString, final String text) throws XsrfException {
public ValidToken checkToken(final String tokenString, final String text)
throws XsrfException, CheckTokenException {
if (tokenString == null || tokenString.length() == 0) {
return null;
throw new CheckTokenException("Empty token");
}
final int s = tokenString.indexOf('$');
if (s <= 0) {
return null;
throw new CheckTokenException("Token does not contain character '$'");
}
final String recvText = tokenString.substring(s + 1);
@@ -113,24 +118,25 @@ public class SignedToken {
try {
in = decodeBase64(tokenString.substring(0, s));
} catch (RuntimeException e) {
return null;
throw new CheckTokenException("Base64 decoding failed", e);
}
if (in.length != tokenLength) {
return null;
throw new CheckTokenException("Token length mismatch");
}
final int q = decodeInt(in, 0);
final int c = decodeInt(in, INT_SZ) ^ q;
final int n = now();
if (maxAge > 0 && Math.abs(c - n) > maxAge) {
return null;
throw new CheckTokenException("Token is expired");
}
final byte[] gen = new byte[tokenLength];
System.arraycopy(in, 0, gen, 0, 2 * INT_SZ);
computeToken(gen, text != null ? text : recvText);
if (!Arrays.equals(gen, in)) {
return null;
throw new CheckTokenException("Token text mismatch");
}
return new ValidToken(maxAge > 0 && c + (maxAge >> 1) <= n, recvText);
@@ -164,11 +170,11 @@ public class SignedToken {
}
private static byte[] decodeBase64(final String s) {
return BaseEncoding.base64().decode(s);
return BaseEncoding.base64Url().decode(s);
}
private static String encodeBase64(final byte[] buf) {
return BaseEncoding.base64().encode(buf);
return BaseEncoding.base64Url().encode(buf);
}
private static void encodeInt(final byte[] buf, final int o, final int v) {

View File

@@ -50,7 +50,7 @@ public class SignedTokenEmailTokenVerifier implements EmailTokenVerifier {
try {
String payload = String.format("%s:%s", accountId, emailAddress);
byte[] utf8 = payload.getBytes(UTF_8);
String base64 = BaseEncoding.base64().encode(utf8);
String base64 = BaseEncoding.base64Url().encode(utf8);
return emailRegistrationToken.newToken(base64);
} catch (XsrfException e) {
throw new IllegalArgumentException(e);
@@ -63,14 +63,14 @@ public class SignedTokenEmailTokenVerifier implements EmailTokenVerifier {
ValidToken token;
try {
token = emailRegistrationToken.checkToken(tokenString, null);
} catch (XsrfException err) {
} catch (XsrfException | CheckTokenException err) {
throw new InvalidTokenException(err);
}
if (token == null || token.getData() == null || token.getData().isEmpty()) {
throw new InvalidTokenException();
}
String payload = new String(BaseEncoding.base64().decode(token.getData()), UTF_8);
String payload = new String(BaseEncoding.base64Url().decode(token.getData()), UTF_8);
Matcher matcher = Pattern.compile("^([0-9]+):(.+@.+)$").matcher(payload);
if (!matcher.matches()) {
throw new InvalidTokenException();

View File

@@ -1163,7 +1163,7 @@ public class AccountIT extends AbstractDaemonTest {
@Test
@GerritConfig(
name = "auth.registerEmailPrivateKey",
value = "HsOc6l+2lhS9G7sE/RsnS7Z6GJjdRDX14co=")
value = "HsOc6l_2lhS9G7sE_RsnS7Z6GJjdRDX14co=")
public void addEmailSendsConfirmationEmail() throws Exception {
String email = "new.email@example.com";
EmailInput input = newEmailInput(email, false);
@@ -1177,7 +1177,7 @@ public class AccountIT extends AbstractDaemonTest {
@Test
@GerritConfig(
name = "auth.registerEmailPrivateKey",
value = "HsOc6l+2lhS9G7sE/RsnS7Z6GJjdRDX14co=")
value = "HsOc6l_2lhS9G7sE-RsnS7Z6GJjdRDX14co=")
public void addEmailToBeConfirmedToOwnAccount() throws Exception {
TestAccount user = accountCreator.create();
requestScopeOperations.setApiUser(user.id());
@@ -1206,7 +1206,7 @@ public class AccountIT extends AbstractDaemonTest {
@Test
@GerritConfig(
name = "auth.registerEmailPrivateKey",
value = "HsOc6l+2lhS9G7sE/RsnS7Z6GJjdRDX14co=")
value = "HsOc6l_2lhS9G7sE-RsnS7Z6GJjdRDX14co=")
public void addEmailToBeConfirmedToOtherAccount() throws Exception {
TestAccount user = accountCreator.create();
String email = "me@example.com";

View File

@@ -0,0 +1,125 @@
// Copyright (C) 2020 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.server.mail;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.io.BaseEncoding;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.server.mail.EmailTokenVerifier;
import com.google.gerrit.server.mail.EmailTokenVerifier.InvalidTokenException;
import com.google.gerrit.server.mail.SignedToken;
import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier;
import com.google.gerrit.testing.ConfigSuite;
import java.nio.charset.StandardCharsets;
import org.eclipse.jgit.lib.Config;
import org.junit.Before;
import org.junit.Test;
public class SignedTokenEmailTokenVerifierIT extends AbstractDaemonTest {
@ConfigSuite.Default
public static Config defaultConfig() {
Config cfg = new Config();
cfg.setString("auth", null, "registerEmailPrivateKey", SignedToken.generateRandomKey());
return cfg;
}
private SignedTokenEmailTokenVerifier signedTokenEmailTokenVerifier;
@Before
public void setUp() throws Exception {
signedTokenEmailTokenVerifier =
server
.getTestInjector()
.getBinding(SignedTokenEmailTokenVerifier.class)
.getProvider()
.get();
}
/** Test encode */
@Test
public void encode() throws Exception {
String tokenString = signedTokenEmailTokenVerifier.encode(user.id(), user.email());
int index = tokenString.indexOf("$");
String text = tokenString.substring(index + 1);
String textDecoded = new String(BaseEncoding.base64Url().decode(text), StandardCharsets.UTF_8);
int pos = textDecoded.indexOf(":");
assertThat(textDecoded.substring(0, pos)).isEqualTo(user.id().toString());
assertThat(textDecoded.substring(pos + 1)).isEqualTo(user.email());
}
/** Test decode */
@Test
public void decode() throws Exception {
String tokenString = signedTokenEmailTokenVerifier.encode(user.id(), user.email());
String tokenKey = tokenString.substring(0, tokenString.indexOf("$"));
String text = user.id() + ":" + user.email();
String invalidTokenString =
tokenKey + "$" + BaseEncoding.base64Url().encode(text.getBytes(StandardCharsets.UTF_8));
EmailTokenVerifier.ParsedToken parsedToken =
signedTokenEmailTokenVerifier.decode(invalidTokenString);
assertThat(parsedToken.getAccountId()).isEqualTo(user.id());
assertThat(parsedToken.getEmailAddress()).isEqualTo(user.email());
}
/** Test token format is wrong(without '$' to split key and text) */
@Test
public void invalidFormat() throws Exception {
InvalidTokenException thrown =
assertThrows(
InvalidTokenException.class,
() -> signedTokenEmailTokenVerifier.decode("Invalid token"));
assertThat(thrown)
.hasCauseThat()
.hasMessageThat()
.isEqualTo("Token does not contain character '$'");
}
/** Test input token string is empty or null */
@Test
public void emptyInput() throws Exception {
InvalidTokenException thrownWithNull =
assertThrows(InvalidTokenException.class, () -> signedTokenEmailTokenVerifier.decode(null));
InvalidTokenException thrownWithEmpty =
assertThrows(InvalidTokenException.class, () -> signedTokenEmailTokenVerifier.decode(""));
assertThat(thrownWithNull).hasCauseThat().hasMessageThat().isEqualTo("Empty token");
assertThat(thrownWithEmpty).hasCauseThat().hasMessageThat().isEqualTo("Empty token");
}
/** Test token format is right but key is an illegal BASE64 string */
@Test
public void illegalTokenKey() throws Exception {
InvalidTokenException thrown =
assertThrows(
InvalidTokenException.class,
() -> signedTokenEmailTokenVerifier.decode("Illegal token key$...."));
assertThat(thrown).hasCauseThat().hasMessageThat().isEqualTo("Base64 decoding failed");
}
/** Test token text not match the required pattern */
@Test
public void tokenTextPatternMismatch() throws Exception {
String tokenString = signedTokenEmailTokenVerifier.encode(user.id(), user.email());
String tokenKey = tokenString.substring(0, tokenString.indexOf("$"));
String pattern = user.id() + ":" + user.email();
String invalidTokenTextPattern = tokenKey + "$" + pattern.replace(":", "");
InvalidTokenException thrown =
assertThrows(
InvalidTokenException.class,
() -> signedTokenEmailTokenVerifier.decode(invalidTokenTextPattern));
assertThat(thrown).hasCauseThat().hasMessageThat().isEqualTo("Token text mismatch");
}
}

View File

@@ -0,0 +1,172 @@
// Copyright (C) 2020 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.mail;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import java.util.Random;
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 int maxAge = 5;
private static final String TEXT = "This is a text";
private static final String FORGED_TEXT = "This is a forged text";
private static final String FORGED_TOKEN = String.format("Zm9yZ2VkJTIwa2V5$%s", TEXT);
private SignedToken signedToken;
@Before
public void setUp() throws Exception {
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
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);
}
/** 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));
assertThat(thrown)
.hasMessageThat()
.isEqualTo(
"com.google.common.io.BaseEncoding$DecodingException: Unrecognized character: +");
}
/** 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));
assertThat(thrown)
.hasMessageThat()
.isEqualTo(
"com.google.common.io.BaseEncoding$DecodingException: Unrecognized character: /");
}
/** Test check token: BASE64 encoding and decoding in a safe URL way */
@Test
public void checkToken() throws Exception {
String token = signedToken.newToken(TEXT);
ValidToken validToken = signedToken.checkToken(token, TEXT);
assertThat(validToken).isNotNull();
assertThat(validToken.getData()).isEqualTo(TEXT);
}
/** Test check token: input token string is null */
@Test
public void checkTokenInputTokenNull() throws Exception {
CheckTokenException thrown =
assertThrows(CheckTokenException.class, () -> signedToken.checkToken(null, TEXT));
assertThat(thrown).hasMessageThat().isEqualTo("Empty token");
}
/** Test check token: input token string is empty */
@Test
public void checkTokenInputTokenEmpty() throws Exception {
CheckTokenException thrown =
assertThrows(CheckTokenException.class, () -> signedToken.checkToken("", TEXT));
assertThat(thrown).hasMessageThat().isEqualTo("Empty token");
}
/** Test check token: token string is not illegal with no '$' character */
@Test
public void checkTokenInputTokenNoDollarSplitChar() throws Exception {
String token = signedToken.newToken(TEXT).replace("$", "¥");
CheckTokenException thrown =
assertThrows(CheckTokenException.class, () -> signedToken.checkToken(token, TEXT));
assertThat(thrown).hasMessageThat().isEqualTo("Token does not contain character '$'");
}
/** Test check token: token string length is match but is not a legal BASE64 string */
@Test
public void checkTokenInputTokenKeyBase64DecodeFail() throws Exception {
String token = signedToken.newToken(TEXT);
String key = randomString(token.indexOf("$") + 1);
String illegalBase64Token = key + "$" + TEXT;
CheckTokenException thrown =
assertThrows(
CheckTokenException.class, () -> signedToken.checkToken(illegalBase64Token, TEXT));
assertThat(thrown).hasMessageThat().isEqualTo("Base64 decoding failed");
}
/** Test check token: token is illegal with a forged key */
@Test
public void checkTokenForgedKey() throws Exception {
CheckTokenException thrown =
assertThrows(CheckTokenException.class, () -> signedToken.checkToken(FORGED_TOKEN, TEXT));
assertThat(thrown).hasMessageThat().isEqualTo("Token length mismatch");
}
/** Test check token: token is illegal with a forged text */
@Test
public void checkTokenForgedText() throws Exception {
CheckTokenException thrown =
assertThrows(
CheckTokenException.class,
() -> {
String token = signedToken.newToken(TEXT);
signedToken.checkToken(token, FORGED_TEXT);
});
assertThat(thrown).hasMessageThat().isEqualTo("Token text mismatch");
}
private static String randomString(int length) {
String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(62);
sb.append(str.charAt(number));
}
return sb.toString();
}
}

View File

@@ -120,24 +120,24 @@ def declare_nongoogle_deps():
sha1 = "dc13ae4faca6df981fc7aeb5a522d9db446d5d50",
)
TESTCONTAINERS_VERSION = "1.14.0"
TESTCONTAINERS_VERSION = "1.14.1"
maven_jar(
name = "testcontainers",
artifact = "org.testcontainers:testcontainers:" + TESTCONTAINERS_VERSION,
sha1 = "c0d6aea93f4f7ff4b0d559e31308340eaa398798",
sha1 = "defd04ff6ffc93e1ff988024048e8ba5bd298df3",
)
maven_jar(
name = "testcontainers-elasticsearch",
artifact = "org.testcontainers:elasticsearch:" + TESTCONTAINERS_VERSION,
sha1 = "6df7bc7cb5e99c6d9528ea28dd16dbb042b1beec",
sha1 = "d682965bbf1334ef40720b4ad2eda2c12bf0b440",
)
maven_jar(
name = "duct-tape",
artifact = "org.rnorth.duct-tape:duct-tape:1.0.7",
sha1 = "a26b5d90d88c91321dc7a3734ea72d2fc019ebb6",
artifact = "org.rnorth.duct-tape:duct-tape:1.0.8",
sha1 = "92edc22a9ab2f3e17c9bf700aaee377d50e8b530",
)
maven_jar(
@@ -148,6 +148,6 @@ def declare_nongoogle_deps():
maven_jar(
name = "jna",
artifact = "net.java.dev.jna:jna:5.2.0",
sha1 = "ed8b772eb077a9cb50e44e90899c66a9a6c00e67",
artifact = "net.java.dev.jna:jna:5.5.0",
sha1 = "0e0845217c4907822403912ad6828d8e0b256208",
)