diff --git a/api/git/git.go b/api/git/git.go index ec4478e3a..a715c5ca9 100644 --- a/api/git/git.go +++ b/api/git/git.go @@ -47,10 +47,6 @@ func cloneRepository(repositoryURL, referenceName string, destination string) er } func (service *Service) ClonePrivateRepositoryWithDeploymentKey(repositoryURL, referenceName string, destination string, privateKeyPem []byte) error { - // url := "git@github.com:ssbkang/personal-development.git" - // directory := "personal-development" - // https://github.com/portainer/portainer-compose - signer, _ := ssh.ParsePrivateKey(privateKeyPem) auth := &gitSsh.PublicKeys{ User: "git", diff --git a/api/http/handler/deploymentkeys/deploymentkey_create.go b/api/http/handler/deploymentkeys/deploymentkey_create.go index 91345e7eb..5bcda5359 100644 --- a/api/http/handler/deploymentkeys/deploymentkey_create.go +++ b/api/http/handler/deploymentkeys/deploymentkey_create.go @@ -36,8 +36,6 @@ func (handler *Handler) deploymentkeyCreate(w http.ResponseWriter, r *http.Reque return &httperror.HandlerError{http.StatusConflict, "A deploymentkey for this resource already exists", portainer.ErrDeploymentkeyAlreadyExists} } - // Add a function to call and create public key and private key - private, public, err := handler.SignatureService.GenerateDeploymentKeyPair() if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to create private and public key pairs", err} diff --git a/api/http/handler/deploymentkeys/handler.go b/api/http/handler/deploymentkeys/handler.go index d93ee35a0..e409ad130 100644 --- a/api/http/handler/deploymentkeys/handler.go +++ b/api/http/handler/deploymentkeys/handler.go @@ -26,16 +26,12 @@ func NewHandler(bouncer *security.RequestBouncer) *Handler { Router: mux.NewRouter(), } h.Handle("/deployment_keys", - // bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.deploymentkeyCreate))).Methods(http.MethodPost) - bouncer.PublicAccess(httperror.LoggerHandler(h.deploymentkeyCreate))).Methods(http.MethodPost) + bouncer.AuthorizedAccess(httperror.LoggerHandler(h.deploymentkeyCreate))).Methods(http.MethodPost) h.Handle("/deployment_keys", - // bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.deploymentkeyList))).Methods(http.MethodGet) - bouncer.PublicAccess(httperror.LoggerHandler(h.deploymentkeyList))).Methods(http.MethodGet) + bouncer.AuthorizedAccess(httperror.LoggerHandler(h.deploymentkeyList))).Methods(http.MethodGet) h.Handle("/deployment_keys/{id}", - // bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.deploymentkeyList))).Methods(http.MethodGet) - bouncer.PublicAccess(httperror.LoggerHandler(h.deploymentkeyInspect))).Methods(http.MethodGet) + bouncer.AuthorizedAccess(httperror.LoggerHandler(h.deploymentkeyInspect))).Methods(http.MethodGet) h.Handle("/deployment_keys/{id}", - // bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.deploymentkeyDelete))).Methods(http.MethodDelete) - bouncer.PublicAccess(httperror.LoggerHandler(h.deploymentkeyDelete))).Methods(http.MethodDelete) + bouncer.AuthorizedAccess(httperror.LoggerHandler(h.deploymentkeyDelete))).Methods(http.MethodDelete) return h } diff --git a/api/http/handler/stacks/create_compose_stack.go b/api/http/handler/stacks/create_compose_stack.go index f9a6b9136..709584513 100644 --- a/api/http/handler/stacks/create_compose_stack.go +++ b/api/http/handler/stacks/create_compose_stack.go @@ -87,17 +87,16 @@ func (handler *Handler) createComposeStackFromFileContent(w http.ResponseWriter, return response.JSON(w, stack) } -// Update struc to cater for deployment key type composeStackFromGitRepositoryPayload struct { - Name string - RepositoryURL string - RepositoryReferenceName string - RepositoryAuthentication bool - RepositoryDeploymentKey string - RepositoryUsername string - RepositoryPassword string - ComposeFilePathInRepository string - Env []portainer.Pair + Name string + RepositoryURL string + RepositoryReferenceName string + RepositoryAuthenticationType string + RepositoryDeploymentKey string + RepositoryUsername string + RepositoryPassword string + ComposeFilePathInRepository string + Env []portainer.Pair } func (payload *composeStackFromGitRepositoryPayload) Validate(r *http.Request) error { @@ -107,8 +106,11 @@ func (payload *composeStackFromGitRepositoryPayload) Validate(r *http.Request) e if govalidator.IsNull(payload.RepositoryURL) || !govalidator.IsURL(payload.RepositoryURL) { return portainer.Error("Invalid repository URL. Must correspond to a valid URL format") } - if payload.RepositoryAuthentication && (govalidator.IsNull(payload.RepositoryUsername) || govalidator.IsNull(payload.RepositoryPassword)) { - return portainer.Error("Invalid repository credentials. Username and password must be specified when authentication is enabled") + if payload.RepositoryAuthenticationType == "BasicAuth" && (govalidator.IsNull(payload.RepositoryUsername) || govalidator.IsNull(payload.RepositoryPassword)) { + return portainer.Error("Invalid repository credentials or deploymenet key. Either username & password must be specified when authentication type is BasicAuth") + } + if payload.RepositoryAuthenticationType == "DeploymentKey" && govalidator.IsNull(payload.RepositoryDeploymentKey) { + return portainer.Error("Invalid repository deploymenet key. A deployment key must be specified when authentication type is DeploymentKey") } if govalidator.IsNull(payload.ComposeFilePathInRepository) { payload.ComposeFilePathInRepository = filesystem.ComposeFileDefaultName @@ -147,15 +149,22 @@ func (handler *Handler) createComposeStackFromGitRepository(w http.ResponseWrite projectPath := handler.FileService.GetStackProjectPath(strconv.Itoa(int(stack.ID))) stack.ProjectPath = projectPath - // Add Deployment Key gitCloneParams := &cloneRepositoryParameters{ - url: payload.RepositoryURL, - referenceName: payload.RepositoryReferenceName, - path: projectPath, - authentication: payload.RepositoryAuthentication, - deploymentKey: payload.RepositoryDeploymentKey, - username: payload.RepositoryUsername, - password: payload.RepositoryPassword, + url: payload.RepositoryURL, + referenceName: payload.RepositoryReferenceName, + path: projectPath, + authenticationType: payload.RepositoryAuthenticationType, + deploymentKey: nil, + username: payload.RepositoryUsername, + password: payload.RepositoryPassword, + } + + if payload.RepositoryDeploymentKey != "" { + deploymentKey, err := handler.DeploymentKeyService.DeploymentKeyByName(payload.Name) + if err != nil && err != portainer.ErrObjectNotFound { + return &httperror.HandlerError{http.StatusInternalServerError, "An error occurred retrieving deploymentkey from the database", err} + } + gitCloneParams.deploymentKey = deploymentKey.PrivateKey } doCleanUp := true diff --git a/api/http/handler/stacks/create_swarm_stack.go b/api/http/handler/stacks/create_swarm_stack.go index 3453e656d..f1cdac9ac 100644 --- a/api/http/handler/stacks/create_swarm_stack.go +++ b/api/http/handler/stacks/create_swarm_stack.go @@ -93,16 +93,16 @@ func (handler *Handler) createSwarmStackFromFileContent(w http.ResponseWriter, r // Update struc to cater for deployment key type swarmStackFromGitRepositoryPayload struct { - Name string - SwarmID string - Env []portainer.Pair - RepositoryURL string - RepositoryReferenceName string - RepositoryAuthentication bool - RepositoryDeploymentKey string - RepositoryUsername string - RepositoryPassword string - ComposeFilePathInRepository string + Name string + SwarmID string + Env []portainer.Pair + RepositoryURL string + RepositoryReferenceName string + RepositoryAuthenticationType string + RepositoryDeploymentKey string + RepositoryUsername string + RepositoryPassword string + ComposeFilePathInRepository string } func (payload *swarmStackFromGitRepositoryPayload) Validate(r *http.Request) error { @@ -112,18 +112,15 @@ func (payload *swarmStackFromGitRepositoryPayload) Validate(r *http.Request) err if govalidator.IsNull(payload.SwarmID) { return portainer.Error("Invalid Swarm ID") } - if govalidator.IsNull(payload.RepositoryURL) || !govalidator.IsURL(payload.RepositoryURL) { return portainer.Error("Invalid repository URL. Must correspond to a valid URL format") } - - // Need to improve validators for SSH based URL and deployment key type - /* - if payload.RepositoryAuthentication && (govalidator.IsNull(payload.RepositoryDeploymentKey)) || (govalidator.IsNull(payload.RepositoryUsername) || govalidator.IsNull(payload.RepositoryPassword)) { - return portainer.Error("Invalid repository credentials or deploymenet key. Either Username & password or a deployment key must be specified when authentication is enabled") + if payload.RepositoryAuthenticationType == "BasicAuth" && (govalidator.IsNull(payload.RepositoryUsername) || govalidator.IsNull(payload.RepositoryPassword)) { + return portainer.Error("Invalid repository credentials or deploymenet key. Either username & password must be specified when authentication type is BasicAuth") + } + if payload.RepositoryAuthenticationType == "DeploymentKey" && govalidator.IsNull(payload.RepositoryDeploymentKey) { + return portainer.Error("Invalid repository deploymenet key. A deployment key must be specified when authentication type is DeploymentKey") } - */ - if govalidator.IsNull(payload.ComposeFilePathInRepository) { payload.ComposeFilePathInRepository = filesystem.ComposeFileDefaultName } @@ -163,13 +160,22 @@ func (handler *Handler) createSwarmStackFromGitRepository(w http.ResponseWriter, stack.ProjectPath = projectPath gitCloneParams := &cloneRepositoryParameters{ - url: payload.RepositoryURL, - referenceName: payload.RepositoryReferenceName, - path: projectPath, - authentication: payload.RepositoryAuthentication, - deploymentKey: payload.RepositoryDeploymentKey, - username: payload.RepositoryUsername, - password: payload.RepositoryPassword, + url: payload.RepositoryURL, + referenceName: payload.RepositoryReferenceName, + path: projectPath, + authenticationType: payload.RepositoryAuthenticationType, + deploymentKey: nil, + username: payload.RepositoryUsername, + password: payload.RepositoryPassword, + } + + if payload.RepositoryDeploymentKey != "" { + deploymentKey, err := handler.DeploymentKeyService.DeploymentKeyByName(payload.RepositoryDeploymentKey) + if err != nil { + return &httperror.HandlerError{http.StatusInternalServerError, "An error occurred retrieving deploymentkey from the database", err} + } + + gitCloneParams.deploymentKey = deploymentKey.PrivateKey } doCleanUp := true diff --git a/api/http/handler/stacks/git.go b/api/http/handler/stacks/git.go index 55ba22d67..cf2b76d25 100644 --- a/api/http/handler/stacks/git.go +++ b/api/http/handler/stacks/git.go @@ -1,22 +1,21 @@ package stacks type cloneRepositoryParameters struct { - url string - referenceName string - path string - authentication bool - deploymentKey string - username string - password string + url string + referenceName string + path string + authenticationType string + deploymentKey []byte + username string + password string } func (handler *Handler) cloneGitRepository(parameters *cloneRepositoryParameters) error { - if parameters.authentication && parameters.username != "" && parameters.password != "" { + if parameters.authenticationType != "" && parameters.username != "" && parameters.password != "" { return handler.GitService.ClonePrivateRepositoryWithBasicAuth(parameters.url, parameters.referenceName, parameters.path, parameters.username, parameters.password) } - if parameters.authentication && parameters.deploymentKey != "" { - deploymentKey, _ := handler.DeploymentKeyService.DeploymentKeyByName(parameters.deploymentKey) - return handler.GitService.ClonePrivateRepositoryWithDeploymentKey(parameters.url, parameters.referenceName, parameters.path, deploymentKey.PrivateKey) + if parameters.authenticationType != "" && parameters.deploymentKey != nil { + return handler.GitService.ClonePrivateRepositoryWithDeploymentKey(parameters.url, parameters.referenceName, parameters.path, parameters.deploymentKey) } return handler.GitService.ClonePublicRepository(parameters.url, parameters.referenceName, parameters.path) }