configstore: implement secret update
This commit is contained in:
parent
6508fd2b53
commit
289e691a58
|
@ -66,30 +66,38 @@ func (h *ActionHandler) GetSecrets(ctx context.Context, parentType types.ConfigT
|
||||||
return secrets, nil
|
return secrets, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *ActionHandler) CreateSecret(ctx context.Context, secret *types.Secret) (*types.Secret, error) {
|
func (h *ActionHandler) ValidateSecret(ctx context.Context, secret *types.Secret) error {
|
||||||
if secret.Name == "" {
|
if secret.Name == "" {
|
||||||
return nil, util.NewErrBadRequest(errors.Errorf("secret name required"))
|
return util.NewErrBadRequest(errors.Errorf("secret name required"))
|
||||||
}
|
}
|
||||||
if !util.ValidateName(secret.Name) {
|
if !util.ValidateName(secret.Name) {
|
||||||
return nil, util.NewErrBadRequest(errors.Errorf("invalid secret name %q", secret.Name))
|
return util.NewErrBadRequest(errors.Errorf("invalid secret name %q", secret.Name))
|
||||||
}
|
}
|
||||||
if secret.Type != types.SecretTypeInternal {
|
if secret.Type != types.SecretTypeInternal {
|
||||||
return nil, util.NewErrBadRequest(errors.Errorf("invalid secret type %q", secret.Type))
|
return util.NewErrBadRequest(errors.Errorf("invalid secret type %q", secret.Type))
|
||||||
}
|
}
|
||||||
switch secret.Type {
|
switch secret.Type {
|
||||||
case types.SecretTypeInternal:
|
case types.SecretTypeInternal:
|
||||||
if len(secret.Data) == 0 {
|
if len(secret.Data) == 0 {
|
||||||
return nil, util.NewErrBadRequest(errors.Errorf("empty secret data"))
|
return util.NewErrBadRequest(errors.Errorf("empty secret data"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if secret.Parent.Type == "" {
|
if secret.Parent.Type == "" {
|
||||||
return nil, util.NewErrBadRequest(errors.Errorf("secret parent type required"))
|
return util.NewErrBadRequest(errors.Errorf("secret parent type required"))
|
||||||
}
|
}
|
||||||
if secret.Parent.ID == "" {
|
if secret.Parent.ID == "" {
|
||||||
return nil, util.NewErrBadRequest(errors.Errorf("secret parentid required"))
|
return util.NewErrBadRequest(errors.Errorf("secret parentid required"))
|
||||||
}
|
}
|
||||||
if secret.Parent.Type != types.ConfigTypeProject && secret.Parent.Type != types.ConfigTypeProjectGroup {
|
if secret.Parent.Type != types.ConfigTypeProject && secret.Parent.Type != types.ConfigTypeProjectGroup {
|
||||||
return nil, util.NewErrBadRequest(errors.Errorf("invalid secret parent type %q", secret.Parent.Type))
|
return util.NewErrBadRequest(errors.Errorf("invalid secret parent type %q", secret.Parent.Type))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ActionHandler) CreateSecret(ctx context.Context, secret *types.Secret) (*types.Secret, error) {
|
||||||
|
if err := h.ValidateSecret(ctx, secret); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var cgt *datamanager.ChangeGroupsUpdateToken
|
var cgt *datamanager.ChangeGroupsUpdateToken
|
||||||
|
@ -144,6 +152,86 @@ func (h *ActionHandler) CreateSecret(ctx context.Context, secret *types.Secret)
|
||||||
return secret, err
|
return secret, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UpdateSecretRequest struct {
|
||||||
|
SecretName string
|
||||||
|
|
||||||
|
Secret *types.Secret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ActionHandler) UpdateSecret(ctx context.Context, req *UpdateSecretRequest) (*types.Secret, error) {
|
||||||
|
if err := h.ValidateSecret(ctx, req.Secret); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var curSecret *types.Secret
|
||||||
|
var cgt *datamanager.ChangeGroupsUpdateToken
|
||||||
|
// changegroup is the secret name
|
||||||
|
|
||||||
|
// must do all the checks in a single transaction to avoid concurrent changes
|
||||||
|
err := h.readDB.Do(func(tx *db.Tx) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
parentID, err := h.readDB.ResolveConfigID(tx, req.Secret.Parent.Type, req.Secret.Parent.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req.Secret.Parent.ID = parentID
|
||||||
|
|
||||||
|
// check secret exists
|
||||||
|
curSecret, err = h.readDB.GetSecretByName(tx, req.Secret.Parent.ID, req.SecretName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if curSecret == nil {
|
||||||
|
return util.NewErrBadRequest(errors.Errorf("secret with name %q for %s with id %q doesn't exists", req.SecretName, req.Secret.Parent.Type, req.Secret.Parent.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
if curSecret.Name != req.Secret.Name {
|
||||||
|
// check duplicate secret name
|
||||||
|
u, err := h.readDB.GetSecretByName(tx, req.Secret.Parent.ID, req.Secret.Name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if u != nil {
|
||||||
|
return util.NewErrBadRequest(errors.Errorf("secret with name %q for %s with id %q already exists", req.Secret.Name, req.Secret.Parent.Type, req.Secret.Parent.ID))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set/override ID that must be kept from the current secret
|
||||||
|
req.Secret.ID = curSecret.ID
|
||||||
|
|
||||||
|
cgNames := []string{
|
||||||
|
util.EncodeSha256Hex("secretname-" + req.Secret.ID),
|
||||||
|
util.EncodeSha256Hex("secretname-" + req.Secret.Name),
|
||||||
|
}
|
||||||
|
cgt, err = h.readDB.GetChangeGroupsUpdateTokens(tx, cgNames)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
secretj, err := json.Marshal(req.Secret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Errorf("failed to marshal secret: %w", err)
|
||||||
|
}
|
||||||
|
actions := []*datamanager.Action{
|
||||||
|
{
|
||||||
|
ActionType: datamanager.ActionTypePut,
|
||||||
|
DataType: string(types.ConfigTypeSecret),
|
||||||
|
ID: req.Secret.ID,
|
||||||
|
Data: secretj,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = h.dm.WriteWal(ctx, actions, cgt)
|
||||||
|
return req.Secret, err
|
||||||
|
}
|
||||||
|
|
||||||
func (h *ActionHandler) DeleteSecret(ctx context.Context, parentType types.ConfigType, parentRef, secretName string) error {
|
func (h *ActionHandler) DeleteSecret(ctx context.Context, parentType types.ConfigType, parentRef, secretName string) error {
|
||||||
var secret *types.Secret
|
var secret *types.Secret
|
||||||
|
|
||||||
|
|
|
@ -226,6 +226,28 @@ func (c *Client) CreateProjectSecret(ctx context.Context, projectRef string, sec
|
||||||
return resSecret, resp, err
|
return resSecret, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) UpdateProjectGroupSecret(ctx context.Context, projectGroupRef, secretName string, secret *types.Secret) (*Secret, *http.Response, error) {
|
||||||
|
pj, err := json.Marshal(secret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resSecret := new(Secret)
|
||||||
|
resp, err := c.getParsedResponse(ctx, "PUT", fmt.Sprintf("/projectgroups/%s/secrets/%s", url.PathEscape(projectGroupRef), secretName), nil, jsonContent, bytes.NewReader(pj), resSecret)
|
||||||
|
return resSecret, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) UpdateProjectSecret(ctx context.Context, projectRef, secretName string, secret *types.Secret) (*Secret, *http.Response, error) {
|
||||||
|
pj, err := json.Marshal(secret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resSecret := new(Secret)
|
||||||
|
resp, err := c.getParsedResponse(ctx, "PUT", fmt.Sprintf("/projects/%s/secrets/%s", url.PathEscape(projectRef), secretName), nil, jsonContent, bytes.NewReader(pj), resSecret)
|
||||||
|
return resSecret, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) DeleteProjectGroupSecret(ctx context.Context, projectGroupRef, secretName string) (*http.Response, error) {
|
func (c *Client) DeleteProjectGroupSecret(ctx context.Context, projectGroupRef, secretName string) (*http.Response, error) {
|
||||||
return c.getResponse(ctx, "DELETE", fmt.Sprintf("/projectgroups/%s/secrets/%s", url.PathEscape(projectGroupRef), secretName), nil, jsonContent, nil)
|
return c.getResponse(ctx, "DELETE", fmt.Sprintf("/projectgroups/%s/secrets/%s", url.PathEscape(projectGroupRef), secretName), nil, jsonContent, nil)
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,6 +154,51 @@ func (h *CreateSecretHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 secret *types.Secret
|
||||||
|
d := json.NewDecoder(r.Body)
|
||||||
|
if err := d.Decode(&secret); err != nil {
|
||||||
|
httpError(w, util.NewErrBadRequest(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
secret.Parent.Type = parentType
|
||||||
|
secret.Parent.ID = parentRef
|
||||||
|
|
||||||
|
areq := &action.UpdateSecretRequest{
|
||||||
|
SecretName: secretName,
|
||||||
|
Secret: secret,
|
||||||
|
}
|
||||||
|
secret, err = h.ah.UpdateSecret(ctx, areq)
|
||||||
|
if httpError(w, err) {
|
||||||
|
h.log.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := httpResponse(w, http.StatusOK, secret); err != nil {
|
||||||
|
h.log.Errorf("err: %+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type DeleteSecretHandler struct {
|
type DeleteSecretHandler struct {
|
||||||
log *zap.SugaredLogger
|
log *zap.SugaredLogger
|
||||||
ah *action.ActionHandler
|
ah *action.ActionHandler
|
||||||
|
|
|
@ -134,6 +134,7 @@ func (s *Configstore) Run(ctx context.Context) error {
|
||||||
|
|
||||||
secretsHandler := api.NewSecretsHandler(logger, s.ah, s.readDB)
|
secretsHandler := api.NewSecretsHandler(logger, s.ah, s.readDB)
|
||||||
createSecretHandler := api.NewCreateSecretHandler(logger, s.ah)
|
createSecretHandler := api.NewCreateSecretHandler(logger, s.ah)
|
||||||
|
updateSecretHandler := api.NewUpdateSecretHandler(logger, s.ah)
|
||||||
deleteSecretHandler := api.NewDeleteSecretHandler(logger, s.ah)
|
deleteSecretHandler := api.NewDeleteSecretHandler(logger, s.ah)
|
||||||
|
|
||||||
variablesHandler := api.NewVariablesHandler(logger, s.ah, s.readDB)
|
variablesHandler := api.NewVariablesHandler(logger, s.ah, s.readDB)
|
||||||
|
@ -190,6 +191,8 @@ func (s *Configstore) Run(ctx context.Context) error {
|
||||||
apirouter.Handle("/projects/{projectref}/secrets", secretsHandler).Methods("GET")
|
apirouter.Handle("/projects/{projectref}/secrets", secretsHandler).Methods("GET")
|
||||||
apirouter.Handle("/projectgroups/{projectgroupref}/secrets", createSecretHandler).Methods("POST")
|
apirouter.Handle("/projectgroups/{projectgroupref}/secrets", createSecretHandler).Methods("POST")
|
||||||
apirouter.Handle("/projects/{projectref}/secrets", createSecretHandler).Methods("POST")
|
apirouter.Handle("/projects/{projectref}/secrets", createSecretHandler).Methods("POST")
|
||||||
|
apirouter.Handle("/projectgroups/{projectgroupref}/secrets/{secretname}", updateSecretHandler).Methods("PUT")
|
||||||
|
apirouter.Handle("/projects/{projectref}/secrets/{secretname}", updateSecretHandler).Methods("PUT")
|
||||||
apirouter.Handle("/projectgroups/{projectgroupref}/secrets/{secretname}", deleteSecretHandler).Methods("DELETE")
|
apirouter.Handle("/projectgroups/{projectgroupref}/secrets/{secretname}", deleteSecretHandler).Methods("DELETE")
|
||||||
apirouter.Handle("/projects/{projectref}/secrets/{secretname}", deleteSecretHandler).Methods("DELETE")
|
apirouter.Handle("/projects/{projectref}/secrets/{secretname}", deleteSecretHandler).Methods("DELETE")
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue