Skip to content

Commit

Permalink
Release 1.12.3
Browse files Browse the repository at this point in the history
Fixes:

- Fixed `PublicKeyCredential` failing to parse from JSON if an
  `"authenticatorAttachment"` attribute was present.
- Bumped Jackson dependency to version [2.13.2.1,3) in response to
  CVE-2020-36518
- Fixed bug in `RelyingParty.finishAssertion` that would throw a nondescript
  `NoSuchElementException` if username and user handle are both absent, instead
  of an `IllegalArgumentException` with a better error message.
  • Loading branch information
emlun committed Apr 15, 2022
2 parents 5f14dc4 + c81c9a8 commit 8eb6278
Show file tree
Hide file tree
Showing 20 changed files with 97 additions and 542 deletions.
13 changes: 13 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
== Version 1.12.3 ==

Fixes:

* Fixed `PublicKeyCredential` failing to parse from JSON if an
`"authenticatorAttachment"` attribute was present.
* Bumped Jackson dependency to version [2.13.2.1,3) in response to
CVE-2020-36518
* Fixed bug in `RelyingParty.finishAssertion` that would throw a nondescript
`NoSuchElementException` if username and user handle are both absent, instead
of an `IllegalArgumentException` with a better error message.


== Version 1.12.2 ==

Fixes:
Expand Down
4 changes: 2 additions & 2 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ Maven:
<dependency>
<groupId>com.yubico</groupId>
<artifactId>webauthn-server-core</artifactId>
<version>1.12.2</version>
<version>1.12.3</version>
<scope>compile</scope>
</dependency>
----------

Gradle:

----------
compile 'com.yubico:webauthn-server-core:1.12.2'
compile 'com.yubico:webauthn-server-core:1.12.3'
----------

