Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
* Creates common {@link AuthorizationManagerFactory} instances.
*
* @author Rob Winch
* @author Andrey Litvitski
* @since 7.0
* @see DefaultAuthorizationManagerFactory
*/
Expand Down Expand Up @@ -57,6 +58,7 @@ public static <T> AdditionalRequiredFactorsBuilder<T> multiFactor() {
*
* @param <T> the type for the {@link DefaultAuthorizationManagerFactory}
* @author Rob Winch
* @author Andrey Litvitski
*/
public static final class AdditionalRequiredFactorsBuilder<T> {

Expand All @@ -65,6 +67,8 @@ public static final class AdditionalRequiredFactorsBuilder<T> {

private @Nullable Predicate<Authentication> whenCondition;

private @Nullable Predicate<Authentication> unlessCondition;

/**
* Apply the required factors only when the given condition is true for the
* current {@link Authentication}. When the condition is false, no additional
Expand All @@ -81,6 +85,21 @@ public AdditionalRequiredFactorsBuilder<T> when(Predicate<Authentication> condit
return this;
}

/**
* Skip the required factors when the given condition is true for the current
* {@link Authentication}. When the condition is true, no additional factors are
* required. Implemented using
* {@link ConditionalAuthorizationManager#when(java.util.function.Predicate)}.
* @param condition the condition to evaluate (must not be null)
* @return the {@link AdditionalRequiredFactorsBuilder} to further customize
* @since 7.1
*/
public AdditionalRequiredFactorsBuilder<T> unless(Predicate<Authentication> condition) {
Assert.notNull(condition, "condition cannot be null");
this.unlessCondition = condition;
return this;
}

/**
* Customize the condition that determines if the required factors are evaluated.
* @param condition a function that takes the current condition and returns the
Expand All @@ -95,6 +114,20 @@ public AdditionalRequiredFactorsBuilder<T> withWhen(
return this;
}

/**
* Customize the condition that determines if the required factors are skipped.
* @param condition a function that takes the current condition and returns the
* new condition
* @return the {@link AdditionalRequiredFactorsBuilder} to further customize
* @since 7.1
*/
public AdditionalRequiredFactorsBuilder<T> withUnless(
Comment on lines +117 to +124
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency, we'll add this method (since there's withWhen)

Function<@Nullable Predicate<Authentication>, @Nullable Predicate<Authentication>> condition) {
Assert.notNull(condition, "condition cannot be null");
this.unlessCondition = condition.apply(this.unlessCondition);
return this;
}

/**
* Add additional authorities that will be required.
* @param additionalAuthorities the additional authorities.
Expand Down Expand Up @@ -134,6 +167,12 @@ public DefaultAuthorizationManagerFactory<T> build() {
.whenTrue(additionalChecks)
.build();
}
if (this.unlessCondition != null) {
additionalChecks = ConditionalAuthorizationManager.<T>when(this.unlessCondition)
.whenTrue(SingleResultAuthorizationManager.permitAll())
.whenFalse(additionalChecks)
.build();
}
result.setAdditionalAuthorization(additionalChecks);
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
* Tests for {@link AuthorizationManagerFactory}.
*
* @author Steve Riesenberg
* @author Andrey Litvitski
*/
public class AuthorizationManagerFactoryTests {

Expand Down Expand Up @@ -366,6 +367,42 @@ public void builderWhenWithWhenNullThenIllegalArgumentException() {
.withMessage("condition cannot be null");
}

@Test
public void builderWhenUnlessConditionFalseThenRequiredFactorsEnforced() {
AuthorizationManagerFactory<String> factory = AuthorizationManagerFactories.<String>multiFactor()
.requireFactors("ROLE_ADMIN")
.unless((auth) -> "bearer".equals(auth.getName()))
.build();
assertUserDenied(factory.hasRole("USER"));
}

@Test
public void builderWhenUnlessConditionTrueThenMfaSkipped() {
AuthorizationManagerFactory<String> factory = AuthorizationManagerFactories.<String>multiFactor()
.requireFactors("ROLE_ADMIN")
.unless((auth) -> "bearer".equals(auth.getName()))
.build();
assertThat(factory.hasRole("USER")
.authorize(() -> new TestingAuthenticationToken("bearer", "password", "ROLE_USER"), "")
.isGranted()).isTrue();
}

@Test
public void builderWhenWithUnlessConditionThenConditionIsCustomized() {
AuthorizationManagerFactory<String> factory = AuthorizationManagerFactories.<String>multiFactor()
.requireFactors("ROLE_ADMIN")
.unless((auth) -> "bearer".equals(auth.getName()))
.withUnless((current) -> (auth) -> current != null && current.test(auth) && auth.isAuthenticated())
.build();
assertThat(factory.hasRole("USER")
.authorize(() -> new TestingAuthenticationToken("bearer", "password", "ROLE_USER"), "")
.isGranted()).isTrue();
TestingAuthenticationToken unauthenticatedBearer = new TestingAuthenticationToken("bearer", "password",
"ROLE_USER");
unauthenticatedBearer.setAuthenticated(false);
assertThat(factory.hasRole("USER").authorize(() -> unauthenticatedBearer, "").isGranted()).isFalse();
}

private void assertUserGranted(AuthorizationManager<String> manager) {
assertThat(manager.authorize(() -> TestAuthentication.authenticatedUser(), "").isGranted()).isTrue();
}
Expand Down
Loading