Form Runner Access Control


Overview 

When it comes to access control, Orbeon Forms leverages and delegates as much as possible to your existing security infrastructure:
  • You define users and roles outside of Orbeon Forms.
  • Whenever possible, access control is path-based, so you can define who can access what based on the path in the URL with your existing security infrastructure.
You can have access control at two levels:
  • Form level – Can the current user access this form?
  • Field level – If the current user can access the form, can they access a particular field? If they can, can they change the field or just read its value?
You implement the later in your form definition, by using the $fr-roles in the in the visibility and read-only XPath expressions of the Form Builder control validation dialog. You can make a control non-visible to the current user by defining a visibility expression that returns false. If the control is visible, you can make it read-only to current user by defining a read-only expression that returns true. The rest of this page focuses on form-level access control.

Accessing the username and roles

[SINCE: 2011-05-18]
  • Username/role from headers or container — Orbeon Forms automatically adds two headers, which are available within Orbeon Forms applications, in particular Form Runner:
  • Orbeon-Username — if present, the value contains the current username
    • if oxf.fr.authentication.method == "container":
      • obtained through the servlet/portlet container's getRemoteUser() function
    • if oxf.fr.authentication.method == "header"
      • obtained via the header specified by oxf.fr.authentication.header.username
  • Orbeon-Roles — if present, is a list of values, each with one role
    • if oxf.fr.authentication.method == "container":
      • each role listed in oxf.fr.authentication.container.roles is check against the container's isUserInRole() function
    • if oxf.fr.authentication.method == "header"
      • obtained via the header specified by oxf.fr.authentication.header.roles
  • Forwarding headers — When using header-based authentication, in addition to defining the name of the headers Form Runner gets the username and role from oxf.fr.authentication.header.username and oxf.fr.authentication.header.roles, you need to add those header names to the oxf.xforms.forward-submission-headers property, so the headers are forwarded by the XForms engine to Form Runner. For instance:

    <property as="xs:string" name="oxf.xforms.forward-submission-headers"
                             value="My-Username-Header My-Roles-Header"/>

  • Persistence — These headers are forwarded to the persistence layer, which can make use of them. In particular, the Oracle and MySQL persistence layers stores the current username when doing any database update.


Access control for deployed forms 

With Orbeon Forms 3.9

You control access to forms entirely based on paths. For this discussion, let's assume that:
  1. You deployed Orbeon Forms on /orbeon. (You can choose to deploy Orbeon Forms on any context, including just on /.)
  2. You have two applications (or group of forms), which correspond to two services of your company: hr and sales.
  3. HR has a number of forms including one called expense-report.

Given this, here is how paths will look like:

Path
Description
/orbeon/fr/hr/expense-report/new To create a new expense report.
/orbeon/fr/hr/expense-report/edit/{id} To edit an expense report having that given, system-generated id.
/orbeon/fr/hr/expense-report/summary To view all the submitted expense reports.


Now, consider the following access rules you might want to put in place:

