diff options
author | Hans Hagen <pragma@wxs.nl> | 2018-03-15 16:04:31 +0100 |
---|---|---|
committer | Context Git Mirror Bot <phg42.2a@gmail.com> | 2018-03-15 16:04:31 +0100 |
commit | a4e07f30e880ab27c2918f81f136e257475b7729 (patch) | |
tree | 02db002d3001a49777a049f9a98fdc872a5e1ad1 /tex/context/base/mkiv/util-sql-logins.lua | |
parent | cbc37c39432e0ebe38e0922fc6d14c2955ab3ba2 (diff) | |
download | context-a4e07f30e880ab27c2918f81f136e257475b7729.tar.gz |
2018-03-15 15:36:00
Diffstat (limited to 'tex/context/base/mkiv/util-sql-logins.lua')
-rw-r--r-- | tex/context/base/mkiv/util-sql-logins.lua | 305 |
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 |