Advertisement
Guest User

Secure File System API for ComputerCraft 1.5

a guest
May 1st, 2017
217
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 13.48 KB | None | 0 0
  1. -- Secure File System (sfs) implements file system
  2. -- permissions. Each file and directory has an
  3. -- owner and four access flags: owner read, owner
  4. -- write, other read , and other write. The owner
  5. -- read and owner write flags control the file
  6. -- owners access to the file. The other read and
  7. -- other write flags control all other users access
  8. -- to the file. Each directory has a .sfs file
  9. -- which stores permission data for that
  10. -- directory's files and sub-directories. If
  11. -- a .sfs file is not found then access to that
  12. -- directory is not restricted. If the .sfs file
  13. -- is missing an entry for a file, that file will
  14. -- also not have access restricted.
  15. --
  16. -- Author: GhastTearz
  17. -- Writen in CC Version: 1.5
  18. -- LICENCE: Public Domain
  19. -------------------------------
  20.  
  21. -- local copy of original fs api
  22. local _fs = fs
  23.  
  24.  
  25. -- Returns the parent directory of path. The root
  26. -- directory's parent is itself ( / maps to / ). If
  27. -- path is relative or the path is invalid returns
  28. -- an empty string.
  29. --
  30. -- path is a path to a directory or file
  31. local function getParentDirectory(path)
  32.   if path == "/" or path == "\\" then
  33.     return "/"
  34.   end
  35.  
  36.   -- remove any tailing slashes
  37.   local lastChar = string.sub(path,#path,#path)
  38.   if lastChar == "/" or lastChar == "\\" then
  39.     path = string.sub(path,1,#path - 1)
  40.   end
  41.  
  42.   for i = #path, 1, -1 do
  43.     local char = string.sub(path,i,i)
  44.     if char == "/" or char == "\\" then
  45.       return string.sub(path,1,i)
  46.     end
  47.   end
  48.  
  49.   return ""
  50.  
  51. end
  52.  
  53.  
  54. -- returns the components of a path as a table.
  55. -- example: a path of "/a/b/c" returns a table of
  56. -- {"/","/a/","/a/b/","/a/b/c"}
  57. --
  58. -- path is an absolute path
  59. local function getPathComponents(path)
  60.   local components = {}
  61.   for i = 1, #path do
  62.     local char = string.sub(path,i,i)
  63.     if char == "/" or
  64.        char == "\\" or
  65.        i == #path then
  66.       table.insert(components,string.sub(path,1,i))
  67.     end
  68.   end
  69.   return components
  70. end
  71.  
  72.  
  73. -- Same as fs.getName(), execpt an input of "/"
  74. -- returns "/" instead of "root"
  75. local function getName(path)
  76.   if path == "/" or path == "\\" then
  77.     return "/"
  78.   end
  79.   return _fs.getName(path)
  80. end
  81.  
  82.  
  83. -- get the sfs file table. returns nil if
  84. -- not successful.
  85. --
  86. -- path is an absolute path to directory
  87. local function getFileTable(path)
  88.    -- get contents of sfs file
  89.   local sfsPath = getParentDirectory(path)..".sfs"
  90.   if not _fs.exists(sfsPath) then
  91.     return nil
  92.   end
  93.   local sfsFile = _fs.open(sfsPath,"r")
  94.   local contents = sfsFile.readAll()
  95.   sfsFile.close()
  96.  
  97.   -- try to parse contents into a table
  98.   local fileTable = textutils.unserialize(
  99.       contents )
  100.   if fileTable == nil then
  101.     return nil
  102.   end
  103.   return fileTable
  104. end
  105.  
  106.  
  107. -- attempts to get the permission table for then
  108. -- file at path. Returns nil if it cannot get
  109. -- the file's permission table
  110. --
  111. -- path is an absolute path to a direcory
  112. local function getPermTable(path)
  113.   local sfsTable = getFileTable(path)
  114.   if sfsTable == nil then
  115.     return nil    
  116.   end
  117.  
  118.   -- try to get the table for file at path
  119.   local permTable = sfsTable[getName(path)]
  120.   -- no entry for the file at path
  121.   if permTable == nil then
  122.     return nil
  123.   end
  124.  
  125.   -- Check that permTable has all required fields.
  126.   -- If any fields are missing return nil.
  127.   if permTable["owner"]      == nil or
  128.      permTable["ownerRead"]  == nil or
  129.      permTable["ownerWrite"] == nil or
  130.      permTable["otherRead"]  == nil or
  131.      permTable["otherWrite"] == nil then
  132.     return nil
  133.   end
  134.     return permTable
  135. end
  136.  
  137.  
  138. -- removes an entry from an sfs file
  139. --
  140. -- path is an absolute path of file
  141. local function removePermTable(path)
  142.   local sfsTable = getFileTable(path)
  143.   if sfsTable == nil then
  144.     return nil
  145.   end
  146.   sfsTable[getName(path)] = nil
  147.  
  148.   -- save to file
  149.   local file = _fs.open(getParentDirectory(path)..".sfs","w")
  150.   file.write(textutils.serialize(sfsTable))
  151.   file.close()
  152. end
  153.        
  154.    
  155. -- adds an enrty to a sfs file
  156. --
  157. -- user is a table with a "userName" field
  158. -- path is an absolute path of file
  159. -- permTable is a permission table for the file
  160. local function addPermTable(user, path, permTable)
  161.   local sfsTable = getFileTable(path)
  162.   if sfsTable == nil then
  163.     sfsTable = {
  164.       [".sfs"] =
  165.         {
  166.         ["owner"]      = "root",
  167.         ["ownerRead"]  = true,
  168.         ["ownerWrite"] = true,
  169.         ["otherRead"]  = false,
  170.         ["otherWrite"] = false,
  171.         },
  172.       }
  173.   end
  174.  
  175.   if permTable == nil then
  176.     permTable =
  177.       {
  178.       ["owner"]      = user.userName,
  179.       ["ownerRead"]  = true,
  180.       ["ownerWrite"] = true,
  181.       ["otherRead"]  = true,
  182.       ["otherWrite"] = true,
  183.       }
  184.   end
  185.   sfsTable[getName(path)] = permTable
  186.  
  187.   -- save to file
  188.   local file = _fs.open(getParentDirectory(path)..".sfs","w")
  189.   file.write(textutils.serialize(sfsTable))
  190.   file.close()
  191. end
  192.  
  193.  
  194. -- Checks if user has read permission to the  file
  195. -- at path. Returns false if the file does not exist.
  196. -- "root" user always can read.
  197. --
  198. -- user is a table with a "userName" field
  199. -- path is an absolute path to file
  200. local function hasRead(user, path)
  201.   if not _fs.exists(path) then
  202.     return false
  203.   end
  204.  
  205.   if user.userName == "root" then
  206.     return true
  207.   end
  208.  
  209.   permTable = getPermTable(path)
  210.   if permTable == nil then
  211.     return true
  212.   end
  213.   if user.userName == permTable.owner then
  214.     return permTable.ownerRead
  215.   else
  216.     return permTable.otherRead
  217.   end
  218. end
  219.  
  220.  
  221. -- Checks each directory from root all the way
  222. -- down path if user has read access to file.
  223. -- If user does not have read permission an
  224. -- error is thrown. Returns nothing.
  225. --
  226. -- user is a table with a "userName" field
  227. -- path is an absolute path to a file
  228. local function assertHasRead(user, path)
  229.   local pathComponents = getPathComponents(path)
  230.   for i = 1, #pathComponents do
  231.     local permTable = getPermTable(pathComponents[i])
  232.     if permTable ~= nil and
  233.        not hasRead(user,pathComponents[i]) then
  234.       error("Cannot Access "..pathComponents[i])
  235.     end
  236.   end
  237. end
  238.  
  239.  
  240. -- Checks if user has write permission to the file
  241. -- at path. If the file does not exist, checks
  242. -- the write permission value of parent directory.
  243. -- The user must have read permission to parent
  244. -- directory in order to change an existing file.
  245. -- This function returns nothing. "root" user
  246. -- always has write access.
  247. --
  248. -- user is a table with a "userName" field
  249. -- path is an absolute path to a file
  250. local function assertHasWrite(user, path)
  251.   if user.userName == "root" then
  252.     return    
  253.   end
  254.  
  255.   if _fs.exists(path) then
  256.     assertHasRead(user,getParentDirectory(path))
  257.     local permTable = getPermTable(path)
  258.     if permTable ~= nil then
  259.       if permTable.owner == user.userName then
  260.         if not permTable.ownerWrite then
  261.           error("Cannot write to "..path)
  262.         end
  263.       else
  264.         if not permTable.otherWrite then
  265.           error("Cannot write to "..path)
  266.         end
  267.       end
  268.     end
  269.   else
  270.     assertHasWrite(user,getParentDirectory(path))
  271.   end
  272. end
  273.  
  274.  
  275. -- creates a wrapper of the fs api using sfs
  276. --
  277. -- user is a table containing a "userName" field
  278. local function newFs(user)
  279.   local FS = {}
  280.   FS.list =
  281.     function(path)
  282.       assertHasRead(user,path)
  283.       return _fs.list(path)
  284.     end
  285.    
  286.   FS.exists =
  287.     function(path)
  288.       assertHasRead(user,getParentDirectory(path))
  289.       return _fs.exists(path)
  290.     end
  291.    
  292.   FS.isDir =
  293.     function(path)
  294.       assertHasRead(user,getParentDirectory(path))
  295.       return _fs.isDir(path)
  296.     end
  297.  
  298.   FS.isReadOnly =
  299.     function (path)
  300.       assertHasRead(user,getParentDirectory(path))
  301.       return _fs.isReadOnly(path) or
  302.              (not pcall(assertHasWrite,user,path))
  303.     end
  304.  
  305.   FS.getName = _fs.getName
  306.  
  307.   FS.getDrive =
  308.     function(path)
  309.       assertHasRead(user,getParentDirectory(path))
  310.       return _fs.getDrive(path)
  311.     end
  312.    
  313.   FS.getSize =
  314.     function(path)
  315.       assertHasRead(user,path)
  316.       return _fs.getSize(path)
  317.     end
  318.    
  319.   FS.getFreeSpace =
  320.     function(path)
  321.       assertHasRead(user,path)   print("getting parent of "..path)
  322.       return _fs.getFreeSpace(path)
  323.     end
  324.    
  325.   FS.makeDir =
  326.     function(path)
  327.       assertHasWrite(user,getParentDirectory(path))
  328.       addPermTable(user,path,{
  329.         ["owner"]      = user.userName,
  330.         ["ownerRead"]  = true,
  331.         ["ownerWrite"] = true,
  332.         ["otherRead"]  = true,
  333.         ["otherWrite"] = true,
  334.         })
  335.       return _fs.makeDir(path)
  336.     end
  337.    
  338.   -- user needs access to read source and to write
  339.   -- destination
  340.   FS.move =
  341.     function(fromPath, toPath)
  342.       assertHasRead(user,fromPath)
  343.       assertHasWrite(user,toPath)
  344.       addPermTable(user,toPath,getPermTable(fromPath))
  345.       removePermTable(fromPath)
  346.       return _fs.move(fromPath, toPath)
  347.     end
  348.    
  349.   -- user needs access to read source and to write
  350.   -- destination
  351.   FS.copy =
  352.     function(fromPath, toPath)
  353.       assertHasRead(user,fromPath)
  354.       assertHasWrite(user,toPath)
  355.       addPermTable(user,toPath,getPermTable(fromPath))
  356.       return _fs.copy(fromPath,toPath)
  357.     end
  358.  
  359.   FS.delete =
  360.     function(path)
  361.       assertHasWrite(user,path)
  362.       removePermTable(path)
  363.       return _fs.delete(path)
  364.     end
  365.    
  366.   -- like normal combine, execpt with leading slash
  367.   FS.combine =
  368.     function(pathA, pathB)
  369.       local result = _fs.combine(pathA,pathB)
  370.       local char = string.sub(result,1,1)
  371.       if char == "/" or char == "\\" then
  372.         return result
  373.       else
  374.         return "/"..result
  375.       end
  376.     end
  377.  
  378.   FS.open =
  379.     function(path, mode)
  380.       local char = string.sub(mode,1,1)
  381.       if char == "r" then
  382.         assertHasRead(user,path)
  383.         return _fs.open(path,mode)
  384.       elseif char == "w" or char == "a" then
  385.         assertHasWrite(user,path)
  386.         -- if path does not exist add perm table
  387.         if not _fs.exists(path) then
  388.           addPermTable(user,path,{
  389.             ["owner"]      = user.userName,
  390.             ["ownerRead"]  = true,
  391.             ["ownerWrite"] = true,
  392.             ["otherRead"]  = true,
  393.             ["otherWrite"] = true,
  394.             })
  395.         end
  396.         return _fs.open(path,mode)
  397.       end
  398.     end
  399.    
  400.   return FS
  401. end
  402.  
  403.  
  404. -- for the setter functions below
  405. local mainUser
  406.  
  407.  
  408. -- changes the fs api to use sfs. To prevent users
  409. -- from bypassing sfs, the computer will need to
  410. -- be rebooted to restore original fs api. start
  411. -- can only be called once, it removes itself.
  412. --
  413. -- user is a table with a "userName" field
  414. function start ( user )
  415.   -- Check if user has userName field
  416.   if user.userName == nil then
  417.     error("user does not contain userName field")
  418.   end
  419.  
  420.   mainUser = user
  421.  
  422.   _G.fs = newFs(user)
  423.   fs = newFs(user)
  424.  
  425.   --remove this function
  426.   _G.sfs.start = nil
  427.   sfs.start = nil
  428. end
  429.  
  430. -- sets the field of a sfs entry
  431. --
  432. -- path is an absolute path to directory
  433. -- field is field to be set
  434. -- value is the value field is set to
  435. local function setField(path,field,value)
  436.   local permTable = getPermTable(path)
  437.   if permTable == nil then
  438.     permTable = {
  439.       ["owner"]      = mainUser.userName,
  440.       ["ownerRead"]  = true,
  441.       ["ownerWrite"] = true,
  442.       ["otherRead"]  = true,
  443.       ["otherWrite"] = true,
  444.       }
  445.   end
  446.   permTable[field] = value
  447.   addPermTable(mainUser,path,permTable)
  448. end
  449.  
  450. -- changes owner of a file.
  451. -- Only "root" user can change the owner of a file
  452. --
  453. -- path is an absolute path to file
  454. -- newOwner is a string username of new owner
  455. function setOwner(path, newOwner)
  456.   if mainUser.userName ~= "root" then
  457.     error("Only root user may change owner")
  458.   end
  459.   setField(path,"owner", newOwner)
  460. end
  461.  
  462.  
  463. -- Sets the ownerRead flag. only the
  464. -- owner and root may change this value
  465. --
  466. -- path is absolute path to file
  467. -- flag is new boolean value for ownerRead flag
  468. function setOwnerRead(path, flag)
  469.   local permTable = getPermTable(path)
  470.   local owner = permTable.owner
  471.   if mainUser.userName == owner or
  472.      mainUser.userName == "root" then
  473.     setField(path,"ownerRead", flag)
  474.   else
  475.     error("Only owner may set ownerRead flag")
  476.   end
  477. end
  478.  
  479.  
  480. -- Sets the ownerWrite flag. only the
  481. -- owner and root may change this value
  482. --
  483. -- path is absolute path to file
  484. -- flag is new boolean value for ownerWrite flag
  485. function setOwnerWrite(path, flag)
  486.   local permTable = getPermTable(path)
  487.   local owner = permTable.owner
  488.   if mainUser.userName == owner or
  489.      mainUser.userName == "root" then
  490.     setField(path,"ownerWrite", flag)
  491.   else
  492.     error("Only owner may set ownerWrite flag")
  493.   end
  494. end
  495.  
  496.  
  497. -- Sets the otherRead flag. only the
  498. -- owner and root may change this value
  499. --
  500. -- path is absolute path to file
  501. -- flag is new boolean value for otherRead flag
  502. function setOtherRead(path, flag)
  503.   local permTable = getPermTable(path)
  504.   local owner = permTable.owner
  505.   if mainUser.userName == owner or
  506.      mainUser.userName == "root" then
  507.     setField(path,"otherRead", flag)
  508.   else
  509.     error("Only owner may set otherRead flag")
  510.   end
  511. end
  512.  
  513.  
  514. -- Sets the otherWrite flag. only the
  515. -- owner and root may change this value
  516. --
  517. -- path is absolute path to file
  518. -- flag is new boolean value for otherRead flag
  519. function setOtherWrite(path, flag)
  520.   local permTable = getPermTable(path)
  521.   local owner = permTable.owner
  522.   if mainUser.userName == owner or
  523.      mainUser.userName == "root" then
  524.       setField(path,"otherWrite",flag)
  525.   else
  526.     error("Only owner may set ownerRead flag")
  527.   end
  528. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement