Skip to content

Commit

Permalink
Introduce FAPI policy
Browse files Browse the repository at this point in the history
  • Loading branch information
tkan145 committed Jun 6, 2024
1 parent 7e7eaf6 commit e83e17f
Show file tree
Hide file tree
Showing 6 changed files with 318 additions and 0 deletions.
16 changes: 16 additions & 0 deletions gateway/src/apicast/policy/fapi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# FAPI Policy

## Description

The FAPI policy supports various features of the Financial-grade API (FAPI) standard.

## Example configuration

```
"policy_chain": [
{ "name": "apicast.policy.fapi", "configuration": {} },
{
"name": "apicast.policy.apicast"
}
]
```
12 changes: 12 additions & 0 deletions gateway/src/apicast/policy/fapi/apicast-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"$schema": "http://apicast.io/policy-v1/schema#manifest#",
"name": "The Financial-grade API (FAPI)",
"summary": "Support FAPI profiles",
"description": ["This policy adding support for Financial-grade API (API) profiles"
],
"version": "builtin",
"configuration": {
"type": "object",
"properties": {}
}
}
29 changes: 29 additions & 0 deletions gateway/src/apicast/policy/fapi/fapi.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
--- Financial-grade API (FAPI) policy

local policy = require('apicast.policy')
local _M = policy.new('Financial-grade API (FAPI) Policy', 'builtin')

local uuid = require 'resty.jit-uuid'

local new = _M.new
local X_FAPI_TRANSACTION_ID_HEADER = "x-fapi-transaction-id"

function _M.new(config)
local self = new(config)
return self
end

function _M:header_filter()
-- Get x-fapi-transaction-id from the request
local transaction_id = ngx.req.get_headers()[X_FAPI_TRANSACTION_ID_HEADER]
if not transaction_id or transaction_id == "" then
-- Nothing found, generate one
transaction_id = ngx.resp.get_headers()[X_FAPI_TRANSACTION_ID_HEADER]
if not transaction_id or transaction_id == "" then
transaction_id = uuid.generate_v4()
end
end
ngx.header[X_FAPI_TRANSACTION_ID_HEADER] = transaction_id
end

return _M
1 change: 1 addition & 0 deletions gateway/src/apicast/policy/fapi/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
return require('fapi')
54 changes: 54 additions & 0 deletions spec/policy/fapi/fapi_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
local FAPIPolicy = require('apicast.policy.fapi')
local uuid = require('resty.jit-uuid')

describe('fapi_1_baseline_profile policy', function()
local ngx_req_headers = {}
local ngx_resp_headers = {}
local context = {}
before_each(function()
ngx.header = {}
ngx_req_headers = {}
ngx_resp_headers = {}
context = {}
stub(ngx.req, 'get_headers', function() return ngx_req_headers end)
stub(ngx.req, 'set_header', function(name, value) ngx_req_headers[name] = value end)
stub(ngx.resp, 'get_headers', function() return ngx_resp_headers end)
stub(ngx.resp, 'set_header', function(name, value) ngx_resp_headers[name] = value end)
end)

describe('.new', function()
it('works without configuration', function()
assert(FAPIPolicy.new({}))
end)
end)

describe('.header_filter', function()
it('Use value from request', function()
ngx_req_headers['x-fapi-transaction-id'] = 'abc'
local transaction_id_policy = FAPIPolicy.new({})
transaction_id_policy:header_filter()
assert.same('abc', ngx.header['x-fapi-transaction-id'])
end)

it('Only use x-fapi-transaction-id from request if the header also exist in response from upstream', function()
ngx_req_headers['x-fapi-transaction-id'] = 'abc'
ngx_resp_headers['x-fapi-transaction-id'] = 'bdf'
local transaction_id_policy = FAPIPolicy.new({})
transaction_id_policy:header_filter()
assert.same('abc', ngx.header['x-fapi-transaction-id'])
end)

it('Use x-fapi-transaction-id from upstream response', function()
ngx_resp_headers['x-fapi-transaction-id'] = 'abc'
local transaction_id_policy = FAPIPolicy.new({})
transaction_id_policy:header_filter()
assert.same('abc', ngx.header['x-fapi-transaction-id'])
end)

it('generate uuid if header does not exist in both request and response', function()
local transaction_id_policy = FAPIPolicy.new({})
transaction_id_policy:header_filter()
assert.is_true(uuid.is_valid(ngx.header['x-fapi-transaction-id']))
end)
end)
end)
206 changes: 206 additions & 0 deletions t/apicast-policy-fapi.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
use lib 't';
use Test::APIcast::Blackbox 'no_plan';

