001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019 package org.apache.shiro.authz;
020
021 import org.apache.shiro.authz.permission.PermissionResolver;
022 import org.apache.shiro.authz.permission.PermissionResolverAware;
023 import org.apache.shiro.authz.permission.RolePermissionResolver;
024 import org.apache.shiro.authz.permission.RolePermissionResolverAware;
025 import org.apache.shiro.realm.Realm;
026 import org.apache.shiro.subject.PrincipalCollection;
027
028 import java.util.Collection;
029 import java.util.List;
030
031
032 /**
033 * A <tt>ModularRealmAuthorizer</tt> is an <tt>Authorizer</tt> implementation that consults one or more configured
034 * {@link Realm Realm}s during an authorization operation.
035 *
036 * @since 0.2
037 */
038 public class ModularRealmAuthorizer implements Authorizer, PermissionResolverAware, RolePermissionResolverAware {
039
040 /**
041 * The realms to consult during any authorization check.
042 */
043 protected Collection<Realm> realms;
044
045 /**
046 * A PermissionResolver to be used by <em>all</em> configured realms. Leave <code>null</code> if you wish
047 * to configure different resolvers for different realms.
048 */
049 protected PermissionResolver permissionResolver;
050
051 /**
052 * A RolePermissionResolver to be used by <em>all</em> configured realms. Leave <code>null</code> if you wish
053 * to configure different resolvers for different realms.
054 */
055 protected RolePermissionResolver rolePermissionResolver;
056
057 /**
058 * Default no-argument constructor, does nothing.
059 */
060 public ModularRealmAuthorizer() {
061 }
062
063 /**
064 * Constructor that accepts the <code>Realm</code>s to consult during an authorization check. Immediately calls
065 * {@link #setRealms setRealms(realms)}.
066 *
067 * @param realms the realms to consult during an authorization check.
068 */
069 public ModularRealmAuthorizer(Collection<Realm> realms) {
070 setRealms(realms);
071 }
072
073 /**
074 * Returns the realms wrapped by this <code>Authorizer</code> which are consulted during an authorization check.
075 *
076 * @return the realms wrapped by this <code>Authorizer</code> which are consulted during an authorization check.
077 */
078 public Collection<Realm> getRealms() {
079 return this.realms;
080 }
081
082 /**
083 * Sets the realms wrapped by this <code>Authorizer</code> which are consulted during an authorization check.
084 *
085 * @param realms the realms wrapped by this <code>Authorizer</code> which are consulted during an authorization check.
086 */
087 public void setRealms(Collection<Realm> realms) {
088 this.realms = realms;
089 applyPermissionResolverToRealms();
090 applyRolePermissionResolverToRealms();
091 }
092
093 /**
094 * Returns the PermissionResolver to be used on <em>all</em> configured realms, or <code>null</code (the default)
095 * if all realm instances will each configure their own permission resolver.
096 *
097 * @return the PermissionResolver to be used on <em>all</em> configured realms, or <code>null</code (the default)
098 * if realm instances will each configure their own permission resolver.
099 * @since 1.0
100 */
101 public PermissionResolver getPermissionResolver() {
102 return this.permissionResolver;
103 }
104
105 /**
106 * Sets the specified {@link PermissionResolver PermissionResolver} on <em>all</em> of the wrapped realms that
107 * implement the {@link org.apache.shiro.authz.permission.PermissionResolverAware PermissionResolverAware} interface.
108 * <p/>
109 * Only call this method if you want the permission resolver to be passed to all realms that implement the
110 * <code>PermissionResolver</code> interface. If you do not want this to occur, the realms must
111 * configure themselves individually (or be configured individually).
112 *
113 * @param permissionResolver the permissionResolver to set on all of the wrapped realms that implement the
114 * {@link org.apache.shiro.authz.permission.PermissionResolverAware PermissionResolverAware} interface.
115 */
116 public void setPermissionResolver(PermissionResolver permissionResolver) {
117 this.permissionResolver = permissionResolver;
118 applyPermissionResolverToRealms();
119 }
120
121 /**
122 * Sets the internal {@link #getPermissionResolver} on any internal configured
123 * {@link #getRealms Realms} that implement the {@link org.apache.shiro.authz.permission.PermissionResolverAware PermissionResolverAware} interface.
124 * <p/>
125 * This method is called after setting a permissionResolver on this ModularRealmAuthorizer via the
126 * {@link #setPermissionResolver(org.apache.shiro.authz.permission.PermissionResolver) setPermissionResolver} method.
127 * <p/>
128 * It is also called after setting one or more realms via the {@link #setRealms setRealms} method to allow these
129 * newly available realms to be given the <code>PermissionResolver</code> already in use.
130 *
131 * @since 1.0
132 */
133 protected void applyPermissionResolverToRealms() {
134 PermissionResolver resolver = getPermissionResolver();
135 Collection<Realm> realms = getRealms();
136 if (resolver != null && realms != null && !realms.isEmpty()) {
137 for (Realm realm : realms) {
138 if (realm instanceof PermissionResolverAware) {
139 ((PermissionResolverAware) realm).setPermissionResolver(resolver);
140 }
141 }
142 }
143 }
144
145 /**
146 * Returns the RolePermissionResolver to be used on <em>all</em> configured realms, or <code>null</code (the default)
147 * if all realm instances will each configure their own permission resolver.
148 *
149 * @return the RolePermissionResolver to be used on <em>all</em> configured realms, or <code>null</code (the default)
150 * if realm instances will each configure their own role permission resolver.
151 * @since 1.0
152 */
153 public RolePermissionResolver getRolePermissionResolver() {
154 return this.rolePermissionResolver;
155 }
156
157 /**
158 * Sets the specified {@link RolePermissionResolver RolePermissionResolver} on <em>all</em> of the wrapped realms that
159 * implement the {@link org.apache.shiro.authz.permission.RolePermissionResolverAware PermissionResolverAware} interface.
160 * <p/>
161 * Only call this method if you want the permission resolver to be passed to all realms that implement the
162 * <code>RolePermissionResolver</code> interface. If you do not want this to occur, the realms must
163 * configure themselves individually (or be configured individually).
164 *
165 * @param rolePermissionResolver the rolePermissionResolver to set on all of the wrapped realms that implement the
166 * {@link org.apache.shiro.authz.permission.RolePermissionResolverAware RolePermissionResolverAware} interface.
167 */
168 public void setRolePermissionResolver(RolePermissionResolver rolePermissionResolver) {
169 this.rolePermissionResolver = rolePermissionResolver;
170 applyRolePermissionResolverToRealms();
171 }
172
173
174 /**
175 * Sets the internal {@link #getRolePermissionResolver} on any internal configured
176 * {@link #getRealms Realms} that implement the {@link org.apache.shiro.authz.permission.RolePermissionResolverAware RolePermissionResolverAware} interface.
177 * <p/>
178 * This method is called after setting a rolePermissionResolver on this ModularRealmAuthorizer via the
179 * {@link #setRolePermissionResolver(org.apache.shiro.authz.permission.RolePermissionResolver) setRolePermissionResolver} method.
180 * <p/>
181 * It is also called after setting one or more realms via the {@link #setRealms setRealms} method to allow these
182 * newly available realms to be given the <code>RolePermissionResolver</code> already in use.
183 *
184 * @since 1.0
185 */
186 protected void applyRolePermissionResolverToRealms() {
187 RolePermissionResolver resolver = getRolePermissionResolver();
188 Collection<Realm> realms = getRealms();
189 if (resolver != null && realms != null && !realms.isEmpty()) {
190 for (Realm realm : realms) {
191 if (realm instanceof RolePermissionResolverAware) {
192 ((RolePermissionResolverAware) realm).setRolePermissionResolver(resolver);
193 }
194 }
195 }
196 }
197
198
199 /**
200 * Used by the {@link Authorizer Authorizer} implementation methods to ensure that the {@link #setRealms realms}
201 * has been set. The default implementation ensures the property is not null and not empty.
202 *
203 * @throws IllegalStateException if the <tt>realms</tt> property is configured incorrectly.
204 */
205 protected void assertRealmsConfigured() throws IllegalStateException {
206 Collection<Realm> realms = getRealms();
207 if (realms == null || realms.isEmpty()) {
208 String msg = "Configuration error: No realms have been configured! One or more realms must be " +
209 "present to execute an authorization operation.";
210 throw new IllegalStateException(msg);
211 }
212 }
213
214 /**
215 * Returns <code>true</code> if any of the configured realms'
216 * {@link #isPermitted(org.apache.shiro.subject.PrincipalCollection, String)} returns <code>true</code>,
217 * <code>false</code> otherwise.
218 */
219 public boolean isPermitted(PrincipalCollection principals, String permission) {
220 assertRealmsConfigured();
221 for (Realm realm : getRealms()) {
222 if (!(realm instanceof Authorizer)) continue;
223 if (((Authorizer) realm).isPermitted(principals, permission)) {
224 return true;
225 }
226 }
227 return false;
228 }
229
230 /**
231 * Returns <code>true</code> if any of the configured realms'
232 * {@link #isPermitted(org.apache.shiro.subject.PrincipalCollection, Permission)} call returns <code>true</code>,
233 * <code>false</code> otherwise.
234 */
235 public boolean isPermitted(PrincipalCollection principals, Permission permission) {
236 assertRealmsConfigured();
237 for (Realm realm : getRealms()) {
238 if (!(realm instanceof Authorizer)) continue;
239 if (((Authorizer) realm).isPermitted(principals, permission)) {
240 return true;
241 }
242 }
243 return false;
244 }
245
246 /**
247 * Returns <code>true</code> if any of the configured realms'
248 * {@link #isPermittedAll(org.apache.shiro.subject.PrincipalCollection, String...)} call returns
249 * <code>true</code>, <code>false</code> otherwise.
250 */
251 public boolean[] isPermitted(PrincipalCollection principals, String... permissions) {
252 assertRealmsConfigured();
253 if (permissions != null && permissions.length > 0) {
254 boolean[] isPermitted = new boolean[permissions.length];
255 for (int i = 0; i < permissions.length; i++) {
256 isPermitted[i] = isPermitted(principals, permissions[i]);
257 }
258 return isPermitted;
259 }
260 return new boolean[0];
261 }
262
263 /**
264 * Returns <code>true</code> if any of the configured realms'
265 * {@link #isPermitted(org.apache.shiro.subject.PrincipalCollection, List)} call returns <code>true</code>,
266 * <code>false</code> otherwise.
267 */
268 public boolean[] isPermitted(PrincipalCollection principals, List<Permission> permissions) {
269 assertRealmsConfigured();
270 if (permissions != null && !permissions.isEmpty()) {
271 boolean[] isPermitted = new boolean[permissions.size()];
272 int i = 0;
273 for (Permission p : permissions) {
274 isPermitted[i++] = isPermitted(principals, p);
275 }
276 return isPermitted;
277 }
278
279 return new boolean[0];
280 }
281
282 /**
283 * Returns <code>true</code> if any of the configured realms'
284 * {@link #isPermitted(org.apache.shiro.subject.PrincipalCollection, String)} call returns <code>true</code>
285 * for <em>all</em> of the specified string permissions, <code>false</code> otherwise.
286 */
287 public boolean isPermittedAll(PrincipalCollection principals, String... permissions) {
288 assertRealmsConfigured();
289 if (permissions != null && permissions.length > 0) {
290 for (String perm : permissions) {
291 if (!isPermitted(principals, perm)) {
292 return false;
293 }
294 }
295 }
296 return true;
297 }
298
299 /**
300 * Returns <code>true</code> if any of the configured realms'
301 * {@link #isPermitted(org.apache.shiro.subject.PrincipalCollection, Permission)} call returns <code>true</code>
302 * for <em>all</em> of the specified Permissions, <code>false</code> otherwise.
303 */
304 public boolean isPermittedAll(PrincipalCollection principals, Collection<Permission> permissions) {
305 assertRealmsConfigured();
306 if (permissions != null && !permissions.isEmpty()) {
307 for (Permission permission : permissions) {
308 if (!isPermitted(principals, permission)) {
309 return false;
310 }
311 }
312 }
313 return true;
314 }
315
316 /**
317 * If !{@link #isPermitted(org.apache.shiro.subject.PrincipalCollection, String) isPermitted(permission)}, throws
318 * an <code>UnauthorizedException</code> otherwise returns quietly.
319 */
320 public void checkPermission(PrincipalCollection principals, String permission) throws AuthorizationException {
321 assertRealmsConfigured();
322 if (!isPermitted(principals, permission)) {
323 throw new UnauthorizedException("Subject does not have permission [" + permission + "]");
324 }
325 }
326
327 /**
328 * If !{@link #isPermitted(org.apache.shiro.subject.PrincipalCollection, Permission) isPermitted(permission)}, throws
329 * an <code>UnauthorizedException</code> otherwise returns quietly.
330 */
331 public void checkPermission(PrincipalCollection principals, Permission permission) throws AuthorizationException {
332 assertRealmsConfigured();
333 if (!isPermitted(principals, permission)) {
334 throw new UnauthorizedException("Subject does not have permission [" + permission + "]");
335 }
336 }
337
338 /**
339 * If !{@link #isPermitted(org.apache.shiro.subject.PrincipalCollection, String...) isPermitted(permission)},
340 * throws an <code>UnauthorizedException</code> otherwise returns quietly.
341 */
342 public void checkPermissions(PrincipalCollection principals, String... permissions) throws AuthorizationException {
343 assertRealmsConfigured();
344 if (permissions != null && permissions.length > 0) {
345 for (String perm : permissions) {
346 checkPermission(principals, perm);
347 }
348 }
349 }
350
351 /**
352 * If !{@link #isPermitted(org.apache.shiro.subject.PrincipalCollection, Permission) isPermitted(permission)} for
353 * <em>all</em> the given Permissions, throws
354 * an <code>UnauthorizedException</code> otherwise returns quietly.
355 */
356 public void checkPermissions(PrincipalCollection principals, Collection<Permission> permissions) throws AuthorizationException {
357 assertRealmsConfigured();
358 if (permissions != null) {
359 for (Permission permission : permissions) {
360 checkPermission(principals, permission);
361 }
362 }
363 }
364
365 /**
366 * Returns <code>true</code> if any of the configured realms'
367 * {@link #hasRole(org.apache.shiro.subject.PrincipalCollection, String)} call returns <code>true</code>,
368 * <code>false</code> otherwise.
369 */
370 public boolean hasRole(PrincipalCollection principals, String roleIdentifier) {
371 assertRealmsConfigured();
372 for (Realm realm : getRealms()) {
373 if (!(realm instanceof Authorizer)) continue;
374 if (((Authorizer) realm).hasRole(principals, roleIdentifier)) {
375 return true;
376 }
377 }
378 return false;
379 }
380
381 /**
382 * Calls {@link #hasRole(org.apache.shiro.subject.PrincipalCollection, String)} for each role name in the specified
383 * collection and places the return value from each call at the respective location in the returned array.
384 */
385 public boolean[] hasRoles(PrincipalCollection principals, List<String> roleIdentifiers) {
386 assertRealmsConfigured();
387 if (roleIdentifiers != null && !roleIdentifiers.isEmpty()) {
388 boolean[] hasRoles = new boolean[roleIdentifiers.size()];
389 int i = 0;
390 for (String roleId : roleIdentifiers) {
391 hasRoles[i++] = hasRole(principals, roleId);
392 }
393 return hasRoles;
394 }
395
396 return new boolean[0];
397 }
398
399 /**
400 * Returns <code>true</code> iff any of the configured realms'
401 * {@link #hasRole(org.apache.shiro.subject.PrincipalCollection, String)} call returns <code>true</code> for
402 * <em>all</em> roles specified, <code>false</code> otherwise.
403 */
404 public boolean hasAllRoles(PrincipalCollection principals, Collection<String> roleIdentifiers) {
405 assertRealmsConfigured();
406 for (String roleIdentifier : roleIdentifiers) {
407 if (!hasRole(principals, roleIdentifier)) {
408 return false;
409 }
410 }
411 return true;
412 }
413
414 /**
415 * If !{@link #hasRole(org.apache.shiro.subject.PrincipalCollection, String) hasRole(role)}, throws
416 * an <code>UnauthorizedException</code> otherwise returns quietly.
417 */
418 public void checkRole(PrincipalCollection principals, String role) throws AuthorizationException {
419 assertRealmsConfigured();
420 if (!hasRole(principals, role)) {
421 throw new UnauthorizedException("Subject does not have role [" + role + "]");
422 }
423 }
424
425 /**
426 * Calls {@link #checkRoles(PrincipalCollection principals, String... roles) checkRoles(PrincipalCollection principals, String... roles) }.
427 */
428 public void checkRoles(PrincipalCollection principals, Collection<String> roles) throws AuthorizationException {
429 //SHIRO-234 - roles.toArray() -> roles.toArray(new String[roles.size()])
430 if (roles != null && !roles.isEmpty()) checkRoles(principals, roles.toArray(new String[roles.size()]));
431 }
432
433 /**
434 * Calls {@link #checkRole(org.apache.shiro.subject.PrincipalCollection, String) checkRole} for each role specified.
435 */
436 public void checkRoles(PrincipalCollection principals, String... roles) throws AuthorizationException {
437 assertRealmsConfigured();
438 if (roles != null) {
439 for (String role : roles) {
440 checkRole(principals, role);
441 }
442 }
443 }
444 }