fakeclient.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. /*
  2. Copyright © The ESO Authors
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. https://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package client
  14. import (
  15. "context"
  16. "errors"
  17. "time"
  18. "github.com/go-logr/logr"
  19. "github.com/google/go-cmp/cmp"
  20. "github.com/google/go-cmp/cmp/cmpopts"
  21. "github.com/google/uuid"
  22. api "github.com/yandex-cloud/go-genproto/yandex/cloud/certificatemanager/v1"
  23. "github.com/yandex-cloud/go-sdk/iamkey"
  24. ydxcommon "github.com/external-secrets/external-secrets/providers/v1/yandex/common"
  25. "github.com/external-secrets/external-secrets/providers/v1/yandex/common/clock"
  26. )
  27. // Fake implementation of CertificateManagerClient.
  28. type fakeCertificateManagerClient struct {
  29. fakeCertificateManagerServer *FakeCertificateManagerServer
  30. }
  31. // NewFakeCertificateManagerClient creates a new fake client for testing.
  32. func NewFakeCertificateManagerClient(fakeCertificateManagerServer *FakeCertificateManagerServer) CertificateManagerClient {
  33. return &fakeCertificateManagerClient{fakeCertificateManagerServer}
  34. }
  35. func (c *fakeCertificateManagerClient) GetCertificateContent(_ context.Context, iamToken, certificateID, versionID string) (*api.GetCertificateContentResponse, error) {
  36. return c.fakeCertificateManagerServer.getCertificateContent(iamToken, certificateID, versionID)
  37. }
  38. func (c *fakeCertificateManagerClient) GetExCertificateContent(_ context.Context, iamToken, folderID, name, versionID string) (*api.GetExCertificateContentResponse, error) {
  39. return c.fakeCertificateManagerServer.getExCertificateContent(iamToken, folderID, name, versionID)
  40. }
  41. // FakeCertificateManagerServer fakes Yandex Certificate Manager service backend.
  42. type FakeCertificateManagerServer struct {
  43. certificateMap map[certificateKey]certificateValue // certificate specific data
  44. versionMap map[versionKey]versionValue // version specific data
  45. tokenMap map[tokenKey]tokenValue // token specific data
  46. folderAndNameMap map[folderAndNameKey]folderAndNameValue // folderAndName specific data
  47. tokenExpirationDuration time.Duration
  48. clock clock.Clock
  49. logger logr.Logger
  50. }
  51. type certificateKey struct {
  52. certificateID string
  53. }
  54. type certificateValue struct {
  55. expectedAuthorizedKey *iamkey.Key // authorized key expected to access the certificate
  56. }
  57. type versionKey struct {
  58. certificateID string
  59. versionID string
  60. }
  61. type versionValue struct {
  62. content *api.GetCertificateContentResponse
  63. }
  64. type tokenKey struct {
  65. token string
  66. }
  67. type tokenValue struct {
  68. authorizedKey *iamkey.Key
  69. expiresAt time.Time
  70. }
  71. type folderAndNameKey struct {
  72. folderID string
  73. name string
  74. }
  75. type folderAndNameValue struct {
  76. certificateID string
  77. }
  78. // NewFakeCertificateManagerServer creates a new fake server for testing.
  79. func NewFakeCertificateManagerServer(clock clock.Clock, tokenExpirationDuration time.Duration) *FakeCertificateManagerServer {
  80. return &FakeCertificateManagerServer{
  81. certificateMap: make(map[certificateKey]certificateValue),
  82. versionMap: make(map[versionKey]versionValue),
  83. tokenMap: make(map[tokenKey]tokenValue),
  84. folderAndNameMap: make(map[folderAndNameKey]folderAndNameValue),
  85. tokenExpirationDuration: tokenExpirationDuration,
  86. clock: clock,
  87. }
  88. }
  89. // CreateCertificate creates a new certificate in the fake server.
  90. func (s *FakeCertificateManagerServer) CreateCertificate(authorizedKey *iamkey.Key, folderID, name string, content *api.GetCertificateContentResponse) (string, string) {
  91. certificateID := uuid.NewString()
  92. versionID := uuid.NewString()
  93. s.certificateMap[certificateKey{certificateID}] = certificateValue{authorizedKey}
  94. s.versionMap[versionKey{certificateID, ""}] = versionValue{content} // empty versionID corresponds to the latest version
  95. s.versionMap[versionKey{certificateID, versionID}] = versionValue{content}
  96. if _, exists := s.folderAndNameMap[folderAndNameKey{folderID, name}]; exists {
  97. s.logger.Error(nil, "ERROR: On the fake server, you cannot add two certificates with the same name in the same folder.")
  98. }
  99. s.folderAndNameMap[folderAndNameKey{folderID, name}] = folderAndNameValue{certificateID}
  100. return certificateID, versionID
  101. }
  102. // AddVersion adds a new version to an existing certificate.
  103. func (s *FakeCertificateManagerServer) AddVersion(certificateID string, content *api.GetCertificateContentResponse) string {
  104. versionID := uuid.NewString()
  105. s.versionMap[versionKey{certificateID, ""}] = versionValue{content} // empty versionID corresponds to the latest version
  106. s.versionMap[versionKey{certificateID, versionID}] = versionValue{content}
  107. return versionID
  108. }
  109. // NewIamToken creates a new IAM token for the given authorized key.
  110. func (s *FakeCertificateManagerServer) NewIamToken(authorizedKey *iamkey.Key) *ydxcommon.IamToken {
  111. token := uuid.NewString()
  112. expiresAt := s.clock.CurrentTime().Add(s.tokenExpirationDuration)
  113. s.tokenMap[tokenKey{token}] = tokenValue{authorizedKey, expiresAt}
  114. return &ydxcommon.IamToken{Token: token, ExpiresAt: expiresAt}
  115. }
  116. func (s *FakeCertificateManagerServer) getCertificateContent(iamToken, certificateID, versionID string) (*api.GetCertificateContentResponse, error) {
  117. if _, ok := s.certificateMap[certificateKey{certificateID}]; !ok {
  118. return nil, errors.New("certificate not found")
  119. }
  120. if _, ok := s.versionMap[versionKey{certificateID, versionID}]; !ok {
  121. return nil, errors.New("version not found")
  122. }
  123. if _, ok := s.tokenMap[tokenKey{iamToken}]; !ok {
  124. return nil, errors.New("unauthenticated")
  125. }
  126. if s.tokenMap[tokenKey{iamToken}].expiresAt.Before(s.clock.CurrentTime()) {
  127. return nil, errors.New("iam token expired")
  128. }
  129. if !cmp.Equal(s.tokenMap[tokenKey{iamToken}].authorizedKey, s.certificateMap[certificateKey{certificateID}].expectedAuthorizedKey, cmpopts.IgnoreUnexported(iamkey.Key{})) {
  130. return nil, errors.New("permission denied")
  131. }
  132. return s.versionMap[versionKey{certificateID, versionID}].content, nil
  133. }
  134. func (s *FakeCertificateManagerServer) getExCertificateContent(iamToken, folderID, name, versionID string) (*api.GetExCertificateContentResponse, error) {
  135. if _, ok := s.folderAndNameMap[folderAndNameKey{folderID, name}]; !ok {
  136. return nil, errors.New("certificate not found")
  137. }
  138. certificateID := s.folderAndNameMap[folderAndNameKey{folderID, name}].certificateID
  139. if _, ok := s.versionMap[versionKey{certificateID, versionID}]; !ok {
  140. return nil, errors.New("version not found")
  141. }
  142. if _, ok := s.tokenMap[tokenKey{iamToken}]; !ok {
  143. return nil, errors.New("unauthenticated")
  144. }
  145. if s.tokenMap[tokenKey{iamToken}].expiresAt.Before(s.clock.CurrentTime()) {
  146. return nil, errors.New("iam token expired")
  147. }
  148. if !cmp.Equal(s.tokenMap[tokenKey{iamToken}].authorizedKey, s.certificateMap[certificateKey{certificateID}].expectedAuthorizedKey, cmpopts.IgnoreUnexported(iamkey.Key{})) {
  149. return nil, errors.New("permission denied")
  150. }
  151. certificateChain := s.versionMap[versionKey{certificateID, versionID}].content.CertificateChain
  152. privateKey := s.versionMap[versionKey{certificateID, versionID}].content.PrivateKey
  153. return &api.GetExCertificateContentResponse{
  154. CertificateId: certificateID,
  155. CertificateChain: certificateChain,
  156. PrivateKey: privateKey,
  157. }, nil
  158. }