=== Semantic versioning
Expand Down
16 changes: 11 additions & 5 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ buildscript {
}
dependencies {
classpath 'com.cinnober.gradle:semver-git:2.5.0'
classpath 'com.diffplug.spotless:spotless-plugin-gradle:6.2.0'
classpath 'com.diffplug.spotless:spotless-plugin-gradle:6.3.0'
classpath 'io.github.cosmicsilence:gradle-scalafix:0.1.8'
}
}
Expand Down Expand Up @@ -45,11 +45,17 @@ wrapper {

dependencies {
constraints {
api('ch.qos.logback:logback-classic:[1.2.3,2)')
api('com.augustcellars.cose:cose-java:[1.0.0,2)')
api('com.fasterxml.jackson.core:jackson-databind:[2.11.0,3)')
api('com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:[2.11.0,3)')
api('com.fasterxml.jackson.datatype:jackson-datatype-jdk8:[2.11.0,3)')
api('com.fasterxml.jackson.core:jackson-databind:[2.13.2.1,3)')
api('com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:[2.13.2,3)')
api('com.fasterxml.jackson.datatype:jackson-datatype-jdk8:[2.13.2,3)')
api('com.fasterxml.jackson:jackson-bom') {
version {
strictly '[2.13.2.1,3)'
reject '2.13.2.1'
}
because 'jackson-databind 2.13.2.1 references nonexistent BOM'
}
api('com.google.guava:guava:[24.1.1,31)')
api('com.upokecenter:cbor:[4.5.1,5)')
api('javax.ws.rs:javax.ws.rs-api:[2.1,3)')
Expand Down
7 changes: 0 additions & 7 deletions webauthn-server-attestation/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,10 @@ dependencies {
'org.scalatest:scalatest_2.13',
)

testRuntimeOnly(
'ch.qos.logback:logback-classic',
)
testRuntimeOnly(
// Transitive dependency from :webauthn-server-core:test
'org.bouncycastle:bcpkix-jdk15on',
)

testRuntimeOnly(
'ch.qos.logback:logback-classic',
)
}


Expand Down
15 changes: 0 additions & 15 deletions webauthn-server-attestation/src/test/resources/logback.xml

This file was deleted.

4 changes: 0 additions & 4 deletions webauthn-server-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,6 @@ dependencies {
'org.scalacheck:scalacheck_2.13',
'org.scalatest:scalatest_2.13',
)

testRuntimeOnly(
'ch.qos.logback:logback-classic',
)
}

jar {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,16 +124,19 @@ class Step0 implements Step<Step1> {
.getUserHandle()
.map(Optional::of)
.orElseGet(
() -> credentialRepository.getUserHandleForUsername(request.getUsername().get()));
() ->
request.getUsername().flatMap(credentialRepository::getUserHandleForUsername));

private final Optional<String> username =
request
.getUsername()
.map(Optional::of)
.orElseGet(
() ->
credentialRepository.getUsernameForUserHandle(
response.getResponse().getUserHandle().get()));
response
.getResponse()
.getUserHandle()
.flatMap(credentialRepository::getUsernameForUserHandle));

@Override
public Step1 nextStep() {
Expand All @@ -147,12 +150,12 @@ public void validate() {
"At least one of username and user handle must be given; none was.");
assure(
userHandle.isPresent(),
"No user found for username: %s, userHandle: %s",
"User handle not found for username: %s",
request.getUsername(),
response.getResponse().getUserHandle());
assure(
username.isPresent(),
"No user found for username: %s, userHandle: %s",
"Username not found for userHandle: %s",
request.getUsername(),
response.getResponse().getUserHandle());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
package com.yubico.webauthn.data;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.type.TypeReference;
import com.yubico.internal.util.JacksonCodecs;
Expand All @@ -45,6 +46,7 @@
*/
@Value
@Builder(toBuilder = true)
@JsonIgnoreProperties({"authenticatorAttachment"})
public class PublicKeyCredential<
A extends AuthenticatorResponse, B extends ClientExtensionOutputs> {

Expand Down
15 changes: 0 additions & 15 deletions webauthn-server-core/src/test/resources/logback.xml

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ class OriginMatcherSpec
it("accepts nothing if no allowed origins are given.") {
forAll(urlOrArbitraryString, arbitrary[Boolean], arbitrary[Boolean]) {
(origin, allowPort, allowSubdomain) =>
println(origin)
OriginMatcher.isAllowed(
origin,
Set.empty[String].asJava,
Expand All @@ -114,7 +113,6 @@ class OriginMatcherSpec
it("always accepts string equality even for invalid URLs.") {
forAll(urlOrArbitraryString, arbitrary[Boolean], arbitrary[Boolean]) {
(origin, allowPort, allowSubdomain) =>
println(origin)
OriginMatcher.isAllowed(
origin,
Set(origin).asJava,
Expand All @@ -127,7 +125,6 @@ class OriginMatcherSpec
it("does not accept superdomains.") {
forAll(superAndSubdomain) {
case (origin: URL, allowedOrigin: URL) =>
println(allowedOrigin, origin)
OriginMatcher.isAllowed(
origin.toExternalForm,
Set(allowedOrigin.toExternalForm).asJava,
Expand All @@ -141,7 +138,6 @@ class OriginMatcherSpec
it("by default.") {
forAll(superAndSubdomain, arbitrary[Boolean]) { (origins, allowPort) =>
val (allowedOrigin: URL, origin: URL) = origins
println(allowedOrigin, origin)

OriginMatcher.isAllowed(
origin.toExternalForm,
Expand All @@ -156,8 +152,6 @@ class OriginMatcherSpec
forAll(superAndSubdomain) {
case (allowedOrigin: URL, origin: URL) =>
val invalidAllowedOrigin = invalidize(allowedOrigin)
println(allowedOrigin, origin, invalidAllowedOrigin)

OriginMatcher.isAllowed(
origin.toExternalForm,
Set(invalidAllowedOrigin).asJava,
Expand All @@ -171,8 +165,6 @@ class OriginMatcherSpec
forAll(superAndSubdomain) {
case (allowedOrigin: URL, origin: URL) =>
val invalidOrigin = invalidize(origin)
println(allowedOrigin, origin, invalidOrigin)

OriginMatcher.isAllowed(
invalidOrigin,
Set(allowedOrigin.toExternalForm).asJava,
Expand All @@ -185,8 +177,6 @@ class OriginMatcherSpec
it("unless configured to.") {
forAll(superAndSubdomain, arbitrary[Boolean]) { (origins, allowPort) =>
val (allowedOrigin: URL, origin: URL) = origins
println(allowedOrigin, origin)

OriginMatcher.isAllowed(
origin.toExternalForm,
Set(allowedOrigin.toExternalForm).asJava,
Expand All @@ -203,8 +193,6 @@ class OriginMatcherSpec
(allowedOrigin, port, allowSubdomain) =>
whenever(port > 0) {
val origin = replacePort(allowedOrigin, port)
println(allowedOrigin, origin)

OriginMatcher.isAllowed(
origin.toExternalForm,
Set(allowedOrigin.toExternalForm).asJava,
Expand All @@ -218,8 +206,6 @@ class OriginMatcherSpec
it("unless the same port is specified in an allowed origin.") {
forAll(urlWithPort, arbitrary[Boolean]) {
(origin: URL, allowSubdomain: Boolean) =>
println(origin)

OriginMatcher.isAllowed(
origin.toExternalForm,
Set(origin.toExternalForm).asJava,
Expand All @@ -242,8 +228,6 @@ class OriginMatcherSpec
port,
allowedOrigin.getFile,
)
println(allowedOrigin, origin)

OriginMatcher.isAllowed(
origin.toExternalForm,
Set(allowedOrigin.toExternalForm).asJava,
Expand All @@ -258,8 +242,6 @@ class OriginMatcherSpec
it("accepts subdomains and arbitrary ports when configured to.") {
forAll(superAndSubdomainWithPorts) {
case (allowedOrigin, origin) =>
println(allowedOrigin, origin)

OriginMatcher.isAllowed(
origin.toExternalForm,
Set(allowedOrigin.toExternalForm).asJava,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ class RelyingPartyAssertionSpec
Defaults.requestedExtensions,
rpId: RelyingPartyIdentity = Defaults.rpId,
signature: ByteArray = Defaults.signature,
userHandleForResponse: ByteArray = Defaults.userHandle,
userHandleForResponse: Option[ByteArray] = Some(Defaults.userHandle),
userHandleForUser: ByteArray = Defaults.userHandle,
usernameForRequest: Option[String] = Some(Defaults.username),
usernameForUser: String = Defaults.username,
Expand Down Expand Up @@ -220,7 +220,7 @@ class RelyingPartyAssertionSpec
if (clientDataJsonBytes == null) null else clientDataJsonBytes
)
.signature(if (signature == null) null else signature)
.userHandle(userHandleForResponse)
.userHandle(userHandleForResponse.asJava)
.build()
)
.clientExtensionResults(clientExtensionResults)
Expand Down Expand Up @@ -523,7 +523,7 @@ class RelyingPartyAssertionSpec
credentialRepository = credentialRepository,
usernameForRequest = Some(owner.username),
userHandleForUser = owner.userHandle,
userHandleForResponse = nonOwner.userHandle,
userHandleForResponse = Some(nonOwner.userHandle),
)
val step: FinishAssertionSteps#Step2 = steps.begin.next.next

Expand All @@ -537,7 +537,7 @@ class RelyingPartyAssertionSpec
credentialRepository = credentialRepository,
usernameForRequest = Some(owner.username),
userHandleForUser = owner.userHandle,
userHandleForResponse = owner.userHandle,
userHandleForResponse = Some(owner.userHandle),
)
val step: FinishAssertionSteps#Step2 = steps.begin.next.next

Expand All @@ -554,7 +554,7 @@ class RelyingPartyAssertionSpec
credentialRepository = credentialRepository,
usernameForRequest = None,
userHandleForUser = owner.userHandle,
userHandleForResponse = nonOwner.userHandle,
userHandleForResponse = Some(nonOwner.userHandle),
)
val step: FinishAssertionSteps#Step2 = steps.begin.next.next

Expand All @@ -563,12 +563,26 @@ class RelyingPartyAssertionSpec
step.tryNext shouldBe a[Failure[_]]
}

it("Fails if neither username nor user handle is given.") {
val steps = finishAssertion(
credentialRepository = credentialRepository,
usernameForRequest = None,
userHandleForUser = owner.userHandle,
userHandleForResponse = None,
)
val step: FinishAssertionSteps#Step0 = steps.begin

step.validations shouldBe a[Failure[_]]
step.validations.failed.get shouldBe an[IllegalArgumentException]
step.tryNext shouldBe a[Failure[_]]
}

it("Succeeds if credential ID is owned by the given user handle.") {
val steps = finishAssertion(
credentialRepository = credentialRepository,
usernameForRequest = None,
userHandleForUser = owner.userHandle,
userHandleForResponse = owner.userHandle,
userHandleForResponse = Some(owner.userHandle),
)
val step: FinishAssertionSteps#Step2 = steps.begin.next.next

Expand Down Expand Up @@ -1320,12 +1334,6 @@ class RelyingPartyAssertionSpec

forAll(Extensions.unrequestedClientAssertionExtensions) {
case (extensionInputs, clientExtensionOutputs, _) =>
println(extensionInputs.getExtensionIds, extensionInputs)
println(
clientExtensionOutputs.getExtensionIds,
clientExtensionOutputs,
)

val steps = finishAssertion(
requestedExtensions = extensionInputs,
clientExtensionResults = clientExtensionOutputs,
Expand All @@ -1344,12 +1352,6 @@ class RelyingPartyAssertionSpec
it("Succeeds if clientExtensionResults is not a subset of the extensions requested by the Relying Party, but the Relying Party has enabled allowing unrequested extensions.") {
forAll(Extensions.unrequestedClientAssertionExtensions) {
case (extensionInputs, clientExtensionOutputs, _) =>
println(extensionInputs.getExtensionIds, extensionInputs)
println(
clientExtensionOutputs.getExtensionIds,
clientExtensionOutputs,
)

val steps = finishAssertion(
allowUnrequestedExtensions = true,
requestedExtensions = extensionInputs,
Expand All @@ -1366,12 +1368,6 @@ class RelyingPartyAssertionSpec
it("Succeeds if clientExtensionResults is a subset of the extensions requested by the Relying Party.") {
forAll(Extensions.subsetAssertionExtensions) {
case (extensionInputs, clientExtensionOutputs, _) =>
println(extensionInputs.getExtensionIds, extensionInputs)
println(
clientExtensionOutputs.getExtensionIds,
clientExtensionOutputs,
)

val steps = finishAssertion(
requestedExtensions = extensionInputs,
clientExtensionResults = clientExtensionOutputs,
Expand All @@ -1393,9 +1389,6 @@ class RelyingPartyAssertionSpec
_,
authenticatorExtensionOutputs: CBORObject,
) =>
println(extensionInputs)
println(authenticatorExtensionOutputs)

val steps = finishAssertion(
requestedExtensions = extensionInputs,
authenticatorData = TestAuthenticator.makeAuthDataBytes(
Expand Down
Loading

0 comments on commit 8eb6278

Please sign in to comment.