Explorar el Código

basic DB layers for gitlab integration

Mohammed Nafees hace 4 años
padre
commit
92caea1e5e

+ 23 - 0
api/types/project_integration.go

@@ -159,3 +159,26 @@ type CreateAzureResponse struct {
 }
 
 type ListAzureResponse []*AzureIntegration
+
+type GitlabIntegration struct {
+	CreatedAt time.Time `json:"created_at"`
+
+	ID uint `json:"id"`
+
+	// The id of the user that linked this auth mechanism
+	UserID uint `json:"user_id"`
+
+	// The project that this integration belongs to
+	ProjectID uint `json:"project_id"`
+}
+
+type ListGitlabResponse []*GitlabIntegration
+
+type CreateGitlabRequest struct {
+	SudoAccessToken string `json:"sudo_access_token" form:"required"`
+	SudoUsername    string `json:"sudo_username" form:"required"`
+}
+
+type CreateGitlabResponse struct {
+	*GitlabIntegration
+}

+ 11 - 0
ee/integrations/vault/types.go

@@ -1,3 +1,4 @@
+//go:build ee
 // +build ee
 
 package vault
@@ -58,6 +59,16 @@ type GetAzureCredentialData struct {
 	Data     *credentials.AzureCredential `json:"data"`
 }
 
+type GetGitlabCredentialResponse struct {
+	*VaultGetResponse
+	Data *GetGitlabCredentialData `json:"data"`
+}
+
+type GetGitlabCredentialData struct {
+	Metadata *VaultMetadata                `json:"metadata"`
+	Data     *credentials.GitlabCredential `json:"data"`
+}
+
 type CreatePolicyRequest struct {
 	Policy string `json:"policy"`
 }

+ 34 - 0
ee/integrations/vault/vault.go

@@ -1,3 +1,4 @@
+//go:build ee
 // +build ee
 
 package vault
@@ -185,6 +186,39 @@ func (c *Client) getAzureCredentialPath(azIntegration *integrations.AzureIntegra
 	)
 }
 
+func (c *Client) WriteGitlabCredential(giIntegration *integrations.GitlabIntegration, data *credentials.GitlabCredential) error {
+	reqData := &CreateVaultSecretRequest{
+		Data: data,
+	}
+
+	return c.postRequest(fmt.Sprintf("/v1/%s", c.getGitlabCredentialPath(giIntegration)), reqData, nil)
+}
+
+func (c *Client) GetGitlabCredential(giIntegration *integrations.GitlabIntegration) (*credentials.GitlabCredential, error) {
+	resp := &GetGitlabCredentialResponse{}
+
+	err := c.getRequest(fmt.Sprintf("/v1/%s", c.getGitlabCredentialPath(giIntegration)), resp)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return resp.Data.Data, nil
+}
+
+func (c *Client) CreateGitlabToken(giIntegration *integrations.GitlabIntegration) (string, error) {
+	panic("not implemented")
+}
+
+func (c *Client) getGitlabCredentialPath(giIntegration *integrations.GitlabIntegration) string {
+	return fmt.Sprintf(
+		"kv/data/secret/%s/%d/gitlab/%d",
+		c.secretPrefix,
+		giIntegration.ProjectID,
+		giIntegration.ID,
+	)
+}
+
 const readOnlyPolicyTemplate = `path "%s" {
   capabilities = ["read"]
 }`

+ 6 - 3
go.mod

