1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
--[[
Copyright (c) 2012-2016 Kaarle Ritvanen
See LICENSE file for license details
--]]
local M = require('aconf.model')
local node = require('aconf.model.node')
local object = require('aconf.object')
local digest = require('crypto').digest
local conf_addr = '/json'..require('posix').getcwd()..'/config'
local Role = M.new()
Role.permissions = M.Set{type=M.Reference{scope='../../../permissions'}}
local function hash_password(algorithm, salt, password)
return algorithm..'$'..salt..'$'..digest(algorithm, salt..password)
end
local hash_pattern = '^(%w+)%$(%w+)%$%x+$'
local Password = object.class(M.String)
function Password:init() object.super(self, Password):init{detail=true} end
function Password:normalize(context, value)
if value:find(hash_pattern) then return value end
local salt = ''
for i = 1,12 do
local c = math.random(48, 109)
if c > 57 then c = c + 7 end
if c > 90 then c = c + 6 end
salt = salt..string.char(c)
end
return hash_password('sha256', salt, value)
end
local User = M.new()
User.password = Password
User.real_name = M.String
User.superuser = M.Boolean{default=false}
User.roles = M.Set{type=M.Reference{scope='../../../roles'}}
function User:check_password(password)
if not self.password then return false end
local _, _, algorithm, salt = self.password:find(hash_pattern)
if not salt then return false end
return hash_password(algorithm, salt, password) == self.password
end
function User:check_permission(permission)
local mt = getmetatable(self)
if not mt.permissions then
mt.permissions = {}
for _, p in pairs(self:fetch('../../permissions')) do
mt.permissions[p] = false
end
for _, role in node.pairs(self.roles, true) do
for _, p in node.pairs(role.permissions, true) do
mt.permissions[p] = true
end
end
end
return mt.permissions[permission]
end
local Record = M.new()
Record.user = M.String{required=true, editable=false}
Record.action = M.String{required=true, editable=false}
Record.path = M.String{editable=false}
Record.data = M.String{editable=false}
Record.timestamp = M.time.Timestamp{editable=false}
local Aaa = M.new()
Aaa.users = M.Collection{type=User}
Aaa.roles = M.Collection{type=Role}
Aaa.permissions = M.Set{
type=M.String, addr='/volatile/aaa/permissions', editable=false
}
Aaa.audit_trail = M.List{
type=Record,
addr=conf_addr..'/audit.json',
editable=false,
ui_name='Audit trail',
ui_member='Record'
}
Aaa.action_log = M.List{type=Record, addr=node.null_addr, visible=false}
function Aaa:validate()
local time = os.time()
for _, action in pairs(self.action_log) do
action.timestamp = time
node.insert(self.audit_trail, action)
end
end
M.register('aaa', Aaa, {addr=conf_addr..'/auth.json', ui_name='AAA'})
M.permission.defaults('/aaa')
|