]> granicus.if.org Git - apache/commitdiff
mod_include: Add an "if" directive syntax to test whether an URL
authorGraham Leggett <minfrin@apache.org>
Sat, 1 Sep 2007 21:27:26 +0000 (21:27 +0000)
committerGraham Leggett <minfrin@apache.org>
Sat, 1 Sep 2007 21:27:26 +0000 (21:27 +0000)
is accessible, and if so, conditionally display content. This
allows a webmaster to hide a link to a private page when the user
has no access to that page.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@571872 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
docs/manual/mod/mod_include.xml
modules/filters/mod_include.c
modules/filters/mod_include.h

diff --git a/CHANGES b/CHANGES
index b6ddfb499b4bd5a66a71ee0dc3e7fe749d4ed89f..f419a79c010694923586614f08804c0820bbdfc9 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,11 @@
 Changes with Apache 2.3.0
 [ When backported to 2.2.x, remove entry from this file ]
 
+  *) mod_include: Add an "if" directive syntax to test whether an URL
+     is accessible, and if so, conditionally display content. This
+     allows a webmaster to hide a link to a private page when the user
+     has no access to that page. [Graham Leggett]
+
   *) mod_authnz_ldap, mod_authn_dbd: Tidy up the code to expose authn
      parameters to the environment. Improve portability to
      EBCDIC machines by using apr_toupper(). [Martin Kraemer]
index b821b91fd61eaf7040d15c2e9aedff67aa77c6e0..cff773dccb600a34d82863c174b33b546a3cf6b6 100644 (file)
       <dt><code><var>string</var></code></dt>
       <dd>true if <var>string</var> is not empty</dd>
 
+      <dt><code><var>-A string</var></code></dt>
+      <dd><p>true if the URL represented by the string is accessible by
+      configuration, false otherwise. This test only has an effect if
+      <directive>SSIEnableAccess</directive> is on. This is useful
+      where content on a page is to be hidden from users who are not
+      authorized to view the URL, such as a link to that URL. Note
+      that the URL is only tested for whether access would be granted,
+      not whether the URL exists.</p>
+
+      <example><title>Example</title>
+        &lt;!--#if expr="-A /private" --&gt;<br />
+        <indent>
+          Click &lt;a href="/private"&gt;here&lt;/a&gt; to access private
+          information.<br />
+        </indent>
+        &lt;!--#endif --&gt;
+      </example>
+      </dd>
+
       <dt><code><var>string1</var> = <var>string2</var><br />
       <var>string1</var> == <var>string2</var><br />
       <var>string1</var> != <var>string2</var></code></dt>
@@ -744,6 +763,34 @@ displayed</description>
 </usage>
 </directivesynopsis>
 
+<directivesynopsis>
+<name>SSIEnableAccess</name>
+<description>Enable the -A flag during conditional flow control processing.</description>
+<syntax>SSIEnableAccess on|off</syntax>
+<default>SSIEnableAccess off</default>
+<contextlist><context>directory</context><context>location</context></contextlist>
+
+<usage>
+    <p>The <directive>SSIEnableAccess</directive> directive controls whether
+    the -A test is enabled during conditional flow control processing.
+    <directive>SSIEnableAccess</directive> can take on the following values:</p>
+
+    <dl>
+
+      <dt><code>off</code></dt>
+      <dd>&lt;!--#if expr="-A /foo"--&gt; will be interpreted as a series
+      of string and regular expression tokens, the -A has no special
+      meaning.</dd>
+
+      <dt><code>on</code></dt>
+      <dd>&lt;!--#if expr="-A /foo"--&gt; will evaluate to false if the
+      URL /foo is inaccessible by configuration, or true otherwise.</dd>
+
+    </dl>
+
+</usage>
+</directivesynopsis>
+
 <directivesynopsis>
 <name>XBitHack</name>
 <description>Parse SSI directives in files with the execute bit
@@ -785,7 +832,7 @@ set</description>
       </dd>
     </dl>
 
-    </usage>
+</usage>
 </directivesynopsis>
 
 </modulesynopsis>
