diff --git a/src/lua/api-gateway/validation/key/redisApiKeyValidator.lua b/src/lua/api-gateway/validation/key/redisApiKeyValidator.lua index 78c7e64..8d6cb6f 100644 --- a/src/lua/api-gateway/validation/key/redisApiKeyValidator.lua +++ b/src/lua/api-gateway/validation/key/redisApiKeyValidator.lua @@ -41,14 +41,24 @@ local RedisHealthCheck = require "api-gateway.redis.redisHealthCheck" local ApiKeyValidator = BaseValidator:new() +local super = { + instance = BaseValidator, + getKeyFromRedis = BaseValidator.getKeyFromRedis, + setKeyInRedis = BaseValidator.setKeyInRedis +} + local RESPONSES = { MISSING_KEY = { error_code = "403000", message = "Api KEY is missing" }, INVALID_KEY = { error_code = "403003", message = "Api KEY is invalid" }, UNKNOWN_ERROR = { error_code = "503000", message = "Could not validate API KEY"} } -function ApiKeyValidator:getKeyFromRedis(hashed_key) - local redis_key = "cachedkey:" .. hashed_key; +--- @Deprecated +-- Returns a set of fields associated to the api-key from Redis, if the key exists +-- @param hashed_key +-- +function ApiKeyValidator:getLegacyKeyFromRedis(redis_key) + ngx.log(ngx.DEBUG, "Looking for a legacy api-key in Redis") local red = redis:new(); local redis_host, redis_port = self:getRedisUpstream() @@ -86,6 +96,21 @@ function ApiKeyValidator:getKeyFromRedis(hashed_key) end end +function ApiKeyValidator:getKeyFromRedis(hashed_key) + local redis_key = "cachedkey:" .. hashed_key; + --1. try to read the key in the new format + local redis_metadata = super.getKeyFromRedis(ApiKeyValidator, redis_key, "metadata") + if redis_metadata ~= nil then + ngx.log(ngx.DEBUG, "Found API KEY Metadata in Redis:", tostring(redis_metadata)) + local metadata = assert( cjson.decode(redis_metadata), "Invalid metadata found in Redis:" .. tostring(redis_metadata) ) + if metadata ~= nil then + return metadata + end + end + --2. the key in the new format doesn't exist, try the old format + return self:getLegacyKeyFromRedis(redis_key) +end + function ApiKeyValidator:validate_api_key() local api_key = ngx.var.api_key diff --git a/src/lua/api-gateway/validation/validator.lua b/src/lua/api-gateway/validation/validator.lua index d347cb3..bac55ca 100644 --- a/src/lua/api-gateway/validation/validator.lua +++ b/src/lua/api-gateway/validation/validator.lua @@ -100,7 +100,6 @@ function BaseValidator:getKeyFromRedis(key, hash_name) if ok then local redis_key, selecterror = redisread:hget(key, hash_name) redisread:set_keepalive(30000, 100) - --ngx.log(ngx.WARN, "GOT REDIS RESPONSE:" .. type(redis_key)); if (type(redis_key) == 'string') then return redis_key end @@ -122,7 +121,9 @@ function BaseValidator:setKeyInRedis(key, hash_name, keyexpires, value) --ngx.log(ngx.DEBUG, "WRITING IN REDIS JSON OBJ key=" .. key .. "=" .. value .. ",expiring in:" .. (keyexpires - (os.time() * 1000)) ) rediss:init_pipeline() rediss:hset(key, hash_name, value) - rediss:pexpireat(key, keyexpires) + if keyexpires ~= nil then + rediss:pexpireat(key, keyexpires) + end local commit_res, commit_err = rediss:commit_pipeline() rediss:set_keepalive(30000, 100) --ngx.log(ngx.WARN, "SAVE RESULT:" .. cjson.encode(commit_res) ) diff --git a/test/perl/api-gateway/validation/key/apiKeyValidator.t b/test/perl/api-gateway/validation/key/apiKeyValidator.t new file mode 100644 index 0000000..2bcd102 --- /dev/null +++ b/test/perl/api-gateway/validation/key/apiKeyValidator.t @@ -0,0 +1,367 @@ +#/* +# * Copyright (c) 2015 Adobe Systems Incorporated. All rights reserved. +# * +# * Permission is hereby granted, free of charge, to any person obtaining a +# * copy of this software and associated documentation files (the "Software"), +# * to deal in the Software without restriction, including without limitation +# * the rights to use, copy, modify, merge, publish, distribute, sublicense, +# * and/or sell copies of the Software, and to permit persons to whom the +# * Software is furnished to do so, subject to the following conditions: +# * +# * The above copyright notice and this permission notice shall be included in +# * all copies or substantial portions of the Software. +# * +# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# * DEALINGS IN THE SOFTWARE. +# * +# */ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use lib 'lib'; +use Test::Nginx::Socket::Lua; +use Cwd qw(cwd); + +#worker_connections(1014); +#master_process_enabled(1); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 4) + 18; + +my $pwd = cwd(); + +our $HttpConfig = <<_EOC_; + # lua_package_path "$pwd/scripts/?.lua;;"; + lua_package_path "src/lua/?.lua;/usr/local/lib/lua/?.lua;;"; + init_by_lua ' + local v = require "jit.v" + v.on("$Test::Nginx::Util::ErrLogFile") + require "resty.core" + '; + init_worker_by_lua ' + ngx.apiGateway = ngx.apiGateway or {} + ngx.apiGateway.validation = require "api-gateway.validation.factory" + '; + lua_shared_dict cachedkeys 50m; # caches api-keys + include ../../api-gateway/redis-upstream.conf; +_EOC_ + +#no_diff(); +no_long_string(); +run_tests(); + +__DATA__ + +=== TEST 1: test api_key is saved in redis +--- http_config eval: $::HttpConfig +--- config + include ../../api-gateway/api_key_service.conf; + error_log ../test-logs/apiKeyValidator_test1_error.log debug; + +--- more_headers +X-Test: test +--- request +POST /cache/api_key?key=key-123&service_id=s-123 +--- response_body eval +['{ + "key":"key-123", + "key_secret":"-", + "realm":"sandbox", + "service_id":"s-123", + "service_name":"_undefined_", + "consumer_org_name":"_undefined_", + "app_name":"_undefined_", + "plan_name":"_undefined_" + } +'] +--- error_code: 200 +--- no_error_log +[error] + +=== TEST 2: check request without api_key parameter is rejected +--- http_config eval: $::HttpConfig +--- config + include ../../api-gateway/api_key_service.conf; + include ../../api-gateway/default_validators.conf; + error_log ../test-logs/apiKeyValidator_test2_error.log debug; + + location /test-api-key { + set $service_id s-123; + + set $api_key $arg_api_key; + set_if_empty $api_key $http_x_api_key; + + set $validate_api_key on; + + access_by_lua "ngx.apiGateway.validation.validateRequest()"; + content_by_lua "ngx.say('api-key is valid.')"; + } +--- request +GET /test-api-key +--- response_body_like: {"error_code":"403000","message":"Api Key is required"} +--- error_code: 403 +--- no_error_log +[error] + +=== TEST 3: check request with invalid api_key is rejected +--- http_config eval: $::HttpConfig +--- config + include ../../api-gateway/api_key_service.conf; + include ../../api-gateway/default_validators.conf; + error_log ../test-logs/apiKeyValidator_test3_error.log debug; + + location /test-api-key { + set $service_id s-123; + + set $api_key $arg_api_key; + set_if_empty $api_key $http_x_api_key; + + set $validate_api_key on; + + access_by_lua "ngx.apiGateway.validation.validateRequest()"; + content_by_lua "ngx.say('api-key is valid.')"; + } +--- request +GET /test-api-key?api_key=ab123 +--- response_body_like: {"error_code":"403003","message":"Api Key is invalid"} +--- error_code: 403 +--- no_error_log +[error] + +=== TEST 4: test request with valid api_key +--- http_config eval: $::HttpConfig +--- config + include ../../api-gateway/api_key_service.conf; + include ../../api-gateway/default_validators.conf; + error_log ../test-logs/apiKeyValidator_test4_error.log debug; + + location /test-api-key { + set $service_id s-123; + + set $api_key $arg_api_key; + set_if_empty $api_key $http_x_api_key; + + set $validate_api_key on; + + access_by_lua "ngx.apiGateway.validation.validateRequest()"; + content_by_lua "ngx.say('api-key is valid.')"; + } +--- pipelined_requests eval +["POST /cache/api_key?key=test-apikey-1234&service_id=s-123", +"GET /test-api-key?api_key=test-apikey-1234", +"GET /test-api-key?api_key=test-apikey-1234"] +--- response_body eval +['{ + "key":"test-apikey-1234", + "key_secret":"-", + "realm":"sandbox", + "service_id":"s-123", + "service_name":"_undefined_", + "consumer_org_name":"_undefined_", + "app_name":"_undefined_", + "plan_name":"_undefined_" + } +', +"api-key is valid.\n", +"api-key is valid.\n" +] +--- no_error_log + + +=== TEST 5: test that api_key fields are saved in the request variables +--- http_config eval: $::HttpConfig +--- config + include ../../api-gateway/api_key_service.conf; + include ../../api-gateway/default_validators.conf; + error_log ../test-logs/apiKeyValidator_test5_error.log debug; + + location /test-api-key-5 { + set $service_id s-123; + + set $api_key $arg_api_key; + set_if_empty $api_key $http_x_api_key; + + set $validate_api_key on; + + access_by_lua "ngx.apiGateway.validation.validateRequest()"; + content_by_lua " + ngx.say('service_name=' .. ngx.var.service_name .. ',consumer_org_name=' .. ngx.var.consumer_org_name .. ',app_name=' .. ngx.var.app_name .. ',secret=' .. tostring(ngx.var.key_secret) ) + "; + } +--- pipelined_requests eval +["POST /cache/api_key?key=test-apikey-12345&service_id=s-123&service_name=test-service-name&consumer_org_name=test-consumer-name&app_name=test-app-name&secret=my-secret", +"GET /cache/api_key/get?key=test-apikey-12345&service_id=s-123", +"GET /test-api-key-5?api_key=test-apikey-12345"] +--- response_body eval +['{ + "key":"test-apikey-12345", + "key_secret":"my-secret", + "realm":"sandbox", + "service_id":"s-123", + "service_name":"test-service-name", + "consumer_org_name":"test-consumer-name", + "app_name":"test-app-name", + "plan_name":"_undefined_" + } +', +'{"valid":true}' . "\n", +"service_name=test-service-name,consumer_org_name=test-consumer-name,app_name=test-app-name,secret=my-secret\n"] +--- no_error_log + + +=== TEST 6: test debug headers +--- http_config eval: $::HttpConfig +--- config + include ../../api-gateway/api_key_service.conf; + include ../../api-gateway/default_validators.conf; + error_log ../test-logs/apiKeyValidator_test6_error.log debug; + + location /test-api-key { + set $service_id s-123; + + set $api_key $arg_api_key; + set_if_empty $api_key $http_x_api_key; + + set $validate_api_key on; + + access_by_lua "ngx.apiGateway.validation.validateRequest()"; + content_by_lua "ngx.say('api-key is valid.')"; + } +--- pipelined_requests eval +["POST /cache/api_key?key=test-key-123&service_id=s-123", +"GET /test-api-key?api_key=test-key-123&debug=true"] +--- response_body eval +['{ + "key":"test-key-123", + "key_secret":"-", + "realm":"sandbox", + "service_id":"s-123", + "service_name":"_undefined_", + "consumer_org_name":"_undefined_", + "app_name":"_undefined_", + "plan_name":"_undefined_" + } +', +"api-key is valid.\n"] +--- response_headers_like eval +[ +"", +"X-Debug-Validation-Response-Times: /validate_api_key, \\d+ ms, status:200, request_validator \\[order:1\\], \\d+ ms, status:200" +] +--- no_error_log +[error] + + +=== TEST 7: test api-key related field starting with capital H +--- http_config eval: $::HttpConfig +--- config + include ../../api-gateway/api_key_service.conf; + include ../../api-gateway/default_validators.conf; + error_log ../test-logs/apiKeyValidator_test7_error.log debug; + + location /test-api-key { + set $service_id HH-123; + + set $api_key $arg_api_key; + set_if_empty $api_key $http_x_api_key; + + set $validate_api_key on; + + access_by_lua "ngx.apiGateway.validation.validateRequest()"; + content_by_lua "ngx.say('api-key is valid.')"; + } +--- pipelined_requests eval +[ +"POST /cache/api_key?key=H-test-apikey-1234&service_id=HH-123&app_name=HHHH", +"GET /test-api-key?api_key=H-test-apikey-1234&debug=true"] +--- response_body eval +[ +'{ + "key":"H-test-apikey-1234", + "key_secret":"-", + "realm":"sandbox", + "service_id":"HH-123", + "service_name":"_undefined_", + "consumer_org_name":"_undefined_", + "app_name":"HHHH", + "plan_name":"_undefined_" + } +', +"api-key is valid.\n"] +--- response_headers_like eval +[ +"", +"X-Debug-Validation-Response-Times: /validate_api_key, \\d+ ms, status:200, request_validator \\[order:1\\], \\d+ ms, status:200" +] +--- no_error_log +[error] + + + +=== TEST 8: test with more api-key fields +--- http_config eval: $::HttpConfig +--- config + include ../../api-gateway/api_key_service.conf; + include ../../api-gateway/default_validators.conf; + error_log ../test-logs/apiKeyValidator_test5_error.log debug; + + location /custom_api_key { + content_by_lua ' + local cjson = require "cjson" + local BaseValidator = require "api-gateway.validation.validator" + + ngx.req.read_body() + local data = ngx.req.get_body_data() + local metadata = cjson.decode(data) + local validator = BaseValidator:new() + validator:setKeyInRedis("cachedkey:" .. metadata.key .. ":" .. metadata.service_id, "metadata", nil, data) + ngx.say("OK") + '; + } + + location /test-api-key-5 { + set $service_id s-123; + + set $extra_field 'N/A'; + + set $api_key $arg_api_key; + set_if_empty $api_key $http_x_api_key; + + set $validate_api_key on; + + access_by_lua "ngx.apiGateway.validation.validateRequest()"; + content_by_lua " + ngx.say('service_name=' .. ngx.var.service_name .. ',consumer_org_name=' .. ngx.var.consumer_org_name .. ',app_name=' .. ngx.var.app_name .. ',secret=' .. tostring(ngx.var.key_secret) .. ',extra_field=' .. tostring(ngx.var.extra_field) ) + "; + } +--- pipelined_requests eval +[ +'POST /custom_api_key +{ + "key": "test-apikey-8", + "key_secret": "my-secret", + "realm": "sandbox", + "service_id": "s-123", + "service_name": "test-service-name", + "consumer_org_name": "test-consumer-name", + "app_name": "test-app-name", + "plan_name": "_undefined_", + "extra_field": "extra_value", + "extra_field_2": "extra_value_2" +} +', +"GET /cache/api_key/get?key=test-apikey-8&service_id=s-123", +"GET /test-api-key-5?api_key=test-apikey-8"] +--- response_body eval +['OK +', +'{"valid":true}' . "\n", +"service_name=test-service-name,consumer_org_name=test-consumer-name,app_name=test-app-name,secret=my-secret,extra_field=extra_value\n"] +--- no_error_log + + diff --git a/test/perl/api-gateway/validation/key/api_key.t b/test/perl/api-gateway/validation/key/api_key_deprecated.t similarity index 93% rename from test/perl/api-gateway/validation/key/api_key.t rename to test/perl/api-gateway/validation/key/api_key_deprecated.t index 7d63679..e653bb3 100644 --- a/test/perl/api-gateway/validation/key/api_key.t +++ b/test/perl/api-gateway/validation/key/api_key_deprecated.t @@ -1,5 +1,5 @@ #/* -# * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved. +# * Copyright (c) 2015 Adobe Systems Incorporated. All rights reserved. # * # * Permission is hereby granted, free of charge, to any person obtaining a # * copy of this software and associated documentation files (the "Software"), @@ -60,7 +60,7 @@ __DATA__ === TEST 1: test api_key is saved in redis --- http_config eval: $::HttpConfig --- config - include ../../api-gateway/api_key_service.conf; + include ../../api-gateway/api_key_service_deprecated.conf; error_log ../test-logs/api_key_test1_error.log debug; --- more_headers @@ -76,7 +76,7 @@ POST /cache/api_key?key=k-123&service_id=s-123 === TEST 2: check request without api_key parameter is rejected --- http_config eval: $::HttpConfig --- config - include ../../api-gateway/api_key_service.conf; + include ../../api-gateway/api_key_service_deprecated.conf; include ../../api-gateway/default_validators.conf; error_log ../test-logs/api_key_test2_error.log debug; @@ -101,7 +101,7 @@ GET /test-api-key === TEST 3: check request with invalid api_key is rejected --- http_config eval: $::HttpConfig --- config - include ../../api-gateway/api_key_service.conf; + include ../../api-gateway/api_key_service_deprecated.conf; include ../../api-gateway/default_validators.conf; error_log ../test-logs/api_key_test3_error.log debug; @@ -126,7 +126,7 @@ GET /test-api-key?api_key=ab123 === TEST 4: test request with valid api_key --- http_config eval: $::HttpConfig --- config - include ../../api-gateway/api_key_service.conf; + include ../../api-gateway/api_key_service_deprecated.conf; include ../../api-gateway/default_validators.conf; error_log ../test-logs/api_key_test4_error.log debug; @@ -156,7 +156,7 @@ GET /test-api-key?api_key=ab123 === TEST 5: test that api_key fields are saved in the request variables --- http_config eval: $::HttpConfig --- config - include ../../api-gateway/api_key_service.conf; + include ../../api-gateway/api_key_service_deprecated.conf; include ../../api-gateway/default_validators.conf; error_log ../test-logs/api_key_test5_error.log debug; @@ -187,7 +187,7 @@ GET /test-api-key?api_key=ab123 === TEST 6: test debug headers --- http_config eval: $::HttpConfig --- config - include ../../api-gateway/api_key_service.conf; + include ../../api-gateway/api_key_service_deprecated.conf; include ../../api-gateway/default_validators.conf; error_log ../test-logs/api_key_test6_error.log debug; @@ -220,7 +220,7 @@ GET /test-api-key?api_key=ab123 === TEST 7: test api-key related field starting with capital H --- http_config eval: $::HttpConfig --- config - include ../../api-gateway/api_key_service.conf; + include ../../api-gateway/api_key_service_deprecated.conf; include ../../api-gateway/default_validators.conf; error_log ../test-logs/api_key_test7_error.log debug; diff --git a/test/perl/api-gateway/validation/signing/hmacGenericSignatureValidator.t b/test/perl/api-gateway/validation/signing/hmacGenericSignatureValidator.t index 96b4c30..bc26f63 100644 --- a/test/perl/api-gateway/validation/signing/hmacGenericSignatureValidator.t +++ b/test/perl/api-gateway/validation/signing/hmacGenericSignatureValidator.t @@ -121,7 +121,17 @@ __DATA__ ] --- response_body eval [ -"+OK\r\n", +'{ + "key":"test-key-1234", + "key_secret":"-", + "realm":"sandbox", + "service_id":"s-123", + "service_name":"_undefined_", + "consumer_org_name":"_undefined_", + "app_name":"_undefined_", + "plan_name":"_undefined_" + } +', "signature is valid\n" ] --- error_code_like eval @@ -162,7 +172,17 @@ __DATA__ ] --- response_body eval [ -"+OK\r\n", +'{ + "key":"sZ28nvYnStSUS2dSzedgnwkJtUdLkNdR", + "key_secret":"mO2AIfdUQeQFiGQq", + "realm":"sandbox", + "service_id":"s-123", + "service_name":"_undefined_", + "consumer_org_name":"_undefined_", + "app_name":"_undefined_", + "plan_name":"_undefined_" + } +', "signature is valid\n" ] --- error_code_like eval @@ -170,10 +190,10 @@ __DATA__ --- no_error_log [error] -=== TEST 4: test HMAC SHA1 validator with API KEY validation +=== TEST 4: test HMAC SHA1 validator with API KEY validation with deprecated API-KEY API --- http_config eval: $::HttpConfig --- config - include ../../api-gateway/api_key_service.conf; + include ../../api-gateway/api_key_service_deprecated.conf; include ../../api-gateway/default_validators.conf; location /v1.0/accounts/ { @@ -260,7 +280,17 @@ __DATA__ ] --- response_body eval [ -"+OK\r\n", +'{ + "key":"sZ28nvYnStSUS2dSzedgnwkJtUdLkNdR", + "key_secret":"mO2AIfdUQeQFiGQq", + "realm":"sandbox", + "service_id":"s-123", + "service_name":"_undefined_", + "consumer_org_name":"_undefined_", + "app_name":"_undefined_", + "plan_name":"_undefined_" + } +', "signature is valid\n", 'while (1) {}{"code":1033,"description":"Developer key missing or invalid"}' . "\n", 'while (1) {}{"code":1033,"description":"Developer key missing or invalid"}' . "\n", @@ -325,7 +355,17 @@ __DATA__ ] --- response_body eval [ -"+OK\r\n", +'{ + "key":"sZ28nvYnStSUS2dSzedgnwkJtUdLkNdR", + "key_secret":"mO2AIfdUQeQFiGQq", + "realm":"sandbox", + "service_id":"123456", + "service_name":"_undefined_", + "consumer_org_name":"_undefined_", + "app_name":"_undefined_", + "plan_name":"_undefined_" + } +', "5XPFapKr91/nLn3F+tzfkvSuE4A=\n", 'while (1) {}{"code":1033,"description":"Developer key missing or invalid"}' . "\n", 'while (1) {}{"code":1033,"description":"Developer key missing or invalid"}' . "\n", diff --git a/test/resources/api-gateway/api_key_service.conf b/test/resources/api-gateway/api_key_service.conf index a88d971..328ccb2 100644 --- a/test/resources/api-gateway/api_key_service.conf +++ b/test/resources/api-gateway/api_key_service.conf @@ -65,10 +65,25 @@ location ~ /cache/api_key/set { set_if_empty $app_name _undefined_; set_if_empty $plan_name _undefined_; - - set $redis_cmd "HMSET cachedkey:$key:$service_id key_secret $key_secret service-id $service_id service-name $service_name realm $realm consumer-org-name $consumer_org_name app-name $app_name plan-name $plan_name"; - - proxy_pass http://127.0.0.1:$server_port/cache/redis_query?$redis_cmd; + set $metadata '{ + "key":"$key", + "key_secret":"$key_secret", + "realm":"$realm", + "service_id":"$service_id", + "service_name":"$service_name", + "consumer_org_name":"$consumer_org_name", + "app_name":"$app_name", + "plan_name":"$plan_name" + }'; + + default_type application/json; + + content_by_lua ' + local BaseValidator = require "api-gateway.validation.validator" + local validator = BaseValidator:new() + validator:setKeyInRedis("cachedkey:" .. ngx.var.key .. ":" .. ngx.var.service_id, "metadata", nil, ngx.var.metadata) + ngx.say(ngx.var.metadata) + '; header_filter_by_lua ' ngx.header.Description="Method used to add a new API-KEY into the cache. "; @@ -117,9 +132,6 @@ location ~ /cache/api_key/get { set $api_key $arg_key; set $service_id $arg_service_id; - # set $redis_cmd "HMGET cachedkey:$key:$service_id service-id service-name realm consumer-org-name app-name plan-name"; - # proxy_pass http://127.0.0.1:9191/cache/redis_query?$redis_cmd; - header_filter_by_lua ' ngx.header.Description="Retrieves all the fields of the key associated to the service_id parameter."; ngx.header.Allowed_Query_Params="key, service_id"; diff --git a/test/resources/api-gateway/api_key_service_deprecated.conf b/test/resources/api-gateway/api_key_service_deprecated.conf new file mode 100644 index 0000000..448b78b --- /dev/null +++ b/test/resources/api-gateway/api_key_service_deprecated.conf @@ -0,0 +1,151 @@ +#/* +# * Copyright (c) 2015 Adobe Systems Incorporated. All rights reserved. +# * +# * Permission is hereby granted, free of charge, to any person obtaining a +# * copy of this software and associated documentation files (the "Software"), +# * to deal in the Software without restriction, including without limitation +# * the rights to use, copy, modify, merge, publish, distribute, sublicense, +# * and/or sell copies of the Software, and to permit persons to whom the +# * Software is furnished to do so, subject to the following conditions: +# * +# * The above copyright notice and this permission notice shall be included in +# * all copies or substantial portions of the Software. +# * +# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# * DEALINGS IN THE SOFTWARE. +# * +# */ +# This is a sample file containing a basic API for Managing API-KEYs with Redis + +# +# ---------------------- +# Elasti Cache Proxy +# for api-key management +# ---------------------- +# + +# sample query: curl -i http://localhost:9191/cache/redis_query?KEYS%20*cachedkey* -u user:password +# Sample query to list all keys; +# curl http://localhost:9191/cache/redis_query?KEYS%20*cachedkey* -u user:password | grep cachedkey | awk -F ":" '{printf "%+ 32s %+ 20s \n",$2,$3}' | sort +location /cache/redis_query { + # auth_basic "Redis Master"; + # auth_basic_user_file /path/to/htpasswd; + allow 127.0.0.1; + set_unescape_uri $query $query_string; + redis2_raw_query '$query\r\n'; + redis2_pass api-gateway-redis; +} + +location ~ /cache/api_key/set { + internal; + + limit_except POST OPTIONS { + deny all; + } + + set $key $arg_key; + set $key_secret $arg_secret; + set $realm $arg_realm; + set $service_id $arg_service_id; + set $service_name $arg_service_name; + set $consumer_org_name $arg_consumer_org_name; + set $app_name $arg_app_name; + set $plan_name $arg_plan_name; + + set_if_empty $key_secret '-'; + set_if_empty $realm sandbox; + set_if_empty $service_id _undefined_; + set_if_empty $service_name _undefined_; + set_if_empty $consumer_org_name _undefined_; + set_if_empty $app_name _undefined_; + set_if_empty $plan_name _undefined_; + + set $redis_cmd "HMSET cachedkey:$key:$service_id key_secret $key_secret service-id $service_id service-name $service_name realm $realm consumer-org-name $consumer_org_name app-name $app_name plan-name $plan_name"; + + proxy_pass http://127.0.0.1:$server_port/cache/redis_query?$redis_cmd; + + header_filter_by_lua ' + ngx.header.Description="Method used to add a new API-KEY into the cache. "; + ngx.header.Allowed_Query_Params="key, secret, realm, service_id, service_name, consumer_org_name, app_name, plan_name"; + ngx.header.Key_Description="Parameter representing the API KEY"; + ngx.header.Realm_Description="The environment where the api-key is applicable. It should only be \'sandbox\' or \'prod\'. Other values are saved but ignored."; + ngx.header.Sample_query="curl -i -X POST \'http://" .. ngx.var.host .. "/cache/api_key?key=k-123&service_id=s-123&realm=sandbox\'"; + ngx.header["Content-Type"] = "text/plain"; + '; +} + +location ~ /cache/api_key/del { + + limit_except DELETE { + deny all; + } + + set $key $arg_key; + set $service_id $arg_service_id; + + set $redis_cmd "DEL cachedkey:$key:$service_id"; + + # limit_except OPTIONS + proxy_pass http://127.0.0.1:9191/cache/redis_query?$redis_cmd; + + header_filter_by_lua ' + ngx.header.Description="Deletes a key associated to a service from the cache"; + ngx.header.Allowed_Query_Params="key, service_id"; + ngx.header.Sample_query="curl -i -X DELETE \'http://" .. ngx.var.host .. "/cache/api_key?key=k-123&service_id=s-123\'"; + ngx.header["Content-Type"] = "text/plain"; + '; + +} + +location ~ /cache/api_key/get { + # TODO: restrict outside access, + # but test first if features are not broken by turning this endpoint to internal + # internal; + + uninitialized_variable_warn off; + + limit_except GET OPTIONS { + deny all; + } + + set $api_key $arg_key; + set $service_id $arg_service_id; + + # set $redis_cmd "HMGET cachedkey:$key:$service_id service-id service-name realm consumer-org-name app-name plan-name"; + # proxy_pass http://127.0.0.1:9191/cache/redis_query?$redis_cmd; + + header_filter_by_lua ' + ngx.header.Description="Retrieves all the fields of the key associated to the service_id parameter."; + ngx.header.Allowed_Query_Params="key, service_id"; + ngx.header.Sample_query="curl -i \'http://" .. ngx.var.host .. "/cache/api_key?key=k-123&service_id=s-123\'"; + ngx.header["Content-Type"] = "application/json"; + if ( ngx.var.arg_debug == "true" ) then + local request_time = ngx.now() - ngx.req.start_time(); + ngx.header["ResponseTime-Api-Key-Get"] = request_time; + end; + '; + + content_by_lua 'ngx.apiGateway.validation.validateApiKey()'; +} + +# pure REST API URI where POST goes to /set, GET to /get, DELETE to /del through internal redirect +location = /cache/api_key { + uninitialized_variable_warn off; + + if ($request_method = POST) { + rewrite ^/cache/api_key(.*)$ /cache/api_key/set$1 last; + } + + if ($request_method = DELETE) { + rewrite ^/cache/api_key(.*)$ /cache/api_key/del$1 last; + } + + if ($request_method ~* ^(GET|OPTIONS)$ ) { + rewrite ^/cache/api_key(.*)$ /cache/api_key/get$1 last; + } +}