    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.wiki.auth.permissions;
    020
    021import java.io.Serializable;
    022import java.security.Permission;
    023import java.security.PermissionCollection;
    024import 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 */
    038public 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    static final int         CREATE_GROUPS_MASK      = 0x1;
    061
    062    static final int         CREATE_PAGES_MASK       = 0x2;
    063
    064    static final int         EDIT_PREFERENCES_MASK   = 0x4;
    065
    066    static final int         EDIT_PROFILE_MASK       = 0x8;
    067
    068    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(final String wiki, final String actions )
    097    {
    098        super( wiki );
    099        final String[] pageActions = actions.toLowerCase().split( "," );
    100        Arrays.sort( pageActions, String.CASE_INSENSITIVE_ORDER );
    101        m_mask = createMask( actions );
    102        final StringBuilder buffer = new StringBuilder();
    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(final Object obj )
    123    {
    124        if ( !( obj instanceof WikiPermission ) )
    125        {
    126            return false;
    127        }
    128        final 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    @Override
    141    public String getActions()
    142    {
    143        return m_actionString;
    144    }
    145
    146    /**
    147     * Returns the name of the wiki containing the page represented by this
    148     * permission; may return the wildcard string.
    149     * @return the wiki
    150     */
    151    public String getWiki()
    152    {
    153        return m_wiki;
    154    }
    155
    156    /**
    157     * Returns the hash code for this WikiPermission.
    158     * @return {@inheritDoc}
    159     */
    160    public int hashCode()
    161    {
    162        return m_mask + ( ( 13 * m_actionString.hashCode() ) * 23 * m_wiki.hashCode() );
    163    }
    164
    165    /**
    166     * WikiPermission can only imply other WikiPermissions; no other permission
    167     * types are implied. One WikiPermission implies another if all of the other
    168     * WikiPermission's actions are equal to, or a subset of, those for this
    169     * permission.
    170     * @param permission the permission which may (or may not) be implied by
    171     * this instance
    172     * @return <code>true</code> if the permission is implied,
    173     * <code>false</code> otherwise
    174     * @see java.security.Permission#implies(java.security.Permission)
    175     */
    176    @Override
    177    public boolean implies(final Permission permission )
    178    {
    179        // Permission must be a WikiPermission
    180        if ( !( permission instanceof WikiPermission ) )
    181        {
    182            return false;
    183        }
    184        final WikiPermission p = (WikiPermission) permission;
    185
    186        // See if the wiki is implied
    187        final boolean impliedWiki = PagePermission.isSubset( m_wiki, p.m_wiki );
    188
    189        // Build up an "implied mask" for actions
    190        final int impliedMask = impliedMask( m_mask );
    191
    192        // If actions aren't a proper subset, return false
    193        return impliedWiki && ( impliedMask & p.m_mask ) == p.m_mask;
    194    }
    195
    196    /**
    197     * Returns a new {@link AllPermissionCollection}.
    198     * @return {@inheritDoc}
    199     */
    200    @Override
    201    public PermissionCollection newPermissionCollection()
    202    {
    203        return new AllPermissionCollection();
    204    }
    205
    206    /**
    207     * Prints a human-readable representation of this permission.
    208     * @return {@inheritDoc}
    209     */
    210    public String toString()
    211    {
    212        return "(\"" + this.getClass().getName() + "\",\"" + m_wiki + "\",\"" + getActions() + "\")";
    213    }
    214
    215    /**
    216     * Creates an "implied mask" based on the actions originally assigned: for
    217     * example, <code>createGroups</code> implies <code>createPages</code>.
    218     * @param mask the initial mask
    219     * @return the implied mask
    220     */
    221    static int impliedMask( int mask )
    222    {
    223        if ( ( mask & CREATE_GROUPS_MASK ) > 0 )
    224        {
    225            mask |= CREATE_PAGES_MASK;
    226        }
    227        return mask;
    228    }
    229
    230    /**
    231     * Private method that creates a binary mask based on the actions specified.
    232     * This is used by {@link #implies(Permission)}.
    233     * @param actions the permission actions, separated by commas
    234     * @return binary mask representing the permissions
    235     */
    236    static int createMask(final String actions )
    237    {
    238        if ( actions == null || actions.isEmpty() )
    239        {
    240            throw new IllegalArgumentException( "Actions cannot be blank or null" );
    241        }
    242        int mask = 0;
    243        final String[] actionList = actions.split( "," );
    244        for (final String action : actionList) {
    245            if (action.equalsIgnoreCase(CREATE_GROUPS_ACTION)) {
    246                mask |= CREATE_GROUPS_MASK;
    247            } else if (action.equalsIgnoreCase(CREATE_PAGES_ACTION)) {
    248                mask |= CREATE_PAGES_MASK;
    249            } else if (action.equalsIgnoreCase(LOGIN_ACTION)) {
    250                mask |= LOGIN_MASK;
    251            } else if (action.equalsIgnoreCase(EDIT_PREFERENCES_ACTION)) {
    252                mask |= EDIT_PREFERENCES_MASK;
    253            } else if (action.equalsIgnoreCase(EDIT_PROFILE_ACTION)) {
    254                mask |= EDIT_PROFILE_MASK;
    255            } else {
    256                throw new IllegalArgumentException("Unrecognized action: " + action);
    257            }
    258        }
    259        return mask;
    260    }
    261}