index 3d5c4667789a19df76840ce20921d58c61e947c8..11357b760e1d99e7aa4d9086abf062b97abb8ce0 100644 (file)
@@ -80,7 +80,8 @@ typedef enum {
     TOKEN_GE,
     TOKEN_LE,
     TOKEN_GT,
-    TOKEN_LT
+    TOKEN_LT,
+    TOKEN_ACCESS
 } token_type_t;
 
 typedef struct {
@@ -114,6 +115,7 @@ typedef struct {
     const char *default_time_fmt;
     const char *undefined_echo;
     xbithack_t  xbithack;
+    const int accessenable;
 } include_dir_config;
 
 typedef struct {
@@ -941,7 +943,7 @@ static APR_INLINE int re_check(include_ctx_t *ctx, const char *string,
     return rc;
 }
 
-static int get_ptoken(apr_pool_t *pool, const char **parse, token_t *token)
+static int get_ptoken(include_ctx_t *ctx, const char **parse, token_t *token, token_t *previous)
 {
     const char *p;
     apr_size_t shift;
@@ -990,6 +992,10 @@ static int get_ptoken(apr_pool_t *pool, const char **parse, token_t *token)
         unmatched = '\'';
         break;
     case '/':
+        /* if last token was ACCESS, this token is STRING */
+        if (previous != NULL && TOKEN_ACCESS == previous->type) {
+            break;
+        }
         TYPE_TOKEN(token, TOKEN_RE);
         unmatched = '/';
         break;
@@ -1023,6 +1029,13 @@ static int get_ptoken(apr_pool_t *pool, const char **parse, token_t *token)
         }
         TYPE_TOKEN(token, TOKEN_LT);
         return 0;
+    case '-':
+        if (**parse == 'A' && (ctx->accessenable)) {
+            TYPE_TOKEN(token, TOKEN_ACCESS);
+            ++*parse;
+            return 0;
+        }
+        break;
     }
 
     /* It's a string or regex token
@@ -1079,11 +1092,11 @@ static int get_ptoken(apr_pool_t *pool, const char **parse, token_t *token)
     }
 
     if (unmatched) {
-        token->value = apr_pstrdup(pool, "");
+        token->value = apr_pstrdup(ctx->dpool, "");
     }
     else {
         apr_size_t len = p - token->value - shift;
-        char *c = apr_palloc(pool, len + 1);
+        char *c = apr_palloc(ctx->dpool, len + 1);
 
         p = token->value;
         token->value = c;
@@ -1111,6 +1124,7 @@ static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error)
 {
     parse_node_t *new, *root = NULL, *current = NULL;
     request_rec *r = ctx->intern->r;
+    request_rec *rr = NULL;
     const char *error = "Invalid expression \"%s\" in file %s";
     const char *parse = expr;
     int was_unmatched = 0;
@@ -1130,7 +1144,8 @@ static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error)
          */
         CREATE_NODE(ctx, new);
 
-        was_unmatched = get_ptoken(ctx->dpool, &parse, &new->token);
+        was_unmatched = get_ptoken(ctx, &parse, &new->token,
+                         (current != NULL ? &current->token : NULL));
         if (!parse) {
             break;
         }
@@ -1142,6 +1157,7 @@ static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error)
             switch (new->token.type) {
             case TOKEN_STRING:
             case TOKEN_NOT:
+            case TOKEN_ACCESS:
             case TOKEN_LBRACE:
                 root = current = new;
                 continue;
@@ -1276,6 +1292,7 @@ static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error)
             break;
 
         case TOKEN_NOT:
+        case TOKEN_ACCESS:
         case TOKEN_LBRACE:
             switch (current->token.type) {
             case TOKEN_STRING:
@@ -1462,6 +1479,34 @@ static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error)
             }
             break;
 
+        case TOKEN_ACCESS:
+            if (current->left || !current->right ||
+                (current->right->token.type != TOKEN_STRING &&
+                 current->right->token.type != TOKEN_RE)) {
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                            "Invalid expression \"%s\" in file %s: Token '-A' must be followed by a URI string.",
+                            expr, r->filename);
+                *was_error = 1;
+                return 0;
+            }
+            current->right->token.value =
+                ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0,
+                                    SSI_EXPAND_DROP_NAME);
+            rr = ap_sub_req_lookup_uri(current->right->token.value, r, NULL);
+            /* 400 and higher are considered access denied */
+            if (rr->status < HTTP_BAD_REQUEST) {
+                current->value = 1;
+            }
+            else {
+                current->value = 0;
+                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rr->status, r, 
+                              "mod_include: The tested "
+                              "subrequest -A \"%s\" returned an error code.",
+                              current->right->token.value);
+            }
+            ap_destroy_sub_req(rr);
+            break;
+
         case TOKEN_RE:
             if (!error) {
                 error = "No operator before regex in expr \"%s\" in file %s";
@@ -3526,6 +3571,7 @@ static apr_status_t includes_filter(ap_filter_t *f, apr_bucket_brigade *b)
         if (ap_allow_options(r) & OPT_INCNOEXEC) {
             ctx->flags |= SSI_FLAG_NO_EXEC;
         }
+        ctx->accessenable = conf->accessenable;
 
         ctx->if_nesting_level = 0;
         intern->re = NULL;
@@ -3808,6 +3854,9 @@ static const command_rec includes_cmds[] =
                   "SSI End String Tag"),
     AP_INIT_TAKE1("SSIUndefinedEcho", set_undefined_echo, NULL, OR_ALL,
                   "String to be displayed if an echoed variable is undefined"),
+    AP_INIT_FLAG("SSIAccessEnable", ap_set_flag_slot,
+                  (void *)APR_OFFSETOF(include_dir_config, accessenable),
+                  OR_LIMIT, "Whether testing access is enabled. Limited to 'on' or 'off'"),
     {NULL}
 };
 
index fd5621c0b57e17d642823c51c1a7ce546faafa31..d357a49995a7a107134196bcd762aaf406601c90 100644 (file)
@@ -96,6 +96,9 @@ typedef struct {
 
     /* pointer to internal (non-public) data, don't touch */
     struct ssi_internal_ctx *intern;
+
+    /* is using the access tests allowed? */
+    int         accessenable;
 } include_ctx_t;
 
 typedef apr_status_t (include_handler_fn_t)(include_ctx_t *, ap_filter_t *,