diff options
Diffstat (limited to 'lib/authenticator.lua')
-rw-r--r-- | lib/authenticator.lua | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/lib/authenticator.lua b/lib/authenticator.lua new file mode 100644 index 0000000..e6310ec --- /dev/null +++ b/lib/authenticator.lua @@ -0,0 +1,293 @@ +-- ACF Authenticator - does validation and loads sub-authenticator to read/write database +module (..., package.seeall) + +require("modelfunctions") +require("fs") + +-- This will be the sub-authenticator +local auth +-- This will hold the auth structure from the database +local authstruct +-- This is a list of fields in the database that we are allowed to use. +-- Could be used to check that right variable-name is used. +local availablefields = { + ['userid']=true, + ['password']=true, + ['username']=true, + ['roles']=true, + ['dnsfiles']=true, + } + + +local load_auth = function(self) + -- For now, just loads the plaintext version + auth = auth or require("authenticator-plaintext") +end + +local load_database = function(self) + load_auth(self) + authstruct = authstruct or auth.load_database(self) +end + +local get_id = function(userid) + if authstruct ~= nil then + for x = 1,#authstruct do + if authstruct[x].userid == userid then + return authstruct[x] + end + end + end + return nil +end + +local weak_password = function(password) + -- If password is too short, return false + if (#password < 4) then + return true, "Password is too short!" + end + if (tonumber(password)) then + return true, "Password can't contain only numbers!" + end + + return false, nil +end + +local write_settings = function(self, settings, id) + load_auth() + id = id or {} + -- Password, password_confirm, roles, dnsfiles are allowed to not exist, just leave the same + id.userid = settings.value.userid.value + id.username = settings.value.username.value + if settings.value.password then id.password = fs.md5sum_string(settings.value.password.value) end + if settings.value.roles then id.roles = table.concat(settings.value.roles.value, ",") end + if settings.value.dnsfiles then id.dnsfiles = table.concat(settings.value.dnsfiles.value, ",") end + + return auth.write_entry(self, id) +end + +-- validate the settings (ignore password if it's nil) +local validate_settings = function(settings) + -- Password, password_confirm, roles, dnsfiles are allowed to not exist, just leave the same + -- Set errtxt when entering invalid values + if (#settings.value.userid.value == 0) then settings.value.userid.errtxt = "You need to enter a valid userid!" end + if string.find(settings.value.userid.value, "[^%w_]") then settings.value.userid.errtxt = "Can only contain letters, numbers, and '_'" end + if string.find(settings.value.username.value, "%p") then settings.value.username.value = "Cannot contain punctuation" end + if settings.value.password then + if (#settings.value.password.value == 0) then + settings.value.password.errtxt = "Password cannot be blank!" + elseif (not settings.value.password_confirm) or (settings.value.password.value ~= settings.value.password_confirm.value) then + settings.value.password.errtxt = "You entered wrong password/confirmation" + else + local weak_password_result, weak_password_errormessage = weak_password(settings.value.password.value) + if (weak_password_result) then settings.value.password.errtxt = weak_password_errormessage end + end + end + if settings.value.roles then modelfunctions.validatemulti(settings.value.roles) end + if settings.value.dnsfiles then modelfunctions.validatemulti(settings.value.dnsfiles) end + + -- Return false if any errormessages are set + for name,value in pairs(settings.value) do + if value.errtxt then + return false, settings + end + end + + return true, settings +end + +--- public methods + +-- This function returns true or false, and +-- if false: the reason for failure +authenticate = function(self, userid, password) + local errtxt + + if not userid or not password then + errtxt = "Invalid parameter" + else + load_database(self) + + if authstruct == false then + errtxt = "Could not load authentication database" + else + local id = get_id(userid) + if not id then + errtxt = "Userid not found" + elseif id.password ~= fs.md5sum_string(password) then + errtxt = "Invalid password" + end + end + end + + return (errtxt == nil), errtxt +end + +-- This function returns the username, roles, ... +get_userinfo = function(self, userid) + load_database(self) + local id = get_id(userid) + local user = cfe({ value=userid, label="User id" }) + local username = cfe({ label="Real name" }) + if id then + username.value = id.username + elseif userid then + user.errtxt = "User does not exist" + end + local password = cfe({ label="Password" }) + local password_confirm = cfe({ label="Password (confirm)" }) + local roles = get_userinfo_roles(self, userid) + local dnsfiles = get_userinfo_dnsfiles(self, userid) + + return cfe({ type="group", value={ userid=user, username=username, password=password, password_confirm=password_confirm, roles=roles, dnsfiles=dnsfiles }, label="User Config" }) +end + +get_userinfo_roles = function(self, userid) + load_database(self) + local id = get_id(userid) + local roles = cfe({ type="multi", value={}, label="Roles", option={} }) + if id then + for x in string.gmatch(id.roles or "", "([^,]+),?") do + roles.value[#roles.value + 1] = x + end + elseif userid then + roles.errtxt = "Could not load roles" + end + local rol = require("roles") + if rol then + local avail_roles = rol.list_all_roles() + for x,role in ipairs(avail_roles) do + if role=="ALL" then + table.remove(avail_roles,x) + break + end + end + roles.option = avail_roles + end + return roles +end + +get_userinfo_dnsfiles = function(self, userid) + load_database(self) + local id = get_id(userid) + local dnsfiles = cfe({ type="multi", value={}, label="DNS Files", option={} }) + if id then + for x in string.gmatch(id.dnsfiles or "", "([^,]+),?") do + dnsfiles.value[#dnsfiles.value + 1] = x + end + elseif userid then + dnsfiles.errtxt = "Could not load DNS files" + end + local dns = self:new("tinydns/tinydns") + if dns then + local avail_files = dns.model.getfilelist() + dnsfiles.option = avail_files.value + dns:destroy() + end + return dnsfiles +end + +list_users = function (self) + load_database(self) + local output = {} + if authstruct then + for k,v in pairs(authstruct) do + table.insert(output,v.userid) + end + end + return output +end + +-- UNTESTED +-- This function will change one user setting by name +-- Cannot be used for password or userid +change_setting = function (self, userid, parameter, value) + local success = false + local cmdresult = "Failed to change setting" + local errtxt + + -- Get the current user info + local userinfo = get_userinfo(self, userid) + if not userinfo then + errtxt = "This userid does not exist" + end + + -- Check if user entered available commands + if not value then + errtxt = "Invalid value" + elseif not (pvt.availablefields(parameter)) then + errtxt = "Invalid parameter" + elseif parameter == "userid" or parameter == "password" then + errtxt = "Cannot change "..parameter.." with this function" + else + userinfo.value[parameter].value = value + userinfo.value.password = nil + userinfo.value.password_confirm = nil + if not validate_settings(userinfo) then + errtxt = userinfo.value[parameter].errtxt + else + success = write_settings(settings) + end + end + + if success then cmdresult = "Changed setting" end + + return cfe({ value=cmdresult, label="Change setting result", errtxt=errtxt }) +end + +-- For an existing user, change the settings that are non-nil +change_settings = function (self, settings) + local success, settings = validate_settings(settings) + + -- Get the current user info + local id + if success then + load_database(self) + id = get_id(settings.value.userid.value) + if not id then + settings.value.userid.errtxt = "This userid does not exist!" + success = false + end + end + + if success then + success = write_settings(self, settings, id) + end + + if not success then + settings.errtxt = "Failed to save settings" + end + + return settings +end + +new_settings = function (self, settings) + local success, settings = validate_settings(settings) + + if success then + load_database(self) + local id = get_id(settings.value.userid.value) + if id then + settings.value.userid.errtxt = "This userid already exists!" + success = false + end + end + + if success then + success = write_settings(self, settings, id) + end + + if not success then + settings.errtxt = "Failed to create new user" + end + + return settings +end + +delete_user = function (self, userid) + load_auth(self) + local cmdresult = "Failed to delete user" + if auth.delete_entry(self, userid) then + cmdresult = "User deleted" + end + return cfe({ value=cmdresult, label="Delete user result" }) +end |