summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile1
-rw-r--r--lib/authenticator-plaintext.lua327
-rw-r--r--lib/authenticator.lua293
3 files changed, 321 insertions, 300 deletions
diff --git a/lib/Makefile b/lib/Makefile
index c064fe4..23a1a31 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -13,6 +13,7 @@ LIB_DIST=fs.lua\
split.lua\
validator.lua\
web_elements.lua\
+ authenticator.lua\
authenticator-plaintext.lua\
daemoncontrol.lua\
getopts.lua\
diff --git a/lib/authenticator-plaintext.lua b/lib/authenticator-plaintext.lua
index f7bdd1a..e2262b3 100644
--- a/lib/authenticator-plaintext.lua
+++ b/lib/authenticator-plaintext.lua
@@ -5,41 +5,36 @@
The password file is in the format:
-userid:password:username:role1[,role2...]
+userid:password:username:role1[,role2...]:dnsfile1[,dnsfile2...]
]]--
module (..., package.seeall)
-local sess = require ("session")
-require("roles")
-
-local pvt={}
-
-pvt.parse_authfile = function(filename)
+load_database = function(self)
local row = {}
-- open our password file
- local f = io.open (filename)
+ local passwd_path = self.conf.confdir .. "/passwd"
+ local f = io.open(passwd_path)
if f then
local m = (f:read("*all") or "" ).. "\n"
f:close()
- for l in string.gmatch(m, "(%C*)\n") do
- local userid, password, username, roles =
- string.match(l, "([^:]*):([^:]*):([^:]*):(.*)")
- local r = {}
- roles=roles or ""
- for x in string.gmatch(roles, "([^,][%w_]+),?") do
- table.insert (r, x )
+ for l in string.gmatch(m, "([^\n]+)\n?") do
+ local fields = {}
+ for x in string.gmatch(l, "([^:]*):?") do
+ fields[#fields + 1] = x
+ end
+ if fields[1] and fields[1] ~= "" then
+ local a = {}
+ a.userid = fields[1] or ""
+ a.password = fields[2] or ""
+ a.username = fields[3] or ""
+ a.roles = fields[4] or ""
+ a.dnsfiles = fields[5] or ""
+ table.insert (row, a)
end
-
- local a = {}
- a.userid = userid
- a.password = password
- a.username = username
- a.roles = r
- table.insert (row, a)
end
return row
else
@@ -47,295 +42,27 @@ pvt.parse_authfile = function(filename)
end
end
-pvt.get_id = function(userid, authstruct)
- if authstruct ~= nil then
- for x = 1,#authstruct do
- if authstruct[x].userid == userid then
- return authstruct[x]
- end
- end
- end
- return nil
-end
-
-pvt.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
-end
-
-pvt.availablefields = function (field)
- -- This is a list of fileds in the /passwd file that we are allowed to use.
- -- Could be used to check that right variable-name is used.
- local availablefileds = {
- ['userid']=true,
- ['password']=true,
- ['username']=true,
- ['roles']=true,
- }
- return availablefileds[field]
-end
-
--- validate the settings (ignore password if it's nil)
-local validate_settings = function (self, userid, username, password, password_confirm, roles)
- local errormessage = {}
-
- -- Set errormessages when entering invalid values
- if (#userid == 0) then errormessage.userid = "You need to enter a valid userid!" end
- if string.find(userid, "[^%w_]") then errormessage.userid = "Userid can only contain letters, numbers, and '_'" end
- if string.find(username, "%p") then errormessage.username = "Real name cannot contain punctuation" end
- if password then
- if (#password == 0) then
- errormessage.password = "Password cannot be blank!"
- elseif (password ~= password_confirm) then
- errormessage.password_confirm = "You entered wrong password/confirmation"
- else
- local weak_password_result, weak_password_errormessage = pvt.weak_password(password)
- if (weak_password_result) then errormessage.password = weak_password_errormessage end
- end
- end
- local reverseroles = {}
- for x,role in pairs(list_roles(self)) do
- reverseroles[role] = x
- end
- for x,role in pairs(roles) do
- if reverseroles[role] == nil then
- errormessage.roles = "Invalid role"
- break
- end
- end
-
- -- Return false if any errormessages are set
- for k,v in pairs(errormessage) do
- return false, errormessage
- end
-
- return true, errormessage
-end
-
---- public methods
-
--- This function returns true or false, and
--- if false: the reason for failure
-authenticate = function ( self, userid, password )
- password = password or ""
- userid = userid or ""
-
- local t = pvt.parse_authfile(self.conf.confdir .. "/passwd")
-
- if t == false then
- return false, "password file is missing"
- end
-
- if userid ~= nil then
- local id = pvt.get_id (userid, t)
- if id == false or id == nil then
- return false, "Userid not found"
- end
- if id.password ~= fs.md5sum_string(password) then
- return false, "Invalid password"
- end
- else
- return false
- end
+write_entry = function(self, entry)
+ delete_entry(self, entry.userid)
+ -- Set path to passwordfile
+ local passwd_path = self.conf.confdir .. "/passwd"
+ -- Write the newline into the file
+ fs.write_line_file(passwd_path, (entry.userid or "") .. ":" .. (entry.password or "") .. ":" .. (entry.username or "") .. ":" .. (entry.roles or "") .. ":" .. (entry.dnsfiles or "") )
return true
end
--- This function returns the username and roles
--- or false on an error
-get_userinfo = function ( self, userid )
- local t = pvt.parse_authfile(self.conf.confdir .. "/passwd")
- if t == false then
- return nil
- else
- return pvt.get_id (userid, t)
- end
-end
-
-get_userinfo_roles = function (self, userid)
- local t = pvt.parse_authfile(self.conf.confdir .. "/passwd")
- if t == false then
- return nil
- else
- temp = pvt.get_id (userid, t)
- return temp.roles
- end
-end
-
-list_users = function (self)
- local output = {}
- local t = pvt.parse_authfile(self.conf.confdir .. "/passwd")
- if t == false then
- return nil
- else
- for k,v in pairs(t) do
- table.insert(output,v.userid)
- end
- return output
-
- end
-end
-
-list_roles = function (self)
- -- Get list of available roles (everything except ALL)
- local avail_roles = roles.list_all_roles()
- for x,role in ipairs(avail_roles) do
- if role=="ALL" then
- table.remove(avail_roles,x)
- break
- end
- end
- return avail_roles
-end
-
-change_setting = function (self, userid, parameter, value)
- local result = true
- local errormessage = {}
-
- -- Get the current user info
- local userinfo = get_userinfo(self, userid)
- if userinfo == nil then
- errormessage.userid = "This userid does not exist!"
- result = false
- end
-
- -- Check if user entered available commands
- if not (userid) or not (parameter) or not (pvt.availablefields(parameter)) or not (value) then
- errormessage.userid = "You need to enter valid userid, parameter and value!"
- result = false
- end
-
- -- Check if userid already used
- if (parameter == "userid") and (userid ~= value) then
- for k,v in pairs(list_users(self)) do
- if (v == value) then
- errormessage.userid = "This userid already exists!"
- result = false
- end
- end
- end
-
- if result == true then
- -- Validate parameter
- userinfo[parameter] = value
- local password, password_confirm
- if (parameter == "password") then
- userinfo.password = fs.md5sum_string(value)
- password = value
- password_confirm = value
- end
- result, errormessage = validate_settings(self, username.userid, userinfo.username, password, password_confirm, userinfo.roles)
- end
-
- -- Write the updated user
- if (result == true) then
- delete_user(self, userid)
-
- -- Set path to passwordfile
- local passwd_path = self.conf.confdir .. "/passwd"
- -- Write the newline into the file
- fs.write_line_file(passwd_path, userinfo.userid .. ":" .. userinfo.password .. ":" .. userinfo.username .. ":" .. table.concat(userinfo.roles,",") )
- end
-
- return result, errormessage
-end
-
--- For an existing user, change the settings that are non-nil
-change_settings = function (self, userid, username, password, password_confirm, roles)
- local result = true
- local errormessage = {}
-
- -- Get the current user info
- local userinfo = get_userinfo(self, userid)
- if userinfo == nil then
- errormessage.userid = "This userid does not exist!"
- result = false
- end
-
- local change = username or password or password_confirm or roles
- if change then
- -- Validate the inputs
- if (result == true) then
- -- Use the current settings if new ones are nil, except for password
- result, errormessage = validate_settings(self, userid, username or userinfo.username, password, password_confirm, roles or userinfo.roles)
- end
-
- -- Update all the fields
- if (result == true) then
- userinfo.username = username or userinfo.username
- if password then
- userinfo.password = fs.md5sum_string(password)
- end
- userinfo.roles = roles or userinfo.roles
-
- -- Write the updated user
- delete_user(self, userid)
-
- -- Set path to passwordfile
- local passwd_path = self.conf.confdir .. "/passwd"
- -- Write the newline into the file
- fs.write_line_file(passwd_path, userid .. ":" .. userinfo.password .. ":" .. userinfo.username .. ":" .. table.concat(userinfo.roles,",") )
- end
- end
-
- return result, errormessage
-end
-
-new_settings = function (self, userid, username, password, password_confirm, roles)
- local result = true
- local errormessage = {}
- -- make sure to check all fields
- userid = userid or ""
- username = username or ""
- password = password or ""
- password_confirm = password_confirm or ""
- roles = roles or {}
-
- -- Check if userid already used
- for k,v in pairs(list_users(self)) do
- if (v == userid) then
- errormessage.userid = "This userid already exists!"
- result = false
- end
- end
-
- -- validate the settings
- if (result == true) then
- result, errormessage = validate_settings(self, userid, username, password, password_confirm, roles)
- end
-
- -- write the new user
- if (result == true) then
- -- Set path to passwordfile
- local passwd_path = self.conf.confdir .. "/passwd"
-
- -- Write the newline into the file
- fs.write_line_file(passwd_path, userid .. ":" .. fs.md5sum_string(password) .. ":" .. username .. ":" .. table.concat(roles,",") )
- end
-
- return result, errormessage
-end
-
-delete_user = function (self, userid)
+delete_entry = function (self, userid)
local result = false
- local errormessage = {userid="User not found"}
-
+
local passwd_path = self.conf.confdir .. "/passwd"
local passwdfilecontent = fs.read_file_as_array(passwd_path)
local output = {}
for k,v in pairs(passwdfilecontent) do
- if not ( string.match(v, "^".. userid .. ":") ) then
+ if not ( string.match(v, "^".. userid .. ":") ) and not string.match(v, "^%s*$") then
table.insert(output, v)
else
result = true
- errormessage = {}
end
end
@@ -344,5 +71,5 @@ delete_user = function (self, userid)
fs.write_file(passwd_path, table.concat(output,"\n"))
end
- return result, errormessage
+ return result
end
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