@@ -47,8 +47,8 @@ require (
 	github.com/spf13/viper v1.10.0
 	github.com/stretchr/testify v1.7.0
 	golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
-	golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4
-	golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
+	golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2
+	golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
 	google.golang.org/api v0.62.0
 	google.golang.org/genproto v0.0.0-20220422154200-b37d22cd5731
 	google.golang.org/grpc v1.46.0
@@ -80,8 +80,11 @@ require (
 	github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerregistry/armcontainerregistry v0.5.0 // indirect
 	github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0 // indirect
 	github.com/golang-jwt/jwt v3.2.1+incompatible // indirect
+	github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
+	github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
 	github.com/kylelemons/godebug v1.1.0 // indirect
 	github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 // indirect
+	github.com/xanzy/go-gitlab v0.68.0 // indirect
 )
 
 require (
@@ -249,7 +252,7 @@ require (
 	golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect
 	golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 // indirect
 	golang.org/x/text v0.3.7 // indirect
-	golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
+	golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect
 	google.golang.org/appengine v1.6.7 // indirect
 	gopkg.in/gorp.v1 v1.7.2 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect

+ 13 - 0
go.sum

@@ -876,8 +876,10 @@ github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv
 github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg=
 github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
 github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
 github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
 github.com/hashicorp/go-getter v1.5.3/go.mod h1:BrrV/1clo8cCYu6mxvboYg+KutTiFnXjMEgDD8+i7ZI=
+github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
 github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
 github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
 github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
@@ -889,6 +891,8 @@ github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHh
 github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
 github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
 github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
+github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ=
+github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
 github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
 github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
 github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
@@ -1597,6 +1601,8 @@ github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+
 github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
 github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
 github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
+github.com/xanzy/go-gitlab v0.68.0 h1:b2iMQHgZ1V+NyRqLRJVv6RFfr4xnd/AASeS/PETYL0Y=
+github.com/xanzy/go-gitlab v0.68.0/go.mod h1:o4yExCtdaqlM8YGdDJWuZoBmfxBsmA9TPEjs9mx1UO4=
 github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
 github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
 github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo=
@@ -1859,9 +1865,12 @@ golang.org/x/net v0.0.0-20211203184738-4852103109b8/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220107192237-5cfca573fb4d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
 golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y=
+golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1881,6 +1890,8 @@ golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ
 golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=
 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 h1:OSnWWcOd/CtWQC2cYSBgbTSJv3ciqd8r54ySIW2y3RE=
+golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -2057,6 +2068,8 @@ golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxb
 golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
 golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w=
+golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

+ 23 - 0
internal/models/integrations/gitlab.go

@@ -0,0 +1,23 @@
+package integrations
+
+import "gorm.io/gorm"
+
+// GitlabIntegration takes care of Gitlab related auth mechanisms and data
+type GitlabIntegration struct {
+	gorm.Model
+
+	// The id of the user that linked this auth mechanism
+	UserID uint `json:"user_id"`
+
+	// Project ID of the project that this gitlab integration is linked with
+	ProjectID uint `json:"project_id"`
+
+	// URL of the Gitlab instance to talk to
+	ServerURL string `json:"server_url"`
+
+	// Personal access token from Gitlab for a sudo user
+	SudoAccessToken string `json:"sudo_access_token,omitempty"`
+
+	// Username of the sudo admin account holder
+	SudoUsername string `json:"sudo_username,omitempty"`
+}

+ 8 - 7
internal/models/project.go

@@ -49,13 +49,14 @@ type Project struct {
 	Infras []Infra `json:"infras"`
 
 	// auth mechanisms
-	KubeIntegrations  []ints.KubeIntegration  `json:"kube_integrations"`
-	BasicIntegrations []ints.BasicIntegration `json:"basic_integrations"`
-	OIDCIntegrations  []ints.OIDCIntegration  `json:"oidc_integrations"`
-	OAuthIntegrations []ints.OAuthIntegration `json:"oauth_integrations"`
-	AWSIntegrations   []ints.AWSIntegration   `json:"aws_integrations"`
-	GCPIntegrations   []ints.GCPIntegration   `json:"gcp_integrations"`
-	AzureIntegrations []ints.AzureIntegration `json:"azure_integrations"`
+	KubeIntegrations   []ints.KubeIntegration   `json:"kube_integrations"`
+	BasicIntegrations  []ints.BasicIntegration  `json:"basic_integrations"`
+	OIDCIntegrations   []ints.OIDCIntegration   `json:"oidc_integrations"`
+	OAuthIntegrations  []ints.OAuthIntegration  `json:"oauth_integrations"`
+	AWSIntegrations    []ints.AWSIntegration    `json:"aws_integrations"`
+	GCPIntegrations    []ints.GCPIntegration    `json:"gcp_integrations"`
+	AzureIntegrations  []ints.AzureIntegration  `json:"azure_integrations"`
+	GitlabIntegrations []ints.GitlabIntegration `json:"gitlab_integrations"`
 
 	PreviewEnvsEnabled  bool
 	RDSDatabasesEnabled bool

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

@@ -54,6 +54,11 @@ type AzureCredential struct {
 	AKSPassword []byte `json:"aks_password,omitempty"`
 }
 
+type GitlabCredential struct {
+	SudoAccessToken string `json:"sudo_access_token"`
+	SudoUsername    string `json:"sudo_username"`
+}
+
 type CredentialStorage interface {
 	WriteOAuthCredential(oauthIntegration *integrations.OAuthIntegration, data *OAuthCredential) error
 	GetOAuthCredential(oauthIntegration *integrations.OAuthIntegration) (*OAuthCredential, error)
@@ -67,4 +72,7 @@ type CredentialStorage interface {
 	WriteAzureCredential(azIntegration *integrations.AzureIntegration, data *AzureCredential) error
 	GetAzureCredential(azIntegration *integrations.AzureIntegration) (*AzureCredential, error)
 	CreateAzureToken(azIntegration *integrations.AzureIntegration) (string, error)
+	WriteGitlabCredential(giIntegration *integrations.GitlabIntegration, data *GitlabCredential) error
+	GetGitlabCredential(giIntegration *integrations.GitlabIntegration) (*GitlabCredential, error)
+	CreateGitlabToken(giIntegration *integrations.GitlabIntegration) (string, error)
 }

+ 140 - 0
internal/repository/gorm/auth.go

@@ -1570,3 +1570,143 @@ func (repo *AzureIntegrationRepository) DecryptAzureIntegrationData(
 
 	return nil
 }
+
+// GitlabIntegrationRepository uses gorm.DB for querying the database
+type GitlabIntegrationRepository struct {
+	db             *gorm.DB
+	key            *[32]byte
+	storageBackend credentials.CredentialStorage
+}
+
+// NewGitlabIntegrationRepository returns a GitlabIntegrationRepository which uses
+// gorm.DB for querying the database
+func NewGitlabIntegrationRepository(
+	db *gorm.DB,
+	key *[32]byte,
+	storageBackend credentials.CredentialStorage,
+) repository.GitlabIntegrationRepository {
+	return &GitlabIntegrationRepository{db, key, storageBackend}
+}
+
+// CreateIntegration adds a new GitlabIntegration row to the gitlab_integration table in the database
+func (repo *GitlabIntegrationRepository) CreateGitlabIntegration(gi *ints.GitlabIntegration) (*ints.GitlabIntegration, error) {
+	err := repo.EncryptGitlabIntegrationData(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.SudoAccessToken = gi.SudoAccessToken
+		credentialData.SudoUsername = gi.SudoUsername
+
+		gi.SudoAccessToken = ""
+		gi.SudoUsername = ""
+	}
+
+	project := &models.Project{}
+
+	if err := repo.db.Where("id = ?", gi.ProjectID).First(&project).Error; err != nil {
+		return nil, err
+	}
+
+	assoc := repo.db.Model(&project).Association("GitlabIntegrations")
+
+	if assoc.Error != nil {
+		return nil, assoc.Error
+	}
+
+	if err := assoc.Append(gi); err != nil {
+		return nil, err
+	}
+
+	if repo.storageBackend != nil {
+		err = repo.storageBackend.WriteGitlabCredential(gi, credentialData)
+
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	return gi, nil
+}
+
+func (repo *GitlabIntegrationRepository) ReadGitlabIntegration(projectID, id uint) (*ints.GitlabIntegration, error) {
+	gi := &ints.GitlabIntegration{}
+
+	if err := repo.db.Where("project_id = ? AND id = ?", projectID, id).First(&gi).Error; err != nil {
+		return nil, err
+	}
+
+	if repo.storageBackend != nil {
+		credentialData, err := repo.storageBackend.GetGitlabCredential(gi)
+
+		if err != nil {
+			return nil, err
+		}
+
+		gi.SudoAccessToken = credentialData.SudoAccessToken
+
+		gi.SudoUsername = credentialData.SudoUsername
+	}
+
+	err := repo.DecryptGitlabIntegrationData(gi, repo.key)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return gi, nil
+}
+
+func (repo *GitlabIntegrationRepository) ListGitlabIntegrationsByProjectID(projectID uint) ([]*ints.GitlabIntegration, error) {
+	gi := []*ints.GitlabIntegration{}
+
+	if err := repo.db.Where("project_id = ?", projectID).Find(&gi).Error; err != nil {
+		return nil, err
+	}
+
+	return gi, nil
+}
+
+// EncryptGitlabIntegrationData will encrypt the gitlab integration data before
+// writing to the DB
+func (repo *GitlabIntegrationRepository) EncryptGitlabIntegrationData(
+	gi *ints.GitlabIntegration,
+	key *[32]byte,
+) error {
+	if len(gi.SudoAccessToken) > 0 {
+		cipherData, err := encryption.Encrypt([]byte(gi.SudoAccessToken), key)
+
+		if err != nil {
+			return err
+		}
+
+		gi.SudoAccessToken = string(cipherData)
+	}
+
+	return nil
+}
+
+// DecryptGitlabIntegrationData will decrypt the gitlab integration data before
+// returning it from the DB
+func (repo *GitlabIntegrationRepository) DecryptGitlabIntegrationData(
+	gi *ints.GitlabIntegration,
+	key *[32]byte,
+) error {
+	if len(gi.SudoAccessToken) > 0 {
+		plaintext, err := encryption.Decrypt([]byte(gi.SudoAccessToken), key)
+
+		if err != nil {
+			return err
+		}
+
+		gi.SudoAccessToken = string(plaintext)
+	}
+
+	return nil
+}

+ 1 - 0
internal/repository/gorm/migrate.go

@@ -56,6 +56,7 @@ func AutoMigrate(db *gorm.DB, debug bool) error {
 		&ints.GCPIntegration{},
 		&ints.AWSIntegration{},
 		&ints.AzureIntegration{},
+		&ints.GitlabIntegration{},
 		&ints.TokenCache{},
 		&ints.ClusterTokenCache{},
 		&ints.RegTokenCache{},

+ 6 - 0
internal/repository/gorm/repository.go

@@ -33,6 +33,7 @@ type GormRepository struct {
 	githubAppInstallation     repository.GithubAppInstallationRepository
 	githubAppOAuthIntegration repository.GithubAppOAuthIntegrationRepository
 	slackIntegration          repository.SlackIntegrationRepository
+	gitlabIntegration         repository.GitlabIntegrationRepository
 	notificationConfig        repository.NotificationConfigRepository
 	jobNotificationConfig     repository.JobNotificationConfigRepository
 	buildEvent                repository.BuildEventRepository
@@ -149,6 +150,10 @@ func (t *GormRepository) SlackIntegration() repository.SlackIntegrationRepositor
 	return t.slackIntegration
 }
 
+func (t *GormRepository) GitlabIntegration() repository.GitlabIntegrationRepository {
+	return t.gitlabIntegration
+}
+
 func (t *GormRepository) NotificationConfig() repository.NotificationConfigRepository {
 	return t.notificationConfig
 }
@@ -219,6 +224,7 @@ func NewRepository(db *gorm.DB, key *[32]byte, storageBackend credentials.Creden
 		githubAppInstallation:     NewGithubAppInstallationRepository(db),
 		githubAppOAuthIntegration: NewGithubAppOAuthIntegrationRepository(db),
 		slackIntegration:          NewSlackIntegrationRepository(db, key),
+		gitlabIntegration:         NewGitlabIntegrationRepository(db, key, storageBackend),
 		notificationConfig:        NewNotificationConfigRepository(db),
 		jobNotificationConfig:     NewJobNotificationConfigRepository(db),
 		buildEvent:                NewBuildEventRepository(db),

+ 7 - 0
internal/repository/integrations.go

@@ -86,3 +86,10 @@ type GithubAppInstallationRepository interface {
 	ReadGithubAppInstallationByAccountIDs(accountIDs []int64) ([]*ints.GithubAppInstallation, error)
 	DeleteGithubAppInstallationByAccountID(accountID int64) error
 }
+
+// GitlabIntegrationRepository represents the set of queries on the GitlabIntegration model
+type GitlabIntegrationRepository interface {
+	CreateGitlabIntegration(user *ints.GitlabIntegration) (*ints.GitlabIntegration, error)
+	ReadGitlabIntegration(projectID, id uint) (*ints.GitlabIntegration, error)
+	ListGitlabIntegrationsByProjectID(projectID uint) ([]*ints.GitlabIntegration, error)
+}