From 34d36e9e24b9bb393206c0c653006412200106af Mon Sep 17 00:00:00 2001 From: Simone Gotti Date: Mon, 8 Jul 2019 10:32:45 +0200 Subject: [PATCH] gateway: implement secret update --- internal/services/gateway/action/secret.go | 55 +++++++++++++++++++ internal/services/gateway/api/client.go | 22 ++++++++ internal/services/gateway/api/secret.go | 62 ++++++++++++++++++++++ internal/services/gateway/gateway.go | 3 ++ 4 files changed, 142 insertions(+) diff --git a/internal/services/gateway/action/secret.go b/internal/services/gateway/action/secret.go index a74e1c0..15ec9d0 100644 --- a/internal/services/gateway/action/secret.go +++ b/internal/services/gateway/action/secret.go @@ -102,6 +102,61 @@ func (h *ActionHandler) CreateSecret(ctx context.Context, req *CreateSecretReque return rs, nil } +type UpdateSecretRequest struct { + SecretName string + + Name string + + ParentType types.ConfigType + ParentRef string + + Type types.SecretType + + // internal secret + Data map[string]string + + // external secret + SecretProviderID string + Path string +} + +func (h *ActionHandler) UpdateSecret(ctx context.Context, req *UpdateSecretRequest) (*csapi.Secret, error) { + isVariableOwner, err := h.IsVariableOwner(ctx, req.ParentType, req.ParentRef) + if err != nil { + return nil, errors.Errorf("failed to determine ownership: %w", err) + } + if !isVariableOwner { + return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) + } + + if !util.ValidateName(req.Name) { + return nil, util.NewErrBadRequest(errors.Errorf("invalid secret name %q", req.Name)) + } + + s := &types.Secret{ + Name: req.Name, + Type: req.Type, + Data: req.Data, + } + + var resp *http.Response + var rs *csapi.Secret + switch req.ParentType { + case types.ConfigTypeProjectGroup: + h.log.Infof("updating project group secret") + rs, resp, err = h.configstoreClient.UpdateProjectGroupSecret(ctx, req.ParentRef, req.SecretName, s) + case types.ConfigTypeProject: + h.log.Infof("updating project secret") + rs, resp, err = h.configstoreClient.UpdateProjectSecret(ctx, req.ParentRef, req.SecretName, s) + } + if err != nil { + return nil, errors.Errorf("failed to update secret: %w", ErrFromRemote(resp, err)) + } + h.log.Infof("secret %s updated, ID: %s", rs.Name, rs.ID) + + return rs, nil +} + func (h *ActionHandler) DeleteSecret(ctx context.Context, parentType types.ConfigType, parentRef, name string) error { isVariableOwner, err := h.IsVariableOwner(ctx, parentType, parentRef) if err != nil { diff --git a/internal/services/gateway/api/client.go b/internal/services/gateway/api/client.go index 0da8b81..8b370d1 100644 --- a/internal/services/gateway/api/client.go +++ b/internal/services/gateway/api/client.go @@ -172,6 +172,17 @@ func (c *Client) CreateProjectGroupSecret(ctx context.Context, projectGroupRef s return secret, resp, err } +func (c *Client) UpdateProjectGroupSecret(ctx context.Context, projectGroupRef, secretName string, req *UpdateSecretRequest) (*SecretResponse, *http.Response, error) { + reqj, err := json.Marshal(req) + if err != nil { + return nil, nil, err + } + + secret := new(SecretResponse) + resp, err := c.getParsedResponse(ctx, "PUT", path.Join("/projectgroups", url.PathEscape(projectGroupRef), "secrets", secretName), nil, jsonContent, bytes.NewReader(reqj), secret) + return secret, resp, err +} + func (c *Client) DeleteProjectGroupSecret(ctx context.Context, projectGroupRef, secretName string) (*http.Response, error) { return c.getResponse(ctx, "DELETE", path.Join("/projectgroups", url.PathEscape(projectGroupRef), "secrets", secretName), nil, jsonContent, nil) } @@ -187,6 +198,17 @@ func (c *Client) CreateProjectSecret(ctx context.Context, projectRef string, req return secret, resp, err } +func (c *Client) UpdateProjectSecret(ctx context.Context, projectRef, secretName string, req *UpdateSecretRequest) (*SecretResponse, *http.Response, error) { + reqj, err := json.Marshal(req) + if err != nil { + return nil, nil, err + } + + secret := new(SecretResponse) + resp, err := c.getParsedResponse(ctx, "PUT", path.Join("/projects", url.PathEscape(projectRef), "secrets", secretName), nil, jsonContent, bytes.NewReader(reqj), secret) + return secret, resp, err +} + func (c *Client) DeleteProjectSecret(ctx context.Context, projectRef, secretName string) (*http.Response, error) { return c.getResponse(ctx, "DELETE", path.Join("/projects", url.PathEscape(projectRef), "secrets", secretName), nil, jsonContent, nil) } diff --git a/internal/services/gateway/api/secret.go b/internal/services/gateway/api/secret.go index 0ed9950..2cefc59 100644 --- a/internal/services/gateway/api/secret.go +++ b/internal/services/gateway/api/secret.go @@ -133,6 +133,68 @@ func (h *CreateSecretHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) return } + res := createSecretResponse(cssecret) + if err := httpResponse(w, http.StatusCreated, res); err != nil { + h.log.Errorf("err: %+v", err) + } +} + +type UpdateSecretRequest struct { + Name string `json:"name,omitempty"` + + Type types.SecretType `json:"type,omitempty"` + + // internal secret + Data map[string]string `json:"data,omitempty"` + + // external secret + SecretProviderID string `json:"secret_provider_id,omitempty"` + Path string `json:"path,omitempty"` +} + +type UpdateSecretHandler struct { + log *zap.SugaredLogger + ah *action.ActionHandler +} + +func NewUpdateSecretHandler(logger *zap.Logger, ah *action.ActionHandler) *UpdateSecretHandler { + return &UpdateSecretHandler{log: logger.Sugar(), ah: ah} +} + +func (h *UpdateSecretHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + vars := mux.Vars(r) + secretName := vars["secretname"] + + parentType, parentRef, err := GetConfigTypeRef(r) + if httpError(w, err) { + h.log.Errorf("err: %+v", err) + return + } + + var req UpdateSecretRequest + d := json.NewDecoder(r.Body) + if err := d.Decode(&req); err != nil { + httpError(w, util.NewErrBadRequest(err)) + return + } + areq := &action.UpdateSecretRequest{ + SecretName: secretName, + + Name: req.Name, + ParentType: parentType, + ParentRef: parentRef, + Type: req.Type, + Data: req.Data, + SecretProviderID: req.SecretProviderID, + Path: req.Path, + } + cssecret, err := h.ah.UpdateSecret(ctx, areq) + if httpError(w, err) { + h.log.Errorf("err: %+v", err) + return + } + res := createSecretResponse(cssecret) if err := httpResponse(w, http.StatusOK, res); err != nil { h.log.Errorf("err: %+v", err) diff --git a/internal/services/gateway/gateway.go b/internal/services/gateway/gateway.go index 81b92b9..43f8fc8 100644 --- a/internal/services/gateway/gateway.go +++ b/internal/services/gateway/gateway.go @@ -161,6 +161,7 @@ func (g *Gateway) Run(ctx context.Context) error { secretHandler := api.NewSecretHandler(logger, g.ah) createSecretHandler := api.NewCreateSecretHandler(logger, g.ah) + updateSecretHandler := api.NewUpdateSecretHandler(logger, g.ah) deleteSecretHandler := api.NewDeleteSecretHandler(logger, g.ah) variableHandler := api.NewVariableHandler(logger, g.ah) @@ -246,6 +247,8 @@ func (g *Gateway) Run(ctx context.Context) error { apirouter.Handle("/projects/{projectref}/secrets", authForcedHandler(secretHandler)).Methods("GET") apirouter.Handle("/projectgroups/{projectgroupref}/secrets", authForcedHandler(createSecretHandler)).Methods("POST") apirouter.Handle("/projects/{projectref}/secrets", authForcedHandler(createSecretHandler)).Methods("POST") + apirouter.Handle("/projectgroups/{projectgroupref}/secrets/{secretname}", authForcedHandler(updateSecretHandler)).Methods("PUT") + apirouter.Handle("/projects/{projectref}/secrets/{secretname}", authForcedHandler(updateSecretHandler)).Methods("PUT") apirouter.Handle("/projectgroups/{projectgroupref}/secrets/{secretname}", authForcedHandler(deleteSecretHandler)).Methods("DELETE") apirouter.Handle("/projects/{projectref}/secrets/{secretname}", authForcedHandler(deleteSecretHandler)).Methods("DELETE")