# Test::Nginx does not allow to grep access logs, so we redirect them to
# stderr to be able to use "grep_error_log" by setting APICAST_ACCESS_LOG_FILE

run_tests();

__DATA__

=== TEST 1: Enables fapi policy inject x-fapi-transaction-id header to the response
--- configuration
{
"services": [
{
"id": 42,
"backend_version": 1,
"backend_authentication_type": "service_token",
"backend_authentication_value": "token-value",
"proxy": {
"api_backend": "http://test:$TEST_NGINX_SERVER_PORT/",
"proxy_rules": [
{ "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 1 }
],
"policy_chain": [
{
"name": "apicast.policy.fapi", "configuration": {}
},
{
"name": "apicast.policy.apicast"
}
]
}
}
]
}
--- backend
location /transactions/authrep.xml {
content_by_lua_block {
ngx.exit(200)
}
}
--- upstream
location / {
content_by_lua_block {
ngx.exit(200)
}
}
--- more_headers
x-fapi-transaction-id: abc
--- response_headers
x-fapi-transaction-id: abc
--- request
GET /?user_key=value
--- error_code: 200
--- no_error_log
[error]



=== TEST 2: When x-fapi-transaction-id exist in both request and response headers, always use
value from request
--- configuration
{
"services": [
{
"id": 42,
"backend_version": 1,
"backend_authentication_type": "service_token",
"backend_authentication_value": "token-value",
"proxy": {
"api_backend": "http://test:$TEST_NGINX_SERVER_PORT/",
"proxy_rules": [
{ "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 1 }
],
"policy_chain": [
{
"name": "apicast.policy.fapi"
},
{
"name": "apicast.policy.apicast"
}
]
}
}
]
}
--- backend
location /transactions/authrep.xml {
content_by_lua_block {
ngx.exit(200)
}
}
--- upstream
location / {
content_by_lua_block {
ngx.header['x-fapi-transaction-id'] = "blah"
ngx.exit(200)
}
}
--- more_headers
x-fapi-transaction-id: abc
--- request
GET /?user_key=value
--- response_headers
x-fapi-transaction-id: abc
--- error_code: 200
--- no_error_log
[error]



=== TEST 3: Use x-fapi-transaction-id header from upstream response
--- configuration
{
"services": [
{
"id": 42,
"backend_version": 1,
"backend_authentication_type": "service_token",
"backend_authentication_value": "token-value",
"proxy": {
"api_backend": "http://test:$TEST_NGINX_SERVER_PORT/",
"proxy_rules": [
{ "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 1 }
],
"policy_chain": [
{
"name": "apicast.policy.fapi"
},
{
"name": "apicast.policy.apicast"
}
]
}
}
]
}
--- backend
location /transactions/authrep.xml {
content_by_lua_block {
ngx.exit(200)
}
}
--- upstream
location / {
content_by_lua_block {
ngx.header['x-fapi-transaction-id'] = "blah"
ngx.exit(200)
}
}
--- request
GET /?user_key=value
--- response_headers
x-fapi-transaction-id: blah
--- error_code: 200
--- no_error_log
[error]



=== TEST 4: inject uuid to the response header
--- configuration
{
"services": [
{
"id": 42,
"backend_version": 1,
"backend_authentication_type": "service_token",
"backend_authentication_value": "token-value",
"proxy": {
"api_backend": "http://test:$TEST_NGINX_SERVER_PORT/",
"proxy_rules": [
{ "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 1 }
],
"policy_chain": [
{
"name": "apicast.policy.fapi"
},
{
"name": "apicast.policy.apicast"
}
]
}
}
]
}
--- backend
location /transactions/authrep.xml {
content_by_lua_block {
ngx.exit(200)
}
}
--- upstream
location / {
content_by_lua_block {
ngx.exit(200)
}
}
--- request
GET /?user_key=value
--- response_headers_like
x-fapi-transaction-id: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
--- error_code: 200
--- no_error_log
[error]

0 comments on commit e83e17f

Please sign in to comment.