Skip to content

Commit

Permalink
- Added Conjunctor, CollectionMutator, and NestedCollection
Browse files Browse the repository at this point in the history
- Added JwkBuilder#operations() NestedCollection builder and removed #operation(KeyOperation) and #operations(Collection<KeyOperation>)
- KeyOperationPolicyBuilder now extends CollectionMutator
  • Loading branch information
lhazlewood committed Sep 28, 2023
1 parent 20b2fa9 commit 3fcad92
Show file tree
Hide file tree
Showing 21 changed files with 416 additions and 156 deletions.
21 changes: 5 additions & 16 deletions api/src/main/java/io/jsonwebtoken/JwtBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.io.Encoder;
import io.jsonwebtoken.io.Serializer;
import io.jsonwebtoken.lang.Conjunctor;
import io.jsonwebtoken.lang.MapMutator;
import io.jsonwebtoken.security.AeadAlgorithm;
import io.jsonwebtoken.security.InvalidKeyException;
Expand Down Expand Up @@ -1052,14 +1053,8 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
*
* @since JJWT_RELEASE_VERSION
*/
interface BuilderClaims extends MapMutator<String, Object, BuilderClaims>, ClaimsMutator<BuilderClaims> {

/**
* Returns the associated JwtBuilder for continued configuration.
*
* @return the associated JwtBuilder for continued configuration.
*/
JwtBuilder and();
interface BuilderClaims extends MapMutator<String, Object, BuilderClaims>, ClaimsMutator<BuilderClaims>,
Conjunctor<JwtBuilder> {
}

/**
Expand All @@ -1069,13 +1064,7 @@ interface BuilderClaims extends MapMutator<String, Object, BuilderClaims>, Claim
*
* @since JJWT_RELEASE_VERSION
*/
interface BuilderHeader extends JweHeaderMutator<BuilderHeader>, X509Builder<BuilderHeader> {

/**
* Returns the associated JwtBuilder for continued configuration.
*
* @return the associated JwtBuilder for continued configuration.
*/
JwtBuilder and();
interface BuilderHeader extends JweHeaderMutator<BuilderHeader>, X509Builder<BuilderHeader>,
Conjunctor<JwtBuilder> {
}
}
61 changes: 61 additions & 0 deletions api/src/main/java/io/jsonwebtoken/lang/CollectionMutator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright © 2023 jsonwebtoken.io
*
* 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 io.jsonwebtoken.lang;

import java.util.Collection;

/**
* Mutation (modifications) to a {@link java.util.Collection} instance while also supporting method chaining. The
* {@link Collection#add(Object)}, {@link Collection#addAll(Collection)}, {@link Collection#remove(Object)}, and
* {@link Collection#clear()} methods do not support method chaining, so this interface enables that behavior.
*
* @param <E> the type of elements in the collection
* @param <M> the mutator subtype, for method chaining
* @since JJWT_RELEASE_VERSION
*/
public interface CollectionMutator<E, M extends CollectionMutator<E, M>> {

/**
* Adds the specified element to the collection.
*
* @param e the element to add.
* @return the mutator/builder for method chaining.
*/
M add(E e);

/**
* Adds the elements to the collection in iteration order.
*
* @param c the collection to add
* @return the mutator/builder for method chaining.
*/
M add(Collection<? extends E> c);

/**
* Removes all elements in the collection.
*
* @return the mutator/builder for method chaining.
*/
M clear();

/**
* Removes the specified element from the collection.
*
* @param e the element to remove.
* @return the mutator/builder for method chaining.
*/
M remove(E e);
}
33 changes: 33 additions & 0 deletions api/src/main/java/io/jsonwebtoken/lang/Conjunctor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright © 2023 jsonwebtoken.io
*
* 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 io.jsonwebtoken.lang;

/**
* A {@code Conjunctor} supplies a joined object. It is typically used for nested builders to return
* to the source/original builder.
*
* @param <T> the type of joined object to return.
* @since JJWT_RELEASE_VERSION
*/
public interface Conjunctor<T> {

/**
* Returns the joined object.
*
* @return the joined object.
*/
T and();
}
27 changes: 27 additions & 0 deletions api/src/main/java/io/jsonwebtoken/lang/NestedCollection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright © 2023 jsonwebtoken.io
*
* 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 io.jsonwebtoken.lang;

/**
* A {@link CollectionMutator} that can return access to its parent via the {@link Conjunctor#and() and()} method for
* continued configuration.
*
* @param <E> the type of elements in the collection
* @param <P> the parent to return
* @since JJWT_RELEASE_VERSION
*/
public interface NestedCollection<E, P> extends CollectionMutator<E, NestedCollection<E, P>>, Conjunctor<P> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package io.jsonwebtoken.security;

import java.security.Key;
import java.util.Collection;

/**
* A {@link JwkBuilder} that builds asymmetric (public or private) JWKs.
Expand Down Expand Up @@ -69,7 +68,7 @@ public interface AsymmetricJwkBuilder<K extends Key, J extends AsymmetricJwk<K>,
*
* <p>Per
* <a href="https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3">JWK RFC 7517, Section 4.3, last paragraph</a>,
* the <code>use (Public Key Use)</code> and {@link #operations(Collection) key_ops (Key Operations)} members
* the <code>use (Public Key Use)</code> and {@link #operations() key_ops (Key Operations)} members
* <em>SHOULD NOT</em> be used together; however, if both are used, the information they convey <em>MUST</em> be
* consistent. Applications should specify which of these members they use, if either is to be used by the
* application.</p>
Expand Down
82 changes: 17 additions & 65 deletions api/src/main/java/io/jsonwebtoken/security/JwkBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@
*/
package io.jsonwebtoken.security;

import io.jsonwebtoken.lang.Conjunctor;
import io.jsonwebtoken.lang.MapMutator;
import io.jsonwebtoken.lang.NestedCollection;

import java.security.Key;
import java.util.Collection;

/**
* A {@link SecurityBuilder} that produces a JWK. A JWK is an immutable set of name/value pairs that represent a
Expand Down Expand Up @@ -104,83 +105,34 @@ public interface JwkBuilder<K extends Key, J extends Jwk<K>, T extends JwkBuilde
T idFromThumbprint(HashAlgorithm alg);

/**
* Specifies an operation for which the key may be used by adding it to the
* JWK <a href="https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3">{@code key_ops} (Key Operations)
* Parameter</a> values. This method may be called multiple times.
* Configures the JWK's {@link KeyOperation}s that identify the operation(s) for which the key is intended to be
* used. Resume JWK modifications by using the nested collection's {@link Conjunctor#and() and()} method
* to return to the JWK builder, for example:</p>
* <blockquote><pre>
* jwkBuilder.operations().add(aKeyOperation).{@link Conjunctor#and() and()} // etc...</pre></blockquote>
*
* <p>The {@code key_ops} (key operations) parameter identifies the operation(s) for which the key is
* intended to be used. The {@code key_ops} parameter is intended for use cases in which public,
* private, or symmetric keys may be present.</p>
*
* <p><b>Security Vulnerability Notice</b></p>
*
* <p>Multiple unrelated key operations <em>SHOULD NOT</em> be specified for a key because of the potential
* vulnerabilities associated with using the same key with multiple algorithms. Thus, the combinations
* {@link Jwks.OP#SIGN sign} with {@link Jwks.OP#VERIFY verify},
* {@link Jwks.OP#ENCRYPT encrypt} with {@link Jwks.OP#DECRYPT decrypt}, and
* {@link Jwks.OP#WRAP_KEY wrapKey} with {@link Jwks.OP#UNWRAP_KEY unwrapKey} are permitted, but other combinations
* <em>SHOULD NOT</em> be used. This is enforced by the builder's key operation
* {@link #operationPolicy(KeyOperationPolicy) policy}.</p>
* <p>The {@code and()} method will throw an {@link IllegalArgumentException} if any of the specified
* {@code KeyOperation}s are not permitted by the JWK's
* {@link #operationPolicy(KeyOperationPolicy) operationPolicy}. See that method's documentation for more
* information on security vulnerabilities when using the same key with multiple algorithms.</p>
*
* <p><b>Standard {@code KeyOperation}s and Overrides</b></p>
*
* <p>All RFC-standard JWK Key Operations in the {@link Jwks.OP} registry are supported via the builder's default
* operations {@link #operationPolicy(KeyOperationPolicy) policy}, but other (custom) values
* {@link #operationPolicy(KeyOperationPolicy) operationPolicy}, but other (custom) values
* <em>MAY</em> be specified (for example, using a {@link Jwks.OP#builder()}).</p>
*
* <p>If the {@code JwkBuilder} is being used to rebuild or parse an existing JWK however, any custom operations
* should be enabled for the {@code JwkBuilder} by {@link #operationPolicy(KeyOperationPolicy) specifying}
* an operations policy that includes the custom values (e.g. via
* should be enabled by configuring an {@link #operationPolicy(KeyOperationPolicy) operationPolicy}
* that includes the custom values (e.g. via
* {@link Jwks.OP#policy()}.{@link KeyOperationPolicyBuilder#add(KeyOperation) add(customKeyOperation)}).</p>
*
* <p>For best interoperability with other applications however, it is recommended to use only the {@link Jwks.OP}
* constants.</p>
*
* @param operation the value to add to the JWK {@code key_ops} value set
* @return the builder for method chaining.
* @throws IllegalArgumentException if the {@code op} is not permitted by the operations
* {@link #operationPolicy(KeyOperationPolicy) policy}.
* @see Jwks.OP
*/
T operation(KeyOperation operation) throws IllegalArgumentException;

/**
* Adds {@code ops} to the JWK <a href="https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3">{@code key_ops}
* (Key Operations) Parameter</a> values.
*
* <p>The {@code key_ops} (key operations) parameter identifies the operation(s) for which the key is
* intended to be used. The {@code key_ops} parameter is intended for use cases in which public,
* private, or symmetric keys may be present.</p>
*
* <p><b>Security Vulnerability Notice</b></p>
*
* <p>Multiple unrelated key operations <em>SHOULD NOT</em> be specified for a key because of the potential
* vulnerabilities associated with using the same key with multiple algorithms. Thus, the combinations
* {@link Jwks.OP#SIGN sign} with {@link Jwks.OP#VERIFY verify},
* {@link Jwks.OP#ENCRYPT encrypt} with {@link Jwks.OP#DECRYPT decrypt}, and
* {@link Jwks.OP#WRAP_KEY wrapKey} with {@link Jwks.OP#UNWRAP_KEY unwrapKey} are permitted, but other combinations
* <em>SHOULD NOT</em> be used. This is enforced by the builder's default
* operation {@link #operationPolicy(KeyOperationPolicy) policy}.</p>
*
* <p><b>Standard {@code KeyOperation}s and Overrides</b></p>
*
* <p>All RFC-standard JWK Key Operations in the {@link Jwks.OP} registry are supported via the builder's default
* operations {@link #operationPolicy(KeyOperationPolicy) policy}, but other (custom) values
* <em>MAY</em> be specified (for example, using a {@link Jwks.OP#builder()}).</p>
*
* <p>If the {@code JwkBuilder} is being used to rebuild or parse an existing JWK however, any custom operations
* should be enabled for the {@code JwkBuilder} by {@link #operationPolicy(KeyOperationPolicy) specifying}
* an operations policy that includes the custom values (e.g. via
* {@link Jwks.OP#policy()}.{@link KeyOperationPolicyBuilder#add(KeyOperation) add(customKeyOperation)}).</p>
*
* <p>For best interoperability with other applications however, it is recommended to use only the {@link Jwks.OP}
* constants.</p>
*
* @param ops the {@code KeyOperation} values to add to the JWK {@code key_ops} value set
* @return the builder for method chaining.
* @throws IllegalArgumentException if any of the operations are not permitted by the operations
* {@link #operationPolicy(KeyOperationPolicy) policy}.
* @return the {@link NestedCollection} to use for {@code key_ops} configuration.
* @see Jwks.OP
* @see <a href="https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3">RFC 7517: key_ops (Key Operations) Parameter</a>
*/
T operations(Collection<KeyOperation> ops) throws IllegalArgumentException;
NestedCollection<KeyOperation, T> operations();
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,9 @@

import io.jsonwebtoken.lang.Builder;

import java.util.Collection;

/**
* A {@code KeyOperationBuilder} produces {@link KeyOperation} instances that may be added to a JWK's
* {@link JwkBuilder#operations(Collection) key operations} parameter. This is primarily only useful for creating
* {@link JwkBuilder#operations() key operations} parameter. This is primarily only useful for creating
* custom (non-standard) {@code KeyOperation}s for use with a custom {@link KeyOperationPolicy}, as all standard ones
* are available already via the {@link Jwks.OP} registry singleton.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ public interface KeyOperationPolicied<T extends KeyOperationPolicied<T>> {
* <blockquote><pre>
* Multiple unrelated key operations SHOULD NOT be specified for a key
* because of the potential vulnerabilities associated with using the
* same key with multiple algorithms.
* </pre></blockquote></li>
* same key with multiple algorithms. Thus, the combinations "{@link Jwks.OP#SIGN sign}"
* with "{@link Jwks.OP#VERIFY verify}", "{@link Jwks.OP#ENCRYPT encrypt}" with "{@link Jwks.OP#DECRYPT decrypt}", and "{@link Jwks.OP#WRAP_KEY wrapKey}" with
* "{@link Jwks.OP#UNWRAP_KEY unwrapKey}" are permitted, but other combinations SHOULD NOT be used.</pre></blockquote>
* </li>
* </ul>
*
* <p>If you wish to enable a different policy, perhaps to support additional custom {@code KeyOperation} values,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
/**
* A key operation policy determines which {@link KeyOperation}s may be assigned to a JWK.
*
* @since JJWT_RELEASE_VERSION
* @see JwkBuilder#operationPolicy(KeyOperationPolicy)
* @since JJWT_RELEASE_VERSION
*/
public interface KeyOperationPolicy {

Expand All @@ -39,5 +39,5 @@ public interface KeyOperationPolicy {
* @param ops the operations to validate
*/
@SuppressWarnings("GrazieInspection")
void validate(Collection<KeyOperation> ops) throws IllegalArgumentException;
void validate(Collection<? extends KeyOperation> ops) throws IllegalArgumentException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import io.jsonwebtoken.Identifiable;
import io.jsonwebtoken.lang.Builder;
import io.jsonwebtoken.lang.CollectionMutator;

import java.util.Collection;

Expand All @@ -32,7 +33,8 @@
* @see Jwks.OP#builder()
* @since JJWT_RELEASE_VERSION
*/
public interface KeyOperationPolicyBuilder extends Builder<KeyOperationPolicy> {
public interface KeyOperationPolicyBuilder extends CollectionMutator<KeyOperation, KeyOperationPolicyBuilder>,
Builder<KeyOperationPolicy> {

/**
* Allows a JWK to have unrelated {@link KeyOperation}s in its {@code key_ops} parameter values. <b>Be careful
Expand Down Expand Up @@ -72,8 +74,10 @@ public interface KeyOperationPolicyBuilder extends Builder<KeyOperationPolicy> {
* @see Jwks.OP
* @see Jwks.OP#builder()
* @see JwkBuilder#operationPolicy(KeyOperationPolicy)
* @see JwkBuilder#operations(Collection)
* @see JwkBuilder#operations()
*/
@Override
// for better JavaDoc
KeyOperationPolicyBuilder add(KeyOperation op);

/**
Expand Down Expand Up @@ -101,8 +105,10 @@ public interface KeyOperationPolicyBuilder extends Builder<KeyOperationPolicy> {
* @see Jwks.OP
* @see Jwks.OP#builder()
* @see JwkBuilder#operationPolicy(KeyOperationPolicy)
* @see JwkBuilder#operations(Collection)
* @see JwkBuilder#operations()
*/
KeyOperationPolicyBuilder add(Collection<KeyOperation> ops);
@Override
// for better JavaDoc
KeyOperationPolicyBuilder add(Collection<? extends KeyOperation> ops);

}
Loading

0 comments on commit 3fcad92

Please sign in to comment.