1
1
using System ;
2
2
using System . Collections . Generic ;
3
3
using System . IO ;
4
+ using System . Linq ;
4
5
using System . Text ;
5
6
6
7
namespace GitCredentialManager
@@ -22,44 +23,26 @@ public PlaintextCredentialStore(IFileSystem fileSystem, string storeRoot, string
22
23
protected string Namespace { get ; }
23
24
protected virtual string CredentialFileExtension => ".credential" ;
24
25
25
- #region ICredentialStore
26
-
27
26
public ICredential Get ( string service , string account )
28
27
{
29
- string serviceSlug = CreateServiceSlug ( service ) ;
30
- string searchPath = Path . Combine ( StoreRoot , serviceSlug ) ;
31
- bool anyAccount = string . IsNullOrWhiteSpace ( account ) ;
32
-
33
- if ( ! FileSystem . DirectoryExists ( searchPath ) )
34
- {
35
- return null ;
36
- }
37
-
38
- IEnumerable < string > allFiles = FileSystem . EnumerateFiles ( searchPath , $ "*{ CredentialFileExtension } ") ;
39
-
40
- foreach ( string fullPath in allFiles )
41
- {
42
- string accountFile = Path . GetFileNameWithoutExtension ( fullPath ) ;
43
- if ( anyAccount || StringComparer . OrdinalIgnoreCase . Equals ( account , accountFile ) )
44
- {
45
- // Validate the credential metadata also matches our search
46
- if ( TryDeserializeCredential ( fullPath , out FileCredential credential ) &&
47
- StringComparer . OrdinalIgnoreCase . Equals ( service , credential . Service ) &&
48
- ( anyAccount || StringComparer . OrdinalIgnoreCase . Equals ( account , credential . Account ) ) )
49
- {
50
- return credential ;
51
- }
52
- }
53
- }
54
-
55
- return null ;
28
+ return Enumerate ( service , account ) . FirstOrDefault ( ) ;
56
29
}
57
30
58
31
public void AddOrUpdate ( string service , string account , string secret )
59
32
{
60
33
// Ensure the store root exists and permissions are set
61
34
EnsureStoreRoot ( ) ;
62
35
36
+ FileCredential existingCredential = Enumerate ( service , account ) . FirstOrDefault ( ) ;
37
+
38
+ // No need to update existing credential if nothing has changed
39
+ if ( existingCredential != null &&
40
+ StringComparer . Ordinal . Equals ( account , existingCredential . Account ) &&
41
+ StringComparer . Ordinal . Equals ( secret , existingCredential . Password ) )
42
+ {
43
+ return ;
44
+ }
45
+
63
46
string serviceSlug = CreateServiceSlug ( service ) ;
64
47
string servicePath = Path . Combine ( StoreRoot , serviceSlug ) ;
65
48
@@ -75,39 +58,16 @@ public void AddOrUpdate(string service, string account, string secret)
75
58
76
59
public bool Remove ( string service , string account )
77
60
{
78
- string serviceSlug = CreateServiceSlug ( service ) ;
79
- string searchPath = Path . Combine ( StoreRoot , serviceSlug ) ;
80
- bool anyAccount = string . IsNullOrWhiteSpace ( account ) ;
81
-
82
- if ( ! FileSystem . DirectoryExists ( searchPath ) )
83
- {
84
- return false ;
85
- }
86
-
87
- IEnumerable < string > allFiles = FileSystem . EnumerateFiles ( searchPath , $ "*{ CredentialFileExtension } ") ;
88
-
89
- foreach ( string fullPath in allFiles )
61
+ foreach ( FileCredential credential in Enumerate ( service , account ) )
90
62
{
91
- string accountFile = Path . GetFileNameWithoutExtension ( fullPath ) ;
92
- if ( anyAccount || StringComparer . OrdinalIgnoreCase . Equals ( account , accountFile ) )
93
- {
94
- // Validate the credential metadata also matches our search
95
- if ( TryDeserializeCredential ( fullPath , out FileCredential credential ) &&
96
- StringComparer . OrdinalIgnoreCase . Equals ( service , credential . Service ) &&
97
- ( anyAccount || StringComparer . OrdinalIgnoreCase . Equals ( account , credential . Account ) ) )
98
- {
99
- // Delete the credential file
100
- FileSystem . DeleteFile ( fullPath ) ;
101
- return true ;
102
- }
103
- }
63
+ // Only delete the first match
64
+ FileSystem . DeleteFile ( credential . FullPath ) ;
65
+ return true ;
104
66
}
105
67
106
68
return false ;
107
69
}
108
70
109
- #endregion
110
-
111
71
protected virtual bool TryDeserializeCredential ( string path , out FileCredential credential )
112
72
{
113
73
string text ;
@@ -162,6 +122,35 @@ protected virtual void SerializeCredential(FileCredential credential)
162
122
}
163
123
}
164
124
125
+ private IEnumerable < FileCredential > Enumerate ( string service , string account )
126
+ {
127
+ string serviceSlug = CreateServiceSlug ( service ) ;
128
+ string searchPath = Path . Combine ( StoreRoot , serviceSlug ) ;
129
+ bool anyAccount = string . IsNullOrWhiteSpace ( account ) ;
130
+
131
+ if ( ! FileSystem . DirectoryExists ( searchPath ) )
132
+ {
133
+ yield break ;
134
+ }
135
+
136
+ IEnumerable < string > allFiles = FileSystem . EnumerateFiles ( searchPath , $ "*{ CredentialFileExtension } ") ;
137
+
138
+ foreach ( string fullPath in allFiles )
139
+ {
140
+ string accountFile = Path . GetFileNameWithoutExtension ( fullPath ) ;
141
+ if ( anyAccount || StringComparer . OrdinalIgnoreCase . Equals ( account , accountFile ) )
142
+ {
143
+ // Validate the credential metadata also matches our search
144
+ if ( TryDeserializeCredential ( fullPath , out FileCredential credential ) &&
145
+ StringComparer . OrdinalIgnoreCase . Equals ( service , credential . Service ) &&
146
+ ( anyAccount || StringComparer . OrdinalIgnoreCase . Equals ( account , credential . Account ) ) )
147
+ {
148
+ yield return credential ;
149
+ }
150
+ }
151
+ }
152
+ }
153
+
165
154
/// <summary>
166
155
/// Ensure the store root directory exists. If it does not, create a new directory with
167
156
/// permissions that only permit the owner to read/write/execute. Permissions on an existing
0 commit comments