liquid_feedback_frontend

changeset 1071:58f48a8a202a

Imported and merged LDAP patch
author bsw
date Fri Jul 18 21:42:59 2014 +0200 (2014-07-18)
parents effce9b361b2
children ba51e72830e6
files app/main/_filter/21_auth.lua app/main/_filter_view/30_navigation.lua app/main/index/_action/cancel_register.lua app/main/index/_action/login.lua app/main/index/_action/register.lua app/main/index/register.lua app/main/member/_action/update.lua app/main/member/_action/update_email.lua app/main/member/_action/update_login.lua app/main/member/_action/update_name.lua app/main/member/_profile.lua app/main/member/_sidebar_whatcanido.lua config/example.lua config/init.lua env/encode/pg_hstore.lua env/encode/pg_hstore_value.lua env/ldap/__init.lua env/ldap/bind.lua env/ldap/bind_as_app.lua env/ldap/check_credentials.lua env/ldap/create_member.lua env/ldap/escape_filter.lua env/ldap/get_hosts.lua env/ldap/get_member_entry.lua env/ldap/test.lua env/ldap/update_all_members.lua env/ldap/update_member_attr.lua env/ldap/update_member_privileges.lua env/util/is_profile_field_locked.lua lib/mldap/Makefile lib/mldap/convert_errorcodes.lua lib/mldap/mldap.c lib/mldap/mldap_errorcodes.c model/member.lua model/privilege.lua model/session.lua
line diff
     1.1 --- a/app/main/_filter/21_auth.lua	Thu Jul 17 23:38:35 2014 +0200
     1.2 +++ b/app/main/_filter/21_auth.lua	Fri Jul 18 21:42:59 2014 +0200
     1.3 @@ -9,6 +9,7 @@
     1.4      or action == "login"
     1.5      or view   == "register"
     1.6      or action == "register"
     1.7 +    or action == "cancel_register"
     1.8      or view   == "about"
     1.9      or view   == "reset_password"
    1.10      or action == "reset_password"
     2.1 --- a/app/main/_filter_view/30_navigation.lua	Thu Jul 17 23:38:35 2014 +0200
     2.2 +++ b/app/main/_filter_view/30_navigation.lua	Fri Jul 18 21:42:59 2014 +0200
     2.3 @@ -46,6 +46,9 @@
     2.4      }
     2.5      
     2.6      slot.put ( " " )
     2.7 +  end
     2.8 +  
     2.9 +  if app.session.member == nil and not config.registration_disabled then
    2.10      
    2.11      ui.link {
    2.12        text   = _"Registration",
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/app/main/index/_action/cancel_register.lua	Fri Jul 18 21:42:59 2014 +0200
     3.3 @@ -0,0 +1,5 @@
     3.4 +app.session.authority = nil
     3.5 +app.session.authority_data = nil
     3.6 +app.session:save()
     3.7 +
     3.8 +return true
     3.9 \ No newline at end of file
     4.1 --- a/app/main/index/_action/login.lua	Thu Jul 17 23:38:35 2014 +0200
     4.2 +++ b/app/main/index/_action/login.lua	Fri Jul 18 21:42:59 2014 +0200
     4.3 @@ -1,7 +1,22 @@
     4.4  local login = param.get("login")
     4.5  local password = param.get("password")
     4.6  
     4.7 -local member = Member:by_login_and_password(login, password)
     4.8 +local member, err, uid = Member:by_login_and_password(login, password)
     4.9 +
    4.10 +if err == "ldap_credentials_valid_but_no_member" then
    4.11 +  app.session.authority = "ldap"
    4.12 +  app.session.authority_data = encode.pg_hstore{
    4.13 +    login = login,
    4.14 +    uid = uid
    4.15 +  }
    4.16 +  app.session:save()
    4.17 +  request.redirect{
    4.18 +    module = "index", view = "register", params = {
    4.19 +      ldap_login = login
    4.20 +    }
    4.21 +  }
    4.22 +  return
    4.23 +end
    4.24  
    4.25  function do_etherpad_auth(member)
    4.26    local result = net.curl(
     5.1 --- a/app/main/index/_action/register.lua	Thu Jul 17 23:38:35 2014 +0200
     5.2 +++ b/app/main/index/_action/register.lua	Fri Jul 18 21:42:59 2014 +0200
     5.3 @@ -1,12 +1,26 @@
     5.4  local code = util.trim(param.get("code"))
     5.5  
     5.6 -local member = Member:new_selector()
     5.7 -  :add_where{ "invite_code = ?", code }
     5.8 -  :add_where{ "activated ISNULL" }
     5.9 -  :add_where{ "NOT locked" }
    5.10 -  :optional_object_mode()
    5.11 -  :for_update()
    5.12 -  :exec()
    5.13 +local member
    5.14 +
    5.15 +if app.session.authority == "ldap" then
    5.16 +  if not config.ldap.member or not config.ldap.member.registration == "manual" then
    5.17 +    error("access denied")
    5.18 +  end
    5.19 +  member = ldap.create_member(app.session.authority_data_uid, true)
    5.20 +  
    5.21 +else
    5.22 +  if config.registration_disabled then
    5.23 +    error("registration disabled")
    5.24 +  end
    5.25 +  member = Member:new_selector()
    5.26 +    :add_where{ "invite_code = ?", code }
    5.27 +    :add_where{ "activated ISNULL" }
    5.28 +    :add_where{ "NOT locked" }
    5.29 +    :optional_object_mode()
    5.30 +    :for_update()
    5.31 +    :exec()
    5.32 +end
    5.33 +
    5.34    
    5.35  if not member then
    5.36    slot.put_into("error", _"The code you've entered is invalid")
    5.37 @@ -20,7 +34,7 @@
    5.38  
    5.39  local notify_email = param.get("notify_email")
    5.40  
    5.41 -if not config.locked_profile_fields.notify_email and notify_email then
    5.42 +if not util.is_profile_field_locked(member, "notify_email") and notify_email then
    5.43    if #notify_email < 5 then
    5.44      slot.put_into("error", _"Email address too short!")
    5.45      request.redirect{
    5.46 @@ -33,7 +47,7 @@
    5.47    end
    5.48  end
    5.49  
    5.50 -if member and not notify_email then
    5.51 +if member and not util.is_profile_field_locked(member, "notify_email") and not notify_email then
    5.52    request.redirect{
    5.53      mode   = "redirect",
    5.54      module = "index",
    5.55 @@ -46,7 +60,7 @@
    5.56  
    5.57  local name = util.trim(param.get("name"))
    5.58  
    5.59 -if not config.locked_profile_fields.name and name then
    5.60 +if not util.is_profile_field_locked(member, "name") and name then
    5.61  
    5.62    if #name < 3 then
    5.63      slot.put_into("error", _"This screen name is too short!")
    5.64 @@ -83,7 +97,7 @@
    5.65  
    5.66  end
    5.67  
    5.68 -if notify_email and not member.name then
    5.69 +if notify_email and not util.is_profile_field_locked(member, "name") and not member.name then
    5.70    request.redirect{
    5.71      mode   = "redirect",
    5.72      module = "index",
    5.73 @@ -97,10 +111,9 @@
    5.74    return false
    5.75  end
    5.76  
    5.77 -
    5.78  local login = util.trim(param.get("login"))
    5.79  
    5.80 -if not config.locked_profile_fields.login and login then
    5.81 +if not util.is_profile_field_locked(member, "login") and login then
    5.82    if #login < 3 then 
    5.83      slot.put_into("error", _"This login is too short!")
    5.84      request.redirect{
    5.85 @@ -136,7 +149,7 @@
    5.86    member.login = login
    5.87  end
    5.88  
    5.89 -if member.name and not member.login then
    5.90 +if member.name and not util.is_profile_field_locked(member, "login") and not member.login then
    5.91    request.redirect{
    5.92      mode   = "redirect",
    5.93      module = "index",
    5.94 @@ -163,40 +176,43 @@
    5.95      end
    5.96    end  
    5.97  
    5.98 -  local password1 = param.get("password1")
    5.99 -  local password2 = param.get("password2")
   5.100 +  if not member.authority == "ldap" then
   5.101 +  
   5.102 +    local password1 = param.get("password1")
   5.103 +    local password2 = param.get("password2")
   5.104  
   5.105 -  if login and not password1 then
   5.106 -    request.redirect{
   5.107 -      mode   = "redirect",
   5.108 -      module = "index",
   5.109 -      view   = "register",
   5.110 -      params = { 
   5.111 -        code = member.invite_code,
   5.112 -        notify_email = notify_email,
   5.113 -        name = member.name,
   5.114 -        login = member.login
   5.115 +    if login and not password1 then
   5.116 +      request.redirect{
   5.117 +        mode   = "redirect",
   5.118 +        module = "index",
   5.119 +        view   = "register",
   5.120 +        params = { 
   5.121 +          code = member.invite_code,
   5.122 +          notify_email = notify_email,
   5.123 +          name = member.name,
   5.124 +          login = member.login
   5.125 +        }
   5.126        }
   5.127 -    }
   5.128 -  --]]
   5.129 -    return false
   5.130 +    --]]
   5.131 +      return false
   5.132 +    end
   5.133 +
   5.134 +    if password1 ~= password2 then
   5.135 +      slot.put_into("error", _"Passwords don't match!")
   5.136 +      return false
   5.137 +    end
   5.138 +
   5.139 +    if #password1 < 8 then
   5.140 +      slot.put_into("error", _"Passwords must consist of at least 8 characters!")
   5.141 +      return false
   5.142 +    end
   5.143    end
   5.144  
   5.145 -  if password1 ~= password2 then
   5.146 -    slot.put_into("error", _"Passwords don't match!")
   5.147 -    return false
   5.148 -  end
   5.149 -
   5.150 -  if #password1 < 8 then
   5.151 -    slot.put_into("error", _"Passwords must consist of at least 8 characters!")
   5.152 -    return false
   5.153 -  end
   5.154 -
   5.155 -  if not config.locked_profile_fields.login then
   5.156 +  if not util.is_profile_field_locked(member, "login") then
   5.157      member.login = login
   5.158    end
   5.159  
   5.160 -  if not config.locked_profile_fields.name then
   5.161 +  if not util.is_profile_field_locked(member, "name") then
   5.162      member.name = name
   5.163    end
   5.164  
   5.165 @@ -208,7 +224,9 @@
   5.166      end
   5.167    end
   5.168    
   5.169 -  member:set_password(password1)
   5.170 +  if not member.authority == "ldap" then
   5.171 +    member:set_password(password1)
   5.172 +  end
   5.173  
   5.174    local now = db:query("SELECT now() AS now", "object").now
   5.175  
   5.176 @@ -221,7 +239,7 @@
   5.177    member.active = true
   5.178    member.last_activity = 'now'
   5.179    member:save()
   5.180 -
   5.181 +  
   5.182    slot.put_into("notice", _"You've successfully registered and you can login now with your login and password!")
   5.183  
   5.184    request.redirect{
     6.1 --- a/app/main/index/register.lua	Thu Jul 17 23:38:35 2014 +0200
     6.2 +++ b/app/main/index/register.lua	Fri Jul 18 21:42:59 2014 +0200
     6.3 @@ -1,3 +1,13 @@
     6.4 +local ldap_uid
     6.5 +
     6.6 +if config.ldap.member and app.session.authority == "ldap" then
     6.7 +  ldap_uid = app.session.authority_data_uid
     6.8 +end
     6.9 +
    6.10 +if config.registration_disabled and not ldap_uid then
    6.11 +  error("registration disabled")
    6.12 +end
    6.13 +
    6.14  execute.view{ module = "index", view = "_lang_chooser" }
    6.15  
    6.16  local step = param.get("step", atom.integer)
    6.17 @@ -6,6 +16,7 @@
    6.18  local name = param.get("name")
    6.19  local login = param.get("login")
    6.20  
    6.21 +
    6.22  ui.form{
    6.23    attr = { class = "section vertical" },
    6.24    module = 'index',
    6.25 @@ -18,7 +29,7 @@
    6.26    },
    6.27    content = function()
    6.28  
    6.29 -    if not code then
    6.30 +    if not code and not ldap_uid then
    6.31        ui.field.hidden{ name = "step", value = 1 }
    6.32        ui.title(_"Registration (step 1 of 3: Invite code)")
    6.33        ui.sectionHead( function()
    6.34 @@ -37,17 +48,32 @@
    6.35          ui.link{
    6.36            content = _"cancel registration",
    6.37            module = "index",
    6.38 -          view = "index"
    6.39 +          action = "cancel_register",
    6.40 +          routing = { default = {
    6.41 +            mode = "redirect", module = "index", view = "index"
    6.42 +          } }
    6.43          }
    6.44        end )
    6.45      else
    6.46 -      local member = Member:new_selector()
    6.47 -        :add_where{ "invite_code = ?", code }
    6.48 -        :add_where{ "activated ISNULL" }
    6.49 -        :optional_object_mode()
    6.50 -        :exec()
    6.51 +      local member
    6.52 +      
    6.53 +      if ldap_uid then
    6.54 +        member, err = ldap.create_member(ldap_uid, true)
    6.55 +        if err then
    6.56 +          error(err)
    6.57 +        end
    6.58 +      else
    6.59 +        member = Member:new_selector()
    6.60 +          :add_where{ "invite_code = ?", code }
    6.61 +          :add_where{ "activated ISNULL" }
    6.62 +          :optional_object_mode()
    6.63 +          :exec()
    6.64 +      end
    6.65  
    6.66 -      if not member.notify_email and not notify_email or not member.name and not name or not member.login and not login or step == 1 then
    6.67 +      if    (not member.notify_email and not notify_email)
    6.68 +         or (not member.name and not name)
    6.69 +         or (not member.login and not login and not member.authority)
    6.70 +         or step == 1 then
    6.71          ui.title(_"Registration (step 2 of 3: Personal information)")
    6.72          ui.field.hidden{ name = "step", value = 2 }
    6.73  
    6.74 @@ -62,7 +88,7 @@
    6.75            
    6.76            execute.view{ module = "member", view = "_profile", params = { member = member, for_registration = true } }
    6.77  
    6.78 -          if not config.locked_profile_fields.notify_email then
    6.79 +          if not util.is_profile_field_locked(member, "notify_email") then
    6.80              ui.tag{
    6.81                tag = "p",
    6.82                content = _"Please enter your email address. This address will be used for automatic notifications (if you request them) and in case you've lost your password. This address will not be published. After registration you will receive an email with a confirmation link."
    6.83 @@ -73,7 +99,7 @@
    6.84                value     = param.get("notify_email") or member.notify_email
    6.85              }
    6.86            end
    6.87 -          if not config.locked_profile_fields.name then
    6.88 +          if not util.is_profile_field_locked(member, "name") then
    6.89              ui.tag{
    6.90                tag = "p",
    6.91                content = _"Please choose a name, i.e. your real name or your nick name. This name will be shown to others to identify you."
    6.92 @@ -84,7 +110,7 @@
    6.93                value     = param.get("name") or member.name
    6.94              }
    6.95            end
    6.96 -          if not config.locked_profile_fields.login then
    6.97 +          if not util.is_profile_field_locked(member, "login") then
    6.98              ui.tag{
    6.99                tag = "p",
   6.100                content = _"Please choose a login name. This name will not be shown to others and is used only by you to login into the system. The login name is case sensitive."
   6.101 @@ -111,7 +137,10 @@
   6.102            ui.link{
   6.103              content = _"cancel registration",
   6.104              module = "index",
   6.105 -            view = "index"
   6.106 +            action = "cancel_register",
   6.107 +            routing = { default = {
   6.108 +              mode = "redirect", module = "index", view = "index"
   6.109 +            } }
   6.110            }
   6.111          end )
   6.112        else
   6.113 @@ -165,24 +194,27 @@
   6.114  
   6.115            slot.put("<br />")
   6.116  
   6.117 -          ui.tag{
   6.118 -            tag = "p",
   6.119 -            content = _"Please choose a password and enter it twice. The password is case sensitive."
   6.120 -          }
   6.121 -          ui.field.text{
   6.122 -            readonly  = true,
   6.123 -            label     = _'Login name',
   6.124 -            name      = 'login',
   6.125 -            value     = member.login
   6.126 -          }
   6.127 -          ui.field.password{
   6.128 -            label     = _'Password',
   6.129 -            name      = 'password1',
   6.130 -          }
   6.131 -          ui.field.password{
   6.132 -            label     = _'Password (repeat)',
   6.133 -            name      = 'password2',
   6.134 -          }
   6.135 +          if not member.authority == "ldap" then
   6.136 +            ui.tag{
   6.137 +              tag = "p",
   6.138 +              content = _"Please choose a password and enter it twice. The password is case sensitive."
   6.139 +            }
   6.140 +            ui.field.text{
   6.141 +              readonly  = true,
   6.142 +              label     = _'Login name',
   6.143 +              name      = 'login',
   6.144 +              value     = member.login
   6.145 +            }
   6.146 +            ui.field.password{
   6.147 +              label     = _'Password',
   6.148 +              name      = 'password1',
   6.149 +            }
   6.150 +            ui.field.password{
   6.151 +              label     = _'Password (repeat)',
   6.152 +              name      = 'password2',
   6.153 +            }
   6.154 +          end
   6.155 +          
   6.156            ui.submit{
   6.157              text = _'activate account'
   6.158            }
   6.159 @@ -203,7 +235,10 @@
   6.160            ui.link{
   6.161              content = _"cancel registration",
   6.162              module = "index",
   6.163 -            view = "index"
   6.164 +            action = "cancel_register",
   6.165 +            routing = { default = {
   6.166 +              mode = "redirect", module = "index", view = "index"
   6.167 +            } }
   6.168            }
   6.169          end )
   6.170        end
     7.1 --- a/app/main/member/_action/update.lua	Thu Jul 17 23:38:35 2014 +0200
     7.2 +++ b/app/main/member/_action/update.lua	Fri Jul 18 21:42:59 2014 +0200
     7.3 @@ -17,12 +17,12 @@
     7.4  local update_args = { app.session.member }
     7.5  
     7.6  for i, field in ipairs(fields) do
     7.7 -  if not config.locked_profile_fields[field] then
     7.8 +  if not util.is_profile_field_locked(app.session.member, field) then
     7.9      param.update(app.session.member, field)
    7.10    end
    7.11  end
    7.12  
    7.13 -if not config.locked_profile_fields.statement then
    7.14 +if not util.is_profile_field_locked(app.session.member, "statement") then
    7.15    local formatting_engine = param.get("formatting_engine") or config.enforce_formatting_engine
    7.16  
    7.17    local formatting_engine_valid = false
    7.18 @@ -47,7 +47,7 @@
    7.19  
    7.20  end
    7.21  
    7.22 -if not config.locked_profile_fields.birthday then
    7.23 +if not util.is_profile_field_locked(app.session.member, "birthday") then
    7.24    if tostring(app.session.member.birthday) == "invalid_date" then
    7.25      app.session.member.birthday = nil
    7.26      slot.put_into("error", _"Date format is not valid. Please use following format: YYYY-MM-DD")
     8.1 --- a/app/main/member/_action/update_email.lua	Thu Jul 17 23:38:35 2014 +0200
     8.2 +++ b/app/main/member/_action/update_email.lua	Fri Jul 18 21:42:59 2014 +0200
     8.3 @@ -1,6 +1,6 @@
     8.4  local resend = param.get("resend", atom.boolean)
     8.5  
     8.6 -if not resend and config.locked_profile_fields.notify_email then
     8.7 +if not resend and util.is_profile_field_locked(app.session.member, "notify_email") then
     8.8    error("access denied")
     8.9  end
    8.10  
     9.1 --- a/app/main/member/_action/update_login.lua	Thu Jul 17 23:38:35 2014 +0200
     9.2 +++ b/app/main/member/_action/update_login.lua	Fri Jul 18 21:42:59 2014 +0200
     9.3 @@ -1,4 +1,4 @@
     9.4 -if config.locked_profile_fields.login then
     9.5 +if util.is_profile_field_locked(app.session.member, "login") then
     9.6    error("access denied")
     9.7  end
     9.8  
    10.1 --- a/app/main/member/_action/update_name.lua	Thu Jul 17 23:38:35 2014 +0200
    10.2 +++ b/app/main/member/_action/update_name.lua	Fri Jul 18 21:42:59 2014 +0200
    10.3 @@ -1,4 +1,4 @@
    10.4 -if config.locked_profile_fields.name then
    10.5 +if util.is_profile_field_locked(app.session.member, "name") then
    10.6    error("access denied")
    10.7  end
    10.8  
    11.1 --- a/app/main/member/_profile.lua	Thu Jul 17 23:38:35 2014 +0200
    11.2 +++ b/app/main/member/_profile.lua	Fri Jul 18 21:42:59 2014 +0200
    11.3 @@ -104,7 +104,7 @@
    11.4      if member.last_activity then
    11.5        ui.field.text{ label = _"Last activity (updated daily)", value = format.date(member.last_activity) or _"not yet" }
    11.6      end
    11.7 -    if member.statement and #member.statement > 0 then
    11.8 +    if member.id and member.statement and #member.statement > 0 then
    11.9        slot.put("<br />")
   11.10        slot.put("<br />")
   11.11        ui.container{
    12.1 --- a/app/main/member/_sidebar_whatcanido.lua	Thu Jul 17 23:38:35 2014 +0200
    12.2 +++ b/app/main/member/_sidebar_whatcanido.lua	Fri Jul 18 21:42:59 2014 +0200
    12.3 @@ -69,16 +69,18 @@
    12.4        local pages = {}
    12.5  
    12.6        pages[#pages+1] = { view = "settings_notification", text = _"notification settings" }
    12.7 -      if not config.locked_profile_fields.notify_email then
    12.8 +      if not util.is_profile_field_locked(app.session.member, "notify_email") then
    12.9          pages[#pages+1] = { view = "settings_email",          text = _"change your notification email address" }
   12.10        end
   12.11 -      if not config.locked_profile_fields.name then
   12.12 +      if not util.is_profile_field_locked(app.session.member, "name") then
   12.13          pages[#pages+1] = { view = "settings_name",           text = _"change your screen name" }
   12.14        end
   12.15 -      if not config.locked_profile_fields.login then
   12.16 +      if not util.is_profile_field_locked(app.session.member, "login") then
   12.17          pages[#pages+1] = { view = "settings_login",          text = _"change your login" }
   12.18        end
   12.19 -      pages[#pages+1] = { view = "settings_password",       text = _"change your password" }
   12.20 +      if not util.is_profile_field_locked(app.session.member, "password") then
   12.21 +        pages[#pages+1] = { view = "settings_password",       text = _"change your password" }
   12.22 +      end
   12.23        pages[#pages+1] = { view = "developer_settings",      text = _"developer settings" }
   12.24  
   12.25        if config.download_dir then
    13.1 --- a/config/example.lua	Thu Jul 17 23:38:35 2014 +0200
    13.2 +++ b/config/example.lua	Fri Jul 18 21:42:59 2014 +0200
    13.3 @@ -96,6 +96,14 @@
    13.4  -- Remove leading -- to use a option
    13.5  -- ========================================================================
    13.6  
    13.7 +-- Disable registration
    13.8 +-- ------------------------------------------------------------------------
    13.9 +-- Available options:
   13.10 +-- false: registration is enabled (default)
   13.11 +-- true: registration is disabled
   13.12 +-- ------------------------------------------------------------------------
   13.13 +-- config.disable_registration = true
   13.14 +
   13.15  -- List of enabled languages, defaults to available languages
   13.16  -- ------------------------------------------------------------------------
   13.17  -- config.enabled_languages = { 'en', 'de', 'eo', 'el', 'hu', 'it', 'nl', 'zh-Hans', 'zh-TW' }
    14.1 --- a/config/init.lua	Thu Jul 17 23:38:35 2014 +0200
    14.2 +++ b/config/init.lua	Fri Jul 18 21:42:59 2014 +0200
    14.3 @@ -3,7 +3,7 @@
    14.4  -- (except when you really know what you are doing!)
    14.5  -- ========================================================================
    14.6  
    14.7 -config.app_version = "3.0.2"
    14.8 +config.app_version = "3.0.2+ldap"
    14.9  
   14.10  if not config.password_hash_algorithm then
   14.11    config.password_hash_algorithm = "crypt_sha512"
   14.12 @@ -48,6 +48,10 @@
   14.13    config.check_delegations_default = "confirm"
   14.14  end
   14.15  
   14.16 +if config.ldap == nil then
   14.17 +  config.ldap = {}
   14.18 +end
   14.19 +
   14.20  if not config.database then
   14.21    config.database = { engine='postgresql', dbname='liquid_feedback' }
   14.22  end
    15.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.2 +++ b/env/encode/pg_hstore.lua	Fri Jul 18 21:42:59 2014 +0200
    15.3 @@ -0,0 +1,16 @@
    15.4 +-- Encodes a Lua table as PostgreSQL hstore text input
    15.5 +-- TODO This should be implemented in the SQL abstraction layer
    15.6 +
    15.7 +function encode.pg_hstore(hstore_values)
    15.8 +
    15.9 +  local entries = {}
   15.10 +  
   15.11 +  for key, val in pairs(hstore_values) do
   15.12 +    local escaped_key = encode.pg_hstore_value(key)
   15.13 +    local escaped_val = encode.pg_hstore_value(val)
   15.14 +    entries[#entries+1] = escaped_key .. "=>" .. escaped_val
   15.15 +  end
   15.16 +
   15.17 +  return table.concat(entries, ", ")
   15.18 +
   15.19 +end
    16.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    16.2 +++ b/env/encode/pg_hstore_value.lua	Fri Jul 18 21:42:59 2014 +0200
    16.3 @@ -0,0 +1,6 @@
    16.4 +-- Formats a value (or a key) for usage in the text representation of
    16.5 +-- hstore fields
    16.6 +
    16.7 +function encode.pg_hstore_value(value)
    16.8 +  return '"' .. string.gsub(value, '([\\"])', "\\%1") .. '"'
    16.9 +end
   16.10 \ No newline at end of file
    17.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    17.2 +++ b/env/ldap/__init.lua	Fri Jul 18 21:42:59 2014 +0200
    17.3 @@ -0,0 +1,2 @@
    17.4 +-- Lua library path for C modules for mldap
    17.5 +package.cpath = request.get_app_basepath() .. "/lib/mldap/?.so" .. ";" .. package.cpath
    17.6 \ No newline at end of file
    18.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    18.2 +++ b/env/ldap/bind.lua	Fri Jul 18 21:42:59 2014 +0200
    18.3 @@ -0,0 +1,55 @@
    18.4 +-- binds to configured LDAP server
    18.5 +-- --------------------------------------------------------------------------
    18.6 +-- omit arguments for anonymous bind
    18.7 +--
    18.8 +-- arguments:
    18.9 +--   dn: the distinguished name to be used fo binding (string)
   18.10 +--   password: password credentials (string)
   18.11 +--
   18.12 +-- returns:
   18.13 +--   ldap: in case of success, an LDAP connection handle
   18.14 +--   err: in case of an error, an error code (string)
   18.15 +--   err2: error dependent extra error information
   18.16 +
   18.17 +function ldap.bind(dn, password)
   18.18 +  
   18.19 +  local libldap = require("mldap")
   18.20 +
   18.21 +  local hostlist = ldap.get_hosts()
   18.22 +
   18.23 +  -- try binding to LDAP server until success of no host entry left  
   18.24 +  local ldap
   18.25 +  while not ldap do
   18.26 +  
   18.27 +    if #hostlist < 1 then
   18.28 +      break
   18.29 +    end
   18.30 +    
   18.31 +    local host = table.remove(hostlist, 1)
   18.32 +    
   18.33 +    local err
   18.34 +    ldap, err, errno = libldap.bind{
   18.35 +      uri = host.uri,
   18.36 +      timeout = host.timeout,
   18.37 +      who = dn,
   18.38 +      password = password
   18.39 +    }
   18.40 +    
   18.41 +    if not err and ldap then
   18.42 +      return ldap, nil
   18.43 +    end
   18.44 +
   18.45 +    local errno_string
   18.46 +    
   18.47 +    if errno then
   18.48 +      errno_string = libldap.errorcodes[errno]
   18.49 +    end
   18.50 +    
   18.51 +    if errno == libldap.errorcodes.invalid_credentials then
   18.52 +      return nil, "invalid_credentials", errno_string
   18.53 +    end
   18.54 +  end
   18.55 +
   18.56 +  return nil, "cant_contact_ldap_server"
   18.57 +  
   18.58 +end
    19.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    19.2 +++ b/env/ldap/bind_as_app.lua	Fri Jul 18 21:42:59 2014 +0200
    19.3 @@ -0,0 +1,21 @@
    19.4 +-- binds to configured LDAP server with application's credentials
    19.5 +-- --------------------------------------------------------------------------
    19.6 +--
    19.7 +-- returns
    19.8 +--   ldap_conn: in case of success, an LDAP connection handle
    19.9 +--   err: in case of an error, an error code (string)
   19.10 +
   19.11 +function ldap.bind_as_app()
   19.12 +
   19.13 +  local dn, password
   19.14 +  
   19.15 +  if config.ldap.bind_as then
   19.16 +    dn       = config.ldap.bind_as.dn
   19.17 +    password = config.ldap.bind_as.password
   19.18 +  end
   19.19 +  
   19.20 +  local ldap_conn, err = ldap.bind(dn, password)
   19.21 +  
   19.22 +  return ldap_conn, err
   19.23 +  
   19.24 +end
    20.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    20.2 +++ b/env/ldap/check_credentials.lua	Fri Jul 18 21:42:59 2014 +0200
    20.3 @@ -0,0 +1,47 @@
    20.4 +-- check if credentials (given by a user) are valid to bind to LDAP
    20.5 +-- --------------------------------------------------------------------------
    20.6 +--
    20.7 +-- arguments:
    20.8 +--   dn: The distinguished name to be used fo binding (string, required)
    20.9 +--   password: Password credentials (string, required)
   20.10 +--
   20.11 +-- returns
   20.12 +--   success: true in cases of valid credentials
   20.13 +--            false in cases of invalid credentials
   20.14 +--            nil in undetermined cases, i.e. unavailable LDAP server
   20.15 +--   err: error code in case of errors, otherwise nil (string)
   20.16 +--   err2: error dependent extra error information
   20.17 +
   20.18 +function ldap.check_credentials(login, password)
   20.19 +
   20.20 +  local filter = config.ldap.member.login_filter_map(login)
   20.21 +  local ldap_entry, err, err2 = ldap.get_member_entry(filter)
   20.22 +
   20.23 +  if err == "too_many_entries_found" then
   20.24 +    return false, "invalid_credentials"
   20.25 +  end
   20.26 +  
   20.27 +  if err then
   20.28 +    return nil, err
   20.29 +  end
   20.30 +  if not ldap_entry then
   20.31 +    return false, "invalid_credentials"
   20.32 +  end
   20.33 +  
   20.34 +  local dn = ldap_entry.dn
   20.35 +  
   20.36 +  local ldap, err, err2 = ldap.bind(dn, password)
   20.37 + 
   20.38 +  if err == "invalid_credentials" then
   20.39 +    return false, "invalid_credentials"
   20.40 +  end
   20.41 +  
   20.42 +  if err then
   20.43 +    return nil, err, err2
   20.44 +  end
   20.45 +  
   20.46 +  ldap:unbind()
   20.47 +  
   20.48 +  return ldap_entry
   20.49 +  
   20.50 +end
    21.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    21.2 +++ b/env/ldap/create_member.lua	Fri Jul 18 21:42:59 2014 +0200
    21.3 @@ -0,0 +1,39 @@
    21.4 +-- Create a new member object from LDAP for an uid
    21.5 +-- --------------------------------------------------------------------------
    21.6 +--
    21.7 +-- arguments:
    21.8 +--   uid: uid of the new member object (required)
    21.9 +--
   21.10 +-- returns:
   21.11 +--   member: a LiquidFeedback Member object (in case of success)
   21.12 +--   err: error code in case of an error (string)
   21.13 +--   err2: error dependent extra error information
   21.14 +
   21.15 +function ldap.create_member(uid)
   21.16 +  
   21.17 +  local member = Member:new()
   21.18 +
   21.19 +  member.authority = "ldap"
   21.20 +
   21.21 +  member.authority_data = encode.pg_hstore{
   21.22 +    uid = uid
   21.23 +  }
   21.24 +  
   21.25 +  local ldap_conn, ldap_entry, err, err2 = ldap.update_member_attr(member, nil, uid)
   21.26 +  
   21.27 +  if ldap_conn then
   21.28 +    ldap_conn:unbind()
   21.29 +  end
   21.30 +  
   21.31 +  member.authority_data = encode.pg_hstore{
   21.32 +    uid = uid,
   21.33 +    login = config.ldap.member.login_map(ldap_entry)
   21.34 +  }
   21.35 +  
   21.36 +  if not err then
   21.37 +    return member
   21.38 +  end
   21.39 +  
   21.40 +  return nil, err, err2
   21.41 +  
   21.42 +end
    22.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    22.2 +++ b/env/ldap/escape_filter.lua	Fri Jul 18 21:42:59 2014 +0200
    22.3 @@ -0,0 +1,20 @@
    22.4 +-- Escape a string to be used as safe LDAP filter
    22.5 +-- --------------------------------------------------------------------------
    22.6 +--
    22.7 +-- arguments:
    22.8 +--   filter: the string to be escaped (required)
    22.9 +--
   22.10 +-- returns:
   22.11 +--   escaped_filter: the escaped result
   22.12 +
   22.13 +function ldap.escape_filter(filter)
   22.14 +
   22.15 +  local null_pattern = (_VERSION == "Lua 5.1") and "%z" or "\000"
   22.16 +  
   22.17 +  return string.gsub(filter, "[\\%*%(%)\128-\255" .. null_pattern .. "]", function (char)
   22.18 +    
   22.19 +    return string.format("%02x", string.byte(char))
   22.20 +    
   22.21 +  end)
   22.22 +  
   22.23 +end
   22.24 \ No newline at end of file
    23.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    23.2 +++ b/env/ldap/get_hosts.lua	Fri Jul 18 21:42:59 2014 +0200
    23.3 @@ -0,0 +1,46 @@
    23.4 +-- generate a ready-to-use list of the configures LDAP servers
    23.5 +-- --------------------------------------------------------------------------
    23.6 +-- result is in order of preference, including a round robin style random
    23.7 +-- selection of the preference of hosts with the same preference
    23.8 +--
    23.9 +-- returns:
   23.10 +--   hostlist: flattened list of LDAP servers in order of preference
   23.11 +
   23.12 +function ldap.get_hosts()
   23.13 +  
   23.14 +  math.randomseed(os.time())
   23.15 +  
   23.16 +  local hostlist = {}
   23.17 +  
   23.18 +  -- iterate through all entries on base level
   23.19 +  for i, host in ipairs (config.ldap.hosts) do
   23.20 +    
   23.21 +    -- a single host entry
   23.22 +    if host.uri then
   23.23 +      hostlist[#hostlist+1] = host
   23.24 +      
   23.25 +    -- or a list of host entries
   23.26 +    else
   23.27 +      local subhostlist = {}
   23.28 +      
   23.29 +      -- sort list of host entries by random
   23.30 +      for j, subhost in ipairs(host) do
   23.31 +        subhost.priority = math.random()
   23.32 +        subhostlist[#subhostlist+1] = subhost
   23.33 +      end
   23.34 +      table.sort(subhostlist, function (a,b)
   23.35 +        return a.priority < b.priority
   23.36 +      end)
   23.37 +      
   23.38 +      -- and add them to the main list
   23.39 +      for i, subhost in ipairs(subhostlist) do
   23.40 +        hostlist[#hostlist+1] = subhost
   23.41 +      end
   23.42 +      
   23.43 +    end
   23.44 +    
   23.45 +  end
   23.46 +  
   23.47 +  return hostlist
   23.48 +  
   23.49 +end
    24.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    24.2 +++ b/env/ldap/get_member_entry.lua	Fri Jul 18 21:42:59 2014 +0200
    24.3 @@ -0,0 +1,52 @@
    24.4 +-- gets the corresponding ldap entry for a given member login
    24.5 +-- --------------------------------------------------------------------------
    24.6 +--
    24.7 +-- arguments:
    24.8 +--   filter: the LDAP filter for searching the member (required)
    24.9 +--   use_ldap_conn: an already existing LDAP connection to be used (optional)
   24.10 +--
   24.11 +-- returns:
   24.12 +--   ldap_entry: in case of success, the LDAP entry (object)
   24.13 +--   err: in case of an error, an error message (string)
   24.14 +--   err2: error dependent extra error information
   24.15 +
   24.16 +function ldap.get_member_entry(filter, use_ldap_conn)
   24.17 +  
   24.18 +  local ldap_conn, err
   24.19 +  
   24.20 +  if use_ldap_conn then
   24.21 +    ldap_conn = use_ldap_conn
   24.22 +  else
   24.23 +    ldap_conn, bind_err = ldap.bind_as_app()
   24.24 +  end
   24.25 +  
   24.26 +  if not ldap_conn then
   24.27 +    return nil, "ldap_bind_error", bind_err
   24.28 +  end
   24.29 +
   24.30 +  local entries, search_err = ldap_conn:search{
   24.31 +    base = config.ldap.base,
   24.32 +    scope = config.ldap.member.scope,
   24.33 +    filter = filter,
   24.34 +    attr = config.ldap.member.fetch_attr,
   24.35 +  }
   24.36 +  
   24.37 +  if not use_ldap_conn then
   24.38 +    ldap_conn:unbind()
   24.39 +  end
   24.40 +  
   24.41 +  if not entries then
   24.42 +    return nil, "ldap_search_error", search_err
   24.43 +  end
   24.44 +  
   24.45 +  if #entries > 1 then
   24.46 +    return nil, "too_many_ldap_entries_found"
   24.47 +  end
   24.48 +  
   24.49 +  if #entries < 0 then
   24.50 +    return nil, "no_ldap_entry_found"
   24.51 +  end
   24.52 +  
   24.53 +  return entries[1]
   24.54 +  
   24.55 +end
    25.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    25.2 +++ b/env/ldap/test.lua	Fri Jul 18 21:42:59 2014 +0200
    25.3 @@ -0,0 +1,141 @@
    25.4 +-- Part of test case for ldap4lqfb
    25.5 +
    25.6 +function ldap.test()
    25.7 +
    25.8 +  local global_count = 0
    25.9 +  local global_failed = 0
   25.10 +  
   25.11 +  local function checkMember(uid, login, name, units_with_voting_priv, units_with_polling_priv)
   25.12 +
   25.13 +    local failed = false
   25.14 +    
   25.15 +    local function testError(err, expected, got)
   25.16 +      failed = true
   25.17 +      global_failed = global_failed + 1
   25.18 +      if expected then
   25.19 +        print ("[" .. uid .. "] FAILED: " .. err .. " (expected: '" .. expected .. "' but got '" .. tostring(got) .. "')") 
   25.20 +      else
   25.21 +        print ("[" .. uid .. "] FAILED: " .. err)
   25.22 +      end
   25.23 +    end
   25.24 +
   25.25 +    local function testOk(test, expected)
   25.26 +      if test then
   25.27 +        print ("[" .. uid .. "] " .. test .. " ok ('" .. expected .. "')")
   25.28 +      else
   25.29 +        print ("[" .. uid .. "] success")
   25.30 +      end
   25.31 +    end
   25.32 +    
   25.33 +    local function test(field, expected, value)
   25.34 +      global_count = global_count + 1
   25.35 +      if expected ~= value then
   25.36 +        testError(field, expected, value)
   25.37 +        return
   25.38 +      end
   25.39 +      testOk(field, expected)
   25.40 +    end
   25.41 +    
   25.42 +    local members = Member:new_selector()
   25.43 +      :add_field{ "authority_data->'login' as authority_data_login" }
   25.44 +      :add_where{ "authority = ? AND authority_data->'uid' = ?", "ldap", uid }
   25.45 +      :exec()
   25.46 +
   25.47 +    if #members < 1 then
   25.48 +      testError("Member not found in DB")
   25.49 +      return
   25.50 +    end
   25.51 +    
   25.52 +    if #members > 1 then
   25.53 +      testError("Found more than one DB entry")
   25.54 +      return
   25.55 +    end
   25.56 +    
   25.57 +    local member = members[1]
   25.58 +    
   25.59 +    if login == nil then
   25.60 +      if not member.locked then
   25.61 +        testError("Member not locked")
   25.62 +      else
   25.63 +        testOk("Member is locked", "true")
   25.64 +      end
   25.65 +      return
   25.66 +    end
   25.67 +      
   25.68 +    test("login", login, member.authority_data_login)
   25.69 +    test("name", name, member.name)
   25.70 +    
   25.71 +    for i, unit_id in ipairs(units_with_voting_priv) do
   25.72 +      global_count = global_count + 1
   25.73 +      local privilege = Privilege:by_pk(unit_id, member.id)
   25.74 +      if privilege and privilege.voting_right then
   25.75 +        testOk("voting_right", unit_id)
   25.76 +      else
   25.77 +        testError("voting_right", unit_id, "")
   25.78 +      end
   25.79 +    end
   25.80 +    
   25.81 +    local privileges_selector = Privilege:new_selector()
   25.82 +      :add_where{ "member_id = ?", member.id }
   25.83 +      :add_where("voting_right = true")
   25.84 +    if #units_with_voting_priv > 0 then
   25.85 +      local privileges = privileges_selector:add_where{ "unit_id NOT IN ($)", units_with_voting_priv }
   25.86 +    end
   25.87 +    local privileges = privileges_selector:exec()
   25.88 +      
   25.89 +    if #privileges > 0 then
   25.90 +      testError("voting_right", "count: 0", "count: " .. #privileges)
   25.91 +    else
   25.92 +      testOk("voting_right", "count: 0")
   25.93 +    end
   25.94 +    
   25.95 +    for i, unit_id in ipairs(units_with_polling_priv) do
   25.96 +      global_count = global_count + 1
   25.97 +      local privilege = Privilege:by_pk(unit_id, member.id)
   25.98 +      if privilege and privilege.polling_right then
   25.99 +        testOk("polling_right", unit_id)
  25.100 +      else
  25.101 +        testError("polling_right", unit_id, "")
  25.102 +      end
  25.103 +    end
  25.104 +    
  25.105 +    local privileges_selector = Privilege:new_selector()
  25.106 +      :add_where{ "member_id = ?", member.id }
  25.107 +      :add_where("polling_right = true")
  25.108 +    if #units_with_polling_priv > 0 then
  25.109 +      privileges_selector:add_where{ "unit_id NOT IN ($)", units_with_polling_priv }
  25.110 +    end
  25.111 +    local privileges = privileges_selector:exec()
  25.112 +      
  25.113 +    if #privileges > 0 then
  25.114 +      testError("polling_right", "count: " .. #units_with_polling_priv, "count: " .. #privileges)
  25.115 +    else
  25.116 +      testOk("polling_right", "count: " .. #units_with_polling_priv)
  25.117 +    end
  25.118 +    
  25.119 +    if not failed then
  25.120 +      return true
  25.121 +    end
  25.122 +    
  25.123 +    return false
  25.124 +    
  25.125 +  end
  25.126 +  
  25.127 +  checkMember("1000", "alice", "Alice Amberg", { 1 }, { })
  25.128 +  checkMember("1001", "bob", "Bob Bobbersen", { 1, 2 }, { 1, 3 })
  25.129 +  checkMember("1002", "chris", "Chris Carv", { 1, 2 }, { 1, 3 })
  25.130 +  checkMember("1003", "daisy", "Daisy Duck", { 3 }, { 1, 2 })
  25.131 +  checkMember("1004", "ernst", "Ernst Ernst", { 3 }, { 1, 2 })
  25.132 +  checkMember("1005", "fredi", "Frederike Frei", { 1 }, { })
  25.133 +  checkMember("1006")
  25.134 +  checkMember("1007", "helen", "Helen Hofstatter", { 1, 2 }, { 1, 3 })
  25.135 +  checkMember("1008", "iwan", "Iwan Iwanowski", { 1 }, { })
  25.136 +  checkMember("1009", "jasmina", "Jasmina Jasik", { 1 }, { })
  25.137 +
  25.138 +  print()
  25.139 +  
  25.140 +  local success_count = global_count - global_failed
  25.141 +  
  25.142 +  print (success_count .. " of " .. global_count .. " tests succeeded.")
  25.143 + 
  25.144 +end
    26.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    26.2 +++ b/env/ldap/update_all_members.lua	Fri Jul 18 21:42:59 2014 +0200
    26.3 @@ -0,0 +1,68 @@
    26.4 +-- check for all LiquidFeedback Members with LDAP authentication
    26.5 +-- if the corresponding LDAP entry is still existent and updates
    26.6 +-- changed attributes
    26.7 +-- --------------------------------------------------------------------------
    26.8 +-- prints debug output to stdout
    26.9 +--
   26.10 +-- returns
   26.11 +--   success: true if no error occured during run
   26.12 +--            false if at least one error occured during run
   26.13 +
   26.14 +function ldap.update_all_members()
   26.15 +
   26.16 +  local some_error_occured = false
   26.17 +  
   26.18 +  local ldap_conn = ldap.bind_as_app()
   26.19 +  
   26.20 +  function update_member(member)
   26.21 +
   26.22 +    local function failure (err, err2)
   26.23 +      Member.get_db_conn():query("ROLLBACK")
   26.24 +      io.stdout:write("ERROR: ", err, " (", err2, ") id=", tostring(member.id), " uid=", tostring(member.authority_data_uid), "\n")
   26.25 +      some_error_occured = true
   26.26 +    end
   26.27 +    
   26.28 +    local function success ()
   26.29 +      Member.get_db_conn():query("COMMIT")
   26.30 +      io.stdout:write("ok: id=", tostring(member.id), " uid=", tostring(member.authority_data_uid), "\n")
   26.31 +    end
   26.32 +
   26.33 +    Member.get_db_conn():query("BEGIN")
   26.34 +
   26.35 +    local ldap_conn, ldap_entry, err, err2 = ldap.update_member_attr(member, ldap_conn)
   26.36 +    if err then
   26.37 +      failure("ldap_update_member", err)
   26.38 +      return
   26.39 +    end
   26.40 +
   26.41 +    local err = member:try_save()
   26.42 +    if err then
   26.43 +      failure("member_try_save", err)
   26.44 +      return
   26.45 +    end
   26.46 +
   26.47 +    if ldap_entry then
   26.48 +      local success, err, err2 = ldap.update_member_privileges(member, ldap_entry)
   26.49 +      if err then
   26.50 +        failure("ldap_update_member_privileges", err)
   26.51 +        return
   26.52 +      end
   26.53 +      
   26.54 +    end
   26.55 +
   26.56 +    success()
   26.57 +
   26.58 +  end
   26.59 +
   26.60 +  
   26.61 +  local members = Member:get_all_by_authority("ldap")
   26.62 +    
   26.63 +  for i, member in ipairs(members) do 
   26.64 +    update_member(member)
   26.65 +  end
   26.66 +  
   26.67 +  ldap_conn:unbind()
   26.68 +  
   26.69 +  return not some_error_occured
   26.70 +  
   26.71 +end
    27.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    27.2 +++ b/env/ldap/update_member_attr.lua	Fri Jul 18 21:42:59 2014 +0200
    27.3 @@ -0,0 +1,52 @@
    27.4 +-- check if the corresponding LDAP entry for an LiquidFeedback member
    27.5 +-- object is still existent and updates changed attributes
    27.6 +-- --------------------------------------------------------------------------
    27.7 +--
    27.8 +-- arguments:
    27.9 +--   member: a LiquidFeedback Member object (required)
   27.10 +--   ldap_conn: a ldap connection handle (optional)
   27.11 +--   uid: the uid of the member (optional, required when creating members)
   27.12 +--
   27.13 +-- returns:
   27.14 +--   ldap_conn: an LDAP connection
   27.15 +--   ldap_entry: the found LDAP entry (if any)
   27.16 +--   err: error code in case of an error (string)
   27.17 +--   err2: error dependent extra error information
   27.18 +--   err3: error dependent extra error information
   27.19 +
   27.20 +function ldap.update_member_attr(member, ldap_conn, uid)
   27.21 +  
   27.22 +  -- do this only for members with ldap authentication
   27.23 +  if member.authority ~= "ldap" then
   27.24 +    return nil, nil, "member_is_not_authenticated_by_ldap"
   27.25 +  end
   27.26 +  
   27.27 +  local filter = config.ldap.member.uid_filter_map(member.authority_data_uid or uid)
   27.28 +  local ldap_entry, err, err2 = ldap.get_member_entry(filter, ldap_conn)
   27.29 +
   27.30 +  if err then
   27.31 +    return ldap_conn, nil, "ldap_error", err, err2
   27.32 +  end
   27.33 +  
   27.34 +  -- If no corresponding entry found, lock the member
   27.35 +  if not ldap_entry then
   27.36 +    member.locked = true
   27.37 +    member.active = false
   27.38 +    return ldap_conn
   27.39 +  end
   27.40 +
   27.41 +  -- If exactly one corresponding entry found, update the attributes
   27.42 +  local err = config.ldap.member.attr_map(ldap_entry, member)
   27.43 +  
   27.44 +  member.authority_data = encode.pg_hstore{
   27.45 +    uid = member.authority_data_uid or uid,
   27.46 +    login = config.ldap.member.login_map(ldap_entry)
   27.47 +  }
   27.48 +  
   27.49 +  if err then
   27.50 +    return ldap_conn, ldap_entry, "attr_map_error", err
   27.51 +  end
   27.52 +  
   27.53 +  return ldap_conn, ldap_entry
   27.54 +    
   27.55 +end
    28.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    28.2 +++ b/env/ldap/update_member_privileges.lua	Fri Jul 18 21:42:59 2014 +0200
    28.3 @@ -0,0 +1,56 @@
    28.4 +-- Update member privileges from LDAP
    28.5 +-- --------------------------------------------------------------------------
    28.6 +--
    28.7 +-- arguments:
    28.8 +--   member: the member for which the privileges should be updated
    28.9 +--   ldap_entry: the ldap entry to be used for updating the privileges
   28.10 +--
   28.11 +-- returns:
   28.12 +--   err: an error code, if an error occured (string)
   28.13 +--   err2: Error dependent extra error information
   28.14 +
   28.15 +function ldap.update_member_privileges(member, ldap_entry)
   28.16 +
   28.17 +  local privileges, err = config.ldap.member.privilege_map(ldap_entry, member)
   28.18 +
   28.19 +  if err then
   28.20 +    return false, "privilege_map_error", err
   28.21 +  end
   28.22 +
   28.23 +  local privileges_by_unit_id = {}
   28.24 +  for i, privilege in ipairs(privileges) do
   28.25 +    privileges_by_unit_id[privilege.unit_id] = privilege
   28.26 +  end
   28.27 +
   28.28 +  local current_privileges = Privilege:by_member_id(member.id)
   28.29 +  local current_privilege_ids = {}
   28.30 +
   28.31 +  for i, privilege in ipairs(current_privileges) do
   28.32 +    if privileges_by_unit_id[privilege.unit_id] then
   28.33 +      current_privilege_ids[privilege.unit_id] = privilege
   28.34 +    else
   28.35 +      privilege:destroy()
   28.36 +    end
   28.37 +  end
   28.38 +
   28.39 +  for i, privilege in ipairs(privileges) do
   28.40 +    local current_privilege = current_privilege_ids[privilege.unit_id]
   28.41 +    if not current_privilege then
   28.42 +      current_privilege = Privilege:new()
   28.43 +      current_privilege.member_id = member.id
   28.44 +      current_privileges[#current_privileges+1] = current_privilege
   28.45 +    end
   28.46 +    for key, val in pairs(privilege) do
   28.47 +      current_privilege[key] = val
   28.48 +    end
   28.49 +  end
   28.50 +
   28.51 +  for i, privilege in ipairs(current_privileges) do
   28.52 +    local err = privilege:try_save()
   28.53 +    if err then
   28.54 +      return false, "privilege_save_error", err
   28.55 +    end
   28.56 +  end
   28.57 +
   28.58 +  return true
   28.59 +end
    29.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    29.2 +++ b/env/util/is_profile_field_locked.lua	Fri Jul 18 21:42:59 2014 +0200
    29.3 @@ -0,0 +1,16 @@
    29.4 +function util.is_profile_field_locked(member, field_name)
    29.5 +  if member.authority == "ldap" then
    29.6 +    if config.ldap.member.locked_profile_fields and config.ldap.member.locked_profile_fields[field_name] 
    29.7 +        or field_name == "login" 
    29.8 +        or field_name == "password" 
    29.9 +    then
   29.10 +      return true
   29.11 +    end
   29.12 +  end
   29.13 +  
   29.14 +  if config.locked_profile_fields[field_name] then
   29.15 +    return true
   29.16 +  end
   29.17 +
   29.18 +  return false
   29.19 +end
   29.20 \ No newline at end of file
    30.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    30.2 +++ b/lib/mldap/Makefile	Fri Jul 18 21:42:59 2014 +0200
    30.3 @@ -0,0 +1,8 @@
    30.4 +mldap.so: mldap.o
    30.5 +	ld -shared -L/usr/lib -o mldap.so mldap.o /usr/lib/libldap.so
    30.6 +
    30.7 +mldap.o: mldap.c
    30.8 +	gcc -g -c -fPIC -I/usr/include -I/usr/include/lua5.1 -Wall -o mldap.o mldap.c
    30.9 +
   30.10 +clean::
   30.11 +	rm -f mldap.so mldap.o
    31.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    31.2 +++ b/lib/mldap/convert_errorcodes.lua	Fri Jul 18 21:42:59 2014 +0200
    31.3 @@ -0,0 +1,7 @@
    31.4 +#!/usr/bin/env lua
    31.5 +for line in io.lines() do
    31.6 +  local ident, code = line:match("^#define[ \t]+LDAP_([A-Z][A-Z_]*)[ \t]+([^ \t/]+)")
    31.7 +  if ident then
    31.8 +    io.stdout:write('  {"', ident:lower(), '", ', tonumber(code) or code, '},\n')
    31.9 +  end
   31.10 +end
    32.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    32.2 +++ b/lib/mldap/mldap.c	Fri Jul 18 21:42:59 2014 +0200
    32.3 @@ -0,0 +1,567 @@
    32.4 +/*
    32.5 + * minimalistic Lua LDAP interface library
    32.6 + *
    32.7 + * The library does not set any global variable itself and must thus be
    32.8 + * loaded with
    32.9 + *
   32.10 + *     mldap = require("mldap")
   32.11 + *
   32.12 + * or a similar statement.
   32.13 + *
   32.14 + * The library provides two functions, conn = bind{...} and unbind(conn)
   32.15 + * to connect to and disconnect from the LDAP server respectively. The
   32.16 + * unbind function is also provided as a method of the connection userdata
   32.17 + * object (see below).
   32.18 + *
   32.19 + * The arguments to the bind{...} function are documented in the source code
   32.20 + * (see C function mldap_bind). In case of success, the bind function returns
   32.21 + * a userdata object that provides two methods: query{...} and unbind(). In
   32.22 + * case of error, the bind function returns nil as first return value, an
   32.23 + * error string as second return value, and a numeric error code as third
   32.24 + * return value. A positive error code is an LDAP resultCode, a negative
   32.25 + * error code is an OpenLDAP API error code:
   32.26 + *
   32.27 + *     connection, error_string, numeric_error_code = mldap.bind{...}
   32.28 + *
   32.29 + * For translating numeric error codes to an identifying (machine readable)
   32.30 + * string identifier, the library provides in addition to the two functions
   32.31 + * a table named 'errorcodes', for example:
   32.32 + *
   32.33 + *     49 == mldap.errorcodes["invalid_credentials"]
   32.34 + *
   32.35 + * and
   32.36 + *
   32.37 + *     mldap.errorcodes[49] == "invalid_credentials"
   32.38 + *
   32.39 + * The arguments and return value of the query{...} method of the connection
   32.40 + * userdata object are also documented in the source code below (see
   32.41 + * C function mldap_query). Error conditions are reported the same way as the
   32.42 + * bind{...} function does.
   32.43 + *
   32.44 + * To close the connection, either the unbind() function of the library or
   32.45 + * the unbind() method can be called; it is allowed to call them multiple
   32.46 + * times, and they are also invoked by the garbage collector.
   32.47 + *
   32.48 + */
   32.49 +
   32.50 +// Lua header inclusions:
   32.51 +#include <lua.h>
   32.52 +#include <lauxlib.h>
   32.53 +
   32.54 +// OpenLDAP header inclusions:
   32.55 +#include <ldap.h>
   32.56 +
   32.57 +// Standard C inclusions:
   32.58 +#include <stdlib.h>
   32.59 +#include <stdbool.h>
   32.60 +#include <unistd.h>
   32.61 +
   32.62 +// Error code translation is included from separate C file:
   32.63 +#include "mldap_errorcodes.c"
   32.64 +
   32.65 +// Provide compatibility with Lua 5.1:
   32.66 +#if LUA_VERSION_NUM < 502
   32.67 +#define luaL_newlib(L, t) lua_newtable((L)); luaL_register(L, NULL, t)
   32.68 +#define lua_rawlen lua_objlen
   32.69 +#define lua_len lua_objlen
   32.70 +#define luaL_setmetatable(L, regkey) \
   32.71 +  lua_getfield((L), LUA_REGISTRYINDEX, (regkey)); \
   32.72 +  lua_setmetatable((L), -2);
   32.73 +#endif
   32.74 +
   32.75 +// prefix for all Lua registry entries of this library:
   32.76 +#define MLDAP_REGKEY "556aeaf3c864af2e_mldap_"
   32.77 +
   32.78 +
   32.79 +static const char *mldap_get_named_string_arg(
   32.80 +  // gets a named argument of type "string" from a table at the given stack position
   32.81 +
   32.82 +  lua_State *L,         // pointer to lua_State variable
   32.83 +  int idx,              // stack index of the table containing the named arguments
   32.84 +  const char *argname,  // name of the argument
   32.85 +  int mandatory         // if not 0, then the argument is mandatory and an error is raised if it isn't found
   32.86 +
   32.87 +  // leaves the string as new element on top of the stack
   32.88 +) {
   32.89 +
   32.90 +  // pushes the table entry with the given argument name on top of the stack:
   32.91 +  lua_getfield(L, idx, argname);
   32.92 +
   32.93 +  // check, if the entry is nil or false:
   32.94 +  if (!lua_toboolean(L, -1)) {
   32.95 +
   32.96 +    // throw error, if named argument is mandatory:
   32.97 +    if (mandatory) return luaL_error(L, "Named argument '%s' missing", argname), NULL;
   32.98 +
   32.99 +    // return NULL pointer, if named argument is not mandatory:
  32.100 +    return NULL;
  32.101 +
  32.102 +  }
  32.103 +
  32.104 +  // throw error, if the value of the argument is not string:
  32.105 +  if (lua_type(L, -1) != LUA_TSTRING) return luaL_error(L, "Named argument '%s' is not a string", argname), NULL;
  32.106 +
  32.107 +  // return a pointer to the string, leaving the string on top of the stack to avoid garbage collection:
  32.108 +  return lua_tostring(L, -1);
  32.109 +
  32.110 +  // leaves one element on the stack
  32.111 +}
  32.112 +
  32.113 +
  32.114 +static int mldap_get_named_number_arg(
  32.115 +  // gets a named argument of type "number" from a table at the given stack position
  32.116 +
  32.117 +  lua_State *L,             // pointer to lua_State variable
  32.118 +  int idx,                  // stack index of the table containing the named arguments
  32.119 +  const char *argname,      // name of the argument
  32.120 +  int mandatory,            // if not 0, then the argument is mandatory and an error is raised if it isn't found
  32.121 +  lua_Number default_value  // default value to return, if the argument is not mandatory and nil or false
  32.122 +
  32.123 +  // opposed to 'mldap_get_named_string_arg', this function leaves no element on the stack
  32.124 +) {
  32.125 +
  32.126 +  lua_Number value;  // value to return
  32.127 +
  32.128 +  // pushes the table entry with the given argument name on top of the stack:
  32.129 +  lua_getfield(L, idx, argname);
  32.130 +
  32.131 +  // check, if the entry is nil or false:
  32.132 +  if (lua_isnil(L, -1)) {
  32.133 +
  32.134 +    // throw error, if named argument is mandatory:
  32.135 +    if (mandatory) return luaL_error(L, "Named argument '%s' missing", argname), 0;
  32.136 +
  32.137 +    // set default value as return value, if named argument is not mandatory:
  32.138 +    value = default_value;
  32.139 +
  32.140 +  } else {
  32.141 +
  32.142 +    // throw error, if the value of the argument is not a number:
  32.143 +    if (lua_type(L, -1) != LUA_TNUMBER) return luaL_error(L, "Named argument '%s' is not a number", argname), 0;
  32.144 +
  32.145 +    // set return value to the number:
  32.146 +    value = lua_tonumber(L, -1);
  32.147 +
  32.148 +  }
  32.149 +
  32.150 +  // remove unnecessary element from stack (not needed to avoid garbage collection):
  32.151 +  return value;
  32.152 +
  32.153 +  // leaves no new elements on the stack
  32.154 +}
  32.155 +
  32.156 +
  32.157 +static int mldap_scope(
  32.158 +  // converts a string ("base", "onelevel", "subtree", "children") to an integer representing the LDAP scope
  32.159 +  // and throws an error for any unknown string
  32.160 +
  32.161 +  lua_State *L,             // pointer to lua_State variable (needed to throw errors)
  32.162 +  const char *scope_string  // string that is either ("base", "onelevel", "subtree", "children")
  32.163 +
  32.164 +  // does not affect or use the Lua stack at all
  32.165 +) {
  32.166 +
  32.167 +  // return integer according to string value:
  32.168 +  if (!strcmp(scope_string, "base"))     return LDAP_SCOPE_BASE;
  32.169 +  if (!strcmp(scope_string, "onelevel")) return LDAP_SCOPE_ONELEVEL;
  32.170 +  if (!strcmp(scope_string, "subtree"))  return LDAP_SCOPE_SUBTREE;
  32.171 +  if (!strcmp(scope_string, "children")) return LDAP_SCOPE_CHILDREN;
  32.172 +
  32.173 +  // throw error for unknown string values:
  32.174 +  return luaL_error(L, "Invalid LDAP scope: '%s'", scope_string), 0;
  32.175 +
  32.176 +}
  32.177 +
  32.178 +
  32.179 +static int mldap_bind(lua_State *L) {
  32.180 +  // Lua C function that takes named arguments as a table
  32.181 +  // and returns a userdata object, representing the LDAP connection
  32.182 +  // or returns nil, an error string, and an error code (integer) on error
  32.183 +
  32.184 +  // named arguments:
  32.185 +  // "uri"      (string)  server URI to connect to
  32.186 +  // "who"      (string)  DN to bind as
  32.187 +  // "password" (string)  password for DN to bind as
  32.188 +  // "timeout"  (number)  timeout in seconds
  32.189 +
  32.190 +  static const int ldap_version = LDAP_VERSION3;  // providing a pointer (&ldap_version) to set LDAP protocol version 3
  32.191 +  const char *uri;           // C string for "uri" argument
  32.192 +  const char *who;           // C string for "who" argument
  32.193 +  struct berval cred;        // credentials ("password") are stored as struct berval
  32.194 +  lua_Number timeout_float;  // float (lua_Number) for timeout
  32.195 +  struct timeval timeout;    // timeout is stored as struct timeval
  32.196 +  int ldap_error;            // LDAP error code (as returned by libldap calls)
  32.197 +  LDAP *ldp;                 // pointer to an opaque OpenLDAP structure representing the connection
  32.198 +  LDAP **ldp_ptr;            // pointer to a Lua userdata structure (that only contains the 'ldp' pointer)
  32.199 +
  32.200 +  // throw error if first argument is not a table:
  32.201 +  if (lua_type(L, 1) != LUA_TTABLE) {
  32.202 +    luaL_error(L, "Argument to function 'bind' is not a table.");
  32.203 +  }
  32.204 +
  32.205 +  // extract arguments:
  32.206 +  uri = mldap_get_named_string_arg(L, 1, "uri", true);
  32.207 +  who = mldap_get_named_string_arg(L, 1, "who", false);
  32.208 +  cred.bv_val = mldap_get_named_string_arg(L, 1, "password", false);
  32.209 +  if (cred.bv_val) cred.bv_len = strlen(cred.bv_val);
  32.210 +  else cred.bv_len = 0;
  32.211 +  timeout_float = mldap_get_named_number_arg(L, 1, "timeout", false, -1);
  32.212 +  timeout.tv_sec = timeout_float;
  32.213 +  timeout.tv_usec = (timeout_float - timeout.tv_sec) * 1000000;
  32.214 +
  32.215 +  // initialize OpenLDAP structure and provide URI for connection:
  32.216 +  ldap_error = ldap_initialize(&ldp, uri);
  32.217 +  // on error, jump to label "mldap_queryconn_error1", as no ldap_unbind_ext_s() is needed:
  32.218 +  if (ldap_error != LDAP_SUCCESS) goto mldap_queryconn_error1;
  32.219 +
  32.220 +  // set LDAP protocol version 3:
  32.221 +  ldap_error = ldap_set_option(ldp, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);
  32.222 +  // on error, jump to label "mldap_queryconn_error2", as ldap_unbind_ext_s() must be called:
  32.223 +  if (ldap_error != LDAP_SUCCESS) goto mldap_queryconn_error2;
  32.224 +
  32.225 +  // set timeout for asynchronous OpenLDAP library calls:
  32.226 +  ldap_error = ldap_set_option(ldp, LDAP_OPT_TIMEOUT, &timeout);
  32.227 +  // on error, jump to label "mldap_queryconn_error2", as ldap_unbind_ext_s() must be called:
  32.228 +  if (ldap_error != LDAP_SUCCESS) goto mldap_queryconn_error2;
  32.229 +
  32.230 +  // connect to LDAP server:
  32.231 +  ldap_error = ldap_sasl_bind_s(
  32.232 +    ldp,               // pointer to opaque OpenLDAP structure representing the connection
  32.233 +    who,               // DN to bind as
  32.234 +    LDAP_SASL_SIMPLE,  // SASL mechanism "simple" for password authentication
  32.235 +    &cred,             // password as struct berval
  32.236 +    NULL,              // no server controls
  32.237 +    NULL,              // no client controls
  32.238 +    NULL               // do not store server credentials
  32.239 +  );
  32.240 +
  32.241 +  // error handling:
  32.242 +  if (ldap_error != LDAP_SUCCESS) {
  32.243 +
  32.244 +    // error label to jump to, if a call of ldap_unbind_ext_s() is required:
  32.245 +    mldap_queryconn_error2:
  32.246 +
  32.247 +    // close connection and free resources:
  32.248 +    ldap_unbind_ext_s(ldp, NULL, NULL);
  32.249 +
  32.250 +    // error label to jump to, if no call of ldap_unbind_ext_s() is required:
  32.251 +    mldap_queryconn_error1:
  32.252 +
  32.253 +    // return three values:
  32.254 +    lua_pushnil(L);                                  // return nil as first value
  32.255 +    lua_pushstring(L, ldap_err2string(ldap_error));  // return error string as second value
  32.256 +    lua_pushinteger(L, ldap_error);                  // return error code (integer) as third value
  32.257 +    return 3;
  32.258 +
  32.259 +  }
  32.260 +
  32.261 +  // create new Lua userdata object (that will contain the 'ldp' pointer) on top of stack:
  32.262 +  ldp_ptr = lua_newuserdata(L, sizeof(LDAP *));
  32.263 +
  32.264 +  // set metatable of Lua userdata object:
  32.265 +  luaL_setmetatable(L, MLDAP_REGKEY "connection_metatable");
  32.266 +
  32.267 +  // write contents of Lua userdata object (the 'ldp' pointer):
  32.268 +  *ldp_ptr = ldp;
  32.269 +
  32.270 +  // return Lua userdata object from top of stack:
  32.271 +  return 1;
  32.272 +
  32.273 +}
  32.274 +
  32.275 +
  32.276 +static int mldap_search(lua_State *L) {
  32.277 +  // Lua C function used as "search" method of Lua userdata object representing the LDAP connection
  32.278 +  // that takes a Lua userdata object (the LDAP connection) as first argument,
  32.279 +  // a table with named arguments as second argument,
  32.280 +  // and returns a result table on success (see below)
  32.281 +  // or returns nil, an error string, and an error code (integer) on error
  32.282 +
  32.283 +  // named arguments:
  32.284 +  // "base"  (string)   DN of the entry at which to start the search
  32.285 +  // "scope" (string)   scope of the search, one of:
  32.286 +  //                      "base" to search the object itself
  32.287 +  //                      "onelevel" to search the object's immediate children
  32.288 +  //                      "subtree" to search the object and all its descendants
  32.289 +  //                      "children" to search all of the descendants
  32.290 +  // "filter" (string)  string representation of the filter to apply in the search
  32.291 +  //                    (conforming to RFC 4515 as extended in RFC 4526)
  32.292 +  // "attrs"  (table)   list of attribute descriptions (each a string) to return from matching entries
  32.293 +
  32.294 +  // structure of result table:
  32.295 +  // {
  32.296 +  //   { dn      = <distinguished name (DN)>,
  32.297 +  //     <attr1> = { <value1>, <value2>, ... },
  32.298 +  //     <attr2> = { <value1>, <value2>, ... },
  32.299 +  //     ...
  32.300 +  //   },
  32.301 +  //   { dn      = <distinguished name (DN)>,
  32.302 +  //     <attr1> = { <value1>, <value2>, ... },
  32.303 +  //     <attr2> = { <value1>, <value2>, ... },
  32.304 +  //     ...
  32.305 +  //   },
  32.306 +  //   ...
  32.307 +  // }
  32.308 +
  32.309 +  const char *base;          // C string for "base" argument
  32.310 +  const char *scope_string;  // C string for "scope" argument
  32.311 +  int scope;                 // integer representing the scope
  32.312 +  const char *filter;        // C string for "filter" argument
  32.313 +  size_t nattrs;             // number of attributes in "attrs" argument
  32.314 +  char **attrs;              // C string array of "attrs" argument
  32.315 +  size_t attr_idx;           // index variable for building the C string array of "attrs"
  32.316 +  int ldap_error;            // LDAP error code (as returned by libldap calls)
  32.317 +  LDAP **ldp_ptr;            // pointer to a pointer to the OpenLDAP structure representing the connection
  32.318 +  LDAPMessage *res;          // pointer to the result of ldap_search_ext_s() call
  32.319 +  LDAPMessage *ent;          // pointer to an entry in the result of ldap_search_ext_s() call
  32.320 +  int i;                     // integer to fill the Lua table returned as result
  32.321 +
  32.322 +  // truncate the Lua stack to 2 elements:
  32.323 +  lua_settop(L, 2);
  32.324 +
  32.325 +  // check if the first argument is a Lua userdata object with the correct metatable
  32.326 +  // and get a C pointer to that userdata object:
  32.327 +  ldp_ptr = luaL_checkudata(L, 1, MLDAP_REGKEY "connection_metatable");
  32.328 +
  32.329 +  // throw an error, if the connection has already been closed:
  32.330 +  if (!*ldp_ptr) {
  32.331 +    return luaL_error(L, "LDAP connection has already been closed");
  32.332 +  }
  32.333 +
  32.334 +  // check if the second argument is a table, and throw an error if it's not a table:
  32.335 +  if (lua_type(L, 2) != LUA_TTABLE) {
  32.336 +    luaL_error(L, "Argument to function 'bind' is not a table.");
  32.337 +  }
  32.338 +
  32.339 +  // extract named arguments (requires memory allocation for 'attrs'):
  32.340 +  base = mldap_get_named_string_arg(L, 2, "base", true);  // pushed to 3
  32.341 +  scope_string = mldap_get_named_string_arg(L, 2, "scope", true);  // pushed to 4
  32.342 +  scope = mldap_scope(L, scope_string);
  32.343 +  lua_pop(L, 1);  // removes stack element 4
  32.344 +  filter = mldap_get_named_string_arg(L, 2, "filter", false);  // pushed to 4
  32.345 +  lua_getfield(L, 2, "attrs");  // pushed to 5
  32.346 +  nattrs = lua_len(L, -1);
  32.347 +  attrs = calloc(nattrs + 1, sizeof(char *));  // memory allocation, +1 for terminating NULL
  32.348 +  if (!attrs) return luaL_error(L, "Memory allocation error in C function 'mldap_queryconn'");
  32.349 +  for (attr_idx=0; attr_idx<nattrs; attr_idx++) {
  32.350 +    lua_pushinteger(L, attr_idx+1);
  32.351 +    lua_gettable(L, 5);  // pushed onto variable stack position
  32.352 +    if (lua_type(L, -1) != LUA_TSTRING) {
  32.353 +      free(attrs);
  32.354 +      return luaL_error(L, "Element in attribute list is not a string");
  32.355 +    }
  32.356 +    attrs[i] = lua_tostring(L, -1);
  32.357 +  }
  32.358 +  // attrs[nattrs] = NULL;  // unnecessary due to calloc
  32.359 +
  32.360 +  // execute LDAP search and store pointer to the result in 'res':
  32.361 +  ldap_error = ldap_search_ext_s(
  32.362 +    *ldp_ptr,  // pointer to the opaque OpenLDAP structure representing the connection
  32.363 +    base,      // DN of the entry at which to start the search
  32.364 +    scope,     // scope of the search
  32.365 +    filter,    // string representation of the filter to apply in the search
  32.366 +    attrs,     // array of attribute descriptions (array of strings)
  32.367 +    0,         // do not only request attribute descriptions but also values
  32.368 +    NULL,      // no server controls
  32.369 +    NULL,      // no client controls
  32.370 +    NULL,      // do not set another timeout
  32.371 +    0,         // no sizelimit
  32.372 +    &res       // store result in 'res'
  32.373 +  );
  32.374 +
  32.375 +  // free data structures that have been allocated for the 'attrs' array:
  32.376 +  free(attrs);
  32.377 +
  32.378 +  // return nil, an error string, and an error code (integer) in case of error:
  32.379 +  if (ldap_error != LDAP_SUCCESS) {
  32.380 +    lua_pushnil(L);
  32.381 +    lua_pushstring(L, ldap_err2string(ldap_error));
  32.382 +    lua_pushinteger(L, ldap_error);
  32.383 +    return 3;
  32.384 +  }
  32.385 +
  32.386 +  // clear Lua stack:
  32.387 +  lua_settop(L, 0);
  32.388 +
  32.389 +  // create result table for all entries at stack position 1:
  32.390 +  lua_newtable(L);
  32.391 +
  32.392 +  // use ldap_first_entry() and ldap_next_entry() functions
  32.393 +  // to iterate through all entries in the result 'res'
  32.394 +  // and count 'i' from 1 up:
  32.395 +  for (
  32.396 +    ent=ldap_first_entry(*ldp_ptr, res), i=1;
  32.397 +    ent;
  32.398 +    ent=ldap_next_entry(*ldp_ptr, ent), i++
  32.399 +  ) {
  32.400 +
  32.401 +    char *attr;       // name of attribute
  32.402 +    BerElement *ber;  // structure to iterate through attributes
  32.403 +    char *dn;         // LDAP entry name (distinguished name)
  32.404 +
  32.405 +    // create result table for one entry at stack position 2:
  32.406 +    lua_newtable(L);
  32.407 +
  32.408 +    // use ldap_first_attribute() and ldap_next_attribute()
  32.409 +    // as well as 'BerElement *ber' to iterate through all attributes
  32.410 +    // ('ber' must be free'd with ber_free(ber, 0) call later):
  32.411 +    for (
  32.412 +      attr=ldap_first_attribute(*ldp_ptr, ent, &ber);
  32.413 +      attr;
  32.414 +      attr=ldap_next_attribute(*ldp_ptr, ent, ber)
  32.415 +    ) {
  32.416 +
  32.417 +      struct berval **vals;  // pointer to (first element of) array of values
  32.418 +      struct berval **val;   // pointer to a value represented as 'struct berval' structure
  32.419 +      int j;                 // integer to fill a Lua table
  32.420 +
  32.421 +      // push the attribute name to Lua stack position 3:
  32.422 +      lua_pushstring(L, attr);
  32.423 +
  32.424 +      // create a new table for the attribute's values on Lua stack position 4:
  32.425 +      lua_newtable(L);
  32.426 +
  32.427 +      // call ldap_get_values_len() to obtain the values
  32.428 +      // (required to be free'd later with ldap_value_free_len()):
  32.429 +      vals = ldap_get_values_len(*ldp_ptr, ent, attr);
  32.430 +
  32.431 +      // iterate through values and count 'j' from 1 up:
  32.432 +      for (val=vals, j=1; *val; val++, j++) {
  32.433 +
  32.434 +        // push value to Lua stack position 5:
  32.435 +        lua_pushlstring(L, (*val)->bv_val, (*val)->bv_len);
  32.436 +
  32.437 +        // pop value from Lua stack position 5
  32.438 +        // and store it in table on Lua stack position 4:
  32.439 +        lua_rawseti(L, 4, j);
  32.440 +
  32.441 +      }
  32.442 +
  32.443 +      // free data structure of values:
  32.444 +      ldap_value_free_len(vals);
  32.445 +
  32.446 +      // pop attribute name from Lua stack position 3
  32.447 +      // and pop value table from Lua stack position 4
  32.448 +      // and store them in result table for entry at Lua stack position 2:
  32.449 +      lua_settable(L, 2);
  32.450 +
  32.451 +    }
  32.452 +
  32.453 +    // free 'BerElement *ber' stucture that has been used to iterate through all attributes
  32.454 +    // (second argument is zero due to manpage of ldap_first_attribute()):
  32.455 +    ber_free(ber, 0);
  32.456 +
  32.457 +    // store distinguished name (DN) on Lua stack position 3
  32.458 +    // (aquired memory is free'd with ldap_memfree()):
  32.459 +    dn = ldap_get_dn(*ldp_ptr, ent);
  32.460 +    lua_pushstring(L, dn);
  32.461 +    ldap_memfree(dn);
  32.462 +
  32.463 +    // pop distinguished name (DN) from Lua stack position 3
  32.464 +    // and store it in field "dn" of entry result table at stack position 2
  32.465 +    lua_setfield(L, 2, "dn");
  32.466 +
  32.467 +    // pop entry result table from Lua stack position 2
  32.468 +    // and store it in table at stack position 1:
  32.469 +    lua_rawseti(L, 1, i);
  32.470 +    
  32.471 +  }
  32.472 +
  32.473 +  // return result table from top of Lua stack (stack position 1):
  32.474 +  return 1;
  32.475 +
  32.476 +}
  32.477 +
  32.478 +static int mldap_unbind(lua_State *L) {
  32.479 +  // Lua C function used as "unbind" function of module and "unbind" method of Lua userdata object
  32.480 +  // closing the LDAP connection (if still open)
  32.481 +  // returning nothing
  32.482 +
  32.483 +  LDAP **ldp_ptr;  // pointer to a pointer to the OpenLDAP structure representing the connection
  32.484 +
  32.485 +  // check if the first argument is a Lua userdata object with the correct metatable
  32.486 +  // and get a C pointer to that userdata object:
  32.487 +  ldp_ptr = luaL_checkudata(L, 1, MLDAP_REGKEY "connection_metatable");
  32.488 +
  32.489 +  // check if the Lua userdata object still contains a pointer:
  32.490 +  if (*ldp_ptr) {
  32.491 +
  32.492 +    // if it does, then call ldap_unbind_ext_s():
  32.493 +    ldap_unbind_ext_s(
  32.494 +      *ldp_ptr,  // pointer to the opaque OpenLDAP structure representing the connection
  32.495 +      NULL,      // no server controls
  32.496 +      NULL       // no client controls
  32.497 +    );
  32.498 +
  32.499 +    // store NULL pointer in Lua userdata to mark connection as closed
  32.500 +    *ldp_ptr = NULL;
  32.501 +  }
  32.502 +
  32.503 +  // returning nothing:
  32.504 +  return 0;
  32.505 +
  32.506 +}
  32.507 +
  32.508 +
  32.509 +// registration information for library functions:
  32.510 +static const struct luaL_Reg mldap_module_functions[] = {
  32.511 +  {"bind", mldap_bind},
  32.512 +  {"unbind", mldap_unbind},
  32.513 +  {NULL, NULL}
  32.514 +};
  32.515 +
  32.516 +
  32.517 +// registration information for methods of connection object: 
  32.518 +static const struct luaL_Reg mldap_connection_methods[] = {
  32.519 +  {"search", mldap_search},
  32.520 +  {"unbind", mldap_unbind},
  32.521 +  {NULL, NULL}
  32.522 +};
  32.523 +
  32.524 +
  32.525 +// registration information for connection metatable: 
  32.526 +static const struct luaL_Reg mldap_connection_metamethods[] = {
  32.527 +  {"__gc", mldap_unbind},
  32.528 +  {NULL, NULL}
  32.529 +};
  32.530 +
  32.531 +
  32.532 +// luaopen function to initialize/register library:
  32.533 +int luaopen_mldap(lua_State *L) {
  32.534 +
  32.535 +  // clear Lua stack:
  32.536 +  lua_settop(L, 0);
  32.537 +
  32.538 +  // create table with library functions on Lua stack position 1:
  32.539 +  luaL_newlib(L, mldap_module_functions);
  32.540 +
  32.541 +  // create metatable for connection objects on Lua stack position 2:
  32.542 +  luaL_newlib(L, mldap_connection_metamethods);
  32.543 +
  32.544 +  // create table with methods for connection object on Lua stack position 3:
  32.545 +  luaL_newlib(L, mldap_connection_methods);
  32.546 +
  32.547 +  // pop table with methods for connection object from Lua stack position 3
  32.548 +  // and store it as "__index" in metatable:
  32.549 +  lua_setfield(L, 2, "__index");
  32.550 +
  32.551 +  // pop table with metatable for connection objects from Lua stack position 2
  32.552 +  // and store it in the Lua registry:
  32.553 +  lua_setfield(L, LUA_REGISTRYINDEX, MLDAP_REGKEY "connection_metatable");
  32.554 +
  32.555 +  // create table for error code mappings on Lua stack position 2:
  32.556 +  lua_newtable(L);
  32.557 +
  32.558 +  // fill table for error code mappings
  32.559 +  // that maps integer error codes to error code strings
  32.560 +  // and vice versa:
  32.561 +  mldap_set_errorcodes(L);
  32.562 +
  32.563 +  // pop table for error code mappings from Lua stack position 2
  32.564 +  // and store it as "errorcodes" in table with library functions:
  32.565 +  lua_setfield(L, 1, "errorcodes");
  32.566 +
  32.567 +  // return table with library functions from top of Lua stack:
  32.568 +  return 1;
  32.569 +
  32.570 +}
    33.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    33.2 +++ b/lib/mldap/mldap_errorcodes.c	Fri Jul 18 21:42:59 2014 +0200
    33.3 @@ -0,0 +1,199 @@
    33.4 +/* This file contains error code mappings of LDAP error codes and OpenLDAP
    33.5 + * error codes.
    33.6 + *
    33.7 + * The collection of error codes (mldap_errorcodes[]) has been derived from
    33.8 + * the file ldap.h that is part of OpenLDAP Software. OpenLDAP's license
    33.9 + * information is stated below:
   33.10 + *
   33.11 + * This work is part of OpenLDAP Software <http://www.openldap.org/>.
   33.12 + * 
   33.13 + * Copyright 1998-2013 The OpenLDAP Foundation.
   33.14 + * All rights reserved.
   33.15 + *
   33.16 + * Redistribution and use in source and binary forms, with or without
   33.17 + * modification, are permitted only as authorized by the OpenLDAP
   33.18 + * Public License.
   33.19 + *
   33.20 + * A copy of this license is available below:
   33.21 + *
   33.22 + * The OpenLDAP Public License
   33.23 + * Version 2.8, 17 August 2003
   33.24 + *
   33.25 + * Redistribution and use of this software and associated documentation
   33.26 + * ("Software"), with or without modification, are permitted provided
   33.27 + * that the following conditions are met:
   33.28 + * 
   33.29 + * 1. Redistributions in source form must retain copyright statements
   33.30 + *    and notices,
   33.31 + * 
   33.32 + * 2. Redistributions in binary form must reproduce applicable copyright
   33.33 + *    statements and notices, this list of conditions, and the following
   33.34 + *    disclaimer in the documentation and/or other materials provided
   33.35 + *    with the distribution, and
   33.36 + * 
   33.37 + * 3. Redistributions must contain a verbatim copy of this document.
   33.38 + * 
   33.39 + * The OpenLDAP Foundation may revise this license from time to time.
   33.40 + * Each revision is distinguished by a version number.  You may use
   33.41 + * this Software under terms of this license revision or under the
   33.42 + * terms of any subsequent revision of the license.
   33.43 + * 
   33.44 + * THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS
   33.45 + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
   33.46 + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
   33.47 + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT
   33.48 + * SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S)
   33.49 + * OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
   33.50 + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   33.51 + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   33.52 + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   33.53 + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   33.54 + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   33.55 + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   33.56 + * POSSIBILITY OF SUCH DAMAGE.
   33.57 + * 
   33.58 + * The names of the authors and copyright holders must not be used in
   33.59 + * advertising or otherwise to promote the sale, use or other dealing
   33.60 + * in this Software without specific, written prior permission.  Title
   33.61 + * to copyright in this Software shall at all times remain with copyright
   33.62 + * holders.
   33.63 + * 
   33.64 + * OpenLDAP is a registered trademark of the OpenLDAP Foundation.
   33.65 + * 
   33.66 + * Copyright 1999-2003 The OpenLDAP Foundation, Redwood City,
   33.67 + * California, USA.  All Rights Reserved.  Permission to copy and
   33.68 + * distribute verbatim copies of this document is granted.
   33.69 + *
   33.70 + * End of OpenLDAP Public License
   33.71 + *
   33.72 + * Portions Copyright (c) 1990 Regents of the University of Michigan.
   33.73 + * All rights reserved.
   33.74 + *
   33.75 + * Redistribution and use in source and binary forms are permitted
   33.76 + * provided that this notice is preserved and that due credit is given
   33.77 + * to the University of Michigan at Ann Arbor. The name of the University
   33.78 + * may not be used to endorse or promote products derived from this
   33.79 + * software without specific prior written permission. This software
   33.80 + * is provided ``as is'' without express or implied warranty.
   33.81 + *
   33.82 + * End of OpenLDAP's license information
   33.83 + */
   33.84 +
   33.85 +// type for entry in mldap_errorcodes[] array:
   33.86 +struct mldap_errorcode {
   33.87 +  const char *ident;
   33.88 +  int code;
   33.89 +};
   33.90 +
   33.91 +// NULL terminated array of error code strings with error code integers
   33.92 +// derived from ldap.h (see above copyright notice):
   33.93 +static const struct mldap_errorcode mldap_errorcodes[] = {
   33.94 +  {"operations_error", 1},
   33.95 +  {"protocol_error", 2},
   33.96 +  {"timelimit_exceeded", 3},
   33.97 +  {"sizelimit_exceeded", 4},
   33.98 +  {"compare_false", 5},
   33.99 +  {"compare_true", 6},
  33.100 +//  {"auth_method_not_supported", 7},
  33.101 +  {"strong_auth_not_supported", 7},
  33.102 +  {"strong_auth_required", 8},
  33.103 +//  {"stronger_auth_required", 8},
  33.104 +  {"partial_results", 9},
  33.105 +  {"referral", 10},
  33.106 +  {"adminlimit_exceeded", 11},
  33.107 +  {"unavailable_critical_extension", 12},
  33.108 +  {"confidentiality_required", 13},
  33.109 +  {"sasl_bind_in_progress", 14},
  33.110 +  {"no_such_attribute", 16},
  33.111 +  {"undefined_type", 17},
  33.112 +  {"inappropriate_matching", 18},
  33.113 +  {"constraint_violation", 19},
  33.114 +  {"type_or_value_exists", 20},
  33.115 +  {"invalid_syntax", 21},
  33.116 +  {"no_such_object", 32},
  33.117 +  {"alias_problem", 33},
  33.118 +  {"invalid_dn_syntax", 34},
  33.119 +  {"is_leaf", 35},
  33.120 +  {"alias_deref_problem", 36},
  33.121 +  {"x_proxy_authz_failure", 47},
  33.122 +  {"inappropriate_auth", 48},
  33.123 +  {"invalid_credentials", 49},
  33.124 +  {"insufficient_access", 50},
  33.125 +  {"busy", 51},
  33.126 +  {"unavailable", 52},
  33.127 +  {"unwilling_to_perform", 53},
  33.128 +  {"loop_detect", 54},
  33.129 +  {"naming_violation", 64},
  33.130 +  {"object_class_violation", 65},
  33.131 +  {"not_allowed_on_nonleaf", 66},
  33.132 +  {"not_allowed_on_rdn", 67},
  33.133 +  {"already_exists", 68},
  33.134 +  {"no_object_class_mods", 69},
  33.135 +  {"results_too_large", 70},
  33.136 +  {"affects_multiple_dsas", 71},
  33.137 +  {"vlv_error", 76},
  33.138 +  {"other", 80},
  33.139 +  {"cup_resources_exhausted", 113},
  33.140 +  {"cup_security_violation", 114},
  33.141 +  {"cup_invalid_data", 115},
  33.142 +  {"cup_unsupported_scheme", 116},
  33.143 +  {"cup_reload_required", 117},
  33.144 +  {"cancelled", 118},
  33.145 +  {"no_such_operation", 119},
  33.146 +  {"too_late", 120},
  33.147 +  {"cannot_cancel", 121},
  33.148 +  {"assertion_failed", 122},
  33.149 +  {"proxied_authorization_denied", 123},
  33.150 +  {"sync_refresh_required", 4096},
  33.151 +  {"x_sync_refresh_required", 16640},
  33.152 +  {"x_assertion_failed", 16655},
  33.153 +  {"x_no_operation", 16654},
  33.154 +  {"x_no_referrals_found", 16656},
  33.155 +  {"x_cannot_chain", 16657},
  33.156 +  {"x_invalidreference", 16658},
  33.157 +  {"x_txn_specify_okay", 16672},
  33.158 +  {"x_txn_id_invalid", 16673},
  33.159 +  {"server_down", (-1)},
  33.160 +  {"local_error", (-2)},
  33.161 +  {"encoding_error", (-3)},
  33.162 +  {"decoding_error", (-4)},
  33.163 +  {"timeout", (-5)},
  33.164 +  {"auth_unknown", (-6)},
  33.165 +  {"filter_error", (-7)},
  33.166 +  {"user_cancelled", (-8)},
  33.167 +  {"param_error", (-9)},
  33.168 +  {"no_memory", (-10)},
  33.169 +  {"connect_error", (-11)},
  33.170 +  {"not_supported", (-12)},
  33.171 +  {"control_not_found", (-13)},
  33.172 +  {"no_results_returned", (-14)},
  33.173 +  {"more_results_to_return", (-15)},
  33.174 +  {"client_loop", (-16)},
  33.175 +  {"referral_limit_exceeded", (-17)},
  33.176 +  {"x_connecting", (-18)},
  33.177 +  {NULL, 0}
  33.178 +};
  33.179 +
  33.180 +void mldap_set_errorcodes(lua_State *L) {
  33.181 +  // stores mldap_errorcodes[] mappings in the Lua table on top of the stack
  33.182 +  // in both directions (string mapped to integer and vice versa)
  33.183 +
  33.184 +  const struct mldap_errorcode *errorcode;  // pointer to entry in mldap_errorcodes[] array
  33.185 +
  33.186 +  // iterate through entries in mldap_errorcodes[] array:
  33.187 +  for (errorcode=mldap_errorcodes; errorcode->ident; errorcode++) {
  33.188 +
  33.189 +    // store a mapping from the string to the integer:
  33.190 +    lua_pushstring(L, errorcode->ident);
  33.191 +    lua_pushinteger(L, errorcode->code);
  33.192 +    lua_settable(L, -3);
  33.193 +
  33.194 +    // store a mapping from the integer to the string:
  33.195 +    lua_pushinteger(L, errorcode->code);
  33.196 +    lua_pushstring(L, errorcode->ident);
  33.197 +    lua_settable(L, -3);
  33.198 +
  33.199 +  }
  33.200 +
  33.201 +}
  33.202 +
    34.1 --- a/model/member.lua	Thu Jul 17 23:38:35 2014 +0200
    34.2 +++ b/model/member.lua	Fri Jul 18 21:42:59 2014 +0200
    34.3 @@ -273,6 +273,17 @@
    34.4    self.get_db_conn().query("LOCK TABLE " .. self:get_qualified_table() .. " IN ROW SHARE MODE")
    34.5  end
    34.6  
    34.7 +
    34.8 +function Member:get_all_by_authority(authority)
    34.9 +  
   34.10 +  local members = Member:new_selector()
   34.11 +    :add_where{ "authority = ?", authority }
   34.12 +    :add_field("authority_data->'uid' as authority_data_uid")
   34.13 +    :exec()
   34.14 +    
   34.15 +  return members
   34.16 +end
   34.17 +
   34.18  function Member.object:set_password(password)
   34.19    trace.disable()
   34.20    
   34.21 @@ -372,17 +383,144 @@
   34.22  end
   34.23  
   34.24  function Member:by_login_and_password(login, password)
   34.25 -  local selector = self:new_selector()
   34.26 -  selector:add_field({ "now() > COALESCE(last_delegation_check, activated) + ?::interval", config.check_delegations_interval_hard }, "needs_delegation_check_hard")
   34.27 -  selector:add_where{'"login" = ?', login }
   34.28 -  selector:add_where('NOT "locked"')
   34.29 -  selector:optional_object_mode()
   34.30 -  local member = selector:exec()
   34.31 -  if member and member:check_password(password) then
   34.32 -    return member
   34.33 -  else
   34.34 -    return nil
   34.35 + 
   34.36 +  local function prepare_login_selector()
   34.37 +    local selector = self:new_selector()
   34.38 +    selector:add_field({ "now() > COALESCE(last_delegation_check, activated) + ?::interval", config.check_delegations_interval_hard }, "needs_delegation_check_hard")
   34.39 +    selector:add_where('NOT "locked"')
   34.40 +    selector:optional_object_mode()
   34.41 +    return selector
   34.42 +  end
   34.43 +  
   34.44 +  local function do_local_login()
   34.45 +    local selector = prepare_login_selector()
   34.46 +    selector:add_where{'"login" = ?', login }
   34.47 +    local member = selector:exec()
   34.48 +    if member and member:check_password(password) then
   34.49 +      return member
   34.50 +    else
   34.51 +      return nil
   34.52 +    end
   34.53    end
   34.54 +  
   34.55 +  if config.ldap.member then
   34.56 +
   34.57 +    -- Let's check the users credentials against the LDAP      
   34.58 +    local ldap_entry, ldap_err = ldap.check_credentials(login, password)
   34.59 +
   34.60 +    -- Is the user already registered as member?
   34.61 +    local uid
   34.62 +    local selector = prepare_login_selector()
   34.63 +
   34.64 +    -- Get login name from LDAP entry
   34.65 +    if ldap_entry then
   34.66 +      uid = config.ldap.member.uid_map(ldap_entry)
   34.67 +      selector:add_where{'"authority" = ? AND "authority_data"->\'uid\' = ?', "ldap", uid }
   34.68 +
   34.69 +    -- or build it from the login
   34.70 +    else
   34.71 +      login = config.ldap.member.login_normalizer(login)
   34.72 +      selector:add_where{'"authority" = ? AND "authority_data"->\'login\' = ?', "ldap", login }
   34.73 +    end
   34.74 +    
   34.75 +    local member = selector:exec()
   34.76 +    -- The member is already registered
   34.77 +    if member then
   34.78 +
   34.79 +      -- The credentials entered by the user are invalid
   34.80 +      if ldap_err == "invalid_credentials" then
   34.81 +        
   34.82 +        -- Check if the user tried a cached password (which is invalid now)
   34.83 +        if config.ldap.member.cache_passwords and member:check_password(password) then
   34.84 +          member.password = nil
   34.85 +          member:save()
   34.86 +        end
   34.87 +        
   34.88 +        -- Try a regular login
   34.89 +        return do_local_login()
   34.90 +
   34.91 +      end
   34.92 +      
   34.93 +      -- The credentials were accepted by the LDAP server and no error occured
   34.94 +      if ldap_entry and not ldap_err then
   34.95 +        
   34.96 +        -- Cache the password (if feature enabled)
   34.97 +        if config.ldap.member.cache_passwords and not member:check_password(password) then
   34.98 +          member:set_password(password)
   34.99 +        end
  34.100 +
  34.101 +        -- update the member attributes and privileges from LDAP
  34.102 +        local ldap_conn, ldap_err, err, err2 = ldap.update_member_attr(member, nil, uid)
  34.103 +        if not err then
  34.104 +          local err = member:try_save()
  34.105 +          if err then
  34.106 +            return nil, "member_save_error", err
  34.107 +          end
  34.108 +          local succes, err, err2 = ldap.update_member_privileges(member, ldap_entry)
  34.109 +          if err then
  34.110 +            return nil, "update_member_privileges_error", err, err2
  34.111 +          end
  34.112 +          return member
  34.113 +        end
  34.114 +
  34.115 +      end
  34.116 +
  34.117 +      -- Some kind of LDAP error happened, if cached password are enabled,
  34.118 +      -- check user credentials against the cache
  34.119 +      if config.ldap.member.cache_passwords and member:check_password(password) then
  34.120 +
  34.121 +        -- return the successfully logged in member
  34.122 +        return member
  34.123 +
  34.124 +      end
  34.125 +      
  34.126 +    -- The member is not registered
  34.127 +    elseif config.ldap.member.registration and ldap_entry and not ldap_err then
  34.128 +      -- Automatic registration ("auto")
  34.129 +      if config.ldap.member.registration == "auto" then
  34.130 +        member = Member:new()
  34.131 +        member.authority = "ldap"
  34.132 +        local ldap_login
  34.133 +        if config.ldap.member.cache_passwords then 
  34.134 +          if config.ldap.member.login_normalizer then
  34.135 +            ldap_login = config.ldap.member.login_normalizer(login)
  34.136 +          else
  34.137 +            ldap_login = login
  34.138 +          end
  34.139 +        end
  34.140 +        -- TODO change this when SQL layers supports hstore
  34.141 +        member.authority_data = encode.pg_hstore{
  34.142 +          uid = uid,
  34.143 +          login = ldap_login
  34.144 +        }
  34.145 +        member.activated = "now"
  34.146 +        member.last_activity = "now"
  34.147 +        if config.ldap.member.cache_passwords then
  34.148 +          member:set_password(password)
  34.149 +        end
  34.150 +        local ldap_conn, ldap_err, err, err2 = ldap.update_member_attr(member, nil, uid)
  34.151 +        if not err then
  34.152 +          local err = member:try_save()
  34.153 +          if err then
  34.154 +            return nil, "member_save_error", err
  34.155 +          end
  34.156 +          local success, err, err2 = ldap.update_member_privileges(member, ldap_entry)
  34.157 +          if err then
  34.158 +            return nil, "update_member_privileges_error", err, err2
  34.159 +          end
  34.160 +          return member
  34.161 +        end
  34.162 +
  34.163 +      -- No automatic registration
  34.164 +      else
  34.165 +        return nil, "ldap_credentials_valid_but_no_member", uid
  34.166 +      end
  34.167 +    end
  34.168 +    
  34.169 +  end
  34.170 +
  34.171 +  return do_local_login()
  34.172 +  
  34.173  end
  34.174  
  34.175  function Member:by_login(login)
    35.1 --- a/model/privilege.lua	Thu Jul 17 23:38:35 2014 +0200
    35.2 +++ b/model/privilege.lua	Fri Jul 18 21:42:59 2014 +0200
    35.3 @@ -23,4 +23,10 @@
    35.4      :add_where{ "unit_id = ? AND member_id = ?", unit_id, member_id }
    35.5      :optional_object_mode()
    35.6      :exec()
    35.7 +end
    35.8 +
    35.9 +function Privilege:by_member_id(member_id)
   35.10 +  return self:new_selector()
   35.11 +    :add_where{ "member_id = ?", member_id }
   35.12 +    :exec()
   35.13  end
   35.14 \ No newline at end of file
    36.1 --- a/model/session.lua	Thu Jul 17 23:38:35 2014 +0200
    36.2 +++ b/model/session.lua	Fri Jul 18 21:42:59 2014 +0200
    36.3 @@ -28,6 +28,7 @@
    36.4  function Session:by_ident(ident)
    36.5    local selector = self:new_selector()
    36.6    selector:add_where{ 'ident = ?', ident }
    36.7 +  selector:add_field{ '"authority_data" -> \'uid\' as authority_data_uid' }
    36.8    selector:optional_object_mode()
    36.9    return selector:exec()
   36.10  end

Impressum / About Us