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 */
019package org.apache.shiro.authz;
020
021import org.apache.shiro.authz.permission.PermissionResolver;
022import org.apache.shiro.authz.permission.PermissionResolverAware;
023import org.apache.shiro.authz.permission.RolePermissionResolver;
024import org.apache.shiro.authz.permission.RolePermissionResolverAware;
025import org.apache.shiro.realm.Realm;
026import org.apache.shiro.subject.PrincipalCollection;
027
028import java.util.Collection;
029import 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 */
038public 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}