]> granicus.if.org Git - apache/commitdiff
Allow other modules to become providers and add ACLs
authorGraham Leggett <minfrin@apache.org>
Mon, 13 Jun 2016 22:33:35 +0000 (22:33 +0000)
committerGraham Leggett <minfrin@apache.org>
Mon, 13 Jun 2016 22:33:35 +0000 (22:33 +0000)
to the DAV response. Requires apr-util v1.6+.

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

CHANGES
include/ap_mmn.h
modules/dav/main/mod_dav.c
modules/dav/main/mod_dav.h
modules/dav/main/props.c
modules/dav/main/providers.c

diff --git a/CHANGES b/CHANGES
index 0e3a6ffadf17ab36a7c3a89408059471e0cdbcca..9999c76569d56d2dab35a672b75f70e0e297f773 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,10 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.5.0
 
+  *) mod_dav: Allow other modules to become providers and add ACLs
+     to the DAV response.
+     [Jari Urpalainen <jari.urpalainen nokia.com>, Graham Leggett]
+
   *) mod_dav: Add dav_begin_multistatus, dav_send_one_response,
      dav_finish_multistatus, dav_send_multistatus, dav_handle_err,
      dav_failed_proppatch, dav_success_proppatch to mod_dav.h.
index e6d23d37fac1953b0dd039dcd2697be0fb8f3b9a..c4c4188b01d179718aee237cb4697a9d33b2c87f 100644 (file)
  *                         dav_finish_multistatus, dav_send_multistatus,
  *                         dav_handle_err, dav_failed_proppatch,
  *                         dav_success_proppatch.
+ * 20160608.4 (2.5.0-dev)  Add dav_acl_provider, dav_acl_provider_register
+ *                         dav_get_acl_providers.
  */
 
 #define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */
 #ifndef MODULE_MAGIC_NUMBER_MAJOR
 #define MODULE_MAGIC_NUMBER_MAJOR 20160608
 #endif
-#define MODULE_MAGIC_NUMBER_MINOR 3                 /* 0...n */
+#define MODULE_MAGIC_NUMBER_MINOR 4                 /* 0...n */
 
 /**
  * Determine if the server's current MODULE_MAGIC_NUMBER is at least a
index a534b9ffe146ff58b945d76fe644c074906bd30d..f5e94be4047ad189a7a6161d2ab2657befe2f5b4 100644 (file)
@@ -770,6 +770,14 @@ static dav_error *dav_get_resource(request_rec *r, int label_allowed,
      * add it now */
     dav_add_vary_header(r, r, *res_p);
 
+#ifdef APR_XML_X2T_PARSED
+    /* if acls checking -> check if allowed method excluding propfind */
+    if (((*res_p)->acls = dav_get_acl_providers()) &&
+        (err = (*res_p)->acls->acl_check_method(r, *res_p))) {
+        return err;
+    }
+#endif
+
     return NULL;
 }
 
@@ -926,6 +934,7 @@ static int dav_method_put(request_rec *r)
     int has_range;
     apr_off_t range_start;
     apr_off_t range_end;
+    int rc;
 
     /* Ask repository module to resolve the resource */
     err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
@@ -1151,7 +1160,16 @@ static int dav_method_put(request_rec *r)
     /* NOTE: WebDAV spec, S8.7.1 states properties should be unaffected */
 
     /* return an appropriate response (HTTP_CREATED or HTTP_NO_CONTENT) */
-    return dav_created(r, NULL, "Resource", resource_state == DAV_RESOURCE_EXISTS);
+    rc = dav_created(r, NULL, "Resource", resource_state == DAV_RESOURCE_EXISTS);
+
+#ifdef APR_XML_X2T_PARSED
+    if (resource->acls) {
+        resource->acls->acl_post_processing(r, resource,
+                                                 r->status == HTTP_CREATED);
+    }
+#endif
+
+    return rc;
 }
 
 
@@ -1300,6 +1318,12 @@ static int dav_method_delete(request_rec *r)
         dav_log_err(r, err, APLOG_WARNING);
     }
 
+#ifdef APR_XML_X2T_PARSED
+    if (resource->acls) {
+        resource->acls->acl_post_processing(r, resource, FALSE);
+    }
+#endif
+
     /* ### HTTP_NO_CONTENT if no body, HTTP_OK if there is a body (some day) */
 
     /* Apache will supply a default error for this. */
@@ -1982,6 +2006,18 @@ static dav_error * dav_propfind_walker(dav_walk_resource *wres, int calltype)
     dav_error *err;
     dav_propdb *propdb;
     dav_get_props_result propstats = { 0 };
