6 changed files with 471 additions and 3 deletions
@ -0,0 +1,78 @@
@@ -0,0 +1,78 @@
|
||||
/* |
||||
* Copyright 2002-2022 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.web.access.expression; |
||||
|
||||
import org.springframework.security.access.expression.AbstractSecurityExpressionHandler; |
||||
import org.springframework.security.access.expression.SecurityExpressionHandler; |
||||
import org.springframework.security.access.expression.SecurityExpressionOperations; |
||||
import org.springframework.security.authentication.AuthenticationTrustResolver; |
||||
import org.springframework.security.authentication.AuthenticationTrustResolverImpl; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.security.web.access.intercept.RequestAuthorizationContext; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* A {@link SecurityExpressionHandler} that uses a {@link RequestAuthorizationContext} to |
||||
* create a {@link WebSecurityExpressionRoot}. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
* @since 5.8 |
||||
*/ |
||||
public class DefaultHttpSecurityExpressionHandler extends AbstractSecurityExpressionHandler<RequestAuthorizationContext> |
||||
implements SecurityExpressionHandler<RequestAuthorizationContext> { |
||||
|
||||
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl(); |
||||
|
||||
private String defaultRolePrefix = "ROLE_"; |
||||
|
||||
@Override |
||||
protected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, |
||||
RequestAuthorizationContext context) { |
||||
WebSecurityExpressionRoot root = new WebSecurityExpressionRoot(authentication, context.getRequest()); |
||||
root.setRoleHierarchy(getRoleHierarchy()); |
||||
root.setPermissionEvaluator(getPermissionEvaluator()); |
||||
root.setTrustResolver(this.trustResolver); |
||||
root.setDefaultRolePrefix(this.defaultRolePrefix); |
||||
return root; |
||||
} |
||||
|
||||
/** |
||||
* Sets the {@link AuthenticationTrustResolver} to be used. The default is |
||||
* {@link AuthenticationTrustResolverImpl}. |
||||
* @param trustResolver the {@link AuthenticationTrustResolver} to use |
||||
*/ |
||||
public void setTrustResolver(AuthenticationTrustResolver trustResolver) { |
||||
Assert.notNull(trustResolver, "trustResolver cannot be null"); |
||||
this.trustResolver = trustResolver; |
||||
} |
||||
|
||||
/** |
||||
* Sets the default prefix to be added to |
||||
* {@link org.springframework.security.access.expression.SecurityExpressionRoot#hasAnyRole(String...)} |
||||
* or |
||||
* {@link org.springframework.security.access.expression.SecurityExpressionRoot#hasRole(String)}. |
||||
* For example, if hasRole("ADMIN") or hasRole("ROLE_ADMIN") is passed in, then the |
||||
* role ROLE_ADMIN will be used when the defaultRolePrefix is "ROLE_" (default). |
||||
* @param defaultRolePrefix the default prefix to add to roles. The default is |
||||
* "ROLE_". |
||||
*/ |
||||
public void setDefaultRolePrefix(String defaultRolePrefix) { |
||||
Assert.hasText(defaultRolePrefix, "defaultRolePrefix cannot be empty"); |
||||
this.defaultRolePrefix = defaultRolePrefix; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,55 @@
@@ -0,0 +1,55 @@
|
||||
/* |
||||
* Copyright 2002-2022 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.web.access.expression; |
||||
|
||||
import org.springframework.expression.Expression; |
||||
import org.springframework.security.authorization.AuthorizationDecision; |
||||
|
||||
/** |
||||
* An expression-based {@link AuthorizationDecision}. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
* @since 5.8 |
||||
*/ |
||||
public final class ExpressionAuthorizationDecision extends AuthorizationDecision { |
||||
|
||||
private final Expression expression; |
||||
|
||||
/** |
||||
* Creates an instance. |
||||
* @param granted the decision to use |
||||
* @param expression the {@link Expression} to use |
||||
*/ |
||||
public ExpressionAuthorizationDecision(boolean granted, Expression expression) { |
||||
super(granted); |
||||
this.expression = expression; |
||||
} |
||||
|
||||
/** |
||||
* Returns the {@link Expression}. |
||||
* @return the {@link Expression} to use |
||||
*/ |
||||
public Expression getExpression() { |
||||
return this.expression; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "ExpressionAuthorizationDecision[granted=" + isGranted() + ", expression='" + this.expression + "']"; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,84 @@
@@ -0,0 +1,84 @@
|
||||
/* |
||||
* Copyright 2002-2022 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.web.access.expression; |
||||
|
||||
import java.util.function.Supplier; |
||||
|
||||
import org.springframework.expression.EvaluationContext; |
||||
import org.springframework.expression.Expression; |
||||
import org.springframework.security.access.expression.ExpressionUtils; |
||||
import org.springframework.security.access.expression.SecurityExpressionHandler; |
||||
import org.springframework.security.authorization.AuthorizationDecision; |
||||
import org.springframework.security.authorization.AuthorizationManager; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.security.web.access.intercept.RequestAuthorizationContext; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* An expression-based {@link AuthorizationManager} that determines the access by |
||||
* evaluating the provided expression. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
* @since 5.8 |
||||
*/ |
||||
public final class WebExpressionAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> { |
||||
|
||||
private SecurityExpressionHandler<RequestAuthorizationContext> expressionHandler = new DefaultHttpSecurityExpressionHandler(); |
||||
|
||||
private Expression expression; |
||||
|
||||
/** |
||||
* Creates an instance. |
||||
* @param expressionString the raw expression string to parse |
||||
*/ |
||||
public WebExpressionAuthorizationManager(String expressionString) { |
||||
Assert.hasText(expressionString, "expressionString cannot be empty"); |
||||
this.expression = this.expressionHandler.getExpressionParser().parseExpression(expressionString); |
||||
} |
||||
|
||||
/** |
||||
* Sets the {@link SecurityExpressionHandler} to be used. The default is |
||||
* {@link DefaultHttpSecurityExpressionHandler}. |
||||
* @param expressionHandler the {@link SecurityExpressionHandler} to use |
||||
*/ |
||||
public void setExpressionHandler(SecurityExpressionHandler<RequestAuthorizationContext> expressionHandler) { |
||||
Assert.notNull(expressionHandler, "expressionHandler cannot be null"); |
||||
this.expressionHandler = expressionHandler; |
||||
this.expression = expressionHandler.getExpressionParser() |
||||
.parseExpression(this.expression.getExpressionString()); |
||||
} |
||||
|
||||
/** |
||||
* Determines the access by evaluating the provided expression. |
||||
* @param authentication the {@link Supplier} of the {@link Authentication} to check |
||||
* @param context the {@link RequestAuthorizationContext} to check |
||||
* @return an {@link ExpressionAuthorizationDecision} based on the evaluated |
||||
* expression |
||||
*/ |
||||
@Override |
||||
public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext context) { |
||||
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), context); |
||||
boolean granted = ExpressionUtils.evaluateAsBoolean(this.expression, ctx); |
||||
return new ExpressionAuthorizationDecision(granted, this.expression); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "WebExpressionAuthorizationManager[expression='" + this.expression + "']"; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,105 @@
@@ -0,0 +1,105 @@
|
||||
/* |
||||
* Copyright 2002-2022 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.web.access.expression; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.expression.Expression; |
||||
import org.springframework.expression.ExpressionParser; |
||||
import org.springframework.mock.web.MockHttpServletRequest; |
||||
import org.springframework.security.authentication.TestAuthentication; |
||||
import org.springframework.security.authorization.AuthorizationDecision; |
||||
import org.springframework.security.web.access.intercept.RequestAuthorizationContext; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; |
||||
import static org.mockito.BDDMockito.given; |
||||
import static org.mockito.Mockito.mock; |
||||
import static org.mockito.Mockito.verify; |
||||
|
||||
/** |
||||
* Tests for {@link WebExpressionAuthorizationManager}. |
||||
* |
||||
* @author Evgeniy Cheban |
||||
*/ |
||||
class WebExpressionAuthorizationManagerTests { |
||||
|
||||
@Test |
||||
void instantiateWhenExpressionStringNullThenIllegalArgumentException() { |
||||
assertThatIllegalArgumentException().isThrownBy(() -> new WebExpressionAuthorizationManager(null)) |
||||
.withMessage("expressionString cannot be empty"); |
||||
} |
||||
|
||||
@Test |
||||
void instantiateWhenExpressionStringEmptyThenIllegalArgumentException() { |
||||
assertThatIllegalArgumentException().isThrownBy(() -> new WebExpressionAuthorizationManager("")) |
||||
.withMessage("expressionString cannot be empty"); |
||||
} |
||||
|
||||
@Test |
||||
void instantiateWhenExpressionStringBlankThenIllegalArgumentException() { |
||||
assertThatIllegalArgumentException().isThrownBy(() -> new WebExpressionAuthorizationManager(" ")) |
||||
.withMessage("expressionString cannot be empty"); |
||||
} |
||||
|
||||
@Test |
||||
void instantiateWhenExpressionHandlerNotSetThenDefaultUsed() { |
||||
WebExpressionAuthorizationManager manager = new WebExpressionAuthorizationManager("hasRole('ADMIN')"); |
||||
assertThat(manager).extracting("expressionHandler").isInstanceOf(DefaultHttpSecurityExpressionHandler.class); |
||||
} |
||||
|
||||
@Test |
||||
void setExpressionHandlerWhenNullThenIllegalArgumentException() { |
||||
WebExpressionAuthorizationManager manager = new WebExpressionAuthorizationManager("hasRole('ADMIN')"); |
||||
assertThatIllegalArgumentException().isThrownBy(() -> manager.setExpressionHandler(null)) |
||||
.withMessage("expressionHandler cannot be null"); |
||||
} |
||||
|
||||
@Test |
||||
void setExpressionHandlerWhenNotNullThenVerifyExpressionHandler() { |
||||
String expressionString = "hasRole('ADMIN')"; |
||||
WebExpressionAuthorizationManager manager = new WebExpressionAuthorizationManager(expressionString); |
||||
DefaultHttpSecurityExpressionHandler expressionHandler = new DefaultHttpSecurityExpressionHandler(); |
||||
ExpressionParser mockExpressionParser = mock(ExpressionParser.class); |
||||
Expression mockExpression = mock(Expression.class); |
||||
given(mockExpressionParser.parseExpression(expressionString)).willReturn(mockExpression); |
||||
expressionHandler.setExpressionParser(mockExpressionParser); |
||||
manager.setExpressionHandler(expressionHandler); |
||||
assertThat(manager).extracting("expressionHandler").isEqualTo(expressionHandler); |
||||
assertThat(manager).extracting("expression").isEqualTo(mockExpression); |
||||
verify(mockExpressionParser).parseExpression(expressionString); |
||||
} |
||||
|
||||
@Test |
||||
void checkWhenExpressionHasRoleAdminConfiguredAndRoleAdminThenGrantedDecision() { |
||||
WebExpressionAuthorizationManager manager = new WebExpressionAuthorizationManager("hasRole('ADMIN')"); |
||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin, |
||||
new RequestAuthorizationContext(new MockHttpServletRequest())); |
||||
assertThat(decision).isNotNull(); |
||||
assertThat(decision.isGranted()).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
void checkWhenExpressionHasRoleAdminConfiguredAndRoleUserThenDeniedDecision() { |
||||
WebExpressionAuthorizationManager manager = new WebExpressionAuthorizationManager("hasRole('ADMIN')"); |
||||
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, |
||||
new RequestAuthorizationContext(new MockHttpServletRequest())); |
||||
assertThat(decision).isNotNull(); |
||||
assertThat(decision.isGranted()).isFalse(); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue