rev |
line source |
bsw/jbe@0
|
1 Member = mondelefant.new_class()
|
bsw/jbe@0
|
2 Member.table = 'member'
|
bsw/jbe@0
|
3
|
bsw/jbe@0
|
4 Member:add_reference{
|
bsw@9
|
5 mode = "1m",
|
bsw@9
|
6 to = "MemberHistory",
|
bsw@9
|
7 this_key = 'id',
|
bsw@9
|
8 that_key = 'member_id',
|
bsw@9
|
9 ref = 'history_entries',
|
bsw@9
|
10 back_ref = 'member'
|
bsw@9
|
11 }
|
bsw@9
|
12
|
bsw@9
|
13 Member:add_reference{
|
bsw/jbe@4
|
14 mode = '1m',
|
bsw@2
|
15 to = "MemberImage",
|
bsw@2
|
16 this_key = 'id',
|
bsw@2
|
17 that_key = 'member_id',
|
bsw/jbe@4
|
18 ref = 'images',
|
bsw@2
|
19 back_ref = 'member'
|
bsw@2
|
20 }
|
bsw@2
|
21
|
bsw@2
|
22 Member:add_reference{
|
bsw/jbe@0
|
23 mode = '1m',
|
bsw/jbe@0
|
24 to = "Contact",
|
bsw/jbe@0
|
25 this_key = 'id',
|
bsw/jbe@0
|
26 that_key = 'member_id',
|
bsw/jbe@0
|
27 ref = 'contacts',
|
bsw/jbe@0
|
28 back_ref = 'member',
|
bsw/jbe@0
|
29 default_order = '"other_member_id"'
|
bsw/jbe@0
|
30 }
|
bsw/jbe@0
|
31
|
bsw/jbe@0
|
32 Member:add_reference{
|
bsw/jbe@0
|
33 mode = '1m',
|
bsw/jbe@0
|
34 to = "Contact",
|
bsw/jbe@0
|
35 this_key = 'id',
|
bsw/jbe@0
|
36 that_key = 'member_id',
|
bsw/jbe@0
|
37 ref = 'foreign_contacts',
|
bsw/jbe@0
|
38 back_ref = 'other_member',
|
bsw/jbe@0
|
39 default_order = '"member_id"'
|
bsw/jbe@0
|
40 }
|
bsw/jbe@0
|
41
|
bsw/jbe@0
|
42 Member:add_reference{
|
bsw/jbe@0
|
43 mode = '1m',
|
bsw/jbe@0
|
44 to = "Session",
|
bsw/jbe@0
|
45 this_key = 'id',
|
bsw/jbe@0
|
46 that_key = 'member_id',
|
bsw/jbe@0
|
47 ref = 'sessions',
|
bsw/jbe@0
|
48 back_ref = 'member',
|
bsw/jbe@0
|
49 default_order = '"ident"'
|
bsw/jbe@0
|
50 }
|
bsw/jbe@0
|
51
|
bsw/jbe@0
|
52 Member:add_reference{
|
bsw/jbe@0
|
53 mode = '1m',
|
bsw/jbe@0
|
54 to = "Draft",
|
bsw/jbe@0
|
55 this_key = 'id',
|
bsw/jbe@0
|
56 that_key = 'author_id',
|
bsw/jbe@0
|
57 ref = 'drafts',
|
bsw/jbe@0
|
58 back_ref = 'author',
|
bsw/jbe@0
|
59 default_order = '"id"'
|
bsw/jbe@0
|
60 }
|
bsw/jbe@0
|
61
|
bsw/jbe@0
|
62 Member:add_reference{
|
bsw/jbe@0
|
63 mode = '1m',
|
bsw/jbe@0
|
64 to = "Suggestion",
|
bsw/jbe@0
|
65 this_key = 'id',
|
bsw/jbe@0
|
66 that_key = 'author_id',
|
bsw/jbe@0
|
67 ref = 'suggestions',
|
bsw/jbe@0
|
68 back_ref = 'author',
|
bsw/jbe@0
|
69 default_order = '"id"'
|
bsw/jbe@0
|
70 }
|
bsw/jbe@0
|
71
|
bsw/jbe@0
|
72 Member:add_reference{
|
bsw/jbe@0
|
73 mode = '1m',
|
bsw/jbe@0
|
74 to = "Membership",
|
bsw/jbe@0
|
75 this_key = 'id',
|
bsw/jbe@0
|
76 that_key = 'member_id',
|
bsw/jbe@0
|
77 ref = 'memberships',
|
bsw/jbe@0
|
78 back_ref = 'member',
|
bsw/jbe@0
|
79 default_order = '"area_id"'
|
bsw/jbe@0
|
80 }
|
bsw/jbe@0
|
81
|
bsw/jbe@0
|
82 Member:add_reference{
|
bsw/jbe@0
|
83 mode = '1m',
|
bsw/jbe@0
|
84 to = "Interest",
|
bsw/jbe@0
|
85 this_key = 'id',
|
bsw/jbe@0
|
86 that_key = 'member_id',
|
bsw/jbe@0
|
87 ref = 'interests',
|
bsw/jbe@0
|
88 back_ref = 'member',
|
bsw/jbe@0
|
89 default_order = '"id"'
|
bsw/jbe@0
|
90 }
|
bsw/jbe@0
|
91
|
bsw/jbe@0
|
92 Member:add_reference{
|
bsw/jbe@0
|
93 mode = '1m',
|
bsw/jbe@0
|
94 to = "Initiator",
|
bsw/jbe@0
|
95 this_key = 'id',
|
bsw/jbe@0
|
96 that_key = 'member_id',
|
bsw/jbe@0
|
97 ref = 'initiators',
|
bsw@10
|
98 back_ref = 'member'
|
bsw/jbe@0
|
99 }
|
bsw/jbe@0
|
100
|
bsw/jbe@0
|
101 Member:add_reference{
|
bsw/jbe@0
|
102 mode = '1m',
|
bsw/jbe@0
|
103 to = "Supporter",
|
bsw/jbe@0
|
104 this_key = 'id',
|
bsw/jbe@0
|
105 that_key = 'member_id',
|
bsw/jbe@0
|
106 ref = 'supporters',
|
bsw@2
|
107 back_ref = 'member'
|
bsw/jbe@0
|
108 }
|
bsw/jbe@0
|
109
|
bsw/jbe@0
|
110 Member:add_reference{
|
bsw/jbe@0
|
111 mode = '1m',
|
bsw/jbe@0
|
112 to = "Opinion",
|
bsw/jbe@0
|
113 this_key = 'id',
|
bsw/jbe@0
|
114 that_key = 'member_id',
|
bsw/jbe@0
|
115 ref = 'opinions',
|
bsw/jbe@0
|
116 back_ref = 'member',
|
bsw/jbe@0
|
117 default_order = '"id"'
|
bsw/jbe@0
|
118 }
|
bsw/jbe@0
|
119
|
bsw/jbe@0
|
120 Member:add_reference{
|
bsw/jbe@0
|
121 mode = '1m',
|
bsw/jbe@0
|
122 to = "Delegation",
|
bsw/jbe@0
|
123 this_key = 'id',
|
bsw/jbe@0
|
124 that_key = 'truster_id',
|
bsw/jbe@0
|
125 ref = 'outgoing_delegations',
|
bsw/jbe@0
|
126 back_ref = 'truster',
|
bsw@1045
|
127 -- default_order = '"id"'
|
bsw/jbe@0
|
128 }
|
bsw/jbe@0
|
129
|
bsw/jbe@0
|
130 Member:add_reference{
|
bsw/jbe@0
|
131 mode = '1m',
|
bsw/jbe@0
|
132 to = "Delegation",
|
bsw/jbe@0
|
133 this_key = 'id',
|
bsw/jbe@0
|
134 that_key = 'trustee_id',
|
bsw/jbe@0
|
135 ref = 'incoming_delegations',
|
bsw/jbe@0
|
136 back_ref = 'trustee',
|
bsw@1045
|
137 -- default_order = '"id"'
|
bsw/jbe@0
|
138 }
|
bsw/jbe@0
|
139
|
bsw/jbe@0
|
140 Member:add_reference{
|
bsw/jbe@0
|
141 mode = '1m',
|
bsw/jbe@0
|
142 to = "DirectVoter",
|
bsw/jbe@0
|
143 this_key = 'id',
|
bsw/jbe@0
|
144 that_key = 'member_id',
|
bsw/jbe@0
|
145 ref = 'direct_voter',
|
bsw/jbe@0
|
146 back_ref = 'member',
|
bsw/jbe@0
|
147 default_order = '"issue_id"'
|
bsw/jbe@0
|
148 }
|
bsw/jbe@0
|
149
|
bsw/jbe@0
|
150 Member:add_reference{
|
bsw/jbe@0
|
151 mode = '1m',
|
bsw/jbe@0
|
152 to = "Vote",
|
bsw/jbe@0
|
153 this_key = 'id',
|
bsw/jbe@0
|
154 that_key = 'member_id',
|
bsw/jbe@0
|
155 ref = 'vote',
|
bsw/jbe@0
|
156 back_ref = 'member',
|
bsw/jbe@0
|
157 default_order = '"issue_id", "initiative_id"'
|
bsw/jbe@0
|
158 }
|
bsw/jbe@0
|
159
|
bsw/jbe@0
|
160 Member:add_reference{
|
bsw/jbe@0
|
161 mode = 'mm',
|
bsw/jbe@0
|
162 to = "Member",
|
bsw/jbe@0
|
163 this_key = 'id',
|
bsw/jbe@0
|
164 that_key = 'id',
|
bsw/jbe@0
|
165 connected_by_table = 'contact',
|
bsw/jbe@0
|
166 connected_by_this_key = 'member_id',
|
bsw/jbe@0
|
167 connected_by_that_key = 'other_member_id',
|
bsw/jbe@0
|
168 ref = 'saved_members',
|
bsw/jbe@0
|
169 }
|
bsw/jbe@0
|
170
|
bsw/jbe@0
|
171 Member:add_reference{
|
bsw/jbe@0
|
172 mode = 'mm',
|
bsw/jbe@0
|
173 to = "Member",
|
bsw/jbe@0
|
174 this_key = 'id',
|
bsw/jbe@0
|
175 that_key = 'id',
|
bsw/jbe@0
|
176 connected_by_table = 'contact',
|
bsw/jbe@0
|
177 connected_by_this_key = 'other_member_id',
|
bsw/jbe@0
|
178 connected_by_that_key = 'member_id',
|
bsw/jbe@0
|
179 ref = 'saved_by_members',
|
bsw/jbe@0
|
180 }
|
bsw/jbe@0
|
181
|
bsw/jbe@0
|
182 Member:add_reference{
|
bsw/jbe@0
|
183 mode = 'mm',
|
bsw@281
|
184 to = "Unit",
|
bsw@281
|
185 this_key = 'id',
|
bsw@281
|
186 that_key = 'id',
|
bsw@281
|
187 connected_by_table = 'privilege',
|
bsw@281
|
188 connected_by_this_key = 'member_id',
|
bsw@281
|
189 connected_by_that_key = 'unit_id',
|
bsw@281
|
190 ref = 'units'
|
bsw@281
|
191 }
|
bsw@281
|
192
|
bsw@281
|
193 Member:add_reference{
|
bsw@281
|
194 mode = 'mm',
|
bsw/jbe@0
|
195 to = "Area",
|
bsw/jbe@0
|
196 this_key = 'id',
|
bsw/jbe@0
|
197 that_key = 'id',
|
bsw/jbe@0
|
198 connected_by_table = 'membership',
|
bsw/jbe@0
|
199 connected_by_this_key = 'member_id',
|
bsw/jbe@0
|
200 connected_by_that_key = 'area_id',
|
bsw/jbe@0
|
201 ref = 'areas'
|
bsw/jbe@0
|
202 }
|
bsw/jbe@0
|
203
|
bsw/jbe@0
|
204 Member:add_reference{
|
bsw/jbe@0
|
205 mode = 'mm',
|
bsw/jbe@0
|
206 to = "Issue",
|
bsw/jbe@0
|
207 this_key = 'id',
|
bsw/jbe@0
|
208 that_key = 'id',
|
bsw/jbe@0
|
209 connected_by_table = 'interest',
|
bsw/jbe@0
|
210 connected_by_this_key = 'member_id',
|
bsw/jbe@0
|
211 connected_by_that_key = 'issue_id',
|
bsw/jbe@0
|
212 ref = 'issues'
|
bsw/jbe@0
|
213 }
|
bsw/jbe@0
|
214
|
bsw/jbe@0
|
215 Member:add_reference{
|
bsw/jbe@0
|
216 mode = 'mm',
|
bsw/jbe@0
|
217 to = "Initiative",
|
bsw/jbe@0
|
218 this_key = 'id',
|
bsw/jbe@0
|
219 that_key = 'id',
|
bsw/jbe@0
|
220 connected_by_table = 'initiator',
|
bsw/jbe@0
|
221 connected_by_this_key = 'member_id',
|
bsw/jbe@0
|
222 connected_by_that_key = 'initiative_id',
|
bsw/jbe@0
|
223 ref = 'initiated_initiatives'
|
bsw/jbe@0
|
224 }
|
bsw/jbe@0
|
225
|
bsw/jbe@0
|
226 Member:add_reference{
|
bsw/jbe@0
|
227 mode = 'mm',
|
bsw/jbe@0
|
228 to = "Initiative",
|
bsw/jbe@0
|
229 this_key = 'id',
|
bsw/jbe@0
|
230 that_key = 'id',
|
bsw/jbe@0
|
231 connected_by_table = 'supporter',
|
bsw/jbe@0
|
232 connected_by_this_key = 'member_id',
|
bsw/jbe@0
|
233 connected_by_that_key = 'initiative_id',
|
bsw/jbe@0
|
234 ref = 'supported_initiatives'
|
bsw/jbe@0
|
235 }
|
bsw/jbe@0
|
236
|
bsw@279
|
237 model.has_rendered_content(Member, RenderedMemberStatement, "statement")
|
bsw@279
|
238
|
bsw@193
|
239 function Member:build_selector(args)
|
bsw@193
|
240 local selector = self:new_selector()
|
bsw@193
|
241 if args.active ~= nil then
|
bsw@193
|
242 selector:add_where{ "member.active = ?", args.active }
|
bsw@193
|
243 end
|
bsw@581
|
244 if args.locked ~= nil then
|
bsw@581
|
245 selector:add_where{ "member.locked = ?", args.locked }
|
bsw@581
|
246 end
|
bsw@199
|
247 if args.is_contact_of_member_id then
|
bsw@199
|
248 selector:join("contact", "__model_member__contact", "member.id = __model_member__contact.other_member_id")
|
bsw@199
|
249 selector:add_where{ "__model_member__contact.member_id = ?", args.is_contact_of_member_id }
|
bsw@199
|
250 end
|
bsw@297
|
251 if args.voting_right_for_unit_id then
|
bsw@299
|
252 selector:join("privilege", "__model_member__privilege", { "member.id = __model_member__privilege.member_id AND __model_member__privilege.voting_right AND __model_member__privilege.unit_id = ?", args.voting_right_for_unit_id })
|
bsw@297
|
253 end
|
bsw@581
|
254 if args.admin_search then
|
bsw@581
|
255 local search_string = "%" .. args.admin_search .. "%"
|
bsw@581
|
256 selector:add_where{ "member.identification ILIKE ? OR member.name ILIKE ?", search_string, search_string }
|
bsw@581
|
257 end
|
bsw@193
|
258 if args.order then
|
bsw@193
|
259 if args.order == "id" then
|
bsw@193
|
260 selector:add_order_by("id")
|
bsw@581
|
261 elseif args.order == "identification" then
|
bsw@581
|
262 selector:add_order_by("identification")
|
bsw@193
|
263 elseif args.order == "name" then
|
bsw@193
|
264 selector:add_order_by("name")
|
bsw@193
|
265 else
|
bsw@193
|
266 error("invalid order")
|
bsw@193
|
267 end
|
bsw@193
|
268 end
|
bsw@193
|
269 return selector
|
bsw@193
|
270 end
|
bsw@193
|
271
|
bsw@929
|
272 function Member:lockForReference()
|
bsw@929
|
273 self.get_db_conn().query("LOCK TABLE " .. self:get_qualified_table() .. " IN ROW SHARE MODE")
|
bsw@929
|
274 end
|
bsw@929
|
275
|
bsw@1071
|
276
|
bsw@1071
|
277 function Member:get_all_by_authority(authority)
|
bsw@1071
|
278
|
bsw@1071
|
279 local members = Member:new_selector()
|
bsw@1071
|
280 :add_where{ "authority = ?", authority }
|
bsw@1074
|
281 :add_field("authority_uid")
|
bsw@1071
|
282 :exec()
|
bsw@1071
|
283
|
bsw@1071
|
284 return members
|
bsw@1071
|
285 end
|
bsw@1071
|
286
|
bsw/jbe@0
|
287 function Member.object:set_password(password)
|
bsw@865
|
288 trace.disable()
|
bsw@905
|
289
|
bsw@905
|
290 local hash_prefix
|
bsw@905
|
291 local salt_length
|
bsw@905
|
292
|
bsw@905
|
293 local function rounds()
|
bsw@905
|
294 return multirand.integer(
|
bsw@905
|
295 config.password_hash_min_rounds,
|
bsw@905
|
296 config.password_hash_max_rounds
|
bsw@905
|
297 )
|
bsw@905
|
298 end
|
bsw@905
|
299
|
bsw@905
|
300 if config.password_hash_algorithm == "crypt_md5" then
|
bsw@905
|
301 hash_prefix = "$1$"
|
bsw@905
|
302 salt_length = 8
|
bsw@905
|
303
|
bsw@905
|
304 elseif config.password_hash_algorithm == "crypt_sha256" then
|
bsw@905
|
305 hash_prefix = "$5$rounds=" .. rounds() .. "$"
|
bsw@905
|
306 salt_length = 16
|
bsw@905
|
307
|
bsw@905
|
308 elseif config.password_hash_algorithm == "crypt_sha512" then
|
bsw@905
|
309 hash_prefix = "$6$rounds=" .. rounds() .. "$"
|
bsw@905
|
310 salt_length = 16
|
bsw@905
|
311
|
bsw@905
|
312 else
|
bsw@905
|
313 error("Unknown hash algorithm selected in configuration")
|
bsw@905
|
314
|
bsw@905
|
315 end
|
bsw@906
|
316
|
bsw@906
|
317 hash_prefix = hash_prefix .. multirand.string(
|
bsw@906
|
318 salt_length,
|
bsw@906
|
319 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./"
|
bsw@906
|
320 )
|
bsw@905
|
321
|
bsw@906
|
322 local hash = extos.crypt(password, hash_prefix)
|
bsw@905
|
323
|
bsw@905
|
324 if not hash or hash:sub(1, #hash_prefix) ~= hash_prefix then
|
bsw@905
|
325 error("Password hashing algorithm failed")
|
bsw@905
|
326 end
|
bsw@905
|
327
|
bsw/jbe@0
|
328 self.password = hash
|
bsw@1231
|
329 self.password_reset_secret = nil
|
bsw@1231
|
330 self.password_reset_secret_expiry = nil
|
bsw/jbe@0
|
331 end
|
bsw/jbe@0
|
332
|
bsw/jbe@0
|
333 function Member.object:check_password(password)
|
bsw/jbe@0
|
334 if type(password) == "string" and type(self.password) == "string" then
|
bsw@728
|
335 return extos.crypt(password, self.password) == self.password
|
bsw/jbe@0
|
336 else
|
bsw/jbe@0
|
337 return false
|
bsw/jbe@0
|
338 end
|
bsw/jbe@0
|
339 end
|
bsw/jbe@0
|
340
|
bsw@905
|
341 function Member.object_get:password_hash_needs_update()
|
bsw@905
|
342
|
bsw@905
|
343 if self.password == nil then
|
bsw@905
|
344 return nil
|
bsw@905
|
345 end
|
bsw@905
|
346
|
bsw@905
|
347 local function check_rounds(rounds)
|
bsw@905
|
348 if rounds then
|
bsw@905
|
349 rounds = tonumber(rounds)
|
bsw@905
|
350 if
|
bsw@905
|
351 rounds >= config.password_hash_min_rounds and
|
bsw@905
|
352 rounds <= config.password_hash_max_rounds
|
bsw@905
|
353 then
|
bsw@905
|
354 return false
|
bsw@905
|
355 end
|
bsw@905
|
356 end
|
bsw@905
|
357 return true
|
bsw@905
|
358 end
|
bsw@905
|
359
|
bsw@905
|
360 if config.password_hash_algorithm == "crypt_md5" then
|
bsw@905
|
361
|
bsw@905
|
362 return self.password:sub(1,3) ~= "$1$"
|
bsw@905
|
363
|
bsw@905
|
364 elseif config.password_hash_algorithm == "crypt_sha256" then
|
bsw@905
|
365
|
bsw@905
|
366 return check_rounds(self.password:match("^%$5%$rounds=([1-9][0-9]*)%$"))
|
bsw@905
|
367
|
bsw@905
|
368 elseif config.password_hash_algorithm == "crypt_sha512" then
|
bsw@905
|
369
|
bsw@905
|
370 return check_rounds(self.password:match("^%$6%$rounds=([1-9][0-9]*)%$"))
|
bsw@905
|
371
|
bsw@905
|
372 else
|
bsw@905
|
373 error("Unknown hash algorithm selected in configuration")
|
bsw@905
|
374
|
bsw@905
|
375 end
|
bsw@905
|
376
|
bsw@905
|
377 end
|
bsw@905
|
378
|
bsw/jbe@0
|
379 function Member.object_get:published_contacts()
|
bsw/jbe@0
|
380 return Member:new_selector()
|
bsw/jbe@0
|
381 :join('"contact"', nil, '"contact"."other_member_id" = "member"."id"')
|
bsw/jbe@0
|
382 :add_where{ '"contact"."member_id" = ?', self.id }
|
bsw/jbe@0
|
383 :add_where("public")
|
bsw/jbe@0
|
384 :exec()
|
bsw/jbe@0
|
385 end
|
bsw/jbe@0
|
386
|
bsw/jbe@0
|
387 function Member:by_login_and_password(login, password)
|
bsw@1071
|
388
|
bsw@1071
|
389 local function prepare_login_selector()
|
bsw@1071
|
390 local selector = self:new_selector()
|
bsw@1071
|
391 selector:add_field({ "now() > COALESCE(last_delegation_check, activated) + ?::interval", config.check_delegations_interval_hard }, "needs_delegation_check_hard")
|
bsw@1071
|
392 selector:add_where('NOT "locked"')
|
bsw@1071
|
393 selector:optional_object_mode()
|
bsw@1071
|
394 return selector
|
bsw@1071
|
395 end
|
bsw@1071
|
396
|
bsw@1071
|
397 local function do_local_login()
|
bsw@1071
|
398 local selector = prepare_login_selector()
|
bsw@1071
|
399 selector:add_where{'"login" = ?', login }
|
bsw@1071
|
400 local member = selector:exec()
|
bsw@1071
|
401 if member and member:check_password(password) then
|
bsw@1071
|
402 return member
|
bsw@1071
|
403 else
|
bsw@1071
|
404 return nil
|
bsw@1071
|
405 end
|
bsw/jbe@0
|
406 end
|
bsw@1071
|
407
|
bsw@1071
|
408 if config.ldap.member then
|
bsw@1071
|
409
|
bsw@1071
|
410 -- Let's check the users credentials against the LDAP
|
bsw@1071
|
411 local ldap_entry, ldap_err = ldap.check_credentials(login, password)
|
bsw@1071
|
412
|
bsw@1071
|
413 -- Is the user already registered as member?
|
bsw@1071
|
414 local uid
|
bsw@1071
|
415 local selector = prepare_login_selector()
|
bsw@1071
|
416
|
bsw@1071
|
417 -- Get login name from LDAP entry
|
bsw@1071
|
418 if ldap_entry then
|
bsw@1071
|
419 uid = config.ldap.member.uid_map(ldap_entry)
|
bsw@1074
|
420 selector:add_where{'"authority" = ? AND "authority_uid" = ?', "ldap", uid }
|
bsw@1071
|
421
|
bsw@1071
|
422 -- or build it from the login
|
bsw@1071
|
423 else
|
bsw@1071
|
424 login = config.ldap.member.login_normalizer(login)
|
bsw@1074
|
425 selector:add_where{'"authority" = ? AND "authority_uid" = ?', "ldap", login }
|
bsw@1071
|
426 end
|
bsw@1071
|
427
|
bsw@1071
|
428 local member = selector:exec()
|
bsw@1071
|
429 -- The member is already registered
|
bsw@1071
|
430 if member then
|
bsw@1071
|
431
|
bsw@1071
|
432 -- The credentials entered by the user are invalid
|
bsw@1071
|
433 if ldap_err == "invalid_credentials" then
|
bsw@1071
|
434
|
bsw@1071
|
435 -- Check if the user tried a cached password (which is invalid now)
|
bsw@1071
|
436 if config.ldap.member.cache_passwords and member:check_password(password) then
|
bsw@1071
|
437 member.password = nil
|
bsw@1071
|
438 member:save()
|
bsw@1071
|
439 end
|
bsw@1071
|
440
|
bsw@1071
|
441 -- Try a regular login
|
bsw@1071
|
442 return do_local_login()
|
bsw@1071
|
443
|
bsw@1071
|
444 end
|
bsw@1071
|
445
|
bsw@1071
|
446 -- The credentials were accepted by the LDAP server and no error occured
|
bsw@1071
|
447 if ldap_entry and not ldap_err then
|
bsw@1071
|
448
|
bsw@1071
|
449 -- Cache the password (if feature enabled)
|
bsw@1071
|
450 if config.ldap.member.cache_passwords and not member:check_password(password) then
|
bsw@1071
|
451 member:set_password(password)
|
bsw@1071
|
452 end
|
bsw@1071
|
453
|
bsw@1071
|
454 -- update the member attributes and privileges from LDAP
|
bsw@1071
|
455 local ldap_conn, ldap_err, err, err2 = ldap.update_member_attr(member, nil, uid)
|
bsw@1071
|
456 if not err then
|
bsw@1071
|
457 local err = member:try_save()
|
bsw@1071
|
458 if err then
|
bsw@1071
|
459 return nil, "member_save_error", err
|
bsw@1071
|
460 end
|
bsw@1071
|
461 local succes, err, err2 = ldap.update_member_privileges(member, ldap_entry)
|
bsw@1071
|
462 if err then
|
bsw@1071
|
463 return nil, "update_member_privileges_error", err, err2
|
bsw@1071
|
464 end
|
bsw@1071
|
465 return member
|
bsw@1071
|
466 end
|
bsw@1071
|
467
|
bsw@1071
|
468 end
|
bsw@1071
|
469
|
bsw@1071
|
470 -- Some kind of LDAP error happened, if cached password are enabled,
|
bsw@1071
|
471 -- check user credentials against the cache
|
bsw@1071
|
472 if config.ldap.member.cache_passwords and member:check_password(password) then
|
bsw@1071
|
473
|
bsw@1071
|
474 -- return the successfully logged in member
|
bsw@1071
|
475 return member
|
bsw@1071
|
476
|
bsw@1071
|
477 end
|
bsw@1071
|
478
|
bsw@1071
|
479 -- The member is not registered
|
bsw@1071
|
480 elseif config.ldap.member.registration and ldap_entry and not ldap_err then
|
bsw@1071
|
481 -- Automatic registration ("auto")
|
bsw@1071
|
482 if config.ldap.member.registration == "auto" then
|
bsw@1071
|
483 member = Member:new()
|
bsw@1071
|
484 member.authority = "ldap"
|
bsw@1071
|
485 local ldap_login
|
bsw@1071
|
486 if config.ldap.member.cache_passwords then
|
bsw@1071
|
487 if config.ldap.member.login_normalizer then
|
bsw@1071
|
488 ldap_login = config.ldap.member.login_normalizer(login)
|
bsw@1071
|
489 else
|
bsw@1071
|
490 ldap_login = login
|
bsw@1071
|
491 end
|
bsw@1071
|
492 end
|
bsw@1071
|
493 -- TODO change this when SQL layers supports hstore
|
bsw@1074
|
494 member.authority_uid = uid
|
bsw@1074
|
495 member.authority_login = ldap_login
|
bsw@1071
|
496 member.activated = "now"
|
bsw@1071
|
497 member.last_activity = "now"
|
bsw@1071
|
498 if config.ldap.member.cache_passwords then
|
bsw@1071
|
499 member:set_password(password)
|
bsw@1071
|
500 end
|
bsw@1071
|
501 local ldap_conn, ldap_err, err, err2 = ldap.update_member_attr(member, nil, uid)
|
bsw@1071
|
502 if not err then
|
bsw@1071
|
503 local err = member:try_save()
|
bsw@1071
|
504 if err then
|
bsw@1071
|
505 return nil, "member_save_error", err
|
bsw@1071
|
506 end
|
bsw@1071
|
507 local success, err, err2 = ldap.update_member_privileges(member, ldap_entry)
|
bsw@1071
|
508 if err then
|
bsw@1071
|
509 return nil, "update_member_privileges_error", err, err2
|
bsw@1071
|
510 end
|
bsw@1071
|
511 return member
|
bsw@1071
|
512 end
|
bsw@1071
|
513
|
bsw@1071
|
514 -- No automatic registration
|
bsw@1071
|
515 else
|
bsw@1071
|
516 return nil, "ldap_credentials_valid_but_no_member", uid
|
bsw@1071
|
517 end
|
bsw@1071
|
518 end
|
bsw@1071
|
519
|
bsw@1071
|
520 end
|
bsw@1071
|
521
|
bsw@1071
|
522 return do_local_login()
|
bsw@1071
|
523
|
bsw/jbe@0
|
524 end
|
bsw/jbe@0
|
525
|
bsw/jbe@5
|
526 function Member:by_login(login)
|
bsw/jbe@5
|
527 local selector = self:new_selector()
|
bsw/jbe@5
|
528 selector:add_where{'"login" = ?', login }
|
bsw/jbe@5
|
529 selector:optional_object_mode()
|
bsw/jbe@5
|
530 return selector:exec()
|
bsw/jbe@5
|
531 end
|
bsw/jbe@5
|
532
|
bsw/jbe@5
|
533 function Member:by_name(name)
|
bsw/jbe@5
|
534 local selector = self:new_selector()
|
bsw/jbe@5
|
535 selector:add_where{'"name" = ?', name }
|
bsw/jbe@5
|
536 selector:optional_object_mode()
|
bsw/jbe@5
|
537 return selector:exec()
|
bsw/jbe@5
|
538 end
|
bsw/jbe@5
|
539
|
bsw@2
|
540 function Member:get_search_selector(search_string)
|
bsw/jbe@0
|
541 return self:new_selector()
|
bsw@2
|
542 :add_field( {'"highlight"("member"."name", ?)', search_string }, "name_highlighted")
|
bsw@2
|
543 :add_where{ '"member"."text_search_data" @@ "text_search_query"(?)', search_string }
|
bsw@362
|
544 :add_where("activated NOTNULL AND active")
|
bsw/jbe@0
|
545 end
|
bsw@2
|
546
|
bsw@1231
|
547 function Member.object:send_password_reset_mail()
|
bsw@1231
|
548 trace.disable()
|
bsw@1231
|
549 if not self.notify_email then
|
bsw@1231
|
550 return false
|
bsw@1231
|
551 end
|
bsw@1231
|
552 self.password_reset_secret = multirand.string( 24, "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" )
|
bsw@1231
|
553 local expiry = db:query("SELECT now() + '1 days'::interval as expiry", "object").expiry
|
bsw@1231
|
554 self.password_reset_secret_expiry = expiry
|
bsw@1231
|
555 self:save()
|
bsw@1231
|
556 local content = slot.use_temporary(function()
|
bsw@1231
|
557 slot.put(_"Hello " .. self.name .. ",\n\n")
|
bsw@1231
|
558 slot.put(_"to reset your password please click on the following link:\n\n")
|
bsw@1231
|
559 slot.put(request.get_absolute_baseurl() .. "index/reset_password.html?secret=" .. self.password_reset_secret .. "\n\n")
|
bsw@1231
|
560 slot.put(_"If this link is not working, please open following url in your web browser:\n\n")
|
bsw@1231
|
561 slot.put(request.get_absolute_baseurl() .. "index/reset_password.html\n\n")
|
bsw@1231
|
562 slot.put(_"On that page please enter the reset code:\n\n")
|
bsw@1231
|
563 slot.put(self.password_reset_secret .. "\n\n")
|
bsw@1231
|
564 end)
|
bsw@1231
|
565 local success = net.send_mail{
|
bsw@1231
|
566 envelope_from = config.mail_envelope_from,
|
bsw@1231
|
567 from = config.mail_from,
|
bsw@1231
|
568 reply_to = config.mail_reply_to,
|
bsw@1231
|
569 to = self.notify_email,
|
bsw@1231
|
570 subject = config.mail_subject_prefix .. _"Password reset request",
|
bsw@1231
|
571 content_type = "text/plain; charset=UTF-8",
|
bsw@1231
|
572 content = content
|
bsw@1231
|
573 }
|
bsw@1231
|
574 return success
|
bsw@1231
|
575 end
|
bsw@1231
|
576
|
bsw@388
|
577 function Member.object:send_invitation(template_file, subject)
|
bsw@286
|
578 trace.disable()
|
bsw@286
|
579 self.invite_code = multirand.string( 24, "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" )
|
bsw@388
|
580 self:save()
|
bsw@388
|
581
|
bsw@388
|
582 local subject = subject
|
bsw@388
|
583 local content
|
bsw@388
|
584
|
bsw@388
|
585 if template_file then
|
bsw@388
|
586 local fh = io.open(template_file, "r")
|
bsw@388
|
587 content = fh:read("*a")
|
bsw@388
|
588 content = (content:gsub("#{invite_code}", self.invite_code))
|
bsw@388
|
589 else
|
bsw@388
|
590 subject = config.mail_subject_prefix .. _"Invitation to LiquidFeedback"
|
bsw@388
|
591 content = slot.use_temporary(function()
|
bsw@388
|
592 slot.put(_"Hello\n\n")
|
bsw@388
|
593 slot.put(_"You are invited to LiquidFeedback. To register please click the following link:\n\n")
|
bsw@388
|
594 slot.put(request.get_absolute_baseurl() .. "index/register.html?invite=" .. self.invite_code .. "\n\n")
|
bsw@388
|
595 slot.put(_"If this link is not working, please open following url in your web browser:\n\n")
|
bsw@388
|
596 slot.put(request.get_absolute_baseurl() .. "index/register.html\n\n")
|
bsw@388
|
597 slot.put(_"On that page please enter the invite key:\n\n")
|
bsw@388
|
598 slot.put(self.invite_code .. "\n\n")
|
bsw@388
|
599 end)
|
bsw@388
|
600 end
|
bsw@388
|
601
|
bsw@286
|
602 local success = net.send_mail{
|
bsw@286
|
603 envelope_from = config.mail_envelope_from,
|
bsw@286
|
604 from = config.mail_from,
|
bsw@286
|
605 reply_to = config.mail_reply_to,
|
bsw@286
|
606 to = self.notify_email_unconfirmed or self.notify_email,
|
bsw@388
|
607 subject = subject,
|
bsw@286
|
608 content_type = "text/plain; charset=UTF-8",
|
bsw@286
|
609 content = content
|
bsw@286
|
610 }
|
bsw@286
|
611 return success
|
bsw/jbe@0
|
612 end
|
bsw@2
|
613
|
bsw/jbe@6
|
614 function Member.object:set_notify_email(notify_email)
|
bsw@224
|
615 trace.disable()
|
bsw/jbe@6
|
616 local expiry = db:query("SELECT now() + '7 days'::interval as expiry", "object").expiry
|
bsw/jbe@6
|
617 self.notify_email_unconfirmed = notify_email
|
bsw/jbe@6
|
618 self.notify_email_secret = multirand.string( 24, "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" )
|
bsw/jbe@6
|
619 self.notify_email_secret_expiry = expiry
|
bsw/jbe@6
|
620 local content = slot.use_temporary(function()
|
bsw/jbe@6
|
621 slot.put(_"Hello " .. self.name .. ",\n\n")
|
bsw/jbe@6
|
622 slot.put(_"Please confirm your email address by clicking the following link:\n\n")
|
jbe@326
|
623 slot.put(request.get_absolute_baseurl() .. "index/confirm_notify_email.html?secret=" .. self.notify_email_secret .. "\n\n")
|
bsw/jbe@6
|
624 slot.put(_"If this link is not working, please open following url in your web browser:\n\n")
|
jbe@326
|
625 slot.put(request.get_absolute_baseurl() .. "index/confirm_notify_email.html\n\n")
|
bsw/jbe@6
|
626 slot.put(_"On that page please enter the confirmation code:\n\n")
|
bsw/jbe@6
|
627 slot.put(self.notify_email_secret .. "\n\n")
|
bsw/jbe@6
|
628 end)
|
bsw/jbe@6
|
629 local success = net.send_mail{
|
bsw/jbe@6
|
630 envelope_from = config.mail_envelope_from,
|
bsw/jbe@6
|
631 from = config.mail_from,
|
bsw/jbe@6
|
632 reply_to = config.mail_reply_to,
|
bsw/jbe@6
|
633 to = self.notify_email_unconfirmed,
|
bsw/jbe@6
|
634 subject = config.mail_subject_prefix .. _"Email confirmation request",
|
bsw/jbe@6
|
635 content_type = "text/plain; charset=UTF-8",
|
bsw/jbe@6
|
636 content = content
|
bsw/jbe@6
|
637 }
|
bsw@75
|
638 if success then
|
bsw@75
|
639 local lock_expiry = db:query("SELECT now() + '1 hour'::interval AS lock_expiry", "object").lock_expiry
|
bsw@75
|
640 self.notify_email_lock_expiry = lock_expiry
|
bsw@75
|
641 end
|
bsw@75
|
642 self:save()
|
bsw/jbe@6
|
643 return success
|
bsw/jbe@6
|
644 end
|
bsw@11
|
645
|
bsw/jbe@19
|
646 function Member.object:get_setting(key)
|
bsw@79
|
647 return Setting:by_pk(self.id, key)
|
bsw/jbe@19
|
648 end
|
bsw/jbe@19
|
649
|
bsw/jbe@19
|
650 function Member.object:get_setting_value(key)
|
bsw@79
|
651 local setting = Setting:by_pk(self.id, key)
|
bsw/jbe@19
|
652 if setting then
|
bsw/jbe@19
|
653 return setting.value
|
bsw/jbe@19
|
654 end
|
bsw@11
|
655 end
|
bsw@11
|
656
|
bsw@11
|
657 function Member.object:set_setting(key, value)
|
bsw/jbe@19
|
658 local setting = self:get_setting(key)
|
bsw/jbe@19
|
659 if not setting then
|
bsw/jbe@19
|
660 setting = Setting:new()
|
bsw@79
|
661 setting.member_id = self.id
|
bsw/jbe@19
|
662 setting.key = key
|
bsw/jbe@19
|
663 end
|
bsw/jbe@19
|
664 setting.value = value
|
bsw/jbe@19
|
665 setting:save()
|
bsw@11
|
666 end
|
bsw@11
|
667
|
bsw@11
|
668 function Member.object:get_setting_maps_by_key(key)
|
bsw@11
|
669 return SettingMap:new_selector()
|
bsw@11
|
670 :add_where{ "member_id = ?", self.id }
|
bsw@11
|
671 :add_where{ "key = ?", key }
|
bsw@11
|
672 :add_order_by("subkey")
|
bsw@11
|
673 :exec()
|
bsw@11
|
674 end
|
bsw@11
|
675
|
bsw@11
|
676 function Member.object:get_setting_map_by_key_and_subkey(key, subkey)
|
bsw@11
|
677 return SettingMap:new_selector()
|
bsw@11
|
678 :add_where{ "member_id = ?", self.id }
|
bsw@11
|
679 :add_where{ "key = ?", key }
|
bsw@11
|
680 :add_where{ "subkey = ?", subkey }
|
bsw@11
|
681 :add_order_by("subkey")
|
bsw@11
|
682 :optional_object_mode()
|
bsw@11
|
683 :exec()
|
bsw@11
|
684 end
|
bsw@11
|
685
|
bsw@11
|
686 function Member.object:set_setting_map(key, subkey, value)
|
poelzi@144
|
687 setting_map = self:get_setting_map_by_key_and_subkey(key, subkey)
|
poelzi@144
|
688 if not setting_map then
|
poelzi@144
|
689 setting_map = SettingMap:new()
|
poelzi@144
|
690 setting_map.member_id = self.id
|
poelzi@144
|
691 setting_map.key = key
|
poelzi@144
|
692 setting_map.subkey = subkey
|
poelzi@144
|
693 end
|
poelzi@144
|
694 setting_map.value = value
|
poelzi@144
|
695 setting_map:save()
|
bsw@11
|
696 end
|
bsw@75
|
697
|
bsw@75
|
698 function Member.object_get:notify_email_locked()
|
bsw@75
|
699 return(
|
bsw@75
|
700 Member:new_selector()
|
bsw@75
|
701 :add_where{ "id = ?", app.session.member.id }
|
bsw@75
|
702 :add_where("notify_email_lock_expiry > now()")
|
bsw@75
|
703 :count() == 1
|
bsw@75
|
704 )
|
poelzi@134
|
705 end
|
poelzi@134
|
706
|
bsw@273
|
707 function Member.object_get:units_with_voting_right()
|
bsw@273
|
708 return(Unit:new_selector()
|
bsw@273
|
709 :join("privilege", nil, { "privilege.unit_id = unit.id AND privilege.member_id = ? AND privilege.voting_right", self.id })
|
bsw@273
|
710 :exec()
|
bsw@273
|
711 )
|
bsw@273
|
712 end
|
bsw@273
|
713
|
poelzi@134
|
714 function Member.object:ui_field_text(args)
|
poelzi@134
|
715 args = args or {}
|
bsw@813
|
716 if app.session:has_access("authors_pseudonymous") then
|
poelzi@134
|
717 -- ugly workaround for getting html into a replaced string and to the user
|
poelzi@134
|
718 ui.container{label = args.label, label_attr={class="ui_field_label"}, content = function()
|
poelzi@134
|
719 slot.put(string.format('<span><a href="%s">%s</a></span>',
|
poelzi@134
|
720 encode.url{
|
poelzi@134
|
721 module = "member",
|
poelzi@134
|
722 view = "show",
|
poelzi@134
|
723 id = self.id,
|
poelzi@134
|
724 },
|
poelzi@134
|
725 encode.html(self.name)))
|
poelzi@134
|
726 end
|
poelzi@134
|
727 }
|
poelzi@134
|
728 else
|
poelzi@134
|
729 ui.field.text{ label = args.label, value = _"[not displayed public]" }
|
poelzi@134
|
730 end
|
poelzi@134
|
731 end
|
bsw@281
|
732
|
bsw@281
|
733 function Member.object:has_voting_right_for_unit_id(unit_id)
|
bsw@547
|
734 if not self.__units_with_voting_right_hash then
|
bsw@547
|
735 local privileges = Privilege:new_selector()
|
bsw@547
|
736 :add_where{ "member_id = ?", self.id }
|
bsw@547
|
737 :add_where("voting_right")
|
bsw@547
|
738 :exec()
|
bsw@547
|
739 self.__units_with_voting_right_hash = {}
|
bsw@551
|
740 for i, privilege in ipairs(privileges) do
|
bsw@551
|
741 self.__units_with_voting_right_hash[privilege.unit_id] = true
|
bsw@551
|
742 end
|
bsw@547
|
743 end
|
bsw@547
|
744 return self.__units_with_voting_right_hash[unit_id] and true or false
|
jbe@326
|
745 end
|
bsw@525
|
746
|
bsw@894
|
747 function Member.object:has_polling_right_for_unit_id(unit_id)
|
bsw@894
|
748 if not self.__units_with_polling_right_hash then
|
bsw@894
|
749 local privileges = Privilege:new_selector()
|
bsw@894
|
750 :add_where{ "member_id = ?", self.id }
|
bsw@894
|
751 :add_where("polling_right")
|
bsw@894
|
752 :exec()
|
bsw@894
|
753 self.__units_with_polling_right_hash = {}
|
bsw@894
|
754 for i, privilege in ipairs(privileges) do
|
bsw@894
|
755 self.__units_with_polling_right_hash[privilege.unit_id] = true
|
bsw@894
|
756 end
|
bsw@894
|
757 end
|
bsw@894
|
758 return self.__units_with_polling_right_hash[unit_id] and true or false
|
bsw@894
|
759 end
|
bsw@894
|
760
|
bsw@525
|
761 function Member.object:get_delegatee_member(unit_id, area_id, issue_id)
|
bsw@525
|
762 local selector = Member:new_selector()
|
bsw@525
|
763 if unit_id then
|
bsw@525
|
764 selector:join("delegation", nil, { "delegation.trustee_id = member.id AND delegation.scope = 'unit' AND delegation.unit_id = ? AND delegation.truster_id = ?", unit_id, self.id })
|
bsw@525
|
765 end
|
bsw@525
|
766 selector:optional_object_mode()
|
bsw@525
|
767 return selector:exec()
|
bsw@533
|
768 end
|
bsw@929
|
769
|
bsw@1088
|
770 function Member.object:delete()
|
bsw@1088
|
771 db:query{ "SELECT delete_member(?)", self.id }
|
bsw@1088
|
772 end
|