fake.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. /*
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.
  11. */
  12. package fake
  13. import (
  14. "context"
  15. "encoding/json"
  16. "errors"
  17. "fmt"
  18. "strings"
  19. "github.com/tidwall/gjson"
  20. corev1 "k8s.io/api/core/v1"
  21. "sigs.k8s.io/controller-runtime/pkg/client"
  22. "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
  23. esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  24. esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
  25. prov "github.com/external-secrets/external-secrets/apis/providers/v1alpha1"
  26. "github.com/external-secrets/external-secrets/pkg/find"
  27. "github.com/external-secrets/external-secrets/pkg/utils"
  28. )
  29. var (
  30. errMissingStore = errors.New("missing store provider")
  31. errMissingFakeProvider = errors.New("missing store provider fake")
  32. errMissingKeyField = "key must be set in data %v"
  33. errMissingValueField = "at least one of value or valueMap must be set in data %v"
  34. )
  35. type SourceOrigin string
  36. const (
  37. FakeSecretStore SourceOrigin = "SecretStore"
  38. FakeSetSecret SourceOrigin = "SetSecret"
  39. )
  40. type Data struct {
  41. Value string
  42. Version string
  43. ValueMap map[string]string
  44. Origin SourceOrigin
  45. }
  46. type Config map[string]*Data
  47. type Provider struct {
  48. config Config
  49. database map[string]Config
  50. }
  51. // Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
  52. func (p *Provider) Capabilities() esv1beta1.SecretStoreCapabilities {
  53. return esv1beta1.SecretStoreReadWrite
  54. }
  55. func (p *Provider) ApplyReferent(spec client.Object, caller esmeta.ReferentCallOrigin, ns string) (client.Object, error) {
  56. converted, ok := spec.(*prov.Fake)
  57. out := converted.DeepCopy()
  58. if !ok {
  59. return nil, fmt.Errorf("could not convert source object %v into 'fake' provider type: object from type %T", spec.GetName(), spec)
  60. }
  61. switch caller {
  62. case esmeta.ReferentCallSecretStore:
  63. // Logic to update 'namespace' fields from provider original spec.
  64. fmt.Printf("i would do something here if I was a legit implementation updating all ns references to %v\n", ns)
  65. case esmeta.ReferentCallProvider:
  66. // Logic to update 'namespace' fields from provider original spec.
  67. fmt.Printf("i would do something here if I was a legit implementation updating all ns references to %v\n", ns)
  68. case esmeta.ReferentCallClusterSecretStore:
  69. default:
  70. // Logic to not update 'namespace' and use provider configuration as is. (if nil, keep nil as this means referent)
  71. fmt.Println("I wouldn't do anything here")
  72. }
  73. return out, nil
  74. }
  75. func (p *Provider) Convert(in esv1beta1.GenericStore) (client.Object, error) {
  76. out := &prov.Fake{}
  77. tmp := map[string]interface{}{
  78. "spec": in.GetSpec().Provider.Fake,
  79. }
  80. d, err := json.Marshal(tmp)
  81. if err != nil {
  82. return nil, err
  83. }
  84. err = json.Unmarshal(d, out)
  85. if err != nil {
  86. return nil, fmt.Errorf("could not convert %v in a valid fake provider: %w", in.GetName(), err)
  87. }
  88. return out, nil
  89. }
  90. func (p *Provider) NewClientFromObj(_ context.Context, obj client.Object, _ client.Client, _ string) (esv1beta1.SecretsClient, error) {
  91. if p.database == nil {
  92. p.database = make(map[string]Config)
  93. }
  94. f, ok := obj.(*prov.Fake)
  95. if !ok {
  96. return nil, fmt.Errorf("could not open Provider Spec for obj '%v': expected 'Fake' type, got '%T'", obj.GetName(), obj)
  97. }
  98. cfg := p.database[f.GetName()]
  99. if cfg == nil {
  100. cfg = Config{}
  101. }
  102. for key, data := range cfg {
  103. if data.Origin == FakeSecretStore {
  104. delete(cfg, key)
  105. }
  106. }
  107. for _, data := range f.Spec.Data {
  108. mapKey := fmt.Sprintf("%v%v", data.Key, data.Version)
  109. cfg[mapKey] = &Data{
  110. Value: data.Value,
  111. Version: data.Version,
  112. Origin: FakeSecretStore,
  113. }
  114. if data.ValueMap != nil {
  115. cfg[mapKey].ValueMap = data.ValueMap
  116. }
  117. }
  118. p.database[f.GetName()] = cfg
  119. return &Provider{
  120. config: cfg,
  121. }, nil
  122. }
  123. func (p *Provider) NewClient(_ context.Context, store esv1beta1.GenericStore, _ client.Client, _ string) (esv1beta1.SecretsClient, error) {
  124. if p.database == nil {
  125. p.database = make(map[string]Config)
  126. }
  127. c, err := getProvider(store)
  128. if err != nil {
  129. return nil, err
  130. }
  131. cfg := p.database[store.GetName()]
  132. if cfg == nil {
  133. cfg = Config{}
  134. }
  135. // We want to remove any FakeSecretStore entry from memory
  136. // this will ensure SecretStores can delete from memory.
  137. for key, data := range cfg {
  138. if data.Origin == FakeSecretStore {
  139. delete(cfg, key)
  140. }
  141. }
  142. for _, data := range c.Data {
  143. key := mapKey(data.Key, data.Version)
  144. cfg[key] = &Data{
  145. Value: data.Value,
  146. Version: data.Version,
  147. Origin: FakeSecretStore,
  148. }
  149. if data.ValueMap != nil {
  150. cfg[key].ValueMap = data.ValueMap
  151. }
  152. }
  153. p.database[store.GetName()] = cfg
  154. return &Provider{
  155. config: cfg,
  156. }, nil
  157. }
  158. func getProvider(store esv1beta1.GenericStore) (*esv1beta1.FakeProvider, error) {
  159. if store == nil {
  160. return nil, errMissingStore
  161. }
  162. spc := store.GetSpec()
  163. if spc == nil || spc.Provider == nil || spc.Provider.Fake == nil {
  164. return nil, errMissingFakeProvider
  165. }
  166. return spc.Provider.Fake, nil
  167. }
  168. func (p *Provider) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error {
  169. return nil
  170. }
  171. func (p *Provider) SecretExists(_ context.Context, ref esv1beta1.PushSecretRemoteRef) (bool, error) {
  172. _, ok := p.config[ref.GetRemoteKey()]
  173. return ok, nil
  174. }
  175. func (p *Provider) PushSecret(_ context.Context, secret *corev1.Secret, data esv1beta1.PushSecretData) error {
  176. value := secret.Data[data.GetSecretKey()]
  177. currentData, ok := p.config[data.GetRemoteKey()]
  178. if !ok {
  179. p.config[data.GetRemoteKey()] = &Data{
  180. Value: string(value),
  181. Origin: FakeSetSecret,
  182. }
  183. return nil
  184. }
  185. if currentData.Origin != FakeSetSecret {
  186. return errors.New("key already exists")
  187. }
  188. currentData.Value = string(value)
  189. return nil
  190. }
  191. // GetAllSecrets returns multiple secrets from the given ExternalSecretFind
  192. // Currently, only the Name operator is supported.
  193. func (p *Provider) GetAllSecrets(_ context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
  194. if ref.Name != nil {
  195. matcher, err := find.New(*ref.Name)
  196. if err != nil {
  197. return nil, err
  198. }
  199. latestVersionMap := make(map[string]string)
  200. dataMap := make(map[string][]byte)
  201. for key, data := range p.config {
  202. // Reconstruct the original key without the version suffix
  203. // See the mapKey function to know how the provider generates keys
  204. originalKey := strings.TrimSuffix(key, data.Version)
  205. if !matcher.MatchName(originalKey) {
  206. continue
  207. }
  208. if version, ok := latestVersionMap[originalKey]; ok {
  209. // Need to get only the latest version
  210. if version < data.Version {
  211. latestVersionMap[originalKey] = data.Version
  212. dataMap[originalKey] = []byte(data.Value)
  213. }
  214. } else {
  215. latestVersionMap[originalKey] = data.Version
  216. dataMap[originalKey] = []byte(data.Value)
  217. }
  218. }
  219. return utils.ConvertKeys(ref.ConversionStrategy, dataMap)
  220. }
  221. return nil, fmt.Errorf("unsupported find operator: %#v", ref)
  222. }
  223. // GetSecret returns a single secret from the provider.
  224. func (p *Provider) GetSecret(_ context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  225. data, ok := p.config[mapKey(ref.Key, ref.Version)]
  226. if !ok || data.Version != ref.Version {
  227. return nil, esv1beta1.NoSecretErr
  228. }
  229. if ref.Property != "" {
  230. val := gjson.Get(data.Value, ref.Property)
  231. if !val.Exists() {
  232. return nil, esv1beta1.NoSecretErr
  233. }
  234. return []byte(val.String()), nil
  235. }
  236. return []byte(data.Value), nil
  237. }
  238. // GetSecretMap returns multiple k/v pairs from the provider.
  239. func (p *Provider) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  240. ddata, ok := p.config[mapKey(ref.Key, ref.Version)]
  241. if !ok || ddata.Version != ref.Version {
  242. return nil, esv1beta1.NoSecretErr
  243. }
  244. // Due to backward compatibility valueMap will still be returned for now
  245. if ddata.ValueMap != nil {
  246. return convertMap(ddata.ValueMap), nil
  247. }
  248. data, err := p.GetSecret(ctx, ref)
  249. if err != nil {
  250. return nil, err
  251. }
  252. secretData := make(map[string][]byte)
  253. kv := make(map[string]json.RawMessage)
  254. err = json.Unmarshal(data, &kv)
  255. if err != nil {
  256. return nil, fmt.Errorf("unable to unmarshal secret: %w", err)
  257. }
  258. for k, v := range kv {
  259. var strVal string
  260. err = json.Unmarshal(v, &strVal)
  261. if err == nil {
  262. secretData[k] = []byte(strVal)
  263. } else {
  264. secretData[k] = v
  265. }
  266. }
  267. return secretData, nil
  268. }
  269. func convertMap(in map[string]string) map[string][]byte {
  270. m := make(map[string][]byte)
  271. for k, v := range in {
  272. m[k] = []byte(v)
  273. }
  274. return m
  275. }
  276. func (p *Provider) Close(_ context.Context) error {
  277. return nil
  278. }
  279. func (p *Provider) Validate() (esv1beta1.ValidationResult, error) {
  280. return esv1beta1.ValidationResultReady, nil
  281. }
  282. func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnings, error) {
  283. prov := store.GetSpec().Provider.Fake
  284. if prov == nil {
  285. return nil, nil
  286. }
  287. for pos, data := range prov.Data {
  288. if data.Key == "" {
  289. return nil, fmt.Errorf(errMissingKeyField, pos)
  290. }
  291. if data.Value == "" && data.ValueMap == nil {
  292. return nil, fmt.Errorf(errMissingValueField, pos)
  293. }
  294. }
  295. return nil, nil
  296. }
  297. func mapKey(key, version string) string {
  298. // Add the version suffix to preserve entries with the old versions as well.
  299. return fmt.Sprintf("%v%v", key, version)
  300. }
  301. func init() {
  302. esv1beta1.Register(&Provider{}, &esv1beta1.SecretStoreProvider{
  303. Fake: &esv1beta1.FakeProvider{},
  304. })
  305. esv1beta1.RegisterByName(&Provider{}, prov.FakeKind)
  306. ref := esmeta.ProviderRef{
  307. APIVersion: prov.Group + "/" + prov.Version,
  308. Kind: prov.FakeKind,
  309. }
  310. prov.RefRegister(&prov.Fake{}, ref)
  311. }