ORDS Prehook Function

Jon DixonJon Dixon
7 min read

Introduction

In version 18.3, Oracle REST Data Services (ORDS) introduced a new configuration file parameter called procedure.rest.preHook. This parameter allows developers to identify a PL/SQL procedure that ORDS calls before every web service call in a particular connection pool. This post will describe how to use this parameter and several reasons why you might find it helpful.

What Does an ORDS PreHook Do?

Once configured, every REST API call is routed to the PreHook function after authentication but before the REST API is executed. If the PreHook function returns TRUE, then ORDS continues with executing the REST API; if the function returns FALSE, then ORDS returns an HTTP 403 ‘forbidden’ error.

Per the documentation, the parameter ‘procedure.rest.preHook’ : “Specifies the function to be invoked prior to dispatching each Oracle REST Data Services based REST Service. The function can perform configuration of the database session, perform additional validation or authorization of the request. If the function returns true, then processing of the request continues. If the function returns false, then processing of the request is aborted and an HTTP 403 Forbidden status is returned.”

One thing to note is that your PreHook function will get called for all REST API calls. This includes the REST API calls that SQL Developer Web makes!
🚤
Another thing to note is that by configuring a PreHook, ORDS must make an additional roundtrip to the DB to execute and validate the result of running the PreHook function. This will impact performance, so keep processing in your PreHook function to a minimum.

Configuration

From the command line on the ORDS server, you can set up the PreHook function as follows.

ords --config ${ORDS_CONFIG} config set procedure.rest.preHook demo.ords_utl_pk.pre_hook

This adds the following entry in the file: ords_config/databases/default/pool.xml

<entry key="procedure.rest.preHook">demo.ords_utl_pk.pre_hook</entry>
💡
The above entry assumes I have a package called ords_utl_pk with a function called pre_hook, which I created in the schema demo.

The package spec looks like this:

CREATE OR REPLACE PACKAGE "ORDS_UTL_PK" AS 

-- ORDS Pre-Hook Function.
FUNCTION pre_hook RETURN BOOLEAN;

END XXAZZ_ORDS_PK;

The package body looks something like this:

CREATE OR REPLACE PACKAGE BODY ORDS_UTL_PK AS 

FUNCTION pre_hook RETURN BOOLEAN IS

  l_method   VARCHAR2(10)  := owa_util.get_cgi_env('REQUEST_METHOD');
  l_api_path VARCHAR2(500) := owa_util.get_cgi_env('X-APEX-PATH');

BEGIN

  IF l_method = 'GET' AND l_api_path LIKE 'module1/%/orders%' THEN
    -- Code for specific URL path gooes here.
    -- Retun FALSE to block the request.
    -- Call from any other paths (e.g. SQL Developer Web) are ignored.
  END IF;

  RETURN TRUE;

EXCEPTION WHEN OTHERS THEN
  -- Log error and notify admin here.
  RAISE;
END pre_hook;

END ORDS_UTL_PK;

Grants

The procedure must be executable from all schemas your ORDS services run. For example, if you create a Prehook function called ORDS_UTL_PK.PRE_HOOK in the schema DEMO, and you are running ORDS services from schemas XXORDS and DEMO, then you must grant execute on DEMO.ORDS_UTL_PK.PRE_HOOK to the schema XXORDS. If you do not, you will get the following error when running services from XXORDS:

ORDS PreHook Impact of not performing grants

From APEX 24.1 onward, you must also issue a grant to APEX_PUBLIC_ROUTER. Otherwise, SQL Developer Web will stop working!

GRANT EXECUTE ON DEMO.ORDS_UTL_PK TO APEX_PUBLIC_ROUTER;

Use Cases

Logging Calls to ORDS Services

​Logging ORDS activity to a database table is probably the subject of a longer post. In short, you may want to log invocations of your ORDS services to track usage. Knowing what methods and resources are being utilized is extremely helpful. You can track which services are not being used, which clients are using which services, etc.

In some cases, we could already do this by adding the log table insert to our ORDS handler logic. This does not, however, work for GET services, where your handler was an SQL statement.

Let’s look at how we could go about adding usage logging to a table for all our ORDS services using the PreHook function.

ORDS PreHook Usage Logging

​First, create a logging table with columns to capture service invocation information. Let’s consider the following table called rest_log. In this example, we will capture all the valuable information in a JSON document stored in a CLOB column.

