main.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. package main
  2. import (
  3. "encoding/json"
  4. "io/ioutil"
  5. "log"
  6. "net/http"
  7. "os"
  8. "os/exec"
  9. "os/signal"
  10. "path/filepath"
  11. "runtime"
  12. "strings"
  13. "syscall"
  14. "git.arozos.com/ArSamba/apt"
  15. "git.arozos.com/ArSamba/aroz"
  16. )
  17. var (
  18. handler *aroz.ArozHandler
  19. )
  20. func SetupCloseHandler() {
  21. c := make(chan os.Signal, 2)
  22. signal.Notify(c, os.Interrupt, syscall.SIGTERM)
  23. go func() {
  24. <-c
  25. log.Println("\r- Shutting down ArSamba module.")
  26. os.Exit(0)
  27. }()
  28. }
  29. func main() {
  30. //If you have other flags, please add them here
  31. //Start the aoModule pipeline (which will parse the flags as well). Pass in the module launch information
  32. handler = aroz.HandleFlagParse(aroz.ServiceInfo{
  33. Name: "ArSamba",
  34. Desc: "arozos Samba Setting Subservice",
  35. Group: "System Settings",
  36. IconPath: "arsamba/img/icon.png",
  37. Version: "1.0",
  38. StartDir: "arsamba/index.html",
  39. SupportFW: true,
  40. LaunchFWDir: "arsamba/index.html",
  41. InitFWSize: []int{350, 560},
  42. })
  43. //Register the standard web services urls
  44. fs := http.FileServer(http.Dir("./web"))
  45. http.HandleFunc("/create", handleNewUser)
  46. http.HandleFunc("/remove", handleUserRemove)
  47. http.HandleFunc("/getStatus", handleGetStatus)
  48. http.Handle("/", fs)
  49. SetupCloseHandler()
  50. go func(port string) {
  51. log.Println("ArSamba subservice started. Listening on " + handler.Port)
  52. err := http.ListenAndServe(port, nil)
  53. if err != nil {
  54. log.Fatal(err)
  55. }
  56. }(handler.Port)
  57. //Mkdir for user samba profile
  58. os.MkdirAll("./profiles", 0755)
  59. //Install samba if it is not installed
  60. pm := apt.NewPackageManager(true)
  61. err := pm.InstallIfNotExists("samba", true)
  62. if err != nil {
  63. log.Println("Unable to install samba on this host! Assume already exists")
  64. }
  65. //Do a blocking loop
  66. select {}
  67. }
  68. func handleGetStatus(w http.ResponseWriter, r *http.Request) {
  69. //Get username from request
  70. username, _ := handler.GetUserInfoFromRequest(w, r)
  71. if runtime.GOOS == "windows" {
  72. sendErrorResponse(w, "not supported platform")
  73. return
  74. }
  75. //Check if the user has already in samba user
  76. log.Println("Checking User Status", username)
  77. userExists := false
  78. out, err := execute("pdbedit -L | grep " + username)
  79. if err != nil {
  80. userExists = false
  81. }
  82. if strings.TrimSpace(string(out)) != "" {
  83. userExists = true
  84. }
  85. //Send the results
  86. js, _ := json.Marshal(userExists)
  87. sendJSONResponse(w, string(js))
  88. }
  89. func handleNewUser(w http.ResponseWriter, r *http.Request) {
  90. //Get the required information
  91. username, err := mv(r, "username", true)
  92. if err != nil {
  93. sendErrorResponse(w, "Invalid username given")
  94. return
  95. }
  96. //Match the session username
  97. proxyUser, token := handler.GetUserInfoFromRequest(w, r)
  98. if username != proxyUser {
  99. sendErrorResponse(w, "User not logged in")
  100. return
  101. }
  102. password, err := mv(r, "password", true)
  103. if err != nil {
  104. sendErrorResponse(w, "Invalid password given")
  105. return
  106. }
  107. //Add the user to samba
  108. log.Println("Adding User", username)
  109. //Add user to linux
  110. out, _ := execute("useradd -m \"" + username + "\"")
  111. log.Println(string(out))
  112. //Set password for the new user
  113. out, _ = execute(`(echo "` + password + `"; sleep 1; echo "` + password + `";) | passwd "` + username + `"`)
  114. log.Println(string(out))
  115. //Add it to samba user
  116. out, _ = execute(`(echo "` + password + `"; sleep 1; echo "` + password + `" ) | sudo smbpasswd -s -a "` + username + `"`)
  117. log.Println(string(out))
  118. //Create an AGI Call that get the user's storage directories files
  119. script := `
  120. requirelib("filelib");
  121. //Get the roots of this user
  122. var roots = filelib.glob("/");
  123. var userdirs = [];
  124. for (var i = 0; i < roots.length; i++){
  125. //Translate all these roots to realpath
  126. userdirs.push([roots[i].split(":").shift(), decodeAbsoluteVirtualPath(roots[i]), pathCanWrite(roots[i])])
  127. }
  128. sendJSONResp(JSON.stringify(userdirs))
  129. `
  130. userProfile := []string{}
  131. //Execute the AGI request on server side
  132. resp, err := handler.RequestGatewayInterface(token, script)
  133. if err != nil {
  134. //Something went wrong when performing POST request
  135. log.Println(err)
  136. } else {
  137. //Try to read the resp body
  138. bodyBytes, err := ioutil.ReadAll(resp.Body)
  139. if err != nil {
  140. log.Println(err)
  141. w.Write([]byte(err.Error()))
  142. return
  143. }
  144. resp.Body.Close()
  145. log.Println(string(bodyBytes))
  146. //Decode the json
  147. type Results [][]interface{}
  148. results := new(Results)
  149. err = json.Unmarshal(bodyBytes, &results)
  150. if err != nil {
  151. log.Println(err)
  152. return
  153. }
  154. log.Println(results)
  155. //Generate user root folders
  156. for _, r := range *results {
  157. if len(r) == 3 {
  158. pathname := r[0].(string)
  159. if pathname == "tmp" {
  160. //Do not expose tmp folder
  161. continue
  162. }
  163. rpath := r[1].(string)
  164. canWrite := r[2].(bool)
  165. uuidOfStorage := username + " (" + pathname + ")"
  166. if canWrite {
  167. userProfile = append(userProfile, `[`+uuidOfStorage+`]
  168. comment=`+username+"'s "+pathname+`
  169. path=`+rpath+`
  170. read only=no
  171. valid users = `+username+`
  172. guest ok=no
  173. browseable=yes
  174. create mask=0777
  175. directory mask=0777`)
  176. } else {
  177. userProfile = append(userProfile, `[`+uuidOfStorage+`]
  178. comment=`+username+"'s "+pathname+`
  179. path=`+rpath+`
  180. read only=yes
  181. valid users = `+username+`
  182. guest ok=no
  183. browseable=yes
  184. create mask=0777
  185. directory mask=0777`)
  186. }
  187. }
  188. }
  189. }
  190. log.Println(strings.Join(userProfile, "\n\n"))
  191. //Write the user profiles to file
  192. ioutil.WriteFile("./profiles/"+username+".conf", []byte(strings.Join(userProfile, "\n\n")), 0755)
  193. updateSmbConfig()
  194. //Return ok
  195. sendOK(w)
  196. }
  197. func handleUserRemove(w http.ResponseWriter, r *http.Request) {
  198. //Get the required information
  199. username, err := mv(r, "username", true)
  200. if err != nil {
  201. sendErrorResponse(w, "Invalid username given")
  202. return
  203. }
  204. //Match the session username
  205. proxyUser, _ := handler.GetUserInfoFromRequest(w, r)
  206. if username != proxyUser {
  207. sendErrorResponse(w, "User not logged in")
  208. return
  209. }
  210. //OK! Remove user
  211. log.Println("Remove user", username)
  212. //Remove user from samba
  213. out, _ := execute("smbpasswd -x \"" + username + "\"")
  214. log.Println(string(out))
  215. //Remove user from linux as well
  216. out, _ = execute("userdel -r \"" + username + "\"")
  217. log.Println(string(out))
  218. //Remove user profiles
  219. if fileExists("./profiles/" + username + ".conf") {
  220. os.Remove("./profiles/" + username + ".conf")
  221. }
  222. updateSmbConfig()
  223. //Return OK
  224. sendOK(w)
  225. }
  226. func updateSmbConfig() {
  227. //Update the system config
  228. profiles, _ := filepath.Glob("./profiles/*.conf")
  229. base, _ := ioutil.ReadFile("smb.conf")
  230. additionalProfiles := []string{}
  231. for _, profile := range profiles {
  232. thisProfileContent, _ := ioutil.ReadFile(profile)
  233. additionalProfiles = append(additionalProfiles, string(thisProfileContent))
  234. }
  235. finalConfigFile := string(base) + strings.Join(additionalProfiles, "\n\n")
  236. ioutil.WriteFile("/etc/samba/smb.conf", []byte(finalConfigFile), 0777)
  237. out, err := execute("systemctl restart smbd.service")
  238. log.Println("Samba restarted: ", string(out), err)
  239. }
  240. func execute(command string) (string, error) {
  241. cmd := exec.Command("bash", "-c", command)
  242. out, err := cmd.CombinedOutput()
  243. if err != nil {
  244. return string(out), err
  245. }
  246. return string(out), nil
  247. }