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}