What You See Is What You Get Element

REST Data Services Logo 5Oracle REST Data Services (formerly APEX Listener)


Contents

Copyright Notice

Copyright © 2011, 2014, Oracle and/or its affiliates. All rights reserved.

Introduction

About Oracle Application Express

Oracle Application Express is a declarative, rapid web application development tool for the Oracle database. It is a fully supported, no cost option available with all editions of the Oracle database. Using only a web browser, you can develop and deploy professional applications that are both fast and secure.

About Oracle REST Data Services

Oracle REST Data Services (ORDS) is a Java EE-based alternative for Oracle HTTP Server (OHS) and mod_plsql. The Java EE implementation offers increased functionality including command line based configuration, enhanced security, file caching and RESTful Web Services. Oracle REST Data Services also provides increased flexibility by supporting deployments using Oracle WebLogic Server, Oracle Glassfish Server, Apache Tomcat, and a standalone mode.

Tip Oracle REST Data Services was previously known as Oracle Application Express Listener.

About RESTful Web Services

Representational State Transfer (REST) is a style of software architecture for distributed hypermedia systems such as the World Wide Web. An API is described as RESTful when it conforms to the tenets of REST. Although a full discussion of REST is outside the scope of this document, a RESTful API has the following characteristics:

  • Data is modelled as a set of resources. Resources are identified by URIs.
  • A small, uniform set of operations are used to manipulate resources (for example, PUT, POST, GET, DELETE).
  • A resource can have multiple representations (for example, a blog might have a HTML representation and a RSS representation).
  • Services are stateless and since it is likely that the client will want to access related resources, these should be identified in the representation returned, typically by providing hypertext links.

Release 4.2 of Oracle Application Express leverages the capabilities of Oracle REST Data Services 2.0 to provide developers with an easy to use graphical user interface for defining and testing RESTful Web Services.

About this Document

This document will provide an introduction to developing RESTful Web Services with Oracle Application Express and Oracle REST Data Services.

Getting Started with RESTful Services

Tip Please ensure that you have installed and configured both Oracle Application Express 4.2 or later, and Oracle REST Data Services 2.0.6 or later before attempting to follow any of the examples in this document.

Tip This document assumes your are familiar with the usage of Oracle Application Express. If you are new to Oracle Application Express, please consult the Oracle Application Express documentation.

RESTful Services Terminology

This section introduces some common terms that are used throughout this document:

  • RESTful Service: A HTTP web service that conforms to the tenets of the RESTful Architectural Style, see “About RESTful Web Services” above.

  • Resource Module: An organizational unit that is used to group related Resource Templates together, similar in purpose to a PL/SQL package.

  • Resource Template: An individual RESTful Service that is able to service requests for some set of URIs (Universal Resource Locators). The set of URIs is defined by the URI Template of the Resource Template.

  • URI Template: A simple grammar that defines the particular patterns of URIs that a given Resource Template can handle, for example the template: images/{id} will match any URI whose path begins with images/ for example: images/101.

  • Resource Handler: Provides the logic required to service a particular HTTP method, for a specific Resource Template. For example the logic of the GET HTTP method for the above Resource Template might be: select content_type, contents from images where id = :id.

  • HTTP Operation: HTTP (HyperText Transport Protocol) defines a number of standard methods that can be performed on resources:

    • GET: Retrieve the resource contents.
    • POST: Store a new resource.
    • PUT: Update an existing resource.
    • DELETE: Remove a resource.

Accessing RESTful Services

To view, create, edit and test RESTul Services in Oracle Application Express, access the ‘RESTful Services’ option within ‘SQL Workshop’:

  • Log into Oracle Application Express as a Developer or as an Administrator.
  • On the page displayed after login, click the icon labelled ‘SQL Workshop’.
  • On the next page click the icon labelled ‘RESTful Services’.

Exploring the sample RESTful Services

If your Application Express instance is configured to automatically add the sample application and sample database objects to workspaces, then a sample Resource Module named: oracle.example.hr will be visible in the list of Resource Modules. If not then you can click the ‘Reset Sample Data’ task on the right hand side of the RESTful Services Page to create the sample Resource Module.

Click on the oracle.example.hr icon to view the Resource Templates and Resource Handlers defined within the module. Note how the module has a URI prefix with the value: hr/. This means that all URIs serviced by this module will start with the characters hr/.

Click on the Resource Template named employees/{id}. Note how the template has a URI Template with the value: employees/{id}. This means that all URIS starting with hr/employees/ will be serviced by this Resource Template.

The HTTP methods supported by a Resource Template are listed beneath the Resource Template, in this case, the only supported method is the GET method. Click on the GET Resource Handler for hr/employees/{id} to view it’s configuration. The Source Type for this handler is ‘Query One Row’. This means that the resource is expected to be mapped to a single row in the query result set. The ‘Source’ for this handler is:

select * from emp 
         where empno = :id

Assuming that the empno column is unique, the query should only produce a single result (or no result at all if no match is found for :id). Let’s try it out. Press the ‘Test’ button. The following error message should be displayed:

400 - Bad Request Request path contains unbound parameters: id

If you look at the URI displayed in the browser, it will look something like this:

https://server:port/ords/workspace/hr/employees/{id}

where:

  • server is the DNS name of the server where Oracle Application Express is deployed
  • port is the port the server is listening on
  • workspace is the name of the Oracle Application Express workspace you are logged into

Note the final part of the URI: hr/employees/{id}, the error message is telling us that this is not a valid URI, the problem is that we did not substitute in a concrete value for the parameter named {id}. Let’s fix that, press the browser back button, then click ‘Set Bind Variables’.

In the page that appears you are prompted to enter a value for the bind variable named :id, enter the value 7369 and press the ‘Test’ button. A new browser window appears displaying the following JSON (JavaScript Object Notation):

{
 "empno":7369,
 "ename":"SMITH",
 "job":"CLERK",
 "mgr":7902,
 "hiredate":"1980-12-17T08:00:00Z",
 "sal":800,
 "deptno":20
}

Note also the URI displayed in the browser for this resource:

https://server:port/ords/workspace/hr/employees/7369

The {id} URI Template parameter is bound to the SQL :id bind variable, and in this case it has been given the concrete value of 7369 so the query executed by the RESTful Service becomes:

 select * from emp
          where empno = 7369

The results of this query are then rendered as JSON as shown above.

Tip Reading JSON can be difficult, to make it easier to read, it is recommended to install a browser extension that pretty prints the JSON. For example, Mozilla Firefox and Google Chrome both have extensions named JSONView:

Now lets see what happens when we enter the URI of a resource that does not exist. In the ‘Set Bind Variables’ page, change the value of :id from 7369 to 1111, and press the ‘Test’ button. As before a new window pops up, but instead of displaying a JSON resource, it displays an error message reading:

404 - Not Found

This is the expected behaviour of this handler, when a value is bound to :id that does not exist in the emp table, the query produces no results and consequently the standard HTTP Status Code of 404 - Not Found is returned.

We have a service that will provide us information about individual employees, if we know the id of an employee, but how do we discover the set of valid employee ids? Press the ‘Cancel’ button to return to the previous page displaying the contents of the Resource Module. Click on the template named employees/. Let’s look at the resource it generates first, and then afterwards let’s understand it’s logic.

Click on the GET handler beneath employees/, and click the ‘Test’ button. A resource similar to the following is displayed (If you haven’t already done so, now would be a good time to install a JSON viewer extension in your browser to make it easier to view the output):

