Skip to content

Commit

Permalink
feat(api,cli,ui): disable project key (#6429)
Browse files Browse the repository at this point in the history
  • Loading branch information
fsamin authored Jan 25, 2023
1 parent 87dbb36 commit 5399721
Show file tree
Hide file tree
Showing 19 changed files with 312 additions and 16 deletions.
32 changes: 32 additions & 0 deletions cli/cdsctl/project_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ func projectKey() *cobra.Command {
cli.NewCommand(projectKeyCreateCmd, projectCreateKeyRun, nil, withAllCommandModifiers()...),
cli.NewListCommand(projectKeyListCmd, projectListKeyRun, nil, withAllCommandModifiers()...),
cli.NewCommand(projectKeyDeleteCmd, projectDeleteKeyRun, nil, withAllCommandModifiers()...),
cli.NewCommand(projectDisableKeyCmd, projectDisableKeyRun, nil, withAllCommandModifiers()...),
cli.NewCommand(projectEnableKeyCmd, projectEnableKeyRun, nil, withAllCommandModifiers()...),
})
}

Expand Down Expand Up @@ -79,3 +81,33 @@ var projectKeyDeleteCmd = cli.Command{
func projectDeleteKeyRun(v cli.Values) error {
return client.ProjectKeysDelete(v.GetString(_ProjectKey), v.GetString("key-name"))
}

var projectDisableKeyCmd = cli.Command{
Name: "disable",
Short: "Disable CDS project key",
Ctx: []cli.Arg{
{Name: _ProjectKey},
},
Args: []cli.Arg{
{Name: "key-name"},
},
}

func projectDisableKeyRun(v cli.Values) error {
return client.ProjectKeysDisable(v.GetString(_ProjectKey), v.GetString("key-name"))
}

var projectEnableKeyCmd = cli.Command{
Name: "enable",
Short: "Enable CDS project key",
Ctx: []cli.Arg{
{Name: _ProjectKey},
},
Args: []cli.Arg{
{Name: "key-name"},
},
}

func projectEnableKeyRun(v cli.Values) error {
return client.ProjectKeysEnable(v.GetString(_ProjectKey), v.GetString("key-name"))
}
3 changes: 2 additions & 1 deletion cli/cobra_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ func TestListItem(t *testing.T) {
Public: "pubb",
Private: "privv",
ProjectID: 1,
Disabled: true,
}

result := listItem(keyProject, nil, false, nil, false, map[string]string{})
assert.Equal(t, 3, len(result))
assert.Equal(t, 4, len(result))

result = listItem(keyProject, nil, false, []string{"name"}, false, map[string]string{})
assert.Equal(t, map[string]string{"name": "myKey"}, result)
Expand Down
2 changes: 2 additions & 0 deletions engine/api/api_routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ func (api *API) InitRouter() {
r.Handle("/project/{permProjectKey}/notifications", Scope(sdk.AuthConsumerScopeProject), r.GET(api.getProjectNotificationsHandler, DEPRECATED))
r.Handle("/project/{permProjectKey}/keys", Scope(sdk.AuthConsumerScopeProject), r.GET(api.getKeysInProjectHandler), r.POST(api.addKeyInProjectHandler))
r.Handle("/project/{permProjectKey}/keys/{name}", Scope(sdk.AuthConsumerScopeProject), r.DELETE(api.deleteKeyInProjectHandler))
r.Handle("/project/{permProjectKey}/keys/{name}/disable", Scope(sdk.AuthConsumerScopeProject), r.POST(api.postDisableKeyInProjectHandler))
r.Handle("/project/{permProjectKey}/keys/{name}/enable", Scope(sdk.AuthConsumerScopeProject), r.POST(api.postEnableKeyInProjectHandler))

// Import Application
r.Handle("/project/{permProjectKey}/import/application", Scope(sdk.AuthConsumerScopeProject), r.POST(api.postApplicationImportHandler))
Expand Down
16 changes: 16 additions & 0 deletions engine/api/event/publish_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,22 @@ func PublishAddProjectKey(ctx context.Context, p *sdk.Project, k sdk.ProjectKey,
PublishProjectEvent(ctx, e, p.Key, u)
}

func PublishDisableProjectKey(ctx context.Context, p *sdk.Project, k sdk.ProjectKey, u sdk.Identifiable) {
k.Private = sdk.PasswordPlaceholder
e := sdk.EventProjectKeyDisable{
Key: k,
}
PublishProjectEvent(ctx, e, p.Key, u)
}

func PublishEnableProjectKey(ctx context.Context, p *sdk.Project, k sdk.ProjectKey, u sdk.Identifiable) {
k.Private = sdk.PasswordPlaceholder
e := sdk.EventProjectKeyEnable{
Key: k,
}
PublishProjectEvent(ctx, e, p.Key, u)
}

// PublishDeleteProjectKey publishes an event on deleting a project key
func PublishDeleteProjectKey(ctx context.Context, p *sdk.Project, k sdk.ProjectKey, u sdk.Identifiable) {
k.Private = sdk.PasswordPlaceholder
Expand Down
12 changes: 12 additions & 0 deletions engine/api/project/dao_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,18 @@ func DeleteProjectKey(db gorp.SqlExecutor, projectID int64, keyName string) erro
return sdk.WrapError(err, "Cannot delete key %s", keyName)
}

// DisableProjectKey Disable the given key from the given project
func DisableProjectKey(db gorp.SqlExecutor, projectID int64, keyName string) error {
_, err := db.Exec("UPDATE project_key SET disabled = true WHERE project_id = $1 AND name = $2", projectID, keyName)
return sdk.WrapError(err, "unable to disable key %s", keyName)
}

// EnableProjectKey Enable the given key from the given project
func EnableProjectKey(db gorp.SqlExecutor, projectID int64, keyName string) error {
_, err := db.Exec("UPDATE project_key SET disabled = false WHERE project_id = $1 AND name = $2", projectID, keyName)
return sdk.WrapError(err, "unable to enable key %s", keyName)
}

func loadBuiltinKey(ctx context.Context, db gorp.SqlExecutor, projectID int64) (*sdk.ProjectKey, error) {
query := gorpmapping.NewQuery(`
SELECT *
Expand Down
86 changes: 86 additions & 0 deletions engine/api/project_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,89 @@ func (api *API) addKeyInProjectHandler() service.Handler {
return service.WriteJSON(w, newKey, http.StatusOK)
}
}

func (api *API) postDisableKeyInProjectHandler() service.Handler {
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
if !isAdmin(ctx) {
return sdk.ErrForbidden
}

vars := mux.Vars(r)
key := vars[permProjectKey]
keyName := vars["name"]

p, err := project.Load(ctx, api.mustDB(), key, project.LoadOptions.WithKeys)
if err != nil {
return err
}

tx, err := api.mustDB().Begin()
if err != nil {
return err
}
defer tx.Rollback() // nolint

var updateKey sdk.ProjectKey
for _, k := range p.Keys {
if k.Name == keyName {
updateKey = k
updateKey.Disabled = true
if err := project.DisableProjectKey(tx, p.ID, keyName); err != nil {
return err
}
break
}
}

if err := tx.Commit(); err != nil {
return err
}

event.PublishDisableProjectKey(ctx, p, updateKey, getUserConsumer(ctx))

return service.WriteJSON(w, nil, http.StatusOK)
}
}

func (api *API) postEnableKeyInProjectHandler() service.Handler {
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
if !isAdmin(ctx) {
return sdk.ErrForbidden
}

vars := mux.Vars(r)
key := vars[permProjectKey]
keyName := vars["name"]

p, err := project.Load(ctx, api.mustDB(), key, project.LoadOptions.WithKeys)
if err != nil {
return err
}

tx, err := api.mustDB().Begin()
if err != nil {
return err
}
defer tx.Rollback() // nolint

var updateKey sdk.ProjectKey
for _, k := range p.Keys {
if k.Name == keyName {
updateKey = k
updateKey.Disabled = false
if err := project.EnableProjectKey(tx, p.ID, keyName); err != nil {
return err
}
break
}
}

if err := tx.Commit(); err != nil {
return sdk.WithStack(err)
}

event.PublishEnableProjectKey(ctx, p, updateKey, getUserConsumer(ctx))

return service.WriteJSON(w, nil, http.StatusOK)
}
}
16 changes: 16 additions & 0 deletions engine/api/project_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,22 @@ func Test_getKeysInProjectHandler(t *testing.T) {
var keys []sdk.ProjectKey
test.NoError(t, json.Unmarshal(w.Body.Bytes(), &keys))
assert.Equal(t, len(keys), 1)

uri = router.GetRoute("POST", api.postDisableKeyInProjectHandler, vars)
req, err = http.NewRequest("POST", uri, nil)
test.NoError(t, err)
assets.AuthentifyRequest(t, req, u, pass)
w = httptest.NewRecorder()
router.Mux.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)

uri = router.GetRoute("POST", api.postEnableKeyInProjectHandler, vars)
req, err = http.NewRequest("POST", uri, nil)
test.NoError(t, err)
assets.AuthentifyRequest(t, req, u, pass)
w = httptest.NewRecorder()
router.Mux.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)
}

func Test_deleteKeyInProjectHandler(t *testing.T) {
Expand Down
10 changes: 9 additions & 1 deletion engine/api/workflow/process_parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,15 @@ func getBuildParameterFromNodeContext(proj sdk.Project, w sdk.Workflow, runConte
vars[k] = v
}

tmpProj = sdk.ParametersFromProjectKeys(proj)
for _, k := range proj.Keys {
if k.Disabled {
continue
}
kk := fmt.Sprintf("cds.key.%s.pub", k.Name)
tmpProj[kk] = k.Public
kk = fmt.Sprintf("cds.key.%s.id", k.Name)
tmpProj[kk] = k.KeyID
}
for k, v := range tmpProj {
vars[k] = v
}
Expand Down
4 changes: 4 additions & 0 deletions engine/api/workflow_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,10 @@ func saveWorkflowRunSecrets(ctx context.Context, db *gorp.DbMap, projID int64, w
}

for _, k := range p.Keys {
log.Debug(ctx, "checking %q (disabled:%v)", k.Name, k.Disabled)
if k.Disabled {
continue // skip disabled keys, so they are not usable in workers
}
wrSecret := sdk.WorkflowRunSecret{
WorkflowRunID: wr.ID,
Context: workflow.SecretProjContext,
Expand Down
7 changes: 7 additions & 0 deletions engine/sql/api/263_key_disabled.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

-- +migrate Up
ALTER TABLE project_key ADD COLUMN disabled BOOLEAN NOT NULL DEFAULT FALSE;
UPDATE project_key SET disabled = false;

-- +migrate Down
ALTER TABLE project_key DROP COLUMN disabled;
10 changes: 10 additions & 0 deletions sdk/cdsclient/client_project_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,13 @@ func (c *client) ProjectKeysDelete(projectKey string, keyName string) error {
_, _, _, err := c.Request(context.Background(), "DELETE", "/project/"+projectKey+"/keys/"+url.QueryEscape(keyName), nil)
return err
}

func (c *client) ProjectKeysDisable(projectKey string, keyProjectName string) error {
_, err := c.PostJSON(context.Background(), "/project/"+projectKey+"/keys/"+url.QueryEscape(keyProjectName)+"/disable", nil, nil)
return err
}

func (c *client) ProjectKeysEnable(projectKey string, keyProjectName string) error {
_, err := c.PostJSON(context.Background(), "/project/"+projectKey+"/keys/"+url.QueryEscape(keyProjectName)+"/enable", nil, nil)
return err
}
2 changes: 2 additions & 0 deletions sdk/cdsclient/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,8 @@ type ProjectKeysClient interface {
ProjectKeysList(projectKey string) ([]sdk.ProjectKey, error)
ProjectKeyCreate(projectKey string, key *sdk.ProjectKey) error
ProjectKeysDelete(projectKey string, keyProjectName string) error
ProjectKeysDisable(projectKey string, keyProjectName string) error
ProjectKeysEnable(projectKey string, keyProjectName string) error
}

// ProjectVariablesClient exposes project variables related functions
Expand Down
84 changes: 84 additions & 0 deletions sdk/cdsclient/mock_cdsclient/interface_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions sdk/event_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ type EventProjectKeyDelete struct {
Key ProjectKey `json:"key"`
}

type EventProjectKeyDisable struct {
Key ProjectKey `json:"key"`
}

type EventProjectKeyEnable struct {
Key ProjectKey `json:"key"`
}

// EventProjectVCSServerAdd represents the event when adding a project vcs server
type EventProjectVCSServerAdd struct {
VCSServerName string `json:"vcs_server"`
Expand Down
Loading

0 comments on commit 5399721

Please sign in to comment.