Skip to content

Commit f0839eb

Browse files
authored
Merge pull request #1 from ging/token_validation
New feature for OAuth2 Token Validation using Fiware Wilma PEP-Proxy, v0.1 OAuth2 authentication with FIWARE Pep Proxy only, next version need to allow OAuth2 token validation with any authentication service provider
2 parents 1566eef + bbf9f0b commit f0839eb

File tree

6 files changed

+124
-38
lines changed

6 files changed

+124
-38
lines changed

config/default.yml

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ gatekeeper:
5858
- header
5959
- getParam
6060
- basicAuthUsername
61-
api_key_cache: true
61+
api_key_cache: false
62+
pep_host: 127.0.0.1 # ip or hostname of Pep Proxy host
63+
pep_port: 8090 # listen port of Pep Proxy
6264
trafficserver:
6365
host: 127.0.0.1
6466
port: 14009
@@ -282,12 +284,12 @@ apiSettings:
282284
message: The requested URL was not found on this server.
283285
api_key_missing:
284286
status_code: 403
285-
code: API_KEY_MISSING
286-
message: No api_key was supplied. Get one at {{signup_url}}
287+
code: API_KEY_OR_TOKEN_MISSING
288+
message: No api_key or token was supplied. Get one at {{signup_url}}
287289
api_key_invalid:
288290
status_code: 403
289-
code: API_KEY_INVALID
290-
message: An invalid api_key was supplied. Get one at {{signup_url}}
291+
code: API_KEY_OR_TOKEN_INVALID
292+
message: An invalid api_key or token was supplied. Get one at {{signup_url}}
291293
api_key_disabled:
292294
status_code: 403
293295
code: API_KEY_DISABLED
@@ -298,8 +300,8 @@ apiSettings:
298300
message: The api_key supplied has not been verified yet. Please check your e-mail to verify the API key. Contact us at {{contact_url}} for assistance
299301
api_key_unauthorized:
300302
status_code: 403
301-
code: API_KEY_UNAUTHORIZED
302-
message: The api_key supplied is not authorized to access the given service. Contact us at {{contact_url}} for assistance
303+
code: API_KEY_OR_TOKEN_UNAUTHORIZED
304+
message: The api_key or token supplied is not authorized to access the given service. Contact us at {{contact_url}} for assistance
303305
over_rate_limit:
304306
status_code: 429
305307
code: OVER_RATE_LIMIT
@@ -368,4 +370,4 @@ ban:
368370
message: "Please contact us for assistance."
369371
ember_server:
370372
port: 14050
371-
live_reload_port: 14051
373+
live_reload_port: 14051

src/api-umbrella/proxy/hooks/rewrite.lua

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ wait_for_setup()
1717
-- ngx.var lookups are apparently somewhat expensive.
1818
ngx.ctx.args = ngx_var.args
1919
ngx.ctx.arg_api_key = ngx_var.arg_api_key
20+
ngx.ctx.arg_token = ngx_var.arg_token
2021
if(config["router"]["match_x_forwarded_host"]) then
2122
ngx.ctx.host = ngx_var.http_x_forwarded_host or ngx_var.http_host or ngx_var.host
2223
else
2324
ngx.ctx.host = ngx_var.http_host or ngx_var.host
2425
end
2526
ngx.ctx.host_normalized = host_normalize(ngx.ctx.host)
2627
ngx.ctx.http_x_api_key = ngx_var.http_x_api_key
28+
ngx.ctx.http_x_auth_token = ngx_var.http_x_auth_token
2729
ngx.ctx.port = ngx_var.real_port
2830
ngx.ctx.protocol = ngx_var.real_scheme
2931
ngx.ctx.remote_addr = ngx_var.remote_addr
@@ -69,4 +71,4 @@ else
6971
else
7072
error_handler(api_err)
7173
end
72-
end
74+
end

src/api-umbrella/proxy/middleware/api_key_validator.lua

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,47 @@ local is_empty = types.is_empty
66

77
local function resolve_api_key()
88
local api_key_methods = config["gatekeeper"]["api_key_methods"]
9-
local api_key
9+
local key = {key_value="", key_type="" }
1010