{         
 "next": 
  {"$ref": 
    "https://server:port/ords/workspace/hr/employees/?page=1"},
 "items": [ 
  {
   "uri": 
    {"$ref": 
      "https://server:port/ords/workspace/hr/employees/7369"},
   "empno": 7369,
   "ename": "SMITH"
  }, 
  {
   "uri": 
    {"$ref": 
      "https://server:port/ords/workspace/hr/employees/7499"},
   "empno": 7499,
   "ename": "ALLEN"
  },
  ...
  {
   "uri": 
    {"$ref": 
      "https://server:port/ords/workspace/hr/employees/7782"},
   "empno": 7782,
   "ename": "CLARK"
  }     
 ]
}

This document contains a number of things worth noting:

  • The first element in the document is named next and is a URI pointing to the next page of results, we will explain more about how paginated results are supported later.
  • The second element is named items and contains a number of child elements. Each child element corresponds to a row in the result set generated by the query.
  • The first element of each child element is named uri and contains a URI pointing to the service that provides details of each employee. Note how the latter part of the URI matches the URI Template: employees/{id}. In other words if a client accesses any of these URIs the request will be serviced by the employees/{id} RESTful service we discussed previously.

So this service addresses the problem of identifying valid employee ids by generating a resource that lists all valid employee resources. The key point to realise here is that it does not do this by just listing the id value by itself and expecting the client to be able to take the id and combine it with prior knowledge of the employees/{id} service to produce an employee URI, instead it lists the URIs of each employee.

Since the list of valid employees may be large, the service also breaks the list into smaller pages, and again uses a URI to tell the client where to find the next page in the results.

Now let’s look at how this service is implemented. Press the back button in your browser to return to the GET handler definition.

Note the Source Type is Query, this is the default Source Type, and indicates that the resource can contain zero or more results. The ‘Pagination Size’ is 7, this means that there will be seven items on each page of the results. Finally the Source for the handler looks like this:

select empno "$uri", empno, ename from (
 select emp.*, 
        row_number() over (order by empno) rn 
        from emp
 ) tmp 
 where 
  rn between :row_offset and :row_count
 

Let’s explain this query:

  • The first line states that we want to return three columns, the first is the employee id: empno, but aliased to a column name of $uri, we’ll explain this in a moment, the second is again the employee id and the third is the employee name: ename.

  • Columns in result sets whose first character is $ are given special treatment, they are assumed to denote columns that must be transformed into URIs, these are called Hyperlink Columns. Thus naming columns with a leading $ is a way to generate hyperlinks in resources. When a Hyperlink Column is encountered it’s value is prepended with the URI of the resource in which the column is being rendered, to produce a new URI. For example, recall that the URI of the service we are examining is https://server:port/ords/workspace/hr/employees/. Assume the value of empno in the first row produced by the this service’s query is 7369, then the value of $uri becomes: https://server:port/ords/workspace/hr/employees/7369.

  • JSON does not have a URI data-type, so a convention is needed to make it clear to clients that a particular value represents a URI, Oracle REST Data Services use the JSON Reference proposal, which states that any JSON object containing a member named $ref, and whose value is a string, is a URI. Thus the column: $uri, and it’s value: https://server:port/ords/workspace/hr/employees/7369 is transformed to the following JSON object:

        {"uri": 
         {"$ref": 
          "https://server:port/ords/workspace/hr/employees/7369"
         }
        } 
  • The inner query uses the row_number() analytical function to count the number of rows in the result set, and the outer where clause constrains the result set to only return rows falling within the desired page of results. Oracle REST Data Services defines two implicit bind parameters, :row_offset and :row_count that always contain the indicies of the first and last rows that should be returned in a given page’s results.

    For example if the current page is the first page and the pagination size is 7 then the value of :row_offset will be 1 and the value of :row_count will be 7.

Now let’s look at a simpler way to do both hyperlinks and paged results. Click on the GET handler of the employeesfeed/ resource template, note the Source Type of this handler is Feed and Pagination Size is 25. Change the pagination size to 7 and click ‘Apply Changes’. Note the Source of the handler is just:

select empno, ename from emp 
                    order by deptno, ename

As you can see, the query is much simpler than the previous example, however if you click the ‘Test’ button, you will see a result that is very similar to the result produced by the previous example.

Firstly the Feed Source Type is an enhanced version of the Query Source Type that automatically assumes the first column in a result set should be turned into a hyperlink, eliminating the need to alias columns with a name starting with $. In this example the empno column is automatically transformed into a hyperlink by the ‘Feed’ Source Type.

Secondly this example demonstrates Oracle REST Data Services’s ability to automatically paginate result sets if a ‘Pagination Size’ of greater than zero is defined, and the query does not explicitly dereference the :row_offset or :row_count bind parameters. Since both these conditions hold true for this example, Oracle REST Data Services enhances the query, wrapping it in clauses to count and constrain the number and offset of rows returned. Note that this ability to automatically paginate results also applies to the Query Source Type.

About cURL and testing RESTful Services

So far we have tested RESTful Services using a web browser. Another important and useful way to test RESTful Services is using the command line tool named: cURL.

This powerful tool is available for most platforms, and enables you to see and control exactly what data is being sent/received to/from a RESTful Service. Let’s try it out with the services mentioned above.

curl -i https://server:port/ords/workspace/hr/employees/7369

This will produce a response like the following:

HTTP/1.1 200 OK
Server: Oracle-REST-Data-Services/2.0.6.78.05.25
ETag: "..." 
Content-Type: application/json
Transfer-Encoding: chunked
Date: Thu, 28 Mar 2014 16:49:34 GMT  

{
 "empno":7369,
 "ename":"SMITH",
 "job":"CLERK",
 "mgr":7902,
 "hiredate":"1980-12-17T08:00:00Z",
 "sal":800,
 "deptno":20
}

The -i option tells curl to display the HTTP headers returned by the server.

Example: Creating an Image Gallery

This example builds an image gallery service that provides the ability to store and retrieve images.

Before you Begin

This section describes some common conventions used in this example as well as best practices regarding API entry points.

About URIs

Throughout this example, URIS and URI Templates are referenced using an abbreviated form that omits the host name, context root and workspace path prefix. Consider the following example:

gallery/images/

To access this URI in your Web browser, you would use a URI in the following format:

https://<host>:<port>/ords/<workspace>/gallery/images/

where

  • <host> is the host on which Oracle REST Data Services is running.
  • <port> is the port on which Oracle REST Data Services is listening.
  • /apex is the context root where Oracle REST Data Services is deployed.
  • /<workspace>/ is the workspace path prefix of the Oracle Application Express workspace where the RESTful Service is defined.

About Browser Support

This example uses many modern features defined in HTML5 and related specifications. It has only been tested in Mozilla Firefox and Google Chrome. It has not been tested in Microsoft Internet Explorer or on smart-phone/tablet web browsers. Please use recent versions of either Mozilla Firefox or Google Chrome for this example.

Create an Application Express Workspace

First create a new Oracle Application Express Workspace (in Full Development mode), consult your Oracle Application Express Documentation for details on how to do this.

