From 833ebb331af8d2acedb51923432fad10218fa381 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Samin?= Date: Thu, 16 Sep 2021 09:14:54 +0200 Subject: [PATCH] feat(api,cli): delete secret from commandline (#5933) Signed-off-by: francois samin --- cli/cdsctl/encrypt.go | 23 +++++++++- engine/api/api_routes.go | 2 +- engine/api/project/key.go | 7 +++- engine/api/project_variable.go | 19 +++++++++ engine/api/project_variable_test.go | 9 ++++ sdk/cdsclient/client_project_variable.go | 7 ++++ sdk/cdsclient/interface.go | 1 + .../mock_cdsclient/interface_mock.go | 42 +++++++++++++++++++ 8 files changed, 107 insertions(+), 3 deletions(-) diff --git a/cli/cdsctl/encrypt.go b/cli/cdsctl/encrypt.go index 8faa7dbf6f..635bada043 100644 --- a/cli/cdsctl/encrypt.go +++ b/cli/cdsctl/encrypt.go @@ -59,7 +59,9 @@ my-data: 01234567890987654321`, } func encrypt() *cobra.Command { - return cli.NewCommand(encryptCmd, encryptRun, cli.SubCommands{encryptList()}, withAllCommandModifiers()...) + return cli.NewCommand(encryptCmd, encryptRun, cli.SubCommands{ + encryptList(), encryptDelete(), + }, withAllCommandModifiers()...) } func encryptRun(v cli.Values) error { @@ -96,3 +98,22 @@ func encryptListRun(v cli.Values) (cli.ListResult, error) { } return cli.AsListResult(secrets), nil } + +var encryptDeleteCmd = cli.Command{ + Name: "delete", + Short: "Delete the given encrypted variable of your CDS project", + Ctx: []cli.Arg{ + {Name: _ProjectKey}, + }, + Args: []cli.Arg{ + {Name: "name"}, + }, +} + +func encryptDelete() *cobra.Command { + return cli.NewDeleteCommand(encryptDeleteCmd, encryptDeleteRun, nil, withAllCommandModifiers()...) +} + +func encryptDeleteRun(v cli.Values) error { + return client.VariableEncryptDelete(v.GetString(_ProjectKey), v.GetString("name")) +} diff --git a/engine/api/api_routes.go b/engine/api/api_routes.go index 5b32544143..b9866dfd96 100644 --- a/engine/api/api_routes.go +++ b/engine/api/api_routes.go @@ -158,7 +158,7 @@ func (api *API) InitRouter() { r.Handle("/project/{permProjectKey}/group/import", Scope(sdk.AuthConsumerScopeProject), r.POST(api.postImportGroupsInProjectHandler)) r.Handle("/project/{permProjectKey}/group/{groupName}", Scope(sdk.AuthConsumerScopeProject), r.PUT(api.putGroupRoleOnProjectHandler), r.DELETE(api.deleteGroupFromProjectHandler)) r.Handle("/project/{permProjectKey}/variable", Scope(sdk.AuthConsumerScopeProject), r.GET(api.getVariablesInProjectHandler)) - r.Handle("/project/{permProjectKey}/encrypt", Scope(sdk.AuthConsumerScopeProject), r.POST(api.postEncryptVariableHandler)) + r.Handle("/project/{permProjectKey}/encrypt", Scope(sdk.AuthConsumerScopeProject), r.POST(api.postEncryptVariableHandler), r.DELETE(api.deleteEncryptVariableHandler)) r.Handle("/project/{permProjectKey}/encrypt/list", Scope(sdk.AuthConsumerScopeProject), r.GET(api.getListEncryptVariableHandler)) r.Handle("/project/{permProjectKey}/variable/audit", Scope(sdk.AuthConsumerScopeProject), r.GET(api.getVariablesAuditInProjectnHandler)) r.Handle("/project/{permProjectKey}/variable/{name}", Scope(sdk.AuthConsumerScopeProject), r.GET(api.getVariableInProjectHandler), r.POST(api.addVariableInProjectHandler), r.PUT(api.updateVariableInProjectHandler), r.DELETE(api.deleteVariableFromProjectHandler)) diff --git a/engine/api/project/key.go b/engine/api/project/key.go index a4c1634227..3d4bf52cd0 100644 --- a/engine/api/project/key.go +++ b/engine/api/project/key.go @@ -35,11 +35,16 @@ func ListEncryptedData(ctx context.Context, db gorp.SqlExecutor, projectID int64 var res []sdk.Secret query := gorpmapping.NewQuery("select content_name, token from encrypted_data where project_id = $1").Args(projectID) if err := gorpmapping.GetAll(ctx, db, query, &res); err != nil { - return nil, sdk.WithStack(err) + return nil, err } return res, nil } +func DeleteEncryptedVariable(db gorp.SqlExecutor, projectID int64, name string) error { + _, err := db.Exec("delete from encrypted_data where project_id = $1 and content_name = $2", projectID, name) + return sdk.WithStack(err) +} + // EncryptWithBuiltinKey encrypt a content with the builtin gpg key encode, compress it and encode with base64 func EncryptWithBuiltinKey(db gorp.SqlExecutor, projectID int64, name, content string) (string, error) { existingToken, err := db.SelectStr("select token from encrypted_data where project_id = $1 and content_name = $2", projectID, name) diff --git a/engine/api/project_variable.go b/engine/api/project_variable.go index 26a6c0f954..1d3b714806 100644 --- a/engine/api/project_variable.go +++ b/engine/api/project_variable.go @@ -72,6 +72,25 @@ func (api *API) postEncryptVariableHandler() service.Handler { } } +func (api *API) deleteEncryptVariableHandler() service.Handler { + return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error { + vars := mux.Vars(r) + key := vars[permProjectKey] + + p, err := project.Load(ctx, api.mustDB(), key) + if err != nil { + return err + } + + secretName := r.FormValue("name") + if secretName == "" { + return sdk.WithStack(sdk.ErrWrongRequest) + } + + return project.DeleteEncryptedVariable(api.mustDB(), p.ID, secretName) + } +} + func (api *API) getVariablesAuditInProjectnHandler() service.Handler { return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error { vars := mux.Vars(r) diff --git a/engine/api/project_variable_test.go b/engine/api/project_variable_test.go index e036f68f6a..520b135000 100644 --- a/engine/api/project_variable_test.go +++ b/engine/api/project_variable_test.go @@ -92,4 +92,13 @@ func Test_postEncryptVariableHandler(t *testing.T) { test.NoError(t, err) assert.Equal(t, "bar", decrypt) + + uri = router.GetRoute("DELETE", api.deleteEncryptVariableHandler, vars) + req = assets.NewAuthentifiedRequest(t, u, pass, "DELETE", uri+"?name="+v.Name, v) + + //Do the request + rec = httptest.NewRecorder() + api.Router.Mux.ServeHTTP(rec, req) + assert.Equal(t, 204, rec.Code) + } diff --git a/sdk/cdsclient/client_project_variable.go b/sdk/cdsclient/client_project_variable.go index 22d7c8ea5d..bfb2495f51 100644 --- a/sdk/cdsclient/client_project_variable.go +++ b/sdk/cdsclient/client_project_variable.go @@ -57,3 +57,10 @@ func (c *client) VariableListEncrypt(projectKey string) ([]sdk.Secret, error) { } return secrets, nil } + +func (c *client) VariableEncryptDelete(projectKey, name string) error { + if _, err := c.DeleteJSON(context.Background(), "/project/"+projectKey+"/encrypt?name="+url.QueryEscape(name), nil, nil); err != nil { + return err + } + return nil +} diff --git a/sdk/cdsclient/interface.go b/sdk/cdsclient/interface.go index 0203df2d17..a4d955fd1a 100644 --- a/sdk/cdsclient/interface.go +++ b/sdk/cdsclient/interface.go @@ -245,6 +245,7 @@ type ProjectVariablesClient interface { ProjectVariableUpdate(projectKey string, variable *sdk.Variable) error VariableEncrypt(projectKey string, varName string, content string) (*sdk.Variable, error) VariableListEncrypt(projectKey string) ([]sdk.Secret, error) + VariableEncryptDelete(projectKey, name string) error } // QueueClient exposes queue related functions diff --git a/sdk/cdsclient/mock_cdsclient/interface_mock.go b/sdk/cdsclient/mock_cdsclient/interface_mock.go index fc1f100fb4..b464a29f08 100644 --- a/sdk/cdsclient/mock_cdsclient/interface_mock.go +++ b/sdk/cdsclient/mock_cdsclient/interface_mock.go @@ -2766,6 +2766,20 @@ func (mr *MockProjectClientMockRecorder) VariableEncrypt(projectKey, varName, co return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VariableEncrypt", reflect.TypeOf((*MockProjectClient)(nil).VariableEncrypt), projectKey, varName, content) } +// VariableEncryptDelete mocks base method. +func (m *MockProjectClient) VariableEncryptDelete(projectKey, name string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "VariableEncryptDelete", projectKey, name) + ret0, _ := ret[0].(error) + return ret0 +} + +// VariableEncryptDelete indicates an expected call of VariableEncryptDelete. +func (mr *MockProjectClientMockRecorder) VariableEncryptDelete(projectKey, name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VariableEncryptDelete", reflect.TypeOf((*MockProjectClient)(nil).VariableEncryptDelete), projectKey, name) +} + // VariableListEncrypt mocks base method. func (m *MockProjectClient) VariableListEncrypt(projectKey string) ([]sdk.Secret, error) { m.ctrl.T.Helper() @@ -2957,6 +2971,20 @@ func (mr *MockProjectVariablesClientMockRecorder) VariableEncrypt(projectKey, va return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VariableEncrypt", reflect.TypeOf((*MockProjectVariablesClient)(nil).VariableEncrypt), projectKey, varName, content) } +// VariableEncryptDelete mocks base method. +func (m *MockProjectVariablesClient) VariableEncryptDelete(projectKey, name string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "VariableEncryptDelete", projectKey, name) + ret0, _ := ret[0].(error) + return ret0 +} + +// VariableEncryptDelete indicates an expected call of VariableEncryptDelete. +func (mr *MockProjectVariablesClientMockRecorder) VariableEncryptDelete(projectKey, name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VariableEncryptDelete", reflect.TypeOf((*MockProjectVariablesClient)(nil).VariableEncryptDelete), projectKey, name) +} + // VariableListEncrypt mocks base method. func (m *MockProjectVariablesClient) VariableListEncrypt(projectKey string) ([]sdk.Secret, error) { m.ctrl.T.Helper() @@ -7751,6 +7779,20 @@ func (mr *MockInterfaceMockRecorder) VariableEncrypt(projectKey, varName, conten return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VariableEncrypt", reflect.TypeOf((*MockInterface)(nil).VariableEncrypt), projectKey, varName, content) } +// VariableEncryptDelete mocks base method. +func (m *MockInterface) VariableEncryptDelete(projectKey, name string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "VariableEncryptDelete", projectKey, name) + ret0, _ := ret[0].(error) + return ret0 +} + +// VariableEncryptDelete indicates an expected call of VariableEncryptDelete. +func (mr *MockInterfaceMockRecorder) VariableEncryptDelete(projectKey, name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VariableEncryptDelete", reflect.TypeOf((*MockInterface)(nil).VariableEncryptDelete), projectKey, name) +} + // VariableListEncrypt mocks base method. func (m *MockInterface) VariableListEncrypt(projectKey string) ([]sdk.Secret, error) { m.ctrl.T.Helper()