client.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  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 keepersecurity
  13. import (
  14. "context"
  15. "encoding/json"
  16. "errors"
  17. "fmt"
  18. "regexp"
  19. "strings"
  20. ksm "github.com/keeper-security/secrets-manager-go/core"
  21. "golang.org/x/exp/maps"
  22. esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  23. )
  24. const (
  25. errKeeperSecuritySecretsNotFound = "unable to find secrets. %w"
  26. errKeeperSecuritySecretNotFound = "unable to find secret %s. Error: %w"
  27. errKeeperSecuritySecretNotUnique = "more than 1 secret %s found"
  28. errKeeperSecurityNoSecretsFound = "no secrets found"
  29. errKeeperSecurityInvalidSecretInvalidFormat = "invalid secret. Invalid format: %w"
  30. errKeeperSecurityInvalidSecretDuplicatedKey = "invalid Secret. Following keys are duplicated %s"
  31. errKeeperSecurityInvalidProperty = "invalid Property. Secret %s does not have any key matching %s"
  32. errKeeperSecurityInvalidField = "invalid Field. Key %s does not exists"
  33. errKeeperSecurityNoFields = "invalid Secret. Secret %s does not contain any valid field/file"
  34. keeperSecurityFileRef = "fileRef"
  35. keeperSecurityMfa = "oneTimeCode"
  36. errTagsNotImplemented = "'find.tags' is not implemented in the KeeperSecurity provider"
  37. errPathNotImplemented = "'find.path' is not implemented in the KeeperSecurity provider"
  38. errInvalidJSONSecret = "invalid Secret. Secret %s can not be converted to JSON. %w"
  39. errInvalidRegex = "find.name.regex. Invalid Regular expresion %s. %w"
  40. errInvalidRemoteRefKey = "match.remoteRef.remoteKey. Invalid format. Format should match secretName/key got %s"
  41. errInvalidSecretType = "ESO can only push/delete %s record types. Secret %s is type %s"
  42. errFieldNotFound = "secret %s does not contain any custom field with label %s"
  43. externalSecretType = "externalSecrets"
  44. secretType = "secret"
  45. LoginType = "login"
  46. LoginTypeExpr = "login|username"
  47. PasswordType = "password"
  48. URLTypeExpr = "url|baseurl"
  49. URLType = "url"
  50. )
  51. type Client struct {
  52. ksmClient SecurityClient
  53. folderID string
  54. }
  55. type SecurityClient interface {
  56. GetSecrets(filter []string) ([]*ksm.Record, error)
  57. GetSecretByTitle(recordTitle string) (*ksm.Record, error)
  58. CreateSecretWithRecordData(recUID, folderUID string, recordData *ksm.RecordCreate) (string, error)
  59. DeleteSecrets(recrecordUids []string) (map[string]string, error)
  60. Save(record *ksm.Record) error
  61. }
  62. type Field struct {
  63. Type string `json:"type"`
  64. Value []string `json:"value"`
  65. }
  66. type CustomField struct {
  67. Type string `json:"type"`
  68. Label string `json:"label"`
  69. Value []string `json:"value"`
  70. }
  71. type File struct {
  72. Title string `json:"type"`
  73. Content string `json:"content"`
  74. }
  75. type Secret struct {
  76. Title string `json:"title"`
  77. Type string `json:"type"`
  78. Fields []Field `json:"fields"`
  79. Custom []CustomField `json:"custom"`
  80. Files []File `json:"files"`
  81. }
  82. func (c *Client) Validate() (esv1beta1.ValidationResult, error) {
  83. return esv1beta1.ValidationResultReady, nil
  84. }
  85. func (c *Client) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  86. record, err := c.findSecretByID(ref.Key)
  87. if err != nil {
  88. return nil, err
  89. }
  90. secret, err := c.getValidKeeperSecret(record)
  91. if err != nil {
  92. return nil, err
  93. }
  94. return secret.getItem(ref)
  95. }
  96. func (c *Client) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  97. record, err := c.findSecretByID(ref.Key)
  98. if err != nil {
  99. return nil, err
  100. }
  101. secret, err := c.getValidKeeperSecret(record)
  102. if err != nil {
  103. return nil, err
  104. }
  105. return secret.getItems(ref)
  106. }
  107. func (c *Client) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
  108. if ref.Tags != nil {
  109. return nil, fmt.Errorf(errTagsNotImplemented)
  110. }
  111. if ref.Path != nil {
  112. return nil, fmt.Errorf(errPathNotImplemented)
  113. }
  114. secretData := make(map[string][]byte)
  115. records, err := c.findSecrets()
  116. if err != nil {
  117. return nil, err
  118. }
  119. for _, record := range records {
  120. secret, err := c.getValidKeeperSecret(record)
  121. if err != nil {
  122. return nil, err
  123. }
  124. match, err := regexp.MatchString(ref.Name.RegExp, secret.Title)
  125. if err != nil {
  126. return nil, fmt.Errorf(errInvalidRegex, ref.Name.RegExp, err)
  127. }
  128. if !match {
  129. continue
  130. }
  131. secretData[secret.Title], err = secret.getItem(esv1beta1.ExternalSecretDataRemoteRef{})
  132. if err != nil {
  133. return nil, err
  134. }
  135. }
  136. return secretData, nil
  137. }
  138. func (c *Client) Close(ctx context.Context) error {
  139. return nil
  140. }
  141. func (c *Client) PushSecret(ctx context.Context, value []byte, remoteRef esv1beta1.PushRemoteRef) error {
  142. parts, err := c.buildSecretNameAndKey(remoteRef)
  143. if err != nil {
  144. return err
  145. }
  146. secret, err := c.findSecretByName(parts[0])
  147. if err != nil {
  148. _, err = c.createSecret(parts[0], parts[1], value)
  149. if err != nil {
  150. return err
  151. }
  152. }
  153. if secret != nil {
  154. if secret.Type() != externalSecretType {
  155. return fmt.Errorf(errInvalidSecretType, externalSecretType, secret.Title(), secret.Type())
  156. }
  157. err = c.updateSecret(secret, parts[1], value)
  158. if err != nil {
  159. return err
  160. }
  161. }
  162. return nil
  163. }
  164. func (c *Client) DeleteSecret(ctx context.Context, remoteRef esv1beta1.PushRemoteRef) error {
  165. parts, err := c.buildSecretNameAndKey(remoteRef)
  166. if err != nil {
  167. return err
  168. }
  169. secret, err := c.findSecretByName(parts[0])
  170. if err != nil {
  171. return err
  172. }
  173. if secret.Type() != externalSecretType {
  174. return fmt.Errorf(errInvalidSecretType, externalSecretType, secret.Title(), secret.Type())
  175. }
  176. _, err = c.ksmClient.DeleteSecrets([]string{secret.Uid})
  177. if err != nil {
  178. return nil
  179. }
  180. return nil
  181. }
  182. func (c *Client) buildSecretNameAndKey(remoteRef esv1beta1.PushRemoteRef) ([]string, error) {
  183. parts := strings.Split(remoteRef.GetRemoteKey(), "/")
  184. if len(parts) != 2 {
  185. return nil, fmt.Errorf(errInvalidRemoteRefKey, remoteRef.GetRemoteKey())
  186. }
  187. return parts, nil
  188. }
  189. func (c *Client) createSecret(name, key string, value []byte) (string, error) {
  190. normalizedKey := strings.ToLower(key)
  191. externalSecretRecord := ksm.NewRecordCreate(externalSecretType, name)
  192. login := regexp.MustCompile(LoginTypeExpr)
  193. pass := regexp.MustCompile(PasswordType)
  194. url := regexp.MustCompile(URLTypeExpr)
  195. switch {
  196. case login.MatchString(normalizedKey):
  197. externalSecretRecord.Fields = append(externalSecretRecord.Fields,
  198. ksm.NewLogin(string(value)),
  199. )
  200. case pass.MatchString(normalizedKey):
  201. externalSecretRecord.Fields = append(externalSecretRecord.Fields,
  202. ksm.NewPassword(string(value)),
  203. )
  204. case url.MatchString(normalizedKey):
  205. externalSecretRecord.Fields = append(externalSecretRecord.Fields,
  206. ksm.NewUrl(string(value)),
  207. )
  208. default:
  209. field := ksm.KeeperRecordField{Type: secretType, Label: key}
  210. externalSecretRecord.Custom = append(externalSecretRecord.Custom,
  211. ksm.Secret{KeeperRecordField: field, Value: []string{string(value)}},
  212. )
  213. }
  214. return c.ksmClient.CreateSecretWithRecordData("", c.folderID, externalSecretRecord)
  215. }
  216. func (c *Client) updateSecret(secret *ksm.Record, key string, value []byte) error {
  217. normalizedKey := strings.ToLower(key)
  218. login := regexp.MustCompile(LoginTypeExpr)
  219. pass := regexp.MustCompile(PasswordType)
  220. url := regexp.MustCompile(URLTypeExpr)
  221. custom := false
  222. switch {
  223. case login.MatchString(normalizedKey):
  224. secret.SetFieldValueSingle(LoginType, string(value))
  225. case pass.MatchString(normalizedKey):
  226. secret.SetPassword(string(value))
  227. case url.MatchString(normalizedKey):
  228. secret.SetFieldValueSingle(URLType, string(value))
  229. default:
  230. custom = true
  231. }
  232. if custom {
  233. field := secret.GetCustomFieldValueByLabel(key)
  234. if field != "" {
  235. secret.SetCustomFieldValueSingle(key, string(value))
  236. } else {
  237. return fmt.Errorf(errFieldNotFound, secret.Title(), key)
  238. }
  239. }
  240. return c.ksmClient.Save(secret)
  241. }
  242. func (c *Client) getValidKeeperSecret(secret *ksm.Record) (*Secret, error) {
  243. keeperSecret := Secret{}
  244. err := json.Unmarshal([]byte(secret.RawJson), &keeperSecret)
  245. if err != nil {
  246. return nil, fmt.Errorf(errKeeperSecurityInvalidSecretInvalidFormat, err)
  247. }
  248. keeperSecret.addFiles(secret.Files)
  249. err = keeperSecret.validate()
  250. if err != nil {
  251. return nil, err
  252. }
  253. return &keeperSecret, nil
  254. }
  255. func (c *Client) findSecrets() ([]*ksm.Record, error) {
  256. records, err := c.ksmClient.GetSecrets([]string{})
  257. if err != nil {
  258. return nil, fmt.Errorf(errKeeperSecuritySecretsNotFound, err)
  259. }
  260. return records, nil
  261. }
  262. func (c *Client) findSecretByID(id string) (*ksm.Record, error) {
  263. records, err := c.ksmClient.GetSecrets([]string{id})
  264. if err != nil {
  265. return nil, fmt.Errorf(errKeeperSecuritySecretNotFound, id, err)
  266. }
  267. if len(records) == 0 {
  268. return nil, errors.New(errKeeperSecurityNoSecretsFound)
  269. }
  270. if len(records) > 1 {
  271. return nil, fmt.Errorf(errKeeperSecuritySecretNotUnique, id)
  272. }
  273. return records[0], nil
  274. }
  275. func (c *Client) findSecretByName(name string) (*ksm.Record, error) {
  276. record, err := c.ksmClient.GetSecretByTitle(name)
  277. if err != nil {
  278. return nil, err
  279. }
  280. return record, nil
  281. }
  282. func (s *Secret) validate() error {
  283. fields := make(map[string]int)
  284. for _, field := range s.Fields {
  285. fields[field.Type]++
  286. }
  287. for _, customField := range s.Custom {
  288. fields[customField.Label]++
  289. }
  290. for _, file := range s.Files {
  291. fields[file.Title]++
  292. }
  293. var duplicates []string
  294. for key, ocurrences := range fields {
  295. if ocurrences > 1 {
  296. duplicates = append(duplicates, key)
  297. }
  298. }
  299. if len(duplicates) != 0 {
  300. return fmt.Errorf(errKeeperSecurityInvalidSecretDuplicatedKey, strings.Join(duplicates, ", "))
  301. }
  302. return nil
  303. }
  304. func (s *Secret) addFiles(keeperFiles []*ksm.KeeperFile) {
  305. for _, f := range keeperFiles {
  306. s.Files = append(
  307. s.Files,
  308. File{
  309. Title: f.Title,
  310. Content: string(f.GetFileData()),
  311. },
  312. )
  313. }
  314. }
  315. func (s *Secret) getItem(ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  316. if ref.Property != "" {
  317. return s.getProperty(ref.Property)
  318. }
  319. secret, err := s.toString()
  320. return []byte(secret), err
  321. }
  322. func (s *Secret) getItems(ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  323. secretData := make(map[string][]byte)
  324. if ref.Property != "" {
  325. value, err := s.getProperty(ref.Property)
  326. if err != nil {
  327. return nil, err
  328. }
  329. secretData[ref.Property] = value
  330. return secretData, nil
  331. }
  332. fields := s.getFields()
  333. maps.Copy(secretData, fields)
  334. customFields := s.getCustomFields()
  335. maps.Copy(secretData, customFields)
  336. files := s.getFiles()
  337. maps.Copy(secretData, files)
  338. if len(secretData) == 0 {
  339. return nil, fmt.Errorf(errKeeperSecurityNoFields, s.Title)
  340. }
  341. return secretData, nil
  342. }
  343. func (s *Secret) getField(key string) ([]byte, error) {
  344. for _, field := range s.Fields {
  345. if field.Type == key && field.Type != keeperSecurityFileRef && field.Type != keeperSecurityMfa && len(field.Value) > 0 {
  346. return []byte(field.Value[0]), nil
  347. }
  348. }
  349. return nil, fmt.Errorf(errKeeperSecurityInvalidField, key)
  350. }
  351. func (s *Secret) getFields() map[string][]byte {
  352. secretData := make(map[string][]byte)
  353. for _, field := range s.Fields {
  354. if len(field.Value) > 0 {
  355. secretData[field.Type] = []byte(field.Value[0])
  356. }
  357. }
  358. return secretData
  359. }
  360. func (s *Secret) getCustomField(key string) ([]byte, error) {
  361. for _, field := range s.Custom {
  362. if field.Label == key && len(field.Value) > 0 {
  363. return []byte(field.Value[0]), nil
  364. }
  365. }
  366. return nil, fmt.Errorf(errKeeperSecurityInvalidField, key)
  367. }
  368. func (s *Secret) getCustomFields() map[string][]byte {
  369. secretData := make(map[string][]byte)
  370. for _, field := range s.Custom {
  371. if len(field.Value) > 0 {
  372. secretData[field.Label] = []byte(field.Value[0])
  373. }
  374. }
  375. return secretData
  376. }
  377. func (s *Secret) getFile(key string) ([]byte, error) {
  378. for _, file := range s.Files {
  379. if file.Title == key {
  380. return []byte(file.Content), nil
  381. }
  382. }
  383. return nil, fmt.Errorf(errKeeperSecurityInvalidField, key)
  384. }
  385. func (s *Secret) getProperty(key string) ([]byte, error) {
  386. field, _ := s.getField(key)
  387. if field != nil {
  388. return field, nil
  389. }
  390. customField, _ := s.getCustomField(key)
  391. if customField != nil {
  392. return customField, nil
  393. }
  394. file, _ := s.getFile(key)
  395. if file != nil {
  396. return file, nil
  397. }
  398. return nil, fmt.Errorf(errKeeperSecurityInvalidProperty, s.Title, key)
  399. }
  400. func (s *Secret) getFiles() map[string][]byte {
  401. secretData := make(map[string][]byte)
  402. for _, file := range s.Files {
  403. secretData[file.Title] = []byte(file.Content)
  404. }
  405. return secretData
  406. }
  407. func (s *Secret) toString() (string, error) {
  408. secretJSON, err := json.Marshal(s)
  409. if err != nil {
  410. return "", fmt.Errorf(errInvalidJSONSecret, s.Title, err)
  411. }
  412. return string(secretJSON), nil
  413. }