8 changed files with 968 additions and 0 deletions
@ -0,0 +1,196 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2004-present the original author or authors. |
||||||
|
* |
||||||
|
* 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 |
||||||
|
* |
||||||
|
* https://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 org.springframework.security.authorization; |
||||||
|
|
||||||
|
import java.time.Clock; |
||||||
|
import java.time.Instant; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Objects; |
||||||
|
import java.util.Optional; |
||||||
|
import java.util.function.Consumer; |
||||||
|
import java.util.function.Supplier; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable; |
||||||
|
|
||||||
|
import org.springframework.security.core.Authentication; |
||||||
|
import org.springframework.security.core.authority.FactorGrantedAuthority; |
||||||
|
import org.springframework.util.Assert; |
||||||
|
|
||||||
|
/** |
||||||
|
* An {@link AuthorizationManager} that determines if the current user is authorized by |
||||||
|
* evaluating if the {@link Authentication} contains a {@link FactorGrantedAuthority} that |
||||||
|
* is not expired for each {@link RequiredFactor}. |
||||||
|
* |
||||||
|
* @author Rob Winch |
||||||
|
* @since 7.0 |
||||||
|
* @see AuthorityAuthorizationManager |
||||||
|
*/ |
||||||
|
public final class AllFactorsAuthorizationManager<T> implements AuthorizationManager<T> { |
||||||
|
|
||||||
|
private Clock clock = Clock.systemUTC(); |
||||||
|
|
||||||
|
private final List<RequiredFactor> requiredFactors; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new instance. |
||||||
|
* @param requiredFactors the authorities that are required. |
||||||
|
*/ |
||||||
|
private AllFactorsAuthorizationManager(List<RequiredFactor> requiredFactors) { |
||||||
|
Assert.notEmpty(requiredFactors, "requiredFactors cannot be empty"); |
||||||
|
Assert.noNullElements(requiredFactors, "requiredFactors must not contain null elements"); |
||||||
|
this.requiredFactors = Collections.unmodifiableList(requiredFactors); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the {@link Clock} to use. |
||||||
|
* @param clock the {@link Clock} to use. Cannot be null. |
||||||
|
*/ |
||||||
|
public void setClock(Clock clock) { |
||||||
|
Assert.notNull(clock, "clock cannot be null"); |
||||||
|
this.clock = clock; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* For each {@link RequiredFactor} finds the first |
||||||
|
* {@link FactorGrantedAuthority#getAuthority()} that matches the |
||||||
|
* {@link RequiredFactor#getAuthority()}. The |
||||||
|
* {@link FactorGrantedAuthority#getIssuedAt()} must be more recent than |
||||||
|
* {@link RequiredFactor#getValidDuration()} (if non-null). |
||||||
|
* @param authentication the {@link Supplier} of the {@link Authentication} to check |
||||||
|
* @param object the object to check authorization on (not used). |
||||||
|
* @return an {@link FactorAuthorizationDecision} |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public FactorAuthorizationDecision authorize(Supplier<? extends @Nullable Authentication> authentication, |
||||||
|
T object) { |
||||||
|
List<FactorGrantedAuthority> currentFactorAuthorities = getFactorGrantedAuthorities(authentication.get()); |
||||||
|
List<RequiredFactorError> factorErrors = this.requiredFactors.stream() |
||||||
|
.map((factor) -> requiredFactorError(factor, currentFactorAuthorities)) |
||||||
|
.filter(Objects::nonNull) |
||||||
|
.toList(); |
||||||
|
return new FactorAuthorizationDecision(factorErrors); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Given the {@link RequiredFactor} and the current {@link FactorGrantedAuthority} |
||||||
|
* instances, returns {@link RequiredFactor} or null if granted. |
||||||
|
* @param requiredFactor the {@link RequiredFactor} to check. |
||||||
|
* @param currentFactors the current user's {@link FactorGrantedAuthority}. |
||||||
|
* @return the {@link RequiredFactor} or null if granted. |
||||||
|
*/ |
||||||
|
private @Nullable RequiredFactorError requiredFactorError(RequiredFactor requiredFactor, |
||||||
|
List<FactorGrantedAuthority> currentFactors) { |
||||||
|
Optional<FactorGrantedAuthority> matchingAuthority = currentFactors.stream() |
||||||
|
.filter((authority) -> authority.getAuthority().equals(requiredFactor.getAuthority())) |
||||||
|
.findFirst(); |
||||||
|
if (!matchingAuthority.isPresent()) { |
||||||
|
return RequiredFactorError.createMissing(requiredFactor); |
||||||
|
} |
||||||
|
return matchingAuthority.map((authority) -> { |
||||||
|
if (requiredFactor.getValidDuration() == null) { |
||||||
|
// granted (only requires authority to match)
|
||||||
|
return null; |
||||||
|
} |
||||||
|
Instant now = this.clock.instant(); |
||||||
|
Instant expiresAt = authority.getIssuedAt().plus(requiredFactor.getValidDuration()); |
||||||
|
if (now.isBefore(expiresAt)) { |
||||||
|
// granted
|
||||||
|
return null; |
||||||
|
} |
||||||
|
// denied (expired)
|
||||||
|
return RequiredFactorError.createExpired(requiredFactor); |
||||||
|
}).orElse(null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Extracts all of the {@link FactorGrantedAuthority} instances from |
||||||
|
* {@link Authentication#getAuthorities()}. If {@link Authentication} is null, or |
||||||
|
* {@link Authentication#isAuthenticated()} is false, then an empty {@link List} is |
||||||
|
* returned. |
||||||
|
* @param authentication the {@link Authentication} (possibly null). |
||||||
|
* @return all of the {@link FactorGrantedAuthority} instances from |
||||||
|
* {@link Authentication#getAuthorities()}. |
||||||
|
*/ |
||||||
|
private List<FactorGrantedAuthority> getFactorGrantedAuthorities(@Nullable Authentication authentication) { |
||||||
|
if (authentication == null || !authentication.isAuthenticated()) { |
||||||
|
return Collections.emptyList(); |
||||||
|
} |
||||||
|
// @formatter:off
|
||||||
|
return authentication.getAuthorities().stream() |
||||||
|
.filter(FactorGrantedAuthority.class::isInstance) |
||||||
|
.map(FactorGrantedAuthority.class::cast) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
// @formatter:on
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new {@link Builder} |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static Builder builder() { |
||||||
|
return new Builder(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* A builder for {@link AllFactorsAuthorizationManager}. |
||||||
|
* |
||||||
|
* @author Rob Winch |
||||||
|
* @since 7.0 |
||||||
|
*/ |
||||||
|
public static final class Builder { |
||||||
|
|
||||||
|
private List<RequiredFactor> requiredFactors = new ArrayList<>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Allows the user to consume the {@link RequiredFactor.Builder} that is passed in |
||||||
|
* and then adds the result to the {@link #requiredFactor(RequiredFactor)}. |
||||||
|
* @param requiredFactor the {@link Consumer} to invoke. |
||||||
|
* @return the builder. |
||||||
|
*/ |
||||||
|
public Builder requiredFactor(Consumer<RequiredFactor.Builder> requiredFactor) { |
||||||
|
Assert.notNull(requiredFactor, "requiredFactor cannot be null"); |
||||||
|
RequiredFactor.Builder builder = RequiredFactor.builder(); |
||||||
|
requiredFactor.accept(builder); |
||||||
|
return requiredFactor(builder.build()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The {@link RequiredFactor} to add. |
||||||
|
* @param requiredFactor the requiredFactor to add. Cannot be null. |
||||||
|
* @return the builder. |
||||||
|
*/ |
||||||
|
public Builder requiredFactor(RequiredFactor requiredFactor) { |
||||||
|
Assert.notNull(requiredFactor, "requiredFactor cannot be null"); |
||||||
|
this.requiredFactors.add(requiredFactor); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Builds the {@link AllFactorsAuthorizationManager}. |
||||||
|
* @param <T> the type. |
||||||
|
* @return the {@link AllFactorsAuthorizationManager} |
||||||
|
*/ |
||||||
|
public <T> AllFactorsAuthorizationManager<T> build() { |
||||||
|
return new AllFactorsAuthorizationManager<T>(this.requiredFactors); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,62 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2004-present the original author or authors. |
||||||
|
* |
||||||
|
* 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 |
||||||
|
* |
||||||
|
* https://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 org.springframework.security.authorization; |
||||||
|
|
||||||
|
import java.util.Collections; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import org.springframework.util.Assert; |
||||||
|
|
||||||
|
/** |
||||||
|
* An {@link AuthorizationResult} that contains {@link RequiredFactorError}. |
||||||
|
* |
||||||
|
* @author Rob Winch |
||||||
|
* @since 7.0 |
||||||
|
*/ |
||||||
|
public class FactorAuthorizationDecision implements AuthorizationResult { |
||||||
|
|
||||||
|
private final List<RequiredFactorError> factorErrors; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new instance. |
||||||
|
* @param factorErrors the {@link RequiredFactorError}. If empty, {@link #isGranted()} |
||||||
|
* returns true. Cannot be null or contain empty values. |
||||||
|
*/ |
||||||
|
public FactorAuthorizationDecision(List<RequiredFactorError> factorErrors) { |
||||||
|
Assert.notNull(factorErrors, "factorErrors cannot be null"); |
||||||
|
Assert.noNullElements(factorErrors, "factorErrors must not contain null elements"); |
||||||
|
this.factorErrors = Collections.unmodifiableList(factorErrors); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The specified {@link RequiredFactorError}s |
||||||
|
* @return the errors. Cannot be null or contain null values. |
||||||
|
*/ |
||||||
|
public List<RequiredFactorError> getFactorErrors() { |
||||||
|
return this.factorErrors; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns {@code getFactorErrors().isEmpty()}. |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public boolean isGranted() { |
||||||
|
return this.factorErrors.isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,142 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2004-present the original author or authors. |
||||||
|
* |
||||||
|
* 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 |
||||||
|
* |
||||||
|
* https://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 org.springframework.security.authorization; |
||||||
|
|
||||||
|
import java.time.Duration; |
||||||
|
import java.util.Objects; |
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable; |
||||||
|
|
||||||
|
import org.springframework.security.core.authority.FactorGrantedAuthority; |
||||||
|
import org.springframework.util.Assert; |
||||||
|
|
||||||
|
/** |
||||||
|
* The requirements for an {@link FactorGrantedAuthority} to be considered valid. |
||||||
|
* |
||||||
|
* @author Rob Winch |
||||||
|
* @since 7.0 |
||||||
|
*/ |
||||||
|
public final class RequiredFactor { |
||||||
|
|
||||||
|
private final String authority; |
||||||
|
|
||||||
|
private final @Nullable Duration validDuration; |
||||||
|
|
||||||
|
private RequiredFactor(String authority, @Nullable Duration validDuration) { |
||||||
|
Assert.notNull(authority, "authority cannot be null"); |
||||||
|
this.authority = authority; |
||||||
|
this.validDuration = validDuration; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The {@link FactorGrantedAuthority#getAuthority()}. |
||||||
|
* @return the authority. |
||||||
|
*/ |
||||||
|
public String getAuthority() { |
||||||
|
return this.authority; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* How long the |
||||||
|
* {@link org.springframework.security.core.authority.FactorGrantedAuthority} is valid |
||||||
|
* for. |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public @Nullable Duration getValidDuration() { |
||||||
|
return this.validDuration; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean equals(Object o) { |
||||||
|
if (!(o instanceof RequiredFactor that)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
return Objects.equals(this.authority, that.authority) && Objects.equals(this.validDuration, that.validDuration); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int hashCode() { |
||||||
|
return Objects.hash(this.authority, this.validDuration); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return "RequiredFactor [authority=" + this.authority + ", validDuration=" + this.validDuration + "]"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a {@link Builder} with the specified authority. |
||||||
|
* @param authority the authority. |
||||||
|
* @return the builder. |
||||||
|
*/ |
||||||
|
public static Builder withAuthority(String authority) { |
||||||
|
return builder().authority(authority); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new {@link Builder}. |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static Builder builder() { |
||||||
|
return new Builder(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* A builder for {@link RequiredFactor}. |
||||||
|
* |
||||||
|
* @author Rob Winch |
||||||
|
* @since 7.0 |
||||||
|
*/ |
||||||
|
public static class Builder { |
||||||
|
|
||||||
|
private @Nullable String authority; |
||||||
|
|
||||||
|
private @Nullable Duration validDuration; |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the required authority. |
||||||
|
* @param authority the authority. |
||||||
|
* @return the builder. |
||||||
|
*/ |
||||||
|
public Builder authority(String authority) { |
||||||
|
this.authority = authority; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the optional {@link Duration} of time that the {@link RequiredFactor} is |
||||||
|
* valid for. |
||||||
|
* @param validDuration the {@link Duration}. |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public Builder validDuration(Duration validDuration) { |
||||||
|
this.validDuration = validDuration; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Builds a new instance. |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public RequiredFactor build() { |
||||||
|
Assert.notNull(this.authority, "authority cannot be null"); |
||||||
|
return new RequiredFactor(this.authority, this.validDuration); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,118 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2004-present the original author or authors. |
||||||
|
* |
||||||
|
* 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 |
||||||
|
* |
||||||
|
* https://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 org.springframework.security.authorization; |
||||||
|
|
||||||
|
import java.util.Objects; |
||||||
|
|
||||||
|
import org.springframework.security.core.authority.FactorGrantedAuthority; |
||||||
|
import org.springframework.util.Assert; |
||||||
|
|
||||||
|
/** |
||||||
|
* An error when the requirements of {@link RequiredFactor} are not met. |
||||||
|
* |
||||||
|
* @author Rob Winch |
||||||
|
* @since 7.0 |
||||||
|
*/ |
||||||
|
public class RequiredFactorError { |
||||||
|
|
||||||
|
private final RequiredFactor requiredFactor; |
||||||
|
|
||||||
|
private final Reason reason; |
||||||
|
|
||||||
|
RequiredFactorError(RequiredFactor requiredFactor, Reason reason) { |
||||||
|
Assert.notNull(requiredFactor, "RequiredFactor must not be null"); |
||||||
|
Assert.notNull(reason, "Reason must not be null"); |
||||||
|
if (reason == Reason.EXPIRED && requiredFactor.getValidDuration() == null) { |
||||||
|
throw new IllegalArgumentException( |
||||||
|
"If expired, RequiredFactor.getValidDuration() must not be null. Got " + requiredFactor); |
||||||
|
} |
||||||
|
this.requiredFactor = requiredFactor; |
||||||
|
this.reason = reason; |
||||||
|
} |
||||||
|
|
||||||
|
public RequiredFactor getRequiredFactor() { |
||||||
|
return this.requiredFactor; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* True if not {@link #isMissing()} but was older than the |
||||||
|
* {@link RequiredFactor#getValidDuration()}. |
||||||
|
* @return true if expired, else false |
||||||
|
*/ |
||||||
|
public boolean isExpired() { |
||||||
|
return this.reason == Reason.EXPIRED; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* True if no {@link FactorGrantedAuthority#getAuthority()} on the |
||||||
|
* {@link org.springframework.security.core.Authentication} matched |
||||||
|
* {@link RequiredFactor#getAuthority()}. |
||||||
|
* @return true if missing, else false. |
||||||
|
*/ |
||||||
|
public boolean isMissing() { |
||||||
|
return this.reason == Reason.MISSING; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean equals(Object o) { |
||||||
|
if (o == null || getClass() != o.getClass()) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
RequiredFactorError that = (RequiredFactorError) o; |
||||||
|
return Objects.equals(this.requiredFactor, that.requiredFactor) && this.reason == that.reason; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int hashCode() { |
||||||
|
return Objects.hash(this.requiredFactor, this.reason); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return "RequiredFactorError{" + "requiredFactor=" + this.requiredFactor + ", reason=" + this.reason + '}'; |
||||||
|
} |
||||||
|
|
||||||
|
public static RequiredFactorError createMissing(RequiredFactor requiredFactor) { |
||||||
|
return new RequiredFactorError(requiredFactor, Reason.MISSING); |
||||||
|
} |
||||||
|
|
||||||
|
public static RequiredFactorError createExpired(RequiredFactor requiredFactor) { |
||||||
|
return new RequiredFactorError(requiredFactor, Reason.EXPIRED); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The reason that the error occurred. |
||||||
|
* |
||||||
|
* @author Rob Winch |
||||||
|
* @since 7.0 |
||||||
|
*/ |
||||||
|
private enum Reason { |
||||||
|
|
||||||
|
/** |
||||||
|
* The authority was missing. |
||||||
|
* @see #isMissing() |
||||||
|
*/ |
||||||
|
MISSING, |
||||||
|
/** |
||||||
|
* The authority was considered expired. |
||||||
|
* @see #isExpired() |
||||||
|
*/ |
||||||
|
EXPIRED |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,249 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2004-present the original author or authors. |
||||||
|
* |
||||||
|
* 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 |
||||||
|
* |
||||||
|
* https://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 org.springframework.security.authorization; |
||||||
|
|
||||||
|
import java.time.Clock; |
||||||
|
import java.time.Duration; |
||||||
|
import java.time.Instant; |
||||||
|
import java.time.ZoneId; |
||||||
|
import java.util.function.Consumer; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
|
||||||
|
import org.springframework.security.authentication.TestingAuthenticationToken; |
||||||
|
import org.springframework.security.core.Authentication; |
||||||
|
import org.springframework.security.core.GrantedAuthorities; |
||||||
|
import org.springframework.security.core.authority.FactorGrantedAuthority; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Test {@link AllFactorsAuthorizationManager}. |
||||||
|
* |
||||||
|
* @author Rob Winch |
||||||
|
* @since 7.0 |
||||||
|
*/ |
||||||
|
class AllFactorsAuthorizationManagerTests { |
||||||
|
|
||||||
|
private static final Object DOES_NOT_MATTER = new Object(); |
||||||
|
|
||||||
|
private static RequiredFactor REQUIRED_PASSWORD = RequiredFactor |
||||||
|
.withAuthority(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY) |
||||||
|
.build(); |
||||||
|
|
||||||
|
private static RequiredFactor EXPIRING_PASSWORD = RequiredFactor |
||||||
|
.withAuthority(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY) |
||||||
|
.validDuration(Duration.ofHours(1)) |
||||||
|
.build(); |
||||||
|
|
||||||
|
@Test |
||||||
|
void authorizeWhenGranted() { |
||||||
|
AllFactorsAuthorizationManager<Object> allFactors = AllFactorsAuthorizationManager.builder() |
||||||
|
.requiredFactor(REQUIRED_PASSWORD) |
||||||
|
.build(); |
||||||
|
FactorGrantedAuthority passwordFactor = FactorGrantedAuthority.withAuthority(REQUIRED_PASSWORD.getAuthority()) |
||||||
|
.issuedAt(Instant.now()) |
||||||
|
.build(); |
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "password", passwordFactor); |
||||||
|
FactorAuthorizationDecision result = allFactors.authorize(() -> authentication, DOES_NOT_MATTER); |
||||||
|
assertThat(result.isGranted()).isTrue(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void authorizeWhenConsumerGranted() { |
||||||
|
AllFactorsAuthorizationManager<Object> allFactors = AllFactorsAuthorizationManager.builder() |
||||||
|
.requiredFactor((required) -> required.authority(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY)) |
||||||
|
.build(); |
||||||
|
FactorGrantedAuthority passwordFactor = FactorGrantedAuthority |
||||||
|
.withAuthority(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY) |
||||||
|
.issuedAt(Instant.now()) |
||||||
|
.build(); |
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "password", passwordFactor); |
||||||
|
FactorAuthorizationDecision result = allFactors.authorize(() -> authentication, DOES_NOT_MATTER); |
||||||
|
assertThat(result.isGranted()).isTrue(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void authorizeWhenUnauthenticated() { |
||||||
|
AllFactorsAuthorizationManager<Object> allFactors = AllFactorsAuthorizationManager.builder() |
||||||
|
.requiredFactor(REQUIRED_PASSWORD) |
||||||
|
.build(); |
||||||
|
FactorGrantedAuthority passwordFactor = FactorGrantedAuthority.withAuthority(REQUIRED_PASSWORD.getAuthority()) |
||||||
|
.issuedAt(Instant.now()) |
||||||
|
.build(); |
||||||
|
TestingAuthenticationToken authentication = new TestingAuthenticationToken("user", "password", passwordFactor); |
||||||
|
authentication.setAuthenticated(false); |
||||||
|
FactorAuthorizationDecision result = allFactors.authorize(() -> authentication, DOES_NOT_MATTER); |
||||||
|
assertThat(result.isGranted()).isFalse(); |
||||||
|
assertThat(result.getFactorErrors()).containsExactly(RequiredFactorError.createMissing(REQUIRED_PASSWORD)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void authorizeWhenNullAuthentication() { |
||||||
|
AllFactorsAuthorizationManager<Object> allFactors = AllFactorsAuthorizationManager.builder() |
||||||
|
.requiredFactor(EXPIRING_PASSWORD) |
||||||
|
.build(); |
||||||
|
Authentication authentication = null; |
||||||
|
FactorAuthorizationDecision result = allFactors.authorize(() -> authentication, DOES_NOT_MATTER); |
||||||
|
assertThat(result.isGranted()).isFalse(); |
||||||
|
assertThat(result.getFactorErrors()).containsExactly(RequiredFactorError.createMissing(EXPIRING_PASSWORD)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void authorizeWhenRequiredFactorHasNullDurationThenNullIssuedAtGranted() { |
||||||
|
AllFactorsAuthorizationManager<Object> allFactors = AllFactorsAuthorizationManager.builder() |
||||||
|
.requiredFactor(REQUIRED_PASSWORD) |
||||||
|
.build(); |
||||||
|
FactorGrantedAuthority passwordFactor = FactorGrantedAuthority.withAuthority(REQUIRED_PASSWORD.getAuthority()) |
||||||
|
.build(); |
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "password", passwordFactor); |
||||||
|
FactorAuthorizationDecision result = allFactors.authorize(() -> authentication, DOES_NOT_MATTER); |
||||||
|
assertThat(result.isGranted()).isTrue(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void authorizeWhenRequiredFactorHasDurationAndNotFactorGrantedAuthorityThenMissing() { |
||||||
|
AllFactorsAuthorizationManager<Object> allFactors = AllFactorsAuthorizationManager.builder() |
||||||
|
.requiredFactor(EXPIRING_PASSWORD) |
||||||
|
.build(); |
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "password", |
||||||
|
EXPIRING_PASSWORD.getAuthority()); |
||||||
|
FactorAuthorizationDecision result = allFactors.authorize(() -> authentication, DOES_NOT_MATTER); |
||||||
|
assertThat(result.isGranted()).isFalse(); |
||||||
|
assertThat(result.getFactorErrors()).containsExactly(RequiredFactorError.createMissing(EXPIRING_PASSWORD)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void authorizeWhenFactorAuthorityMissingThenMissing() { |
||||||
|
AllFactorsAuthorizationManager<Object> allFactors = AllFactorsAuthorizationManager.builder() |
||||||
|
.requiredFactor(REQUIRED_PASSWORD) |
||||||
|
.build(); |
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "password", "ROLE_USER"); |
||||||
|
FactorAuthorizationDecision result = allFactors.authorize(() -> authentication, DOES_NOT_MATTER); |
||||||
|
assertThat(result.isGranted()).isFalse(); |
||||||
|
assertThat(result.getFactorErrors()).containsExactly(RequiredFactorError.createMissing(REQUIRED_PASSWORD)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void authorizeWhenFactorGrantedAuthorityMissingThenMissing() { |
||||||
|
AllFactorsAuthorizationManager<Object> allFactors = AllFactorsAuthorizationManager.builder() |
||||||
|
.requiredFactor(REQUIRED_PASSWORD) |
||||||
|
.build(); |
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "password", |
||||||
|
REQUIRED_PASSWORD.getAuthority()); |
||||||
|
FactorAuthorizationDecision result = allFactors.authorize(() -> authentication, DOES_NOT_MATTER); |
||||||
|
assertThat(result.isGranted()).isFalse(); |
||||||
|
assertThat(result.getFactorErrors()).containsExactly(RequiredFactorError.createMissing(REQUIRED_PASSWORD)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void authorizeWhenExpired() { |
||||||
|
AllFactorsAuthorizationManager<Object> allFactors = AllFactorsAuthorizationManager.builder() |
||||||
|
.requiredFactor(EXPIRING_PASSWORD) |
||||||
|
.build(); |
||||||
|
FactorGrantedAuthority passwordFactor = FactorGrantedAuthority.withAuthority(EXPIRING_PASSWORD.getAuthority()) |
||||||
|
.issuedAt(Instant.now().minus(Duration.ofHours(2))) |
||||||
|
.build(); |
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "password", passwordFactor); |
||||||
|
FactorAuthorizationDecision result = allFactors.authorize(() -> authentication, DOES_NOT_MATTER); |
||||||
|
assertThat(result.isGranted()).isFalse(); |
||||||
|
assertThat(result.getFactorErrors()).containsExactly(RequiredFactorError.createExpired(EXPIRING_PASSWORD)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void authorizeWhenJustExpired() { |
||||||
|
Instant now = Instant.now(); |
||||||
|
Duration expiresIn = Duration.ofHours(1); |
||||||
|
Instant justExpired = now.minus(expiresIn); |
||||||
|
Clock clock = Clock.fixed(now, ZoneId.systemDefault()); |
||||||
|
RequiredFactor expiringPassword = RequiredFactor.withAuthority(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY) |
||||||
|
.validDuration(expiresIn) |
||||||
|
.build(); |
||||||
|
AllFactorsAuthorizationManager<Object> allFactors = AllFactorsAuthorizationManager.builder() |
||||||
|
.requiredFactor(expiringPassword) |
||||||
|
.build(); |
||||||
|
allFactors.setClock(clock); |
||||||
|
FactorGrantedAuthority passwordFactor = FactorGrantedAuthority.withAuthority(expiringPassword.getAuthority()) |
||||||
|
.issuedAt(justExpired) |
||||||
|
.build(); |
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "password", passwordFactor); |
||||||
|
FactorAuthorizationDecision result = allFactors.authorize(() -> authentication, DOES_NOT_MATTER); |
||||||
|
assertThat(result.isGranted()).isFalse(); |
||||||
|
assertThat(result.getFactorErrors()).containsExactly(RequiredFactorError.createExpired(expiringPassword)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void authorizeWhenAlmostExpired() { |
||||||
|
Instant now = Instant.now(); |
||||||
|
Duration expiresIn = Duration.ofHours(1); |
||||||
|
Instant justExpired = now.minus(expiresIn).plus(Duration.ofNanos(1)); |
||||||
|
Clock clock = Clock.fixed(now, ZoneId.systemDefault()); |
||||||
|
RequiredFactor expiringPassword = RequiredFactor.withAuthority(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY) |
||||||
|
.validDuration(expiresIn) |
||||||
|
.build(); |
||||||
|
AllFactorsAuthorizationManager<Object> allFactors = AllFactorsAuthorizationManager.builder() |
||||||
|
.requiredFactor(expiringPassword) |
||||||
|
.build(); |
||||||
|
allFactors.setClock(clock); |
||||||
|
FactorGrantedAuthority passwordFactor = FactorGrantedAuthority.withAuthority(expiringPassword.getAuthority()) |
||||||
|
.issuedAt(justExpired) |
||||||
|
.build(); |
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "password", passwordFactor); |
||||||
|
FactorAuthorizationDecision result = allFactors.authorize(() -> authentication, DOES_NOT_MATTER); |
||||||
|
assertThat(result.isGranted()).isTrue(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void authorizeWhenDifferentFactorGrantedAuthorityThenMissing() { |
||||||
|
AllFactorsAuthorizationManager<Object> allFactors = AllFactorsAuthorizationManager.builder() |
||||||
|
.requiredFactor(REQUIRED_PASSWORD) |
||||||
|
.build(); |
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "password", |
||||||
|
FactorGrantedAuthority.fromAuthority(REQUIRED_PASSWORD.getAuthority()) + "DIFFERENT"); |
||||||
|
FactorAuthorizationDecision result = allFactors.authorize(() -> authentication, DOES_NOT_MATTER); |
||||||
|
assertThat(result.isGranted()).isFalse(); |
||||||
|
assertThat(result.getFactorErrors()).containsExactly(RequiredFactorError.createMissing(REQUIRED_PASSWORD)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void setClockWhenNullThenIllegalArgumentException() { |
||||||
|
AllFactorsAuthorizationManager<Object> allFactors = AllFactorsAuthorizationManager.builder() |
||||||
|
.requiredFactor(REQUIRED_PASSWORD) |
||||||
|
.build(); |
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> allFactors.setClock(null)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void builderBuildWhenEmpty() { |
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> AllFactorsAuthorizationManager.builder().build()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void builderWhenNullRequiredFactor() { |
||||||
|
AllFactorsAuthorizationManager.Builder builder = AllFactorsAuthorizationManager.builder(); |
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> builder.requiredFactor((RequiredFactor) null)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void builderWhenNullConsumerRequiredFactorBuilder() { |
||||||
|
AllFactorsAuthorizationManager.Builder builder = AllFactorsAuthorizationManager.builder(); |
||||||
|
assertThatIllegalArgumentException() |
||||||
|
.isThrownBy(() -> builder.requiredFactor((Consumer<RequiredFactor.Builder>) null)); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,77 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2004-present the original author or authors. |
||||||
|
* |
||||||
|
* 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 |
||||||
|
* |
||||||
|
* https://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 org.springframework.security.authorization; |
||||||
|
|
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
|
||||||
|
import org.springframework.security.core.GrantedAuthorities; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests for {@link FactorAuthorizationDecision}. |
||||||
|
* |
||||||
|
* @author Rob Winch |
||||||
|
* @since 7.0 |
||||||
|
*/ |
||||||
|
class FactorAuthorizationDecisionTests { |
||||||
|
|
||||||
|
@Test |
||||||
|
void isGrantedWhenEmptyThenTrue() { |
||||||
|
FactorAuthorizationDecision decision = new FactorAuthorizationDecision(List.of()); |
||||||
|
assertThat(decision.isGranted()).isTrue(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void isGrantedWhenNotEmptyThenFalse() { |
||||||
|
RequiredFactor requiredPassword = RequiredFactor.withAuthority(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY) |
||||||
|
.build(); |
||||||
|
RequiredFactorError missingPassword = RequiredFactorError.createMissing(requiredPassword); |
||||||
|
FactorAuthorizationDecision decision = new FactorAuthorizationDecision(List.of(missingPassword)); |
||||||
|
assertThat(decision.isGranted()).isFalse(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void getFactorErrors() { |
||||||
|
RequiredFactor requiredPassword = RequiredFactor.withAuthority(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY) |
||||||
|
.build(); |
||||||
|
RequiredFactorError missingPassword = RequiredFactorError.createMissing(requiredPassword); |
||||||
|
List<RequiredFactorError> factorErrors = List.of(missingPassword); |
||||||
|
FactorAuthorizationDecision decision = new FactorAuthorizationDecision(factorErrors); |
||||||
|
assertThat(decision.getFactorErrors()).isEqualTo(factorErrors); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void constructorWhenNullThenThrowIllegalArgumentException() { |
||||||
|
List<RequiredFactorError> factorErrors = null; |
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> new FactorAuthorizationDecision(factorErrors)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void constructorWhenContainsNullThenThrowIllegalArgumentException() { |
||||||
|
RequiredFactor requiredPassword = RequiredFactor.withAuthority(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY) |
||||||
|
.build(); |
||||||
|
RequiredFactorError missingPassword = RequiredFactorError.createMissing(requiredPassword); |
||||||
|
List<RequiredFactorError> hasNullValue = Arrays.asList(missingPassword, null); |
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> new FactorAuthorizationDecision(hasNullValue)); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,64 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2004-present the original author or authors. |
||||||
|
* |
||||||
|
* 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 |
||||||
|
* |
||||||
|
* https://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 org.springframework.security.authorization; |
||||||
|
|
||||||
|
import java.time.Duration; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
|
||||||
|
import org.springframework.security.core.GrantedAuthorities; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests for {@link RequiredFactorError}. |
||||||
|
* |
||||||
|
* @author Rob Winch |
||||||
|
* @since 7.0 |
||||||
|
*/ |
||||||
|
class RequiredFactorErrorTests { |
||||||
|
|
||||||
|
public static final RequiredFactor REQUIRED_FACTOR = RequiredFactor |
||||||
|
.withAuthority(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY) |
||||||
|
.validDuration(Duration.ofHours(1)) |
||||||
|
.build(); |
||||||
|
|
||||||
|
@Test |
||||||
|
void createMissing() { |
||||||
|
RequiredFactorError error = RequiredFactorError.createMissing(REQUIRED_FACTOR); |
||||||
|
assertThat(error.isMissing()).isTrue(); |
||||||
|
assertThat(error.isExpired()).isFalse(); |
||||||
|
assertThat(error.getRequiredFactor()).isEqualTo(REQUIRED_FACTOR); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void createExpired() { |
||||||
|
RequiredFactorError error = RequiredFactorError.createExpired(REQUIRED_FACTOR); |
||||||
|
assertThat(error.isMissing()).isFalse(); |
||||||
|
assertThat(error.isExpired()).isTrue(); |
||||||
|
assertThat(error.getRequiredFactor()).isEqualTo(REQUIRED_FACTOR); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void createExpiredWhenNullValidDurationThenIllegalArgumentException() { |
||||||
|
RequiredFactor requiredPassword = RequiredFactor.withAuthority(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY) |
||||||
|
.build(); |
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> RequiredFactorError.createExpired(requiredPassword)); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,60 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2004-present the original author or authors. |
||||||
|
* |
||||||
|
* 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 |
||||||
|
* |
||||||
|
* https://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 org.springframework.security.authorization; |
||||||
|
|
||||||
|
import java.time.Duration; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
|
||||||
|
import org.springframework.security.core.GrantedAuthorities; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests for {@link RequiredFactor}. |
||||||
|
* |
||||||
|
* @author Rob Winch |
||||||
|
* @since 7.0 |
||||||
|
*/ |
||||||
|
class RequiredFactorTests { |
||||||
|
|
||||||
|
@Test |
||||||
|
void builderWhenNullAuthorityIllegalArgumentException() { |
||||||
|
RequiredFactor.Builder builder = RequiredFactor.builder(); |
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> builder.build()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void withAuthorityThenEquals() { |
||||||
|
RequiredFactor requiredPassword = RequiredFactor.withAuthority(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY) |
||||||
|
.build(); |
||||||
|
assertThat(requiredPassword.getAuthority()).isEqualTo(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY); |
||||||
|
assertThat(requiredPassword.getValidDuration()).isNull(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void builderValidDurationThenEquals() { |
||||||
|
Duration validDuration = Duration.ofMinutes(1); |
||||||
|
RequiredFactor requiredPassword = RequiredFactor.withAuthority(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY) |
||||||
|
.validDuration(validDuration) |
||||||
|
.build(); |
||||||
|
assertThat(requiredPassword.getAuthority()).isEqualTo(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY); |
||||||
|
assertThat(requiredPassword.getValidDuration()).isEqualTo(validDuration); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue