1616
1717package org .springframework .security .authorization ;
1818
19+ import java .util .ArrayList ;
20+ import java .util .List ;
21+
1922import org .jspecify .annotations .Nullable ;
2023
2124import org .springframework .security .access .hierarchicalroles .NullRoleHierarchy ;
2225import org .springframework .security .access .hierarchicalroles .RoleHierarchy ;
2326import org .springframework .security .authentication .AuthenticationTrustResolver ;
2427import org .springframework .security .authentication .AuthenticationTrustResolverImpl ;
2528import org .springframework .util .Assert ;
29+ import org .springframework .util .CollectionUtils ;
2630
2731/**
2832 * A factory for creating different kinds of {@link AuthorizationManager} instances.
2933 *
3034 * @param <T> the type of object that the authorization check is being done on
3135 * @author Steve Riesenberg
36+ * @author Andrey Litvitski
37+ * @author Rob Winch
3238 * @since 7.0
3339 */
3440public final class DefaultAuthorizationManagerFactory <T extends @ Nullable Object >
@@ -40,6 +46,8 @@ public final class DefaultAuthorizationManagerFactory<T extends @Nullable Object
4046
4147 private String rolePrefix = "ROLE_" ;
4248
49+ private @ Nullable AuthorizationManager <T > additionalAuthorization ;
50+
4351 /**
4452 * Sets the {@link AuthenticationTrustResolver} used to check the user's
4553 * authentication.
@@ -69,71 +77,173 @@ public void setRolePrefix(String rolePrefix) {
6977 this .rolePrefix = rolePrefix ;
7078 }
7179
80+ /**
81+ * Sets additional authorization to be applied to the returned
82+ * {@link AuthorizationManager} for the following methods:
83+ *
84+ * <ul>
85+ * <li>{@link #hasRole(String)}</li>
86+ * <li>{@link #hasAnyRole(String...)}</li>
87+ * <li>{@link #hasAllRoles(String...)}</li>
88+ * <li>{@link #hasAuthority(String)}</li>
89+ * <li>{@link #hasAnyAuthority(String...)}</li>
90+ * <li>{@link #hasAllAuthorities(String...)}</li>
91+ * <li>{@link #authenticated()}</li>
92+ * <li>{@link #fullyAuthenticated()}</li>
93+ * <li>{@link #rememberMe()}</li>
94+ * </ul>
95+ *
96+ * <p>
97+ * This does not affect {@code anonymous}, {@code permitAll}, or {@code denyAll}.
98+ * </p>
99+ * @param additionalAuthorization the {@link AuthorizationManager} to be applied.
100+ * Default is null (no additional authorization).
101+ */
102+ public void setAdditionalAuthorization (@ Nullable AuthorizationManager <T > additionalAuthorization ) {
103+ this .additionalAuthorization = additionalAuthorization ;
104+ }
105+
72106 @ Override
73107 public AuthorizationManager <T > hasRole (String role ) {
74108 return hasAnyRole (role );
75109 }
76110
77111 @ Override
78112 public AuthorizationManager <T > hasAnyRole (String ... roles ) {
79- return withRoleHierarchy (AuthorityAuthorizationManager .hasAnyRole (this .rolePrefix , roles ));
113+ return createManager (AuthorityAuthorizationManager .hasAnyRole (this .rolePrefix , roles ));
80114 }
81115
82116 @ Override
83117 public AuthorizationManager <T > hasAllRoles (String ... roles ) {
84- return withRoleHierarchy (AllAuthoritiesAuthorizationManager .hasAllPrefixedAuthorities (this .rolePrefix , roles ));
118+ return createManager (AllAuthoritiesAuthorizationManager .hasAllPrefixedAuthorities (this .rolePrefix , roles ));
85119 }
86120
87121 @ Override
88122 public AuthorizationManager <T > hasAuthority (String authority ) {
89- return withRoleHierarchy (AuthorityAuthorizationManager .hasAuthority (authority ));
123+ return createManager (AuthorityAuthorizationManager .hasAuthority (authority ));
90124 }
91125
92126 @ Override
93127 public AuthorizationManager <T > hasAnyAuthority (String ... authorities ) {
94- return withRoleHierarchy (AuthorityAuthorizationManager .hasAnyAuthority (authorities ));
128+ return createManager (AuthorityAuthorizationManager .hasAnyAuthority (authorities ));
95129 }
96130
97131 @ Override
98132 public AuthorizationManager <T > hasAllAuthorities (String ... authorities ) {
99- return withRoleHierarchy (AllAuthoritiesAuthorizationManager .hasAllAuthorities (authorities ));
133+ return createManager (AllAuthoritiesAuthorizationManager .hasAllAuthorities (authorities ));
100134 }
101135
102136 @ Override
103137 public AuthorizationManager <T > authenticated () {
104- return withTrustResolver (AuthenticatedAuthorizationManager .authenticated ());
138+ return createManager (AuthenticatedAuthorizationManager .authenticated ());
105139 }
106140
107141 @ Override
108142 public AuthorizationManager <T > fullyAuthenticated () {
109- return withTrustResolver (AuthenticatedAuthorizationManager .fullyAuthenticated ());
143+ return createManager (AuthenticatedAuthorizationManager .fullyAuthenticated ());
110144 }
111145
112146 @ Override
113147 public AuthorizationManager <T > rememberMe () {
114- return withTrustResolver (AuthenticatedAuthorizationManager .rememberMe ());
148+ return createManager (AuthenticatedAuthorizationManager .rememberMe ());
115149 }
116150
117151 @ Override
118152 public AuthorizationManager <T > anonymous () {
119- return withTrustResolver (AuthenticatedAuthorizationManager .anonymous ());
153+ return createManager (AuthenticatedAuthorizationManager .anonymous ());
120154 }
121155
122- private AuthorityAuthorizationManager <T > withRoleHierarchy (AuthorityAuthorizationManager <T > authorizationManager ) {
156+ /**
157+ * Creates a {@link Builder} that helps build an {@link AuthorizationManager} to set
158+ * on {@link #setAdditionalAuthorization(AuthorizationManager)} for common scenarios.
159+ * <p>
160+ * Does not affect {@code anonymous}, {@code permitAll}, or {@code denyAll}.
161+ * @param <T> the secured object type
162+ * @return a factory configured with the required authorities
163+ */
164+ public static <T > Builder <T > builder () {
165+ return new Builder <>();
166+ }
167+
168+ private AuthorizationManager <T > createManager (AuthorityAuthorizationManager <T > authorizationManager ) {
123169 authorizationManager .setRoleHierarchy (this .roleHierarchy );
124- return authorizationManager ;
170+ return withAdditionalAuthorization ( authorizationManager ) ;
125171 }
126172
127- private AllAuthoritiesAuthorizationManager <T > withRoleHierarchy (
128- AllAuthoritiesAuthorizationManager <T > authorizationManager ) {
173+ private AuthorizationManager <T > createManager (AllAuthoritiesAuthorizationManager <T > authorizationManager ) {
129174 authorizationManager .setRoleHierarchy (this .roleHierarchy );
130- return authorizationManager ;
175+ return withAdditionalAuthorization ( authorizationManager ) ;
131176 }
132177
133- private AuthenticatedAuthorizationManager <T > withTrustResolver (
134- AuthenticatedAuthorizationManager <T > authorizationManager ) {
178+ private AuthorizationManager <T > createManager (AuthenticatedAuthorizationManager <T > authorizationManager ) {
135179 authorizationManager .setTrustResolver (this .trustResolver );
136- return authorizationManager ;
180+ return withAdditionalAuthorization (authorizationManager );
181+ }
182+
183+ private AuthorizationManager <T > withAdditionalAuthorization (AuthorizationManager <T > manager ) {
184+ if (this .additionalAuthorization == null ) {
185+ return manager ;
186+ }
187+ return AuthorizationManagers .allOf (new AuthorizationDecision (false ), this .additionalAuthorization , manager );
188+ }
189+
190+ /**
191+ * A builder that allows creating {@link DefaultAuthorizationManagerFactory} with
192+ * additional authorization for common scenarios.
193+ *
194+ * @param <T> the type for the {@link DefaultAuthorizationManagerFactory}
195+ * @author Rob Winch
196+ */
197+ public static final class Builder <T > {
198+
199+ private final List <String > additionalAuthorities = new ArrayList <>();
200+
201+ private RoleHierarchy roleHierarchy = new NullRoleHierarchy ();
202+
203+ /**
204+ * Add additional authorities that will be required.
205+ * @param additionalAuthorities the additional authorities.
206+ * @return the {@link Builder} to further customize.
207+ */
208+ public Builder <T > requireAdditionalAuthorities (String ... additionalAuthorities ) {
209+ Assert .notEmpty (additionalAuthorities , "additionalAuthorities cannot be empty" );
210+ for (String additionalAuthority : additionalAuthorities ) {
211+ this .additionalAuthorities .add (additionalAuthority );
212+ }
213+ return this ;
214+ }
215+
216+ /**
217+ * The {@link RoleHierarchy} to use.
218+ * @param roleHierarchy the non-null {@link RoleHierarchy} to use. Default is
219+ * {@link NullRoleHierarchy}.
220+ * @return the Builder to further customize.
221+ */
222+ public Builder <T > roleHierarchy (RoleHierarchy roleHierarchy ) {
223+ Assert .notNull (roleHierarchy , "roleHierarchy cannot be null" );
224+ this .roleHierarchy = roleHierarchy ;
225+ return this ;
226+ }
227+
228+ /**
229+ * Builds a {@link DefaultAuthorizationManagerFactory} that has the
230+ * {@link #setAdditionalAuthorization(AuthorizationManager)} set.
231+ * @return the {@link DefaultAuthorizationManagerFactory}.
232+ */
233+ public DefaultAuthorizationManagerFactory <T > build () {
234+ Assert .state (!CollectionUtils .isEmpty (this .additionalAuthorities ), "additionalAuthorities cannot be empty" );
235+ DefaultAuthorizationManagerFactory <T > result = new DefaultAuthorizationManagerFactory <>();
236+ AllAuthoritiesAuthorizationManager <T > additionalChecks = AllAuthoritiesAuthorizationManager
237+ .hasAllAuthorities (this .additionalAuthorities );
238+ result .setRoleHierarchy (this .roleHierarchy );
239+ additionalChecks .setRoleHierarchy (this .roleHierarchy );
240+ result .setAdditionalAuthorization (additionalChecks );
241+ return result ;
242+ }
243+
244+ private Builder () {
245+ }
246+
137247 }
138248
139249}
0 commit comments