handlers.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. package samba
  2. import (
  3. "encoding/json"
  4. "log"
  5. "net/http"
  6. "path/filepath"
  7. "strings"
  8. "imuslab.com/arozos/mod/utils"
  9. )
  10. // Get current samba service status
  11. func (s *ShareManager) SmbdStates(w http.ResponseWriter, r *http.Request) {
  12. set, err := utils.PostPara(r, "set")
  13. if err != nil {
  14. //return the current smbd states
  15. smbdRunning, err := IsSmbdRunning()
  16. if err != nil {
  17. utils.SendErrorResponse(w, err.Error())
  18. return
  19. }
  20. js, _ := json.Marshal(smbdRunning)
  21. utils.SendJSONResponse(w, string(js))
  22. return
  23. } else if set == "enable" {
  24. //Set smbd to enable
  25. err = SetSmbdEnableState(true)
  26. if err != nil {
  27. utils.SendErrorResponse(w, err.Error())
  28. return
  29. }
  30. utils.SendOK(w)
  31. return
  32. } else if set == "disable" {
  33. //Set smbd to disable
  34. err = SetSmbdEnableState(false)
  35. if err != nil {
  36. utils.SendErrorResponse(w, err.Error())
  37. return
  38. }
  39. utils.SendOK(w)
  40. return
  41. }
  42. utils.SendErrorResponse(w, "not support set state: "+set+". Only support enable /disable")
  43. }
  44. // List all the samba shares
  45. func (s *ShareManager) ListSambaShares(w http.ResponseWriter, r *http.Request) {
  46. shares, err := s.ReadSambaShares()
  47. if err != nil {
  48. utils.SendErrorResponse(w, err.Error())
  49. return
  50. }
  51. //Remove those shares that is reserved by systems
  52. shares = s.FilterSystemCreatedShares(shares)
  53. js, _ := json.Marshal(shares)
  54. utils.SendJSONResponse(w, string(js))
  55. }
  56. // Add a samba share
  57. func (s *ShareManager) AddSambaShare(w http.ResponseWriter, r *http.Request) {
  58. shareName, err := utils.PostPara(r, "name")
  59. if err != nil {
  60. utils.SendErrorResponse(w, "share name not given")
  61. return
  62. }
  63. shareName = strings.TrimSpace(shareName)
  64. //Check if this share name already been used
  65. shareNameExists, err := s.ShareNameExists(shareName)
  66. if err != nil {
  67. utils.SendErrorResponse(w, err.Error())
  68. return
  69. }
  70. if shareNameExists {
  71. utils.SendErrorResponse(w, "share with identical name already exists")
  72. return
  73. }
  74. sharePath, err := utils.PostPara(r, "path")
  75. if err != nil {
  76. utils.SendErrorResponse(w, "share path not given")
  77. return
  78. }
  79. //Parse the path to absolute path
  80. absoluteSharePath, err := filepath.Abs(sharePath)
  81. if err != nil {
  82. utils.SendErrorResponse(w, err.Error())
  83. return
  84. }
  85. //Check if path exists
  86. if !utils.FileExists(absoluteSharePath) {
  87. utils.SendErrorResponse(w, "target path not exists")
  88. return
  89. }
  90. //Check if target path is a folder
  91. if !utils.IsDir(absoluteSharePath) {
  92. utils.SendErrorResponse(w, "target path is not a directory")
  93. return
  94. }
  95. //Check if it is a reserved / protected path
  96. if isPathInsideImportantFolders(absoluteSharePath) {
  97. utils.SendErrorResponse(w, "system reserved path cannot be shared")
  98. return
  99. }
  100. validUsersJSON, err := utils.PostPara(r, "users")
  101. if err != nil {
  102. utils.SendErrorResponse(w, "no valid user givens")
  103. return
  104. }
  105. //Parse valid users into string slice
  106. validUsers := []string{}
  107. err = json.Unmarshal([]byte(validUsersJSON), &validUsers)
  108. if err != nil {
  109. utils.SendErrorResponse(w, "unable to parse JSON for valid users")
  110. return
  111. }
  112. //Check if all the users exists in the host OS
  113. for _, validUser := range validUsers {
  114. thisUnixUserExists, err := s.SambaUserExists(validUser)
  115. if err != nil {
  116. utils.SendErrorResponse(w, err.Error())
  117. return
  118. }
  119. if !thisUnixUserExists {
  120. //This user not exists
  121. utils.SendErrorResponse(w, validUser+" is not a valid unix user")
  122. return
  123. }
  124. }
  125. readOnly, err := utils.PostBool(r, "readonly")
  126. if err != nil {
  127. readOnly = false
  128. }
  129. browseable, err := utils.PostBool(r, "browseable")
  130. if err != nil {
  131. browseable = true
  132. }
  133. allowGuest, err := utils.PostBool(r, "guestok")
  134. if err != nil {
  135. allowGuest = false
  136. }
  137. shareToCreate := ShareConfig{
  138. Name: shareName,
  139. Path: absoluteSharePath,
  140. ValidUsers: validUsers,
  141. ReadOnly: readOnly,
  142. Browseable: browseable,
  143. GuestOk: allowGuest,
  144. }
  145. //Add the new share to smb.conf
  146. err = s.CreateNewSambaShare(&shareToCreate)
  147. if err != nil {
  148. utils.SendErrorResponse(w, err.Error())
  149. return
  150. }
  151. //Restart smbd
  152. err = restartSmbd()
  153. if err != nil {
  154. utils.SendErrorResponse(w, err.Error())
  155. return
  156. }
  157. utils.SendOK(w)
  158. }
  159. // Delete a user samba share, that share must only contains the current user / owner
  160. func (s *ShareManager) DelUserSambaShare(w http.ResponseWriter, r *http.Request) {
  161. //Get the user info
  162. userInfo, err := s.UserHandler.GetUserInfoFromRequest(w, r)
  163. if err != nil {
  164. utils.SendErrorResponse(w, "permission denied")
  165. return
  166. }
  167. shareName, err := utils.PostPara(r, "name")
  168. if err != nil {
  169. utils.SendErrorResponse(w, "share name not given")
  170. return
  171. }
  172. //Check if share exists
  173. targetShare, err := s.GetShareByName(shareName)
  174. if err != nil {
  175. utils.SendErrorResponse(w, err.Error())
  176. return
  177. }
  178. //Check if the user can access this share
  179. if !s.UserCanAccessShare(targetShare, userInfo.Username) {
  180. utils.SendErrorResponse(w, "share access denied")
  181. return
  182. }
  183. //Check if the user is the only user in the share access list
  184. if len(targetShare.ValidUsers) == 1 && strings.EqualFold(targetShare.ValidUsers[0], userInfo.Username) {
  185. //This share is own by this user and this user is the only one who can access it
  186. //remove user access will create trash in the smb.conf folder. Remove the whole folder entirely.
  187. err = s.RemoveSambaShareConfig(targetShare.Name)
  188. if err != nil {
  189. utils.SendErrorResponse(w, err.Error())
  190. return
  191. }
  192. utils.SendOK(w)
  193. return
  194. }
  195. //Share also contains other users. Only remove user to that share
  196. err = s.RemoveUserFromSambaShare(targetShare.Name, userInfo.Username)
  197. if err != nil {
  198. utils.SendErrorResponse(w, err.Error())
  199. return
  200. }
  201. utils.SendOK(w)
  202. }
  203. // Remove a samba share by name, can remove any share by name, should be for admin only
  204. // call to DelUserSambaShare for non-admin uses
  205. func (s *ShareManager) DelSambaShare(w http.ResponseWriter, r *http.Request) {
  206. shareName, err := utils.PostPara(r, "name")
  207. if err != nil {
  208. utils.SendErrorResponse(w, "share name not given")
  209. return
  210. }
  211. //Check if share exists
  212. shareExists, err := s.ShareExists(shareName)
  213. if err != nil {
  214. utils.SendErrorResponse(w, err.Error())
  215. return
  216. }
  217. if !shareExists {
  218. utils.SendErrorResponse(w, "share to be remove not exists")
  219. return
  220. }
  221. //Remove the share from config file
  222. err = s.RemoveSambaShareConfig(shareName)
  223. if err != nil {
  224. utils.SendErrorResponse(w, err.Error())
  225. return
  226. }
  227. //Restart smbd
  228. err = restartSmbd()
  229. if err != nil {
  230. utils.SendErrorResponse(w, err.Error())
  231. return
  232. }
  233. utils.SendOK(w)
  234. }
  235. // Add a new samba user
  236. func (s *ShareManager) NewSambaUser(w http.ResponseWriter, r *http.Request) {
  237. username, err := utils.PostPara(r, "username")
  238. if err != nil {
  239. utils.SendErrorResponse(w, "username not given")
  240. return
  241. }
  242. password, err := utils.PostPara(r, "password")
  243. if err != nil {
  244. utils.SendErrorResponse(w, "password not set")
  245. return
  246. }
  247. err = s.AddSambaUser(username, password)
  248. if err != nil {
  249. utils.SendErrorResponse(w, err.Error())
  250. return
  251. }
  252. utils.SendOK(w)
  253. }
  254. // Remove a samba user, check for admin before calling
  255. func (s *ShareManager) DelSambaUser(w http.ResponseWriter, r *http.Request) {
  256. username, err := utils.PostPara(r, "username")
  257. if err != nil {
  258. utils.SendErrorResponse(w, "username not given")
  259. return
  260. }
  261. //Remove the samba user
  262. err = s.RemoveSmbUser(username)
  263. if err != nil {
  264. utils.SendErrorResponse(w, err.Error())
  265. return
  266. }
  267. utils.SendOK(w)
  268. }
  269. // List all samba users info
  270. func (s *ShareManager) ListSambaUsers(w http.ResponseWriter, r *http.Request) {
  271. type SimplifiedUserInfo struct {
  272. UnixUsername string
  273. Domain string
  274. IsArozOSUser bool
  275. }
  276. results := []*SimplifiedUserInfo{}
  277. userInfo, err := s.ListSambaUsersInfo()
  278. if err != nil {
  279. utils.SendErrorResponse(w, err.Error())
  280. return
  281. }
  282. for _, thisUserInfo := range userInfo {
  283. thisIsArozOSUser := s.UserHandler.GetAuthAgent().UserExists(strings.TrimSpace(thisUserInfo.UnixUsername))
  284. results = append(results, &SimplifiedUserInfo{
  285. UnixUsername: strings.TrimSpace(thisUserInfo.UnixUsername),
  286. Domain: thisUserInfo.Domain,
  287. IsArozOSUser: thisIsArozOSUser,
  288. })
  289. }
  290. js, _ := json.Marshal(results)
  291. utils.SendJSONResponse(w, string(js))
  292. }
  293. // Activate a user account from arozos into samba user
  294. func (s *ShareManager) ActivateUserAccount(w http.ResponseWriter, r *http.Request, password string) {
  295. userInfo, _ := s.UserHandler.GetUserInfoFromRequest(w, r)
  296. //Register this user to samba if not exists
  297. sambaUserExists, err := s.SambaUserExists(userInfo.Username)
  298. if err != nil {
  299. utils.SendErrorResponse(w, err.Error())
  300. return
  301. }
  302. if !sambaUserExists {
  303. //This user account not activated yet. Activate it
  304. err = s.AddSambaUser(userInfo.Username, password)
  305. if err != nil {
  306. utils.SendErrorResponse(w, err.Error())
  307. return
  308. }
  309. }
  310. //Create the user root share folders
  311. for _, fsh := range userInfo.GetAllAccessibleFileSystemHandler() {
  312. if fsh.IsNetworkDrive() {
  313. //Samba can only work with drives locally hosted on this server
  314. //Skip network drives
  315. continue
  316. }
  317. fshID := fsh.UUID
  318. fshSharePath := fsh.Path
  319. if fsh.RequierUserIsolation() {
  320. //User seperated storage. Only mount the user one
  321. fshID = userInfo.Username + "_" + fsh.UUID
  322. fshSharePath = filepath.Join(fsh.Path, "/users/", userInfo.Username+"/")
  323. }
  324. fshID = sanitizeShareName(fshID)
  325. //Check if the share already exists
  326. shareExists, err := s.ShareExists(fshID)
  327. if err != nil {
  328. continue
  329. }
  330. if !shareExists {
  331. //Try to create the share
  332. fshShareAbsolutePath, err := filepath.Abs(fshSharePath)
  333. if err != nil {
  334. log.Println("[Samba] Unable to generate share config for path: " + fshSharePath)
  335. continue
  336. }
  337. //Check if that folder exists
  338. if !utils.FileExists(fshShareAbsolutePath) {
  339. //Folder not exists. Continue
  340. log.Println("[Samba] Path not exists for file system handler: " + fshSharePath)
  341. continue
  342. }
  343. //Ok! Create the share with this username
  344. err = s.CreateNewSambaShare(&ShareConfig{
  345. Name: fshID,
  346. Path: fshShareAbsolutePath,
  347. ValidUsers: []string{userInfo.Username},
  348. ReadOnly: false,
  349. Browseable: !fsh.RequierUserIsolation(),
  350. GuestOk: false,
  351. })
  352. if err != nil {
  353. log.Println("[Samba] Failed to create share: " + err.Error())
  354. utils.SendErrorResponse(w, err.Error())
  355. return
  356. }
  357. } else {
  358. //Share exists. Add this user to such share
  359. err = s.AddUserToSambaShare(fshID, userInfo.Username)
  360. if err != nil {
  361. log.Println("[Samba] Failed to add user " + userInfo.Username + " to share " + fshID + ": " + err.Error())
  362. utils.SendErrorResponse(w, err.Error())
  363. return
  364. }
  365. }
  366. }
  367. err = restartSmbd()
  368. if err != nil {
  369. utils.SendErrorResponse(w, err.Error())
  370. return
  371. }
  372. utils.SendOK(w)
  373. }
  374. // Get if the user share has been enabled
  375. func (s *ShareManager) HandleUserSmbStatusList(w http.ResponseWriter, r *http.Request) {
  376. type UserStatus struct {
  377. SmbdEnabled bool
  378. UserSmbShareEnabled bool
  379. UserSmbShareList []*ShareConfig
  380. }
  381. result := UserStatus{
  382. SmbdEnabled: s.IsEnabled(),
  383. UserSmbShareEnabled: false,
  384. UserSmbShareList: []*ShareConfig{},
  385. }
  386. userInfo, err := s.UserHandler.GetUserInfoFromRequest(w, r)
  387. if err != nil {
  388. utils.SendErrorResponse(w, err.Error())
  389. return
  390. }
  391. userAccessibleShares, err := s.GetUsersShare(userInfo.Username)
  392. if err != nil {
  393. //User never used smb service
  394. js, _ := json.Marshal(result)
  395. utils.SendJSONResponse(w, string(js))
  396. return
  397. }
  398. if len(userAccessibleShares) == 0 {
  399. result.UserSmbShareEnabled = false
  400. } else {
  401. result.UserSmbShareEnabled = true
  402. result.UserSmbShareList = userAccessibleShares
  403. }
  404. js, _ := json.Marshal(result)
  405. utils.SendJSONResponse(w, string(js))
  406. }
  407. // Deactivate the user account by removing user access to all shares
  408. func (s *ShareManager) DeactiveUserAccount(w http.ResponseWriter, r *http.Request) {
  409. userInfo, err := s.UserHandler.GetUserInfoFromRequest(w, r)
  410. if err != nil {
  411. utils.SendErrorResponse(w, err.Error())
  412. return
  413. }
  414. userAccessibleShares, err := s.GetUsersShare(userInfo.Username)
  415. if err != nil {
  416. utils.SendErrorResponse(w, err.Error())
  417. return
  418. }
  419. //For each of the shares this user can access, remove his name from the share
  420. for _, userAccessibleShare := range userAccessibleShares {
  421. err = s.RemoveUserFromSambaShare(userAccessibleShare.Name, userInfo.Username)
  422. if err != nil {
  423. log.Println("[Samba] Unable to remove user " + userInfo.Username + " from share: " + err.Error())
  424. continue
  425. }
  426. }
  427. //Remove this samba user
  428. err = s.RemoveSmbUser(userInfo.Username)
  429. if err != nil {
  430. utils.SendErrorResponse(w, "Samba user remove failed: "+err.Error())
  431. return
  432. }
  433. err = restartSmbd()
  434. if err != nil {
  435. utils.SendErrorResponse(w, err.Error())
  436. return
  437. }
  438. utils.SendOK(w)
  439. }
  440. // Handle update to accessible users
  441. func (s *ShareManager) HandleAccessUserUpdate(w http.ResponseWriter, r *http.Request) {
  442. shareName, err := utils.PostPara(r, "name")
  443. if err != nil {
  444. utils.SendErrorResponse(w, "share name not given")
  445. return
  446. }
  447. newUserListJSON, err := utils.PostPara(r, "users")
  448. if err != nil {
  449. utils.SendErrorResponse(w, "list of new users not given")
  450. return
  451. }
  452. //Parse the user list from json string to string slice
  453. newUserList := []string{}
  454. err = json.Unmarshal([]byte(newUserListJSON), &newUserList)
  455. if err != nil {
  456. log.Println("[Samba] Parse new user list failed: " + err.Error())
  457. utils.SendErrorResponse(w, "failed to parse the new user list")
  458. return
  459. }
  460. //read the target share from smb.conf
  461. targetShare, err := s.GetShareByName(shareName)
  462. if err != nil {
  463. utils.SendErrorResponse(w, err.Error())
  464. return
  465. }
  466. for _, originalUser := range targetShare.ValidUsers {
  467. if !utils.StringInArray(newUserList, originalUser) {
  468. //This user is not longer allowed to access this share
  469. //remove this user from this share
  470. s.RemoveUserFromSambaShare(shareName, originalUser)
  471. }
  472. }
  473. for _, newUsername := range newUserList {
  474. if !s.UserCanAccessShare(targetShare, newUsername) {
  475. err = s.AddUserToSambaShare(targetShare.Name, newUsername)
  476. if err != nil {
  477. utils.SendErrorResponse(w, err.Error())
  478. return
  479. }
  480. }
  481. }
  482. err = restartSmbd()
  483. if err != nil {
  484. utils.SendErrorResponse(w, err.Error())
  485. return
  486. }
  487. utils.SendOK(w)
  488. }
  489. // Handle changing path of share
  490. func (s *ShareManager) HandleSharePathChange(w http.ResponseWriter, r *http.Request) {
  491. shareName, err := utils.PostPara(r, "name")
  492. if err != nil {
  493. utils.SendErrorResponse(w, "share name not given")
  494. return
  495. }
  496. newSharePath, err := utils.PostPara(r, "path")
  497. if err != nil {
  498. utils.SendErrorResponse(w, "list of new users not given")
  499. return
  500. }
  501. //Convert path to absolute and check if folder exists
  502. newSharePathAbsolute, err := filepath.Abs(newSharePath)
  503. if err != nil {
  504. utils.SendErrorResponse(w, err.Error())
  505. return
  506. }
  507. if !utils.FileExists(newSharePathAbsolute) {
  508. utils.SendErrorResponse(w, "target folder not exists")
  509. return
  510. }
  511. //Check if path sharing is allowed
  512. if isPathInsideImportantFolders(newSharePathAbsolute) {
  513. utils.SendErrorResponse(w, "path is or inside protected folders")
  514. return
  515. }
  516. //read the target share from smb.conf
  517. targetShare, err := s.GetShareByName(shareName)
  518. if err != nil {
  519. utils.SendErrorResponse(w, err.Error())
  520. return
  521. }
  522. //Update and save share to smb.conf
  523. targetShare.Path = newSharePathAbsolute
  524. err = targetShare.SaveToConfig()
  525. if err != nil {
  526. utils.SendErrorResponse(w, err.Error())
  527. return
  528. }
  529. //Restart smbd
  530. err = restartSmbd()
  531. if err != nil {
  532. utils.SendErrorResponse(w, err.Error())
  533. return
  534. }
  535. utils.SendOK(w)
  536. }