Skip to content

Commit

Permalink
Merge pull request #1267 from posit-dev/mm-verify-redeployment-creden…
Browse files Browse the repository at this point in the history
…tial

Deployment API: fail if redeployment is to a different server
  • Loading branch information
mmarchetti authored Apr 3, 2024
2 parents 10948ca + 3408379 commit 09a6045
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 7 deletions.
8 changes: 4 additions & 4 deletions internal/publish/publish_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,12 +254,12 @@ func (s *PublishSuite) publishWithClient(

publisher.emitErrorEvents(err, s.log)
}
if stateStore.Target != nil {
if target != nil {
if target != nil {
// Creation date is not updated on deployment
s.Equal(stateStore.Target.CreatedAt, stateStore.Target.CreatedAt)
if stateStore.Target != nil {
// Successful redeployment should update the timestamp.
s.NotEqual(stateStore.Target.CreatedAt, stateStore.Target.DeployedAt)
} else {
s.Equal(stateStore.Target.CreatedAt, stateStore.Target.DeployedAt)
}
}
couldCreateDeployment := (authErr == nil && capErr == nil && createErr == nil)
Expand Down
6 changes: 6 additions & 0 deletions internal/services/api/post_deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,17 @@ func PostDeploymentHandlerFunc(
if err != nil {
if errors.Is(err, accounts.ErrAccountNotFound) {
NotFound(w, log, err)
} else if errors.Is(err, state.ErrServerURLMismatch) {
// Redeployments must go to the same server
w.WriteHeader(http.StatusConflict)
w.Write([]byte(err.Error()))
return
} else {
BadRequest(w, req, log, err)
}
return
}

response := PostDeploymentsReponse{
LocalID: localID,
}
Expand Down
55 changes: 53 additions & 2 deletions internal/services/api/post_deployment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (

"github.com/gorilla/mux"
"github.com/rstudio/connect-client/internal/accounts"
"github.com/rstudio/connect-client/internal/config"
"github.com/rstudio/connect-client/internal/deployment"
"github.com/rstudio/connect-client/internal/events"
"github.com/rstudio/connect-client/internal/logging"
"github.com/rstudio/connect-client/internal/publish"
Expand Down Expand Up @@ -80,7 +82,11 @@ func (s *PostDeploymentHandlerFuncSuite) TestPostDeploymentHandlerFunc() {
s.Equal("local", accountName)
s.Equal("default", configName)
s.Equal("", saveName)
return state.Empty(), nil

st := state.Empty()
st.Account = &accounts.Account{}
st.Target = deployment.New()
return st, nil
}
handler := PostDeploymentHandlerFunc(util.AbsolutePath{}, log, lister, events.NewNullEmitter())
handler(rec, req)
Expand Down Expand Up @@ -121,6 +127,47 @@ func (s *PostDeploymentHandlerFuncSuite) TestPostDeploymentHandlerFuncStateErr()
s.Equal(http.StatusBadRequest, rec.Result().StatusCode)
}

func (s *PostDeploymentHandlerFuncSuite) TestPostDeploymentHandlerFuncWrongServer() {
log := logging.New()

deploymentName := "myDeployment"
rec := httptest.NewRecorder()
req, err := http.NewRequest("POST", "/api/deployments/"+deploymentName, nil)
s.NoError(err)
req = mux.SetURLVars(req, map[string]string{"name": deploymentName})

originalAcct := &accounts.Account{
URL: "https://connect.example.com",
}
newAcct := &accounts.Account{
URL: "https://some.other.server.com",
}

d := deployment.New()
d.ServerURL = originalAcct.URL
err = d.WriteFile(deployment.GetDeploymentPath(s.cwd, deploymentName))
s.NoError(err)

cfg := config.New()
cfg.Type = config.ContentTypeHTML
err = cfg.WriteFile(config.GetConfigPath(s.cwd, "default"))
s.NoError(err)

lister := &accounts.MockAccountList{}
lister.On("GetAccountByName", "newAcct").Return(newAcct, nil)

req.Body = io.NopCloser(strings.NewReader(
`{
"account": "newAcct",
"config": "default"
}`))

handler := PostDeploymentHandlerFunc(s.cwd, log, lister, events.NewNullEmitter())
handler(rec, req)

s.Equal(http.StatusConflict, rec.Result().StatusCode)
}

func (s *PostDeploymentHandlerFuncSuite) TestPostDeploymentHandlerFuncPublishErr() {
log := logging.New()
rec := httptest.NewRecorder()
Expand All @@ -134,7 +181,11 @@ func (s *PostDeploymentHandlerFuncSuite) TestPostDeploymentHandlerFuncPublishErr
path util.AbsolutePath,
accountName, configName, targetName, saveName string,
accountList accounts.AccountList) (*state.State, error) {
return state.Empty(), nil

st := state.Empty()
st.Account = &accounts.Account{}
st.Target = deployment.New()
return st, nil
}

testErr := errors.New("test error from PublishDirectory")
Expand Down
6 changes: 5 additions & 1 deletion internal/state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ func Empty() *State {
}
}

var ErrServerURLMismatch = errors.New("the account provided is for a different server; it must match the server for this deployment")

func New(path util.AbsolutePath, accountName, configName, targetName string, saveName string, accountList accounts.AccountList) (*State, error) {
var target *deployment.Deployment
var account *accounts.Account
Expand Down Expand Up @@ -126,7 +128,9 @@ func New(path util.AbsolutePath, accountName, configName, targetName string, sav
return nil, err
}

target.ServerURL = account.URL
if target.ServerURL != "" && target.ServerURL != account.URL {
return nil, ErrServerURLMismatch
}

if configName == "" {
configName = config.DefaultConfigName
Expand Down

0 comments on commit 09a6045

Please sign in to comment.