portersupport 4 лет назад
Родитель
Сommit
8f820be74c
28 измененных файлов с 663 добавлено и 403 удалено
  1. 2 0
      .github/workflows/dev.yaml
  2. 1 0
      .github/workflows/staging.yaml
  3. 0 10
      api/server/authz/policy/loader.go
  4. 23 5
      api/server/handlers/oauth_callback/gitlab.go
  5. 11 0
      api/server/handlers/project_integration/create_gitlab.go
  6. 31 4
      api/server/handlers/project_integration/get_gitlab_repo_buildpack.go
  7. 14 2
      api/server/handlers/project_integration/get_gitlab_repo_contents.go
  8. 14 2
      api/server/handlers/project_integration/get_gitlab_repo_procfile.go
  9. 0 1
      api/server/handlers/project_integration/list_git.go
  10. 14 2
      api/server/handlers/project_integration/list_gitlab_repo_branches.go
  11. 16 3
      api/server/handlers/project_integration/list_gitlab_repos.go
  12. 1 9
      api/server/handlers/release/create.go
  13. 0 2
      api/server/handlers/release/get_gha_template.go
  14. 22 11
      dashboard/src/components/repo-selector/ContentsList.tsx
  15. 7 3
      dashboard/src/components/repo-selector/RepoList.tsx
  16. 1 1
      dashboard/src/main/home/WelcomeForm.tsx
  17. 2 2
      dashboard/src/main/home/integrations/Integrations.tsx
  18. 1 1
      dashboard/src/shared/api.tsx
  19. 3 1
      ee/integrations/vault/vault.go
  20. 85 0
      ee/migrate/migrate_vault.go
  21. 132 129
      internal/integrations/buildpacks/nodejs.go
  22. 255 63
      internal/integrations/ci/gitlab/ci.go
  23. 4 9
      internal/models/integrations/oauth.go
  24. 10 2
      internal/oauth/config.go
  25. 9 0
      internal/repository/credentials/credentials.go
  26. 5 125
      internal/repository/gorm/auth.go
  27. 0 1
      internal/repository/integrations.go
  28. 0 15
      internal/repository/test/auth.go

+ 2 - 0
.github/workflows/dev.yaml

@@ -40,6 +40,7 @@ jobs:
           ENABLE_SENTRY=true
           ENABLE_SENTRY=true
           SENTRY_DSN=${{secrets.SENTRY_DSN}}
           SENTRY_DSN=${{secrets.SENTRY_DSN}}
           SENTRY_ENV=frontend-development
           SENTRY_ENV=frontend-development
+          ENABLE_GITLAB=true
           EOL
           EOL
       - name: Build
       - name: Build
         run: |
         run: |
@@ -117,6 +118,7 @@ jobs:
         ENABLE_SENTRY=true
         ENABLE_SENTRY=true
         SENTRY_DSN=${{secrets.SENTRY_DSN}}
         SENTRY_DSN=${{secrets.SENTRY_DSN}}
         SENTRY_ENV=frontend-development
         SENTRY_ENV=frontend-development
+        ENABLE_GITLAB=true
         EOL
         EOL
     - name: Build
     - name: Build
       run: |
       run: |

+ 1 - 0
.github/workflows/staging.yaml

@@ -47,6 +47,7 @@ jobs:
           ENABLE_SENTRY=true
           ENABLE_SENTRY=true
           SENTRY_DSN=${{secrets.SENTRY_DSN}}
           SENTRY_DSN=${{secrets.SENTRY_DSN}}
           SENTRY_ENV=frontend-staging
           SENTRY_ENV=frontend-staging
+          ENABLE_GITLAB=true
           EOL
           EOL
       - name: Build
       - name: Build
         run: |
         run: |

+ 0 - 10
api/server/authz/policy/loader.go

@@ -40,16 +40,6 @@ func (b *RepoPolicyDocumentLoader) LoadPolicyDocuments(
 			return nil, apierrors.NewErrForbidden(fmt.Errorf("project id %d does not match token id %d", opts.ProjectID, opts.ProjectToken.ProjectID))
 			return nil, apierrors.NewErrForbidden(fmt.Errorf("project id %d does not match token id %d", opts.ProjectID, opts.ProjectToken.ProjectID))
 		}
 		}
 
 
-		proj, err := b.projRepo.ReadProject(opts.ProjectID)
-
-		if err != nil {
-			return nil, apierrors.NewErrForbidden(fmt.Errorf("error fetching project: %w", err))
-		}
-
-		if !proj.APITokensEnabled {
-			return nil, apierrors.NewErrForbidden(fmt.Errorf("api tokens are not enabled for this project"))
-		}
-
 		// load the policy
 		// load the policy
 		apiPolicy, reqErr := GetAPIPolicyFromUID(b.policyRepo, opts.ProjectToken.ProjectID, opts.ProjectToken.PolicyUID)
 		apiPolicy, reqErr := GetAPIPolicyFromUID(b.policyRepo, opts.ProjectToken.ProjectID, opts.ProjectToken.PolicyUID)
 
 

+ 23 - 5
api/server/handlers/oauth_callback/gitlab.go

@@ -12,6 +12,7 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/commonutils"
 	"github.com/porter-dev/porter/api/server/shared/commonutils"
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/internal/models/integrations"
 	"github.com/porter-dev/porter/internal/models/integrations"
 	"gorm.io/gorm"
 	"gorm.io/gorm"
 )
 )
@@ -80,19 +81,36 @@ func (p *OAuthCallbackGitlabHandler) ServeHTTP(w http.ResponseWriter, r *http.Re
 		return
 		return
 	}
 	}
 
 
