Authorizing pages and services
Availability
This feature is available since Orbeon Forms 4.0. Orbeon Forms 3.9 and earlier did not protect pages and services as described below.
Rationale
Orbeon Forms runs within a the servlet container. This has huge benefits, including portability. But in general, such a container is fairly inflexible. For example, you cannot match pages based on regular expressions, and you cannot specify two different forms of authentication in the same web application based on whether you're targeting a page or service (such as form-based authentication, versus basic authentication). In addition, it is not easily possible for a web application to have full access to the container's authentication and authorization mechanisms, certainly not in a pain-free and portable way.
On the other hand, Orbeon Forms would like to allow some level of configuration of this directly in a page flow. The solution chosen is a solution of delegation.
Basic operation
When a request for a page or service reaches the controller, the following takes place by default:
The controller checks whether the request is an internal request, that is, a request from a local Orbeon Forms application. If that's the case, the request is allowed (this is based on a mechanism of private tokens).
If the request is not internal, the controller then checks whether the request has a public method. If so, the request is allowed.
For pages, the public methods are
GET
and HEAD by default.For services, there are no public methods by default.
If the method is not public, the controller then delegates to an authorization service.
Configuration
Public methods
By default, the following applies for requests outside of Orbeon Forms:
Pages: the
GET
andHEAD
HTTP methods are allowed and all other HTTP methods are disallowed.Services: all HTTP methods are disallowed.
This matches the following default properties:
In your properties-local.xml you can change those defaults, for example:
WARNING: Changing those defaults can introduce serious security risks. Please understand the impact that doing so might have.
On a per-controller basis, the <controller>
element supports two attributes, page-public-methods
and service-public-methods
. These override the global properties:
Finally, the <page>
and <service>
elements support the public-methods
attribute, which allows setting the list of public methods for a given page or service:
In addition to any HTTP method name, the property or attribute supports the #all
token to indicate that all methods are allowed.
Backward compatibility
With previous versions of Orbeon Forms, requests were unrestricted. Although we don't recommend it, if you want to, you can enable the old behavior with the following properties:
Authorization service
You can configure the authorization service via a property:
The value of this property is either an absolute URL or an absolute path. If it is an absolute path, it is resolved against the host receiving the request. For example, if your servlet container is deployed on http://localhost:8080/
, the path above resolves to http://localhost:8080/orbeon-auth
.
This means that the authorization service can reside within the same container as Orbeon Forms, or in a completely different location.
How the authorization service works
It's pretty simple: Orbeon Forms forwards the incoming request to that service. This includes: HTTP method, headers, and requested path. Note, that even in the case of a POST
or PUT
, the request body is not forwarded. The requested path is appended to the path of the authorization service. For example, if the request was for the following service:
with the settings above, the following URL would be called:
This allows the authorization service to discriminate between different types of pages and services.
[SINCE Orbeon Forms 2016.3]
An additional header, Orbeon-Remote-Address
, is passed, with the value of the incoming request's remote address, if any. This allows the authorizer to filter on the remote IP without the need for a separate IP filter.
A simple authorization service
Orbeon Forms ships with a very simple WAR file: orbeon-auth.war
. This war file contains a dummy servlet and a web.xml with stub to configure BASIC authentication. You typically deploy this WAR file within the same servlet container as the main Orbeon Forms WAR file. This means that you can set the property above to /orbeon-auth
. Here is the default content of the web.xml
:
By default, this configuration enables BASIC auth and only authorizes requests with a role called orbeon-service
. This role is arbitrary. You can configure your servlet container to handle any role. If you are using Tomcat, you can for example configure users and roles in the tomcat-users.xml file.
With this setup, a request for an Orbeon Forms page or service is forwarded to this authorization service. If the request comes with appropriate credentials for BASIC authentication which translates into a user with the given role, the servlet returns a successful response, and the request is authorized. Otherwise, the request is denied, and the request to the page or service is rejected.
Note that this example is just about the simplest way that you can implement the authorization service. But you most likely will want to do some more advanced configuration. Also, note that you are not limited to BASIC authentication. You could for example fully delegate authorization to your own servlet.
[SINCE Orbeon Forms 2023.1] If you are using Orbeon Forms 2023.1 or newer, and are running a servlet container that uses the Jakarta Servlet API (e.g. Tomcat 10+, WildFly 27+), you need to use the org.orbeon.oxf.controller.JakartaAuthorizerServlet
servlet class instead of org.orbeon.oxf.controller.AuthorizerServlet
.
Implementing your own service
You don't have to use the WAR provided to implement your service. You can implement your own servlet, or in fact implement your own service which can reside on any server and be written with any technology you like.
Just make sure that your service responds with a successful HTTP return code when the request is authorized, and a non-successful HTTP return code when the request is not authorized (such as 401 or 403).
It is very important to validate services independently from logged in users. This is because in general, human users of the application must not be able to access services directly!
Last updated