Decorators

Decorators are heap objects to extend what another object can do. IG defines baseURI, capture, and timer decorators that you can use without explicitly configuring them. For more information about the types of decorators provided by IG, see Decorators.

The following sections provide an overview of how decorators are implemented in IG:

Decorating Objects, the Route Handler, and the Heap

Use decorations that are compatible with the object type. For example, timer records the time to process filters and handlers, but does not record information for other object types. Similarly, baseURI overrides the scheme, host, and ports, but has no other effect.

In a route, you can decorate individual objects, the route handler, and the heap. IG applies decorations in this order:

  1. Decorations declared on individual objects. Local decorations that are part of an object's declaration are inherited wherever the object is used.

  2. globalDecorations declared in parent routes, then in child routes, and then in the current route.

  3. Decorations declared on the route handler.

The following sections describes where to place decorators in a route.

Decorating Individual Objects In a Route

To decorate individual objects, add the decorator's name value as a top-level field of the object, next to type and config.

In this example, the decorator captures all requests going into the SingleSignOnFilter, and all responses coming out of the SingleSignOnFilter:

{
  "heap": [
    {
      "name": "SystemAndEnvSecretStore-1",
      "type": "SystemAndEnvSecretStore"
    },
    {
      "name": "AmService-1",
      "type": "AmService",
      "config": {
        "agent": {
          "username": "ig_agent",
          "passwordSecretId": "agent.secret.id"
        },
        "secretsProvider": "SystemAndEnvSecretStore-1",
        "url": "http://openam.example.com:8088/openam/",
        "version": "7"
      }
    }
  ],
  "handler": {
    "type": "Chain",
    "config": {
      "filters": [
        {
          "capture": "all",
          "type": "SingleSignOnFilter",
          "config": {
            "amService": "AmService-1"
          }
        }
      ],
      "handler": "ReverseProxyHandler"
    }
  }
}

Decorating the Route Handler

To decorate the handler for a route, add the decorator as a top-level field of the route.

In this example, the decorator captures all requests and responses that traverse the route:

{
  "heap": [
    {
      "name": "SystemAndEnvSecretStore-1",
      "type": "SystemAndEnvSecretStore"
    },
    {
      "name": "AmService-1",
      "type": "AmService",
      "config": {
        "agent" : {
          "username" : "ig_agent",
          "passwordSecretId" : "agent.secret.id"
        },
        "secretsProvider": "SystemAndEnvSecretStore-1",
        "url": "http://openam.example.com:8088/openam/",
        "version": "7"
      }
    }
  ],
  "capture": "all",
  "handler": {
    "type": "Chain",
    "config": {
      "filters": [
        {
          "type": "SingleSignOnFilter",
          "config": {
            "amService": "AmService-1"
          }
        }
      ],
      "handler": "ReverseProxyHandler"
    }
  }
}

Decorating the Route Heap

To decorate all compatible objects in a route, configure globalDecorators as a top-level field of the route. The globalDecorators field takes a map of the decorations to apply.

To decorate all compatible objects declared in config.json or admin.json, configure globalDecorators as a top-level field in config.json or admin.json.

In the following example, the route has capture and timer decorations. The capture decoration applies to AmService, Chain, SingleSignOnFilter, and ReverseProxyHandler. The timer decoration doesn't apply to AmService because it is not a filter or handler, but does apply to Chain, SingleSignOnFilter, and ReverseProxyHandler:

{
  "globalDecorators":
  {
    "capture": "all",
    "timer": true
  },
  "heap": [
    {
      "name": "SystemAndEnvSecretStore-1",
      "type": "SystemAndEnvSecretStore"
    },
    {
      "name": "AmService-1",
      "type": "AmService",
      "config": {
        "agent": {
          "username": "ig_agent",
          "passwordSecretId": "agent.secret.id"
        },
        "secretsProvider": "SystemAndEnvSecretStore-1",
        "url": "http://openam.example.com:8088/openam/",
        "version": "7"
      }
    }
  ],
  "handler": {
    "type": "Chain",
    "config": {
      "filters": [
        {
          "type": "SingleSignOnFilter",
          "config": {
            "amService": "AmService-1"
          }
        }
      ],
      "handler": "ReverseProxyHandler"
    }
  }
}

Decorating a Named Object Differently In Different Parts of the Configuration

When a filter or handler is configured in config.json or in the heap, it can be used many times in the configuration. To decorate each use of the filter or handler individually, use a Delegate. For more information, see "Delegate"

In the following example, an AmService heap object configures an amHandler to delegate tasks to ForgeRockClientHandler, and capture all requests and responses passing through the handler.

