dashboard_acl_test.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. package api
  2. import (
  3. "testing"
  4. authpublic "github.com/OliveTin/OliveTin/internal/auth/authpublic"
  5. config "github.com/OliveTin/OliveTin/internal/config"
  6. "github.com/OliveTin/OliveTin/internal/executor"
  7. "github.com/stretchr/testify/assert"
  8. "github.com/stretchr/testify/require"
  9. )
  10. func buildDashboardAclTestConfig() *config.Config {
  11. cfg := config.DefaultConfig()
  12. cfg.DefaultPermissions.View = false
  13. cfg.DefaultPermissions.Exec = false
  14. cfg.AccessControlLists = []*config.AccessControlList{
  15. {
  16. Name: "admins",
  17. MatchUsernames: []string{"admin"},
  18. Permissions: config.PermissionsList{View: true, Exec: true},
  19. },
  20. }
  21. cfg.Dashboards = []*config.DashboardComponent{
  22. {
  23. Title: "Public tools",
  24. Contents: []*config.DashboardComponent{
  25. {Title: "Welcome", Type: "display"},
  26. },
  27. },
  28. {
  29. Title: "Services",
  30. Acls: []string{"admins"},
  31. Contents: []*config.DashboardComponent{
  32. {Title: "Status: running", Type: "display"},
  33. },
  34. },
  35. }
  36. return cfg
  37. }
  38. func TestDashboardAclsRootNavAndGetDashboard(t *testing.T) {
  39. cfg := buildDashboardAclTestConfig()
  40. ex := executor.DefaultExecutor(cfg)
  41. api := newServer(ex)
  42. guest := &authpublic.AuthenticatedUser{Username: "guest", Provider: "system"}
  43. guest.BuildUserAcls(cfg)
  44. admin := &authpublic.AuthenticatedUser{Username: "admin"}
  45. admin.BuildUserAcls(cfg)
  46. guestRoots := api.buildRootDashboards(guest, cfg.Dashboards)
  47. assert.Contains(t, guestRoots, "Public tools")
  48. assert.NotContains(t, guestRoots, "Services")
  49. adminRoots := api.buildRootDashboards(admin, cfg.Dashboards)
  50. assert.Contains(t, adminRoots, "Public tools")
  51. assert.Contains(t, adminRoots, "Services")
  52. guestRR := api.createDashboardRenderRequest(guest, "", "")
  53. assert.Nil(t, renderDashboard(guestRR, "Services"),
  54. "GetDashboard must not leak ACL-restricted dashboard content via deep link")
  55. adminRR := api.createDashboardRenderRequest(admin, "", "")
  56. db := renderDashboard(adminRR, "Services")
  57. require.NotNil(t, db)
  58. assert.Equal(t, "Services", db.Title)
  59. }
  60. func TestRootDashboardEntriesIncludeCategory(t *testing.T) {
  61. cfg := config.DefaultConfig()
  62. cfg.Dashboards = []*config.DashboardComponent{
  63. {
  64. Title: "Misc Tools",
  65. Contents: []*config.DashboardComponent{{Title: "Hello", Type: "display"}},
  66. },
  67. {
  68. Title: "My Servers",
  69. Category: "Infrastructure",
  70. Contents: []*config.DashboardComponent{{Title: "Ping", Type: "display"}},
  71. },
  72. {
  73. Title: "Status Board",
  74. Category: "Monitoring",
  75. Contents: []*config.DashboardComponent{{Title: "Uptime", Type: "display"}},
  76. },
  77. {
  78. Title: "My Containers",
  79. Category: "Infrastructure",
  80. Contents: []*config.DashboardComponent{{Title: "Restart", Type: "display"}},
  81. },
  82. }
  83. ex := executor.DefaultExecutor(cfg)
  84. api := newServer(ex)
  85. user := &authpublic.AuthenticatedUser{Username: "guest", Provider: "system"}
  86. user.BuildUserAcls(cfg)
  87. entries := api.buildRootDashboardEntries(user, cfg.Dashboards)
  88. require.Len(t, entries, 4)
  89. assert.Equal(t, []string{"Misc Tools", "My Servers", "Status Board", "My Containers"}, rootDashboardTitles(entries))
  90. assert.Equal(t, "", entries[0].Category)
  91. assert.Equal(t, "Infrastructure", entries[1].Category)
  92. assert.Equal(t, "Monitoring", entries[2].Category)
  93. assert.Equal(t, "Infrastructure", entries[3].Category)
  94. }
  95. func TestRootDashboardEntriesOmitAclHiddenCategories(t *testing.T) {
  96. cfg := buildDashboardAclTestConfig()
  97. cfg.Dashboards[0].Category = "Public"
  98. cfg.Dashboards[1].Category = "Admin only"
  99. ex := executor.DefaultExecutor(cfg)
  100. api := newServer(ex)
  101. guest := &authpublic.AuthenticatedUser{Username: "guest", Provider: "system"}
  102. guest.BuildUserAcls(cfg)
  103. entries := api.buildRootDashboardEntries(guest, cfg.Dashboards)
  104. require.Len(t, entries, 1)
  105. assert.Equal(t, "Public tools", entries[0].Title)
  106. assert.Equal(t, "Public", entries[0].Category)
  107. }
  108. func TestDashboardAclsNestedDirectoryDeepLink(t *testing.T) {
  109. cfg := buildDashboardAclTestConfig()
  110. cfg.Dashboards = []*config.DashboardComponent{
  111. {
  112. Title: "Public tools",
  113. Contents: []*config.DashboardComponent{
  114. {Title: "Welcome", Type: "display"},
  115. },
  116. },
  117. {
  118. Title: "Services",
  119. Acls: []string{"admins"},
  120. Contents: []*config.DashboardComponent{
  121. {
  122. Title: "Infrastructure",
  123. Contents: []*config.DashboardComponent{
  124. {Title: "Status: running", Type: "display"},
  125. },
  126. },
  127. },
  128. },
  129. }
  130. ex := executor.DefaultExecutor(cfg)
  131. api := newServer(ex)
  132. guest := &authpublic.AuthenticatedUser{Username: "guest", Provider: "system"}
  133. guest.BuildUserAcls(cfg)
  134. admin := &authpublic.AuthenticatedUser{Username: "admin"}
  135. admin.BuildUserAcls(cfg)
  136. guestRR := api.createDashboardRenderRequest(guest, "", "")
  137. assert.Nil(t, renderDashboard(guestRR, "Infrastructure"),
  138. "nested directory under ACL-restricted root must not leak via deep link")
  139. adminRR := api.createDashboardRenderRequest(admin, "", "")
  140. db := renderDashboard(adminRR, "Infrastructure")
  141. require.NotNil(t, db)
  142. assert.Equal(t, "Infrastructure", db.Title)
  143. }