summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/util-sql-logins.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/mkiv/util-sql-logins.lua')
-rw-r--r--tex/context/base/mkiv/util-sql-logins.lua305
1 files changed, 305 insertions, 0 deletions
diff --git a/tex/context/base/mkiv/util-sql-logins.lua b/tex/context/base/mkiv/util-sql-logins.lua
new file mode 100644
index 000000000..dcb48fb35
--- /dev/null
+++ b/tex/context/base/mkiv/util-sql-logins.lua
@@ -0,0 +1,305 @@
+if not modules then modules = { } end modules ['util-sql-logins'] = {
+ version = 1.001,
+ comment = "companion to lmx-*",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+if not utilities.sql then require("util-sql") end
+
+local sql = utilities.sql
+local sqlexecute = sql.execute
+local sqlmakeconverter = sql.makeconverter
+
+local format = string.format
+local ostime = os.time
+local formatter = string.formatter
+
+local trace_logins = true
+local report_logins = logs.reporter("sql","logins")
+
+local logins = sql.logins or { }
+sql.logins = logins
+
+logins.maxnoflogins = logins.maxnoflogins or 10
+logins.cooldowntime = logins.cooldowntime or 10 * 60
+logins.purgetime = logins.purgetime or 1 * 60 * 60
+logins.autopurge = true
+
+local function checkeddb(presets,datatable)
+ return sql.usedatabase(presets,datatable or presets.datatable or "logins")
+end
+
+logins.usedb = checkeddb
+
+local template = [[
+ CREATE TABLE IF NOT EXISTS %basename% (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `name` varchar(50) NOT NULL,
+ `time` int(11) DEFAULT '0',
+ `n` int(11) DEFAULT '0',
+ `state` int(11) DEFAULT '0',
+
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `id_unique_index` (`id`),
+ UNIQUE KEY `name_unique_key` (`name`)
+ ) DEFAULT CHARSET = utf8 ;
+]]
+
+local sqlite_template = [[
+ CREATE TABLE IF NOT EXISTS %basename% (
+ `id` INTEGER NOT NULL AUTO_INCREMENT,
+ `name` TEXT NOT NULL,
+ `time` INTEGER DEFAULT '0',
+ `n` INTEGER DEFAULT '0',
+ `state` INTEGER DEFAULT '0'
+ ) ;
+]]
+
+function logins.createdb(presets,datatable)
+
+ local db = checkeddb(presets,datatable)
+
+ local data, keys = db.execute {
+ template = db.usedmethod == "sqlite" and sqlite_template or template,
+ variables = {
+ basename = db.basename,
+ },
+ }
+
+ report_logins("datatable %a created in %a",db.name,db.base)
+
+ return db
+
+end
+
+local template =[[
+ DROP TABLE IF EXISTS %basename% ;
+]]
+
+function logins.deletedb(presets,datatable)
+
+ local db = checkeddb(presets,datatable)
+
+ local data, keys = db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ },
+ }
+
+ report_logins("datatable %a removed in %a",db.name,db.base)
+
+end
+
+local states = {
+ [0] = "unset",
+ [1] = "known",
+ [2] = "unknown",
+}
+
+local converter_fetch, fields_fetch = sqlmakeconverter {
+ { name = "id", type = "number" },
+ { name = "name", type = "string" },
+ { name = "time", type = "number" },
+ { name = "n", type = "number" },
+ { name = "state", type = "number" }, -- faster than mapping
+}
+
+local template_fetch = format( [[
+ SELECT
+ %s
+ FROM
+ `logins`
+ WHERE
+ `name` = '%%[name]%%'
+]], fields_fetch )
+
+local template_insert = [[
+ INSERT INTO `logins`
+ ( `name`, `state`, `time`, `n`)
+ VALUES
+ ('%[name]%', %state%, %time%, %n%)
+]]
+
+local template_update = [[
+ UPDATE
+ `logins`
+ SET
+ `state` = %state%,
+ `time` = %time%,
+ `n` = %n%
+ WHERE
+ `name` = '%[name]%'
+]]
+
+local template_delete = [[
+ DELETE FROM
+ `logins`
+ WHERE
+ `name` = '%[name]%'
+]]
+
+local template_purge = [[
+ DELETE FROM
+ `logins`
+ WHERE
+ `time` < '%time%'
+]]
+
+-- todo: auto cleanup (when new attempt)
+
+local cache = { } setmetatable(cache, { __mode = 'v' })
+
+-- local function usercreate(presets)
+-- sqlexecute {
+-- template = template_create,
+-- presets = presets,
+-- }
+-- end
+
+function logins.userunknown(db,name)
+ local d = {
+ name = name,
+ state = 2,
+ time = ostime(),
+ n = 0,
+ }
+ db.execute {
+ template = template_update,
+ variables = d,
+ }
+ cache[name] = d
+ report_logins("user %a is registered as unknown",name)
+end
+
+function logins.userknown(db,name)
+ local d = {
+ name = name,
+ state = 1,
+ time = ostime(),
+ n = 0,
+ }
+ db.execute {
+ template = template_update,
+ variables = d,
+ }
+ cache[name] = d
+ report_logins("user %a is registered as known",name)
+end
+
+function logins.userreset(db,name)
+ db.execute {
+ template = template_delete,
+ }
+ cache[name] = nil
+ report_logins("user %a is reset",name)
+end
+
+local function userpurge(db,delay)
+ db.execute {
+ template = template_purge,
+ variables = {
+ time = ostime() - (delay or logins.purgetime),
+ }
+ }
+ cache = { }
+ report_logins("users are purged")
+end
+
+logins.userpurge = userpurge
+
+local function verdict(okay,...)
+ if not trace_logins then
+ -- no tracing
+ elseif okay then
+ report_logins("%s, granted",formatter(...))
+ else
+ report_logins("%s, blocked",formatter(...))
+ end
+ return okay
+end
+
+local lasttime = 0
+
+function logins.userpermitted(db,name)
+ local currenttime = ostime()
+ if logins.autopurge and (lasttime == 0 or (currenttime - lasttime > logins.purgetime)) then
+ report_logins("automatic purge triggered")
+ userpurge(db)
+ lasttime = currenttime
+ end
+ local data = cache[name]
+ if data then
+ report_logins("user %a is cached",name)
+ else
+ report_logins("user %a is fetched",name)
+ data = db.execute {
+ template = template_fetch,
+ converter = converter_fetch,
+ variables = {
+ name = name,
+ }
+ }
+ end
+ if not data or not data.name then
+ local d = {
+ name = name,
+ state = 0,
+ time = currenttime,
+ n = 1,
+ }
+ db.execute {
+ template = template_insert,
+ variables = d,
+ }
+ cache[name] = d
+ return verdict(true,"creating new entry for %a",name)
+ end
+ cache[name] = data[1]
+ local state = data.state
+ if state == 2 then -- unknown
+ return verdict(false,"user %a has state %a",name,states[state])
+ end
+ local n = data.n
+ local m = logins.maxnoflogins
+ if n > m then
+ local deltatime = currenttime - data.time
+ local cooldowntime = logins.cooldowntime
+ if deltatime < cooldowntime then
+ return verdict(false,"user %a is blocked for %s seconds out of %s",name,cooldowntime-deltatime,cooldowntime)
+ else
+ n = 0
+ end
+ end
+ if n == 0 then
+ local d = {
+ name = name,
+ state = 0,
+ time = currenttime,
+ n = 1,
+ }
+ db.execute {
+ template = template_update,
+ variables = d,
+ }
+ cache[name] = d
+ return verdict(true,"user %a gets a first chance",name)
+ else
+ local d = {
+ name = name,
+ state = 0,
+ time = currenttime,
+ n = n + 1,
+ }
+ db.execute {
+ template = template_update,
+ variables = d,
+ }
+ cache[name] = d
+ return verdict(true,"user %a gets a new chance, %s attempts out of %s done",name,n,m)
+ end
+end
+
+return logins