orgs.lua 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. -- ===================================================================
  2. -- /orgs.lua
  3. -- Org-related functions for Modular Politics
  4. -- Called by modpol/modpol/api.lua
  5. -- ===================================================================
  6. -- preserve records -- if true, will store org ledgers when they are closed, for later reference.
  7. local preserve_records = true
  8. -- policies to be assigned to every org can go here
  9. modpol.default_org_policies = modpol.default_org_policies or {}
  10. -- initiate the table of orgs, if it doesnt already exist from storage!
  11. modpol.orgs = modpol.orgs or {}
  12. if preserve_records then
  13. modpol.old_ledgers = modpol.old_ledgers or {} -- a backup of all legders of all closed orgs.
  14. end
  15. -- this counter will increment every time a new org is created, and the current value will be used as the org id
  16. -- we set it to 1 for init, and then run through all existing orgs and take only the greatest... which we then add 1 to.
  17. -- this ensures a unique id for each org.
  18. modpol.id_counter = 1
  19. if #modpol.orgs > 0 then
  20. for id,org in pairs(modpol.orgs) do
  21. if id > modpol.id_counter then
  22. modpol.id_counter = id
  23. end
  24. end
  25. for id,ledg in pairs(modpol.old_ledgers) do
  26. if id > modpol.id_counter then
  27. modpol.id_counter = id
  28. end
  29. end
  30. modpol.id_counter = modpol.id_counter +1
  31. end
  32. --define the template of a ledger entry
  33. modpol.ledger_entry_temp = function()
  34. return {
  35. timestamp = '',
  36. entrytype = nil,
  37. action_msg = '',
  38. org_name = nil,
  39. org_id = nil,
  40. }
  41. end
  42. -- define the basic template of an org.
  43. modpol.org_temp = function()
  44. return {
  45. id = nil,
  46. name = nil,
  47. policies = {},
  48. members = {},
  49. ledger = {},
  50. parent = nil,
  51. children = {},
  52. properties = {},
  53. old_names = {},
  54. }
  55. end
  56. -- ===================================================================
  57. -- Function: modpol.create_ledger_entry
  58. -- Params: strings: action_msg, type, number: org_id
  59. -- Outputs:
  60. -- returns a ledger entry table, with a timestamp
  61. -- This function accepts a message and an optional org_id and type specification
  62. -- and returns a valid ledger entry table
  63. modpol.create_ledger_entry = function(action_msg, org_id, entry_type)
  64. local entry = modpol.ledger_entry_temp()
  65. if action_msg and type(action_msg) == 'string' then
  66. entry.action_msg = action_msg
  67. end
  68. if org_id and type(org_id) == 'number' and modpol.orgs [org_id] then
  69. entry.org_id = org_id
  70. if modpol.orgs[org_id].name then
  71. entry.org_name = modpol.orgs[org_id].name
  72. end
  73. end
  74. if entry_type and type(entry_type) == 'string' then
  75. entry.entrytype = entry_type
  76. end
  77. entry.timestamp = os.time()
  78. return entry
  79. end
  80. -- ===================================================================
  81. -- Function: modpol.record
  82. -- Params: strings msg, type, number org_id
  83. -- Outputs:
  84. -- "msg" specifies an event and/or status message.
  85. -- "org" specifies an "org" id.
  86. -- "type" -- optional type of legder entry. Could be e.g. 'election_result', 'add_member', etc
  87. -- This function adds the message to a global ledger and, if "org"
  88. -- specifies a valid "org", to an "org"-specific ledger. Both the mem-
  89. -- ory-resident and on-disk copies of the data structures used are up-
  90. -- dated.
  91. modpol.record = function (org_id, msg, type)
  92. local entry = modpol.create_ledger_entry(msg, org_id, type)
  93. -- Record in global ledger
  94. table.insert (modpol.ledger, entry)
  95. -- Record in "org"-specific ledger
  96. if modpol.orgs [org_id] ~= nil then
  97. local org_ledger = modpol.orgs [org_id]["ledger"]
  98. if org_ledger == nil then
  99. modpol.orgs [org_id]["ledger"] = { entry }
  100. print("Warning: Org ".. org_id .. " did not have a ledger. This is an error.")
  101. else
  102. table.insert (org_ledger, entry)
  103. modpol.orgs [org_id]["ledger"] = org_ledger
  104. end
  105. end
  106. modpol.store_data() -- Copy data to disk
  107. end
  108. -- ===================================================================
  109. -- Function: modpol.get_org_id_by_name(org_name)
  110. -- Params: org_name
  111. -- Output: an org id or nil
  112. --
  113. modpol.get_org_id_by_name = function(org_name)
  114. local id = nil
  115. for org_id,org in pairs(modpol.orgs) do
  116. if org.name == org_name then
  117. id = org_id
  118. end
  119. end
  120. return id
  121. end
  122. -- ===================================================================
  123. -- Function: modpol.add_org
  124. -- Params: string name, table members, string parent, table properties
  125. -- Output:
  126. --
  127. -- This function creates an "org". It returns a boolean success indicator and
  128. -- either an error message starting with "Error:" or a success message.
  129. --
  130. -- The string parameter specifies the "org" name.
  131. --
  132. -- The members table should be an integer-indexed array of member
  133. -- names.
  134. -- parent should be nil or a valid existing org name. If nil, defaults to
  135. -- 'instance'
  136. -- properties defaults to an empty table. Use it to initialize the org
  137. -- with arbitrary properties
  138. modpol.add_org = function(org_name, members, parent_id, properties)
  139. local str
  140. if modpol.ocutil.str_empty (org_name) then
  141. return false,"Error: Org needs a name"
  142. end
  143. if parent_id and not modpol.orgs[parent_id] then
  144. return false,"Error: Parent_id must be a valid existing org id"
  145. end
  146. if properties and not type(properties) == 'table' then
  147. return false,"Error: Properties must be a table"
  148. end
  149. modpol.ocutil.log ("Attempting to add new org " .. org_name)
  150. if modpol.get_org_id_by_name(org_name) ~= nil then
  151. str = "Error: Org " .. org_name .. " already exists"
  152. modpol.ocutil.log (str)
  153. return false,str
  154. end
  155. -- assign a new id to the new org
  156. local org_id = modpol.id_counter
  157. modpol.id_counter = modpol.id_counter + 1
  158. -- the instance will have a parent of nil if it is the instance.
  159. -- otherwise, it will have a parent of instance's id if not otherwise defined
  160. local parent_org_id = nil
  161. local instance_id = modpol.get_org_id_by_name('instance')
  162. if parent_id and instance_id and not(instance_id == org_id) then
  163. parent_org_id = parent_id
  164. end
  165. if not parent_id and instance_id and not ( instance_id == org_id ) then
  166. parent_org_id = modpol.get_org_id_by_name ( 'instance' )
  167. end
  168. -- actually add the org
  169. local org = modpol.org_temp()
  170. local default_policies = modpol.default_org_policies or {}
  171. org.id = org_id
  172. org.name = org_name -- do not change this without the proper api function. This is only a reference, not the only place this is recorded.
  173. org.members = members
  174. org.parent = parent_org_id
  175. org.properties = properites or org.properties
  176. org.policies = default_policies
  177. org.children = {}
  178. modpol.orgs [org_id] = org
  179. --record the child org in the parent's children table
  180. if parent_org_id then
  181. if modpol.orgs[parent_org_id].children then
  182. table.insert(modpol.orgs[parent_org_id].children, org_id)
  183. else
  184. modpol.orgs[parent_org_id].children = {org_id}
  185. end
  186. end
  187. local parent_id_str = "none"
  188. if parent_org_id then
  189. parent_id_str = tostring(parent_org_id)
  190. end
  191. local msg = "New org: " .. org_name .. " ["..org_id.."], parent = ".. parent_id_str ..", members = "..
  192. " (" .. table.concat (members, ", ") .. ")"
  193. modpol.record (org_id, msg, 'org_init')
  194. return true, msg
  195. end
  196. -- ===================================================================
  197. -- Function: modpol.remove_org
  198. -- Params: number id, opt string reason
  199. -- Output:
  200. --
  201. -- This function removes an "org". It returns a boolean
  202. -- success indicator and either an error message
  203. -- starting with "Error:" or a success message. It also recursively
  204. -- removes all child orgs, with 'remove parent org' as reason. If
  205. -- preserve_records is enabled, it logs the removal in the org ledger,
  206. -- and stores the ledger in modpol.old_ledgers
  207. -- The number parameter specifies the "org" id.
  208. --
  209. -- The reason is an optional string to additionally include in the
  210. -- log as reason for removal
  211. modpol.remove_org = function (org_id, reason)
  212. local str
  213. if not reason then reason = '' end
  214. if not type(org_id) == 'number' or not modpol.orgs[org_id] then
  215. str = "Error: Specify an existing org id to remove"
  216. modpol.ocutil.log (str)
  217. return false,str
  218. end
  219. local org_name = modpol.orgs[org_id]
  220. if org_name == 'instance' then
  221. return false,"Error: You cannot remove instance"
  222. end
  223. --log msg
  224. local msg = 'Removing org '..org_name ..' ['..org_id..'], reason: '.. reason
  225. modpol.ocutil.log (msg)
  226. -- actually remove the org
  227. -- TODO: if callbacks are implemented, do so here --
  228. if preserve_records then
  229. modpol.old_ledgers[org_id] = modpol.orgs[org_id].ledger
  230. end
  231. -- also remove all children
  232. if modpol.orgs[org_id].children and #modpol.orgs[org_id].children > 0 then
  233. for _,child_name in pairs(modpol.orgs[org_id].children) do
  234. --this is recursion. If it gets to be a problem, we will need a separate function
  235. modpol.remove_org(child_name, 'remove parent org')
  236. end
  237. end
  238. --also remove references to this org in parent orgs
  239. if modpol.orgs[org_id].parent then
  240. local parent_id = modpol.orgs[org_id].parent
  241. modpol.orgs[parent_id].children[org_id] = nil
  242. end
  243. modpol.orgs[org_id] = nil
  244. modpol.record (org_id, msg, 'org_remove')
  245. return true,msg
  246. end
  247. -- ===================================================================
  248. -- Function: modpol.list_orgs
  249. -- Params: None
  250. -- Output:
  251. -- This function returns a text-format list of "orgs". The list shows
  252. -- one "org" per line in the following format:
  253. -- org_name (member, member, ...)
  254. -- If there are no "orgs", the output is an empty string.
  255. modpol.list_orgs = function()
  256. local outbuf = ""
  257. local str
  258. for org_id, org_data in pairs (modpol.orgs) do
  259. -- Process next "org"
  260. -- Build string version of member list
  261. local memcat = org_data ["members"]
  262. local org_name = org_data ["name"].."["..org_id.."]"
  263. if modpol.ocutil.str_empty (memcat) then
  264. memcat = "(empty)"
  265. else
  266. memcat = "(" .. table.concat (memcat, ", ") .. ")"
  267. end
  268. -- Build output string
  269. outbuf = outbuf .. org_name .. " " .. memcat .. "\n"
  270. end
  271. return outbuf
  272. end
  273. -- ===================================================================
  274. -- Function: modpol.reset_orgs
  275. -- Params: None
  276. -- Removes all orgs and recreates blank org "instance" with all
  277. -- current users
  278. -- returns confirmation message
  279. modpol.reset_orgs = function()
  280. local users = modpol.list_users()
  281. --store the ledgers in modpol.old_ledgers, and note that each org was closed in each record.
  282. if preserve_records then
  283. for id, org in pairs(modpol.orgs) do
  284. local old_ledger = org.ledger
  285. local entry = modpol.create_ledger_entry('Removing org '.. id, id, 'org_purge')
  286. if old_ledger == nil then
  287. old_ledger = { entry }
  288. else
  289. table.insert(old_ledger, entry)
  290. end
  291. modpol.old_ledgers[id] = old_ledger
  292. end
  293. end
  294. modpol.orgs = {}
  295. local message = "Orgs purged"
  296. modpol.record(nil, message, 'org_purge')
  297. modpol.add_org("instance", users)
  298. return message
  299. end
  300. -- ===================================================================
  301. -- Function: modpol.add_member
  302. -- Params: org_id (number), member (string)
  303. -- Output:
  304. -- Adds the specified member to the specified org
  305. -- Returns a boolean success indicator and
  306. -- either a confirmation or error message
  307. -- TODO, check that member is a user, if the org_name is not instance
  308. modpol.add_member = function(org_id, member)
  309. if (modpol.orgs[org_id] == nil) then
  310. return false,"Error: No such org"
  311. elseif type(member) ~= 'string' then
  312. return false,"Error: member is wrong type"
  313. else
  314. local org_name = modpol.orgs [org_id].name
  315. if modpol.is_member(org_id,member) then
  316. return false,"Error: " .. member .. " is already a member of " .. org_name
  317. else
  318. table.insert(modpol.orgs[org_id]["members"], member)
  319. local message = member .. " added to org " .. org_name .. " ["..org_id.."]"
  320. modpol.record(org_id, message, 'add_member')
  321. return true,message
  322. end
  323. end
  324. end
  325. -- ===================================================================
  326. -- Function: modpol.is_member
  327. -- Params: org (number), member (string)
  328. -- Output: Boolean depending on membership or nil/error message
  329. modpol.is_member = function(org, member)
  330. if (modpol.orgs[org] == nil) then
  331. return nil, "Error: No such org"
  332. else
  333. local member_table = modpol.orgs[org]["members"]
  334. if member_table then
  335. for index, value in pairs(member_table) do
  336. if value == member then
  337. -- match found
  338. return true
  339. end
  340. end
  341. -- no match found
  342. return false
  343. end
  344. return false
  345. end
  346. end
  347. -- ===================================================================
  348. -- Function: modpol.remove_member
  349. -- Params: org (number), member (string)
  350. -- Output:
  351. -- Removes the specified member from the specified org
  352. -- Returns confirmation or error message
  353. function modpol.remove_member(org, member)
  354. local message = "Error: No such org"
  355. if (modpol.orgs[org] == nil) then
  356. return false, message
  357. else
  358. local member_table = modpol.orgs[org]["members"]
  359. if member_table then
  360. for index, value in pairs(member_table) do
  361. if value == member then
  362. -- match found, set to nil
  363. modpol.orgs[org]["members"][value] = nil
  364. -- TODO: add callbacks here, for such as revoking privs, etc --
  365. message = member .. " removed from org " .. org
  366. modpol.record(org, message, 'remove_member')
  367. return true, message
  368. end
  369. end
  370. end
  371. -- no match found (or org has no members)
  372. message = member .. " not found in org " .. org
  373. return false, message
  374. end
  375. end
  376. -- ===================================================================
  377. -- TKTK
  378. -- rename_org(old_name, new_name)
  379. -- Renames an org
  380. -- ===================================================================
  381. -- POLICIES
  382. -- Each policy is a table with a string for a key. Contents:
  383. -- KEY - name of the policy (string)
  384. -- [member orgs]: external orgs allowed to call policy
  385. -- [function]: the function that initiates the policy
  386. -- ===================================================================
  387. -- ===================================================================
  388. -- modpol.add_policy
  389. -- Adds a policy to an org's [org].policies table
  390. -- Params: org_name (string),
  391. -- policy_name (string),
  392. -- policy_table (table)
  393. -- Output: true if successful, nil if error
  394. -- TKTK NEEDS TO BE REWRITTEN
  395. modpol.add_policy = function(org_name, policy_name, policy_table)
  396. -- first, basic checks
  397. local message = "Error: No such org"
  398. if (modpol.orgs[org_name] == nil) then
  399. return nil, message
  400. elseif (modpol[target_function] == nil) then
  401. message = "Error: No such target function"
  402. return nil, message
  403. elseif (modpol[policy_function]) then
  404. message = "Error: No such policy function"
  405. return nil, message
  406. else
  407. -- okay, proceed
  408. modpol.orgs[org_name].policies[target_function] = policy_function
  409. message = "In org " .. org_name .. ", policy for " .. target_function
  410. .. " set to " .. policy_function
  411. record(org_name, message)
  412. return true, message
  413. end
  414. end
  415. -- ===================================================================
  416. -- End of file.