Call the workspace resteasy and call the administrator user of the workspace resteasy_admin. Ensure the resteasy_admin user is a a member of the RESTful Services user group.

  • Log into the resteasy workspace.

  • Navigate to ‘SQL Workshop’ and then ‘SQL Commands’.

  • Enter the following SQL:

      
    CREATE SEQUENCE GALLERY_SEQ
    /
    CREATE TABLE GALLERY (
      ID NUMBER NOT NULL ENABLE,
      TITLE VARCHAR2(1000) NOT NULL ENABLE,
      CONTENT_TYPE VARCHAR2(1000) NOT NULL ENABLE,
      IMAGE BLOB NOT NULL ENABLE,
      CONSTRAINT GALLERY_PK PRIMARY KEY (ID) ENABLE
    )
    /
    CREATE OR REPLACE TRIGGER BI_GALLERY
     before insert on GALLERY for each row
     begin 
      if :NEW.ID is null then 
       select GALLERY_SEQ.nextval into :NEW.ID from sys.dual;
      end if;
     end;
     /
     ALTER TRIGGER BI_GALLERY ENABLE
     /
     
  • Navigate to ‘SQL Workshop’ and then ‘RESTful Services’.

  • Click the ‘Create’ button on the right hand side.

  • Enter gallery.example in the ‘Name’ field.

  • Enter gallery/ in the ‘URI Prefix’ field.

  • Enter images/ in the ‘URI Template’ field.

  • Choose POST in the ‘Method’ field.

  • Enter the following in the ‘Source’ field:

      
    declare 
     image_id integer;
    begin
     insert into gallery (title,content_type,image) 
                 values  (:title,:content_type,:body)
                 returning id into image_id;
     :status := 201;
     :location := image_id;
    end;
    
  • Click ‘Create Module’.

  • Click the POST handler under images/

  • Choose No in the ‘Requires Secure Access’ field.

  • Click ‘Create Parameter’ on the bottom right.

  • Enter Slug in the ‘Name’ field.

  • Enter title in the ‘Bind Variable Name’ field

  • Click ‘Create’.

  • Click ‘Create Parameter’ on the bottom right.

  • Enter X-APEX-FORWARD in the ‘Name’ field.

  • Enter location in the ‘Bind Variable Name’ field.

  • Choose OUT in the ‘Access Method’ field.

  • Click ‘Create’.

  • Click ‘Create Parameter’ on the bottom right.

  • Enter X-APEX-STATUS-CODE in the ‘Name’ field.

  • Enter status in the ‘Bind Variable Name’ field.

  • Choose OUT in the ‘Access Method’ field.

  • Choose Integer in the ‘Parameter Type’ field.

  • Click ‘Create’.

At this point we have created the module with a single service that can store new images. Let’s add a service to display the list of stored images:

  • Navigate to ‘SQL Workshop’ and then ‘RESTful Services’.

  • Click the module named gallery.example.

  • Click ‘Create Handler’ located under images/.

  • Choose GET in the ‘Method’ field.

  • Choose Feed in the ‘Source Type’ field.

  • Choose No in the ‘Requires Secure Access’ field.

  • Enter the following in the ‘Source’ Field:

    select id,title,content_type from gallery order by id desc
    
  • Click ‘Create’.

At this point we have created the service to store and list images. Let’s add a service to display individual images:

  • Click ‘Create Template’ under gallery.example.

  • Enter images/{id} in ‘URI Template’.

  • Click ‘Create’.

  • Click ‘Create Handler’ under images/{id}.

  • Choose GET in the ‘Method’ field.

  • Choose Media Resource in the ‘Source Type’ field.

  • Choose No in the ‘Requires Secure Access’ field.

  • Enter the following in the ‘Source’ Field:

    select content_type, image from gallery where id = :id
  • Click ‘Create’.

Let’s try out the RESTful Service.

  • Navigate to ‘SQL Workshop’ and then ‘RESTful Services’.

  • Click the module named gallery.example.

  • Click the GET handler located under images/.

  • Click the ‘Test’ button.

  • The following URI should be displayed in the browser:

    https://<host>:<port>/ords/resteasy/gallery/images/
  • Content similar to the following should be displayed:

    {"next":
     {"$ref":
      "http://localhost:8080/ords/resteasy/gallery/images/?page=1"
     },
     "items":[]
    }
    
    • The content is a JSON document that lists the location of each image in the gallery, but since we have not yet added any images, the list (the items[] element) is empty.

    • The JSON has no extra whitespace to minimize it’s size, this can make it difficult to decipher, it is recommended to add a JSON viewing plugin to your browser to make viewing the JSON easier.

Let’s create an APEX application to enable us to add and view images in the gallery.

