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    }