main.go 7.1 KB

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