CREATE TABLE REST_LOG
 (log_id     NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY CONSTRAINT REST_LOG_PK PRIMARY KEY,
  cgi_vars   CLOB CONSTRAINT REST_LOG_JSON CHECK (INTERNAL_JSON IS JSON),
  log_date   TIMESTAMP WITH LOCAL TIME ZONE NOT NULL)

Our PreHook function to populate this table could look something like this:

FUNCTION pre_hook RETURN BOOLEAN IS

  l_method   VARCHAR2(10)  := owa_util.get_cgi_env('REQUEST_METHOD');
  l_api_path VARCHAR2(500) := owa_util.get_cgi_env('X-APEX-PATH');
  l_obj      json_object_t := json_object_t();  

BEGIN

  IF l_method = 'GET' AND l_api_path LIKE 'module1/%/orders%' THEN
    -- Capture the CGI Environment, Headers, etc. in a JSON document.
    FOR i in 1 .. owa.num_cgi_vars LOOP
      l_obj.put(owa.cgi_var_name(i), owa.cgi_var_val(i));
    END LOOP;
    INSERT INTO rest_log (cgi_vars, log_date) 
      VALUES (l_obj.to_clob, current_timestamp);
    COMMIT;
  END IF;
  RETURN TRUE;

EXCEPTION WHEN OTHERS THEN
  -- Log error and notify admin here.
  RAISE;
END pre_hook;

​Capturing all of the CGI Environment variables gives you information about the web service that was called and all the HTTP headers and URL parameters passed to the REST API.

Setting Session Context

​Setting database session context is an excellent use of PreHooks. Let’s say you operate in an Oracle e-Business Suite (EBS) environment. Each call to a web service needs to set the EBS session context based on the user and responsibility. This ensures the correct user and operating unit are used when querying data or calling EBS public APIs to create and update records. This is typically performed using the EBS API fnd_global.apps_initialize.

Before now, we could do this for POST services by including a call to fnd_global.apps_initialize at the start of the handler logic. This was not possible, however, for a GET based on a SELECT statement. This meant we had to build GET services using a PL/SQL block and emit the JSON manually using APEX_JSON or JSON_OBJECT.

The PreHook function allows us to call fnd_global.apps_initialize at the start of the session and have that context applied when the web service is called. This is much cleaner.

ORDS PreHook Example of Setting Session Context

Of course, there are use cases outside the EBS world where you need to set session information, especially if you use VPD (Virtual Private Database) or something similar to perform row-based security on your data.

Custom Authorization

​As the documentation suggests, the primary use for the Prehook function is to perform custom authorization. All you have to do is have the function return false, and ORDS will automatically return a 403 ‘Forbidden’ HTTP response code to the caller.

ORDS PreHook Custom Authorization Example

For example, you may want client X to access the GET and POST handler for a resource but not the DELETE handler. In this example, you can use the REQUEST_METHOD CGI environment variable to determine the method (GET, POST, PUT or DELETE), and X-APEX-PATH to determine the resource/web service being called. If you use OAuth to authenticate your ORDS service, you can access the OAuth Client ID in the ORDS PreHook function using the X-ORDS-HOOK-USER HTTP header.

This gives you everything you need to determine if the client is allowed to call a particular method on a specific resource.

Feature Request

​I would also like to have the ability to configure a postHook function; this would ideally give developers access to the resultant http status and allow us to log the final status of each web service call. Ideally, it would also have access to the :body_text and response payloads. This would allow developers to implement a complete table-logging solution for ORDS.

Conclusion

​The Prehook function capability is a well-thought-out feature that is an excellent help to ORDS web service developers. Hopefully, this post gave you some ideas on how it could be used, although I have no doubt there are many more use cases out there.

An entire section of the ORDS documentation is dedicated to this feature, which includes examples. There is also additional information available in the pre_hook folder contained in the ORDS download zip.

P.S. It will be interesting to see if and how the ORDS development team plans to expose parameters like these to the OCI Autonomous Database. This may prove a challenge, given the parameter is currently set in a configuration file on the server.

3
Subscribe to my newsletter

Read articles from Jon Dixon directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Jon Dixon
Jon Dixon

Hi, thanks for stopping by! I am focused on designing and building innovative solutions using the Oracle Database, Oracle APEX, and Oracle REST Data Services (ORDS). I hope you enjoy my blog.