Introduction to OpenIG (Part 5: Ease your development with Routes)

A route is an OpenIG configuration fragment that supports hot reloading. This is the perfect tool for developing your configuration, trying it, fixing it with a very fast feedback. This is also very handy for managing your configuration complexity by splitting it into smaller, more cohesive, sections.

Route

Here is a route configuration file ... just to give you a taste.

Except for the condition property, it's really looking like a regular config.json content, right ?

You'll recognise some attributes from the main configuration file:

  • heap/objects: where you describe all of your route components and how they're linked to each others
  • handler: specify the main entry point of your route
  • baseURI: where the request should be re-routed (optional)

Inheritance

An interesting feature of routes is that they inherit objects from their parent route (config.json content being the primary route, ancestor of all others). That means that any named object defined in a parent config file (let's say config.json) can be re-used by the child route defined components:

Assuming that config.json defines a ClientHandler instance named Forwarder, someone could write in his own route that would includes this Chain:

{
  "name": "OutgoingChain",
  "type": "Chain",
  "config": {
    "filters": [ ],
    "handler": "Forwarder"
  }
}

This feature makes it very easy to share pieces of configuration logic in a common/shared place (the parent route or config.json).

Isolation

Each route is having a dedicated namespace (some kind of private area) where objects declared in its heap/objects array are kept. That means that a route cannot access objects defined in another route (except if the object is declared in the parent route, thanks to inheritance), effectively providing content isolation.

That's utmost useful for multiple reasons:

  • When writing the configuration, errors may happen (we cannot always be right on the first shot), having isolation limits the number of issues that may arise due to unmanaged (or unseen) dependencies.
  • When hot-reloading is enabled (the default), this permits fragmented update of your system (just update what you need, not the whole configuration)

Exchange processing

The heap/objects array contains all of your route components (declaration, configuration and bindings with other components). One of the declared Handler has to be referenced through the handler top level attribute:

{
  "heap": {
    "objects": [
      {
        "name": "EntryPoint",
        "type": "StaticResponseHandler",
        "config": {
          "status": 200
        }
      }
    ]
  },
  "handler": "EntryPoint"
}

Notice that the handler attribute is required, an error will be thrown if not set.

Conditional execution

Unlike config.json, a route is conditionally invoked given the result of a condition:

{
  "condition": "${exchange.request.form['forward'] == true}"
}

The condition is expressed as an Expression and gives you access to all of the properties of the Exchange being processed. It has to return a boolean (any other return type will be considered as false).

If the condition attribute is absent (or null), the route will always accept the Exchange (if proposed).

Here are some examples of useful conditions:

  • Only requests whose paths are starting with /wordpress/:
{
  "condition": "${matches(exchange.request.uri, '^/wordpress/')}"
}
  • Requests paths starting with a value or another:
  {
    "condition": "${matches(exchange.request.uri.path, '^(/carousel|/openid)')}"
  }

Uri Rebasing

When an Exchange is accepted in a route, its request.uri may be rebased (if there is a baseURI top level attribute). Notice this is the very first thing happening to the Exchange inside of the route (even before being handled by your main handler object).

Activation / Deactivation

The RouterHandler (which is the heap object managing routes) is observing a given directory (${openig.base}/routes/ unless specified to something else) for route files. Each file that ends with .json is considered as a route and is tried to be activated. If everything goes well, the new route is available into the system, otherwise, an error is displayed into the logs.

Activating a route is as simple as dropping a .json file in the right folder.

And deactivating as simple as removing that file.

Notice that you can simply rename the file with a different extension to have it ignored by the system (and thus uninstalled if it was previously active):

routes/
  wordpress.json -> wordpress.json.disabled

Scan interval

The route file scan is done at most 1 time per scanInterval (default value to 10 seconds) and that this scan is not done by a background thread but by the thread that handle the request (ie don't wait for a log message saying a new route was discovered :) ).

Ordering

The RouteHandler imposes a lexicographical order when trying to hand-off the current Exchange to one of the available route. The file name is used as a default value when there is no name top level attribute in the route configuration.

For example, given the following routes/ folder content (no name attribute defined in any of the routes), the routes would be tried in this order: 00-main.json, next.json and zz-default.json.

routes/
  00-main.json    (1)
  zz-default.json (3)
  next.json       (2)

You can override the default system provided name (based on the file name) by specifying a name attribute in the route:

{
  "name": "my-name"
}

Conclusion

Routes is a very handy tool for OpenIG users, there is even a global Router in the default configuration: just drop your json files in ${openig.base}/routes/ and you're good to go !

Next

In the next post we'll talk about OAuth 2.0.

Introduction to OpenIG (Part 4: Troubleshooting)

As transformations are dictated by the set of filters/handlers in your configuration, they are not always trivial, it's becoming very important quickly to capture the messages at different phases of the processing.

See the flow

First thing to understand when trying to debug a configuration is "where the hell are all my messages going ?" :)

This is achievable simply by activating DEBUG traces in your LogSink heap object:

When the DEBUG traces are on, you should see something like:

You'll see a new line each time an Exchanges comes into a Handler/Filter and each time it's flowing out of the element (you also get a performance measurement).

Capture the messages (requests/responses)

OpenIG provides a simple, way to see the HTTP message (being a request or a response), including both headers and (optionaly) the entity (if that's a textual content): the CaptureFilter.

Here is an output example you can obtain when you install a CaptureFilter:

Install a CaptureFilter

Being a filter, it has to be installed as part of a Chain:

It is usually best placed either as the OpenIG entry point (the first element to be invoked), that helps to see what the User-Agent sends and receives (as it's perceived by OpenIG) or just before a ClientHandler (that represents a sort of endpoint, usually your protected application).

Capture what you want

CaptureFilter is sufficient for simple capturing needs. When what you want to observe is not contained in the HTTP message, we have to use the OpenIG swiss-knife: ScriptableFilter.

This is a special filter that allows you to execute a Groovy script when traversed by an Exchange.

Here is a sample script that prints the content of the Exchange's session:

Copy this script into ~/.openig/scripts/groovy/PrintSessionFilter.groovy and configure your heap object:

Seeing the messages on wire

Sometimes, all of the previous solutions are not applicable, because you want to see the on-wire message content (as opposed to modelled by OpenIG).

For this case, the only solution is to start your OpenIG with a couple of system properties that will activate deep traces of the http client library we're using: Apache HTTP Client.

>$ bin/catalina.sh -Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog 
                   -Dorg.apache.commons.logging.simplelog.log.httpclient.wire=debug 
                   -Dorg.apache.commons.logging.simplelog.log.org.apache.commons.httpclient=debug 
                   run

See the HTTP Client Logging page for more informations.

Next

In the next post we'll explain how routes can speed-up your configuration preparation.

Introduction to OpenIG (Part 3: Concepts)

The previous posts exposed you to OpenIG: use cases and an initial configuration example. Before going further, I would like to introduce the underlying concepts that you should understand.

HTTP Exchanges Requests and Responses

OpenIG is a specialized HTTP reverse-proxy: it only deals with HTTP messages. The purpose of OpenIG is to give a complete control over the messages flowing through itself (incoming requests and outgoing responses).

In order to ease handling of the whole message processing (request and response), OpenIG uses the concept of Exchange: it's a simple way to link a Request and a Response together. It also contains a Session instance that can be used to store session-scoped properties. The Exchange itself is a Map so it's a natural container for request-scoped properties.

When a request comes through OpenIG, an Exchange object is created and populated with an initial Request and a Session instance.

A Request is the OpenIG model for an incoming HTTP message, it captures both the message's entity and all of its headers. It also has dedicated accessors for message's target URI, cookies and form/query parameters.

A Response is the complementary model object for outgoing HTTP messages. In addition to the entity and headers accessors, Response provides setter for the HTTP status code (20x -> ok, 30x -> redirect, 50x -> server error, ...).

Exchange processing

Now we have a model of the message's content, what can we do to apply transformations to it?

OpenIG offers a simple, but powerful, API to process exchanges:

  • Handler that are responsible to produce a Response object into the Exchange
  • Filter that can intercept the flowing Exchange (incoming and outgoing)

Handler

What do the doc says about Handler.handle() ?

Called to request the handler respond to the request.

A handler that doesn't hand-off an exchange to another handler downstream is responsible for creating the response in the exchange object.

Hmmm, an example would help, right ?

OpenIG offers a rich set of Handlers, the most significative one would be the ClientHandler. This is a highly used component usually ending Exchange's processing that simply forward the Request to the required URI and wraps the returned HTTP message into the Exchange's Response.

In other words, it acts as a client to the protected resource (hence the name).

Filter

Again, what do the doc says about Filter.filter() ?

Filters the request and/or response of an exchange.

Initially, exchange.request contains the request to be filtered. To pass the request to the next filter or handler in the chain, the filter calls next.handle(exchange). After this call, exchange.response contains the response that can be filtered.

This method may elect not to pass the request to the next filter or handler, and instead handle the request itself. It can achieve this by merely avoiding a call to next.handle(exchange) and creating its own response object the exchange. The filter is also at liberty to replace a response with another of its own after the call to next.handle(exchange).

This is easier to understand I think, everyone is used to interceptors nowadays...

The traditional example of a Filter is the CaptureFilter: this filter simply prints the content of the incoming request, then call the next handler in chain and finally prints the outgoing response's content.

Filters are contained inside a special Handler called a Chain. The chain is responsible to sequentially invoke each of the filter declared in its configuration, before handing the flow to its terminal Handler.

All together: a Chain example

If you have your OpenIG up and running, please shut it down and replace its configuration file (config.json) with the following content:

Compared to previous, this configuration will enhance the response message with an additional HTTP header named X-Hello.

The message flow is depicted in the following diagram:

Exchange Flow

A Filter can intercept both the Request and the Reponse flows. In this case, our HeaderFilter is configured to only act on the response flow (because the outgoing message is a handy way to observe a filter in action from an outside perspective).

Wrap up

OpenIG provides a low-level HTTP model API that let you alter HTTP messages in many ways. The message processing is handled through some kind of pipeline composed of handlers and filters.

All the processing logic you want to apply to your messages finally depends on the way you compose your handlers and filters together.

Next

In the next post we'll see how to debug your configurations.

Introduction to OpenIG (Part 2: First contact…)

What do I need to start ?

First, you need to install OpenIG in the root context of a web container (Tomcat used for the example):

  1. Download OpenIG
  2. Grab a fresh Apache Tomcat instance (Tomcat 7.0 or 8.0) and unzip it
  3. Remove all war files from the webapps/ directory
  4. Move OpenIG-3.0.0.war to that directory and rename it to ROOT.war (OpenIG needs to be installed in the root context: /)
  5. Start Tomcat (bin/catalina.sh run from within the tomcat directory).

    Don't worry if you see some warnings about Offending classes during startup: that's because OpenIG web-app provides its own EL implementation (useful when the web container doesn't provide one). This pollutes the startup logs but does not prevent OpenIG to run properly.

If you're curious, you can go to http://localhost:8080 (or whatever port number you're using), you should see the OpenIG's welcome page (admire the ASCII art).

OpenIG 3.0.0 - Welcome Page

If you don't see anything, or an error, make sure that you don't have a previous OpenIG configuration file in $HOME/.openig/config/config.json ($APP_DATAForgeRockOpenIGconfigconfig.json for our windows users).

You might wonder why OpenIG requires to be installed in the root context. The reason is simple: most of the time, when you want to access a web site, you just wanna type a domain name in your browser navbar (like http://app.example.com).

That gives you a very easy to use and to remember entry point URL as well as the flexibility of mapping incoming requests to any internal URL (really routing requests in fact).

Your first configuration

What would you think of an OpenIG Hello World example? After all that reading, I'm sure you'll be happy to get your hands dirty :)

So, finally some code (let's face it; it's JSON configuration really).

Place that configuration file inside the $HOME/.openig/config/routes/ directory. It will be auto-(re)loaded by OpenIG.

It's quite self-descriptive: if the incoming request's path matches ^/hello (starts with /hello), it is dispatched to this route (a JSON file placed in the routes/ directory is called a route). This route is configured with a single StaticResponseHandler that simply return a Hello World message (we'll see what a Handler is in an upcoming post) to the user-agent.

OpenIG Hello World

At first look, that doesn't seem very fancy, but you've been exposed to multiple core concepts of OpenIG (they'll be covered in more details in subsequent posts):

  • The OpenIG configuration file format (expressed as JSON)
  • The expression language (${} for expressing conditions based the value of some properties)
  • The Exchange HTTP model (exchange.request.uri.path in the expression)
  • The route concept (a simple way to isolate processing chains in different files and to ease setup with auto-(re)loading)

In the next post, we'll have a look at the concepts providing the backbone of OpenIG (Exchange, Handler and Filter).

See you soon.

Introduction to OpenIG (Part 1: Use cases)

Welcome

You've probably landed here because you want to know something about OpenIG. This is the right place to be :)

This post is the first one of a serie of OpenIG-related articles that would gives you hand-on samples with explanations.

Identity Gateway

OpenIG stands for Open Identity Gateway, it is an identity/security specialized reverse proxy. That means that it control the access to a set of internal services.

By controling the access, I mean that it intercepts all the requests coming to the protected service (be it a RESTful API or a web application) and process them before (and after) forwarding them to the server.

Different kind of processing can be handled:

  • Request authorization
  • Capture and password replay
  • Message logging
  • Transformation

Commons use cases

Ok, that was a bit of a generalist description (transformation is intentionally vague :) ). Having some real-life use cases will help to have a better feeling/understanding of what OpenIG is capable of.

SAML Application Federation

In this use case, OpenIG acts as a SAML-enabled facade to a somehow legacy application that cannot be adapted to support SAML federation. The Identity Provider (could be OpenAM) will consider OpenIG as a standard SAML Service Provider.

SAML CoT with OpenIG used as a facade to a legacy application

Application Authentication

Here, OpenIG acts as an OpenID Connect Relying Party (OIDC terminology for client) and requires the user to authenticate to an OpenID Connect Provider (the identity provider) before giving him/her access to the protected application.

Authenticated user's profile information (such as name, email, address, picture, ...) are available to enrich the user experience, or make further verifications.

OpenID Connect - OpenIG Relying Party

RESTful Services Protection

This simple case shows OpenIG verifying request to a proxified RESTful API: each request must contains a valid OAuth 2.0 Bearer Token to be allowed to reach the service API. In this case, OpenIG acts as an OAuth 2.0 Resource Server.

Very useful if you have an old-fashioned REST API that you cannot easily update to deal with OAuth 2.0 tokens.

OAuth 2.0 - OpenIG ResourceServer

Not enough ?

Well, we're done with the 'marketing' stuff. In the next post, we will start to play with OpenIG.

See you soon!