Software / code / prosody
Comparison
plugins/mod_register.lua @ 4398:acc37e221940
mod_register: Add support for additional registration fields
| author | Florian Zeitz <florob@babelmonkeys.de> |
|---|---|
| date | Fri, 12 Aug 2011 00:01:35 +0200 |
| parent | 4270:d2d47fde9811 |
| child | 5014:b2006c1cfa85 |
comparison
equal
deleted
inserted
replaced
| 4397:1378e3c79c34 | 4398:acc37e221940 |
|---|---|
| 8 | 8 |
| 9 | 9 |
| 10 local hosts = _G.hosts; | 10 local hosts = _G.hosts; |
| 11 local st = require "util.stanza"; | 11 local st = require "util.stanza"; |
| 12 local datamanager = require "util.datamanager"; | 12 local datamanager = require "util.datamanager"; |
| 13 local dataform_new = require "util.dataforms".new; | |
| 13 local usermanager_user_exists = require "core.usermanager".user_exists; | 14 local usermanager_user_exists = require "core.usermanager".user_exists; |
| 14 local usermanager_create_user = require "core.usermanager".create_user; | 15 local usermanager_create_user = require "core.usermanager".create_user; |
| 15 local usermanager_set_password = require "core.usermanager".set_password; | 16 local usermanager_set_password = require "core.usermanager".set_password; |
| 16 local usermanager_delete_user = require "core.usermanager".delete_user; | 17 local usermanager_delete_user = require "core.usermanager".delete_user; |
| 17 local os_time = os.time; | 18 local os_time = os.time; |
| 18 local nodeprep = require "util.encodings".stringprep.nodeprep; | 19 local nodeprep = require "util.encodings".stringprep.nodeprep; |
| 19 local jid_bare = require "util.jid".bare; | 20 local jid_bare = require "util.jid".bare; |
| 20 | 21 |
| 21 local compat = module:get_option_boolean("registration_compat", true); | 22 local compat = module:get_option_boolean("registration_compat", true); |
| 22 local allow_registration = module:get_option_boolean("allow_registration", false); | 23 local allow_registration = module:get_option_boolean("allow_registration", false); |
| 24 local additional_fields = module:get_option("additional_registration_fields", {}); | |
| 25 | |
| 26 local field_map = { | |
| 27 username = { name = "username", type = "text-single", label = "Username", required = true }; | |
| 28 password = { name = "password", type = "text-private", label = "Password", required = true }; | |
| 29 nick = { name = "nick", type = "text-single", label = "Nickname" }; | |
| 30 name = { name = "name", type = "text-single", label = "Full Name" }; | |
| 31 first = { name = "first", type = "text-single", label = "Given Name" }; | |
| 32 last = { name = "last", type = "text-single", label = "Family Name" }; | |
| 33 email = { name = "email", type = "text-single", label = "Email" }; | |
| 34 address = { name = "address", type = "text-single", label = "Street" }; | |
| 35 city = { name = "city", type = "text-single", label = "City" }; | |
| 36 state = { name = "state", type = "text-single", label = "State" }; | |
| 37 zip = { name = "zip", type = "text-single", label = "Postal code" }; | |
| 38 phone = { name = "phone", type = "text-single", label = "Telephone number" }; | |
| 39 url = { name = "url", type = "text-single", label = "Webpage" }; | |
| 40 date = { name = "date", type = "text-single", label = "Birth date" }; | |
| 41 }; | |
| 42 | |
| 43 local registration_form = dataform_new{ | |
| 44 title = "Creating a new account"; | |
| 45 instructions = "Choose a username and password for use with this service."; | |
| 46 | |
| 47 field_map.username; | |
| 48 field_map.password; | |
| 49 }; | |
| 50 | |
| 51 local registration_query = st.stanza("query", {xmlns = "jabber:iq:register"}) | |
| 52 :tag("instructions"):text("Choose a username and password for use with this service."):up() | |
| 53 :tag("username"):up() | |
| 54 :tag("password"):up(); | |
| 55 | |
| 56 for _, field in ipairs(additional_fields) do | |
| 57 if type(field) == "table" then | |
| 58 registration_form[#registration_form + 1] = field; | |
| 59 else | |
| 60 if field:match("%+$") then | |
| 61 field = field:sub(1, #field - 1); | |
| 62 field_map[field].required = true; | |
| 63 end | |
| 64 | |
| 65 registration_form[#registration_form + 1] = field_map[field]; | |
| 66 registration_query:tag(field):up(); | |
| 67 end | |
| 68 end | |
| 69 registration_query:add_child(registration_form:form()); | |
| 23 | 70 |
| 24 module:add_feature("jabber:iq:register"); | 71 module:add_feature("jabber:iq:register"); |
| 25 | 72 |
| 26 local register_stream_feature = st.stanza("register", {xmlns="http://jabber.org/features/iq-register"}):up(); | 73 local register_stream_feature = st.stanza("register", {xmlns="http://jabber.org/features/iq-register"}):up(); |
| 27 module:hook("stream-features", function(event) | 74 module:hook("stream-features", function(event) |
| 65 session:close({condition = "not-authorized", text = "Account deleted"}); | 112 session:close({condition = "not-authorized", text = "Account deleted"}); |
| 66 end | 113 end |
| 67 -- TODO datamanager should be able to delete all user data itself | 114 -- TODO datamanager should be able to delete all user data itself |
| 68 datamanager.store(username, host, "vcard", nil); | 115 datamanager.store(username, host, "vcard", nil); |
| 69 datamanager.store(username, host, "private", nil); | 116 datamanager.store(username, host, "private", nil); |
| 117 datamanager.store(username, host, "account_details", nil); | |
| 70 datamanager.list_store(username, host, "offline", nil); | 118 datamanager.list_store(username, host, "offline", nil); |
| 71 local bare = username.."@"..host; | 119 local bare = username.."@"..host; |
| 72 for jid, item in pairs(roster) do | 120 for jid, item in pairs(roster) do |
| 73 if jid and jid ~= "pending" then | 121 if jid and jid ~= "pending" then |
| 74 if item.subscription == "both" or item.subscription == "from" or (roster.pending and roster.pending[jid]) then | 122 if item.subscription == "both" or item.subscription == "from" or (roster.pending and roster.pending[jid]) then |
| 113 return handle_registration_stanza(event); | 161 return handle_registration_stanza(event); |
| 114 end | 162 end |
| 115 end); | 163 end); |
| 116 end | 164 end |
| 117 | 165 |
| 166 local function parse_response(query) | |
| 167 local form = query:get_child("x", "jabber:x:data"); | |
| 168 if form then | |
| 169 return registration_form:data(form); | |
| 170 else | |
| 171 local data = {}; | |
| 172 local errors = {}; | |
| 173 for _, field in ipairs(registration_form) do | |
| 174 local name, required = field.name, field.required; | |
| 175 if field_map[name] then | |
| 176 data[name] = query:get_child_text(name); | |
| 177 if (not data[name] or #data[name] == 0) and required then | |
| 178 errors[name] = "Required value missing"; | |
| 179 end | |
| 180 end | |
| 181 end | |
| 182 if next(errors) then | |
| 183 return data, errors; | |
| 184 end | |
| 185 return data; | |
| 186 end | |
| 187 end | |
| 188 | |
| 118 local recent_ips = {}; | 189 local recent_ips = {}; |
| 119 local min_seconds_between_registrations = module:get_option("min_seconds_between_registrations"); | 190 local min_seconds_between_registrations = module:get_option("min_seconds_between_registrations"); |
| 120 local whitelist_only = module:get_option("whitelist_registration_only"); | 191 local whitelist_only = module:get_option("whitelist_registration_only"); |
| 121 local whitelisted_ips = module:get_option("registration_whitelist") or { "127.0.0.1" }; | 192 local whitelisted_ips = module:get_option("registration_whitelist") or { "127.0.0.1" }; |
| 122 local blacklisted_ips = module:get_option("registration_blacklist") or {}; | 193 local blacklisted_ips = module:get_option("registration_blacklist") or {}; |
| 131 session.send(st.error_reply(stanza, "cancel", "service-unavailable")); | 202 session.send(st.error_reply(stanza, "cancel", "service-unavailable")); |
| 132 else | 203 else |
| 133 local query = stanza.tags[1]; | 204 local query = stanza.tags[1]; |
| 134 if stanza.attr.type == "get" then | 205 if stanza.attr.type == "get" then |
| 135 local reply = st.reply(stanza); | 206 local reply = st.reply(stanza); |
| 136 reply:tag("query", {xmlns = "jabber:iq:register"}) | 207 reply:add_child(registration_query); |
| 137 :tag("instructions"):text("Choose a username and password for use with this service."):up() | |
| 138 :tag("username"):up() | |
| 139 :tag("password"):up(); | |
| 140 session.send(reply); | 208 session.send(reply); |
| 141 elseif stanza.attr.type == "set" then | 209 elseif stanza.attr.type == "set" then |
| 142 if query.tags[1] and query.tags[1].name == "remove" then | 210 if query.tags[1] and query.tags[1].name == "remove" then |
| 143 session.send(st.error_reply(stanza, "auth", "registration-required")); | 211 session.send(st.error_reply(stanza, "auth", "registration-required")); |
| 144 else | 212 else |
| 145 local username = query:child_with_name("username"); | 213 local data, errors = parse_response(query); |
| 146 local password = query:child_with_name("password"); | 214 if errors then |
| 147 if username and password then | 215 session.send(st.error_reply(stanza, "modify", "not-acceptable")); |
| 216 else | |
| 148 -- Check that the user is not blacklisted or registering too often | 217 -- Check that the user is not blacklisted or registering too often |
| 149 if not session.ip then | 218 if not session.ip then |
| 150 module:log("debug", "User's IP not known; can't apply blacklist/whitelist"); | 219 module:log("debug", "User's IP not known; can't apply blacklist/whitelist"); |
| 151 elseif blacklisted_ips[session.ip] or (whitelist_only and not whitelisted_ips[session.ip]) then | 220 elseif blacklisted_ips[session.ip] or (whitelist_only and not whitelisted_ips[session.ip]) then |
| 152 session.send(st.error_reply(stanza, "cancel", "not-acceptable", "You are not allowed to register an account.")); | 221 session.send(st.error_reply(stanza, "cancel", "not-acceptable", "You are not allowed to register an account.")); |
| 164 return true; | 233 return true; |
| 165 end | 234 end |
| 166 ip.time = os_time(); | 235 ip.time = os_time(); |
| 167 end | 236 end |
| 168 end | 237 end |
| 169 -- FIXME shouldn't use table.concat | 238 local username, password = nodeprep(data.username), data.password; |
| 170 username = nodeprep(table.concat(username)); | 239 data.username, data.password = nil, nil; |
| 171 password = table.concat(password); | |
| 172 local host = module.host; | 240 local host = module.host; |
| 173 if not username or username == "" then | 241 if not username or username == "" then |
| 174 session.send(st.error_reply(stanza, "modify", "not-acceptable", "The requested username is invalid.")); | 242 session.send(st.error_reply(stanza, "modify", "not-acceptable", "The requested username is invalid.")); |
| 175 elseif usermanager_user_exists(username, host) then | 243 elseif usermanager_user_exists(username, host) then |
| 176 session.send(st.error_reply(stanza, "cancel", "conflict", "The requested username already exists.")); | 244 session.send(st.error_reply(stanza, "cancel", "conflict", "The requested username already exists.")); |
| 177 else | 245 else |
| 246 -- TODO unable to write file, file may be locked, etc, what's the correct error? | |
| 247 local error_reply = st.error_reply(stanza, "wait", "internal-server-error", "Failed to write data to disk."); | |
| 178 if usermanager_create_user(username, password, host) then | 248 if usermanager_create_user(username, password, host) then |
| 249 if next(data) and not datamanager.store(username, host, "account_details", data) then | |
| 250 usermanager_delete_user(username, host); | |
| 251 session.send(error_reply); | |
| 252 return true; | |
| 253 end | |
| 179 session.send(st.reply(stanza)); -- user created! | 254 session.send(st.reply(stanza)); -- user created! |
| 180 module:log("info", "User account created: %s@%s", username, host); | 255 module:log("info", "User account created: %s@%s", username, host); |
| 181 module:fire_event("user-registered", { | 256 module:fire_event("user-registered", { |
| 182 username = username, host = host, source = "mod_register", | 257 username = username, host = host, source = "mod_register", |
| 183 session = session }); | 258 session = session }); |
| 184 else | 259 else |
| 185 -- TODO unable to write file, file may be locked, etc, what's the correct error? | 260 session.send(error_reply); |
| 186 session.send(st.error_reply(stanza, "wait", "internal-server-error", "Failed to write data to disk.")); | |
| 187 end | 261 end |
| 188 end | 262 end |
| 189 else | |
| 190 session.send(st.error_reply(stanza, "modify", "not-acceptable")); | |
| 191 end | 263 end |
| 192 end | 264 end |
| 193 end | 265 end |
| 194 end | 266 end |
| 195 return true; | 267 return true; |