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 @@ -81,6 +81,38 @@ public interface SecurityExpressionOperations {
*/
boolean hasAnyRole(String... roles);

/**
* <p>
* Determines if the {@link #getAuthentication()} has a particular authority within
* {@link Authentication#getAuthorities()}.
* </p>
* <p>
* This is similar to {@link #hasAuthority(String)} except that this method implies
* that the String passed in is a scope. For example, if "read" is passed in the
* implementation may convert it to use "SCOPE_read" instead. The way in which the
* scope is converted may depend on the implementation settings.
* </p>
* @param scope the authority to test (i.e. "read")
* @return true if the authority is found, else false
*/
boolean hasScope(String scope);

/**
* <p>
* Determines if the {@link #getAuthentication()} has any of the specified authorities
* within {@link Authentication#getAuthorities()}.
* </p>
* <p>
* This is similar to {@link #hasAnyAuthority(String...)} except that this method
* implies that the String passed in is a scope. For example, if "read" is passed in
* the implementation may convert it to use "SCOPE_read" instead. The way in which the
* scope is converted may depend on the implementation settings.
* </p>
* @param scopes the authorities to test (i.e. "write", "read")
* @return true if any of the authorities is found, else false
*/
boolean hasAnyScope(String... scopes);

/**
* Always grants access.
* @return true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ public abstract class SecurityExpressionRoot<T extends @Nullable Object> impleme

private String defaultRolePrefix = "ROLE_";

private String defaultScopePrefix = "SCOPE_";

private final T object;

private AuthorizationManagerFactory<T> authorizationManagerFactory = new DefaultAuthorizationManagerFactory<>();
Expand Down Expand Up @@ -165,6 +167,24 @@ public final boolean hasAllRoles(String... roles) {
return isGranted(manager);
}

@Override
public final boolean hasScope(String scope) {
assertScope(scope);
return isGranted(this.authorizationManagerFactory.hasAuthority(this.defaultScopePrefix + scope));
}

@Override
public final boolean hasAnyScope(String... scopes) {
Assert.notNull(scopes, "scopes cannot be null");
if (this.authorizationManagerFactory instanceof DefaultAuthorizationManagerFactory<T>) {
for (int i = 0; i < scopes.length; i++) {
assertScope(scopes[i]);
scopes[i] = this.defaultScopePrefix + scopes[i];
}
}
return isGranted(this.authorizationManagerFactory.hasAnyAuthority(scopes));
}

@Override
public final Authentication getAuthentication() {
return this.authentication.get();
Expand Down Expand Up @@ -208,7 +228,8 @@ private boolean isGranted(AuthorizationManager<T> authorizationManager) {
/**
* Convenience method to access {@link Authentication#getPrincipal()} from
* {@link #getAuthentication()}
* @return
* @return the {@code Principal} being authenticated or the authenticated principal
* after authentication.
*/
public @Nullable Object getPrincipal() {
return getAuthentication().getPrincipal();
Expand Down Expand Up @@ -304,4 +325,11 @@ public void setPermissionEvaluator(PermissionEvaluator permissionEvaluator) {
this.permissionEvaluator = permissionEvaluator;
}

private void assertScope(String scope) {
Assert.notNull(scope, "scope cannot be null");
Assert.isTrue(!scope.startsWith(this.defaultScopePrefix), () -> scope + " should not start with '"
+ this.defaultScopePrefix + "' since '" + this.defaultScopePrefix
+ "' is automatically prepended when using hasScope and hasAnyScope. Consider using AuthorityAuthorizationManager#hasAuthority or #hasAnyAuthority instead.");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,48 @@ public void checkDoSomethingStringWhenArgIsNotGrantThenDeniedDecision() throws E
assertThat(decision.isGranted()).isFalse();
}

@Test
public void checkSecuredScope() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"securedScope");
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
AuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isFalse();

Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password",
"SCOPE_read");
decision = manager.authorize(authentication, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isFalse();

authentication = () -> new TestingAuthenticationToken("user", "password", "SCOPE_write");
decision = manager.authorize(authentication, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}

@Test
public void checkSecuredAnyScope() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"securedAnyScope");
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
AuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isFalse();

Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password",
"SCOPE_read");
decision = manager.authorize(authentication, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();

authentication = () -> new TestingAuthenticationToken("user", "password", "SCOPE_write");
decision = manager.authorize(authentication, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}

@Test
public void checkRequiresAdminWhenClassAnnotationsThenMethodAnnotationsTakePrecedence() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
Expand Down Expand Up @@ -181,6 +223,16 @@ public void inheritedAnnotations() {

}

@PreAuthorize("hasScope('write')")
public void securedScope() {

}

@PreAuthorize("hasAnyScope('write', 'read')")
public void securedAnyScope() {

}

}

@PreAuthorize("hasRole('USER')")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,42 @@ public void checkDoSomethingStringWhenArgIsNotGrantThenDeniedDecision() throws E
assertThat(decision.isGranted()).isFalse();
}

@Test
public void checkSecuredScope() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(
new PreAuthorizeAuthorizationManagerTests.TestClass(),
PreAuthorizeAuthorizationManagerTests.TestClass.class, "securedScope");
PreAuthorizeReactiveAuthorizationManager manager = new PreAuthorizeReactiveAuthorizationManager();
Mono<Authentication> authentication = Mono
.just(new TestingAuthenticationToken("user", "password", "SCOPE_read"));
AuthorizationResult decision = manager.authorize(authentication, methodInvocation).block();
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isFalse();

authentication = Mono.just(new TestingAuthenticationToken("user", "password", "SCOPE_write"));
decision = manager.authorize(authentication, methodInvocation).block();
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}

@Test
public void checkSecuredAnyScope() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(
new PreAuthorizeAuthorizationManagerTests.TestClass(),
PreAuthorizeAuthorizationManagerTests.TestClass.class, "securedAnyScope");
PreAuthorizeReactiveAuthorizationManager manager = new PreAuthorizeReactiveAuthorizationManager();
Mono<Authentication> authentication = Mono
.just(new TestingAuthenticationToken("user", "password", "SCOPE_read"));
AuthorizationResult decision = manager.authorize(authentication, methodInvocation).block();
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();

authentication = Mono.just(new TestingAuthenticationToken("user", "password", "SCOPE_write"));
decision = manager.authorize(authentication, methodInvocation).block();
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}

@Test
public void checkRequiresAdminWhenClassAnnotationsThenMethodAnnotationsTakePrecedence() throws Exception {
Mono<Authentication> authentication = Mono
Expand Down Expand Up @@ -149,6 +185,16 @@ public void inheritedAnnotations() {

}

@PreAuthorize("hasScope('write')")
public void securedScope() {

}

@PreAuthorize("hasAnyScope('write', 'read')")
public void securedAnyScope() {

}

}

@PreAuthorize("hasRole('USER')")
Expand Down