11+
-- The api_key variable is a dictionary compose by two elements, the key_value which stores
12+
-- the api_key value or the user token value and the key_type field in where is stored
13+
-- the type of key that was provided by the user, it value could be an api_key or a token.
14+
-- The validation process is made for all the api_key_methods (except basicAuthUsername)
15+
-- declared in the configuration file checking if the user sends an api_key or token
16+
-- Only the header and get_param methods are supported by the token validation.
1117
for _, method in ipairs(api_key_methods) do
12-
if method == "header" then
13-
api_key = ngx.ctx.http_x_api_key
14-
elseif method == "getParam" then
15-
api_key = ngx.ctx.arg_api_key
16-
elseif method == "basicAuthUsername" then
17-
api_key = ngx.ctx.remote_user
18+
if method == "header" and ngx.ctx.http_x_api_key then
19+
key.key_value = ngx.ctx.http_x_api_key
20+
key.key_type = "api_key"
21+
elseif ngx.ctx.http_x_auth_token then
22+
key.key_value = ngx.ctx.http_x_auth_token
23+
key.key_type = "token"
24+
elseif method == "getParam" and ngx.ctx.arg_api_key then
25+
key.key_value = ngx.ctx.arg_api_key
26+
key.key_type = "api_key"
27+
elseif ngx.ctx.arg_token then
28+
key.key_value = ngx.ctx.arg_token
29+
key.key_type = "token"
30+
elseif method == "basicAuthUsername" and ngx.ctx.remote_user then
31+
key.key_value = ngx.ctx.remote_user
32+
key.key_type = "api_key"
1833
end
1934

20-
if not is_empty(api_key) then
35+
if not is_empty(key["key_value"]) then
2136
break
2237
end
2338
end
2439

2540
-- Store the api key for logging.
26-
ngx.ctx.api_key = api_key
41+
ngx.ctx.api_key = key["key_value"]
2742

28-
return api_key
43+
return key
2944
end
3045

3146
return function(settings)
3247
-- Find the API key in the header, query string, or HTTP auth.
3348
local api_key = resolve_api_key()
34-
if is_empty(api_key) then
49+
if is_empty(api_key["key_value"]) then
3550
if settings and settings["disable_api_key"] then
3651
return nil
3752
else
@@ -47,7 +62,7 @@ return function(settings)
4762

