Skip to content

Commit c1d2761

Browse files
evgeniychebanjzheaux
authored andcommitted
Simplify AuthorizationManager composition
Closes gh-11625
1 parent 3f8503f commit c1d2761

File tree

2 files changed

+185
-0
lines changed

2 files changed

+185
-0
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.authorization;
18+
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
22+
/**
23+
* A factory class to create an {@link AuthorizationManager} instances.
24+
*
25+
* @author Evgeniy Cheban
26+
* @since 5.8
27+
*/
28+
public final class AuthorizationManagers {
29+
30+
/**
31+
* Creates an {@link AuthorizationManager} that grants access if at least one
32+
* {@link AuthorizationManager} granted or abstained, if <code>managers</code> are
33+
* empty then denied decision is returned.
34+
* @param <T> the type of object that is being authorized
35+
* @param managers the {@link AuthorizationManager}s to use
36+
* @return the {@link AuthorizationManager} to use
37+
*/
38+
@SafeVarargs
39+
public static <T> AuthorizationManager<T> anyOf(AuthorizationManager<T>... managers) {
40+
return (authentication, object) -> {
41+
List<AuthorizationDecision> decisions = new ArrayList<>();
42+
for (AuthorizationManager<T> manager : managers) {
43+
AuthorizationDecision decision = manager.check(authentication, object);
44+
if (decision == null || decision.isGranted()) {
45+
return decision;
46+
}
47+
decisions.add(decision);
48+
}
49+
return new CompositeAuthorizationDecision(false, decisions);
50+
};
51+
}
52+
53+
/**
54+
* Creates an {@link AuthorizationManager} that grants access if all
55+
* {@link AuthorizationManager}s granted or abstained, if <code>managers</code> are
56+
* empty then granted decision is returned.
57+
* @param <T> the type of object that is being authorized
58+
* @param managers the {@link AuthorizationManager}s to use
59+
* @return the {@link AuthorizationManager} to use
60+
*/
61+
@SafeVarargs
62+
public static <T> AuthorizationManager<T> allOf(AuthorizationManager<T>... managers) {
63+
return (authentication, object) -> {
64+
List<AuthorizationDecision> decisions = new ArrayList<>();
65+
for (AuthorizationManager<T> manager : managers) {
66+
AuthorizationDecision decision = manager.check(authentication, object);
67+
if (decision != null && !decision.isGranted()) {
68+
return decision;
69+
}
70+
decisions.add(decision);
71+
}
72+
return new CompositeAuthorizationDecision(true, decisions);
73+
};
74+
}
75+
76+
private AuthorizationManagers() {
77+
}
78+
79+
private static final class CompositeAuthorizationDecision extends AuthorizationDecision {
80+
81+
private final List<AuthorizationDecision> decisions;
82+
83+
private CompositeAuthorizationDecision(boolean granted, List<AuthorizationDecision> decisions) {
84+
super(granted);
85+
this.decisions = decisions;
86+
}
87+
88+
@Override
89+
public String toString() {
90+
return "CompositeAuthorizationDecision [decisions=" + this.decisions + ']';
91+
}
92+
93+
}
94+
95+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.authorization;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import static org.assertj.core.api.Assertions.assertThat;
22+
23+
/**
24+
* Tests for {@link AuthorizationManagers}.
25+
*
26+
* @author Evgeniy Cheban
27+
*/
28+
class AuthorizationManagersTests {
29+
30+
@Test
31+
void checkAnyOfWhenOneGrantedThenGrantedDecision() {
32+
AuthorizationManager<?> composed = AuthorizationManagers.anyOf((a, o) -> new AuthorizationDecision(false),
33+
(a, o) -> new AuthorizationDecision(true));
34+
AuthorizationDecision decision = composed.check(null, null);
35+
assertThat(decision).isNotNull();
36+
assertThat(decision.isGranted()).isTrue();
37+
}
38+
39+
@Test
40+
void checkAnyOfWhenOneAbstainedThenAbstainedDecision() {
41+
AuthorizationManager<?> composed = AuthorizationManagers.anyOf((a, o) -> new AuthorizationDecision(false),
42+
(a, o) -> null);
43+
AuthorizationDecision decision = composed.check(null, null);
44+
assertThat(decision).isNull();
45+
}
46+
47+
@Test
48+
void checkAnyOfWhenEmptyThenDeniedDecision() {
49+
AuthorizationManager<?> composed = AuthorizationManagers.anyOf();
50+
AuthorizationDecision decision = composed.check(null, null);
51+
assertThat(decision).isNotNull();
52+
assertThat(decision.isGranted()).isFalse();
53+
}
54+
55+
@Test
56+
void checkAllOfWhenAllGrantedThenGrantedDecision() {
57+
AuthorizationManager<?> composed = AuthorizationManagers.allOf((a, o) -> new AuthorizationDecision(true),
58+
(a, o) -> new AuthorizationDecision(true));
59+
AuthorizationDecision decision = composed.check(null, null);
60+
assertThat(decision).isNotNull();
61+
assertThat(decision.isGranted()).isTrue();
62+
}
63+
64+
@Test
65+
void checkAllOfWhenOneAbstainedThenGrantedDecision() {
66+
AuthorizationManager<?> composed = AuthorizationManagers.allOf((a, o) -> new AuthorizationDecision(true),
67+
(a, o) -> null);
68+
AuthorizationDecision decision = composed.check(null, null);
69+
assertThat(decision).isNotNull();
70+
assertThat(decision.isGranted()).isTrue();
71+
}
72+
73+
@Test
74+
void checkAllOfWhenOneDeniedThenDeniedDecision() {
75+
AuthorizationManager<?> composed = AuthorizationManagers.allOf((a, o) -> new AuthorizationDecision(true),
76+
(a, o) -> new AuthorizationDecision(false));
77+
AuthorizationDecision decision = composed.check(null, null);
78+
assertThat(decision).isNotNull();
79+
assertThat(decision.isGranted()).isFalse();
80+
}
81+
82+
@Test
83+
void checkAllOfWhenEmptyThenGrantedDecision() {
84+
AuthorizationManager<?> composed = AuthorizationManagers.allOf();
85+
AuthorizationDecision decision = composed.check(null, null);
86+
assertThat(decision).isNotNull();
87+
assertThat(decision.isGranted()).isTrue();
88+
}
89+
90+
}

0 commit comments

Comments
 (0)