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.wiki.auth.permissions;
020    
021    import java.io.Serializable;
022    import java.security.Permission;
023    import java.security.PermissionCollection;
024    import java.util.Arrays;
025    
026    /**
027     * <p> Permission to perform an global wiki operation, such as self-registering
028     * or creating new pages. Permission actions include: <code>createGroups</code>,
029     * <code>createPages</code>, <code>editPreferences</code>,
030     * <code>editProfile</code> and <code>login</code>. </p> <p>The target is
031     * a given wiki. The syntax for the target is the wiki name. "All wikis" can be
032     * specified using a wildcard (*). Page collections may also be specified using
033     * a wildcard. For pages, the wildcard may be a prefix, suffix, or all by
034     * itself. <p> Certain permissions imply others. Currently,
035     * <code>createGroups</code> implies <code>createPages</code>. </p>
036     * @since 2.3
037     */
038    public final class WikiPermission extends Permission implements Serializable
039    {
040        private static final long          serialVersionUID        = 1L;
041    
042        /** Name of the action for createGroups permission. */
043        public static final String         CREATE_GROUPS_ACTION    = "createGroups";
044    
045        /** Name of the action for createPages permission. */   
046        public static final String         CREATE_PAGES_ACTION     = "createPages";
047    
048        /** Name of the action for login permission. */
049        public static final String         LOGIN_ACTION            = "login";
050    
051        /** Name of the action for editPreferences permission. */
052        public static final String         EDIT_PREFERENCES_ACTION = "editPreferences";
053    
054        /** Name of the action for editProfile permission. */
055        public static final String         EDIT_PROFILE_ACTION     = "editProfile";
056    
057        /** Value for a generic wildcard. */
058        public static final String         WILDCARD                = "*";
059    
060        protected static final int         CREATE_GROUPS_MASK      = 0x1;
061    
062        protected static final int         CREATE_PAGES_MASK       = 0x2;
063    
064        protected static final int         EDIT_PREFERENCES_MASK   = 0x4;
065    
066        protected static final int         EDIT_PROFILE_MASK       = 0x8;
067    
068        protected static final int         LOGIN_MASK              = 0x10;
069    
070        /** A static instance of the createGroups permission. */
071        public static final WikiPermission CREATE_GROUPS           = new WikiPermission( WILDCARD, CREATE_GROUPS_ACTION );
072    
073        /** A static instance of the createPages permission. */
074        public static final WikiPermission CREATE_PAGES            = new WikiPermission( WILDCARD, CREATE_PAGES_ACTION );
075    
076        /** A static instance of the login permission. */
077        public static final WikiPermission LOGIN                   = new WikiPermission( WILDCARD, LOGIN_ACTION );
078    
079        /** A static instance of the editPreferences permission. */
080        public static final WikiPermission EDIT_PREFERENCES        = new WikiPermission( WILDCARD, EDIT_PREFERENCES_ACTION );
081    
082        /** A static instance of the editProfile permission. */
083        public static final WikiPermission EDIT_PROFILE            = new WikiPermission( WILDCARD, EDIT_PROFILE_ACTION );
084    
085        private final String               m_actionString;
086    
087        private final String               m_wiki;
088    
089        private final int                  m_mask;
090    
091        /**
092         * Creates a new WikiPermission for a specified set of actions.
093         * @param actions the actions for this permission
094         * @param wiki The name of the wiki the permission belongs to.
095         */
096        public WikiPermission( String wiki, String actions )
097        {
098            super( wiki );
099            String[] pageActions = actions.toLowerCase().split( "," );
100            Arrays.sort( pageActions, String.CASE_INSENSITIVE_ORDER );
101            m_mask = createMask( actions );
102            StringBuffer buffer = new StringBuffer();
103            for( int i = 0; i < pageActions.length; i++ )
104            {
105                buffer.append( pageActions[i] );
106                if ( i < ( pageActions.length - 1 ) )
107                {
108                    buffer.append( "," );
109                }
110            }
111            m_actionString = buffer.toString();
112            m_wiki = ( wiki == null ) ? WILDCARD : wiki;
113        }
114    
115        /**
116         * Two WikiPermission objects are considered equal if their wikis and
117         * actions (after normalization) are equal.
118         * @param obj the object to test
119         * @return the result
120         * @see java.lang.Object#equals(java.lang.Object)
121         */
122        public boolean equals( Object obj )
123        {
124            if ( !( obj instanceof WikiPermission ) )
125            {
126                return false;
127            }
128            WikiPermission p = (WikiPermission) obj;
129            return  p.m_mask == m_mask && p.m_wiki != null && p.m_wiki.equals( m_wiki );
130        }
131    
132        /**
133         * Returns the actions for this permission: "createGroups", "createPages",
134         * "editPreferences", "editProfile", or "login". The actions
135         * will always be sorted in alphabetic order, and will always appear in
136         * lower case.
137         * @return the actions
138         * @see java.security.Permission#getActions()
139         */
140        public String getActions()
141        {
142            return m_actionString;
143        }
144    
145        /**
146         * Returns the name of the wiki containing the page represented by this
147         * permission; may return the wildcard string.
148         * @return the wiki
149         */
150        public String getWiki()
151        {
152            return m_wiki;
153        }
154    
155        /**
156         * Returns the hash code for this WikiPermission.
157         * @return {@inheritDoc}
158         */
159        public int hashCode()
160        {
161            return m_mask + ( ( 13 * m_actionString.hashCode() ) * 23 * m_wiki.hashCode() );
162        }
163    
164        /**
165         * WikiPermission can only imply other WikiPermissions; no other permission
166         * types are implied. One WikiPermission implies another if all of the other
167         * WikiPermission's actions are equal to, or a subset of, those for this
168         * permission.
169         * @param permission the permission which may (or may not) be implied by
170         * this instance
171         * @return <code>true</code> if the permission is implied,
172         * <code>false</code> otherwise
173         * @see java.security.Permission#implies(java.security.Permission)
174         */
175        public boolean implies( Permission permission )
176        {
177            // Permission must be a WikiPermission
178            if ( !( permission instanceof WikiPermission ) )
179            {
180                return false;
181            }
182            WikiPermission p = (WikiPermission) permission;
183    
184            // See if the wiki is implied
185            boolean impliedWiki = PagePermission.isSubset( m_wiki, p.m_wiki );
186    
187            // Build up an "implied mask" for actions
188            int impliedMask = impliedMask( m_mask );
189    
190            // If actions aren't a proper subset, return false
191            return impliedWiki && ( impliedMask & p.m_mask ) == p.m_mask;
192        }
193    
194        /**
195         * Returns a new {@link AllPermissionCollection}.
196         * @return {@inheritDoc}
197         */
198        public PermissionCollection newPermissionCollection()
199        {
200            return new AllPermissionCollection();
201        }
202    
203        /**
204         * Prints a human-readable representation of this permission.
205         * @return {@inheritDoc}
206         */
207        public String toString()
208        {
209            return "(\"" + this.getClass().getName() + "\",\"" + m_wiki + "\",\"" + getActions() + "\")";
210        }
211    
212        /**
213         * Creates an "implied mask" based on the actions originally assigned: for
214         * example, <code>createGroups</code> implies <code>createPages</code>.
215         * @param mask the initial mask
216         * @return the implied mask
217         */
218        protected static int impliedMask( int mask )
219        {
220            if ( ( mask & CREATE_GROUPS_MASK ) > 0 )
221            {
222                mask |= CREATE_PAGES_MASK;
223            }
224            return mask;
225        }
226    
227        /**
228         * Private method that creates a binary mask based on the actions specified.
229         * This is used by {@link #implies(Permission)}.
230         * @param actions the permission actions, separated by commas
231         * @return binary mask representing the permissions
232         */
233        protected static int createMask( String actions )
234        {
235            if ( actions == null || actions.length() == 0 )
236            {
237                throw new IllegalArgumentException( "Actions cannot be blank or null" );
238            }
239            int mask = 0;
240            String[] actionList = actions.split( "," );
241            for( int i = 0; i < actionList.length; i++ )
242            {
243                String action = actionList[i];
244                if ( action.equalsIgnoreCase( CREATE_GROUPS_ACTION ) )
245                {
246                    mask |= CREATE_GROUPS_MASK;
247                }
248                else if ( action.equalsIgnoreCase( CREATE_PAGES_ACTION ) )
249                {
250                    mask |= CREATE_PAGES_MASK;
251                }
252                else if ( action.equalsIgnoreCase( LOGIN_ACTION ) )
253                {
254                    mask |= LOGIN_MASK;
255                }
256                else if ( action.equalsIgnoreCase( EDIT_PREFERENCES_ACTION ) )
257                {
258                    mask |= EDIT_PREFERENCES_MASK;
259                }
260                else if ( action.equalsIgnoreCase( EDIT_PROFILE_ACTION ) )
261                {
262                    mask |= EDIT_PROFILE_MASK;
263                }
264                else
265                {
266                    throw new IllegalArgumentException( "Unrecognized action: " + action );
267                }
268            }
269            return mask;
270        }
271    }