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     * @author Les Hazlewood
037     * @since 0.2
038     */
039    public class ModularRealmAuthorizer implements Authorizer, PermissionResolverAware, RolePermissionResolverAware {
040    
041        /**
042         * The realms to consult during any authorization check.
043         */
044        protected Collection<Realm> realms;
045    
046        /**
047         * A PermissionResolver to be used by <em>all</em> configured realms.  Leave <code>null</code> if you wish
048         * to configure different resolvers for different realms.
049         */
050        protected PermissionResolver permissionResolver;
051    
052        /**
053         * A RolePermissionResolver to be used by <em>all</em> configured realms.  Leave <code>null</code> if you wish
054         * to configure different resolvers for different realms.
055         */
056        protected RolePermissionResolver rolePermissionResolver;
057    
058        /**
059         * Default no-argument constructor, does nothing.
060         */
061        public ModularRealmAuthorizer() {
062        }
063    
064        /**
065         * Constructor that accepts the <code>Realm</code>s to consult during an authorization check.  Immediately calls
066         * {@link #setRealms setRealms(realms)}.
067         *
068         * @param realms the realms to consult during an authorization check.
069         */
070        @SuppressWarnings({"UnusedDeclaration"})
071        public ModularRealmAuthorizer(Collection<Realm> realms) {
072            setRealms(realms);
073        }
074    
075        /**
076         * Returns the realms wrapped by this <code>Authorizer</code> which are consulted during an authorization check.
077         *
078         * @return the realms wrapped by this <code>Authorizer</code> which are consulted during an authorization check.
079         */
080        public Collection<Realm> getRealms() {
081            return this.realms;
082        }
083    
084        /**
085         * Sets the realms wrapped by this <code>Authorizer</code> which are consulted during an authorization check.
086         *
087         * @param realms the realms wrapped by this <code>Authorizer</code> which are consulted during an authorization check.
088         */
089        public void setRealms(Collection<Realm> realms) {
090            this.realms = realms;
091            applyPermissionResolverToRealms();
092            applyRolePermissionResolverToRealms();
093        }
094    
095        /**
096         * Returns the PermissionResolver to be used on <em>all</em> configured realms, or <code>null</code (the default)
097         * if all realm instances will each configure their own permission resolver.
098         *
099         * @return the PermissionResolver to be used on <em>all</em> configured realms, or <code>null</code (the default)
100         *         if realm instances will each configure their own permission resolver.
101         * @since 1.0
102         */
103        public PermissionResolver getPermissionResolver() {
104            return this.permissionResolver;
105        }
106    
107        /**
108         * Sets the specified {@link PermissionResolver PermissionResolver} on <em>all</em> of the wrapped realms that
109         * implement the {@link org.apache.shiro.authz.permission.PermissionResolverAware PermissionResolverAware} interface.
110         * <p/>
111         * Only call this method if you want the permission resolver to be passed to all realms that implement the
112         * <code>PermissionResolver</code> interface.  If you do not want this to occur, the realms must
113         * configure themselves individually (or be configured individually).
114         *
115         * @param permissionResolver the permissionResolver to set on all of the wrapped realms that implement the
116         *                           {@link org.apache.shiro.authz.permission.PermissionResolverAware PermissionResolverAware} interface.
117         */
118        public void setPermissionResolver(PermissionResolver permissionResolver) {
119            this.permissionResolver = permissionResolver;
120            applyPermissionResolverToRealms();
121        }
122    
123        /**
124         * Sets the internal {@link #getPermissionResolver} on any internal configured
125         * {@link #getRealms Realms} that implement the {@link org.apache.shiro.authz.permission.PermissionResolverAware PermissionResolverAware} interface.
126         * <p/>
127         * This method is called after setting a permissionResolver on this ModularRealmAuthorizer via the
128         * {@link #setPermissionResolver(org.apache.shiro.authz.permission.PermissionResolver) setPermissionResolver} method.
129         * <p/>
130         * It is also called after setting one or more realms via the {@link #setRealms setRealms} method to allow these
131         * newly available realms to be given the <code>PermissionResolver</code> already in use.
132         *
133         * @since 1.0
134         */
135        protected void applyPermissionResolverToRealms() {
136            PermissionResolver resolver = getPermissionResolver();
137            Collection<Realm> realms = getRealms();
138            if (resolver != null && realms != null && !realms.isEmpty()) {
139                for (Realm realm : realms) {
140                    if (realm instanceof PermissionResolverAware) {
141                        ((PermissionResolverAware) realm).setPermissionResolver(resolver);
142                    }
143                }
144            }
145        }
146    
147        /**
148         * Returns the RolePermissionResolver to be used on <em>all</em> configured realms, or <code>null</code (the default)
149         * if all realm instances will each configure their own permission resolver.
150         *
151         * @return the RolePermissionResolver to be used on <em>all</em> configured realms, or <code>null</code (the default)
152         *         if realm instances will each configure their own role permission resolver.
153         * @since 1.0
154         */
155        public RolePermissionResolver getRolePermissionResolver() {
156            return this.rolePermissionResolver;
157        }
158    
159        /**
160         * Sets the specified {@link RolePermissionResolver RolePermissionResolver} on <em>all</em> of the wrapped realms that
161         * implement the {@link org.apache.shiro.authz.permission.RolePermissionResolverAware PermissionResolverAware} interface.
162         * <p/>
163         * Only call this method if you want the permission resolver to be passed to all realms that implement the
164         * <code>RolePermissionResolver</code> interface.  If you do not want this to occur, the realms must
165         * configure themselves individually (or be configured individually).
166         *
167         * @param rolePermissionResolver the rolePermissionResolver to set on all of the wrapped realms that implement the
168         *                               {@link org.apache.shiro.authz.permission.RolePermissionResolverAware RolePermissionResolverAware} interface.
169         */
170        public void setRolePermissionResolver(RolePermissionResolver rolePermissionResolver) {
171            this.rolePermissionResolver = rolePermissionResolver;
172            applyRolePermissionResolverToRealms();
173        }
174    
175    
176        /**
177         * Sets the internal {@link #getRolePermissionResolver} on any internal configured
178         * {@link #getRealms Realms} that implement the {@link org.apache.shiro.authz.permission.RolePermissionResolverAware RolePermissionResolverAware} interface.
179         * <p/>
180         * This method is called after setting a rolePermissionResolver on this ModularRealmAuthorizer via the
181         * {@link #setRolePermissionResolver(org.apache.shiro.authz.permission.RolePermissionResolver) setRolePermissionResolver} method.
182         * <p/>
183         * It is also called after setting one or more realms via the {@link #setRealms setRealms} method to allow these
184         * newly available realms to be given the <code>RolePermissionResolver</code> already in use.
185         *
186         * @since 1.0
187         */
188        protected void applyRolePermissionResolverToRealms() {
189            RolePermissionResolver resolver = getRolePermissionResolver();
190            Collection<Realm> realms = getRealms();
191            if (resolver != null && realms != null && !realms.isEmpty()) {
192                for (Realm realm : realms) {
193                    if (realm instanceof RolePermissionResolverAware) {
194                        ((RolePermissionResolverAware) realm).setRolePermissionResolver(resolver);
195                    }
196                }
197            }
198        }
199    
200    
201        /**
202         * Used by the {@link Authorizer Authorizer} implementation methods to ensure that the {@link #setRealms realms}
203         * has been set.  The default implementation ensures the property is not null and not empty.
204         *
205         * @throws IllegalStateException if the <tt>realms</tt> property is configured incorrectly.
206         */
207        protected void assertRealmsConfigured() throws IllegalStateException {
208            Collection<Realm> realms = getRealms();
209            if (realms == null || realms.isEmpty()) {
210                String msg = "Configuration error:  No realms have been configured!  One or more realms must be " +
211                        "present to execute an authorization operation.";
212                throw new IllegalStateException(msg);
213            }
214        }
215    
216        /**
217         * Returns <code>true</code> if any of the configured realms'
218         * {@link Realm#isPermitted(org.apache.shiro.subject.PrincipalCollection , String)} returns <code>true</code>,
219         * <code>false</code> otherwise.
220         */
221        public boolean isPermitted(PrincipalCollection principals, String permission) {
222            assertRealmsConfigured();
223            for (Realm realm : getRealms()) {
224                if (realm.isPermitted(principals, permission)) {
225                    return true;
226                }
227            }
228            return false;
229        }
230    
231        /**
232         * Returns <code>true</code> if any of the configured realms'
233         * {@link Realm#isPermitted(org.apache.shiro.subject.PrincipalCollection , Permission)} call returns <code>true</code>,
234         * <code>false</code> otherwise.
235         */
236        public boolean isPermitted(PrincipalCollection principals, Permission permission) {
237            assertRealmsConfigured();
238            for (Realm realm : getRealms()) {
239                if (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 Realm#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 org.apache.shiro.realm.Realm#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 Realm#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 Realm#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 Realm#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.hasRole(principals, roleIdentifier)) {
374                    return true;
375                }
376            }
377            return false;
378        }
379    
380        /**
381         * Calls {@link #hasRole(org.apache.shiro.subject.PrincipalCollection , String)} for each role name in the specified
382         * collection and places the return value from each call at the respective location in the returned array.
383         */
384        public boolean[] hasRoles(PrincipalCollection principals, List<String> roleIdentifiers) {
385            assertRealmsConfigured();
386            if (roleIdentifiers != null && !roleIdentifiers.isEmpty()) {
387                boolean[] hasRoles = new boolean[roleIdentifiers.size()];
388                int i = 0;
389                for (String roleId : roleIdentifiers) {
390                    hasRoles[i++] = hasRole(principals, roleId);
391                }
392                return hasRoles;
393            }
394    
395            return new boolean[0];
396        }
397    
398        /**
399         * Returns <code>true</code> iff any of the configured realms'
400         * {@link Realm#hasRole(org.apache.shiro.subject.PrincipalCollection , String)} call returns <code>true</code> for
401         * <em>all</em> roles specified, <code>false</code> otherwise.
402         */
403        public boolean hasAllRoles(PrincipalCollection principals, Collection<String> roleIdentifiers) {
404            assertRealmsConfigured();
405            for (String roleIdentifier : roleIdentifiers) {
406                if (!hasRole(principals, roleIdentifier)) {
407                    return false;
408                }
409            }
410            return true;
411        }
412    
413        /**
414         * If !{@link #hasRole(org.apache.shiro.subject.PrincipalCollection , String) hasRole(role)}, throws
415         * an <code>UnauthorizedException</code> otherwise returns quietly.
416         */
417        public void checkRole(PrincipalCollection principals, String role) throws AuthorizationException {
418            assertRealmsConfigured();
419            if (!hasRole(principals, role)) {
420                throw new UnauthorizedException("Subject does not have role [" + role + "]");
421            }
422        }
423    
424        /**
425         * Calls {@link #checkRole(org.apache.shiro.subject.PrincipalCollection , String) checkRole} for each role specified.
426         */
427        public void checkRoles(PrincipalCollection principals, Collection<String> roles) throws AuthorizationException {
428            assertRealmsConfigured();
429            if (roles != null) {
430                for (String role : roles) {
431                    checkRole(principals, role);
432                }
433            }
434        }
435    }