/
handler.lua
176 lines (147 loc) · 5.2 KB
/
handler.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
local cache = require "kong.tools.database_cache"
local responses = require "kong.tools.responses"
local constants = require "kong.constants"
local singletons = require "kong.singletons"
local public_tools = require "kong.tools.public"
local BasePlugin = require "kong.plugins.base_plugin"
local ngx_set_header = ngx.req.set_header
local ngx_get_headers = ngx.req.get_headers
local set_uri_args = ngx.req.set_uri_args
local get_uri_args = ngx.req.get_uri_args
local clear_header = ngx.req.clear_header
local ngx_req_read_body = ngx.req.read_body
local ngx_req_set_body_data = ngx.req.set_body_data
local ngx_encode_args = ngx.encode_args
local type = type
local _realm = 'Key realm="'.._KONG._NAME..'"'
local KeyAuthHandler = BasePlugin:extend()
KeyAuthHandler.PRIORITY = 1000
function KeyAuthHandler:new()
KeyAuthHandler.super.new(self, "key-auth")
end
local function load_credential(key)
local creds, err = singletons.dao.keyauth_credentials:find_all {
key = key
}
if not creds then
return nil, err
end
return creds[1]
end
local function load_consumer(consumer_id, anonymous)
local result, err = singletons.dao.consumers:find { id = consumer_id }
if not result then
if anonymous and not err then
err = 'anonymous consumer "'..consumer_id..'" not found'
end
return nil, err
end
return result
end
local function set_consumer(consumer, credential)
ngx_set_header(constants.HEADERS.CONSUMER_ID, consumer.id)
ngx_set_header(constants.HEADERS.CONSUMER_CUSTOM_ID, consumer.custom_id)
ngx_set_header(constants.HEADERS.CONSUMER_USERNAME, consumer.username)
ngx.ctx.authenticated_consumer = consumer
if credential then
ngx_set_header(constants.HEADERS.CREDENTIAL_USERNAME, credential.username)
ngx.ctx.authenticated_credential = credential
ngx_set_header(constants.HEADERS.ANONYMOUS, nil) -- in case of auth plugins concatenation
else
ngx_set_header(constants.HEADERS.ANONYMOUS, true)
end
end
local function do_authentication(conf)
if type(conf.key_names) ~= "table" then
ngx.log(ngx.ERR, "[key-auth] no conf.key_names set, aborting plugin execution")
return false, {status = 500, message= "Invalid plugin configuration"}
end
local key
local headers = ngx_get_headers()
local uri_args = get_uri_args()
local body_data
-- read in the body if we want to examine POST args
if conf.key_in_body then
ngx_req_read_body()
body_data = public_tools.get_post_args()
end
-- search in headers & querystring
for i = 1, #conf.key_names do
local name = conf.key_names[i]
local v = headers[name]
if not v then
-- search in querystring
v = uri_args[name]
end
-- search the body, if we asked to
if not v and conf.key_in_body then
v = body_data[name]
end
if type(v) == "string" then
key = v
if conf.hide_credentials then
uri_args[name] = nil
set_uri_args(uri_args)
clear_header(name)
if conf.key_in_body then
body_data[name] = nil
ngx_req_set_body_data(ngx_encode_args(body_data))
end
end
break
elseif type(v) == "table" then
-- duplicate API key, HTTP 401
return false, {status = 401, message = "Duplicate API key found"}
end
end
-- this request is missing an API key, HTTP 401
if not key then
ngx.header["WWW-Authenticate"] = _realm
return false, {status = 401, message = "No API key found in headers"
.." or querystring"}
end
-- retrieve our consumer linked to this API key
local credential, err = cache.get_or_set(cache.keyauth_credential_key(key),
nil, load_credential, key)
if err then
return responses.send_HTTP_INTERNAL_SERVER_ERROR(err)
end
-- no credential in DB, for this key, it is invalid, HTTP 403
if not credential then
return false, {status = 403, message = "Invalid authentication credentials"}
end
-----------------------------------------
-- Success, this request is authenticated
-----------------------------------------
-- retrieve the consumer linked to this API key, to set appropriate headers
local consumer, err = cache.get_or_set(cache.consumer_key(credential.consumer_id),
nil, load_consumer, credential.consumer_id)
if err then
return responses.send_HTTP_INTERNAL_SERVER_ERROR(err)
end
set_consumer(consumer, credential)
return true
end
function KeyAuthHandler:access(conf)
KeyAuthHandler.super.access(self)
if ngx.ctx.authenticated_credential and conf.anonymous ~= "" then
-- we're already authenticated, and we're configured for using anonymous,
-- hence we're in a logical OR between auth methods and we're already done.
return
end
local ok, err = do_authentication(conf)
if not ok then
if conf.anonymous ~= "" then
-- get anonymous user
local consumer, err = cache.get_or_set(cache.consumer_key(conf.anonymous),
nil, load_consumer, conf.anonymous, true)
if err then
responses.send_HTTP_INTERNAL_SERVER_ERROR(err)
end
set_consumer(consumer, nil)
else
return responses.send(err.status, err.message)
end
end
end
return KeyAuthHandler