Software /
code /
prosody-modules
Comparison
mod_auth_joomla/mod_auth_joomla.lua @ 422:1082856e4612
mod_auth_joomla: Initial commit.
author | Waqas Hussain <waqas20@gmail.com> |
---|---|
date | Sun, 11 Sep 2011 23:58:20 +0500 |
child | 719:5e71e24e33fc |
comparison
equal
deleted
inserted
replaced
421:816d8e3e83a3 | 422:1082856e4612 |
---|---|
1 -- Joomla authentication backend for Prosody | |
2 -- | |
3 -- Copyright (C) 2011 Waqas Hussain | |
4 -- | |
5 | |
6 local new_sasl = require "util.sasl".new; | |
7 local nodeprep = require "util.encodings".stringprep.nodeprep; | |
8 local saslprep = require "util.encodings".stringprep.saslprep; | |
9 local DBI = require "DBI" | |
10 local md5 = require "util.hashes".md5; | |
11 local uuid_gen = require "util.uuid".generate; | |
12 | |
13 local connection; | |
14 local params = module:get_option("sql"); | |
15 | |
16 local resolve_relative_path = require "core.configmanager".resolve_relative_path; | |
17 | |
18 local function test_connection() | |
19 if not connection then return nil; end | |
20 if connection:ping() then | |
21 return true; | |
22 else | |
23 module:log("debug", "Database connection closed"); | |
24 connection = nil; | |
25 end | |
26 end | |
27 local function connect() | |
28 if not test_connection() then | |
29 prosody.unlock_globals(); | |
30 local dbh, err = DBI.Connect( | |
31 params.driver, params.database, | |
32 params.username, params.password, | |
33 params.host, params.port | |
34 ); | |
35 prosody.lock_globals(); | |
36 if not dbh then | |
37 module:log("debug", "Database connection failed: %s", tostring(err)); | |
38 return nil, err; | |
39 end | |
40 module:log("debug", "Successfully connected to database"); | |
41 dbh:autocommit(true); -- don't run in transaction | |
42 connection = dbh; | |
43 return connection; | |
44 end | |
45 end | |
46 | |
47 do -- process options to get a db connection | |
48 params = params or { driver = "SQLite3" }; | |
49 | |
50 if params.driver == "SQLite3" then | |
51 params.database = resolve_relative_path(prosody.paths.data or ".", params.database or "prosody.sqlite"); | |
52 end | |
53 | |
54 assert(params.driver and params.database, "Both the SQL driver and the database need to be specified"); | |
55 | |
56 assert(connect()); | |
57 end | |
58 | |
59 local function getsql(sql, ...) | |
60 if params.driver == "PostgreSQL" then | |
61 sql = sql:gsub("`", "\""); | |
62 end | |
63 if not test_connection() then connect(); end | |
64 -- do prepared statement stuff | |
65 local stmt, err = connection:prepare(sql); | |
66 if not stmt and not test_connection() then error("connection failed"); end | |
67 if not stmt then module:log("error", "QUERY FAILED: %s %s", err, debug.traceback()); return nil, err; end | |
68 -- run query | |
69 local ok, err = stmt:execute(...); | |
70 if not ok and not test_connection() then error("connection failed"); end | |
71 if not ok then return nil, err; end | |
72 | |
73 return stmt; | |
74 end | |
75 local function setsql(sql, ...) | |
76 local stmt, err = getsql(sql, ...); | |
77 if not stmt then return stmt, err; end | |
78 return stmt:affected(); | |
79 end | |
80 | |
81 local function get_password(username) | |
82 local stmt, err = getsql("SELECT `password` FROM `jos_users` WHERE `username`=?", username); | |
83 if stmt then | |
84 for row in stmt:rows(true) do | |
85 return row.password; | |
86 end | |
87 end | |
88 end | |
89 | |
90 | |
91 local function getCryptedPassword(plaintext, salt) | |
92 return md5(plaintext..salt); | |
93 end | |
94 local function joomlaCheckHash(password, hash) | |
95 local crypt, salt = hash:match("^([^:]*):(.*)$"); | |
96 return (crypt or hash) == getCryptedPassword(password, salt or ''); | |
97 end | |
98 local function joomlaCreateHash(password) | |
99 local salt = uuid_gen():gsub("%-", ""); | |
100 local crypt = getCryptedPassword(password, salt); | |
101 return crypt..':'..salt; | |
102 end | |
103 | |
104 | |
105 provider = { name = "joomla" }; | |
106 | |
107 function provider.test_password(username, password) | |
108 local hash = get_password(username); | |
109 return hash and joomlaCheckHash(password, hash); | |
110 end | |
111 function provider.user_exists(username) | |
112 module:log("debug", "test user %s existence", username); | |
113 return get_password(username) and true; | |
114 end | |
115 | |
116 function provider.get_password(username) | |
117 return nil, "Getting password is not supported."; | |
118 end | |
119 function provider.set_password(username, password) | |
120 local hash = joomlaCreateHash(password); | |
121 local stmt, err = setsql("UPDATE `jos_users` SET `password`=? WHERE `username`=?", hash, username); | |
122 return stmt and true, err; | |
123 end | |
124 function provider.create_user(username, password) | |
125 return nil, "Account creation/modification not supported."; | |
126 end | |
127 | |
128 local escapes = { | |
129 [" "] = "\\20"; | |
130 ['"'] = "\\22"; | |
131 ["&"] = "\\26"; | |
132 ["'"] = "\\27"; | |
133 ["/"] = "\\2f"; | |
134 [":"] = "\\3a"; | |
135 ["<"] = "\\3c"; | |
136 [">"] = "\\3e"; | |
137 ["@"] = "\\40"; | |
138 ["\\"] = "\\5c"; | |
139 }; | |
140 local unescapes = {}; | |
141 for k,v in pairs(escapes) do unescapes[v] = k; end | |
142 local function jid_escape(s) return s and (s:gsub(".", escapes)); end | |
143 local function jid_unescape(s) return s and (s:gsub("\\%x%x", unescapes)); end | |
144 | |
145 function provider.get_sasl_handler() | |
146 local sasl = {}; | |
147 function sasl:clean_clone() return provider.get_sasl_handler(); end | |
148 function sasl:mechanisms() return { PLAIN = true; }; end | |
149 function sasl:select(mechanism) | |
150 if not self.selected and mechanism == "PLAIN" then | |
151 self.selected = mechanism; | |
152 return true; | |
153 end | |
154 end | |
155 function sasl:process(message) | |
156 if not message then return "failure", "malformed-request"; end | |
157 local authorization, authentication, password = message:match("^([^%z]*)%z([^%z]+)%z([^%z]+)"); | |
158 if not authorization then return "failure", "malformed-request"; end | |
159 authentication = saslprep(authentication); | |
160 password = saslprep(password); | |
161 if (not password) or (password == "") or (not authentication) or (authentication == "") then | |
162 return "failure", "malformed-request", "Invalid username or password."; | |
163 end | |
164 local function test(authentication) | |
165 local prepped = nodeprep(authentication); | |
166 local normalized = jid_unescape(prepped); | |
167 return normalized and provider.test_password(normalized, password) and prepped; | |
168 end | |
169 local username = test(authentication) or test(jid_escape(authentication)); | |
170 if username then | |
171 self.username = username; | |
172 return "success"; | |
173 end | |
174 return "failure", "not-authorized", "Unable to authorize you with the authentication credentials you've sent."; | |
175 end | |
176 return sasl; | |
177 end | |
178 | |
179 module:add_item("auth-provider", provider); | |
180 |