-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathoauth-callback.lua
More file actions
171 lines (140 loc) · 5.85 KB
/
oauth-callback.lua
File metadata and controls
171 lines (140 loc) · 5.85 KB
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
local json = require("cjson")
local zlib = require("zlib")
local uri_args = ngx.req.get_uri_args()
local client_secret = oauth_client_secret or ngx.var.oauth_client_secret
local client_id = oauth_client_id or ngx.var.oauth_client_id
local scope = oauth_scope or ngx.var.oauth_scope or 'read:org'
local valid_org = oauth_org or ngx.var.oauth_org
local token_secret = oauth_token_secret or ngx.var.oauth_token_secret or 'notsosecret'
local proxy_api_uri = oauth_proxy_api_uri or ngx.var.oauth_proxy_api_uri or '/_oauth/api/'
local access_token_uri = oauth_access_token_uri or ngx.var.oauth_access_token_uri or '/_oauth/access_token'
local blocklist_string = oauth_blocklist or ngx.var.oauth_blocklist or ''
local blocklist = string.gmatch(blocklist_string, "%S+")
local domain = oauth_domain or ngx.var.oauth_domain or ngx.var.host
local cookie_tail = "; Domain=" .. domain .. '; HttpOnly; Path=/'
local function handle_subrequest_error(response)
if not response then
return "failed"
end
if response.status ~= 200 then
return "failed with " .. response.status .. ": " .. response.body
end
return nil
end
local function request_access_token(code)
ngx.log(ngx.ERR, 'Requesting access token with code ' .. code)
local res = ngx.location.capture(
access_token_uri,
{ method=ngx.HTTP_POST
, args={ client_id=client_id
, client_secret=client_secret
, code=code
}})
err = handle_subrequest_error(res)
if err then
ngx.log(ngx.ERR, "Got error during access token request: " .. err)
ngx.header['Content-type'] = 'text/html'
ngx.status = ngx.HTTP_FORBIDDEN
ngx.say("Got error during access token request: " .. err)
return ngx.exit(ngx.HTTP_FORBIDDEN)
else
ngx.log(ngx.ERR, "Decoded access token request: " .. res.body)
return ngx.decode_args(res.body)
end
end
local function provider_api_request(api_uri, token)
local api_request_uri = proxy_api_uri .. api_uri
ngx.log(ngx.ERR, 'Making subrequest to ' .. api_request_uri .. " with token " .. token)
ngx.req.set_header('Authorization', "token " .. token)
local api_response = ngx.location.capture(api_request_uri)
err = handle_subrequest_error(api_response)
if err then
ngx.log(ngx.ERR, "Got error during request to " .. api_uri .. ": " .. err)
ngx.header['Content-type'] = 'text/html'
ngx.status = ngx.HTTP_FORBIDDEN
ngx.say("Got error during request to " .. api_uri .. ": " .. err)
return ngx.exit(ngx.HTTP_FORBIDDEN)
else
local stream = zlib.inflate()
local inflated_body = stream(api_response.body)
ngx.log(ngx.ERR, 'api response body: ' .. inflated_body)
local decoded_body = json.decode(inflated_body)
return decoded_body
end
end
local function validate_orgs(access_token)
local orgs = provider_api_request('user/orgs', access_token)
for _, org in pairs(orgs) do
if org["login"] == valid_org then
ngx.log(ngx.ERR, "User " .. login .. " is in an authorized org")
return true
end
end
ngx.log(ngx.ERR, "User " .. login .. " not in authorized org")
return false
end
local function validate(access_token)
if not access_token or access_token == '' then
ngx.log(ngx.ERR, "No access token")
ngx.header['Content-type'] = 'text/html'
ngx.status = ngx.HTTP_FORBIDDEN
ngx.say("No access token")
return ngx.exit(ngx.HTTP_FORBIDDEN)
end
ngx.log(ngx.ERR, "Validating access token")
local profile = provider_api_request('user', access_token)
login = profile["login"]
for name in blocklist do
if login == name then
ngx.log(ngx.ERR, "Blocking blocklisted user " .. login)
ngx.header['Content-type'] = 'text/html'
ngx.status = ngx.HTTP_FORBIDDEN
ngx.say("Access is not allowed. If you believe this message is in error, please contact devops.")
return ngx.exit(ngx.HTTP_FORBIDDEN)
end
end
if not validate_orgs(access_token) then
return nil
end
local token = ngx.encode_base64(ngx.hmac_sha1(token_secret, domain .. login))
return login, token
end
local function authorize()
if uri_args["error"] then
ngx.log(ngx.ERR, "received " .. uri_args["error"] .. " from OAuth provider")
ngx.header['Content-type'] = 'text/html'
ngx.status = ngx.HTTP_FORBIDDEN
ngx.say("Received " .. uri_args["error"] .. " from OAuth provider")
return ngx.exit(ngx.HTTP_FORBIDDEN)
end
if not uri_args["code"] then
ngx.log(ngx.ERR, "Invalid request: no code for authorization")
ngx.header['Content-type'] = 'text/html'
ngx.status = ngx.HTTP_FORBIDDEN
ngx.say("Invalid request: no code for authorization")
return ngx.exit(ngx.HTTP_FORBIDDEN)
end
local access_token_response = request_access_token(uri_args["code"])
access_token = access_token_response.access_token
local login, token = validate(access_token)
if not token then
ngx.log(ngx.ERR, "Failed to authenticate request")
ngx.header['Content-type'] = 'text/html'
ngx.status = ngx.HTTP_FORBIDDEN
ngx.say("Failed to authenticate request")
return ngx.exit(ngx.HTTP_FORBIDDEN)
end
local expiry = "; Max-Age=" .. (ngx.time() + 60*60)
local cookies = {
"OAuthLogin=" .. ngx.escape_uri(login) .. cookie_tail .. expiry,
"OAuthAccessToken=" .. ngx.escape_uri(token) .. cookie_tail .. expiry,
}
for index, cookie in pairs(cookies) do
ngx.log(ngx.ERR, "Setting cookie " .. cookie .. " " .. index)
end
ngx.header["Set-Cookie"] = cookies
local redirect = uri_args['target_uri'] or '/'
ngx.log(ngx.ERR, "Redirecting to " .. redirect)
return ngx.redirect(redirect)
end
authorize()