8
8
using System . IO ;
9
9
using System . Linq ;
10
10
using System . Security . Cryptography ;
11
+ using System . Threading ;
11
12
using System . Threading . Tasks ;
12
13
using Microsoft . Azure . WebJobs . Host ;
13
14
using Microsoft . Azure . WebJobs . Script . Config ;
@@ -24,6 +25,7 @@ public class SecretManager : IDisposable, ISecretManager
24
25
private readonly ILogger _logger ;
25
26
private readonly ISecretsRepository _repository ;
26
27
private HostSecretsInfo _hostSecrets ;
28
+ private SemaphoreSlim _hostSecretsLock = new SemaphoreSlim ( 1 , 1 ) ;
27
29
28
30
// for testing
29
31
public SecretManager ( )
@@ -62,53 +64,64 @@ protected virtual void Dispose(bool disposing)
62
64
if ( disposing )
63
65
{
64
66
( _repository as IDisposable ) ? . Dispose ( ) ;
67
+ _hostSecretsLock . Dispose ( ) ;
65
68
}
66
69
}
67
70
68
71
public async virtual Task < HostSecretsInfo > GetHostSecretsAsync ( )
69
72
{
70
73
if ( _hostSecrets == null )
71
74
{
72
- HostSecrets hostSecrets = await LoadSecretsAsync < HostSecrets > ( ) ;
73
-
74
- if ( hostSecrets == null )
75
- {
76
- // host secrets do not yet exist so generate them
77
- _traceWriter . Verbose ( Resources . TraceHostSecretGeneration ) ;
78
- _logger ? . LogDebug ( Resources . TraceHostSecretGeneration ) ;
79
- hostSecrets = GenerateHostSecrets ( ) ;
80
- await PersistSecretsAsync ( hostSecrets ) ;
81
- }
82
-
75
+ HostSecrets hostSecrets ;
76
+ // Allow only one thread to modify the secrets
77
+ await _hostSecretsLock . WaitAsync ( ) ;
83
78
try
84
79
{
85
- // Host secrets will be in the original persisted state at this point (e.g. encrypted),
86
- // so we read the secrets running them through the appropriate readers
87
- hostSecrets = ReadHostSecrets ( hostSecrets ) ;
88
- }
89
- catch ( CryptographicException )
90
- {
91
- _traceWriter . Verbose ( Resources . TraceNonDecryptedHostSecretRefresh ) ;
92
- _logger ? . LogDebug ( Resources . TraceNonDecryptedHostSecretRefresh ) ;
93
- await PersistSecretsAsync ( hostSecrets , null , true ) ;
94
- await RefreshSecretsAsync ( hostSecrets ) ;
95
- }
80
+ hostSecrets = await LoadSecretsAsync < HostSecrets > ( ) ;
96
81
97
- // If the persistence state of any of our secrets is stale (e.g. the encryption key has been rotated), update
98
- // the state and persist the secrets
99
- if ( hostSecrets . HasStaleKeys )
100
- {
101
- _traceWriter . Verbose ( Resources . TraceStaleHostSecretRefresh ) ;
102
- _logger ? . LogDebug ( Resources . TraceStaleHostSecretRefresh ) ;
103
- await RefreshSecretsAsync ( hostSecrets ) ;
104
- }
82
+ if ( hostSecrets == null )
83
+ {
84
+ // host secrets do not yet exist so generate them
85
+ _traceWriter . Verbose ( Resources . TraceHostSecretGeneration ) ;
86
+ _logger ? . LogDebug ( Resources . TraceHostSecretGeneration ) ;
87
+ hostSecrets = GenerateHostSecrets ( ) ;
88
+ await PersistSecretsAsync ( hostSecrets ) ;
89
+ }
90
+
91
+ try
92
+ {
93
+ // Host secrets will be in the original persisted state at this point (e.g. encrypted),
94
+ // so we read the secrets running them through the appropriate readers
95
+ hostSecrets = ReadHostSecrets ( hostSecrets ) ;
96
+ }
97
+ catch ( CryptographicException )
98
+ {
99
+ _traceWriter . Verbose ( Resources . TraceNonDecryptedHostSecretRefresh ) ;
100
+ _logger ? . LogDebug ( Resources . TraceNonDecryptedHostSecretRefresh ) ;
101
+ await PersistSecretsAsync ( hostSecrets , null , true ) ;
102
+ await RefreshSecretsAsync ( hostSecrets ) ;
103
+ }
104
+
105
+ // If the persistence state of any of our secrets is stale (e.g. the encryption key has been rotated), update
106
+ // the state and persist the secrets
107
+ if ( hostSecrets . HasStaleKeys )
108
+ {
109
+ _traceWriter . Verbose ( Resources . TraceStaleHostSecretRefresh ) ;
110
+ _logger ? . LogDebug ( Resources . TraceStaleHostSecretRefresh ) ;
111
+ await RefreshSecretsAsync ( hostSecrets ) ;
112
+ }
105
113
106
- _hostSecrets = new HostSecretsInfo
114
+ _hostSecrets = new HostSecretsInfo
115
+ {
116
+ MasterKey = hostSecrets . MasterKey . Value ,
117
+ FunctionKeys = hostSecrets . FunctionKeys . ToDictionary ( s => s . Name , s => s . Value ) ,
118
+ SystemKeys = hostSecrets . SystemKeys . ToDictionary ( s => s . Name , s => s . Value )
119
+ } ;
120
+ }
121
+ finally
107
122
{
108
- MasterKey = hostSecrets . MasterKey . Value ,
109
- FunctionKeys = hostSecrets . FunctionKeys . ToDictionary ( s => s . Name , s => s . Value ) ,
110
- SystemKeys = hostSecrets . SystemKeys . ToDictionary ( s => s . Name , s => s . Value )
111
- } ;
123
+ _hostSecretsLock . Release ( ) ;
124
+ }
112
125
}
113
126
114
127
return _hostSecrets ;
0 commit comments