diff --git a/router/middleware/perm/perm.go b/router/middleware/perm/perm.go index 560753ad8..fe7cea501 100644 --- a/router/middleware/perm/perm.go +++ b/router/middleware/perm/perm.go @@ -417,6 +417,7 @@ func MustWrite() gin.HandlerFunc { // MustRead ensures the user has admin, write or read access to the repo. func MustRead() gin.HandlerFunc { return func(c *gin.Context) { + cl := claims.Retrieve(c) o := org.Retrieve(c) r := repo.Retrieve(c) u := user.Retrieve(c) @@ -437,8 +438,23 @@ func MustRead() gin.HandlerFunc { return } + // return if request is from worker with build token access + if strings.EqualFold(cl.TokenType, constants.WorkerBuildTokenType) { + b := build.Retrieve(c) + if cl.BuildID == b.GetID() { + return + } + + retErr := fmt.Errorf("subject %s does not have 'read' permissions for repo %s", cl.Subject, r.GetFullName()) + + util.HandleError(c, http.StatusUnauthorized, retErr) + + return + } + logger.Debugf("verifying user %s has 'read' permissions for repo %s", u.GetName(), r.GetFullName()) + // return if user is platform admin if u.GetAdmin() { return } diff --git a/router/middleware/perm/perm_test.go b/router/middleware/perm/perm_test.go index a47dcef40..98b00aefd 100644 --- a/router/middleware/perm/perm_test.go +++ b/router/middleware/perm/perm_test.go @@ -1719,6 +1719,88 @@ func TestPerm_MustRead_PlatAdmin(t *testing.T) { } } +func TestPerm_MustRead_WorkerBuildToken(t *testing.T) { + // setup types + secret := "superSecret" + + tm := &token.Manager{ + PrivateKey: "123abc", + SignMethod: jwt.SigningMethodHS256, + UserAccessTokenDuration: time.Minute * 5, + UserRefreshTokenDuration: time.Minute * 30, + } + + r := new(library.Repo) + r.SetID(1) + r.SetUserID(1) + r.SetHash("baz") + r.SetOrg("foo") + r.SetName("bar") + r.SetFullName("foo/bar") + r.SetVisibility("private") + + b := new(library.Build) + b.SetID(1) + b.SetRepoID(1) + b.SetNumber(1) + + mto := &token.MintTokenOpts{ + Hostname: "worker", + TokenDuration: time.Minute * 35, + TokenType: constants.WorkerBuildTokenType, + BuildID: 1, + Repo: "foo/bar", + } + + tok, _ := tm.MintToken(mto) + + // setup context + gin.SetMode(gin.TestMode) + + resp := httptest.NewRecorder() + context, engine := gin.CreateTestContext(resp) + + // setup database + db, _ := sqlite.NewTest() + + defer func() { + db.Sqlite.Exec("delete from builds") + db.Sqlite.Exec("delete from repos;") + _sql, _ := db.Sqlite.DB() + _sql.Close() + }() + + _ = db.CreateBuild(b) + _ = db.CreateRepo(r) + + context.Request, _ = http.NewRequest(http.MethodGet, "/test/foo/bar/builds/1", nil) + context.Request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", tok)) + + // setup vela mock server + engine.Use(func(c *gin.Context) { c.Set("secret", secret) }) + engine.Use(func(c *gin.Context) { c.Set("token-manager", tm) }) + engine.Use(func(c *gin.Context) { database.ToContext(c, db) }) + engine.Use(claims.Establish()) + engine.Use(user.Establish()) + engine.Use(org.Establish()) + engine.Use(repo.Establish()) + engine.Use(build.Establish()) + engine.Use(MustRead()) + engine.GET("/test/:org/:repo/builds/:build", func(c *gin.Context) { + c.Status(http.StatusOK) + }) + + s1 := httptest.NewServer(engine) + defer s1.Close() + + // run test + engine.ServeHTTP(context.Writer, context.Request) + + if resp.Code != http.StatusOK { + t.Errorf("MustRead returned %v, want %v", resp.Code, http.StatusOK) + } +} + func TestPerm_MustRead_RepoAdmin(t *testing.T) { // setup types secret := "superSecret"