2019-05-03 10:47:22 +00:00
// Copyright 2019 Sorint.lab
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
// See the License for the specific language governing permissions and
// limitations under the License.
2019-05-03 21:35:25 +00:00
package action
2019-05-03 10:47:22 +00:00
import (
"context"
"encoding/json"
2019-07-01 09:40:20 +00:00
"agola.io/agola/internal/datamanager"
"agola.io/agola/internal/db"
"agola.io/agola/internal/services/types"
"agola.io/agola/internal/util"
2019-05-03 10:47:22 +00:00
uuid "github.com/satori/go.uuid"
2019-05-23 09:23:14 +00:00
errors "golang.org/x/xerrors"
2019-05-03 10:47:22 +00:00
)
2019-05-12 22:22:23 +00:00
func ( h * ActionHandler ) GetSecret ( ctx context . Context , secretID string ) ( * types . Secret , error ) {
var secret * types . Secret
err := h . readDB . Do ( func ( tx * db . Tx ) error {
var err error
secret , err = h . readDB . GetSecretByID ( tx , secretID )
return err
} )
if err != nil {
return nil , err
}
if secret == nil {
return nil , util . NewErrNotFound ( errors . Errorf ( "secret %q doesn't exist" , secretID ) )
}
return secret , nil
}
func ( h * ActionHandler ) GetSecrets ( ctx context . Context , parentType types . ConfigType , parentRef string , tree bool ) ( [ ] * types . Secret , error ) {
var secrets [ ] * types . Secret
err := h . readDB . Do ( func ( tx * db . Tx ) error {
parentID , err := h . readDB . ResolveConfigID ( tx , parentType , parentRef )
if err != nil {
return err
}
if tree {
secrets , err = h . readDB . GetSecretsTree ( tx , parentType , parentID )
} else {
secrets , err = h . readDB . GetSecrets ( tx , parentID )
}
return err
} )
if err != nil {
return nil , err
}
return secrets , nil
}
2019-07-08 08:32:32 +00:00
func ( h * ActionHandler ) ValidateSecret ( ctx context . Context , secret * types . Secret ) error {
2019-05-03 10:47:22 +00:00
if secret . Name == "" {
2019-07-08 08:32:32 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "secret name required" ) )
2019-05-03 10:47:22 +00:00
}
if ! util . ValidateName ( secret . Name ) {
2019-07-08 08:32:32 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "invalid secret name %q" , secret . Name ) )
2019-05-03 10:47:22 +00:00
}
if secret . Type != types . SecretTypeInternal {
2019-07-08 08:32:32 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "invalid secret type %q" , secret . Type ) )
2019-05-03 10:47:22 +00:00
}
switch secret . Type {
case types . SecretTypeInternal :
if len ( secret . Data ) == 0 {
2019-07-08 08:32:32 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "empty secret data" ) )
2019-05-03 10:47:22 +00:00
}
}
if secret . Parent . Type == "" {
2019-07-08 08:32:32 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "secret parent type required" ) )
2019-05-03 10:47:22 +00:00
}
if secret . Parent . ID == "" {
2019-07-08 08:32:32 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "secret parentid required" ) )
2019-05-03 10:47:22 +00:00
}
if secret . Parent . Type != types . ConfigTypeProject && secret . Parent . Type != types . ConfigTypeProjectGroup {
2019-07-08 08:32:32 +00:00
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
2019-05-03 10:47:22 +00:00
}
var cgt * datamanager . ChangeGroupsUpdateToken
// changegroup is the secret name
cgNames := [ ] string { util . EncodeSha256Hex ( "secretname-" + secret . Name ) }
// must do all the checks in a single transaction to avoid concurrent changes
2019-05-03 21:35:25 +00:00
err := h . readDB . Do ( func ( tx * db . Tx ) error {
2019-05-03 10:47:22 +00:00
var err error
2019-05-03 21:35:25 +00:00
cgt , err = h . readDB . GetChangeGroupsUpdateTokens ( tx , cgNames )
2019-05-03 10:47:22 +00:00
if err != nil {
return err
}
2019-05-03 21:35:25 +00:00
parentID , err := h . readDB . ResolveConfigID ( tx , secret . Parent . Type , secret . Parent . ID )
2019-05-03 10:47:22 +00:00
if err != nil {
return err
}
secret . Parent . ID = parentID
// check duplicate secret name
2019-05-03 21:35:25 +00:00
s , err := h . readDB . GetSecretByName ( tx , secret . Parent . ID , secret . Name )
2019-05-03 10:47:22 +00:00
if err != nil {
return err
}
if s != nil {
return util . NewErrBadRequest ( errors . Errorf ( "secret with name %q for %s with id %q already exists" , secret . Name , secret . Parent . Type , secret . Parent . ID ) )
}
return nil
} )
if err != nil {
return nil , err
}
secret . ID = uuid . NewV4 ( ) . String ( )
secretj , err := json . Marshal ( secret )
if err != nil {
2019-05-23 09:23:14 +00:00
return nil , errors . Errorf ( "failed to marshal secret: %w" , err )
2019-05-03 10:47:22 +00:00
}
actions := [ ] * datamanager . Action {
{
ActionType : datamanager . ActionTypePut ,
DataType : string ( types . ConfigTypeSecret ) ,
ID : secret . ID ,
Data : secretj ,
} ,
}
2019-05-03 21:35:25 +00:00
_ , err = h . dm . WriteWal ( ctx , actions , cgt )
2019-05-03 10:47:22 +00:00
return secret , err
}
2019-07-08 08:32:32 +00:00
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
}
2019-05-03 21:35:25 +00:00
func ( h * ActionHandler ) DeleteSecret ( ctx context . Context , parentType types . ConfigType , parentRef , secretName string ) error {
2019-05-03 10:47:22 +00:00
var secret * types . Secret
var cgt * datamanager . ChangeGroupsUpdateToken
// must do all the checks in a single transaction to avoid concurrent changes
2019-05-03 21:35:25 +00:00
err := h . readDB . Do ( func ( tx * db . Tx ) error {
2019-05-03 10:47:22 +00:00
var err error
2019-05-03 21:35:25 +00:00
parentID , err := h . readDB . ResolveConfigID ( tx , parentType , parentRef )
2019-05-03 10:47:22 +00:00
if err != nil {
return err
}
// check secret existance
2019-05-03 21:35:25 +00:00
secret , err = h . readDB . GetSecretByName ( tx , parentID , secretName )
2019-05-03 10:47:22 +00:00
if err != nil {
return err
}
if secret == nil {
return util . NewErrBadRequest ( errors . Errorf ( "secret with name %q doesn't exist" , secretName ) )
}
// changegroup is the secret id
cgNames := [ ] string { util . EncodeSha256Hex ( "secretid-" + secret . ID ) }
2019-05-03 21:35:25 +00:00
cgt , err = h . readDB . GetChangeGroupsUpdateTokens ( tx , cgNames )
2019-05-03 10:47:22 +00:00
if err != nil {
return err
}
return nil
} )
if err != nil {
return err
}
actions := [ ] * datamanager . Action {
{
ActionType : datamanager . ActionTypeDelete ,
DataType : string ( types . ConfigTypeSecret ) ,
ID : secret . ID ,
} ,
}
2019-05-03 21:35:25 +00:00
_ , err = h . dm . WriteWal ( ctx , actions , cgt )
2019-05-03 10:47:22 +00:00
return err
}