-	oauthInt := &integrations.GitlabAppOAuthIntegration{
+	oauthInt := &integrations.OAuthIntegration{
 		SharedOAuthModel: integrations.SharedOAuthModel{
 		SharedOAuthModel: integrations.SharedOAuthModel{
 			AccessToken:  []byte(token.AccessToken),
 			AccessToken:  []byte(token.AccessToken),
 			RefreshToken: []byte(token.RefreshToken),
 			RefreshToken: []byte(token.RefreshToken),
 			Expiry:       token.Expiry,
 			Expiry:       token.Expiry,
 		},
 		},
-		UserID:        userID,
-		ProjectID:     projID,
-		IntegrationID: integrationID,
+		Client:    types.OAuthGitlab,
+		UserID:    userID,
+		ProjectID: projID,
+	}
+
+	oauthInt, err = p.Repo().OAuthIntegration().CreateOAuthIntegration(oauthInt)
+
+	if err != nil {
+		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	if oauthInt.ID == 0 {
+		p.HandleAPIError(w, r, apierrors.NewErrInternal(fmt.Errorf("error creating oauth integration for gitlab")))
+		return
+	}
+
+	giOAuthInt := &integrations.GitlabAppOAuthIntegration{
+		OAuthIntegrationID:  oauthInt.ID,
+		GitlabIntegrationID: integrationID,
 	}
 	}
 
 
 	// create the oauth integration first
 	// create the oauth integration first
-	_, err = p.Repo().GitlabAppOAuthIntegration().CreateGitlabAppOAuthIntegration(oauthInt)
+	_, err = p.Repo().GitlabAppOAuthIntegration().CreateGitlabAppOAuthIntegration(giOAuthInt)
 
 
 	if err != nil {
 	if err != nil {
 		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
 		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))

+ 11 - 0
api/server/handlers/project_integration/create_gitlab.go

@@ -1,7 +1,9 @@
 package project_integration
 package project_integration
 
 
 import (
 import (
+	"fmt"
 	"net/http"
 	"net/http"
+	"net/url"
 
 
 	"github.com/porter-dev/porter/api/server/handlers"
 	"github.com/porter-dev/porter/api/server/handlers"
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared"
@@ -35,6 +37,15 @@ func (p *CreateGitlabIntegration) ServeHTTP(w http.ResponseWriter, r *http.Reque
 		return
 		return
 	}
 	}
 
 
+	_, err := url.Parse(request.InstanceURL)
+
+	if err != nil {
+		p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(
+			fmt.Errorf("malformed gitlab instance URL"), http.StatusBadRequest,
+		))
+		return
+	}
+
 	gitlabIntegration, err := p.Repo().GitlabIntegration().CreateGitlabIntegration(&ints.GitlabIntegration{
 	gitlabIntegration, err := p.Repo().GitlabIntegration().CreateGitlabIntegration(&ints.GitlabIntegration{
 		ProjectID:       project.ID,
 		ProjectID:       project.ID,
 		InstanceURL:     request.InstanceURL,
 		InstanceURL:     request.InstanceURL,

+ 31 - 4
api/server/handlers/project_integration/get_gitlab_repo_buildpack.go

@@ -4,6 +4,8 @@ import (
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
 	"net/http"
 	"net/http"
+	"net/url"
+	"strings"
 	"sync"
 	"sync"
 
 
 	"github.com/porter-dev/porter/api/server/handlers"
 	"github.com/porter-dev/porter/api/server/handlers"
@@ -98,9 +100,21 @@ func (p *GetGitlabRepoBuildpackHandler) ServeHTTP(w http.ResponseWriter, r *http
 		return
 		return
 	}
 	}
 
 
-	accessToken, _, err := oauth.GetAccessToken(giAppOAuth.SharedOAuthModel, commonutils.GetGitlabOAuthConf(
+	oauthInt, err := p.Repo().OAuthIntegration().ReadOAuthIntegration(project.ID, giAppOAuth.OAuthIntegrationID)
+
+	if err != nil {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(fmt.Errorf("unauthorized gitlab user"), http.StatusUnauthorized))
+			return
+		}
+
+		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	accessToken, _, err := oauth.GetAccessToken(oauthInt.SharedOAuthModel, commonutils.GetGitlabOAuthConf(
 		p.Config(), gi,
 		p.Config(), gi,
-	), oauth.MakeUpdateGitlabAppOAuthIntegrationFunction(giAppOAuth, p.Repo()))
+	), oauth.MakeUpdateGitlabAppOAuthIntegrationFunction(project.ID, giAppOAuth, p.Repo()))
 
 
 	if err != nil {
 	if err != nil {
 		p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(fmt.Errorf("invalid gitlab access token"),
 		p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(fmt.Errorf("invalid gitlab access token"),
@@ -115,8 +129,21 @@ func (p *GetGitlabRepoBuildpackHandler) ServeHTTP(w http.ResponseWriter, r *http
 		return
 		return
 	}
 	}
 
 
+	dir, err := url.QueryUnescape(request.Dir)
+
+	if err != nil {
+		p.HandleAPIError(w, r, apierrors.NewErrForbidden(fmt.Errorf("malformed query param dir")))
+		return
+	}
+
+	dir = strings.TrimPrefix(dir, "./")
+
+	if len(dir) == 0 {
+		dir = "."
+	}
+
 	tree, resp, err := client.Repositories.ListTree(fmt.Sprintf("%s/%s", owner, name), &gitlab.ListTreeOptions{
 	tree, resp, err := client.Repositories.ListTree(fmt.Sprintf("%s/%s", owner, name), &gitlab.ListTreeOptions{
-		Path: gitlab.String(request.Dir),
+		Path: gitlab.String(dir),
 		Ref:  gitlab.String(branch),
 		Ref:  gitlab.String(branch),
 	})
 	})
 
 
@@ -145,7 +172,7 @@ func (p *GetGitlabRepoBuildpackHandler) ServeHTTP(w http.ResponseWriter, r *http
 				}
 				}
 			}()
 			}()
 			buildpacks.Runtimes[idx].DetectGitlab(
 			buildpacks.Runtimes[idx].DetectGitlab(
-				client, tree, owner, name, request.Dir, branch,
+				client, tree, owner, name, dir, branch,
 				builderInfoMap[buildpacks.PaketoBuilder], builderInfoMap[buildpacks.HerokuBuilder],
 				builderInfoMap[buildpacks.PaketoBuilder], builderInfoMap[buildpacks.HerokuBuilder],
 			)
 			)
 			wg.Done()
 			wg.Done()

+ 14 - 2
api/server/handlers/project_integration/get_gitlab_repo_contents.go

@@ -111,9 +111,21 @@ func (p *GetGitlabRepoContentsHandler) ServeHTTP(w http.ResponseWriter, r *http.
 		return
 		return
 	}
 	}
 
 
-	accessToken, _, err := oauth.GetAccessToken(giAppOAuth.SharedOAuthModel, commonutils.GetGitlabOAuthConf(
+	oauthInt, err := p.Repo().OAuthIntegration().ReadOAuthIntegration(project.ID, giAppOAuth.OAuthIntegrationID)
+
+	if err != nil {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(fmt.Errorf("unauthorized gitlab user"), http.StatusUnauthorized))
+			return
+		}
+
+		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	accessToken, _, err := oauth.GetAccessToken(oauthInt.SharedOAuthModel, commonutils.GetGitlabOAuthConf(
 		p.Config(), gi,
 		p.Config(), gi,
-	), oauth.MakeUpdateGitlabAppOAuthIntegrationFunction(giAppOAuth, p.Repo()))
+	), oauth.MakeUpdateGitlabAppOAuthIntegrationFunction(project.ID, giAppOAuth, p.Repo()))
 
 
 	if err != nil {
 	if err != nil {
 		p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(fmt.Errorf("invalid gitlab access token"),
 		p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(fmt.Errorf("invalid gitlab access token"),

+ 14 - 2
api/server/handlers/project_integration/get_gitlab_repo_procfile.go

@@ -108,9 +108,21 @@ func (p *GetGitlabRepoProcfileHandler) ServeHTTP(w http.ResponseWriter, r *http.
 		return
 		return
 	}
 	}
 
 
-	accessToken, _, err := oauth.GetAccessToken(giAppOAuth.SharedOAuthModel, commonutils.GetGitlabOAuthConf(
+	oauthInt, err := p.Repo().OAuthIntegration().ReadOAuthIntegration(project.ID, giAppOAuth.OAuthIntegrationID)
+
+	if err != nil {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(fmt.Errorf("unauthorized gitlab user"), http.StatusUnauthorized))
+			return
+		}
+
+		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	accessToken, _, err := oauth.GetAccessToken(oauthInt.SharedOAuthModel, commonutils.GetGitlabOAuthConf(
 		p.Config(), gi,
 		p.Config(), gi,
-	), oauth.MakeUpdateGitlabAppOAuthIntegrationFunction(giAppOAuth, p.Repo()))
+	), oauth.MakeUpdateGitlabAppOAuthIntegrationFunction(project.ID, giAppOAuth, p.Repo()))
 
 
 	if err != nil {
 	if err != nil {
 		p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(fmt.Errorf("invalid gitlab access token"),
 		p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(fmt.Errorf("invalid gitlab access token"),

+ 0 - 1
api/server/handlers/project_integration/list_git.go

@@ -67,7 +67,6 @@ func (p *ListGitIntegrationHandler) ServeHTTP(w http.ResponseWriter, r *http.Req
 	ghAuthUser, _, err := client.Users.Get(context.Background(), "")
 	ghAuthUser, _, err := client.Users.Get(context.Background(), "")
 
 
 	if err != nil {
 	if err != nil {
-		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
 		p.WriteResult(w, r, res)
 		p.WriteResult(w, r, res)
 		return
 		return
 	}
 	}

+ 14 - 2
api/server/handlers/project_integration/list_gitlab_repo_branches.go

@@ -81,9 +81,21 @@ func (p *ListGitlabRepoBranchesHandler) ServeHTTP(w http.ResponseWriter, r *http
 		return
 		return
 	}
 	}
 
 
-	accessToken, _, err := oauth.GetAccessToken(giAppOAuth.SharedOAuthModel, commonutils.GetGitlabOAuthConf(
+	oauthInt, err := p.Repo().OAuthIntegration().ReadOAuthIntegration(project.ID, giAppOAuth.OAuthIntegrationID)
+
+	if err != nil {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(fmt.Errorf("unauthorized gitlab user"), http.StatusUnauthorized))
+			return
+		}
+
+		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	accessToken, _, err := oauth.GetAccessToken(oauthInt.SharedOAuthModel, commonutils.GetGitlabOAuthConf(
 		p.Config(), gi,
 		p.Config(), gi,
-	), oauth.MakeUpdateGitlabAppOAuthIntegrationFunction(giAppOAuth, p.Repo()))
+	), oauth.MakeUpdateGitlabAppOAuthIntegrationFunction(project.ID, giAppOAuth, p.Repo()))
 
 
 	if err != nil {
 	if err != nil {
 		p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(fmt.Errorf("invalid gitlab access token"),
 		p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(fmt.Errorf("invalid gitlab access token"),

+ 16 - 3
api/server/handlers/project_integration/list_gitlab_repos.go

@@ -67,9 +67,21 @@ func (p *ListGitlabReposHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
 		return
 		return
 	}
 	}
 
 
-	accessToken, _, err := oauth.GetAccessToken(giAppOAuth.SharedOAuthModel, commonutils.GetGitlabOAuthConf(
+	oauthInt, err := p.Repo().OAuthIntegration().ReadOAuthIntegration(project.ID, giAppOAuth.OAuthIntegrationID)
+
+	if err != nil {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(fmt.Errorf("unauthorized gitlab user"), http.StatusUnauthorized))
+			return
+		}
+
+		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	accessToken, _, err := oauth.GetAccessToken(oauthInt.SharedOAuthModel, commonutils.GetGitlabOAuthConf(
 		p.Config(), gi,
 		p.Config(), gi,
-	), oauth.MakeUpdateGitlabAppOAuthIntegrationFunction(giAppOAuth, p.Repo()))
+	), oauth.MakeUpdateGitlabAppOAuthIntegrationFunction(project.ID, giAppOAuth, p.Repo()))
 
 
 	if err != nil {
 	if err != nil {
 		p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(fmt.Errorf("invalid gitlab access token"),
 		p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(fmt.Errorf("invalid gitlab access token"),
@@ -85,7 +97,8 @@ func (p *ListGitlabReposHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
 	}
 	}
 
 
 	giProjects, resp, err := client.Projects.ListProjects(&gitlab.ListProjectsOptions{
 	giProjects, resp, err := client.Projects.ListProjects(&gitlab.ListProjectsOptions{
-		Simple: gitlab.Bool(true),
+		Simple:     gitlab.Bool(true),
+		Membership: gitlab.Bool(true),
 	})
 	})
 
 
 	if resp.StatusCode == http.StatusUnauthorized {
 	if resp.StatusCode == http.StatusUnauthorized {

+ 1 - 9
api/server/handlers/release/create.go

@@ -47,7 +47,6 @@ func NewCreateReleaseHandler(
 
 
 func (c *CreateReleaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 func (c *CreateReleaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	user, _ := r.Context().Value(types.UserScope).(*models.User)
 	user, _ := r.Context().Value(types.UserScope).(*models.User)
-	proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
 	cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
 	cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
 	namespace := r.Context().Value(types.NamespaceScope).(string)
 	namespace := r.Context().Value(types.NamespaceScope).(string)
 	operationID := oauth.CreateRandomState()
 	operationID := oauth.CreateRandomState()
@@ -167,7 +166,6 @@ func (c *CreateReleaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 	if request.GitActionConfig != nil {
 	if request.GitActionConfig != nil {
 		_, _, err := createGitAction(
 		_, _, err := createGitAction(
 			c.Config(),
 			c.Config(),
-			proj,
 			user.ID,
 			user.ID,
 			cluster.ProjectID,
 			cluster.ProjectID,
 			cluster.ID,
 			cluster.ID,
@@ -246,7 +244,6 @@ func createReleaseFromHelmRelease(
 
 
 func createGitAction(
 func createGitAction(
 	config *config.Config,
 	config *config.Config,
-	project *models.Project,
 	userID, projectID, clusterID uint,
 	userID, projectID, clusterID uint,
 	request *types.CreateGitActionConfigRequest,
 	request *types.CreateGitActionConfigRequest,
 	name, namespace string,
 	name, namespace string,
@@ -288,7 +285,7 @@ func createGitAction(
 
 
 	// if this isn't a dry run, generate the token
 	// if this isn't a dry run, generate the token
 	if !isDryRun {
 	if !isDryRun {
-		encoded, err = getToken(config, project, userID, projectID, clusterID, request)
+		encoded, err = getToken(config, userID, projectID, clusterID, request)
 
 
 		if err != nil {
 		if err != nil {
 			return nil, nil, err
 			return nil, nil, err
@@ -394,7 +391,6 @@ func createGitAction(
 
 
 func getToken(
 func getToken(
 	config *config.Config,
 	config *config.Config,
-	proj *models.Project,
 	userID, projectID, clusterID uint,
 	userID, projectID, clusterID uint,
 	request *types.CreateGitActionConfigRequest,
 	request *types.CreateGitActionConfigRequest,
 ) (string, error) {
 ) (string, error) {
@@ -480,10 +476,6 @@ func getToken(
 		SecretKey:       hashedToken,
 		SecretKey:       hashedToken,
 	}
 	}
 
 
-	if !proj.APITokensEnabled {
-		return "", fmt.Errorf("api tokens are not enabled for this project")
-	}
-
 	apiToken, err = config.Repo.APIToken().CreateAPIToken(apiToken)
 	apiToken, err = config.Repo.APIToken().CreateAPIToken(apiToken)
 
 
 	if err != nil {
 	if err != nil {

+ 0 - 2
api/server/handlers/release/get_gha_template.go

@@ -27,7 +27,6 @@ func NewGetGHATemplateHandler(
 
 
 func (c *GetGHATemplateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 func (c *GetGHATemplateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	user, _ := r.Context().Value(types.UserScope).(*models.User)
 	user, _ := r.Context().Value(types.UserScope).(*models.User)
-	proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
 	cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
 	cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
 	namespace := r.Context().Value(types.NamespaceScope).(string)
 	namespace := r.Context().Value(types.NamespaceScope).(string)
 
 
@@ -39,7 +38,6 @@ func (c *GetGHATemplateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request
 
 
 	_, workflowYAML, err := createGitAction(
 	_, workflowYAML, err := createGitAction(
 		c.Config(),
 		c.Config(),
-		proj,
 		user.ID,
 		user.ID,
 		cluster.ProjectID,
 		cluster.ProjectID,
 		cluster.ID,
 		cluster.ID,

+ 22 - 11
dashboard/src/components/repo-selector/ContentsList.tsx

@@ -68,17 +68,28 @@ export default class ContentsList extends Component<PropsType, StateType> {
     const { actionConfig, branch } = this.props;
     const { actionConfig, branch } = this.props;
 
 
     if (actionConfig.kind === "gitlab") {
     if (actionConfig.kind === "gitlab") {
-      return api.getGitlabFolderContent(
-        "<token>",
-        { dir: this.state.currentDir || "./" },
-        {
-          project_id: currentProject.id,
-          integration_id: actionConfig.gitlab_integration_id,
-          repo_owner: actionConfig.git_repo.split("/")[0],
-          repo_name: actionConfig.git_repo.split("/")[1],
-          branch: branch,
-        }
-      );
+      return api
+        .getGitlabFolderContent(
+          "<token>",
+          { dir: this.state.currentDir || "./" },
+          {
+            project_id: currentProject.id,
+            integration_id: actionConfig.gitlab_integration_id,
+            repo_owner: actionConfig.git_repo.split("/")[0],
+            repo_name: actionConfig.git_repo.split("/")[1],
+            branch: branch,
+          }
+        )
+        .then((res) => {
+          const { data } = res;
+
+          return {
+            data: data.map((x: FileType) => ({
+              ...x,
+              type: x.type === "tree" ? "dir" : "file",
+            })),
+          };
+        });
     }
     }
     return api.getBranchContents(
     return api.getBranchContents(
       "<token>",
       "<token>",

+ 7 - 3
dashboard/src/components/repo-selector/RepoList.tsx

@@ -175,7 +175,7 @@ const RepoList: React.FC<Props> = ({
           <LoadingWrapper>
           <LoadingWrapper>
             GitLab could not be reached.
             GitLab could not be reached.
             <A
             <A
-              to={`/api/projects/${currentProject.id}/oauth/gitlab?integration_id=${currentProvider.integration_id}`}
+              to={`${window.location.origin}/api/projects/${currentProject.id}/oauth/gitlab?integration_id=${currentProvider.integration_id}`}
             >
             >
               Connect your GitLab account to Porter
               Connect your GitLab account to Porter
             </A>
             </A>
@@ -186,7 +186,9 @@ const RepoList: React.FC<Props> = ({
         return (
         return (
           <LoadingWrapper>
           <LoadingWrapper>
             No connected Github repos found. You can
             No connected Github repos found. You can
-            <A to={"/api/integrations/github-app/install"}>
+            <A
+              to={`${window.location.origin}/api/integrations/github-app/install`}
+            >
               Install Porter in more repositories
               Install Porter in more repositories
             </A>
             </A>
             or select another git provider.
             or select another git provider.
@@ -278,7 +280,9 @@ const RepoList: React.FC<Props> = ({
                 <div>We couldn't find any git provider to connect with.</div>
                 <div>We couldn't find any git provider to connect with.</div>
                 <div>
                 <div>
                   You can
                   You can
-                  <A to={"/api/integrations/github-app/install"}>
+                  <A
+                    to={`${window.location.origin}/api/integrations/github-app/install`}
+                  >
                     add GitHub repositories
                     add GitHub repositories
                   </A>
                   </A>
                   or
                   or

+ 1 - 1
dashboard/src/main/home/WelcomeForm.tsx

@@ -46,7 +46,7 @@ const WelcomeForm: React.FunctionComponent<Props> = ({}) => {
           <Title>Welcome to Porter</Title>
           <Title>Welcome to Porter</Title>
           <Subtitle>Just two things before getting started.</Subtitle>
           <Subtitle>Just two things before getting started.</Subtitle>
           <SubtitleAlt>
           <SubtitleAlt>
-            <Num>1</Num> What is your company URL? *
+            <Num>1</Num> What is your company website? *
           </SubtitleAlt>
           </SubtitleAlt>
           <Input
           <Input
             placeholder="ex: https://porter.run"
             placeholder="ex: https://porter.run"

+ 2 - 2
dashboard/src/main/home/integrations/Integrations.tsx

@@ -15,7 +15,7 @@ type PropsType = RouteComponentProps;
 const IntegrationCategoryStrings = [
 const IntegrationCategoryStrings = [
   "registry",
   "registry",
   "slack",
   "slack",
-  "gitlab",
+  ...(process.env.ENABLE_GITLAB ? ["gitlab"] : []),
 ]; /*"kubernetes",*/
 ]; /*"kubernetes",*/
 
 
 const Integrations: React.FC<PropsType> = (props) => {
 const Integrations: React.FC<PropsType> = (props) => {
@@ -73,7 +73,7 @@ const Integrations: React.FC<PropsType> = (props) => {
 
 
             <IntegrationList
             <IntegrationList
               currentCategory={""}
               currentCategory={""}
-              integrations={["registry", "slack", "gitlab"]}
+              integrations={IntegrationCategoryStrings}
               setCurrent={(x) =>
               setCurrent={(x) =>
                 pushFiltered(props, `/integrations/${x}`, ["project_id"])
                 pushFiltered(props, `/integrations/${x}`, ["project_id"])
               }
               }

+ 1 - 1
dashboard/src/shared/api.tsx

@@ -570,7 +570,7 @@ const getGitlabProcfileContents = baseApi<
 >(
 >(
   "GET",
   "GET",
   ({ project_id, integration_id, owner, name, branch }) =>
   ({ project_id, integration_id, owner, name, branch }) =>
-    `/projects/${project_id}/integrations/gitlab/${integration_id}/${owner}/${name}/${encodeURIComponent(
+    `/api/projects/${project_id}/integrations/gitlab/${integration_id}/repos/${owner}/${name}/${encodeURIComponent(
       branch
       branch
     )}/procfile`
     )}/procfile`
 );
 );

+ 3 - 1
ee/integrations/vault/vault.go

@@ -194,7 +194,9 @@ func (c *Client) WriteGitlabCredential(giIntegration *integrations.GitlabIntegra
 	return c.postRequest(fmt.Sprintf("/v1/%s", c.getGitlabCredentialPath(giIntegration)), reqData, nil)
 	return c.postRequest(fmt.Sprintf("/v1/%s", c.getGitlabCredentialPath(giIntegration)), reqData, nil)
 }
 }
 
 
-func (c *Client) GetGitlabCredential(giIntegration *integrations.GitlabIntegration) (*credentials.GitlabCredential, error) {
+func (c *Client) GetGitlabCredential(
+	giIntegration *integrations.GitlabIntegration,
+) (*credentials.GitlabCredential, error) {
 	resp := &GetGitlabCredentialResponse{}
 	resp := &GetGitlabCredentialResponse{}
 
 
 	err := c.getRequest(fmt.Sprintf("/v1/%s", c.getGitlabCredentialPath(giIntegration)), resp)
 	err := c.getRequest(fmt.Sprintf("/v1/%s", c.getGitlabCredentialPath(giIntegration)), resp)

+ 85 - 0
ee/migrate/migrate_vault.go

@@ -1,3 +1,4 @@
+//go:build ee
 // +build ee
 // +build ee
 
 
 package migrate
 package migrate
@@ -52,6 +53,14 @@ func MigrateVault(db *gorm.DB, dbConf *env.DBConf, shouldFinalize bool) error {
 		return err
 		return err
 	}
 	}
 
 
+	err = migrateGitlabIntegrationModel(db, vaultClient, shouldFinalize)
+
+	if err != nil {
+		fmt.Printf("failed on gitlab migration: %v\n", err)
+
+		return err
+	}
+
 	return nil
 	return nil
 }
 }
 
 
@@ -286,3 +295,79 @@ func migrateAWSIntegrationModel(db *gorm.DB, client *vault.Client, shouldFinaliz
 
 
 	return nil
 	return nil
 }
 }
+
+func migrateGitlabIntegrationModel(db *gorm.DB, client *vault.Client, shouldFinalize bool) error {
+	// get count of model
+	var count int64
+
+	if err := db.Model(&ints.GitlabIntegration{}).Count(&count).Error; err != nil {
+		return err
+	}
+
+	// make a map of ids to errors -- we don't clear the integrations with errors
+	errors := make(map[uint]error)
+
+	// iterate (count / stepSize) + 1 times using Limit and Offset
+	for i := 0; i < (int(count)/stepSize)+1; i++ {
+		giInts := []*ints.GitlabIntegration{}
+
+		if err := db.Order("id asc").Offset(i * stepSize).Limit(stepSize).Find(&giInts).Error; err != nil {
+			return err
+		}
+
+		// decrypt with the old key
+		for _, gi := range giInts {
+			// Check if record already exists in vault client. If so, we don't write anything to vault,
+			// since we don't want to overwrite any data that's been written.
+			if resp, _ := client.GetGitlabCredential(gi); resp != nil {
+				continue
+			}
+
+			// write the data to the vault client
+			if err := client.WriteGitlabCredential(gi, &credentials.GitlabCredential{
+				AppClientID:     gi.AppClientID,
+				AppClientSecret: gi.AppClientSecret,
+			}); err != nil {
+				errors[gi.ID] = err
+				fmt.Printf("gitlab vault write error on ID %d: %v\n", gi.ID, err)
+			}
+		}
+	}
+
+	fmt.Printf("migrated %d gitlab integrations with %d errors\n", count, len(errors))
+
+	if shouldFinalize {
+		saveErrors := make(map[uint]error, 0)
+
+		// iterate a second time, clearing the data
+		// iterate (count / stepSize) + 1 times using Limit and Offset
+		for i := 0; i < (int(count)/stepSize)+1; i++ {
+			giInts := []*ints.GitlabIntegration{}
+
+			if err := db.Order("id asc").Offset(i * stepSize).Limit(stepSize).Find(&giInts).Error; err != nil {
+				return err
+			}
+
+			// decrypt with the old key
+			for _, gi := range giInts {
+				if _, found := errors[gi.ID]; !found {
+					// clear the data from the db, and save
+					gi.AppClientID = []byte{}
+					gi.AppClientSecret = []byte{}
+
+					if err := db.Save(gi).Error; err != nil {
+						saveErrors[gi.ID] = err
+					}
+				}
+			}
+		}
+
+		fmt.Printf("cleared %d gitlab integrations with %d errors\n", count, len(saveErrors))
+
+		for saveErrorID, saveError := range saveErrors {
+			fmt.Printf("gitlab save error on ID %d: %v\n", saveErrorID, saveError)
+		}
+	}
+
+	return nil
+}

+ 132 - 129
internal/integrations/buildpacks/nodejs.go

@@ -440,135 +440,138 @@ func (runtime *nodejsRuntime) DetectGitlab(
 		return nil
 		return nil
 	}
 	}
 
 
-	foundYarn := false
-	foundNPM := false
-	foundStandalone := false
-	for result := range results {
-		if result.string == yarn {
-			foundYarn = true
-		} else if result.string == npm {
-			foundNPM = true
-		} else if result.string == standalone {
-			foundStandalone = true
-		}
-	}
-
-	if foundYarn || foundNPM {
-		// it is safe to assume that the project contains a package.json
-		fileContent, _, err := client.RepositoryFiles.GetRawFile(
-			fmt.Sprintf("%s/%s", owner, name), fmt.Sprintf("%s/package.json", path),
-			&gitlab.GetRawFileOptions{
-				Ref: gitlab.String(ref),
-			},
-		)
-		if err != nil {
-			paketo.Others = append(paketo.Others, paketoBuildpackInfo)
-			heroku.Others = append(heroku.Others, herokuBuildpackInfo)
-			return fmt.Errorf("error fetching contents of package.json: %v", err)
-		}
-		var packageJSON struct {
-			Scripts map[string]string `json:"scripts"`
-			Engines struct {
-				Node string `json:"node"`
-			} `json:"engines"`
-		}
-
-		data := string(fileContent)
-
-		err = json.NewDecoder(strings.NewReader(data)).Decode(&packageJSON)
-		if err != nil {
-			paketo.Others = append(paketo.Others, paketoBuildpackInfo)
-			heroku.Others = append(heroku.Others, herokuBuildpackInfo)
-			return fmt.Errorf("error decoding package.json contents to struct: %v", err)
-		}
-
-		if packageJSON.Engines.Node == "" {
-			// we should now check for the node engine version in .nvmrc and then .node-version
-			nvmrcFound := false
-			nodeVersionFound := false
-			for i := 0; i < len(tree); i++ {
-				name := tree[i].Name
-				if name == ".nvmrc" {
-					nvmrcFound = true
-				} else if name == ".node-version" {
-					nodeVersionFound = true
-				}
-			}
-
-			if nvmrcFound {
-				// copy exact behavior of https://github.com/paketo-buildpacks/node-engine/blob/main/nvmrc_parser.go
-				fileContent, _, err = client.RepositoryFiles.GetRawFile(
-					fmt.Sprintf("%s/%s", owner, name), fmt.Sprintf("%s/.nvmrc", path),
-					&gitlab.GetRawFileOptions{
-						Ref: gitlab.String(ref),
-					},
-				)
-				if err != nil {
-					paketo.Others = append(paketo.Others, paketoBuildpackInfo)
-					heroku.Others = append(heroku.Others, herokuBuildpackInfo)
-					return fmt.Errorf("error fetching contents of .nvmrc: %v", err)
-				}
-				data = string(fileContent)
-
-				nvmrcVersion, err := validateNvmrc(data)
-				if err != nil {
-					paketo.Others = append(paketo.Others, paketoBuildpackInfo)
-					heroku.Others = append(heroku.Others, herokuBuildpackInfo)
-					return fmt.Errorf("error validating .nvmrc: %v", err)
-				}
-				nvmrcVersion = formatNvmrcContent(nvmrcVersion)
-
-				if nvmrcVersion != "*" {
-					packageJSON.Engines.Node = data
-				}
-			}
-
-			if packageJSON.Engines.Node == "" && nodeVersionFound {
-				// copy exact behavior of https://github.com/paketo-buildpacks/node-engine/blob/main/node_version_parser.go
-				fileContent, _, err = client.RepositoryFiles.GetRawFile(
-					fmt.Sprintf("%s/%s", owner, name), fmt.Sprintf("%s/.node-version", path),
-					&gitlab.GetRawFileOptions{
-						Ref: gitlab.String(ref),
-					},
-				)
-				if err != nil {
-					paketo.Others = append(paketo.Others, paketoBuildpackInfo)
-					heroku.Others = append(heroku.Others, herokuBuildpackInfo)
-					return fmt.Errorf("error fetching contents of .node-version: %v", err)
-				}
-
-				data = string(fileContent)
-
-				nodeVersion, err := validateNodeVersion(data)
-				if err != nil {
-					paketo.Others = append(paketo.Others, paketoBuildpackInfo)
-					heroku.Others = append(heroku.Others, herokuBuildpackInfo)
-					return fmt.Errorf("error validating .node-version: %v", err)
-				}
-				if nodeVersion != "" {
-					packageJSON.Engines.Node = nodeVersion
-				}
-			}
-		}
-
-		if packageJSON.Engines.Node == "" {
-			// use the default node engine version from https://github.com/paketo-buildpacks/node-engine/blob/main/buildpack.toml
-			packageJSON.Engines.Node = "16.*.*"
-		}
-
-		paketoBuildpackInfo.Config = make(map[string]interface{})
-		paketoBuildpackInfo.Config["scripts"] = packageJSON.Scripts
-		paketoBuildpackInfo.Config["node_engine"] = packageJSON.Engines.Node
-		paketo.Detected = append(paketo.Detected, paketoBuildpackInfo)
-
-		herokuBuildpackInfo.Config = make(map[string]interface{})
-		herokuBuildpackInfo.Config["scripts"] = packageJSON.Scripts
-		herokuBuildpackInfo.Config["node_engine"] = packageJSON.Engines.Node
-		heroku.Detected = append(heroku.Detected, herokuBuildpackInfo)
-	} else if foundStandalone {
-		paketo.Detected = append(paketo.Detected, paketoBuildpackInfo)
-		heroku.Detected = append(heroku.Detected, herokuBuildpackInfo)
-	}
+	// foundYarn := false
+	// foundNPM := false
+	// foundStandalone := false
+	// for result := range results {
+	// 	if result.string == yarn {
+	// 		foundYarn = true
+	// 	} else if result.string == npm {
+	// 		foundNPM = true
+	// 	} else if result.string == standalone {
+	// 		foundStandalone = true
+	// 	}
+	// }
+
+	paketo.Detected = append(paketo.Detected, paketoBuildpackInfo)
+	heroku.Detected = append(heroku.Detected, herokuBuildpackInfo)
+
+	// if foundYarn || foundNPM {
+	// 	// it is safe to assume that the project contains a package.json
+	// 	fileContent, _, err := client.RepositoryFiles.GetRawFile(
+	// 		fmt.Sprintf("%s/%s", owner, name), fmt.Sprintf("%s/package.json", path),
+	// 		&gitlab.GetRawFileOptions{
+	// 			Ref: gitlab.String(ref),
+	// 		},
+	// 	)
+	// 	if err != nil {
+	// 		paketo.Others = append(paketo.Others, paketoBuildpackInfo)
+	// 		heroku.Others = append(heroku.Others, herokuBuildpackInfo)
+	// 		return fmt.Errorf("error fetching contents of package.json: %v", err)
+	// 	}
+	// 	var packageJSON struct {
+	// 		Scripts map[string]string `json:"scripts"`
+	// 		Engines struct {
+	// 			Node string `json:"node"`
+	// 		} `json:"engines"`
+	// 	}
+
+	// 	data := string(fileContent)
+
+	// 	err = json.NewDecoder(strings.NewReader(data)).Decode(&packageJSON)
+	// 	if err != nil {
+	// 		paketo.Others = append(paketo.Others, paketoBuildpackInfo)
+	// 		heroku.Others = append(heroku.Others, herokuBuildpackInfo)
+	// 		return fmt.Errorf("error decoding package.json contents to struct: %v", err)
+	// 	}
+
+	// 	if packageJSON.Engines.Node == "" {
+	// 		// we should now check for the node engine version in .nvmrc and then .node-version
+	// 		nvmrcFound := false
+	// 		nodeVersionFound := false
+	// 		for i := 0; i < len(tree); i++ {
+	// 			name := tree[i].Name
+	// 			if name == ".nvmrc" {
+	// 				nvmrcFound = true
+	// 			} else if name == ".node-version" {
+	// 				nodeVersionFound = true
+	// 			}
+	// 		}
+
+	// 		if nvmrcFound {
+	// 			// copy exact behavior of https://github.com/paketo-buildpacks/node-engine/blob/main/nvmrc_parser.go
+	// 			fileContent, _, err = client.RepositoryFiles.GetRawFile(
+	// 				fmt.Sprintf("%s/%s", owner, name), fmt.Sprintf("%s/.nvmrc", path),
+	// 				&gitlab.GetRawFileOptions{
+	// 					Ref: gitlab.String(ref),
+	// 				},
+	// 			)
+	// 			if err != nil {
+	// 				paketo.Others = append(paketo.Others, paketoBuildpackInfo)
+	// 				heroku.Others = append(heroku.Others, herokuBuildpackInfo)
+	// 				return fmt.Errorf("error fetching contents of .nvmrc: %v", err)
+	// 			}
+	// 			data = string(fileContent)
+
+	// 			nvmrcVersion, err := validateNvmrc(data)
+	// 			if err != nil {
+	// 				paketo.Others = append(paketo.Others, paketoBuildpackInfo)
+	// 				heroku.Others = append(heroku.Others, herokuBuildpackInfo)
+	// 				return fmt.Errorf("error validating .nvmrc: %v", err)
+	// 			}
+	// 			nvmrcVersion = formatNvmrcContent(nvmrcVersion)
+
+	// 			if nvmrcVersion != "*" {
+	// 				packageJSON.Engines.Node = data
+	// 			}
+	// 		}
+
+	// 		if packageJSON.Engines.Node == "" && nodeVersionFound {
+	// 			// copy exact behavior of https://github.com/paketo-buildpacks/node-engine/blob/main/node_version_parser.go
+	// 			fileContent, _, err = client.RepositoryFiles.GetRawFile(
+	// 				fmt.Sprintf("%s/%s", owner, name), fmt.Sprintf("%s/.node-version", path),
+	// 				&gitlab.GetRawFileOptions{
+	// 					Ref: gitlab.String(ref),
+	// 				},
+	// 			)
+	// 			if err != nil {
+	// 				paketo.Others = append(paketo.Others, paketoBuildpackInfo)
+	// 				heroku.Others = append(heroku.Others, herokuBuildpackInfo)
+	// 				return fmt.Errorf("error fetching contents of .node-version: %v", err)
+	// 			}
+
+	// 			data = string(fileContent)
+
+	// 			nodeVersion, err := validateNodeVersion(data)
+	// 			if err != nil {
+	// 				paketo.Others = append(paketo.Others, paketoBuildpackInfo)
+	// 				heroku.Others = append(heroku.Others, herokuBuildpackInfo)
+	// 				return fmt.Errorf("error validating .node-version: %v", err)
+	// 			}
+	// 			if nodeVersion != "" {
+	// 				packageJSON.Engines.Node = nodeVersion
+	// 			}
+	// 		}
+	// 	}
+
+	// 	if packageJSON.Engines.Node == "" {
+	// 		// use the default node engine version from https://github.com/paketo-buildpacks/node-engine/blob/main/buildpack.toml
+	// 		packageJSON.Engines.Node = "16.*.*"
+	// 	}
+
+	// 	paketoBuildpackInfo.Config = make(map[string]interface{})
+	// 	paketoBuildpackInfo.Config["scripts"] = packageJSON.Scripts
+	// 	paketoBuildpackInfo.Config["node_engine"] = packageJSON.Engines.Node
+	// 	paketo.Detected = append(paketo.Detected, paketoBuildpackInfo)
+
+	// 	herokuBuildpackInfo.Config = make(map[string]interface{})
+	// 	herokuBuildpackInfo.Config["scripts"] = packageJSON.Scripts
+	// 	herokuBuildpackInfo.Config["node_engine"] = packageJSON.Engines.Node
+	// 	heroku.Detected = append(heroku.Detected, herokuBuildpackInfo)
+	// } else if foundStandalone {
+	// 	paketo.Detected = append(paketo.Detected, paketoBuildpackInfo)
+	// 	heroku.Detected = append(heroku.Detected, herokuBuildpackInfo)
+	// }
 
 
 	return nil
 	return nil
 }
 }

+ 255 - 63
internal/integrations/ci/gitlab/ci.go

@@ -3,6 +3,7 @@ package gitlab
 import (
 import (
 	"fmt"
 	"fmt"
 	"net/http"
 	"net/http"
+	"net/url"
 	"strings"
 	"strings"
 
 
 	"github.com/porter-dev/porter/api/server/shared/commonutils"
 	"github.com/porter-dev/porter/api/server/shared/commonutils"
@@ -32,8 +33,9 @@ type GitlabCI struct {
 	FolderPath       string
 	FolderPath       string
 	PorterToken      string
 	PorterToken      string
 
 
-	defaultGitBranch string
-	pID              string
+	defaultGitBranch  string
+	pID               string
+	gitlabInstanceURL string
 }
 }
 
 
 func (g *GitlabCI) Setup() error {
 func (g *GitlabCI) Setup() error {
@@ -95,37 +97,83 @@ func (g *GitlabCI) Setup() error {
 		return fmt.Errorf("error getting .gitlab-ci.yml file: %w", err)
 		return fmt.Errorf("error getting .gitlab-ci.yml file: %w", err)
 	} else {
 	} else {
 		// update .gitlab-ci.yml if needed
 		// update .gitlab-ci.yml if needed
-		ciFileContentsMap := make(map[string]interface{})
-		err = yaml.Unmarshal(ciFile, ciFileContentsMap)
+
+		// to preserve the order of the YAML, we use a MapSlice
+		ciFileContentsMap := yaml.MapSlice{}
+		err = yaml.Unmarshal(ciFile, &ciFileContentsMap)
 
 
 		if err != nil {
 		if err != nil {
 			return fmt.Errorf("error unmarshalling existing .gitlab-ci.yml: %w", err)
 			return fmt.Errorf("error unmarshalling existing .gitlab-ci.yml: %w", err)
 		}
 		}
 
 
-		stages, ok := ciFileContentsMap["stages"].([]string)
+		var stagesInt []interface{}
+		stagesIdx := -1
 
 
-		if !ok {
-			return fmt.Errorf("error converting stages to string slice")
-		}
+		for idx, elem := range ciFileContentsMap {
+			if key, ok := elem.Key.(string); ok {
+				if key == "stages" {
+					stages, ok := elem.Value.([]interface{})
 
 
-		stageExists := false
+					if !ok {
+						return fmt.Errorf("error converting stages to interface slice")
+					}
 
 
-		for _, stage := range stages {
-			if stage == jobName {
-				stageExists = true
-				break
+					stagesInt = stages
+					stagesIdx = idx
+
+					break
+				}
+			} else {
+				return fmt.Errorf("invalid key '%v' in .gitlab-ci.yml", elem.Key)
 			}
 			}
 		}
 		}
 
 
-		if !stageExists {
-			stages = append(stages, jobName)
+		// two cases can happen here:
+		// 1: "stages" exists
+		// 2: "stages" does not exist
+
+		if stagesIdx >= 0 { // 1: "stages" exists
+			stageExists := false
+
+			for _, stage := range stagesInt {
+				stageStr, ok := stage.(string)
+				if !ok {
+					return fmt.Errorf("error converting from interface to string")
+				}
+
+				if stageStr == jobName {
+					stageExists = true
+					break
+				}
+			}
+
+			if !stageExists {
+				stagesInt = append(stagesInt, jobName)
+
+				ciFileContentsMap[stagesIdx] = yaml.MapItem{
+					Key:   "stages",
+					Value: stagesInt,
+				}
+			}
+		} else { // 2: "stages" does not exist
+			stagesInt = append(stagesInt, jobName)
 
 
-			ciFileContentsMap["stages"] = stages
+			ciFileContentsMap = append(ciFileContentsMap, yaml.MapItem{
+				Key:   "stages",
+				Value: stagesInt,
+			})
 		}
 		}
 
 
-		ciFileContentsMap[jobName] = g.getCIJob(jobName)
+		ciFileContentsMap = append(ciFileContentsMap, yaml.MapItem{
+			Key:   jobName,
+			Value: g.getCIJob(jobName),
+		})
+
+		contentsYAML, err := yaml.Marshal(ciFileContentsMap)
 
 
-		contentsYAML, _ := yaml.Marshal(ciFileContentsMap)
+		if err != nil {
+			return fmt.Errorf("error marshalling contents of .gitlab-ci.yml while updating to add porter job")
+		}
 
 
 		_, _, err = client.RepositoryFiles.UpdateFile(g.pID, ".gitlab-ci.yml", &gitlab.UpdateFileOptions{
 		_, _, err = client.RepositoryFiles.UpdateFile(g.pID, ".gitlab-ci.yml", &gitlab.UpdateFileOptions{
 			Branch:        gitlab.String(g.defaultGitBranch),
 			Branch:        gitlab.String(g.defaultGitBranch),
@@ -183,37 +231,72 @@ func (g *GitlabCI) Cleanup() error {
 		return fmt.Errorf("error getting .gitlab-ci.yml file: %w", err)
 		return fmt.Errorf("error getting .gitlab-ci.yml file: %w", err)
 	}
 	}
 
 
-	ciFileContentsMap := make(map[string]interface{})
-	err = yaml.Unmarshal(ciFile, ciFileContentsMap)
+	ciFileContentsMap := yaml.MapSlice{}
+	err = yaml.Unmarshal(ciFile, &ciFileContentsMap)
 
 
 	if err != nil {
 	if err != nil {
 		return fmt.Errorf("error unmarshalling existing .gitlab-ci.yml: %w", err)
 		return fmt.Errorf("error unmarshalling existing .gitlab-ci.yml: %w", err)
 	}
 	}
 
 
-	stages, ok := ciFileContentsMap["stages"].([]interface{})
+	var stagesInt []interface{}
+	stagesIdx := -1
+
+	for idx, elem := range ciFileContentsMap {
+		if key, ok := elem.Key.(string); ok {
+			if key == "stages" {
+				stages, ok := elem.Value.([]interface{})
 
 
-	if !ok {
-		return fmt.Errorf("error converting stages to interface slice")
+				if !ok {
+					return fmt.Errorf("error converting stages to interface slice")
+				}
+
+				stagesInt = stages
+				stagesIdx = idx
+
+				break
+			}
+		} else {
+			return fmt.Errorf("invalid key '%v' in .gitlab-ci.yml", elem.Key)
+		}
 	}
 	}
 
 
-	var newStages []string
+	if stagesIdx >= 0 { // "stages" exists
+		var newStages []string
+
+		for _, stage := range stagesInt {
+			stageStr, ok := stage.(string)
+			if !ok {
+				return fmt.Errorf("error converting from interface to string")
+			}
 
 
-	for _, stage := range stages {
-		stageStr, ok := stage.(string)
-		if !ok {
-			return fmt.Errorf("error converting from interface to string")
+			if stageStr != jobName {
+				newStages = append(newStages, stageStr)
+			}
 		}
 		}
 
 
-		if stageStr != jobName {
-			newStages = append(newStages, stageStr)
+		ciFileContentsMap[stagesIdx] = yaml.MapItem{
+			Key:   "stages",
+			Value: newStages,
 		}
 		}
 	}
 	}
 
 
-	ciFileContentsMap["stage"] = newStages
+	newCIFileContentsMap := yaml.MapSlice{}
 
 
-	delete(ciFileContentsMap, jobName)
+	for _, elem := range ciFileContentsMap {
+		if key, ok := elem.Key.(string); ok {
+			if key != jobName {
+				newCIFileContentsMap = append(newCIFileContentsMap, elem)
+			}
+		} else {
+			return fmt.Errorf("invalid key '%v' in .gitlab-ci.yml", elem.Key)
+		}
+	}
+
+	contentsYAML, err := yaml.Marshal(newCIFileContentsMap)
 
 
-	contentsYAML, _ := yaml.Marshal(ciFileContentsMap)
+	if err != nil {
+		return fmt.Errorf("error unmarshalling contents of .gitlab-ci.yml while updating to remove porter job")
+	}
 
 
 	_, _, err = client.RepositoryFiles.UpdateFile(g.pID, ".gitlab-ci.yml", &gitlab.UpdateFileOptions{
 	_, _, err = client.RepositoryFiles.UpdateFile(g.pID, ".gitlab-ci.yml", &gitlab.UpdateFileOptions{
 		Branch:        gitlab.String(g.defaultGitBranch),
 		Branch:        gitlab.String(g.defaultGitBranch),
@@ -237,14 +320,23 @@ func (g *GitlabCI) getClient() (*gitlab.Client, error) {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	oauthInt, err := g.Repo.GitlabAppOAuthIntegration().ReadGitlabAppOAuthIntegration(g.UserID, g.ProjectID, g.IntegrationID)
+	giOAuthInt, err := g.Repo.GitlabAppOAuthIntegration().ReadGitlabAppOAuthIntegration(g.UserID, g.ProjectID, g.IntegrationID)
+
+	if err != nil {
+		return nil, err
+	}
+
+	oauthInt, err := g.Repo.OAuthIntegration().ReadOAuthIntegration(g.ProjectID, giOAuthInt.OAuthIntegrationID)
 
 
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	accessToken, _, err := oauth.GetAccessToken(oauthInt.SharedOAuthModel, commonutils.GetGitlabOAuthConf(g.PorterConf, gi),
-		oauth.MakeUpdateGitlabAppOAuthIntegrationFunction(oauthInt, g.Repo))
+	accessToken, _, err := oauth.GetAccessToken(
+		oauthInt.SharedOAuthModel,
+		commonutils.GetGitlabOAuthConf(g.PorterConf, gi),
+		oauth.MakeUpdateGitlabAppOAuthIntegrationFunction(g.ProjectID, giOAuthInt, g.Repo),
+	)
 
 
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -256,50 +348,150 @@ func (g *GitlabCI) getClient() (*gitlab.Client, error) {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
+	g.gitlabInstanceURL = gi.InstanceURL
+
 	return client, nil
 	return client, nil
 }
 }
 
 
-func (g *GitlabCI) getCIJob(jobName string) map[string]interface{} {
-	return map[string]interface{}{
-		"image":   "public.ecr.aws/o1j4x7p4/porter-cli:latest",
-		"stage":   jobName,
-		"timeout": "20 minutes",
-		"variables": map[string]string{
-			"GIT_STRATEGY": "clone",
+func (g *GitlabCI) getCIJob(jobName string) yaml.MapSlice {
+	res := yaml.MapSlice{}
+	url, _ := url.Parse(g.gitlabInstanceURL)
+
+	res = append(res,
+		yaml.MapItem{
+			Key: "rules",
+			Value: []map[string]string{
+				{
+					"if": fmt.Sprintf("$CI_COMMIT_BRANCH == \"%s\" && $CI_PIPELINE_SOURCE == \"push\"", g.GitBranch),
+				},
+			},
 		},
 		},
-		"rules": []map[string]string{
-			{
-				"if": fmt.Sprintf("$CI_COMMIT_BRANCH == \"%s\" && $CI_PIPELINE_SOURCE == \"push\"", g.GitBranch),
+	)
+
+	if url.Hostname() == "gitlab.com" || url.Hostname() == "www.gitlab.com" {
+		res = append(res,
+			yaml.MapItem{
+				Key:   "image",
+				Value: "docker:latest",
+			},
+			yaml.MapItem{
+				Key: "services",
+				Value: []string{
+					"docker:dind",
+				},
+			},
+			yaml.MapItem{
+				Key: "script",
+				Value: []string{
+					fmt.Sprintf(
+						"docker run --rm --workdir=\"/app\" "+
+							"-v /var/run/docker.sock:/var/run/docker.sock "+
+							"-v $(pwd):/app "+
+							"public.ecr.aws/o1j4x7p4/porter-cli:latest "+
+							"update --host \"%s\" --project %d --cluster %d "+
+							"--token \"$%s\" --app \"%s\" "+
+							"--tag \"$(echo $CI_COMMIT_SHA | cut -c1-7)\" --namespace \"%s\" --stream",
+						g.ServerURL, g.ProjectID, g.ClusterID, g.getPorterTokenSecretName(),
+						g.ReleaseName, g.ReleaseNamespace,
+					),
+				},
 			},
 			},
+			yaml.MapItem{
+				Key: "tags",
+				Value: []string{
+					"docker",
+				},
+			},
+		)
+	} else {
+		res = append(res,
+			yaml.MapItem{
+				Key: "image",
+				Value: map[string]interface{}{
+					"name": "public.ecr.aws/o1j4x7p4/porter-cli:latest",
+					"entrypoint": []string{
+						"",
+					},
+				},
+			},
+			yaml.MapItem{
+				Key: "script",
+				Value: []string{
+					fmt.Sprintf(
+						"porter update --host \"%s\" --project %d --cluster %d "+
+							"--token \"$%s\" --app \"%s\" "+
+							"--tag \"$(echo $CI_COMMIT_SHA | cut -c1-7)\" --namespace \"%s\" --stream",
+						g.ServerURL, g.ProjectID, g.ClusterID, g.getPorterTokenSecretName(),
+						g.ReleaseName, g.ReleaseNamespace,
+					),
+				},
+			},
+			yaml.MapItem{
+				Key: "tags",
+				Value: []string{
+					"porter-runner",
+				},
+			},
+		)
+	}
+
+	res = append(res,
+		yaml.MapItem{
+			Key:   "stage",
+			Value: jobName,
 		},
 		},
-		"script": []string{
-			fmt.Sprintf("export PORTER_HOST=\"%s\"", g.ServerURL),
-			fmt.Sprintf("export PORTER_PROJECT=\"%d\"", g.ProjectID),
-			fmt.Sprintf("export PORTER_CLUSTER=\"%d\"", g.ClusterID),
-			fmt.Sprintf("export PORTER_TOKEN=\"$%s\"", g.getPorterTokenSecretName()),
-			"export PORTER_TAG=\"$(echo $CI_COMMIT_SHA | cut -c1-7)\"",
-			fmt.Sprintf("porter update --app \"%s\" --tag \"$PORTER_TAG\" --namespace \"%s\" --path \"%s\" --stream",
-				g.ReleaseName, g.ReleaseNamespace, g.FolderPath),
+		yaml.MapItem{
+			Key:   "timeout",
+			Value: "20 minutes",
 		},
 		},
-	}
+		yaml.MapItem{
+			Key: "variables",
+			Value: map[string]string{
+				"GIT_STRATEGY": "clone",
+			},
+		},
+	)
+
+	return res
 }
 }
 
 
 func (g *GitlabCI) createGitlabSecret(client *gitlab.Client) error {
 func (g *GitlabCI) createGitlabSecret(client *gitlab.Client) error {
-	_, _, err := client.ProjectVariables.CreateVariable(g.pID, &gitlab.CreateProjectVariableOptions{
-		Key:    gitlab.String(g.getPorterTokenSecretName()),
-		Value:  gitlab.String(g.PorterToken),
-		Masked: gitlab.Bool(true),
-	})
+	_, resp, err := client.ProjectVariables.GetVariable(g.pID, g.getPorterTokenSecretName(),
+		&gitlab.GetProjectVariableOptions{})
+
+	if resp.StatusCode == http.StatusNotFound {
+		_, _, err = client.ProjectVariables.CreateVariable(g.pID, &gitlab.CreateProjectVariableOptions{
+			Key:    gitlab.String(g.getPorterTokenSecretName()),
+			Value:  gitlab.String(g.PorterToken),
+			Masked: gitlab.Bool(true),
+		})
+
+		if err != nil {
+			return fmt.Errorf("error creating porter token variable: %w", err)
+		}
+
+		return nil
+	} else if err != nil {
+		return fmt.Errorf("error getting porter token variable: %w", err)
+	}
+
+	_, _, err = client.ProjectVariables.UpdateVariable(g.pID, g.getPorterTokenSecretName(),
+		&gitlab.UpdateProjectVariableOptions{
+			Value:  gitlab.String(g.PorterToken),
+			Masked: gitlab.Bool(true),
+		},
+	)
 
 
 	if err != nil {
 	if err != nil {
-		return fmt.Errorf("error creating porter token variable: %w", err)
+		return fmt.Errorf("error updating porter token variable: %w", err)
 	}
 	}
 
 
 	return nil
 	return nil
 }
 }
 
 
 func (g *GitlabCI) deleteGitlabSecret(client *gitlab.Client) error {
 func (g *GitlabCI) deleteGitlabSecret(client *gitlab.Client) error {
-	_, err := client.ProjectVariables.RemoveVariable(g.pID, g.getPorterTokenSecretName(), &gitlab.RemoveProjectVariableOptions{})
+	_, err := client.ProjectVariables.RemoveVariable(g.pID, g.getPorterTokenSecretName(),
+		&gitlab.RemoveProjectVariableOptions{})
 
 
 	if err != nil {
 	if err != nil {
 		return fmt.Errorf("error removing porter token variable: %w", err)
 		return fmt.Errorf("error removing porter token variable: %w", err)
@@ -309,7 +501,7 @@ func (g *GitlabCI) deleteGitlabSecret(client *gitlab.Client) error {
 }
 }
 
 
 func (g *GitlabCI) getPorterTokenSecretName() string {
 func (g *GitlabCI) getPorterTokenSecretName() string {
-	return fmt.Sprintf("PORTER_TOKEN_%d", g.ProjectID)
+	return fmt.Sprintf("PORTER_TOKEN_%d_%s", g.ProjectID, strings.ToLower(strings.ReplaceAll(g.ReleaseName, "-", "_")))
 }
 }
 
 
 func getGitlabStageJobName(releaseName string) string {
 func getGitlabStageJobName(releaseName string) string {

+ 4 - 9
internal/models/integrations/oauth.go

@@ -96,17 +96,12 @@ type GithubAppOAuthIntegration struct {
 }
 }
 
 
 // GitlabAppOAuthIntegration is the model used for storing gitlab app oauth data
 // GitlabAppOAuthIntegration is the model used for storing gitlab app oauth data
-// Unlike the above, this model is tied to a specific user, not a project
 type GitlabAppOAuthIntegration struct {
 type GitlabAppOAuthIntegration struct {
 	gorm.Model
 	gorm.Model
-	SharedOAuthModel
 
 
-	// The id of the user that linked with this auth mechanism
-	UserID uint `json:"user_id"`
-
-	// The id of the project that linked with this auth mechanism
-	ProjectID uint `json:"project_id"`
+	// The ID of the oauth integration linked with this auth mechanism
+	OAuthIntegrationID uint `json:"oauth_integration_id"`
 
 
-	// The id of the gitlab integration linked with this auth mechanism
-	IntegrationID uint `json:"integration_id"`
+	// The ID of the gitlab integration linked with this auth mechanism
+	GitlabIntegrationID uint `json:"gitlab_integration_id"`
 }
 }

+ 10 - 2
internal/oauth/config.go

@@ -155,14 +155,22 @@ func MakeUpdateGithubAppOauthIntegrationFunction(
 // MakeUpdateGitlabAppOAuthIntegrationFunction creates a function to be passed to GetAccessToken that updates the GitlabAppOAuthIntegration
 // MakeUpdateGitlabAppOAuthIntegrationFunction creates a function to be passed to GetAccessToken that updates the GitlabAppOAuthIntegration
 // if it needs to be updated
 // if it needs to be updated
 func MakeUpdateGitlabAppOAuthIntegrationFunction(
 func MakeUpdateGitlabAppOAuthIntegrationFunction(
+	projectID uint,
 	o *integrations.GitlabAppOAuthIntegration,
 	o *integrations.GitlabAppOAuthIntegration,
-	repo repository.Repository) func(accessToken []byte, refreshToken []byte, expiry time.Time) error {
+	repo repository.Repository,
+) func(accessToken []byte, refreshToken []byte, expiry time.Time) error {
 	return func(accessToken []byte, refreshToken []byte, expiry time.Time) error {
 	return func(accessToken []byte, refreshToken []byte, expiry time.Time) error {
+		o, err := repo.OAuthIntegration().ReadOAuthIntegration(projectID, o.OAuthIntegrationID)
+
+		if err != nil {
+			return err
+		}
+
 		o.AccessToken = accessToken
 		o.AccessToken = accessToken
 		o.RefreshToken = refreshToken
 		o.RefreshToken = refreshToken
 		o.Expiry = expiry
 		o.Expiry = expiry
 
 
-		_, err := repo.GitlabAppOAuthIntegration().UpdateGitlabAppOAuthIntegration(o)
+		_, err = repo.OAuthIntegration().UpdateOAuthIntegration(o)
 
 
 		return err
 		return err
 	}
 	}

+ 9 - 0
internal/repository/credentials/credentials.go

@@ -60,18 +60,27 @@ type GitlabCredential struct {
 }
 }
 
 
 type CredentialStorage interface {
 type CredentialStorage interface {
+	// OAuth
 	WriteOAuthCredential(oauthIntegration *integrations.OAuthIntegration, data *OAuthCredential) error
 	WriteOAuthCredential(oauthIntegration *integrations.OAuthIntegration, data *OAuthCredential) error
 	GetOAuthCredential(oauthIntegration *integrations.OAuthIntegration) (*OAuthCredential, error)
 	GetOAuthCredential(oauthIntegration *integrations.OAuthIntegration) (*OAuthCredential, error)
 	CreateOAuthToken(oauthIntegration *integrations.OAuthIntegration) (string, error)
 	CreateOAuthToken(oauthIntegration *integrations.OAuthIntegration) (string, error)
+
+	// GCP
 	WriteGCPCredential(gcpIntegration *integrations.GCPIntegration, data *GCPCredential) error
 	WriteGCPCredential(gcpIntegration *integrations.GCPIntegration, data *GCPCredential) error
 	GetGCPCredential(gcpIntegration *integrations.GCPIntegration) (*GCPCredential, error)
 	GetGCPCredential(gcpIntegration *integrations.GCPIntegration) (*GCPCredential, error)
 	CreateGCPToken(gcpIntegration *integrations.GCPIntegration) (string, error)
 	CreateGCPToken(gcpIntegration *integrations.GCPIntegration) (string, error)
+
+	// AWS
 	WriteAWSCredential(awsIntegration *integrations.AWSIntegration, data *AWSCredential) error
 	WriteAWSCredential(awsIntegration *integrations.AWSIntegration, data *AWSCredential) error
 	GetAWSCredential(awsIntegration *integrations.AWSIntegration) (*AWSCredential, error)
 	GetAWSCredential(awsIntegration *integrations.AWSIntegration) (*AWSCredential, error)
 	CreateAWSToken(awsIntegration *integrations.AWSIntegration) (string, error)
 	CreateAWSToken(awsIntegration *integrations.AWSIntegration) (string, error)
+
+	// Azure
 	WriteAzureCredential(azIntegration *integrations.AzureIntegration, data *AzureCredential) error
 	WriteAzureCredential(azIntegration *integrations.AzureIntegration, data *AzureCredential) error
 	GetAzureCredential(azIntegration *integrations.AzureIntegration) (*AzureCredential, error)
 	GetAzureCredential(azIntegration *integrations.AzureIntegration) (*AzureCredential, error)
 	CreateAzureToken(azIntegration *integrations.AzureIntegration) (string, error)
 	CreateAzureToken(azIntegration *integrations.AzureIntegration) (string, error)
+
+	// Gitlab
 	WriteGitlabCredential(giIntegration *integrations.GitlabIntegration, data *GitlabCredential) error
 	WriteGitlabCredential(giIntegration *integrations.GitlabIntegration, data *GitlabCredential) error
 	GetGitlabCredential(giIntegration *integrations.GitlabIntegration) (*GitlabCredential, error)
 	GetGitlabCredential(giIntegration *integrations.GitlabIntegration) (*GitlabCredential, error)
 	CreateGitlabToken(giIntegration *integrations.GitlabIntegration) (string, error)
 	CreateGitlabToken(giIntegration *integrations.GitlabIntegration) (string, error)

+ 5 - 125
internal/repository/gorm/auth.go

@@ -1751,27 +1751,10 @@ func NewGitlabAppOAuthIntegrationRepository(
 func (repo *GitlabAppOAuthIntegrationRepository) CreateGitlabAppOAuthIntegration(
 func (repo *GitlabAppOAuthIntegrationRepository) CreateGitlabAppOAuthIntegration(
 	gi *ints.GitlabAppOAuthIntegration,
 	gi *ints.GitlabAppOAuthIntegration,
 ) (*ints.GitlabAppOAuthIntegration, error) {
 ) (*ints.GitlabAppOAuthIntegration, error) {
-	err := repo.EncryptGitlabAppOAuthIntegrationData(gi, repo.key)
-
-	if err != nil {
-		return nil, err
-	}
-
-	// if storage backend is not nil, strip out credential data, which will be stored in credential
-	// storage backend after write to DB
-	// var credentialData = &credentials.GitlabCredential{}
-
-	// if repo.storageBackend != nil {
-	// 	credentialData.AppClientID = gi.AppClientID
-	// 	credentialData.AppClientSecret = gi.AppClientSecret
-
-	// 	gi.AppClientID = []byte{}
-	// 	gi.AppClientSecret = []byte{}
-	// }
-
 	if err := repo.db.Create(gi).Error; err != nil {
 	if err := repo.db.Create(gi).Error; err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
+
 	return gi, nil
 	return gi, nil
 }
 }
 
 
@@ -1782,117 +1765,14 @@ func (repo *GitlabAppOAuthIntegrationRepository) ReadGitlabAppOAuthIntegration(
 
 
 	if err := repo.db.
 	if err := repo.db.
 		Order("gitlab_app_o_auth_integrations.id desc").
 		Order("gitlab_app_o_auth_integrations.id desc").
-		Joins("INNER JOIN gitlab_integrations ON gitlab_integrations.id = gitlab_app_o_auth_integrations.integration_id").
-		Where("gitlab_app_o_auth_integrations.user_id = ? AND gitlab_app_o_auth_integrations.project_id = ? AND"+
+		Joins("INNER JOIN gitlab_integrations ON gitlab_integrations.id = gitlab_app_o_auth_integrations.gitlab_integration_id").
+		Joins("INNER JOIN o_auth_integrations ON o_auth_integrations.id = gitlab_app_o_auth_integrations.o_auth_integration_id").
+		Where("o_auth_integrations.user_id = ? AND o_auth_integrations.project_id = ? AND"+
 			" gitlab_integrations.id = ? AND gitlab_integrations.deleted_at IS NULL AND"+
 			" gitlab_integrations.id = ? AND gitlab_integrations.deleted_at IS NULL AND"+
-			" gitlab_app_o_auth_integrations.deleted_at IS NULL",
+			" gitlab_app_o_auth_integrations.deleted_at IS NULL AND o_auth_integrations.deleted_at IS NULL",
 			userID, projectID, integrationID).First(&gi).Error; err != nil {
 			userID, projectID, integrationID).First(&gi).Error; err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	// if repo.storageBackend != nil {
-	// 	credentialData, err := repo.storageBackend.GetGitlabCredential(gi)
-
-	// 	if err != nil {
-	// 		return nil, err
-	// 	}
-
-	// 	gi.AppClientID = credentialData.AppClientID
-
-	// 	gi.AppClientSecret = credentialData.AppClientSecret
-	// }
-
-	err := repo.DecryptGitlabAppOAuthIntegrationData(gi, repo.key)
-
-	if err != nil {
-		return nil, err
-	}
-
 	return gi, nil
 	return gi, nil
 }
 }
-
-func (repo *GitlabAppOAuthIntegrationRepository) UpdateGitlabAppOAuthIntegration(
-	gi *ints.GitlabAppOAuthIntegration,
-) (*ints.GitlabAppOAuthIntegration, error) {
-	err := repo.EncryptGitlabAppOAuthIntegrationData(gi, repo.key)
-
-	if err != nil {
-		return nil, err
-	}
-
-	// if storage backend is not nil, strip out credential data, which will be stored in credential
-	// storage backend after write to DB
-	// var credentialData = &credentials.GitlabCredential{}
-
-	// if repo.storageBackend != nil {
-	// 	credentialData.AppClientID = gi.AppClientID
-	// 	credentialData.AppClientSecret = gi.AppClientSecret
-
-	// 	gi.AppClientID = []byte{}
-	// 	gi.AppClientSecret = []byte{}
-	// }
-
-	if err := repo.db.Save(gi).Error; err != nil {
-		return nil, err
-	}
-
-	return gi, nil
-}
-
-// EncryptGitlabAppOAuthIntegrationData will encrypt the gitlab app oauth integration data before
-// writing to the DB
-func (repo *GitlabAppOAuthIntegrationRepository) EncryptGitlabAppOAuthIntegrationData(
-	gi *ints.GitlabAppOAuthIntegration,
-	key *[32]byte,
-) error {
-	if len(gi.AccessToken) > 0 {
-		cipherData, err := encryption.Encrypt(gi.AccessToken, key)
-
-		if err != nil {
-			return err
-		}
-
-		gi.AccessToken = cipherData
-	}
-
-	if len(gi.RefreshToken) > 0 {
-		cipherData, err := encryption.Encrypt(gi.RefreshToken, key)
-
-		if err != nil {
-			return err
-		}
-
-		gi.RefreshToken = cipherData
-	}
-
-	return nil
-}
-
-// DecryptAppOAuthGitlabIntegrationData will decrypt the gitlab app oauth integration data before
-// returning it from the DB
-func (repo *GitlabAppOAuthIntegrationRepository) DecryptGitlabAppOAuthIntegrationData(
-	gi *ints.GitlabAppOAuthIntegration,
-	key *[32]byte,
-) error {
-	if len(gi.AccessToken) > 0 {
-		plaintext, err := encryption.Decrypt(gi.AccessToken, key)
-
-		if err != nil {
-			return err
-		}
-
-		gi.AccessToken = plaintext
-	}
-
-	if len(gi.RefreshToken) > 0 {
-		plaintext, err := encryption.Decrypt(gi.RefreshToken, key)
-
-		if err != nil {
-			return err
-		}
-
-		gi.RefreshToken = plaintext
-	}
-
-	return nil
-}

+ 0 - 1
internal/repository/integrations.go

@@ -98,5 +98,4 @@ type GitlabIntegrationRepository interface {
 type GitlabAppOAuthIntegrationRepository interface {
 type GitlabAppOAuthIntegrationRepository interface {
 	CreateGitlabAppOAuthIntegration(gi *ints.GitlabAppOAuthIntegration) (*ints.GitlabAppOAuthIntegration, error)
 	CreateGitlabAppOAuthIntegration(gi *ints.GitlabAppOAuthIntegration) (*ints.GitlabAppOAuthIntegration, error)
 	ReadGitlabAppOAuthIntegration(userID, projectID, integrationID uint) (*ints.GitlabAppOAuthIntegration, error)
 	ReadGitlabAppOAuthIntegration(userID, projectID, integrationID uint) (*ints.GitlabAppOAuthIntegration, error)
-	UpdateGitlabAppOAuthIntegration(gi *ints.GitlabAppOAuthIntegration) (*ints.GitlabAppOAuthIntegration, error)
 }
 }

+ 0 - 15
internal/repository/test/auth.go

@@ -688,18 +688,3 @@ func (repo *GitlabAppOAuthIntegrationRepository) ReadGitlabAppOAuthIntegration(
 ) (*ints.GitlabAppOAuthIntegration, error) {
 ) (*ints.GitlabAppOAuthIntegration, error) {
 	panic("not implemented")
 	panic("not implemented")
 }
 }
-
-func (repo *GitlabAppOAuthIntegrationRepository) UpdateGitlabAppOAuthIntegration(gi *ints.GitlabAppOAuthIntegration) (*ints.GitlabAppOAuthIntegration, error) {
-	if !repo.canQuery {
-		return nil, errors.New("Cannot write database")
-	}
-
-	if int(gi.ID-1) >= len(repo.gitlabAppOAuthIntegrations) || repo.gitlabAppOAuthIntegrations[gi.ID-1] == nil {
-		return nil, gorm.ErrRecordNotFound
-	}
-
-	index := int(gi.ID - 1)
-	repo.gitlabAppOAuthIntegrations[index] = gi
-
-	return gi, nil
-}