001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.authroles.authorization.strategies.role.metadata;
018
019import org.apache.wicket.Application;
020import org.apache.wicket.Component;
021import org.apache.wicket.MetaDataKey;
022import org.apache.wicket.authorization.Action;
023import org.apache.wicket.authroles.authorization.strategies.role.AbstractRoleAuthorizationStrategy;
024import org.apache.wicket.authroles.authorization.strategies.role.IRoleCheckingStrategy;
025import org.apache.wicket.authroles.authorization.strategies.role.Roles;
026import org.apache.wicket.request.component.IRequestableComponent;
027
028
029/**
030 * Strategy that uses the Wicket metadata facility to check authorization. The static
031 * <code>authorize</code> methods are for authorizing component actions and component instantiation
032 * by role. This class is the main entry point for users wanting to use the roles-based
033 * authorization of the wicket-auth-roles package based on wicket metadata.
034 * 
035 * For instance, use like:
036 * 
037 * <pre>
038 * MetaDataRoleAuthorizationStrategy.authorize(myPanel, RENDER, &quot;ADMIN&quot;);
039 * </pre>
040 * 
041 * for actions on component instances, or:
042 * 
043 * <pre>
044 * MetaDataRoleAuthorizationStrategy.authorize(AdminBookmarkablePage.class, &quot;ADMIN&quot;);
045 * </pre>
046 * 
047 * for doing role based authorization for component instantation.
048 * 
049 * @see org.apache.wicket.MetaDataKey
050 * 
051 * @author Eelco Hillenius
052 * @author Jonathan Locke
053 */
054public class MetaDataRoleAuthorizationStrategy extends AbstractRoleAuthorizationStrategy
055{
056        /**
057         * Component meta data key for actions/roles information. Typically, you do not need to use this
058         * meta data key directly, but instead use one of the bind methods of this class.
059         */
060        public static final MetaDataKey<ActionPermissions> ACTION_PERMISSIONS = new MetaDataKey<>()
061        {
062                private static final long serialVersionUID = 1L;
063        };
064
065        /**
066         * Application meta data key for actions/roles information. Typically, you do not need to use
067         * this meta data key directly, but instead use one of the bind methods of this class.
068         */
069        public static final MetaDataKey<InstantiationPermissions> INSTANTIATION_PERMISSIONS = new MetaDataKey<>()
070        {
071                private static final long serialVersionUID = 1L;
072        };
073
074        /** Special role string for denying access to all */
075        public static final String NO_ROLE = "wicket:NO_ROLE";
076
077        /**
078         * Authorizes the given role to create component instances of type componentClass. This
079         * authorization is added to any previously authorized roles.
080         * 
081         * @param <T>
082         * 
083         * @param componentClass
084         *            The component type that is subject for the authorization
085         * @param roles
086         *            The comma separated roles that are authorized to create component instances of
087         *            type componentClass
088         */
089        public static <T extends Component> void authorize(final Class<T> componentClass,
090                final String roles)
091        {
092                final Application application = Application.get();
093                InstantiationPermissions permissions = application.getMetaData(INSTANTIATION_PERMISSIONS);
094                if (permissions == null)
095                {
096                        permissions = new InstantiationPermissions();
097                        application.setMetaData(INSTANTIATION_PERMISSIONS, permissions);
098                }
099                permissions.authorize(componentClass, new Roles(roles));
100        }
101
102        /**
103         * Authorizes the given role to perform the given action on the given component.
104         * 
105         * @param component
106         *            The component that is subject to the authorization
107         * @param action
108         *            The action to authorize
109         * @param roles
110         *            The comma separated roles to authorize
111         */
112        public static void authorize(final Component component, final Action action,
113                final String roles)
114        {
115                ActionPermissions permissions = component.getMetaData(ACTION_PERMISSIONS);
116                if (permissions == null)
117                {
118                        permissions = new ActionPermissions();
119                        component.setMetaData(ACTION_PERMISSIONS, permissions);
120                }
121                permissions.authorize(action, new Roles(roles));
122        }
123
124        /**
125         * Grants permission to all roles to create instances of the given component class.
126         * 
127         * @param <T>
128         * 
129         * @param componentClass
130         *            The component class
131         */
132        public static <T extends Component> void authorizeAll(final Class<T> componentClass)
133        {
134                Application application = Application.get();
135                InstantiationPermissions authorizedRoles = application.getMetaData(INSTANTIATION_PERMISSIONS);
136                if (authorizedRoles != null)
137                {
138                        authorizedRoles.authorizeAll(componentClass);
139                }
140        }
141
142        /**
143         * Grants permission to all roles to perform the given action on the given component.
144         * 
145         * @param component
146         *            The component that is subject to the authorization
147         * @param action
148         *            The action to authorize
149         */
150        public static void authorizeAll(Component component, final Action action)
151        {
152                ActionPermissions permissions = component.getMetaData(ACTION_PERMISSIONS);
153                if (permissions != null)
154                {
155                        permissions.authorizeAll(action);
156                }
157        }
158
159        /**
160         * Removes permission for the given roles to create instances of the given component class.
161         * There is no danger in removing authorization by calling this method. If the last
162         * authorization grant is removed for a given componentClass, the internal role NO_ROLE will
163         * automatically be added, effectively denying access to all roles (if this was not done, all
164         * roles would suddenly have access since no authorization is equivalent to full access).
165         * 
166         * @param <T>
167         * 
168         * @param componentClass
169         *            The component type
170         * @param roles
171         *            The comma separated list of roles that are no longer to be authorized to create
172         *            instances of type componentClass
173         */
174        public static <T extends Component> void unauthorize(final Class<T> componentClass,
175                final String roles)
176        {
177                final InstantiationPermissions permissions = Application.get().getMetaData(
178                        INSTANTIATION_PERMISSIONS);
179                if (permissions != null)
180                {
181                        permissions.unauthorize(componentClass, new Roles(roles));
182                }
183        }
184
185        /**
186         * Removes permission for the given role to perform the given action on the given component.
187         * There is no danger in removing authorization by calling this method. If the last
188         * authorization grant is removed for a given action, the internal role NO_ROLE will
189         * automatically be added, effectively denying access to all roles (if this was not done, all
190         * roles would suddenly have access since no authorization is equivalent to full access).
191         * 
192         * @param component
193         *            The component
194         * @param action
195         *            The action
196         * @param roles
197         *            The comma separated list of roles that are no longer allowed to perform the given
198         *            action
199         */
200        public static void unauthorize(final Component component, final Action action,
201                final String roles)
202        {
203                final ActionPermissions permissions = component.getMetaData(ACTION_PERMISSIONS);
204                if (permissions != null)
205                {
206                        permissions.unauthorize(action, new Roles(roles));
207                }
208        }
209
210        /**
211         * Grants authorization to instantiate the given class to just the role NO_ROLE, effectively
212         * denying all other roles.
213         * 
214         * @param <T>
215         * 
216         * @param componentClass
217         *            The component class
218         */
219        public static <T extends Component> void unauthorizeAll(Class<T> componentClass)
220        {
221                authorizeAll(componentClass);
222                authorize(componentClass, NO_ROLE);
223        }
224
225        /**
226         * Grants authorization to perform the given action to just the role NO_ROLE, effectively
227         * denying all other roles.
228         * 
229         * @param component
230         *            the component that is subject to the authorization
231         * @param action
232         *            the action to authorize
233         */
234        public static void unauthorizeAll(final Component component, final Action action)
235        {
236                authorizeAll(component, action);
237                authorize(component, action, NO_ROLE);
238        }
239
240        /**
241         * Construct.
242         * 
243         * @param roleCheckingStrategy
244         *            the authorizer object
245         */
246        public MetaDataRoleAuthorizationStrategy(final IRoleCheckingStrategy roleCheckingStrategy)
247        {
248                super(roleCheckingStrategy);
249        }
250
251        /**
252         * Uses component level meta data to match roles for component action execution.
253         * 
254         * @see org.apache.wicket.authorization.IAuthorizationStrategy#isActionAuthorized(org.apache.wicket.Component,
255         *      org.apache.wicket.authorization.Action)
256         */
257        @Override
258        public boolean isActionAuthorized(final Component component, final Action action)
259        {
260                if (component == null)
261                {
262                        throw new IllegalArgumentException("argument component has to be not null");
263                }
264                if (action == null)
265                {
266                        throw new IllegalArgumentException("argument action has to be not null");
267                }
268
269                final Roles roles = rolesAuthorizedToPerformAction(component, action);
270                if (roles != null)
271                {
272                        return hasAny(roles);
273                }
274                return true;
275        }
276
277        /**
278         * Uses application level meta data to match roles for component instantiation.
279         * 
280         * @see org.apache.wicket.authorization.IAuthorizationStrategy#isInstantiationAuthorized(java.lang.Class)
281         */
282        @Override
283        public <T extends IRequestableComponent> boolean isInstantiationAuthorized(
284                final Class<T> componentClass)
285        {
286                if (componentClass == null)
287                {
288                        throw new IllegalArgumentException("argument componentClass cannot be null");
289                }
290
291                // as long as the interface does not use generics, we should check this
292                if (!Component.class.isAssignableFrom(componentClass))
293                {
294                        throw new IllegalArgumentException("argument componentClass must be of type " +
295                                Component.class.getName());
296                }
297
298                final Roles roles = rolesAuthorizedToInstantiate(componentClass);
299                if (roles != null)
300                {
301                        return hasAny(roles);
302                }
303                return true;
304        }
305
306        /**
307         * Gets the roles for creation of the given component class, or null if none were registered.
308         * 
309         * @param <T>
310         * 
311         * @param componentClass
312         *            the component class
313         * @return the roles that are authorized for creation of the componentClass, or null if no
314         *         specific authorization was configured
315         */
316        private static <T extends IRequestableComponent> Roles rolesAuthorizedToInstantiate(
317                final Class<T> componentClass)
318        {
319                final InstantiationPermissions permissions = Application.get().getMetaData(
320                        INSTANTIATION_PERMISSIONS);
321                if (permissions != null)
322                {
323                        return permissions.authorizedRoles(componentClass);
324                }
325                return null;
326        }
327
328        /**
329         * Gets the roles for the given action/component combination.
330         * 
331         * @param component
332         *            the component
333         * @param action
334         *            the action
335         * @return the roles for the action as defined with the given component
336         */
337        private static Roles rolesAuthorizedToPerformAction(final Component component,
338                final Action action)
339        {
340                final ActionPermissions permissions = component.getMetaData(ACTION_PERMISSIONS);
341                if (permissions != null)
342                {
343                        return permissions.rolesFor(action);
344                }
345                return null;
346        }
347}