From 09e89c298e5b520b33abf3aa6f2523da2877e6e8 Mon Sep 17 00:00:00 2001 From: Shreya Bhagat Date: Sun, 26 Mar 2023 12:33:05 +0530 Subject: [PATCH 1/6] fix(lib): fix private repo clone bug in docker container --- lib/api/application.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/api/application.go b/lib/api/application.go index 2be9d9db..223d6d48 100644 --- a/lib/api/application.go +++ b/lib/api/application.go @@ -100,7 +100,15 @@ func SetupApplication(app types.Application) types.ResponseError { return types.NewResErr(500, "git init unsuccessful", err) } - _, err = docker.ExecProcess(app.GetContainerID(), []string{"git", "remote", "add", "origin", app.GetGitRepositoryURL()}) + var cloneURL string + if len(app.GetGitAccessToken()) > 0 { + split := strings.Split(app.GetGitRepositoryURL(), "//") + cloneURL = fmt.Sprintf("https://oauth2:%s@%s", app.GetGitAccessToken() , split[1]) + } else { + cloneURL = app.GetGitRepositoryURL() + } + + _, err = docker.ExecProcess(app.GetContainerID(), []string{"git", "remote", "add", "origin", cloneURL}) if err != nil { return types.NewResErr(500, "setting remote unsuccessful", err) } From 8dd79dddb742da2b6d063fef13e6023c2644f14c Mon Sep 17 00:00:00 2001 From: Shreya Bhagat Date: Sat, 25 Mar 2023 17:19:26 +0530 Subject: [PATCH 2/6] feat(*): add endpoint to fetch application remote --- services/master/controllers/application.go | 23 ++++++++++++++++++++++ services/master/routes.go | 1 + types/application.go | 4 ++++ 3 files changed, 28 insertions(+) diff --git a/services/master/controllers/application.go b/services/master/controllers/application.go index d58fc916..28575ced 100644 --- a/services/master/controllers/application.go +++ b/services/master/controllers/application.go @@ -1,6 +1,8 @@ package controllers import ( + "bytes" + "encoding/json" "errors" "fmt" "strconv" @@ -303,3 +305,24 @@ func FetchMetrics(c *gin.Context) { "data": metricsRecord, }) } + +func FetchAppRemote (c *gin.Context) { + appName := c.Param("app") + config, err := mongo.FetchSingleApp(appName) + if err != nil { + c.AbortWithStatusJSON(400, gin.H{ + "success": false, + "error": err.Error(), + }) + } + + response := &types.ApplicationRemote{ + GitURL: config.Git.RepoURL, + } + + fmt.Println(response) + + responseBody := new(bytes.Buffer) + json.NewEncoder(responseBody).Encode(response) + c.Data(200, "application/json", responseBody.Bytes()) +} \ No newline at end of file diff --git a/services/master/routes.go b/services/master/routes.go index e49921a2..1fff15d3 100644 --- a/services/master/routes.go +++ b/services/master/routes.go @@ -72,6 +72,7 @@ func NewService() http.Handler { app.PATCH("/:app/transfer/:user", m.IsAppOwner, c.TransferApplicationOwnership) app.GET("/:app/term", m.IsAppOwner, c.DeployWebTerminal) app.GET("/:app/metrics", m.IsAppOwner, c.FetchMetrics) + app.GET("/:app/remote", m.IsAppOwner, c.FetchAppRemote) } db := router.Group("/dbs") diff --git a/types/application.go b/types/application.go index 5de7ba4d..4640bb9d 100644 --- a/types/application.go +++ b/types/application.go @@ -93,6 +93,10 @@ type ApplicationConfig struct { Success bool `json:"success,omitempty" bson:"-"` } +type ApplicationRemote struct { + GitURL string `json:"giturl" bson:"giturl"` +} + // GetName returns the application's name func (app *ApplicationConfig) GetName() string { return app.Name From 2168bec2af392edff497614afd35dd21c258c3fe Mon Sep 17 00:00:00 2001 From: Shreya Bhagat Date: Tue, 28 Mar 2023 17:26:21 +0530 Subject: [PATCH 3/6] feat(services, types): add endpoint to fetch gasper github user details --- services/master/controllers/application.go | 2 -- services/master/controllers/github.go | 12 ++++++++++++ services/master/routes.go | 7 ++++++- types/application.go | 9 +++++++++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/services/master/controllers/application.go b/services/master/controllers/application.go index 28575ced..5f462166 100644 --- a/services/master/controllers/application.go +++ b/services/master/controllers/application.go @@ -320,8 +320,6 @@ func FetchAppRemote (c *gin.Context) { GitURL: config.Git.RepoURL, } - fmt.Println(response) - responseBody := new(bytes.Buffer) json.NewEncoder(responseBody).Encode(response) c.Data(200, "application/json", responseBody.Bytes()) diff --git a/services/master/controllers/github.go b/services/master/controllers/github.go index 73efd3fd..b851cf4c 100644 --- a/services/master/controllers/github.go +++ b/services/master/controllers/github.go @@ -7,6 +7,7 @@ import ( _ "io/ioutil" "github.com/gin-gonic/gin" + "github.com/sdslabs/gasper/configs" "github.com/sdslabs/gasper/lib/factory" "github.com/sdslabs/gasper/lib/utils" "github.com/sdslabs/gasper/services/master/middlewares" @@ -47,3 +48,14 @@ func CreateRepository(c *gin.Context) { json.NewEncoder(responseBody).Encode(response) c.Data(200, "application/json", responseBody.Bytes()) } + +func FetchPAT (c *gin.Context) { + response := &types.AccessToken{ + PAT: configs.GithubConfig.PAT, + Username: configs.GithubConfig.Username, + Email: configs.GithubConfig.Email, + } + responseBody := new(bytes.Buffer) + json.NewEncoder(responseBody).Encode(response) + c.Data(200, "application/json", responseBody.Bytes()) +} diff --git a/services/master/routes.go b/services/master/routes.go index 1fff15d3..7e555481 100644 --- a/services/master/routes.go +++ b/services/master/routes.go @@ -57,7 +57,12 @@ func NewService() http.Handler { router.GET("/instances", m.AuthRequired(), c.FetchAllInstancesByUser) router.POST("/gctllogin", m.JWTGctl.MiddlewareFunc(), c.GctlLogin) - router.POST("/github", m.AuthRequired(), c.CreateRepository) + + github := router.Group("/github") + { + github.POST("", m.AuthRequired(), c.CreateRepository) + github.GET("/token", m.AuthRequired(), c.FetchPAT) + } app := router.Group("/apps") app.Use(m.AuthRequired()) diff --git a/types/application.go b/types/application.go index 4640bb9d..ec5ad2fd 100644 --- a/types/application.go +++ b/types/application.go @@ -97,6 +97,15 @@ type ApplicationRemote struct { GitURL string `json:"giturl" bson:"giturl"` } +type AccessToken struct { + // PAT for pushing code to repository + PAT string `json:"pat" bson:"pat"` + // Username of Gasper Github user + Username string `json:"username" bson:"username"` + // Email id of Gasper Github user + Email string `json:"email" bson:"email"` +} + // GetName returns the application's name func (app *ApplicationConfig) GetName() string { return app.Name From c85679272fd7d8ac0c71213085b4799a75c2c6fb Mon Sep 17 00:00:00 2001 From: Shreya Bhagat Date: Tue, 11 Apr 2023 22:58:59 +0530 Subject: [PATCH 4/6] refactor(*): remove redundant endpoint --- lib/factory/application_manager.go | 10 +++------- types/application.go | 8 -------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/lib/factory/application_manager.go b/lib/factory/application_manager.go index d407275a..034c361b 100644 --- a/lib/factory/application_manager.go +++ b/lib/factory/application_manager.go @@ -125,7 +125,7 @@ func NewApplicationFactory(bindings pb.ApplicationFactoryServer) *grpc.Server { } // CreateGithubRepository returns a git clone URL after creating a new repository -func CreateGithubRepository(repoName string) (*types.RepositoryResponse, error) { +func CreateGithubRepository(repoName string) (*types.ApplicationRemote, error) { tc := oauth2.NewClient( context.Background(), oauth2.StaticTokenSource( @@ -140,12 +140,8 @@ func CreateGithubRepository(repoName string) (*types.RepositoryResponse, error) Private: github.Bool(true), } repo, _, err := client.Repositories.Create(context.Background(), "", repo) - response := &types.RepositoryResponse{ - CloneURL: *repo.CloneURL, - PAT: configs.GithubConfig.PAT, - Username: configs.GithubConfig.Username, - Repository: repoName, - Email: configs.GithubConfig.Email, + response := &types.ApplicationRemote{ + GitURL: *repo.CloneURL, } return response, err } diff --git a/types/application.go b/types/application.go index ec5ad2fd..84bb960d 100644 --- a/types/application.go +++ b/types/application.go @@ -60,14 +60,6 @@ type RepositoryRequest struct { Name string `json:"name" bson:"name" valid:"required~Field 'name' is required but was not provided,alphanum~Field 'name' should only have alphanumeric characters,stringlength(3|40)~Field 'name' should have length between 3 to 40 characters,lowercase~Field 'name' should have only lowercase characters"` } -type RepositoryResponse struct { - CloneURL string `json:"cloneurl" bson:"cloneurl"` - PAT string `json:"pat" bson:"pat"` - Username string `json:"username" bson:"username"` - Repository string `json:"repository" bson:"repository"` - Email string `json:"email" bson:"email"` -} - // ApplicationConfig is the configuration required for creating an application type ApplicationConfig struct { Name string `json:"name" bson:"name" valid:"required~Field 'name' is required but was not provided,alphanum~Field 'name' should only have alphanumeric characters,stringlength(3|40)~Field 'name' should have length between 3 to 40 characters,lowercase~Field 'name' should have only lowercase characters"` From e97a601631683556edb045165530b4ba178bfc36 Mon Sep 17 00:00:00 2001 From: Shreya Bhagat Date: Wed, 12 Apr 2023 19:16:14 +0530 Subject: [PATCH 5/6] feat(*): implement rsa encryption of PATs --- lib/factory/application_manager.go | 12 ++++++++++++ services/master/controllers/github.go | 26 ++++++++++++++++++++++++-- services/master/routes.go | 2 +- types/application.go | 5 +++++ 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/lib/factory/application_manager.go b/lib/factory/application_manager.go index 034c361b..8354cadf 100644 --- a/lib/factory/application_manager.go +++ b/lib/factory/application_manager.go @@ -2,6 +2,10 @@ package factory import ( "context" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "encoding/base64" "github.com/google/go-github/v41/github" "github.com/sdslabs/gasper/configs" @@ -145,3 +149,11 @@ func CreateGithubRepository(repoName string) (*types.ApplicationRemote, error) { } return response, err } + +func Encrypt(key rsa.PublicKey) (string, error) { + label := []byte("OAEP Encrypted") + rng := rand.Reader + secretMessage := configs.GithubConfig.PAT + ciphertext, err := rsa.EncryptOAEP(sha256.New(), rng, &key, []byte(secretMessage), label) + return base64.StdEncoding.EncodeToString(ciphertext), err +} \ No newline at end of file diff --git a/services/master/controllers/github.go b/services/master/controllers/github.go index b851cf4c..3a834f10 100644 --- a/services/master/controllers/github.go +++ b/services/master/controllers/github.go @@ -49,9 +49,31 @@ func CreateRepository(c *gin.Context) { c.Data(200, "application/json", responseBody.Bytes()) } -func FetchPAT (c *gin.Context) { +func FetchPAT(c *gin.Context) { + raw, err := c.GetRawData() + if err != nil { + c.AbortWithStatusJSON(400, gin.H{ + "success": false, + "error": err.Error(), + }) + } + var data *types.EncryptKey= &types.EncryptKey{} + err = json.Unmarshal(raw, data) + if err != nil { + c.AbortWithStatusJSON(400, gin.H{ + "success": false, + "error": err.Error(), + }) + } + encryptedPAT, err := factory.Encrypt(data.PublicKey) + if err != nil { + c.AbortWithStatusJSON(400, gin.H{ + "success": false, + "error": err.Error(), + }) + } response := &types.AccessToken{ - PAT: configs.GithubConfig.PAT, + PAT: encryptedPAT, Username: configs.GithubConfig.Username, Email: configs.GithubConfig.Email, } diff --git a/services/master/routes.go b/services/master/routes.go index 7e555481..dd3bc54d 100644 --- a/services/master/routes.go +++ b/services/master/routes.go @@ -61,7 +61,7 @@ func NewService() http.Handler { github := router.Group("/github") { github.POST("", m.AuthRequired(), c.CreateRepository) - github.GET("/token", m.AuthRequired(), c.FetchPAT) + github.POST("/token", m.AuthRequired(), c.FetchPAT) } app := router.Group("/apps") diff --git a/types/application.go b/types/application.go index 84bb960d..37add4bd 100644 --- a/types/application.go +++ b/types/application.go @@ -1,6 +1,7 @@ package types import ( + "crypto/rsa" "fmt" "math" "time" @@ -60,6 +61,10 @@ type RepositoryRequest struct { Name string `json:"name" bson:"name" valid:"required~Field 'name' is required but was not provided,alphanum~Field 'name' should only have alphanumeric characters,stringlength(3|40)~Field 'name' should have length between 3 to 40 characters,lowercase~Field 'name' should have only lowercase characters"` } +type EncryptKey struct { + PublicKey rsa.PublicKey `json:"public_key"` +} + // ApplicationConfig is the configuration required for creating an application type ApplicationConfig struct { Name string `json:"name" bson:"name" valid:"required~Field 'name' is required but was not provided,alphanum~Field 'name' should only have alphanumeric characters,stringlength(3|40)~Field 'name' should have length between 3 to 40 characters,lowercase~Field 'name' should have only lowercase characters"` From c4fd532cf767548f3e43fbcaa27a68377994caa4 Mon Sep 17 00:00:00 2001 From: Shreya Bhagat Date: Wed, 12 Apr 2023 19:45:48 +0530 Subject: [PATCH 6/6] cleanup(*): fix comments and whitespaces --- lib/factory/application_manager.go | 3 ++- services/master/controllers/application.go | 3 ++- types/application.go | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/factory/application_manager.go b/lib/factory/application_manager.go index 8354cadf..d466b661 100644 --- a/lib/factory/application_manager.go +++ b/lib/factory/application_manager.go @@ -150,10 +150,11 @@ func CreateGithubRepository(repoName string) (*types.ApplicationRemote, error) { return response, err } +// Encrypt encrypts the PAT using the public key func Encrypt(key rsa.PublicKey) (string, error) { label := []byte("OAEP Encrypted") rng := rand.Reader secretMessage := configs.GithubConfig.PAT ciphertext, err := rsa.EncryptOAEP(sha256.New(), rng, &key, []byte(secretMessage), label) return base64.StdEncoding.EncodeToString(ciphertext), err -} \ No newline at end of file +} diff --git a/services/master/controllers/application.go b/services/master/controllers/application.go index 5f462166..e4b9d86b 100644 --- a/services/master/controllers/application.go +++ b/services/master/controllers/application.go @@ -306,7 +306,8 @@ func FetchMetrics(c *gin.Context) { }) } -func FetchAppRemote (c *gin.Context) { +// FetchAppRemote retrieves the remote URL of an application +func FetchAppRemote(c *gin.Context) { appName := c.Param("app") config, err := mongo.FetchSingleApp(appName) if err != nil { diff --git a/types/application.go b/types/application.go index 37add4bd..8621a760 100644 --- a/types/application.go +++ b/types/application.go @@ -57,10 +57,12 @@ type Resources struct { CPU float64 `json:"cpu" bson:"cpu" valid:"float~Field 'cpu' inside field 'resources' should be of type float"` } +//RepositoryRequest is the request body for containing name of the application for repository creation type RepositoryRequest struct { Name string `json:"name" bson:"name" valid:"required~Field 'name' is required but was not provided,alphanum~Field 'name' should only have alphanumeric characters,stringlength(3|40)~Field 'name' should have length between 3 to 40 characters,lowercase~Field 'name' should have only lowercase characters"` } +//EncryptKey is the request body for containing public key for encrypting the access token type EncryptKey struct { PublicKey rsa.PublicKey `json:"public_key"` } @@ -90,10 +92,12 @@ type ApplicationConfig struct { Success bool `json:"success,omitempty" bson:"-"` } +// ApplicationRemote is the struct containing the remote URL of the application's git repository type ApplicationRemote struct { GitURL string `json:"giturl" bson:"giturl"` } +//AccessToken is the struct containing the access token for the application's git repository type AccessToken struct { // PAT for pushing code to repository PAT string `json:"pat" bson:"pat"`