github.com/zntrio/harp/v2@v2.0.9/pkg/vault/kv/v1_service.go (about)

     1  // Licensed to Elasticsearch B.V. under one or more contributor
     2  // license agreements. See the NOTICE file distributed with
     3  // this work for additional information regarding copyright
     4  // ownership. Elasticsearch B.V. licenses this file to you under
     5  // the Apache License, Version 2.0 (the "License"); you may
     6  // not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //     http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing,
    12  // software distributed under the License is distributed on an
    13  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    14  // KIND, either express or implied.  See the License for the
    15  // specific language governing permissions and limitations
    16  // under the License.
    17  
    18  package kv
    19  
    20  import (
    21  	"context"
    22  	"fmt"
    23  
    24  	"github.com/zntrio/harp/v2/pkg/vault/logical"
    25  	vpath "github.com/zntrio/harp/v2/pkg/vault/path"
    26  )
    27  
    28  type kvv1Backend struct {
    29  	logical   logical.Logical
    30  	mountPath string
    31  }
    32  
    33  // V1 returns a K/V v1 backend service instance.
    34  func V1(l logical.Logical, mountPath string) Service {
    35  	return &kvv1Backend{
    36  		logical:   l,
    37  		mountPath: mountPath,
    38  	}
    39  }
    40  
    41  // -----------------------------------------------------------------------------.
    42  func (s *kvv1Backend) List(ctx context.Context, path string) ([]string, error) {
    43  	// Clean path first
    44  	secretPath := vpath.SanitizePath(path)
    45  	if secretPath == "" {
    46  		return nil, fmt.Errorf("unable to query with empty path")
    47  	}
    48  
    49  	// Create logical client
    50  	secret, err := s.logical.List(secretPath)
    51  	if err != nil {
    52  		return nil, fmt.Errorf("unable to list secret keys: %w", err)
    53  	}
    54  	if secret == nil {
    55  		// Path is a leaf
    56  		return nil, nil
    57  	}
    58  	if secret.Data == nil {
    59  		return nil, fmt.Errorf("invalid secret response")
    60  	}
    61  
    62  	// Check required property
    63  	k, ok := secret.Data["keys"]
    64  	if !ok || k == nil {
    65  		return nil, fmt.Errorf("invalid response missing 'keys' property")
    66  	}
    67  
    68  	// Check value type
    69  	r, ok := k.([]interface{})
    70  	if !ok {
    71  		return nil, fmt.Errorf("invalid response 'keys' is not a list (%T)", k)
    72  	}
    73  
    74  	// Convert list of interface to list of string
    75  	out := make([]string, len(r))
    76  	for i := range r {
    77  		out[i] = fmt.Sprintf("%v", r[i])
    78  	}
    79  
    80  	// No error
    81  	return out, nil
    82  }
    83  
    84  func (s *kvv1Backend) Read(ctx context.Context, path string) (SecretData, SecretMetadata, error) {
    85  	// Clean path first
    86  	secretPath := vpath.SanitizePath(path)
    87  	if secretPath == "" {
    88  		return nil, nil, fmt.Errorf("unable to query with empty path")
    89  	}
    90  
    91  	// Create a logical client
    92  	secret, err := s.logical.Read(secretPath)
    93  	if err != nil {
    94  		return nil, nil, fmt.Errorf("unable to retrieve secret for path %q: %w", path, err)
    95  	}
    96  	if secret == nil {
    97  		return nil, nil, fmt.Errorf("unable to retrieve secret for path %q: %w", path, ErrPathNotFound)
    98  	}
    99  	if secret.Data == nil {
   100  		return nil, nil, fmt.Errorf("unable to retrieve secret for path %q: %w", path, ErrNoData)
   101  	}
   102  
   103  	// Return secret value and no error
   104  	return secret.Data, nil, err
   105  }
   106  
   107  func (s *kvv1Backend) ReadVersion(ctx context.Context, path string, version uint32) (SecretData, SecretMetadata, error) {
   108  	return s.Read(ctx, path)
   109  }
   110  
   111  func (s *kvv1Backend) Write(ctx context.Context, path string, data SecretData) error {
   112  	return s.WriteWithMeta(ctx, path, data, nil)
   113  }
   114  
   115  func (s *kvv1Backend) WriteWithMeta(ctx context.Context, path string, data SecretData, meta SecretMetadata) error {
   116  	// Clean path first
   117  	secretPath := vpath.SanitizePath(path)
   118  	if secretPath == "" {
   119  		return fmt.Errorf("unable to query with empty path")
   120  	}
   121  
   122  	// Add metadata as secret data.
   123  	if len(meta) > 0 {
   124  		data[VaultMetadataDataKey] = meta
   125  	}
   126  
   127  	// Create a logical client
   128  	_, err := s.logical.Write(secretPath, data)
   129  	if err != nil {
   130  		return fmt.Errorf("unable to write secret data for path %q: %w", path, err)
   131  	}
   132  
   133  	return nil
   134  }