From 5399721949262b8f08159c5d10be2ab1302783f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Samin?= Date: Wed, 25 Jan 2023 15:25:31 +0100 Subject: [PATCH] feat(api,cli,ui): disable project key (#6429) --- cli/cdsctl/project_key.go | 32 +++++++ cli/cobra_test.go | 3 +- engine/api/api_routes.go | 2 + engine/api/event/publish_project.go | 16 ++++ engine/api/project/dao_key.go | 12 +++ engine/api/project_key.go | 86 +++++++++++++++++++ engine/api/project_key_test.go | 16 ++++ engine/api/workflow/process_parameters.go | 10 ++- engine/api/workflow_run.go | 4 + engine/sql/api/263_key_disabled.sql | 7 ++ sdk/cdsclient/client_project_key.go | 10 +++ sdk/cdsclient/interface.go | 2 + .../mock_cdsclient/interface_mock.go | 84 ++++++++++++++++++ sdk/event_project.go | 8 ++ sdk/key.go | 1 + sdk/parameter.go | 12 --- tests/03_clictl_project.yml | 16 ++++ ui/src/app/shared/keys/list/keys.list.html | 3 +- ui/src/app/shared/keys/list/keys.list.scss | 4 + 19 files changed, 312 insertions(+), 16 deletions(-) create mode 100644 engine/sql/api/263_key_disabled.sql diff --git a/cli/cdsctl/project_key.go b/cli/cdsctl/project_key.go index e0890eb6f6..b98dc062b0 100644 --- a/cli/cdsctl/project_key.go +++ b/cli/cdsctl/project_key.go @@ -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()...), }) } @@ -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")) +} diff --git a/cli/cobra_test.go b/cli/cobra_test.go index 604a05829b..56c4a82c62 100644 --- a/cli/cobra_test.go +++ b/cli/cobra_test.go @@ -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) diff --git a/engine/api/api_routes.go b/engine/api/api_routes.go index 945fdb7974..6654c1c36f 100644 --- a/engine/api/api_routes.go +++ b/engine/api/api_routes.go @@ -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)) diff --git a/engine/api/event/publish_project.go b/engine/api/event/publish_project.go index b4e4c20f32..b4726d7adf 100644 --- a/engine/api/event/publish_project.go +++ b/engine/api/event/publish_project.go @@ -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 diff --git a/engine/api/project/dao_key.go b/engine/api/project/dao_key.go index cf2d3658e0..96bb2f7fbf 100644 --- a/engine/api/project/dao_key.go +++ b/engine/api/project/dao_key.go @@ -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 * diff --git a/engine/api/project_key.go b/engine/api/project_key.go index b3406104f7..e2df172497 100644 --- a/engine/api/project_key.go +++ b/engine/api/project_key.go @@ -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) + } +} diff --git a/engine/api/project_key_test.go b/engine/api/project_key_test.go index 44006c8217..7c1d4bd608 100644 --- a/engine/api/project_key_test.go +++ b/engine/api/project_key_test.go @@ -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) { diff --git a/engine/api/workflow/process_parameters.go b/engine/api/workflow/process_parameters.go index d9adb593ae..59556f3e20 100644 --- a/engine/api/workflow/process_parameters.go +++ b/engine/api/workflow/process_parameters.go @@ -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 } diff --git a/engine/api/workflow_run.go b/engine/api/workflow_run.go index 4d925c36ca..aa10570fbb 100644 --- a/engine/api/workflow_run.go +++ b/engine/api/workflow_run.go @@ -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, diff --git a/engine/sql/api/263_key_disabled.sql b/engine/sql/api/263_key_disabled.sql new file mode 100644 index 0000000000..bcbfae5d2e --- /dev/null +++ b/engine/sql/api/263_key_disabled.sql @@ -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; diff --git a/sdk/cdsclient/client_project_key.go b/sdk/cdsclient/client_project_key.go index b9b717d028..96635bebb3 100644 --- a/sdk/cdsclient/client_project_key.go +++ b/sdk/cdsclient/client_project_key.go @@ -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 +} diff --git a/sdk/cdsclient/interface.go b/sdk/cdsclient/interface.go index f35fa91753..02f3fe54dd 100644 --- a/sdk/cdsclient/interface.go +++ b/sdk/cdsclient/interface.go @@ -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 diff --git a/sdk/cdsclient/mock_cdsclient/interface_mock.go b/sdk/cdsclient/mock_cdsclient/interface_mock.go index 53a2b79de2..49bfd05f98 100644 --- a/sdk/cdsclient/mock_cdsclient/interface_mock.go +++ b/sdk/cdsclient/mock_cdsclient/interface_mock.go @@ -2889,6 +2889,34 @@ func (mr *MockProjectClientMockRecorder) ProjectKeysDelete(projectKey, keyProjec return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProjectKeysDelete", reflect.TypeOf((*MockProjectClient)(nil).ProjectKeysDelete), projectKey, keyProjectName) } +// ProjectKeysDisable mocks base method. +func (m *MockProjectClient) ProjectKeysDisable(projectKey, keyProjectName string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ProjectKeysDisable", projectKey, keyProjectName) + ret0, _ := ret[0].(error) + return ret0 +} + +// ProjectKeysDisable indicates an expected call of ProjectKeysDisable. +func (mr *MockProjectClientMockRecorder) ProjectKeysDisable(projectKey, keyProjectName interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProjectKeysDisable", reflect.TypeOf((*MockProjectClient)(nil).ProjectKeysDisable), projectKey, keyProjectName) +} + +// ProjectKeysEnable mocks base method. +func (m *MockProjectClient) ProjectKeysEnable(projectKey, keyProjectName string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ProjectKeysEnable", projectKey, keyProjectName) + ret0, _ := ret[0].(error) + return ret0 +} + +// ProjectKeysEnable indicates an expected call of ProjectKeysEnable. +func (mr *MockProjectClientMockRecorder) ProjectKeysEnable(projectKey, keyProjectName interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProjectKeysEnable", reflect.TypeOf((*MockProjectClient)(nil).ProjectKeysEnable), projectKey, keyProjectName) +} + // ProjectKeysList mocks base method. func (m *MockProjectClient) ProjectKeysList(projectKey string) ([]sdk.ProjectKey, error) { m.ctrl.T.Helper() @@ -3373,6 +3401,34 @@ func (mr *MockProjectKeysClientMockRecorder) ProjectKeysDelete(projectKey, keyPr return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProjectKeysDelete", reflect.TypeOf((*MockProjectKeysClient)(nil).ProjectKeysDelete), projectKey, keyProjectName) } +// ProjectKeysDisable mocks base method. +func (m *MockProjectKeysClient) ProjectKeysDisable(projectKey, keyProjectName string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ProjectKeysDisable", projectKey, keyProjectName) + ret0, _ := ret[0].(error) + return ret0 +} + +// ProjectKeysDisable indicates an expected call of ProjectKeysDisable. +func (mr *MockProjectKeysClientMockRecorder) ProjectKeysDisable(projectKey, keyProjectName interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProjectKeysDisable", reflect.TypeOf((*MockProjectKeysClient)(nil).ProjectKeysDisable), projectKey, keyProjectName) +} + +// ProjectKeysEnable mocks base method. +func (m *MockProjectKeysClient) ProjectKeysEnable(projectKey, keyProjectName string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ProjectKeysEnable", projectKey, keyProjectName) + ret0, _ := ret[0].(error) + return ret0 +} + +// ProjectKeysEnable indicates an expected call of ProjectKeysEnable. +func (mr *MockProjectKeysClientMockRecorder) ProjectKeysEnable(projectKey, keyProjectName interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProjectKeysEnable", reflect.TypeOf((*MockProjectKeysClient)(nil).ProjectKeysEnable), projectKey, keyProjectName) +} + // ProjectKeysList mocks base method. func (m *MockProjectKeysClient) ProjectKeysList(projectKey string) ([]sdk.ProjectKey, error) { m.ctrl.T.Helper() @@ -7536,6 +7592,34 @@ func (mr *MockInterfaceMockRecorder) ProjectKeysDelete(projectKey, keyProjectNam return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProjectKeysDelete", reflect.TypeOf((*MockInterface)(nil).ProjectKeysDelete), projectKey, keyProjectName) } +// ProjectKeysDisable mocks base method. +func (m *MockInterface) ProjectKeysDisable(projectKey, keyProjectName string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ProjectKeysDisable", projectKey, keyProjectName) + ret0, _ := ret[0].(error) + return ret0 +} + +// ProjectKeysDisable indicates an expected call of ProjectKeysDisable. +func (mr *MockInterfaceMockRecorder) ProjectKeysDisable(projectKey, keyProjectName interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProjectKeysDisable", reflect.TypeOf((*MockInterface)(nil).ProjectKeysDisable), projectKey, keyProjectName) +} + +// ProjectKeysEnable mocks base method. +func (m *MockInterface) ProjectKeysEnable(projectKey, keyProjectName string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ProjectKeysEnable", projectKey, keyProjectName) + ret0, _ := ret[0].(error) + return ret0 +} + +// ProjectKeysEnable indicates an expected call of ProjectKeysEnable. +func (mr *MockInterfaceMockRecorder) ProjectKeysEnable(projectKey, keyProjectName interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProjectKeysEnable", reflect.TypeOf((*MockInterface)(nil).ProjectKeysEnable), projectKey, keyProjectName) +} + // ProjectKeysList mocks base method. func (m *MockInterface) ProjectKeysList(projectKey string) ([]sdk.ProjectKey, error) { m.ctrl.T.Helper() diff --git a/sdk/event_project.go b/sdk/event_project.go index b84aadda45..a338d9f9e1 100644 --- a/sdk/event_project.go +++ b/sdk/event_project.go @@ -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"` diff --git a/sdk/key.go b/sdk/key.go index 0f1b74c297..416d917442 100644 --- a/sdk/key.go +++ b/sdk/key.go @@ -44,6 +44,7 @@ type ProjectKey struct { Type KeyType `json:"type" db:"type" cli:"type"` ProjectID int64 `json:"project_id" db:"project_id" cli:"-"` Builtin bool `json:"-" db:"builtin" cli:"-"` + Disabled bool `json:"disabled" db:"disabled" cli:"disabled"` } // ApplicationKey represent a key attach to an application diff --git a/sdk/parameter.go b/sdk/parameter.go index 6fd2f587e0..5940fa2d34 100644 --- a/sdk/parameter.go +++ b/sdk/parameter.go @@ -155,18 +155,6 @@ func ParametersFromProjectVariables(proj Project) map[string]string { return ParametersToMap(params) } -func ParametersFromProjectKeys(proj Project) map[string]string { - keys := make(map[string]string, 0) - for _, k := range proj.Keys { - kk := fmt.Sprintf("cds.key.%s.pub", k.Name) - keys[kk] = k.Public - - kk = fmt.Sprintf("cds.key.%s.id", k.Name) - keys[kk] = k.KeyID - } - return keys -} - // ParametersFromApplicationVariables returns a map from a slice of parameters func ParametersFromApplicationVariables(app Application) map[string]string { params := ApplicationVariablesToParameters("cds.app", app.Variables) diff --git a/tests/03_clictl_project.yml b/tests/03_clictl_project.yml index af63f615dc..8f4696c5ae 100644 --- a/tests/03_clictl_project.yml +++ b/tests/03_clictl_project.yml @@ -73,6 +73,22 @@ testcases: steps: - script: {{.cdsctl}} -f {{.cdsctl.config}} application variable delete ITCLIAPPPRJ TestApp var1 +- name: project keys list management + steps: + - script: {{.cdsctl}} -f {{.cdsctl.config}} project keys list ITCLIAPPPRJ + - script: {{.cdsctl}} -f {{.cdsctl.config}} project keys add ITCLIAPPPRJ mykeytest ssh + - script: {{.cdsctl}} -f {{.cdsctl.config}} project keys disable ITCLIAPPPRJ proj-mykeytest + - script: {{.cdsctl}} -f {{.cdsctl.config}} project keys list ITCLIAPPPRJ --fields name,disabled | grep proj-mykeytest + assertions: + - result.code ShouldEqual 0 + - result.systemout ShouldContainSubstring true + - script: {{.cdsctl}} -f {{.cdsctl.config}} project keys enable ITCLIAPPPRJ proj-mykeytest + - script: {{.cdsctl}} -f {{.cdsctl.config}} project keys list ITCLIAPPPRJ --fields name,disabled | grep proj-mykeytest + assertions: + - result.code ShouldEqual 0 + - result.systemout ShouldContainSubstring false + - script: {{.cdsctl}} -f {{.cdsctl.config}} project keys delete ITCLIAPPPRJ proj-mykeytest + - name: clean steps: - script: {{.cdsctl}} -f {{.cdsctl.config}} project delete --force ITCLIAPPPRJ diff --git a/ui/src/app/shared/keys/list/keys.list.html b/ui/src/app/shared/keys/list/keys.list.html index 11ab7bebf8..f261a927c4 100644 --- a/ui/src/app/shared/keys/list/keys.list.html +++ b/ui/src/app/shared/keys/list/keys.list.html @@ -10,7 +10,7 @@ - {{ k.name }} + {{ k.name }} (disabled) {{ k.type }} @@ -22,7 +22,6 @@ -