@@ -7,17 +7,15 @@ import (
77 "log"
88 "os"
99 "path"
10- "sync"
1110
12- "github.com/adrg/xdg "
11+ "golang.org/x/sync/syncmap "
1312)
1413
1514// BasicManager is a simple secrets manager that stores secrets in an
1615// unencrypted file. This is for testing/development purposes only.
1716type BasicManager struct {
1817 filePath string
19- secrets map [string ]string
20- mu sync.RWMutex // Protects concurrent access to secrets map
18+ secrets syncmap.Map // Thread-safe map for storing secrets
2119}
2220
2321// GetSecret retrieves a secret from the secret store.
@@ -26,14 +24,11 @@ func (b *BasicManager) GetSecret(name string) (string, error) {
2624 return "" , errors .New ("secret name cannot be empty" )
2725 }
2826
29- b .mu .RLock ()
30- defer b .mu .RUnlock ()
31-
32- value , ok := b .secrets [name ]
27+ value , ok := b .secrets .Load (name )
3328 if ! ok {
3429 return "" , fmt .Errorf ("secret not found: %s" , name )
3530 }
36- return value , nil
31+ return value .( string ) , nil
3732}
3833
3934// SetSecret stores a secret in the secret store.
@@ -42,10 +37,7 @@ func (b *BasicManager) SetSecret(name, value string) error {
4237 return errors .New ("secret name cannot be empty" )
4338 }
4439
45- b .mu .Lock ()
46- defer b .mu .Unlock ()
47-
48- b .secrets [name ] = value
40+ b .secrets .Store (name , value )
4941 return b .updateFile ()
5042}
5143
@@ -55,32 +47,46 @@ func (b *BasicManager) DeleteSecret(name string) error {
5547 return errors .New ("secret name cannot be empty" )
5648 }
5749
58- b .mu .Lock ()
59- defer b .mu .Unlock ()
60-
61- if _ , exists := b .secrets [name ]; ! exists {
50+ // Check if the secret exists first
51+ _ , ok := b .secrets .Load (name )
52+ if ! ok {
6253 return fmt .Errorf ("cannot delete non-existent secret: %s" , name )
6354 }
6455
65- delete ( b .secrets , name )
56+ b .secrets . Delete ( name )
6657 return b .updateFile ()
6758}
6859
6960// ListSecrets returns a list of all secret names stored in the manager.
7061func (b * BasicManager ) ListSecrets () ([]string , error ) {
71- b .mu .RLock ()
72- defer b .mu .RUnlock ()
62+ var secretNames []string
7363
74- secretNames := make ([] string , 0 , len ( b . secrets ))
75- for name := range b . secrets {
76- secretNames = append ( secretNames , name )
77- }
64+ b . secrets . Range ( func ( key , _ interface {}) bool {
65+ secretNames = append ( secretNames , key .( string ))
66+ return true
67+ })
7868
7969 return secretNames , nil
8070}
8171
72+ // Cleanup removes all secrets managed by this manager.
73+ func (b * BasicManager ) Cleanup () error {
74+ // Create a new empty syncmap.Map
75+ b .secrets = syncmap.Map {}
76+
77+ // Update the file to reflect the empty state
78+ return b .updateFile ()
79+ }
80+
8281func (b * BasicManager ) updateFile () error {
83- contents , err := json .Marshal (fileStructure {Secrets : b .secrets })
82+ // Convert syncmap.Map to map[string]string for JSON marshaling
83+ secretsMap := make (map [string ]string )
84+ b .secrets .Range (func (key , value interface {}) bool {
85+ secretsMap [key .(string )] = value .(string )
86+ return true
87+ })
88+
89+ contents , err := json .Marshal (fileStructure {Secrets : secretsMap })
8490 if err != nil {
8591 return fmt .Errorf ("failed to marshal secrets: %w" , err )
8692 }
@@ -103,16 +109,8 @@ type fileStructure struct {
103109 Secrets map [string ]string `json:"secrets"`
104110}
105111
106- // BasicManagerFactory is an implementation of the ManagerFactory interface for BasicManager.
107- type BasicManagerFactory struct {}
108-
109- // Build creates an instance of BasicManager.
110- func (BasicManagerFactory ) Build (config map [string ]interface {}) (Manager , error ) {
111- filePath , ok := config ["secretsFile" ].(string )
112- if ! ok {
113- return nil , errors .New ("secretsFile is required" )
114- }
115-
112+ // NewBasicManager creates an instance of BasicManager.
113+ func NewBasicManager (filePath string ) (Manager , error ) {
116114 // Add warning for production use
117115 if os .Getenv ("ENVIRONMENT" ) == "production" {
118116 log .Println ("WARNING: BasicManager is not secure for production use" )
@@ -131,32 +129,25 @@ func (BasicManagerFactory) Build(config map[string]interface{}) (Manager, error)
131129 return nil , fmt .Errorf ("failed to stat secrets file: %w" , err )
132130 }
133131
134- var secrets map [string ]string
135- if stat .Size () == 0 {
136- secrets = make (map [string ]string )
137- } else {
132+ // Create a new BasicManager with an empty syncmap.Map
133+ manager := & BasicManager {
134+ filePath : filePath ,
135+ secrets : syncmap.Map {},
136+ }
137+
138+ // If the file is not empty, load the secrets into the syncmap.Map
139+ if stat .Size () > 0 {
138140 var contents fileStructure
139141 err := json .NewDecoder (secretsFile ).Decode (& contents )
140142 if err != nil {
141143 return nil , fmt .Errorf ("failed to decode secrets file: %w" , err )
142144 }
143- secrets = contents .Secrets
144- }
145145
146- return & BasicManager {
147- filePath : filePath ,
148- secrets : secrets ,
149- mu : sync.RWMutex {},
150- }, nil
151- }
152-
153- // CreateDefaultSecretsManager creates a secret manager instance with a default config.
154- // TODO: Remove once we support more than one provider
155- func CreateDefaultSecretsManager () (Manager , error ) {
156- // TODO: make this configurable?
157- secretsPath , err := xdg .DataFile ("vibetool/secrets" )
158- if err != nil {
159- return nil , fmt .Errorf ("unable to access secrets file path %v" , err )
146+ // Store each secret in the syncmap.Map
147+ for key , value := range contents .Secrets {
148+ manager .secrets .Store (key , value )
149+ }
160150 }
161- return BasicManagerFactory {}.Build (map [string ]any {"secretsFile" : secretsPath })
151+
152+ return manager , nil
162153}
0 commit comments