userFunc.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. package agi
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "log"
  6. "net/http"
  7. "os"
  8. "path/filepath"
  9. "github.com/robertkrimen/otto"
  10. "imuslab.com/arozos/mod/agi/static"
  11. "imuslab.com/arozos/mod/filesystem"
  12. "imuslab.com/arozos/mod/filesystem/arozfs"
  13. user "imuslab.com/arozos/mod/user"
  14. )
  15. // Inject user based functions into the virtual machine
  16. // Note that the fsh might be nil and scriptPath must be real path of script being executed
  17. // **Use local file system check if fsh == nil**
  18. func (g *Gateway) injectUserFunctions(vm *otto.Otto, fsh *filesystem.FileSystemHandler, scriptPath string, scriptScope string, u *user.User, w http.ResponseWriter, r *http.Request) {
  19. username := u.Username
  20. vm.Set("USERNAME", username)
  21. vm.Set("USERICON", u.GetUserIcon())
  22. vm.Set("USERQUOTA_TOTAL", u.StorageQuota.TotalStorageQuota)
  23. vm.Set("USERQUOTA_USED", u.StorageQuota.UsedStorageQuota)
  24. vm.Set("USER_VROOTS", u.GetAllAccessibleFileSystemHandler())
  25. vm.Set("USER_MODULES", u.GetUserAccessibleModules())
  26. //File system and path related
  27. vm.Set("decodeVirtualPath", func(call otto.FunctionCall) otto.Value {
  28. log.Println("Call to deprecated function decodeVirtualPath")
  29. return otto.FalseValue()
  30. })
  31. vm.Set("decodeAbsoluteVirtualPath", func(call otto.FunctionCall) otto.Value {
  32. log.Println("Call to deprecated function decodeAbsoluteVirtualPath")
  33. return otto.FalseValue()
  34. })
  35. vm.Set("encodeRealPath", func(call otto.FunctionCall) otto.Value {
  36. log.Println("Call to deprecated function encodeRealPath")
  37. return otto.FalseValue()
  38. })
  39. //Check if a given virtual path is readonly
  40. vm.Set("pathCanWrite", func(call otto.FunctionCall) otto.Value {
  41. vpath, _ := call.Argument(0).ToString()
  42. if u.CanWrite(vpath) {
  43. return otto.TrueValue()
  44. } else {
  45. return otto.FalseValue()
  46. }
  47. })
  48. //Permission related
  49. vm.Set("getUserPermissionGroup", func(call otto.FunctionCall) otto.Value {
  50. groupinfo := u.GetUserPermissionGroup()
  51. jsonString, _ := json.Marshal(groupinfo)
  52. reply, _ := vm.ToValue(string(jsonString))
  53. return reply
  54. })
  55. vm.Set("userIsAdmin", func(call otto.FunctionCall) otto.Value {
  56. reply, _ := vm.ToValue(u.IsAdmin())
  57. return reply
  58. })
  59. //User Account Related
  60. /*
  61. userExists(username);
  62. */
  63. vm.Set("userExists", func(call otto.FunctionCall) otto.Value {
  64. if u.IsAdmin() {
  65. //Get username from function paramter
  66. username, err := call.Argument(0).ToString()
  67. if err != nil || username == "undefined" {
  68. g.RaiseError(errors.New("username is undefined"))
  69. reply, _ := vm.ToValue(nil)
  70. return reply
  71. }
  72. //Check if user exists
  73. userExists := u.Parent().GetAuthAgent().UserExists(username)
  74. if userExists {
  75. return otto.TrueValue()
  76. } else {
  77. return otto.FalseValue()
  78. }
  79. } else {
  80. g.RaiseError(errors.New("Permission Denied: userExists require admin permission"))
  81. return otto.FalseValue()
  82. }
  83. })
  84. /*
  85. createUser(username, password, defaultGroup);
  86. */
  87. vm.Set("createUser", func(call otto.FunctionCall) otto.Value {
  88. if u.IsAdmin() {
  89. //Ok. Create user base on given information
  90. username, err := call.Argument(0).ToString()
  91. if err != nil || username == "undefined" {
  92. g.RaiseError(errors.New("username is undefined"))
  93. reply, _ := vm.ToValue(false)
  94. return reply
  95. }
  96. password, err := call.Argument(1).ToString()
  97. if err != nil || password == "undefined" {
  98. g.RaiseError(errors.New("password is undefined"))
  99. reply, _ := vm.ToValue(false)
  100. return reply
  101. }
  102. defaultGroup, err := call.Argument(2).ToString()
  103. if err != nil || defaultGroup == "undefined" {
  104. g.RaiseError(errors.New("defaultGroup is undefined"))
  105. reply, _ := vm.ToValue(false)
  106. return reply
  107. }
  108. //Check if username already used
  109. userExists := u.Parent().GetAuthAgent().UserExists(username)
  110. if userExists {
  111. g.RaiseError(errors.New("Username already exists"))
  112. reply, _ := vm.ToValue(false)
  113. return reply
  114. }
  115. //Check if the given permission group exists
  116. groupExists := u.Parent().GetPermissionHandler().GroupExists(defaultGroup)
  117. if !groupExists {
  118. g.RaiseError(errors.New(defaultGroup + " user-group not exists"))
  119. reply, _ := vm.ToValue(false)
  120. return reply
  121. }
  122. //Create the user
  123. err = u.Parent().GetAuthAgent().CreateUserAccount(username, password, []string{defaultGroup})
  124. if err != nil {
  125. g.RaiseError(errors.New("User creation failed: " + err.Error()))
  126. reply, _ := vm.ToValue(false)
  127. return reply
  128. }
  129. return otto.TrueValue()
  130. } else {
  131. g.RaiseError(errors.New("Permission Denied: createUser require admin permission"))
  132. return otto.FalseValue()
  133. }
  134. })
  135. vm.Set("editUser", func(call otto.FunctionCall) otto.Value {
  136. if u.IsAdmin() {
  137. } else {
  138. g.RaiseError(errors.New("Permission Denied: editUser require admin permission"))
  139. return otto.FalseValue()
  140. }
  141. //libname, err := call.Argument(0).ToString()
  142. return otto.FalseValue()
  143. })
  144. /*
  145. removeUser(username)
  146. */
  147. vm.Set("removeUser", func(call otto.FunctionCall) otto.Value {
  148. if u.IsAdmin() {
  149. //Get username from function paramters
  150. username, err := call.Argument(0).ToString()
  151. if err != nil || username == "undefined" {
  152. g.RaiseError(errors.New("username is undefined"))
  153. reply, _ := vm.ToValue(false)
  154. return reply
  155. }
  156. //Check if the user exists
  157. userExists := u.Parent().GetAuthAgent().UserExists(username)
  158. if !userExists {
  159. g.RaiseError(errors.New(username + " not exists"))
  160. reply, _ := vm.ToValue(false)
  161. return reply
  162. }
  163. //User exists. Remove it from the system
  164. err = u.Parent().GetAuthAgent().UnregisterUser(username)
  165. if err != nil {
  166. g.RaiseError(errors.New("User removal failed: " + err.Error()))
  167. reply, _ := vm.ToValue(false)
  168. return reply
  169. }
  170. return otto.TrueValue()
  171. } else {
  172. g.RaiseError(errors.New("Permission Denied: removeUser require admin permission"))
  173. return otto.FalseValue()
  174. }
  175. })
  176. //Allow real time library includsion into the virtual machine
  177. vm.Set("requirelib", func(call otto.FunctionCall) otto.Value {
  178. libname, err := call.Argument(0).ToString()
  179. if err != nil {
  180. g.RaiseError(err)
  181. reply, _ := vm.ToValue(nil)
  182. return reply
  183. }
  184. //Handle special case on high level libraries
  185. if libname == "websocket" && w != nil && r != nil {
  186. g.injectWebSocketFunctions(vm, u, w, r)
  187. return otto.TrueValue()
  188. } else {
  189. //Check if the library name exists. If yes, run the initiation script on the vm
  190. if entryPoint, ok := g.LoadedAGILibrary[libname]; ok {
  191. entryPoint(&static.AgiLibInjectionPayload{
  192. VM: vm,
  193. User: u,
  194. ScriptFsh: fsh,
  195. ScriptPath: scriptPath,
  196. Writer: w,
  197. Request: r,
  198. })
  199. return otto.TrueValue()
  200. } else {
  201. //Lib not exists
  202. log.Println("Lib not found: " + libname)
  203. return otto.FalseValue()
  204. }
  205. }
  206. })
  207. //Execd (Execute & detach) run another script and detach the execution
  208. vm.Set("execd", func(call otto.FunctionCall) otto.Value {
  209. //Check if the pkg is already registered
  210. scriptName, err := call.Argument(0).ToString()
  211. if err != nil {
  212. g.RaiseError(err)
  213. return otto.FalseValue()
  214. }
  215. //Carry the payload to the forked process if there are any
  216. payload, _ := call.Argument(1).ToString()
  217. //Check if the script file exists
  218. targetScriptPath := arozfs.ToSlash(filepath.Join(filepath.Dir(scriptPath), scriptName))
  219. if fsh != nil {
  220. if !fsh.FileSystemAbstraction.FileExists(targetScriptPath) {
  221. g.RaiseError(errors.New("[AGI] Target path not exists!"))
  222. return otto.FalseValue()
  223. }
  224. } else {
  225. if !filesystem.FileExists(targetScriptPath) {
  226. g.RaiseError(errors.New("[AGI] Target path not exists!"))
  227. return otto.FalseValue()
  228. }
  229. }
  230. //Run the script
  231. scriptContent, _ := os.ReadFile(targetScriptPath)
  232. go func() {
  233. //Create a new VM to execute the script (also for isolation)
  234. vm := otto.New()
  235. //Inject standard libs into the vm
  236. g.injectStandardLibs(vm, scriptPath, scriptScope)
  237. g.injectUserFunctions(vm, fsh, scriptPath, scriptScope, u, w, r)
  238. vm.Set("PARENT_DETACHED", true)
  239. vm.Set("PARENT_PAYLOAD", payload)
  240. _, err = vm.Run(string(scriptContent))
  241. if err != nil {
  242. //Script execution failed
  243. log.Println("Script Execution Failed: ", err.Error())
  244. g.RaiseError(err)
  245. }
  246. }()
  247. return otto.TrueValue()
  248. })
  249. }