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