permission_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. package permission
  2. import (
  3. "encoding/json"
  4. "net/http"
  5. "net/http/httptest"
  6. "net/url"
  7. "path/filepath"
  8. "strings"
  9. "testing"
  10. db "imuslab.com/arozos/mod/database"
  11. )
  12. func openTempDB(t *testing.T) *db.Database {
  13. t.Helper()
  14. dir := t.TempDir()
  15. database, err := db.NewDatabase(filepath.Join(dir, "test.db"), false)
  16. if err != nil {
  17. t.Fatalf("failed to open test database: %v", err)
  18. }
  19. return database
  20. }
  21. func TestNewPermissionHandler(t *testing.T) {
  22. database := openTempDB(t)
  23. ph, err := NewPermissionHandler(database)
  24. if err != nil {
  25. t.Fatalf("NewPermissionHandler error: %v", err)
  26. }
  27. if ph == nil {
  28. t.Fatal("NewPermissionHandler returned nil handler")
  29. }
  30. }
  31. func TestNewPermissionGroup(t *testing.T) {
  32. database := openTempDB(t)
  33. ph, _ := NewPermissionHandler(database)
  34. pg := ph.NewPermissionGroup("editors", false, 500, []string{"filemanager"}, "Desktop")
  35. if pg == nil {
  36. t.Fatal("NewPermissionGroup returned nil")
  37. }
  38. if pg.Name != "editors" {
  39. t.Errorf("expected name 'editors', got %q", pg.Name)
  40. }
  41. if pg.IsAdmin {
  42. t.Error("expected IsAdmin=false")
  43. }
  44. if pg.DefaultStorageQuota != 500 {
  45. t.Errorf("expected DefaultStorageQuota=500, got %d", pg.DefaultStorageQuota)
  46. }
  47. }
  48. func TestGroupExists(t *testing.T) {
  49. database := openTempDB(t)
  50. ph, _ := NewPermissionHandler(database)
  51. ph.NewPermissionGroup("writers", false, 0, []string{}, "Desktop")
  52. if !ph.GroupExists("writers") {
  53. t.Error("expected GroupExists('writers')=true")
  54. }
  55. if ph.GroupExists("nonexistent") {
  56. t.Error("expected GroupExists('nonexistent')=false")
  57. }
  58. }
  59. func TestGroupExists_CaseInsensitive(t *testing.T) {
  60. database := openTempDB(t)
  61. ph, _ := NewPermissionHandler(database)
  62. ph.NewPermissionGroup("Admins", true, -1, []string{"*"}, "Desktop")
  63. if !ph.GroupExists("admins") {
  64. t.Error("GroupExists should be case-insensitive")
  65. }
  66. }
  67. func TestGetPermissionGroupByName(t *testing.T) {
  68. database := openTempDB(t)
  69. ph, _ := NewPermissionHandler(database)
  70. ph.NewPermissionGroup("viewers", false, 100, []string{"gallery"}, "Desktop")
  71. pg := ph.GetPermissionGroupByName("viewers")
  72. if pg == nil {
  73. t.Fatal("GetPermissionGroupByName returned nil for existing group")
  74. }
  75. if pg.Name != "viewers" {
  76. t.Errorf("expected 'viewers', got %q", pg.Name)
  77. }
  78. if ph.GetPermissionGroupByName("ghost") != nil {
  79. t.Error("expected nil for non-existing group")
  80. }
  81. }
  82. func TestGetPermissionGroupByNameList(t *testing.T) {
  83. database := openTempDB(t)
  84. ph, _ := NewPermissionHandler(database)
  85. ph.NewPermissionGroup("alpha", false, 0, []string{}, "Desktop")
  86. ph.NewPermissionGroup("beta", false, 0, []string{}, "Desktop")
  87. ph.NewPermissionGroup("gamma", false, 0, []string{}, "Desktop")
  88. results := ph.GetPermissionGroupByNameList([]string{"alpha", "gamma"})
  89. if len(results) != 2 {
  90. t.Errorf("expected 2 groups, got %d", len(results))
  91. }
  92. }
  93. func TestUpdatePermissionGroup(t *testing.T) {
  94. database := openTempDB(t)
  95. ph, _ := NewPermissionHandler(database)
  96. ph.NewPermissionGroup("test-group", false, 200, []string{"mod1"}, "Desktop")
  97. err := ph.UpdatePermissionGroup("test-group", true, 999, []string{"mod1", "mod2"}, "Mobile")
  98. if err != nil {
  99. t.Fatalf("UpdatePermissionGroup error: %v", err)
  100. }
  101. pg := ph.GetPermissionGroupByName("test-group")
  102. if pg == nil {
  103. t.Fatal("group not found after update")
  104. }
  105. if !pg.IsAdmin {
  106. t.Error("expected IsAdmin=true after update")
  107. }
  108. if pg.DefaultStorageQuota != 999 {
  109. t.Errorf("expected DefaultStorageQuota=999, got %d", pg.DefaultStorageQuota)
  110. }
  111. }
  112. func TestUpdatePermissionGroup_NonExistent(t *testing.T) {
  113. database := openTempDB(t)
  114. ph, _ := NewPermissionHandler(database)
  115. err := ph.UpdatePermissionGroup("ghost-group", false, 0, []string{}, "Desktop")
  116. if err == nil {
  117. t.Error("expected error when updating non-existent group")
  118. }
  119. }
  120. func TestAddAndRemoveModule(t *testing.T) {
  121. database := openTempDB(t)
  122. ph, _ := NewPermissionHandler(database)
  123. pg := ph.NewPermissionGroup("modtest", false, 0, []string{}, "Desktop")
  124. pg.AddModule("filemanager")
  125. if len(pg.AccessibleModules) != 1 || pg.AccessibleModules[0] != "filemanager" {
  126. t.Errorf("expected [filemanager], got %v", pg.AccessibleModules)
  127. }
  128. // Adding same module twice should be idempotent
  129. pg.AddModule("filemanager")
  130. if len(pg.AccessibleModules) != 1 {
  131. t.Errorf("duplicate AddModule should be no-op, got %v", pg.AccessibleModules)
  132. }
  133. pg.RemoveModule("filemanager")
  134. if len(pg.AccessibleModules) != 0 {
  135. t.Errorf("expected empty modules after remove, got %v", pg.AccessibleModules)
  136. }
  137. }
  138. func TestGetLargestStorageQuotaFromGroups(t *testing.T) {
  139. groups := []*PermissionGroup{
  140. {DefaultStorageQuota: 100},
  141. {DefaultStorageQuota: 500},
  142. {DefaultStorageQuota: 200},
  143. }
  144. quota := GetLargestStorageQuotaFromGroups(groups)
  145. if quota != 500 {
  146. t.Errorf("expected largest quota=500, got %d", quota)
  147. }
  148. }
  149. func TestGetLargestStorageQuotaFromGroups_Unlimited(t *testing.T) {
  150. groups := []*PermissionGroup{
  151. {DefaultStorageQuota: 100},
  152. {DefaultStorageQuota: -1}, // unlimited
  153. }
  154. quota := GetLargestStorageQuotaFromGroups(groups)
  155. if quota != -1 {
  156. t.Errorf("expected -1 (unlimited), got %d", quota)
  157. }
  158. }
  159. // ----- group.go: Remove -----
  160. func TestPermissionGroupRemove(t *testing.T) {
  161. database := openTempDB(t)
  162. ph, _ := NewPermissionHandler(database)
  163. pg := ph.NewPermissionGroup("to-remove", false, 0, []string{"mod1"}, "Desktop")
  164. if !ph.GroupExists("to-remove") {
  165. t.Fatal("group should exist before Remove")
  166. }
  167. pg.Remove()
  168. // Verify database keys are gone
  169. if database.KeyExists("permission", "group/to-remove") {
  170. t.Error("group/to-remove key should be deleted after Remove")
  171. }
  172. if database.KeyExists("permission", "isadmin/to-remove") {
  173. t.Error("isadmin/to-remove key should be deleted after Remove")
  174. }
  175. }
  176. // ----- permission.go: GetUsersPermissionGroup -----
  177. func TestGetUsersPermissionGroup(t *testing.T) {
  178. database := openTempDB(t)
  179. ph, _ := NewPermissionHandler(database)
  180. ph.NewPermissionGroup("readers", false, 100, []string{"gallery"}, "Desktop")
  181. ph.NewPermissionGroup("writers", false, 200, []string{"editor"}, "Desktop")
  182. // Write user -> group mapping into "auth" table (the format used by the system).
  183. // database.Write JSON-encodes the value, so pass the slice directly.
  184. err := database.NewTable("auth")
  185. if err != nil {
  186. t.Fatalf("NewTable auth: %v", err)
  187. }
  188. groups := []string{"readers", "writers"}
  189. database.Write("auth", "group/alice", groups)
  190. result, err := ph.GetUsersPermissionGroup("alice")
  191. if err != nil {
  192. t.Fatalf("GetUsersPermissionGroup error: %v", err)
  193. }
  194. if len(result) != 2 {
  195. t.Errorf("expected 2 groups for alice, got %d", len(result))
  196. }
  197. }
  198. func TestGetUsersPermissionGroup_UserNotFound(t *testing.T) {
  199. database := openTempDB(t)
  200. ph, _ := NewPermissionHandler(database)
  201. // Ensure the "auth" table exists so the database read doesn't panic on a
  202. // missing bucket. When the user key is absent the read returns an empty
  203. // slice with no error; GetUsersPermissionGroup should therefore return an
  204. // empty result with no error.
  205. database.NewTable("auth")
  206. result, err := ph.GetUsersPermissionGroup("ghost")
  207. if err != nil {
  208. t.Errorf("unexpected error for unknown user: %v", err)
  209. }
  210. if len(result) != 0 {
  211. t.Errorf("expected empty group list for unknown user, got %d groups", len(result))
  212. }
  213. }
  214. // ----- request.go: HandleListGroup -----
  215. func newPermHandler(t *testing.T) *PermissionHandler {
  216. t.Helper()
  217. database := openTempDB(t)
  218. ph, err := NewPermissionHandler(database)
  219. if err != nil {
  220. t.Fatalf("NewPermissionHandler: %v", err)
  221. }
  222. ph.NewPermissionGroup("editors", false, 500, []string{"filemanager"}, "Desktop")
  223. ph.NewPermissionGroup("admins", true, -1, []string{"*"}, "Desktop")
  224. return ph
  225. }
  226. func TestHandleListGroup_NamesOnly(t *testing.T) {
  227. ph := newPermHandler(t)
  228. req := httptest.NewRequest(http.MethodGet, "/api/listgroup", nil)
  229. rr := httptest.NewRecorder()
  230. ph.HandleListGroup(rr, req)
  231. if rr.Code != http.StatusOK {
  232. t.Fatalf("expected 200, got %d", rr.Code)
  233. }
  234. body := rr.Body.String()
  235. if !strings.Contains(body, "editors") {
  236. t.Errorf("response should contain 'editors', got: %s", body)
  237. }
  238. if !strings.Contains(body, "admins") {
  239. t.Errorf("response should contain 'admins', got: %s", body)
  240. }
  241. }
  242. func TestHandleListGroup_WithPermissions(t *testing.T) {
  243. ph := newPermHandler(t)
  244. req := httptest.NewRequest(http.MethodGet, "/api/listgroup?showper=true", nil)
  245. rr := httptest.NewRecorder()
  246. ph.HandleListGroup(rr, req)
  247. if rr.Code != http.StatusOK {
  248. t.Fatalf("expected 200, got %d", rr.Code)
  249. }
  250. body := rr.Body.String()
  251. if !strings.Contains(body, "editors") {
  252. t.Errorf("response should contain group info with 'editors', got: %s", body)
  253. }
  254. }
  255. // ----- request.go: HandleGroupEdit -----
  256. func postRequest(path string, params map[string]string) *http.Request {
  257. form := url.Values{}
  258. for k, v := range params {
  259. form.Set(k, v)
  260. }
  261. req := httptest.NewRequest(http.MethodPost, path, strings.NewReader(form.Encode()))
  262. req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  263. return req
  264. }
  265. func TestHandleGroupEdit_ListMode(t *testing.T) {
  266. ph := newPermHandler(t)
  267. req := httptest.NewRequest(http.MethodGet, "/api/groupedit?list=true", nil)
  268. q := req.URL.Query()
  269. q.Set("list", "true")
  270. req.URL.RawQuery = q.Encode()
  271. // PostPara for groupname - use form body
  272. form := url.Values{}
  273. form.Set("groupname", "editors")
  274. req2, _ := http.NewRequest(http.MethodPost, "/api/groupedit?list=true", strings.NewReader(form.Encode()))
  275. req2.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  276. rr := httptest.NewRecorder()
  277. ph.HandleGroupEdit(rr, req2)
  278. if rr.Code != http.StatusOK {
  279. t.Fatalf("expected 200, got %d: %s", rr.Code, rr.Body.String())
  280. }
  281. body := rr.Body.String()
  282. if !strings.Contains(body, "editors") {
  283. t.Errorf("expected group info for 'editors', got: %s", body)
  284. }
  285. }
  286. func TestHandleGroupEdit_ListMode_NonExistentGroup(t *testing.T) {
  287. ph := newPermHandler(t)
  288. form := url.Values{}
  289. form.Set("groupname", "nonexistent")
  290. req, _ := http.NewRequest(http.MethodPost, "/api/groupedit?list=true", strings.NewReader(form.Encode()))
  291. req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  292. rr := httptest.NewRecorder()
  293. ph.HandleGroupEdit(rr, req)
  294. body := rr.Body.String()
  295. if !strings.Contains(body, "error") {
  296. t.Errorf("expected error response for non-existent group, got: %s", body)
  297. }
  298. }
  299. func TestHandleGroupEdit_UpdateMode(t *testing.T) {
  300. ph := newPermHandler(t)
  301. modList, _ := json.Marshal([]string{"filemanager", "settings"})
  302. req := postRequest("/api/groupedit", map[string]string{
  303. "groupname": "editors",
  304. "permission": string(modList),
  305. "isAdmin": "false",
  306. "defaultQuota": "2048",
  307. "interfaceModule": "Desktop",
  308. })
  309. rr := httptest.NewRecorder()
  310. ph.HandleGroupEdit(rr, req)
  311. body := rr.Body.String()
  312. if strings.Contains(body, "error") {
  313. t.Errorf("unexpected error: %s", body)
  314. }
  315. // Verify update took effect
  316. pg := ph.GetPermissionGroupByName("editors")
  317. if pg == nil {
  318. t.Fatal("group 'editors' not found after edit")
  319. }
  320. if pg.DefaultStorageQuota != 2048 {
  321. t.Errorf("expected quota 2048, got %d", pg.DefaultStorageQuota)
  322. }
  323. }
  324. func TestHandleGroupEdit_MissingGroupname(t *testing.T) {
  325. ph := newPermHandler(t)
  326. req := postRequest("/api/groupedit", map[string]string{})
  327. rr := httptest.NewRecorder()
  328. ph.HandleGroupEdit(rr, req)
  329. body := rr.Body.String()
  330. if !strings.Contains(body, "error") {
  331. t.Errorf("expected error when groupname missing, got: %s", body)
  332. }
  333. }
  334. func TestHandleGroupEdit_CannotUnsetAdminFromAdministrator(t *testing.T) {
  335. ph := newPermHandler(t)
  336. // Create the administrator group
  337. ph.NewPermissionGroup("administrator", true, -1, []string{"*"}, "Desktop")
  338. modList, _ := json.Marshal([]string{"*"})
  339. req := postRequest("/api/groupedit", map[string]string{
  340. "groupname": "administrator",
  341. "permission": string(modList),
  342. "isAdmin": "false", // trying to remove admin
  343. "defaultQuota": "-1",
  344. "interfaceModule": "Desktop",
  345. })
  346. rr := httptest.NewRecorder()
  347. ph.HandleGroupEdit(rr, req)
  348. body := rr.Body.String()
  349. if !strings.Contains(body, "error") {
  350. t.Errorf("expected error when unsetting admin from administrator, got: %s", body)
  351. }
  352. }
  353. func TestHandleGroupEdit_InvalidPermissionJSON(t *testing.T) {
  354. ph := newPermHandler(t)
  355. req := postRequest("/api/groupedit", map[string]string{
  356. "groupname": "editors",
  357. "permission": "not-valid-json",
  358. })
  359. rr := httptest.NewRecorder()
  360. ph.HandleGroupEdit(rr, req)
  361. body := rr.Body.String()
  362. if !strings.Contains(body, "error") {
  363. t.Errorf("expected error for invalid JSON permission, got: %s", body)
  364. }
  365. }
  366. // ----- request.go: HandleGroupCreate -----
  367. func TestHandleGroupCreate_Success(t *testing.T) {
  368. ph := newPermHandler(t)
  369. modList, _ := json.Marshal([]string{"gallery"})
  370. req := postRequest("/api/groupcreate", map[string]string{
  371. "groupname": "newgroup",
  372. "permission": string(modList),
  373. "isAdmin": "false",
  374. "defaultQuota": "1024",
  375. "interfaceModule": "Desktop",
  376. })
  377. rr := httptest.NewRecorder()
  378. ph.HandleGroupCreate(rr, req)
  379. body := rr.Body.String()
  380. if strings.Contains(body, "error") {
  381. t.Errorf("unexpected error creating group: %s", body)
  382. }
  383. if !ph.GroupExists("newgroup") {
  384. t.Error("group 'newgroup' should exist after create")
  385. }
  386. }
  387. func TestHandleGroupCreate_DuplicateGroup(t *testing.T) {
  388. ph := newPermHandler(t)
  389. modList, _ := json.Marshal([]string{})
  390. req := postRequest("/api/groupcreate", map[string]string{
  391. "groupname": "editors", // already exists
  392. "permission": string(modList),
  393. "isAdmin": "false",
  394. "defaultQuota": "0",
  395. "interfaceModule": "Desktop",
  396. })
  397. rr := httptest.NewRecorder()
  398. ph.HandleGroupCreate(rr, req)
  399. body := rr.Body.String()
  400. if !strings.Contains(body, "error") {
  401. t.Errorf("expected error for duplicate group, got: %s", body)
  402. }
  403. }
  404. func TestHandleGroupCreate_MissingGroupname(t *testing.T) {
  405. ph := newPermHandler(t)
  406. req := postRequest("/api/groupcreate", map[string]string{})
  407. rr := httptest.NewRecorder()
  408. ph.HandleGroupCreate(rr, req)
  409. body := rr.Body.String()
  410. if !strings.Contains(body, "error") {
  411. t.Errorf("expected error for missing groupname, got: %s", body)
  412. }
  413. }
  414. func TestHandleGroupCreate_InvalidQuota(t *testing.T) {
  415. ph := newPermHandler(t)
  416. modList, _ := json.Marshal([]string{})
  417. req := postRequest("/api/groupcreate", map[string]string{
  418. "groupname": "quotagroup",
  419. "permission": string(modList),
  420. "isAdmin": "false",
  421. "defaultQuota": "notanumber",
  422. "interfaceModule": "Desktop",
  423. })
  424. rr := httptest.NewRecorder()
  425. ph.HandleGroupCreate(rr, req)
  426. body := rr.Body.String()
  427. if !strings.Contains(body, "error") {
  428. t.Errorf("expected error for invalid quota, got: %s", body)
  429. }
  430. }
  431. func TestHandleGroupCreate_NegativeQuota(t *testing.T) {
  432. ph := newPermHandler(t)
  433. modList, _ := json.Marshal([]string{})
  434. req := postRequest("/api/groupcreate", map[string]string{
  435. "groupname": "badquota",
  436. "permission": string(modList),
  437. "isAdmin": "false",
  438. "defaultQuota": "-2", // -1 is unlimited, -2 is invalid
  439. "interfaceModule": "Desktop",
  440. })
  441. rr := httptest.NewRecorder()
  442. ph.HandleGroupCreate(rr, req)
  443. body := rr.Body.String()
  444. if !strings.Contains(body, "error") {
  445. t.Errorf("expected error for quota < -1, got: %s", body)
  446. }
  447. }
  448. // ----- request.go: HandleGroupRemove -----
  449. func TestHandleGroupRemove_Success(t *testing.T) {
  450. ph := newPermHandler(t)
  451. // Add a removable group
  452. ph.NewPermissionGroup("temp-group", false, 0, []string{}, "Desktop")
  453. req := postRequest("/api/groupremove", map[string]string{
  454. "groupname": "temp-group",
  455. })
  456. rr := httptest.NewRecorder()
  457. ph.HandleGroupRemove(rr, req)
  458. body := rr.Body.String()
  459. if strings.Contains(body, "error") {
  460. t.Errorf("unexpected error removing group: %s", body)
  461. }
  462. if ph.GroupExists("temp-group") {
  463. t.Error("group 'temp-group' should be gone after remove")
  464. }
  465. }
  466. func TestHandleGroupRemove_NonExistentGroup(t *testing.T) {
  467. ph := newPermHandler(t)
  468. req := postRequest("/api/groupremove", map[string]string{
  469. "groupname": "doesnotexist",
  470. })
  471. rr := httptest.NewRecorder()
  472. ph.HandleGroupRemove(rr, req)
  473. body := rr.Body.String()
  474. if !strings.Contains(body, "error") {
  475. t.Errorf("expected error for non-existent group, got: %s", body)
  476. }
  477. }
  478. func TestHandleGroupRemove_CannotRemoveAdministrator(t *testing.T) {
  479. ph := newPermHandler(t)
  480. ph.NewPermissionGroup("administrator", true, -1, []string{"*"}, "Desktop")
  481. req := postRequest("/api/groupremove", map[string]string{
  482. "groupname": "administrator",
  483. })
  484. rr := httptest.NewRecorder()
  485. ph.HandleGroupRemove(rr, req)
  486. body := rr.Body.String()
  487. if !strings.Contains(body, "error") {
  488. t.Errorf("expected error when trying to remove administrator group, got: %s", body)
  489. }
  490. }
  491. func TestHandleGroupRemove_MissingGroupname(t *testing.T) {
  492. ph := newPermHandler(t)
  493. req := postRequest("/api/groupremove", map[string]string{})
  494. rr := httptest.NewRecorder()
  495. ph.HandleGroupRemove(rr, req)
  496. body := rr.Body.String()
  497. if !strings.Contains(body, "error") {
  498. t.Errorf("expected error for missing groupname, got: %s", body)
  499. }
  500. }