orgs.lua 15 KB

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