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 020 package org.apache.wiki.plugin; 021 022 import java.security.Principal; 023 import java.util.Map; 024 025 import org.apache.commons.lang.StringUtils; 026 import org.apache.oro.text.regex.*; 027 import org.apache.wiki.WikiContext; 028 import org.apache.wiki.WikiProvider; 029 import org.apache.wiki.api.exceptions.PluginException; 030 import org.apache.wiki.api.plugin.WikiPlugin; 031 import org.apache.wiki.util.HttpUtil; 032 import org.apache.wiki.util.TextUtil; 033 034 /** 035 * The IfPlugin allows parts of a WikiPage to be executed conditionally, and is intended as a flexible way 036 * of customizing a page depending on certain conditions. Do not use it as a security mechanism to conditionally 037 * hide content from users (use page ACLs for that). 038 * 039 * You can also use shorthand "If" to run it. 040 * 041 * Parameters: 042 * <ul> 043 * <li><b>group</b> - A "|" -separated list of group names. 044 * <li><b>user</b> - A "|" -separated list of user names. 045 * <li><b>ip</b> - A "|" -separated list of ip addresses. 046 * <li><b>var</b> - A wiki variable 047 * <li><b>page</b> - A page name 048 * <li><b>contains</b> - A Perl5 regexp pattern 049 * <li><b>is</b> - A Perl5 regexp pattern 050 * <li><b>exists</b> - "true" or "false". 051 * </ul> 052 * 053 * <p>If any of them match, the body of the plugin is executed. You can 054 * negate the content by prefixing it with a "!". For example, to greet 055 * all admins, put the following in your LeftMenu:</p> 056 * <pre> 057 * [{If group='Admin' 058 * 059 * Hello, Admin, and your mighty powers!}] 060 * </pre> 061 * 062 * <p>In order to send a message to everybody except Jack use</p> 063 * <pre> 064 * [{If user='!Jack' 065 * 066 * %%warning 067 * Jack's surprise birthday party at eleven! 068 * %%}] 069 * </pre> 070 * 071 * <p>Note that you can't use "!Jack|!Jill", because for Jack, !Jill matches; 072 * and for Jill, !Jack matches. These are not regular expressions (though 073 * they might become so in the future).<p> 074 * 075 * <p>To check for page content, use</p> 076 * <pre> 077 * [{If page='TestPage' contains='xyzzy' 078 * 079 * Page contains the text "xyzzy"}] 080 * </pre> 081 * 082 * <p>The difference between "contains" and "is" is that "is" is always an exact match, 083 * whereas "contains" just checks if a pattern is available.</p> 084 * 085 * <p>To check for page existence, use</p> 086 * <pre> 087 * [{If page='TestPage' exists='true' 088 * 089 * Page "TestPage" exists.}] 090 * </pre> 091 * <p>With the same mechanism, it's also possible to test for the existence 092 * of a variable - just use "var" instead of "page".</p> 093 * 094 * <p>Another caveat is that the plugin body content is not counted 095 * towards ReferenceManager links. So any links do not appear on any reference 096 * lists. Depending on your position, this may be a good or a bad 097 * thing.</p> 098 * 099 * <h3>Calling Externally</h3> 100 * 101 * <p>The functional, decision-making part of this plugin may be called from 102 * other code (e.g., other plugins) since it is available as a static method 103 * {@link #ifInclude(WikiContext,Map)}. Note that the plugin body may contain 104 * references to other plugins.</p> 105 * 106 * @since 2.6 107 */ 108 public class IfPlugin implements WikiPlugin 109 { 110 /** The parameter name for setting the group to check. Value is <tt>{@value}</tt>. */ 111 public static final String PARAM_GROUP = "group"; 112 113 /** The parameter name for setting the user id to check. Value is <tt>{@value}</tt>. */ 114 public static final String PARAM_USER = "user"; 115 116 /** The parameter name for setting the ip address to check. Value is <tt>{@value}</tt>. */ 117 public static final String PARAM_IP = "ip"; 118 119 /** The parameter name for setting the page name to check. Value is <tt>{@value}</tt>. */ 120 public static final String PARAM_PAGE = "page"; 121 122 /** The parameter name for setting the contents of the page to check. Value is <tt>{@value}</tt>. */ 123 public static final String PARAM_CONTAINS = "contains"; 124 125 /** The parameter name for setting the variable name to check. Value is <tt>{@value}</tt>. */ 126 public static final String PARAM_VAR = "var"; 127 128 /** The parameter name for setting the exact content to check. Value is <tt>{@value}</tt>. */ 129 public static final String PARAM_IS = "is"; 130 131 /** The parameter name for checking whether a page/var exists. Value is <tt>{@value}</tt>. */ 132 public static final String PARAM_EXISTS = "exists"; 133 134 /** 135 * {@inheritDoc} 136 */ 137 public String execute(WikiContext context, Map<String, String> params) throws PluginException 138 { 139 return ifInclude( context,params ) 140 ? context.getEngine().textToHTML( context, params.get( DefaultPluginManager.PARAM_BODY ) ) 141 : "" ; 142 } 143 144 145 /** 146 * Returns a boolean result based on processing the WikiContext and 147 * parameter Map as according to the rules stated in the IfPlugin 148 * documentation. 149 * As a static method this may be called by other classes. 150 * 151 * @param context The current WikiContext. 152 * @param params The parameter Map which contains key-value pairs. 153 * @throws PluginException If something goes wrong 154 * @return True, if the condition holds. 155 */ 156 public static boolean ifInclude( WikiContext context, Map<String, String> params ) throws PluginException 157 { 158 boolean include = false; 159 160 String group = params.get( PARAM_GROUP ); 161 String user = params.get( PARAM_USER ); 162 String ip = params.get( PARAM_IP ); 163 String page = params.get( PARAM_PAGE ); 164 String contains = params.get( PARAM_CONTAINS ); 165 String var = params.get( PARAM_VAR ); 166 String is = params.get( PARAM_IS ); 167 String exists = params.get( PARAM_EXISTS ); 168 169 include |= checkGroup(context, group); 170 include |= checkUser(context, user); 171 include |= checkIP(context, ip); 172 173 if( page != null ) 174 { 175 String content = context.getEngine().getPureText(page, WikiProvider.LATEST_VERSION).trim(); 176 include |= checkContains(content,contains); 177 include |= checkIs(content,is); 178 include |= checkExists(context,page,exists); 179 } 180 181 if( var != null ) 182 { 183 String content = context.getEngine().getVariable(context, var); 184 include |= checkContains(content,contains); 185 include |= checkIs(content,is); 186 include |= checkVarExists(content,exists); 187 } 188 189 return include; 190 } 191 192 private static boolean checkExists( WikiContext context, String page, String exists ) 193 { 194 if( exists == null ) return false; 195 return !context.getEngine().pageExists(page) ^ TextUtil.isPositive(exists); 196 } 197 198 private static boolean checkVarExists( String varContent, String exists ) 199 { 200 if( exists == null ) return false; 201 return (varContent == null ) ^ TextUtil.isPositive(exists); 202 } 203 204 private static boolean checkGroup( WikiContext context, String group ) 205 { 206 if( group == null ) return false; 207 String[] groupList = StringUtils.split(group,'|'); 208 boolean include = false; 209 210 for( int i = 0; i < groupList.length; i++ ) 211 { 212 String gname = groupList[i]; 213 boolean invert = false; 214 if( groupList[i].startsWith("!") ) 215 { 216 if( groupList[i].length() > 1 ) 217 { 218 gname = groupList[i].substring( 1 ); 219 } 220 invert = true; 221 } 222 223 Principal g = context.getEngine().getAuthorizationManager().resolvePrincipal(gname); 224 225 include |= context.getEngine().getAuthorizationManager().isUserInRole( context.getWikiSession(), g ) ^ invert; 226 } 227 return include; 228 } 229 230 private static boolean checkUser( WikiContext context, String user ) 231 { 232 if( user == null || context.getCurrentUser() == null ) return false; 233 234 String[] list = StringUtils.split(user,'|'); 235 boolean include = false; 236 237 for( int i = 0; i < list.length; i++ ) 238 { 239 String userToCheck = list[i]; 240 boolean invert = false; 241 if( list[i].startsWith("!") ) 242 { 243 invert = true; 244 // strip ! 245 if( user.length() > 1 ) 246 { 247 userToCheck = list[i].substring( 1 ); 248 } 249 } 250 251 include |= userToCheck.equals( context.getCurrentUser().getName() ) ^ invert; 252 } 253 return include; 254 } 255 256 // TODO: Add subnetwork matching, e.g. 10.0.0.0/8 257 private static boolean checkIP( WikiContext context, String ipaddr ) 258 { 259 if( ipaddr == null || context.getHttpRequest() == null ) return false; 260 261 262 String[] list = StringUtils.split(ipaddr,'|'); 263 boolean include = false; 264 265 for( int i = 0; i < list.length; i++ ) 266 { 267 String ipaddrToCheck = list[i]; 268 boolean invert = false; 269 if( list[i].startsWith("!") ) 270 { 271 invert = true; 272 // strip ! 273 if( list[i].length() > 1 ) 274 { 275 ipaddrToCheck = list[i].substring( 1 ); 276 } 277 } 278 279 include |= ipaddrToCheck.equals( HttpUtil.getRemoteAddress(context.getHttpRequest()) ) ^ invert; 280 } 281 return include; 282 } 283 284 private static boolean doMatch( String content, String pattern ) 285 throws PluginException 286 { 287 PatternCompiler compiler = new Perl5Compiler(); 288 PatternMatcher matcher = new Perl5Matcher(); 289 290 try 291 { 292 Pattern matchp = compiler.compile( pattern, Perl5Compiler.SINGLELINE_MASK ); 293 // m_exceptPattern = compiler.compile( exceptPattern, Perl5Compiler.SINGLELINE_MASK ); 294 return matcher.matches( content, matchp ); 295 } 296 catch( MalformedPatternException e ) 297 { 298 throw new PluginException("Faulty pattern "+pattern); 299 } 300 301 } 302 303 private static boolean checkContains( String pagecontent, String matchPattern ) 304 throws PluginException 305 { 306 if( pagecontent == null || matchPattern == null ) return false; 307 308 return doMatch( pagecontent, ".*"+matchPattern+".*" ); 309 } 310 311 private static boolean checkIs( String content, String matchPattern ) 312 throws PluginException 313 { 314 if( content == null || matchPattern == null ) return false; 315 316 matchPattern = "^"+matchPattern+"$"; 317 318 return doMatch(content, matchPattern); 319 } 320 }