# HG changeset patch # User Paul Boddie # Date 1360421710 -3600 # Node ID a225b3d53c6dc9ba618a840e706ccbf2cf5034be # Parent d6234d9d208237a838b9a63a91567a7426da31e0 Separated access control logic from the form store functionality. Added notes about appropriate page ACLs and form access configuration. diff -r d6234d9d2082 -r a225b3d53c6d MoinForms.py --- a/MoinForms.py Thu Feb 07 00:15:43 2013 +0100 +++ b/MoinForms.py Sat Feb 09 15:55:10 2013 +0100 @@ -28,6 +28,10 @@ def __init__(self, pagename, request): self.pagename = pagename self.request = request + self.access_handler = None + + def getAccessHandler(self, attributes): + return FormAccess(self.pagename, self.request, attributes) def processForm(self): @@ -49,7 +53,9 @@ # Get the form definition. - self.attributes, structure = self.getFormStructure(fields) + attributes, text = self.getFormForFragment(fields) + structure = getFormStructure(text, self.request) + self.access_handler = self.getAccessHandler(attributes) # Check the permissions on the form. @@ -88,14 +94,13 @@ self.serialiseFields(fields, form) do_show(self.pagename, self.request) - def getFormStructure(self, fields): + def getFormForFragment(self, fields): - "Return the attributes and structure of the form being handled." + "Return the attributes and text of the form being handled." fragment = fields.get("fragment", [None])[0] text = Page(self.request, self.pagename).get_raw_body() - attributes, text = getFormForFragment(text, fragment) - return attributes, getFormStructure(text, self.request) + return getFormForFragment(text, fragment) def checkPermissions(self, action): @@ -104,26 +109,7 @@ the form's 'attributes'. """ - user = self.request.user - - # Use the access definition if one is given. - - if self.attributes.has_key("access"): - access = self.attributes["access"] - acl = security.AccessControlList(self.request.cfg, [access]) - policy = lambda request, pagename, username, action: acl.may(request, username, action) - - # Otherwise, use the page permissions. - - else: - policy = security._check - - # The "read" action is only satisfied by the "admin" role. - - return user and ( - action != "read" and policy(self.request, self.pagename, user.name, action) or - action == "read" and policy(self.request, self.pagename, user.name, "admin") - ) + return self.access_handler.checkPermissions(action) def validateFields(self, fields, structure): @@ -268,24 +254,46 @@ the given 'attributes'. """ - store = FormStore(self) + store = FormStore(self.access_handler) store.append(repr(fields)) def loadFields(self, number): "Load the fields associated with the given submission 'number'." - try: - module = parse(store[number]) - if checkStoredFormData(module): - return eval(module) + store = FormStore(self.access_handler) + return loadFields(store, number) + +def loadFieldsForRequest(number, attrs, request): + + """ + Load the fields corresponding to the given record 'number', using the form + 'attrs' to control access to the form data, and using the 'request' for + access and retrieval purposes. + """ + + access_handler = FormAccess(request.page.page_name, request, attrs) + store = FormStore(access_handler) + return loadFields(store, number) + +def loadFields(store, number): - # NOTE: Should indicate any errors in retrieving form data. + """ + Load the fields from the 'store' that are associated with the given + submission 'number'. + """ - except: - pass + try: + module = parse(store[number]) + if checkStoredFormData(module): + return eval(module) - return {} + # NOTE: Should indicate any errors in retrieving form data. + + except: + pass + + return {} def checkStoredFormData(node): @@ -302,6 +310,43 @@ return True +class FormAccess: + + "A means of checking access to form data." + + def __init__(self, pagename, request, attributes): + self.pagename = pagename + self.request = request + self.attributes = attributes + + def checkPermissions(self, action): + + """ + Check the permissions of the user against any restrictions specified in + the form's 'attributes'. + """ + + user = self.request.user + + # Use the access definition if one is given. + + if self.attributes.has_key("access"): + access = self.attributes["access"] + acl = security.AccessControlList(self.request.cfg, [access]) + policy = lambda request, pagename, username, action: acl.may(request, username, action) + + # Otherwise, use the page permissions. + + else: + policy = security._check + + # The "read" action is only satisfied by the "admin" role. + + return user and ( + action != "read" and policy(self.request, self.pagename, user.name, action) or + action == "read" and policy(self.request, self.pagename, user.name, "admin") + ) + class FormStore(ItemStore): "A form-specific storage mechanism." @@ -793,6 +838,8 @@ fields = getFields(get_form(request)) + # Prepare the query string for the form action URL. + queryparams = [] for argname, default in [("fragment", None), ("action", "MoinFormHandler")]: diff -r d6234d9d2082 -r a225b3d53c6d pages/HelpOnMoinForms --- a/pages/HelpOnMoinForms Thu Feb 07 00:15:43 2013 +0100 +++ b/pages/HelpOnMoinForms Sat Feb 09 15:55:10 2013 +0100 @@ -257,7 +257,7 @@ Thus, on any page for which a user only has read access, any form will by default be visible but not usable for submitting data. -However, it is possible to override these restrictions by specifying an `access` keyword which defines a different set of permissions that applies to a user when using the form. For example: +Since granting write access to a user will also permit them to change the form definition, as discussed below, it is possible to override these restrictions specifically for each form. This is done by specifying an `access` keyword which defines a different set of permissions that applies to a user when using the form. For example: {{{{ {{{#!form fragment=exampleform5 access='All:write' @@ -269,6 +269,10 @@ The `access` keyword supports the conventional [[HelpOnAccessControlLists|ACL]] syntax, and where spaces are present in the specified value, quotes should be placed around the value itself and not the `access` keyword and equals sign as well. +{{{#!wiki important +Note that in practice, any user with write access to a page can change the `access` criteria and grant themselves admin access to a form. Therefore, any use of forms where users are not generally to be trusted with the submitted data or the integrity of the form definition should be protected by a page ACL that denies write access to all but privileged users. The general users of the form can then be granted write access to it specifically. +}}} + === Extending the Default Form Handler === Specific applications will probably need to provide more sophisticated validation and handling of forms than the default action. This is most easily done by writing an action with the following general form: