github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/client/secretbackends/secrets.go (about)

     1  // Copyright 2022 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package secretbackends
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/clock"
    10  	"github.com/juju/errors"
    11  	"github.com/juju/names/v5"
    12  
    13  	commonsecrets "github.com/juju/juju/apiserver/common/secrets"
    14  	apiservererrors "github.com/juju/juju/apiserver/errors"
    15  	"github.com/juju/juju/apiserver/facade"
    16  	"github.com/juju/juju/core/permission"
    17  	"github.com/juju/juju/core/secrets"
    18  	"github.com/juju/juju/rpc/params"
    19  	"github.com/juju/juju/secrets/provider"
    20  	_ "github.com/juju/juju/secrets/provider/all"
    21  	"github.com/juju/juju/secrets/provider/juju"
    22  	"github.com/juju/juju/state"
    23  )
    24  
    25  // SecretBackendsAPI is the server implementation for the SecretBackends facade.
    26  type SecretBackendsAPI struct {
    27  	authorizer     facade.Authorizer
    28  	controllerUUID string
    29  
    30  	clock        clock.Clock
    31  	backendState SecretsBackendState
    32  	secretState  SecretsState
    33  	statePool    StatePool
    34  }
    35  
    36  func (s *SecretBackendsAPI) checkCanAdmin() error {
    37  	return s.authorizer.HasPermission(permission.SuperuserAccess, names.NewControllerTag(s.controllerUUID))
    38  }
    39  
    40  // AddSecretBackends adds new secret backends.
    41  func (s *SecretBackendsAPI) AddSecretBackends(args params.AddSecretBackendArgs) (params.ErrorResults, error) {
    42  	result := params.ErrorResults{
    43  		Results: make([]params.ErrorResult, len(args.Args)),
    44  	}
    45  	if err := s.checkCanAdmin(); err != nil {
    46  		return result, errors.Trace(err)
    47  	}
    48  	for i, arg := range args.Args {
    49  		err := s.createBackend(arg.ID, arg.SecretBackend)
    50  		result.Results[i].Error = apiservererrors.ServerError(err)
    51  	}
    52  	return result, nil
    53  }
    54  
    55  func (s *SecretBackendsAPI) createBackend(id string, arg params.SecretBackend) error {
    56  	if arg.Name == "" {
    57  		return errors.NotValidf("missing backend name")
    58  	}
    59  	if arg.Name == juju.BackendName || arg.Name == provider.Auto {
    60  		return errors.NotValidf("backend %q")
    61  	}
    62  	p, err := commonsecrets.GetProvider(arg.BackendType)
    63  	if err != nil {
    64  		return errors.Annotatef(err, "creating backend provider type %q", arg.BackendType)
    65  	}
    66  	configValidator, ok := p.(provider.ProviderConfig)
    67  	if ok {
    68  		defaults := configValidator.ConfigDefaults()
    69  		if arg.Config == nil && len(defaults) > 0 {
    70  			arg.Config = make(map[string]interface{})
    71  		}
    72  		for k, v := range defaults {
    73  			if _, ok := arg.Config[k]; !ok {
    74  				arg.Config[k] = v
    75  			}
    76  		}
    77  		err = configValidator.ValidateConfig(nil, arg.Config)
    78  		if err != nil {
    79  			return errors.Annotatef(err, "invalid config for provider %q", arg.BackendType)
    80  		}
    81  	}
    82  	if err := commonsecrets.PingBackend(p, arg.Config); err != nil {
    83  		return errors.Trace(err)
    84  	}
    85  
    86  	var nextRotateTime *time.Time
    87  	if arg.TokenRotateInterval != nil && *arg.TokenRotateInterval > 0 {
    88  		if !provider.HasAuthRefresh(p) {
    89  			return errors.NotSupportedf("token refresh on secret backend of type %q", p.Type())
    90  		}
    91  		nextRotateTime, err = secrets.NextBackendRotateTime(s.clock.Now(), *arg.TokenRotateInterval)
    92  		if err != nil {
    93  			return errors.Trace(err)
    94  		}
    95  	}
    96  	_, err = s.backendState.CreateSecretBackend(state.CreateSecretBackendParams{
    97  		ID:                  id,
    98  		Name:                arg.Name,
    99  		BackendType:         arg.BackendType,
   100  		TokenRotateInterval: arg.TokenRotateInterval,
   101  		NextRotateTime:      nextRotateTime,
   102  		Config:              arg.Config,
   103  	})
   104  	if errors.IsAlreadyExists(err) {
   105  		return errors.AlreadyExistsf("secret backend with ID %q", id)
   106  	}
   107  	return errors.Trace(err)
   108  }
   109  
   110  // UpdateSecretBackends updates secret backends.
   111  func (s *SecretBackendsAPI) UpdateSecretBackends(args params.UpdateSecretBackendArgs) (params.ErrorResults, error) {
   112  	result := params.ErrorResults{
   113  		Results: make([]params.ErrorResult, len(args.Args)),
   114  	}
   115  	if err := s.checkCanAdmin(); err != nil {
   116  		return result, errors.Trace(err)
   117  	}
   118  	for i, arg := range args.Args {
   119  		err := s.updateBackend(arg)
   120  		result.Results[i].Error = apiservererrors.ServerError(err)
   121  	}
   122  	return result, nil
   123  }
   124  
   125  func (s *SecretBackendsAPI) updateBackend(arg params.UpdateSecretBackendArg) error {
   126  	if arg.Name == "" {
   127  		return errors.NotValidf("missing backend name")
   128  	}
   129  	if arg.Name == juju.BackendName || arg.Name == provider.Auto {
   130  		return errors.NotValidf("backend %q")
   131  	}
   132  	existing, err := s.backendState.GetSecretBackend(arg.Name)
   133  	if err != nil {
   134  		return errors.Trace(err)
   135  	}
   136  	p, err := commonsecrets.GetProvider(existing.BackendType)
   137  	if err != nil {
   138  		return errors.Trace(err)
   139  	}
   140  
   141  	cfg := make(map[string]interface{})
   142  	for k, v := range existing.Config {
   143  		cfg[k] = v
   144  	}
   145  	for k, v := range arg.Config {
   146  		cfg[k] = v
   147  	}
   148  	for _, k := range arg.Reset {
   149  		delete(cfg, k)
   150  	}
   151  	configValidator, ok := p.(provider.ProviderConfig)
   152  	if ok {
   153  		defaults := configValidator.ConfigDefaults()
   154  		for _, k := range arg.Reset {
   155  			if defaultVal, ok := defaults[k]; ok {
   156  				cfg[k] = defaultVal
   157  			}
   158  		}
   159  		err = configValidator.ValidateConfig(existing.Config, cfg)
   160  		if err != nil {
   161  			return errors.Annotatef(err, "invalid config for provider %q", existing.BackendType)
   162  		}
   163  	}
   164  	if !arg.Force {
   165  		if err := commonsecrets.PingBackend(p, cfg); err != nil {
   166  			return errors.Trace(err)
   167  		}
   168  	}
   169  	var nextRotateTime *time.Time
   170  	if arg.TokenRotateInterval != nil && *arg.TokenRotateInterval > 0 {
   171  		if !provider.HasAuthRefresh(p) {
   172  			return errors.NotSupportedf("token refresh on secret backend of type %q", p.Type())
   173  		}
   174  		nextRotateTime, err = secrets.NextBackendRotateTime(s.clock.Now(), *arg.TokenRotateInterval)
   175  		if err != nil {
   176  			return errors.Trace(err)
   177  		}
   178  	}
   179  	err = s.backendState.UpdateSecretBackend(state.UpdateSecretBackendParams{
   180  		ID:                  existing.ID,
   181  		NameChange:          arg.NameChange,
   182  		TokenRotateInterval: arg.TokenRotateInterval,
   183  		NextRotateTime:      nextRotateTime,
   184  		Config:              cfg,
   185  	})
   186  	if errors.IsNotFound(err) {
   187  		return errors.NotFoundf("secret backend %q", arg.Name)
   188  	}
   189  	return err
   190  }
   191  
   192  // ListSecretBackends lists available secret backends.
   193  func (s *SecretBackendsAPI) ListSecretBackends(arg params.ListSecretBackendsArgs) (params.ListSecretBackendsResults, error) {
   194  	result := params.ListSecretBackendsResults{}
   195  	if arg.Reveal {
   196  		if err := s.checkCanAdmin(); err != nil {
   197  			return result, errors.Trace(err)
   198  		}
   199  	}
   200  
   201  	results, err := commonsecrets.BackendSummaryInfo(
   202  		s.statePool, s.backendState, s.secretState, s.controllerUUID, arg.Reveal, commonsecrets.BackendFilter{Names: arg.Names, All: true})
   203  	if err != nil {
   204  		return params.ListSecretBackendsResults{}, errors.Trace(err)
   205  	}
   206  	result.Results = results
   207  	return result, nil
   208  }
   209  
   210  // RemoveSecretBackends removes secret backends.
   211  func (s *SecretBackendsAPI) RemoveSecretBackends(args params.RemoveSecretBackendArgs) (params.ErrorResults, error) {
   212  	result := params.ErrorResults{
   213  		Results: make([]params.ErrorResult, len(args.Args)),
   214  	}
   215  	if err := s.checkCanAdmin(); err != nil {
   216  		return result, errors.Trace(err)
   217  	}
   218  	for i, arg := range args.Args {
   219  		err := s.removeBackend(arg)
   220  		result.Results[i].Error = apiservererrors.ServerError(err)
   221  	}
   222  	return result, nil
   223  }
   224  
   225  func (s *SecretBackendsAPI) removeBackend(arg params.RemoveSecretBackendArg) error {
   226  	if arg.Name == "" {
   227  		return errors.NotValidf("missing backend name")
   228  	}
   229  	if arg.Name == juju.BackendName || arg.Name == provider.Auto {
   230  		return errors.NotValidf("backend %q")
   231  	}
   232  	return s.backendState.DeleteSecretBackend(arg.Name, arg.Force)
   233  }