What you want
How to implement it
All the employes should be able to create a new expense report. Give access to /orbeon/fr/hr/expense-report/new to any authenticated employee.
Only HR persons should be able to view or edit submitted expense reports. Restrict access to /orbeon/fr/hr/expense-report/edit/* and /orbeon/fr/hr/expense-report/summary to authenticated users with the appropriate "HR" role defined in your authentication system.
Only persons in the sales department should be able to access the any of the forms in the sales app. Restrict access to /orbeon/fr/sales/* to authenticated users with the appropriate "sales" role defined in your authentication system.


With Orbeon Forms 4.0

[SINCE 2011-07-22]

You can restrict which users can access forms you create in Form Builder and what operations they can perform. Those restrictions apply to the forms you create once they are deployed, not to editing those forms in Form Builder (for the latter, see the section that follows: Access control for editing forms).

Access control is role-based: you define what roles users must have to perform certain operations. By default, no restriction is imposed on who can do what with forms you create in Form Builder. You enable role-based permissions by going to the Form Builder sidebar, and under Advanced, clicking on Set Permissions. This shows the following dialog:


After you click on the checkbox, you'll be able to set access restriction on the create, read, update, and delete permissions. On the first line, set the operations allowed to all the users who can access the form. You can also add lines, to grant more rights to users with specific roles. For instance, the following  configuration, authorizes:
  • Any user to fill out a new form.
  • Users with the role clerk to read  data.
  • Users with the role admin to do any operation, including deleting form data.



When you select a checkbox for a given operation on the first Anyone row, that checkbox will be automatically checked and disabled so you can't change it, for any additional row, since you wouldn't want to authorize users with additional roles to perform less operations.

Which operations the current user can perform drives what page they can access, and on some pages which buttons are shown:
  • On the Form Runner home page, all the forms on which the current user can perform at least one operation are displayed. Then, for each one of those forms:
    • If they can perform the create operation on the form, a link to the new page is shown.
    • If they can perform any of the read, update, or delete operation on the form, a link to the summary page for that form is shown.
  • For the summary page:
    • Access is completely denied if the current user can't perform any of the read, update, or delete operations.
    • The delete button is disabled if the current user can't perform the delete operation.
    • The review and pdf button are disabled if the current user can't perform the read operation.
    • Clicking in a row of the table will open the form in edit mode if the current user can perform the update operation, in view mode if they can perform the read operation, and do nothing otherwise.
  • For the view page, access is denied if the current user can't perform the read operation.
  • For the new page, access is denied if the current user can't perform the create operation.
  • For the edit page, access is denied if the current user can't perform the update operation.

[LIMITATION] Role-based permissions you set in Form Builder can only be driven by container-based roles. At this point it doesn't support header-driven roles, ignoring the value of the oxf.fr.authentication.method property.

Access control for editing forms

Access to Form Builder  as a whole

Given the assumptions made in the previous section, the paths used by Form Builder look as follows:

Path
Description
/orbeon/fr/orbeon/builder/new To create a new form.
/orbeon/fr/orbeon/builder/edit/{id} To edit a form having that given, system-generated id.
/orbeon/fr/orbeon/builder/summary To view all the editable forms.

If you have multiple classes want to give access to Form Builder to one class of users, and those users are able to edit any form in any app, the you can use path-based access restrictions, as described the previous section.

Access to specific apps/forms in Form Builder

form-builder-permissions.xml

If you'd like to have multiple classes of Form Builder users where some case edit, say, form in the hr app, while other can edit forms in the sales app, then you'll want to setup the form-builder-permissions.xml file.

NOTE: [SINCE 2011-09-07] The file is now called form-builder-permissions.xml file. It replaces the file called form-runner-roles.xml. For backward compatibility, form-runner-roles.xml is still supported.

In this file you map role names to applications and forms. For instance, the following tells Orbeon Forms that only users with the role hr-form-editor can edit or create forms in the hr app, and only users with the role sales-form-editor can edit or create forms in the sales app. As you can infer from the syntax, you could be even more specific and only give access to users with a given role to a specific form in a specific app.

<roles>
    <role name="hr-form-editor"    app="hr"    form="*"/>
    <role name="sales-form-editor" app="sales" form="*"/>
</roles>


Orbeon Forms can infer the roles for the current user either based on information it gets from the container or from an HTTP header. Those two cases are detailed in the following two sections. Once you've defined your form-builder-permissions.xml and done the appropriate setup for container-driven or header-driven roles, as described below:
  1. The Form Builder summary page will only show the forms users have access to.
  2. When they create a new form, if users don't have the right to create a form in any app, instead of seeing a text field where they can enter the application name, they will see a drop-down listing the possible application, as shown in the following screenshot:

[LIMITATION] Restrictions on the form name in form-builder-permissions.xml are at this point not supported; only restrictions on the app name are supported. This means that you should always use form="*". If you define a restriction on the form name, it won't be enforced at the time the form is created, allowing users to create, save, and publish a form with an undesirable name. However they then won't be able to see the form they created when going back to the summary page.

Container-driven roles

[SINCE 2011-07-01]

You want to use container roles if your users are setup at the application server level, with container managed security. In Tomcat, this would correspond to using a security realm, which in its simplest form gets users from Tomcat's conf/tomcat-users.xml. To setup container-driven roles, configure your form-builder-permissions.xml as described above, then:
  1. Enable container-driven roles — To do so, set the following property in your properties-local.xml:

    <property as="xs:string" name="oxf.fr.authentication.method" value="container"/>

  2. List possible roles — There is no container API for Orbeon Forms to ask for all the roles for the current user; instead Orbeon Forms can only ask if the current user has a specific role. Because of this, you need to list the possible roles in the following property, which if you have two roles form-builder-hr and form-builder-finance will look like:

    <property as="xs:string" name="oxf.fr.authentication.container.roles"
                             value="form-builder-hr form-builder-finance"/>

  3. Require authentication — You'll also want to have another role, say form-builder, that you grant to all the users who can access Form Builder. Hence, in our example, users will have either the two roles form-builder and form-builder-hr, or the two roles form-builder and form-builder-finance. In Orbeon Forms WEB-INF/web.xml, add the following to require users to login to access Form Builder. This assumes that you're using basic authentication:

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Form Builder</web-resource-name>
            <url-pattern>/fr/orbeon/builder/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>form-builder</role-name>
        </auth-constraint>
    </security-constraint>
    <login-config>
        <auth-method>BASIC</auth-method>
    </login-config>
    <security-role>
        <role-name>form-builder</role-name>
    </security-role>

Header-driven roles

You want to use header-driven roles if you have a servlet filter, single sign-on software, or other system that sets the roles for the current user in an HTTP header. To use header-driven roles, configure your form-builder-permissions.xml as described above, then:
  1. Enable header-driven roles — To do so, set the following property in your properties-local.xml:

    <property as="xs:string" name="oxf.fr.authentication.method" value="header"/>

  2. Header name — Tell Orbeon Forms what is the name of the HTTP header that will contain the roles for the current user. For instance, if that header is My-Roles-Header, use:

    <property as="xs:string" name="oxf.fr.authentication.header.roles" value="My-Roles-Header"/>

  3. LDAP-style header syntax (Optional) — The value of the header is a list of roles separated by spaces, commas, or pipes (|). Furthermore, they can optionally be composed of properties in the form of name=value, where name is specified by a configuration property, and value is the value of the role. This is typically useful the value if the header follows an LDAP-style syntax, for instance: cn=role1,dc=acme,dc=ch|cn=role2,dc=acme,dc=ch. If your header follows a LDAP-style syntax, set the following property to configure what "name" contains the header, which in this example is cn:

    <property as="xs:string" name="oxf.fr.authentication.header.roles.property-name" value="cn"/>