+#ifdef APR_XML_X2T_PARSED
+    dav_resource *resource = (dav_resource *)wres->resource;
+
+    /* propfind skipped if no read privilege to a resource
+    ** setting acls from parent resource
+    */
+    resource->acls = ctx->w.root->acls;
+    if (resource->acls &&
+         (err = resource->acls->acl_check_read(ctx->r, resource))) {
+        return NULL;
+    }
+#endif
 
     /*
     ** Note: ctx->doc can only be NULL for DAV_PROPFIND_IS_ALLPROP. Since
@@ -2461,6 +2497,12 @@ static int dav_method_proppatch(request_rec *r)
 
     dav_send_multistatus(r, HTTP_MULTI_STATUS, &resp, doc->namespaces);
 
+#ifdef APR_XML_X2T_PARSED
+    if (resource->acls) {
+        resource->acls->acl_post_processing(r, resource, FALSE);
+    }
+#endif
+
     /* the response has been sent. */
     return DONE;
 }
@@ -2531,6 +2573,7 @@ static int dav_method_mkcol(request_rec *r)
     dav_error *err;
     dav_error *err2;
     int result;
+    int rc;
     dav_response *multi_status;
 
     /* handle the request body */
@@ -2634,7 +2677,15 @@ static int dav_method_mkcol(request_rec *r)
     }
 
     /* return an appropriate response (HTTP_CREATED) */
-    return dav_created(r, NULL, "Collection", 0);
+    rc = dav_created(r, NULL, "Collection", 0);
+
+#ifdef APR_XML_X2T_PARSED
+    if (resource->acls) {
+        resource->acls->acl_post_processing(r, resource, r->status == 201);
+    }
+#endif
+
+    return rc;
 }
 
 /* handle the COPY and MOVE methods */
@@ -2658,6 +2709,7 @@ static int dav_method_copymove(request_rec *r, int is_move)
     dav_lockdb *lockdb;
     int replace_dest;
     int resnew_state;
+    int rc;
 
     /* Ask repository module to resolve the resource */
     err = dav_get_resource(r, !is_move /* label_allowed */,
@@ -3039,8 +3091,19 @@ static int dav_method_copymove(request_rec *r, int is_move)
     }
 
     /* return an appropriate response (HTTP_CREATED or HTTP_NO_CONTENT) */
-    return dav_created(r, lookup.rnew->unparsed_uri, "Destination",
-                       resnew_state == DAV_RESOURCE_EXISTS);
+    rc = dav_created(r, lookup.rnew->uri, "Destination",
+                         resnew_state == DAV_RESOURCE_EXISTS);
+
+#ifdef APR_XML_X2T_PARSED
+    if (resource->acls) {
+        resource->acls->acl_post_processing(r, resource, FALSE);
+
+        resource->acls->acl_post_processing(r, resnew,
+                                                 r->status == HTTP_CREATED);
+    }
+#endif
+
+    return rc;
 }
 
 /* dav_method_lock:  Handler to implement the DAV LOCK method
index e354e8b44bacaf22385f656fdedc194c9ebfa809..cab65782041fe4da22ebce5ed8cb9dcbdfc427ec 100644 (file)
@@ -319,6 +319,10 @@ typedef enum {
 */
 typedef struct dav_resource_private dav_resource_private;
 
+#ifdef APR_XML_X2T_PARSED
+typedef struct dav_acl_provider dav_acl_provider;
+#endif
+
 /*
 ** Resource descriptor, generated by a repository provider.
 **
@@ -418,6 +422,12 @@ typedef struct dav_resource {
        long as the dav_resource structure. */
     apr_pool_t *pool;
 
+#ifdef APR_XML_X2T_PARSED
+    const dav_acl_provider *acls; /* acls used for this resource */
+#endif
+
+    void *ctx;  /* additional parameter */
+
 } dav_resource;
 
 /*
@@ -2498,6 +2508,39 @@ typedef struct {
     const dav_hooks_liveprop *provider;  /* the provider defining this prop */
 } dav_elem_private;
 