{
  "type": "AmService",
  "config": {
    "agent" : {
      "username" : "ig_agent",
      "passwordSecretId" : "agent.secret.id"
      },
    "secretsProvider": "SystemAndEnvSecretStore-1",
    "amHandler": {
      "type": "Delegate",
      "capture": "all",
      "config": {
        "delegate": "ForgeRockClientHandler"
      }
    },
    "url": "http://openam.example.com:8088/openam"
  }
}

You can use the same ForgeRockClientHandler in another part of the configuration, in a different route for example, without adding a capture decorator. Requests and responses that pass through that use of the handler are not captured.

Decorating IG's Interactions With AM

To log interactions between IG and AM, delegate message handling to a ForgeRockClientHandler, and capture the requests and responses passing through the handler. When the ForgeRockClientHandler communicates with an application, it sends ForgeRock Common Audit transaction IDs.

In the following example, the accessTokenResolver delegates message handling to a decorated ForgeRockClientHandler:

"accessTokenResolver": {
  "name": "token-resolver-1",
  "type": "TokenIntrospectionAccessTokenResolver",
  "config": {
    "amService": "AmService-1",
    "providerHandler": {
      "capture": "all",
      "type": "Delegate",
      "config": {
        "delegate": "ForgeRockClientHandler"
      }
    }
  }
}

To try the example, replace the accessTokenResolver in the IG route of "Validating Access_Tokens Through the Introspection Endpoint". Test the setup as described for the example, and note that the route's log file contains an HTTP call to the introspection endpoint.

Using Multiple Decorators for the Same Object

Decorations can apply more than once. For example, if you set a decoration on a route and another decoration on an object defined within the route, IG applies the decoration twice. In the following route, the request is captured twice:

{
 "handler": {
   "type": "ReverseProxyHandler",
   "capture": "request"
 },
 "capture": "all"
}

When an object has multiple decorations, the decorations are applied in the order they appear in the JSON.

In the following route, the handler is decorated with a baseURI first, and a capture second:

{
  "name": "myroute",
  "baseURI": "http://app.example.com:8081",
  "capture": "all",
  "handler": {
    "type": "StaticResponseHandler",
    "config": {
      "status": 200,
      "reason": "OK",
      "headers": {
        "Content-Type": [ "text/plain" ]
      },
      "entity": "Hello world, from myroute!"
    }
  },
  "condition": "${matches(request.uri.path, '^/myroute1')}"
}

The decoration can be represented as capture[ baseUri[ handler ] ]. When a request is processed, it is captured, and then rebased, and then processed by the handler: The log for this route shows that the capture occurs before the rebase:

2018-09-10T13:23:18,990Z | INFO  | http-nio-8080-exec-1 | o.f.o.d.c.C.c.top-level-handler | @myroute |

--- (request) id:f792d2ad-4409-4907-bc46-28e1c3c19ac3-7 --->

GET http://openig.example.com:8080/myroute HTTP/1.1
...

Conversely, in the following route, the handler is decorated with a capture first, and a baseURI second:

{
  "name": "myroute",
  "capture": "all",
  "baseURI": "http://app.example.com:8081",
  "handler": {
    "type": "StaticResponseHandler",
    "config": {
      "status": 200,
      "reason": "OK",
      "headers": {
        "Content-Type": [ "text/plain" ]
      }
      "entity": "Hello, world from myroute1!"
    }
  },
  "condition": "${matches(request.uri.path, '^/myroute')}"
}

The decoration can be represented as baseUri[ capture[ handler ] ]. When a request is processed, it is rebased, and then captured, and then processed by the handler. The log for this route shows that the rebase occurs before the capture:

2018-09-10T13:07:07,524Z | INFO  | http-nio-8080-exec-1 | o.f.o.d.c.C.c.top-level-handler | @myroute |

--- (request) id:3c26ab12-3cc0-403e-bec6-43bf5621f657-7 --->

GET http://app.example.com:8081/myroute HTTP/1.1
...

Guidelines for Naming Decorators

To prevent unwanted behavior, consider the following points when you name decorators:

  • Avoid decorators named comment or comments, and avoid reserved field names. Instead of using alphanumeric field names, consider using dots in your decorator names, such as my.decorator.

  • For heap objects, avoid the reserved names config, name, and type.

  • For routes, avoid the reserved names auditService, baseURI, condition, globalDecorators, heap, handler, name, secrets, and session.

  • In config.json, avoid the reserved name temporaryStorage.

Read a different version of :