Software /
code /
prosody-modules
Comparison
mod_auth_http_cookie/mod_auth_http_cookie.lua @ 3037:bae7b0a002ef
mod_auth_http_cookie: Possibly temporary fork of mod_http_auth_async that adds cookie auth support
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Thu, 24 May 2018 13:25:13 +0100 |
child | 3223:9a89ec5030b5 |
comparison
equal
deleted
inserted
replaced
3036:f7ebf8fcd602 | 3037:bae7b0a002ef |
---|---|
1 -- Prosody IM | |
2 -- Copyright (C) 2008-2013 Matthew Wild | |
3 -- Copyright (C) 2008-2013 Waqas Hussain | |
4 -- Copyright (C) 2014 Kim Alvefur | |
5 -- | |
6 -- This project is MIT/X11 licensed. Please see the | |
7 -- COPYING file in the source package for more information. | |
8 -- | |
9 | |
10 local new_sasl = require "util.sasl".new; | |
11 local base64 = require "util.encodings".base64.encode; | |
12 local have_async, async = pcall(require, "util.async"); | |
13 | |
14 local nodeprep = require "util.encodings".stringprep.nodeprep; | |
15 | |
16 local log = module._log; | |
17 local host = module.host; | |
18 | |
19 local password_auth_url = module:get_option_string("http_auth_url", ""):gsub("$host", host); | |
20 | |
21 local cookie_auth_url = module:get_option_string("http_cookie_auth_url"); | |
22 if cookie_auth_url then | |
23 cookie_auth_url = cookie_auth_url:gsub("$host", host); | |
24 end | |
25 | |
26 local external_needs_authzid = cookie_auth_url and cookie_auth_url:match("$user"); | |
27 | |
28 if password_auth_url == "" and not cookie_auth_url then error("http_auth_url or http_cookie_auth_url required") end | |
29 | |
30 | |
31 local provider = {}; | |
32 | |
33 -- globals required by socket.http | |
34 if rawget(_G, "PROXY") == nil then | |
35 rawset(_G, "PROXY", false) | |
36 end | |
37 if rawget(_G, "base_parsed") == nil then | |
38 rawset(_G, "base_parsed", false) | |
39 end | |
40 if not have_async then -- FINE! Set your globals then | |
41 prosody.unlock_globals() | |
42 require "ltn12" | |
43 require "socket" | |
44 require "socket.http" | |
45 require "ssl.https" | |
46 prosody.lock_globals() | |
47 end | |
48 | |
49 local function async_http_request(url, headers) | |
50 module:log("debug", "async_http_auth()"); | |
51 local http = require "net.http"; | |
52 local wait, done = async.waiter(); | |
53 local content, code, request, response; | |
54 local ex = { | |
55 headers = headers; | |
56 } | |
57 local function cb(content_, code_, request_, response_) | |
58 content, code, request, response = content_, code_, request_, response_; | |
59 done(); | |
60 end | |
61 http.request(url, ex, cb); | |
62 wait(); | |
63 log("debug", "response code %s", tostring(code)); | |
64 if code >= 200 and code <= 299 then | |
65 return true, content; | |
66 end | |
67 return nil; | |
68 end | |
69 | |
70 local function sync_http_request(url, headers) | |
71 module:log("debug", "sync_http_auth()"); | |
72 require "ltn12"; | |
73 local http = require "socket.http"; | |
74 local https = require "ssl.https"; | |
75 local request; | |
76 if string.sub(url, 1, string.len('https')) == 'https' then | |
77 request = https.request; | |
78 else | |
79 request = http.request; | |
80 end | |
81 local body_chunks = {}; | |
82 local _, code, headers, status = request{ | |
83 url = url, | |
84 headers = headers; | |
85 sink = ltn12.sink.table(body_chunks); | |
86 }; | |
87 log("debug", "response code %s %s", type(code), tostring(code)); | |
88 if type(code) == "number" and code >= 200 and code <= 299 then | |
89 log("debug", "success") | |
90 return true, table.concat(body_chunks); | |
91 end | |
92 return nil; | |
93 end | |
94 | |
95 local http_request = have_async and async_http_request or sync_http_request; | |
96 | |
97 function http_test_password(username, password) | |
98 local url = password_auth_url:gsub("$user", username):gsub("$password", password); | |
99 log("debug", "Testing password for user %s at host %s with URL %s", username, host, url); | |
100 local ok = (http_request(url, { Authorization = "Basic "..base64(username..":"..password); })); | |
101 if not ok then | |
102 return nil, "not authorized"; | |
103 end | |
104 return true; | |
105 end | |
106 | |
107 function http_test_cookie(cookie, username) | |
108 local url = external_needs_authzid and cookie_auth_url:gsub("$user", username) or cookie_auth_url; | |
109 log("debug", "Testing cookie auth for user %s at host %s with URL %s", username or "<unknown>", host, url); | |
110 local ok, resp = http_request(url, { Cookie = cookie; }); | |
111 if not ok then | |
112 return nil, "not authorized"; | |
113 end | |
114 | |
115 return external_needs_authzid or resp; | |
116 end | |
117 | |
118 function provider.test_password(username, password) | |
119 return http_test_password(username, password); | |
120 end | |
121 | |
122 function provider.users() | |
123 return function() | |
124 return nil; | |
125 end | |
126 end | |
127 | |
128 function provider.set_password(username, password) | |
129 return nil, "Changing passwords not supported"; | |
130 end | |
131 | |
132 function provider.user_exists(username) | |
133 return true; | |
134 end | |
135 | |
136 function provider.create_user(username, password) | |
137 return nil, "User creation not supported"; | |
138 end | |
139 | |
140 function provider.delete_user(username) | |
141 return nil , "User deletion not supported"; | |
142 end | |
143 | |
144 local function get_session_cookies(session) | |
145 local response = session.conn._http_open_response; | |
146 local request = response and response.request; | |
147 if request then | |
148 return request.headers.cookie; | |
149 end | |
150 end | |
151 | |
152 function provider.get_sasl_handler(session) | |
153 local cookie = cookie_auth_url and get_session_cookies(session); | |
154 log("debug", "Request cookie: %s", cookie); | |
155 return new_sasl(host, { | |
156 plain_test = function(sasl, username, password, realm) | |
157 return provider.test_password(username, password), true; | |
158 end; | |
159 external = cookie and function (authzid) | |
160 if external_needs_authzid then | |
161 -- Authorize the username provided by the client, using request cookie | |
162 if authzid ~= "" then | |
163 module:log("warn", "Client requested authzid, but cookie auth URL does not contain $user variable"); | |
164 return nil; | |
165 end | |
166 local success = http_test_cookie(cookie); | |
167 if not success then | |
168 return nil; | |
169 end | |
170 return nodeprep(authzid), true; | |
171 else | |
172 -- Authorize client using request cookie, username comes from auth server | |
173 if authzid == "" then | |
174 module:log("warn", "Client did not provide authzid, but cookie auth URL contains $user variable"); | |
175 return nil; | |
176 end | |
177 local unprepped_username = http_test_cookie(cookie, nodeprep(authzid)); | |
178 local username = nodeprep(unprepped_username); | |
179 if not username then | |
180 if unprepped_username then | |
181 log("warn", "Username supplied by cookie_auth_url is not valid for XMPP"); | |
182 end | |
183 return nil; | |
184 end | |
185 return username, true; | |
186 end; | |
187 end; | |
188 }); | |
189 end | |
190 | |
191 module:provides("auth", provider); |