Next we will create an Oracle Application Express Application to demonstrate using the gallery RESTful Services.

  • Navigate to ‘Application Builder’.

  • Click ‘Create’.

  • Choose Database, then click ‘Next’.

  • Enter Image Gallery in the ‘Name’ field, then click ‘Next’.

  • Click ‘Create Application’, and then ‘Create Application’ again to confirm creation of the application.

  • Click page 1 ‘Home’.

  • Under ‘Regions’ click the ’+’ icon to create a new region.

  • Choose HTML as the region type and click ‘Next’. Click ‘Next’ on the next page.

  • Choose No Template in the ‘Region Template’ field.

  • Enter Tasks in the ‘Title’ field, and click ‘Next’.

  • Enter the following in the ‘Enter HTML Text Region Source’ field:

    <a class="button" id="upload-btn">Upload Image</a>
    
  • Click ‘Create Region’.

  • Under ‘Regions’ click the ’+’ icon to create a new region.

  • Choose HTML as the region type and click ‘Next’. Click ‘Next’ on the next page.

  • Choose DIV Region with ID in the ‘Region Template’ field

  • Enter Images in the ‘Title’ field.

  • Click ‘Create Region’.

  • Click the Images region and click the ‘Attributes’ tab.

  • Enter images in the ‘Static ID’ field and click ‘Apply Changes’.

  • Under ‘Page’ click the ‘Edit’ icon, and click the ‘Javascript’ tab.

  • Enter the following in the ‘Function and Global Variable Declaration’ field:

     var workspace_path_prefix = 'resteasy';
     var gallery_url = './' + workspace_path_prefix + '/gallery/images/'; 
     function uploadFiles(url, fileOrBlob, onload) {   
      var name = 'unspecified';
      if ( fileOrBlob['name'] ) {
       name = fileOrBlob.name;
      }   
      var xhr = new XMLHttpRequest();
      xhr.open('POST', url, true);
      xhr.setRequestHeader('Slug',name);
      xhr.onload = onload;   
      xhr.send(fileOrBlob);  
     }
    
     function createUploader() {
      var $upload = $('<div id="uploader" title="Image Upload"\
       style="display:none">\
       <form>\
        <fieldset>\
         <label for="file">File</label>\
         <input type="file" name="file" id="file"\
          class="text ui-widget-content ui-corner-all"/>\
        </fieldset>\
       </form>\
      </div>');
      $(document.body).append($upload);
      $upload.dialog({ 
       autoOpen:false,
       modal: true,
       buttons: {
        "Upload": function() {
         var file = document.querySelector('input[type="file"]');
         uploadFiles(gallery_url,file.files[0],function() {
          $('#uploader').dialog("close");
          getImages();
         });
        },
        "Cancel": function() {
         $('#uploader').dialog("close");
        }
       }  
      });
      $('#upload-btn').click(function() {    
       $('#uploader').dialog("open");  
      }); 
     }  
    
     function getImages() {  
      var xhr = new XMLHttpRequest();
      xhr.open('GET', gallery_url);
      xhr.onload = function(e) {
       var data = JSON.parse(this.response);
       $('#image-list').remove();
       var $images = $('<ol id="image-list"></ol>');
       for ( i in data.items ) {
        var item = data.items[i];
        var uri = item.uri['$ref'];
        var $image = $('<li></li>')                   
                     .append('<a href="' + uri + '" + title="' + 
                             item.title + '"><img src="'+ uri + 
                             '"></a>');
        $images.append($image);
       }
       $('#images').append($images);
      }  
      xhr.send(); 
     }
     
  • Enter the following in the ‘Execute when Page Loads’ field:

    createUploader();
    getImages();
    
  • Click ‘Apply Changes’.

  • Under ‘Page’ click ‘Edit’ icon, and click the ‘CSS’ tab.

  • Enter the following in the ‘Inline’ field:

    a img { border:none; } 
    #images ol { margin: 1em auto; width: 100%; } 
    #images li { display: inline; } 
    #images a { background: #fff; display: inline; float: left; 
                margin: 0 0 27px 30px; width: auto; padding: 10px 10px 15px; 
                textalign: center; text-decoration: none; color: #333; 
                font-size: 18px; -webkit-box-shadow: 0 3px 6px rgba(0,0,0,.25);
                -moz-boxshadow: 0 3px 6px rgba(0,0,0,.25); }
    #images img { display: block; width: 190px; margin-bottom: 12px; } 
    label {font-weight: bold; text-align: right;float: left;
           width: 120px; margin-right: 0.625em; }
    label :after {content(":")} 
    input, textarea { width: 250px; margin-bottom: 5px;textalign: left}
    textarea {height: 150px;}
    br { clear: left; }
    #images a:after { content: attr(title); }
    .button {
      border-top: 1px solid #96d1f8; 
      background: #65a9d7;
      background: 
       -webkit-gradient(linear,left top,left bottom,
                        from(#3e779d),to(#65a9d7));    
      background: 
       -webkit-linear-gradient(top, #3e779d, #65a9d7);
      background: 
       -moz-linear-gradient(top, #3e779d, #65a9d7);
      background: -ms-linear-gradient(top, #3e779d, #65a9d7);
      background: -o-linear-gradient(top, #3e779d, #65a9d7);
      padding: 5px 10px;    
      -webkit-border-radius: 8px;
      -moz-border-radius: 8px;
      border-radius: 8px;    
      -webkit-box-shadow: rgba(0,0,0,1) 0 1px 0;
      -moz-box-shadow: rgba(0,0,0,1) 0 1px 0;
      box-shadow: rgba(0,0,0,1) 0 1px 0;
      text-shadow: rgba(0,0,0,.4) 0 1px 0;
      color: white;
      font-size: 14px;
      text-decoration: none;
      vertical-align: middle;
      } 
    
     .button:hover {
      border-top-color: #28597a;    
      background: #28597a;
      color: #ccc;
      cursor: pointer;     
     }
    
     .button:active {
      border-top-color: #1b435e;    
      background: #1b435e;
     }
     
  • Click ‘Apply Changes’.

Try out the application

  • Navigate to ‘Application Builder’.

  • Click the ‘Run’ button beside the ‘Image Gallery’ application.

  • A login prompt will be displayed, login as the resteasy_admin user.

  • A blank page will be displayed, click the ‘Upload Image’ button.

  • Choose an image file (a JPEG or PNG file) and click the ‘Upload’ button.

  • The gallery will now display the uploaded image.

It is not wise to allow public access to the image uploading service, and it’s probably not ideal to allow public access to the images in the gallery either. Therefore we need to protect access to the RESTful Services.

RESTful Services support two kinds of authentication:

  • First Party Authentication. This is authentication intended to be used by the party who created the RESTful Service, enabling an APEX application to easily consume a protected RESTful Service. The application must be located with the RESTful Service, i.e. it must be located in the same Oracle Application Express workspace. The application must use the standard Oracle Application Express authentication.

  • Third Party Authentication. This is authentication intended to be used by third party applications not related to the party who created the RESTful Service. Third party authentication relies on the OAuth 2.0 protocol.

Protecting the RESTful Services

  • Navigate to ‘SQL Workshop’ and then ‘RESTful Services’.

  • Click ‘RESTful Service Privileges’ in the section labelled: ‘Tasks’.

  • Click the ‘Create’ button.

  • Enter example.gallery in the ‘Name’ field.

  • Enter Gallery Access in the ‘Label’ field.

  • Choose ‘RESTful Services’ in the ‘Assigned Groups’ field.

  • Enter View and Post images in the Gallery in the ‘Description’ field.

  • Choose gallery.example in the ‘Protected Modules’ field.

  • Click ‘Create’.

Let’s check that access to the RESTful Service is now restricted.

  • Navigate to ‘SQL Workshop’ and then ‘RESTful Services’.

  • Click the module named gallery.example.

  • Click the GET handler located under images/.

  • Click the ‘Test’ button.

  • The following URI should be displayed in the browser:

    https://<host>:<port>/ords/resteasy/gallery/images/
  • An error page should be displayed with the error message:

    401 Unauthorized.

This is the expected result, a protected RESTful Service cannot be accessed unless proper credentials are provided. In the following sections we’ll describe how to add the required credentials to the request.

Modify the Application to use First Party Authentication

First Party Authentication relies on the cookie and user session established by the Application Express application, but Oracle REST Data Services needs additional information to enable it to verify the cookie. It needs to know the application id and the current session id. This information is always known to the Application Express application and must be included with the request made to the RESTful service. The information is included by adding the custom Apex-Session HTTP header to each request sent to a RESTful Service. The application id and session id are sent as the value of the header, separated from each other by a comma delimiter, for example:

GET /ords/resteasy/gallery/images/
Host: server.example.com
Apex-Session: 102, 6028968452563

Sometimes it is not possible to include a custom header in the HTTP request, for example when displaying an image in a HTML page using the <img> tag, an alternative mechanism is used for these scenarios, the application id and session id are included in a query parameter named _apex_session is added to the Request URI, containing the application id and session id separated by a comma delimiter, for example:

<img src="/ords/resteasy/gallery/images/101?_apex_session=102,6028968452563">

Note that this approach must only be used when using a custom header is not possible, it’s use otherwise is discouraged because of the increased risk of the session id being inadvertedly stored or disclosed due to it’s inclusion in the URI.

Now let’s modify the application to add the first party authentication information to each request:

  • Navigate to ‘Application Builder’.

  • Click the ‘Edit’ button beside the ‘Image Gallery’ application.

  • Click the first page, named ‘Home’.

  • Under ‘Page’ click the ‘Edit’ icon, and click the ‘Javascript’ tab.

  • Add the following at the start of the ‘Function and Global Variable Declaration’ field:

    function setApexSession(pathOrXhr) {  
     var appId = $v('pFlowId');
     var sessionId = $v('pInstance');
     var apexSession = appId + ',' + sessionId;   
     if ( typeof pathOrXhr === 'string' ) {
      var path = pathOrXhr;   
      if ( path.indexOf('?') == -1 ) {
       path = path + '?_apex_session=' + apexSession;
      } else {
       path = path + '&_apex_session=' + apexSession;
      }
      return path;
     } else {
      var xhr = pathOrXhr;
      xhr.setRequestHeader('Apex-Session',apexSession);
      return xhr;
     }
    }
    
  • This defines a JavaScript function named setApexSession() which will add the first party authentication information to an XMLHttpRequest object or a string containing a path. Now we need to modify the existing JavaScript code to add call this function when appropriate:

  • After the line reading: xhr.open('POST',url,true); add the following line:

    setApexSession(xhr);
  • After the line reading: xhr.open('GET', gallery_url) add the following line:

    setApexSession(xhr);
  • Change the line reading: var uri = item.uri['$ref']; to:

    var uri = setApexSession(item.uri['$ref']);
  • Click ‘Apply Changes’.

  • Try running the application as before, it should continue to work as before, because it is now providing the RESTful Services with the required authentication information.

Accessing the RESTful Services from a Third Party Application

If third parties wish to consume and use the Gallery RESTful Services, then they must register the 3rd party application in order to gain OAuth 2.0 credentials, which can then be used to initiate an interactive process where the user can authorize the 3rd party application to access the RESTful Services on their behalf.

Once an application is registered, it can then acquire an access token, the access token must be provided with each request to a protected RESTful Service. Oracle REST Data Services verifies the access token before allowing access to the RESTful Service.

OAuth 2.0 defines a number of differerent protocol flows that can be used by applications to acquire an access token. Oracle REST Data Services supports two of these flows:

  • Authorization Code. This flow is used when the third party application is able to keep it’s client credentials secure, for example a third party web-site that is properly secured.

  • Implicit Grant. This flow is used when the third party application cannot assure that it’s credentials would remain secret, for example a Javascript based browser application or a native smartphone application.

The first step then is to register the 3rd party application, to demonstrate this, we will create a user representing the third party developer, and then use that user to register an application.

Tip The steps below create a user in the RESTEASY workspace user repository. Tip In addition to authenticating users defined in workspace user repositories Oracle REST Data Services can also authenticate against any user repository accessible from WebLogic or GlassFish. See the section below titled: ‘Authenticating against WebLogic and GlassFish user repositories’.

Create the 3rd party developer user

  • Navigate to ‘Administration’.

  • Click ‘Manage Users and Groups’.

  • Click ‘Create User’.

  • Enter ‘3rdparty_dev’ in the ‘Username’ field.

  • Enter any email address in the ‘Email Address’ field.

  • Enter a password for the user.

  • Choose ‘OAuth 2.0 Client Developer’ in the ‘User Groups’ field.

  • Click ‘Create User’.

This is the user account for the 3rd party developer who wishes to register an application to access the RESTful Services.

Registering the 3rd party application

We will first register the application to use the Implicit Grant OAuth 2.0 protocol flow.

  • Go to the following URI in your browser:

    https://server:port/ords/resteasy/ui/oauth2/clients/

  • Enter the credentials of the 3rdparty_dev user created above, and click ‘Sign In’.

  • Click ‘Register Client’.

  • Enter 3rd Party Gallery in the ‘Name’ field.

  • Enter Demonstrates consuming the Gallery RESTful Service in the ‘Description’ field.

  • Choose Token in the ‘Response Type’ field.

  • Enter https://server:port/i/oauthdemo/gallery.html in the ‘Redirect URI’ field.

  • Enter any email address in the ‘Support EMail’ field.

  • Choose Gallery Access in the ‘Required Scopes’ field.

  • Click ‘Register’.

  • Click ‘3rd Party Gallery’ in the list that appears on the next page.

  • Note the values of the ‘Client Identifier’ and the ‘Authorization URI’ fields.

Acquiring an Access Token

To acquire an access token the user must be prompted to approve access. To initiate the approval process, direct the user to the approval page using the following URI:

https://server:port/ords/resteasy/oauth2/auth?response_type=token&\
                                              client_id=CLIENT_IDENTIFIER&\
                                              state=STATE

where:

  • CLIENT_IDENTIFIER is the ‘Client Identifier’ assigned to the application when it was registered.
  • STATE is a unique value generated by the application used to prevent Cross Site Request Forgery (CSRF) attacks.

There are a few important points to note about the Oracle REST Data Services OAuth 2.0 implementation:

  • The OAuth 2.0 specification allows two optional parameters to be supplied in the above request:

    • redirect_uri: The location where the authorization server will redirect back to after the user has approved/denied access.
    • scope: Identifies the RESTful Service Privileges that the client wishes to access.

    Oracle REST Data Services does not support either of these parameters, both of these values are specified when the client is registered, it would be redundant to repeat them here. Any values supplied for these parameters will be ignored.

  • The OAuth 2.0 specification only recommends the use of the state parameter, but Oracle REST Data Services requires the use of the parameter, because of it’s importance in helping prevent CSRF attacks.

  • The response type is also specified when the application is registered, hence the response_type parameter is also redundant, but the OAuth 2.0 specification states the parameter is always required, so it must be included. It is an error for the response_type value to differ from the registered response type.

When the above URI is accessed in a browser the user is prompted to sign on, and then prompted to review the application’s request for access and choose whether to approve or deny access.

If the user approves the request then the browser will be redirected back to the registered redirect URI, the access token will be encoded in the fragment portion of the URI:

https://server:port/i/oauthdemo/gallery.html#token_type=bearer&\
                                             access_token=ACCESS_TOKEN&\
                                             expires_in=TOKEN_LIFETIME&\
                                             state=STATE

where:

  • ACCESS_TOKEN is the unique unguessable access token assigned to the current user session, and which must be provided with subsequent requests to the RESTful Service.
  • TOKEN_LIFETIME is the number of seconds the access token is valid for.
  • STATE is the unique value supplied by the application at the start of the authorization flow. If the returned state value does not match the initial state value, then there is an error condition, the access token must not be used. It is possible an attacker is attempting to subvert the authorization process via a CSRF attack.

If the user denies the request, or the user is not authorized to access the RESTful Service, the browser will be redirected back to the registered redirect URI, and an error message will be encoded in the fragment portion of the URI:

https://server:port/i/oauthdemo/gallery.html#error=access_denied&state=STATE

where:

  • error=access_denied informs the client that the user is not authorized to access the RESTful Service, or chose not to approve access to the application.
  • STATE is the unique value supplied by the application at the start of the authorization flow. If the returned state value does not match the initial state value, then there is an error condition, the client should ignore this response. It is possible an attacker is attempting to subvert the authorization process via a CSRF attack.

Using an Access Token

Once the application has acquired an access token, it must be included with each request made to the protected RESTful Service. To do this an Authorization header is added to the HTTP request, with the following syntax:

Authorization: Bearer ACCESS_TOKEN

where:

  • ACCESS_TOKEN is the access token value.

For example, a JavaScript based browser application might invoke the Gallery service as shown below:

var accessToken = ... /* initialize with the value of the access token */
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://server:port/ords/resteasy/gallery/images/',true);
/* Add the Access Token to the request */ 
xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken);  
xhr.onload = function(e) {   
 /* logic to process the returned JSON document */
 ...   
};
xhr.send();

The above example uses the XMLHttpRequest.setRequestHeader(name,value) function to add the Authorization header to the HTTP request. If the access token is valid then the server will respond with the previously described JSON document listing the images in the gallery.

About Browser Origins

One of the key security concepts of web browsers is the ‘Same Origin Policy’. The policy permits scripts running on pages originating from the same web site (an ‘Origin’) to access each other’s data with no restrictions, but prevents access to data originating from other web sites.

An origin is defined by the protocol, host name and port of a web-site. For example https://example.com is one origin and https://another.example.com is a different origin, because the host name differs. Similarily http://example.com is a different origin to https://example.com because the protocol differs. Finally http://example.com is a different origin to http://example.com:8080 because the port differs.

If for example a third party client of the Gallery RESTful Service is located at:

https://thirdparty.com/gallery.html

and the Gallery RESTful service is located at:

https://example.com/ords/resteasy/gallery/images/

then the ‘Same Origin Policy’ will prevent gallery.html making an XMLHttpRequest to https://example.com/ords/resteasy/gallery/images/, because scripts in the https://thirdparty.com origin can only access data from that same origin, and https://example.com is clearly a different origin.

This is proper, if the authors of https://example.com do not trust the authors of https://thirdparty.com. If the authors do have reason to trust each other then the ‘Same Origin Policy’ is too restrictive. Fortunately a protocol called ‘Cross Origin Resource Sharing’ (CORS), provides a means for https://example.com to inform the web browser that it trusts https://thirdparty.com and thus instruct the browser to permit gallery.html to make an XMLHttpRequest to https://example.com/ords/resteasy/gallery/images/.

Configuring a RESTful Service for Cross Origin Resource Sharing

  • Navigate to ‘SQL Workshop’ and then ‘RESTful Services’.

  • Click the module named gallery.example.

  • In the field named ‘Origins Allowed’ enter the origins that are permitted to access the RESTful Service, origins are separated by a comma character.

  • Press Apply Changes

Acquiring a Token using the Authorization Code Protocol Flow

In the previous sections we demonstrated acquiring an accces token using the OAuth 2.0 Implicit protocol flow. In this section we demonstrate how to do the same using the Authorization Code protocol flow. The process is slightly more involved than for the Implicit protocol flow, because it requires us to exchange an authorization code for an access token.

For this example we will mimic this exchange process using cURL.

Register the client application

  • Go to the following URI in your browser:

    https://server:port/ords/resteasy/ui/oauth2/clients/

  • Enter the credentials of the 3rdparty_dev user created above, and click ‘Sign In’.

  • Click ‘Register Client’.

  • Enter Another Gallery in the ‘Name’ field.

  • Enter Demonstrates using the Authorization Code OAuth 2.0 Protocol Flow in the ‘Description’ field.

  • Choose Code in the ‘Response Type’ field.

  • Enter https://gallery.example.demo in the ‘Redirect URI’ field.

  • Enter any email address in the ‘Support EMail’ field.

  • Choose Gallery Access in the ‘Required Scopes’ field.

  • Click ‘Register’.

  • Click ‘3rd Party Gallery’ in the list that appears on the next page.

  • Note the values of the ‘Client Identifier’, ‘Client Secret’ and the ‘Authorization URI’ fields.

Acquiring an Authorization Code

The first step in the Authorization Code protocol flow is to acquire an authorization code. An authorization code is a short lived token that when presented along with the application’s client identifier and secret can be exchanged for an access token.

To acquire an access token the user must be prompted to approve access. To initiate the approval process, direct the user to the approval page using the following URI:

https://server:port/ords/resteasy/oauth2/auth?response_type=code&\
                                                   client_id=CLIENT_IDENTIFIER&\
                                                   state=STATE
                                                   

where:

  • CLIENT_IDENTIFIER is the ‘Client Identifier’ assigned to the application when it was registered.
  • STATE is a unique value generated by the application used to prevent Cross Site Request Forgery (CSRF) attacks.

If the user approves the request then the browser will be redirected back to the registered redirect URI, the access token will be encoded in the query string portion of the URI:

https://gallery.example.demo?code=AUTHORIZATION_CODE&state=STATE

where:

  • AUTHORIZATION_CODE is the authorization code value.

  • STATE is the unique value supplied by the application at the start of the authorization flow. If the returned state value does not match the initial state value, then there is an error condition, the authorization code must not be used. It is possible an attacker is attempting to subvert the authorization process via a CSRF attack.

    Since the registered https://gallery.example.demo redirect URI does not exist, the browser will report a server not found error, but for the purposes of this example, this does not matter, we can still see the authorization code value encoded in the URI. Make a note of the value of the code parameter, we will use it in the next section.

Exchanging an Authorization Code for an Access Token

In the previous step we acquired an authorization code, in this step we will use cURL to exchange the authorization code for an access token.

To exchange an authorization code the application must make a HTTP request to the Oracle REST Data Services OAuth 2.0 token endpoint providing the authorization code and it’s client identifier and secret. If the credentials are correct Oracle REST Data Services will respond with a JSON document containing the access token. Note the application makes the HTTP request from it’s server side (where the client identifier and secret are securely stored) directly to Oracle REST Data Services, the web-browser is not involved at all in this step of the protocol flow.

  • Use the following cURL command to exchange the authorization code for an access token:

    curl -i -d "grant_type=authorization_code&code=AUTHORIZATION_CODE" \
         --user CLIENT_IDENTIFER:CLIENT_SECRET \
         https://server:port/ords/resteasy/oauth2/token
    

    where:

  • AUTHORIZATION_CODE is the authorization code value (which was encoded in the code parameter of the query string in the redirect URI in the previous section).

  • CLIENT_IDENTIFER is the client identifier value.

  • CLIENT_SECRET is the client secret value.

cURL translates the above commands into a HTTP request like the following:

POST /ords/resteasy/oauth2/token HTTP/1.1
Authorization: Basic Q0xJRU5UX0lERU5USUZJRVI6Q0xJRU5UX1NFQ1JFVA== 
Host: server:port 
Accept: */* 
Content-Length: 59 
Content-Type: application/x-www-form-urlencoded  

grant_type=authorization_code&code=AUTHORIZATION_CODE

where:

  • The request is a HTTP POST to the oauth2/token OAuth 2.0 token endpoint.
  • The Authorization header uses the HTTP BASIC authentication protocol to encode the client identifier and secret to assert the application’s identity.
  • The Content-Type of the request is form data (application/x-www-form-urlencoded) and the content of the request is the form data asserting the OAuth 2.0 token grant type and the OAuth 2.0 authorization code value.

The above HTTP request will produce a response like the following:

HTTP/1.1 200 OK
ETag: "..."
Content-Type: application/json

{
 "access_token":"04tss-gM35uOeQzR_2ve4Q..",
 "token_type":"bearer",
 "expires_in":3600,
 "refresh_token":"UX4FVHhPFJl6GokvTXYw0A.."
}

The response is a JSON document containing the access token along with a refresh token (more on refresh tokens in the next section). Once the application has acquired an access token, it must be included with each request made to the protected RESTful Service. To do this an Authorization header is added to the HTTP request, with the following syntax:

Authorization: Bearer ACCESS_TOKEN

Extending OAuth 2.0 session duration

To extend the lifetime of an OAuth 2.0 session, a refresh token can be exchanged for a new access token with a new expiry time. Note refresh tokens are only issued for the Authorization Code protocol flow.

The application makes a similar request to that used to exchange an authorization code for an access token:

  • Use the following cURL command to exchange the refresh token for an access token:

    curl -i -d "grant_type=refresh_token&refresh_token=REFRESH_TOKEN" \      
         --user CLIENT_IDENTIFER:CLIENT_SECRET \
         https://server:port/ords/resteasy/oauth2/token

where:

  • REFRESH_TOKEN is the refresh token value returned when the access token was initially issued.
  • CLIENT_IDENTIFER is the client identifier value.
  • CLIENT_SECRET is the client secret value.

cURL translates the above commands into a HTTP request like the following:

POST /ords/resteasy/oauth2/token HTTP/1.1 
Authorization: Basic Q0xJRU5UX0lERU5USUZJRVI6Q0xJRU5UX1NFQ1JFVA==
Host: server:port
Accept: */*
Content-Length: 53 
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=REFRESH_TOKEN

where:

  • The request is a HTTP POST to the oauth2/token OAuth 2.0 token endpoint.
  • The Authorization header uses the HTTP BASIC authentication protocol to encode the client identifier and secret to assert the application’s identity.
  • The Content-Type of the request is form data (application/x-www-form-urlencoded) and the content of the request is the form data asserting the OAuth 2.0 token grant type and the refresh token value.

The above HTTP request will produce a response like the following:

HTTP/1.1 200 OK
ETag: "..."
Content-Type: application/json  

{
 "access_token":"hECH_Fc7os2KtXT4pDfkzw..",
 "token_type":"bearer",
 "expires_in":3600,
 "refresh_token":"-7OBQKc_gUQG93ZHCi08Hg.."
}

The response is a JSON document containing the new access token along with a new refresh token. The existing access token and refresh token are invalidated, any attempt to access a service using the old access token will fail.

About Securing the Access Token

In OAuth 2.0 the access token is the sole credential required to provide access to a protected service. It is therefore essential to keep the access token secure. Follow these guidelines to help keep the token secure:

  • It is strongly recommended to use HTTPS for all protected RESTful Services. This prevents snooping attacks where an attacker may be able to steal access tokens by eavesdropping on insecure channels. It also prevents attackers snooping the sensitive data that may be present in the payload of the requests.

  • Ensure the client application is not located in a browser origin with other applications/scripts that cannot be trusted. For example say Alice has a client application hosted at the following location:

    https://sharedhosting.com/alice/application

    If another party Chuck is also able to host his application in the same origin, for example at:

    https://sharedhosting.com/chuck/trouble

    then it will be trivial for /chuck/trouble to steal any access token acquired by /alice/application, since they share the same origin: https://sharedhost.com, and hence the browser will not prevent either application accessing the other’s data.

    To protect against this scenario it is imperative that Alice’s application is deployed in it’s own origin, for example:

    https://alice.sharedhosting.com/application

    or:

    https://application.alice.sharedhosting.com

    or:

    https://aliceapp.com

About Oracle REST Data Services User Roles

Oracle REST Data Services defines a small number of predefined user roles:

  • RESTful Services - This is the default role associated with a protected RESTful Service.

  • OAuth2 Client Developer - Users that wish to register OAuth 2.0 applications must have this role.

  • SQL Developer - Users that wish to use Oracle SQL Developer to develop RESTful Services must have this role.

  • Listener Administrator - Users that wish to administrate an Oracle REST Data Services instance via Oracle SQL Developer must have this role. Typically only users created via the java -jar ords.war user command will have this role.

About Oracle Application Express Users and Oracle REST Data Services Roles

By default Oracle Application Express Users do not have any of the above roles. This means that out of the box, Application Express Users cannot:

  • Invoke protected RESTful Services
  • Register OAuth 2.0 applications
  • Use Oracle SQL Developer to develop RESTful Services.

This applies to all Application Express users, including Application Express developers and administrators. It is therefore important to remember to follow the steps below to add Application Express users to the appropriate user groups, so that they can successfully perform the above actions.

About the Listener Administrator Role

Since the Listener Administrator role enables a user to configure an Oracle REST Data Services instance, and therefore has the capability to affect all Application Express workspaces served through that instance, Application Express users are not permitted to acquire the Listener Administrator role.

Granting Application Express Users Oracle REST Data Services Roles

To give an Application Express User any of the roles above, the user must be added to the equivalent Application Express user group. For example to give the RESTEASY_ADMIN user the RESTful Services role, perform the following steps:

  • Log in to the RESTEASY workspace as a RESTEASY_ADMIN.

  • Navigate to ‘Administration’ and then ‘Manage Users and Groups’.

  • Click the ‘Edit’ icon to the left of the RESTEASY_ADMIN user.

  • Choose RESTful Services in the ‘User Groups’ list.

  • Click ‘Apply Changes’.

Automatically granting Application Express Users Oracle REST Data Services Roles

Adding Application Express users to the appropriate user groups can be an easily overlooked step, or can become a repetitive task if there are many users to be managed.

To address these issues, it is possible to configure Oracle REST Data Services to automatically grant Application Express Users a pre-defined set of RESTful Service roles via manual configuration of the defaults.xml configuration file.

Oracle REST Data Services defines three property settings to configure roles:

  • apex.security.user.roles - A comma separated list of roles to grant ‘ordinary’ users, i.e. users who are not developers or administrators.

  • apex.security.developer.roles - A comma separated list of roles to grant users who have the Developer account privilege. Developers also inherit any roles defined by the apex.security.user.roles setting.

  • apex.security.administrator.roles - A comma separated list of roles to grant users who have the Administrator account privilege. Administrators also inherit any roles defined by the apex.security.user.roles and apex.security.developer.roles settings.

For example to automatically give all users the RESTful Services privilege and all developers and adminstrators the OAuth2 Client Developer and SQL Developer roles, add the following to the defaults.xml configuration file:

<!-- Grant all Application Express Users the ability      
        to invoke protected RESTful Services -->
<entry key="apex.security.user.roles">RESTful Services</entry> 
<!-- Grant Application Express Developers and Administrators the ability
        to register OAuth 2.0 applications and use Oracle SQL Developer
        to define RESTful Services --> 
<entry key="apex.security.developer.roles">
   OAuth2 Client Developer, SQL Developer</entry>

Oracle REST Data Services must be restarted after making the above changes.

Controlling RESTful Service access with roles

The built in RESTful Service role is a useful default for indicating users permitted to access protected RESTful Services.

In many cases though it will be necessary to define finer grained roles to accurately limit the set of users that may access a particular RESTful Service.

About Defining Roles

A RESTful Service ‘role’ is an Application Express User Group. Let’s create a user group to control access to the Gallery RESTful Service.

  • Log in to the RESTEASY workspace as a workspace administrator.

  • Navigate to ‘Administration’ and then ‘Manage Users and Groups’.

  • Click the ‘Groups’ tab.

  • Click the ‘Create User Group’ button.

  • Enter Gallery Users in the ‘Name’ field.

  • Click the ‘Create Group’ button.

Associating Roles with RESTful Privileges

Once a user group has been created, it can be associated with a RESTful Privilege. Follow the steps below to associate the ‘Gallery Users’ role with the example.gallery privilege.

  • Navigate to ‘SQL Workshop’ and then ‘RESTful Services’.

  • Click ‘RESTful Service Privileges’ in the section labelled: ‘Tasks’.

  • Click Gallery Access

  • Choose Gallery Users in the ‘Assigned Groups’ list box.

  • Click ‘Apply Changes’.

With these changes, users must have the ‘Gallery Users’ role to be able to access the Gallery RESTful Service.

Authenticating against WebLogic and GlassFish user repositories

Oracle REST Data Services can use APIs provided by WebLogic and GlassFish to verify credentials (username and password) and retrieve the set of groups/roles that the user is a member of.

In the examples below we walk through creating a user in the built in user repositories provided by WebLogic and GlassFish, and verifying that we can authenticate against that user.

It is beyond the scope of this document to describe how to integrate WebLogic and GlassFish with the many popular user repository systems such as LDAP repositories, suffice it to say that Oracle REST Data Services can authenticate against such repositories once WebLogic or GlassFish has been correctly configured. Please consult your application server documentation for more information on what user repositories are supported by your application server and how to configure access to these repositories.

Authenticating against WebLogic

Create a User

  • Start WebLogic if it is not already running

  • Access the WebLogic Administration Console (typically http://server:7001/console), enter your credentials.

  • In the navigation tree on the left, click the Security Realms node

  • If no realm exists click the New button. If a realm already exists click on it, and skip to the next step.

    • Enter Test-Realm for the Name value.
    • Press the OK button.
    • Click on the Test-Realm.
    • Click on the Providers tab.
    • Click on the New button.
    • Enter test-authenticator for the Name value.
    • Choose DefaultAuthenticator for the Type value.
    • Restart WebLogic if warned that a restart is necessary.
    • Click on the Test-Realm.
  • Click the Users and Groups tab.

  • Click the New button.

  • Enter 3rdparty_dev2 as the Name value

  • Enter a password and confirm it’s value.

  • Click the OK button.

  • Click the Groups tab.

  • Click the New button.

  • Enter OAuth2 Client Developer as the Name value. Take care to enter this value correctly, it is case sensitive.

  • Click the OK button.

  • Click the Users tab.

  • Click the 3rdparty_dev2 user.

  • Click the Groups tab.

  • Add OAuth2 Client Developer to the Chosen list.

  • Click the Save button.

Now we have created a user named 3rdparty_dev2 and made it a member of a group named OAuth2 Client Developer. This means the user will acquire the OAuth2 Client Developer role and therefore will be authorized to register OAuth 2.0 applications.

Let’s verify that the user can be successfully authenticated.

Verify the WebLogic user

  • Go to the following URI in your browser:

    https://server:port/ords/resteasy/ui/oauth2/clients/

  • Enter the credentials of the 3rdparty_dev2 user created above, and click ‘Sign In’.

  • The OAuth 2.0 Client Registration page should be displayed. There should be no applications listed. If this page is displayed then we have verified that authentication against the WebLogic user repository is working. If the sign on prompt is displayed again with the message: User is not authorized to access resource, then the Group List value was mis-typed.

Authenticating against GlassFish

Create a User

  • Start GlassFish if it is not already running.

  • Access the GlassFish Administration Console (typically http://server:4848). If you have configured a password, enter your credentials.

  • Navigate to the Security Configuration Pages: * In the navigation tree on the left of the page, expand the Configurations node.

    • Expand the server-config node.
    • Expand the Security node.
    • Expand the Realms node.
    • Choose the file realm.
  • Click the Manage Users button

  • Click the New... button

  • Enter 3rdparty_dev2 as the User ID value

  • Enter OAuth2 Client Developer as the Group List value. Take care to enter this value correctly, it is case sensitive.

  • Enter a password and confirm it’s value.

  • Click the OK button.

Now we have created a user named 3rdparty_dev2 and made it a member of a group named OAuth2 Client Developer. This means the user will acquire the OAuth2 Client Developer role and therefore will be authorized to register OAuth 2.0 applications.

Let’s verify that the user can be successfully authenticated.

Verify the GlassFish user

  • Go to the following URI in your browser:

    https://server:port/ords/resteasy/ui/oauth2/clients/

  • Enter the credentials of the 3rdparty_dev2 user created above, and click ‘Sign In’.

  • The OAuth 2.0 Client Registration page should be displayed. There should be no applications listed. If this page is displayed then we have verified that authentication against the GlassFish user repository is working. If the sign on prompt is displayed again with the message: User is not authorized to access resource, then the Group List value was mis-typed.

Integrating with existing Group/Role Models

The examples above demonstrate configuring the built in user repositories of WebLogic and GlassFish. In this scenario we have full control over how user groups are named.

If a user is a member of a group with the exact same (case sensitive) name as a role then it is considered that the user has that role.

When integrating with existing user repositories the RESTful Service developer will often not have any control over the naming and organization of user groups used in the user repository.

In this scenario we need a mechanism to map from existing ‘phyiscal’ user groups defined in the user repository to the ‘logical’ roles defined by Oracle REST Data Services and/or RESTful Services.

In Oracle REST Data Services, this ‘physical’ group to ‘logical’ role mapping is performed by configuring a configuration file name role-mapping.xml.

About role-mapping.xml

role-mapping.xml is a Java XML Properties file where each property key defines a pattern that matches against a set of ‘phyiscal’ user groups, and each property value enumerates the ‘logical’ roles that the matched user group should be mapped to. It must be located in the same folder as the defaults.xml configuration file. The file must be manually created and edited.

Consider this example:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
 <entry key="webdevs">RESTful Services</entry>
</properties>

This is role mapping is very straightforward, it states that any user who is a member of a group named: webdevs, is given the role RESTful Services meaning that all members of the webdevs group can invoke RESTful Services.

A mapping can apply more than one role to a group, for example:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
 <entry key="webdevs">RESTful Services, SQL Developer</entry>
</properties>

This is rule gives members of the ‘webdevs’ group both RESTful Services and SQL Developer roles.

Parameterizing Mapping Rules

Having to explicitly map from each group to each role may not be scalable if the number of groups or roles is large. To address this concern we can parameterize rules, consider this example:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> 
<properties>
 <entry key="{prefix}.webdevs">RESTful Services</entry>
</properties>

In this example we are saying that any group name that ends with .webdevs will be mapped to the RESTful Services role. For example a group named: HQ.webdevs would match this rule, as would a group named: EAST.webdevs.

The syntax for specifying parameters in rules is the exact same as that used for URI Templates, the parameter name is delimited by curly braces ({}).

Dereferencing Parameters

Any parameter defined in the group rule can also be dereferenced in the role rule. Consider this example:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> 
<properties>
 <entry key="cn={userid},ou={group},dc=MyDomain,dc=com">{group}</entry>
</properties>

In this example we are attempting to map the organizational unit component of an LDAP distinguished name to a logical role. We are stating that the organizational unit name maps directly to a logical role with exact same name. Note that we refer to a {userid} parameter but never actually use it. This is fine, in effect we are using {userid} as a wildcard flag.

For example the distinguished name: cn=jsmith,ou=Developers,dc=MyDomain,dc=com will be mapped to the logical role named Developers.

Indirect Mappings

Sometimes to accomplish the desired role mapping it may be necessary to apply multiple intermediate rules, consider this example:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
 <entry key="cn={userid},ou={group},dc=example,dc=com">{group}</entry>
 <entry key="{prefix},ou={group},dc=acquired,dc=com">{group}</entry>
 <entry key="Developers">RESTful Services, SQL Developer</entry>
</properties>

In this example we are attempting to map the organizational unit component of an LDAP distinguished name to some logical roles. Complicating matters is the fact that users can come from two different organizations resulting in differing distinguised name patterns.

  • Users from example.com always have a single common name (CN) identifying their user id, followed by the organizational unit (OU) and the domain name (DC). For example: cn=jsmith,ou=Developers,dc=example,dc=com.
  • Users from acquired.com have varying numbers of common name (CN) prefixes, but the the organizational unit is the field we are interested in. For example: cn=ProductDev,cn=abell,ou=Engineering,dc=acquired,dc=com.
  • Both organizations denote software engineers with ou=Developers.

We want to map engineers in both organizations to the RESTful Services and SQL Developer roles

  • The first rule maps engineers in the example.com organization to the intermediate Developers role.
  • The second rule maps engineers in the acquired.com organization to the intermediate Developers role.
  • The final rule maps from the intermediate Developers role to the RESTful Services and SQL Developer roles.