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