+
+/* --------------------------------------------------------------------
+**
+** DAV ACL HOOKS
+*/
+#ifdef APR_XML_X2T_PARSED
+
+typedef struct dav_acl_provider
+{
+    dav_error * (*acl_check_method)(request_rec *r,
+                                    const dav_resource *resource);
+
+    dav_error * (*acl_check_read)(request_rec *r,
+                                  const dav_resource *resource);
+
+    dav_error * (*acl_check_prop)(request_rec *r,
+                                  const dav_resource *resource,
+                                  const dav_prop_name *name,
+                                  dav_prop_insert what);
+
+    void (*acl_post_processing)(request_rec *r,
+                                const dav_resource *resource,
+                                int new_resource_created);
+    void *ctx;
+} dav_acl_provider;
+
+DAV_DECLARE(void) dav_acl_provider_register(apr_pool_t *p,
+                                            const dav_acl_provider *acl);
+
+DAV_DECLARE(const dav_acl_provider *) dav_get_acl_providers();
+
+#endif
+
 /* --------------------------------------------------------------------
 **
 ** DAV OPTIONS
index 47cc509bd470c0963138a28da56ca25ac8c656cc..792c87db8be06d69ef75ed76f050d1469fd89265 100644 (file)
@@ -578,6 +578,9 @@ DAV_DECLARE(dav_get_props_result) dav_get_allprops(dav_propdb *propdb,
     int found_contenttype = 0;
     int found_contentlang = 0;
     dav_prop_insert unused_inserted;
+#ifdef APR_XML_X2T_PARSED
+    apr_text *text;
+#endif
 
     /* if not just getting supported live properties,
      * scan all properties in the dead prop database
@@ -622,6 +625,17 @@ DAV_DECLARE(dav_get_props_result) dav_get_allprops(dav_propdb *propdb,
                     }
                 }
 
+#ifdef APR_XML_X2T_PARSED
+                /* check for possible special acl restrictions not provided by read privilege */
+                if (propdb->resource->acls &&
+                    propdb->resource->acls->acl_check_prop &&
+                    propdb->resource->acls->
+                        acl_check_prop(propdb->r, propdb->resource,
+                                       &name, what)) {
+                    goto next_key;
+                }
+#endif
+
                 if (what == DAV_PROP_INSERT_VALUE) {
                     int found;
 
@@ -653,6 +667,9 @@ DAV_DECLARE(dav_get_props_result) dav_get_allprops(dav_propdb *propdb,
         /* add namespaces for all the liveprop providers */
         dav_add_all_liveprop_xmlns(propdb->p, &hdr_ns);
     }
+#ifdef APR_XML_X2T_PARSED
+    text = hdr.last;
+#endif
 
     /* ask the liveprop providers to insert their properties */
     dav_run_insert_all_liveprops(propdb->r, propdb->resource, what, &hdr);
@@ -682,6 +699,62 @@ DAV_DECLARE(dav_get_props_result) dav_get_allprops(dav_propdb *propdb,
                                   what, &hdr, &unused_inserted);
     }
 
