def _render_template_value(self, secret: types.JSONValue) -> types.JSONValue: if isinstance(secret, dict): return {k: self._render_template_value(v) for k, v in secret.items()} if not isinstance(secret, str): return secret if not secret.startswith(self.template_prefix): return secret return self.render_template(secret[len(self.template_prefix) :])
def set_secret( self, path: str, value: types.JSONValue, force: Optional[bool] = None, update: Optional[bool] = None, ) -> None: """ Sets the value of a secret Parameters ---------- path : str Path to the secret value : types.JSONValue Value of the secret force : Optional[bool], optional If safe_mode is True, whether to overwrite existing secret update: Optional[bool], optional If true then merge the value with the existing one, else overwrite it Raises ------ exceptions.VaultOverwriteSecretError Cannot overwrite a secret if safe_mode is True and force is False exceptions.VaultMixSecretAndFolder Either the path is an existing folder or a parent folder is a secret """ assert isinstance(value, dict) force = self.get_force(force) try: existing_value = self.get_secret(path=path, render=False) assert isinstance(existing_value, dict) except exceptions.VaultSecretNotFound: pass except exceptions.VaultForbidden: logger.warning( f"Read access '{path}' forbidden: if it exists, secret will be overridden." ) else: # if we overwrite the whole mapping we can compare the value directly # if we update the mapping, we only have to check the updated keys if not update and not force and existing_value != value: raise exceptions.VaultOverwriteSecretError(path=path) if (update and not force and any(existing_value[key] != value[key] for key in value.keys() & existing_value.keys())): raise exceptions.VaultOverwriteSecretError(path=path) if update: # merge value with existing_value value = {**existing_value, **value} try: problematic_secrets = self.list_secrets(path=path) if problematic_secrets: secrets = [ f"{path}/{secret}" for secret in problematic_secrets ] raise exceptions.VaultMixSecretAndFolder( f"Cannot create a secret at '{path}' because it is already a " f"folder containing {', '.join(secrets)}") except exceptions.VaultForbidden: logger.info( f"List '{path}' forbidden: if it exists, secret will be overridden." ) path = path.rstrip("/") for parent in list(pathlib.PurePath(path).parents)[:-1]: try: self.get_secret(str(parent), render=False) except exceptions.VaultSecretNotFound: pass except exceptions.VaultForbidden: logger.info( f"Read access '{parent}' forbidden: cannot check if a secret exists here." ) else: raise exceptions.VaultMixSecretAndFolder( f"Cannot create a secret at '{path}' because '{parent}' already exists as a secret" ) self._set_secret(path=self._build_full_path(path), secret=value)