github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/server/controller_admin_test.go (about) 1 package server 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "net/http" 8 "net/url" 9 "strconv" 10 "time" 11 12 "github.com/pyroscope-io/pyroscope/pkg/model" 13 "github.com/pyroscope-io/pyroscope/pkg/service" 14 "github.com/pyroscope-io/pyroscope/pkg/sqlstore" 15 16 . "github.com/onsi/ginkgo/v2" 17 . "github.com/onsi/gomega" 18 "github.com/prometheus/client_golang/prometheus" 19 "github.com/sirupsen/logrus" 20 21 "github.com/pyroscope-io/pyroscope/pkg/config" 22 "github.com/pyroscope-io/pyroscope/pkg/exporter" 23 "github.com/pyroscope-io/pyroscope/pkg/health" 24 "github.com/pyroscope-io/pyroscope/pkg/parser" 25 "github.com/pyroscope-io/pyroscope/pkg/storage" 26 "github.com/pyroscope-io/pyroscope/pkg/testing" 27 ) 28 29 var _ = Describe("server", func() { 30 type testServices struct { 31 s *storage.Storage 32 k service.APIKeyService 33 } 34 ingest := func(lines string, app string) { 35 u, _ := url.Parse("http://localhost:4040/ingest") 36 q := u.Query() 37 q.Add("name", app) 38 q.Add("from", strconv.Itoa(int(testing.ParseTime("2020-01-01-01:01:00").Unix()))) 39 q.Add("until", strconv.Itoa(int(testing.ParseTime("2020-01-01-01:01:10").Unix()))) 40 q.Add("format", "lines") 41 u.RawQuery = q.Encode() 42 req, err := http.NewRequest("POST", u.String(), bytes.NewBuffer([]byte(lines))) 43 Expect(err).ToNot(HaveOccurred()) 44 req.Header.Set("Content-Type", "text/plain") 45 res, err := http.DefaultClient.Do(req) 46 Expect(err).ToNot(HaveOccurred()) 47 Expect(res.StatusCode).To(Equal(200)) 48 } 49 deleteApp := func(app string, key string) *http.Response { 50 input := struct { 51 Name string `json:"name"` 52 }{app} 53 body, _ := json.Marshal(input) 54 req, err := http.NewRequest("DELETE", "http://localhost:4040/api/apps", bytes.NewBuffer(body)) 55 if key != "" { 56 req.Header.Set("Authorization", "Bearer "+key) 57 } 58 Expect(err).ToNot(HaveOccurred()) 59 res, err := http.DefaultClient.Do(req) 60 Expect(err).ToNot(HaveOccurred()) 61 return res 62 } 63 runServer := func(cfg **config.Config, cb func(s testServices)) { 64 defer GinkgoRecover() 65 s, err := storage.New( 66 storage.NewConfig(&(*cfg).Server), 67 logrus.StandardLogger(), 68 prometheus.NewRegistry(), 69 new(health.Controller), 70 storage.NoopApplicationMetadataService{}, 71 ) 72 Expect(err).ToNot(HaveOccurred()) 73 e, _ := exporter.NewExporter(nil, nil) 74 sql, err := sqlstore.Open(&(*cfg).Server) 75 Expect(err).ToNot(HaveOccurred()) 76 77 l := logrus.New() 78 79 c, _ := New(Config{ 80 Configuration: &(*cfg).Server, 81 Storage: s, 82 Ingester: parser.New(logrus.StandardLogger(), s, e), 83 Logger: l, 84 MetricsRegisterer: prometheus.NewRegistry(), 85 ExportedMetricsRegistry: prometheus.NewRegistry(), 86 Notifier: mockNotifier{}, 87 DB: sql.DB(), 88 }) 89 c.dir = http.Dir("testdata") 90 startController(c, "http", ":4040") 91 defer c.Stop() 92 93 k := service.NewAPIKeyService(sql.DB(), 0) 94 cb(testServices{s, k}) 95 } 96 createToken := func(s testServices, role model.Role) string { 97 params := model.CreateAPIKeyParams{Name: "t" + strconv.Itoa(int(role)), Role: role} 98 _, key, err := s.k.CreateAPIKey(context.TODO(), params) 99 Expect(err).ToNot(HaveOccurred()) 100 return key 101 } 102 checkRoleAccess := func(s testServices, role model.Role, expectAccess bool, expectStatus int) { 103 ingest("foo;bar\nfoo;bar\nfoo;baz\nfoo;baz\nfoo;baz\n", "test.app1") 104 ingest("foo;bar\nfoo;bar\nfoo;baz\nfoo;baz\nfoo;baz\n", "test.app2") 105 time.Sleep(100 * time.Millisecond) 106 Expect(s.s.GetAppNames(context.TODO())).To(Equal([]string{"test.app1", "test.app2"})) 107 key := "" 108 if role != model.InvalidRole { 109 key = createToken(s, role) 110 } 111 res := deleteApp("test.app2", key) 112 Expect(res.StatusCode).To(Equal(expectStatus)) 113 if expectAccess { 114 Expect(s.s.GetAppNames(context.TODO())).To(Equal([]string{"test.app1"})) 115 } else { 116 Expect(s.s.GetAppNames(context.TODO())).To(Equal([]string{"test.app1", "test.app2"})) 117 } 118 } 119 120 testing.WithConfig(func(cfg **config.Config) { 121 Describe("http api admin controller", func() { 122 Context("with Auth enabled", func() { 123 It("should be accessible to only admin token", func() { 124 (*cfg).Server.Auth.Internal.Enabled = true 125 (*cfg).Server.EnableExperimentalAdmin = true 126 runServer(cfg, func(s testServices) { 127 checkRoleAccess(s, model.AdminRole, true, http.StatusOK) 128 checkRoleAccess(s, model.AgentRole, false, http.StatusForbidden) 129 checkRoleAccess(s, model.ReadOnlyRole, false, http.StatusForbidden) 130 checkRoleAccess(s, model.InvalidRole, false, http.StatusUnauthorized) 131 }) 132 }) 133 }) 134 Context("with Auth disabled", func() { 135 // notice that the routes' access is granular 136 // FIXME update the tests to test each route individually 137 It("should never be accessible", func() { 138 (*cfg).Server.Auth.Internal.Enabled = false 139 (*cfg).Server.EnableExperimentalAdmin = true 140 runServer(cfg, func(s testServices) { 141 checkRoleAccess(s, model.AdminRole, false, http.StatusForbidden) 142 checkRoleAccess(s, model.AgentRole, false, http.StatusForbidden) 143 checkRoleAccess(s, model.ReadOnlyRole, false, http.StatusForbidden) 144 checkRoleAccess(s, model.InvalidRole, false, http.StatusForbidden) 145 }) 146 }) 147 }) 148 }) 149 }) 150 })