4863
-- Store the api key on the user object for easier access (the user object
4964
-- doesn't contain it directly, to save memory storage in the lookup table).
50-
user["api_key"] = api_key
65+
user["api_key"] = api_key["key_value"]
5166

5267
-- Store user details for logging.
5368
ngx.ctx.user_id = user["id"]
@@ -76,4 +91,4 @@ return function(settings)
7691
end
7792

7893
return user
79-
end
94+
end

src/api-umbrella/proxy/user_store.lua

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,33 @@ local mongo = require "api-umbrella.utils.mongo"
88
local shcache = require "shcache"
99
local types = require "pl.types"
1010
local utils = require "api-umbrella.proxy.utils"
11+
local pep = require "api-umbrella.utils.pep"
1112

1213
local cache_computed_settings = utils.cache_computed_settings
1314
local is_empty = types.is_empty
1415

1516
local function lookup_user(api_key)
16-
local raw_user, err = mongo.first("api_users", {
17-
query = {
18-
api_key = api_key,
19-
},
20-
})
21-
22-
if err then
23-
ngx.log(ngx.ERR, "failed to fetch user from mongodb: ", err)
17+
local raw_user
18+
local db_err
19+
local pep_err
20+
21+
-- Checking the field of api_key ["key_type"], if the key_type is api_key
22+
-- the api_key value is checked in the database and retrieve the user information
23+
-- else if the key_type is token, the token is checked using PEP Proxy and
24+
-- the user information is retrieved
25+
if not api_key["key_type"] or api_key["key_type"] == "api_key" then
26+
raw_user, db_err = mongo.first("api_users", {
27+
query = {
28+
api_key = api_key["key_value"],
29+
},
30+
})
31+
elseif api_key["key_type"] == "token" then
32+
raw_user, pep_err = pep.first(config["gatekeeper"]["pep_host"],config["gatekeeper"]["pep_port"],api_key["key_value"])
33+
end
34+
if pep_err then
35+
ngx.log(ngx.ERR, "failed to autenticate , status code:", pep_err)
36+
elseif db_err then
37+
ngx.log(ngx.ERR, "failed to fetch user from mongodb", db_err)
2438
elseif raw_user then
2539
local user = utils.pick_where_present(raw_user, {
2640
"created_at",
@@ -36,15 +50,31 @@ local function lookup_user(api_key)
3650
-- Ensure IDs get stored as strings, even if Mongo ObjectIds are in use.
3751
if raw_user["_id"] and raw_user["_id"]["$oid"] then
3852
user["id"] = raw_user["_id"]["$oid"]
53+
elseif raw_user.Nick_Name then
54+
user["id"] = raw_user.Nick_Name
55+
if not raw_user.Email then
56+
user["email"] = raw_user.Nick_Name
57+
else
58+
user["email"] = raw_user.Email
59+
end
3960
else
40-
user["id"] = raw_user["_id"]
61+
user["id"] = raw_user["_id"]
62+
end
63+
-- If the validation was made using a token, the Nick_Name associate to the token
64+
-- is assigned to the id attribute of the user
65+
if raw_user.Nick_Name then
66+
user["id"] = raw_user.Nick_Name
4167
end
4268

4369
-- Invert the array of roles into a hashy table for more optimized
4470
-- lookups (so we can just check if the key exists, rather than
4571
-- looping over each value).
72+
-- Moreover, in case that the user information have been retrieved using a token validation,
73+
-- the roles associated with the token are stored in user ["roles"]
4674
if user["roles"] then
4775
user["roles"] = invert_table(user["roles"])
76+
elseif raw_user.Roles then
77+
user["roles"] = invert_table(raw_user.Roles)
4878
end
4979

5080
if user["created_at"] and user["created_at"]["$date"] then
@@ -103,14 +133,14 @@ function _M.get(api_key)
103133
return nil
104134
end
105135

106-
user = shared_cache:load(api_key)
136+
user = shared_cache:load(api_key["key_value"])
107137
if user then
108-
local_cache:set(api_key, user, 2)
138+
local_cache:set(api_key["key_value"], user, 2)
109139
else
110-
local_cache:set(api_key, EMPTY_DATA, 2)
140+
local_cache:set(api_key["key_value"], EMPTY_DATA, 2)
111141
end
112142

113143
return user
114144
end
115145

116-
return _M
146+
return _M
Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
return function(table)
2+
local numItems = 0
23
local inverted = {}
34
for key, value in pairs(table) do
4-
inverted[value] = key
5+
if type(value)=="string" then
6+
inverted[value] = key
7+
else
8+
for k,v in pairs(value) do
9+
numItems = numItems + 1
10+
end
11+
if numItems > 1 then
12+
value = value["name"]
13+
end
14+
inverted[value] = key
15+
end
516
end
6-
717
return inverted
8-
end
18+
end

src/api-umbrella/utils/pep.lua

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
local http = require "resty.http"
2+
local cjson = require "cjson"
3+
local _M = {}
4+
-- Function to connect with the Pep Proxy service for checking if the token is valid and retrieve
5+
-- the user properties. The function takes the PEP Proxy host and port as parameters
6+
-- and sends a request with the header X-Auth-Token with the value of the token provided
7+
-- by the user. If the token is valid, PEP proxy sends a response with the user information
8+
-- asociated to the token, otherwise, it sends a message indicating the result of the
9+
-- validation process with his status, 404 , 402, etc.
10+
function _M.first(host, port, token)
11+
local result
12+
local httpc = http.new()
13+
httpc:set_timeout(45000)
14+
httpc:connect(host,port)
15+
local res, err = httpc:request({headers = {["X-Auth-Token"] = token}})
16+
if res and res.status == 200 then
17+
local body, body_err = res:read_body()
18+
if not body then
19+
return nil, body_err
20+
end
21+
result = cjson.decode(body)
22+
end
23+
24+
return result, err
25+
end
26+
27+
return _M

0 commit comments

Comments
 (0)