Comparison

mod_invites_api/mod_invites_api.lua @ 4115:165ade4ce97b

mod_invites_api: New module to create new invites over HTTP
author Matthew Wild <mwild1@gmail.com>
date Sun, 13 Sep 2020 11:05:19 +0100
child 4216:35b678609b79
comparison
equal deleted inserted replaced
4114:4656a64e59be 4115:165ade4ce97b
1 local http_formdecode = require "net.http".formdecode;
2
3 local api_key_store;
4 local invites;
5 -- COMPAT: workaround to avoid executing inside prosodyctl
6 if prosody.shutdown then
7 module:depends("http");
8 api_key_store = module:open_store("invite_api_keys", "map");
9 invites = module:depends("invites");
10 end
11
12 local function get_api_user(request, params)
13 local combined_key;
14
15 local auth_header = request.headers.authorization;
16
17 if not auth_header then
18 params = params or http_formdecode(request.url.query);
19 combined_key = params.key;
20 else
21 local auth_type, value = auth_header:match("^(%S+)%s(%S+)$");
22 if auth_type ~= "Bearer" then
23 return;
24 end
25 combined_key = value;
26 end
27
28 if not combined_key then
29 return;
30 end
31
32 local key_id, key_token = combined_key:match("^([^/]+)/(.+)$");
33
34 if not key_id then
35 return;
36 end
37
38 local api_user = api_key_store:get(nil, key_id);
39
40 if not api_user or api_user.token ~= key_token then
41 return;
42 end
43
44 -- TODO: key expiry, rate limiting, etc.
45 return api_user;
46 end
47
48 function handle_request(event)
49 local query_params = http_formdecode(event.request.url.query);
50
51 local api_user = get_api_user(event.request, query_params);
52
53 if not api_user then
54 return 403;
55 end
56
57 local invite = invites.create_account(nil, { source = "api/token/"..api_user.id });
58 if not invite then
59 return 500;
60 end
61
62 event.response.headers.Location = invite.landing_page or invite.uri;
63
64 if query_params.redirect then
65 return 303;
66 end
67 return 201;
68 end
69
70 if invites then
71 module:provides("http", {
72 route = {
73 ["GET"] = handle_request;
74 };
75 });
76 end
77
78 function module.command(arg)
79 if #arg < 2 then
80 print("Usage:");
81 print("");
82 print(" prosodyctl mod_"..module.name.." create NAME");
83 print(" prosodyctl mod_"..module.name.." delete KEY_ID");
84 print(" prosodyctl mod_"..module.name.." list");
85 print("");
86 end
87
88 local command = table.remove(arg, 1);
89
90 local host = table.remove(arg, 1);
91 if not prosody.hosts[host] then
92 print("Error: please supply a valid host");
93 return 1;
94 end
95 require "core.storagemanager".initialize_host(host);
96 module.host = host; --luacheck: ignore 122/module
97 api_key_store = module:open_store("invite_api_keys", "map");
98
99 if command == "create" then
100 local id = require "util.id".short();
101 local token = require "util.id".long();
102 api_key_store:set(nil, id, {
103 id = id;
104 token = token;
105 name = arg[1];
106 created_at = os.time();
107 });
108 print(id.."/"..token);
109 elseif command == "delete" then
110 local id = table.remove(arg, 1);
111 if not api_key_store:get(nil, id) then
112 print("Error: key not found");
113 return 1;
114 end
115 api_key_store:set(nil, id, nil);
116 elseif command == "list" then
117 local api_key_store_kv = module:open_store("invite_api_keys");
118 for key_id, key_info in pairs(api_key_store_kv:get(nil)) do
119 print(key_id, key_info.name or "<unknown>");
120 end
121 else
122 print("Unknown command - "..command);
123 end
124 end