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 }