+#ifdef APR_XML_X2T_PARSED
+    if (propdb->resource->acls &&
+        propdb->resource->acls->acl_check_prop) {
+
+        /* unfortunately liveprops cannot be checked beforehand */
+        apr_xml_doc *doc = NULL;
+        apr_text *t;
+        apr_xml_elem *elem;
+        apr_pool_t *pool;
+        apr_xml_parser *parser;
+
+        apr_pool_create (&pool, propdb->p);
+        parser = apr_xml_parser_create (pool);
+
+        apr_xml_parser_feed(parser, "<r xmlns:D=\"DAV:\" ", 18);
+        for (t = hdr_ns.first; t; t = t->next) {
+            if (t->text)
+                apr_xml_parser_feed(parser, t->text, strlen(t->text));
+        }
+        apr_xml_parser_feed(parser, ">", 1);
+
+        hdr.last = text ? text : hdr.first;
+
+        for (text = text ? text->next : hdr.first; text; text = text->next) {
+            if (text->text)
+                apr_xml_parser_feed(parser, text->text, strlen(text->text));
+        }
+        apr_xml_parser_feed(parser, "</r>", 4);
+
+        apr_xml_parser_done(parser, &doc);
+        for (elem = doc ? doc->root->first_child : NULL; elem;
+             elem = elem->next) {
+             dav_prop_name name[1];
+
+             name->ns = elem->ns == APR_XML_NS_NONE ? "" :
+                 APR_XML_GET_URI_ITEM(doc->namespaces, elem->ns);
+             name->name = elem->name;
+
+            if (propdb->resource->acls->
+                    acl_check_prop(propdb->r, propdb->resource,
+                                   name, what) == NULL) {
+                const char *buf;
+
+                /* APR_XML_X2T_FULL_NS_LANG mangles original namespace prefixes */
+                apr_xml_to_text(pool, elem, APR_XML_X2T_PARSED,
+                                NULL, NULL, &buf, NULL);
+
+                buf = apr_pstrcat (pool, buf, DEBUG_CR, NULL);
+                apr_text_append(propdb->p, &hdr, buf);
+            }
+        }
+        apr_pool_destroy(pool);
+    }
+#endif
+
     /* if not just reporting on supported live props,
      * terminate the result */
     if (what != DAV_PROP_INSERT_SUPPORTED) {
@@ -703,6 +776,9 @@ DAV_DECLARE(dav_get_props_result) dav_get_props(dav_propdb *propdb,
     apr_xml_elem *elem = dav_find_child(doc->root, "prop");
     apr_text_header hdr_good = { 0 };
     apr_text_header hdr_bad = { 0 };
+#ifdef APR_XML_X2T_PARSED
+    apr_text_header hdr_not_auth = { 0 };
+#endif
     apr_text_header hdr_ns = { 0 };
     int have_good = 0;
     dav_get_props_result result = { 0 };
@@ -744,11 +820,40 @@ DAV_DECLARE(dav_get_props_result) dav_get_props(dav_propdb *propdb,
         priv = elem->priv;
 
         /* cache the propid; dav_get_props() could be called many times */
-        if (priv->propid == 0)
+        if (priv->propid == 0) {
             dav_find_liveprop(propdb, elem);
+        }
 
         if (priv->propid != DAV_PROPID_CORE_UNKNOWN) {
 
+#ifdef APR_XML_X2T_PARSED
+            /* check for possible special acl restrictions not provided by read privilege
+             * ask for each live prop separately (e.g. read-acl privilege) */
+            if (propdb->resource->acls &&
+                propdb->resource->acls->acl_check_prop) {
+                name.ns = elem->ns == APR_XML_NS_NONE ? "" :
+                          APR_XML_GET_URI_ITEM(propdb->ns_xlate, elem->ns);
+                name.name = elem->name;
+
+                if (propdb->resource->acls->
+                        acl_check_prop(propdb->r, propdb->resource,
+                                       &name, DAV_PROP_INSERT_VALUE)) {
+
+                    if (hdr_not_auth.first == NULL) {
+                        apr_text_append(propdb->p, &hdr_not_auth,
+                                        "<D:propstat>" DEBUG_CR
+                                        "<D:prop>" DEBUG_CR);
+                    }
+
+                    if (!name.ns) {
+                        name.ns = "";
+                    }
+                    dav_output_prop_name(propdb->p, &name, xi, &hdr_not_auth);
+                    continue;
+                }
+            }
+#endif
+
             /* insert the property. returns 1 if an insertion was done. */
             if ((err = dav_insert_liveprop(propdb, elem, DAV_PROP_INSERT_VALUE,
                                            &hdr_good, &inserted)) != NULL) {
@@ -878,6 +983,25 @@ DAV_DECLARE(dav_get_props_result) dav_get_props(dav_propdb *propdb,
         }
     }
 
+#ifdef APR_XML_X2T_PARSED
+    if (hdr_not_auth.first != NULL) {
+        apr_text_append(propdb->p, &hdr_not_auth,
+                        "</D:prop>" DEBUG_CR
+                        "<D:status>HTTP/1.1 403 Forbidden</D:status>" DEBUG_CR
+                        "</D:propstat>" DEBUG_CR);
+
+        if (!have_good && !hdr_bad.first) {
+            result.propstats = hdr_not_auth.first;
+        }
+        else if (hdr_bad.first != NULL) {
+            hdr_bad.last->next = hdr_not_auth.first;
+        }
+        else {
+            hdr_good.last->next = hdr_not_auth.first;
+        }
+    }
+#endif
+
     /* add in all the various namespaces, and return them */
     dav_xmlns_generate(xi, &hdr_ns);
     result.xmlns = hdr_ns.first;
index 44870fd01b50fd47a06f359de35cd38e01d9dee1..e1e07d45bdd9868ed986f6c4aa3e895913b4a868 100644 (file)
@@ -32,6 +32,19 @@ DAV_DECLARE(const dav_provider *) dav_lookup_provider(const char *name)
     return ap_lookup_provider(DAV_PROVIDER_GROUP, name, "0");
 }
 
+#ifdef APR_XML_X2T_PARSED
+DAV_DECLARE(void) dav_acl_provider_register(apr_pool_t *p,
+                                            const dav_acl_provider *provider)
+{
+    ap_register_provider(p, DAV_PROVIDER_GROUP, "acl", "0", provider);
+}
+
+DAV_DECLARE(const dav_acl_provider *) dav_get_acl_providers()
+{
+    return ap_lookup_provider(DAV_PROVIDER_GROUP, "acl", "0");
+}
+#endif
+
 DAV_DECLARE(void) dav_options_provider_register(apr_pool_t *p,
                         const char *name,
                         const dav_options_provider *provider)