liquid_feedback_frontend

annotate model/member.lua @ 1676:8fde003bdeb0

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

Impressum / About Us