88using System . IO ;
99using System . Linq ;
1010using System . Security . Cryptography ;
11+ using System . Threading ;
1112using System . Threading . Tasks ;
1213using Microsoft . Azure . WebJobs . Host ;
1314using Microsoft . Azure . WebJobs . Script . Config ;
@@ -24,6 +25,7 @@ public class SecretManager : IDisposable, ISecretManager
2425 private readonly ILogger _logger ;
2526 private readonly ISecretsRepository _repository ;
2627 private HostSecretsInfo _hostSecrets ;
28+ private SemaphoreSlim _hostSecretsLock = new SemaphoreSlim ( 1 , 1 ) ;
2729
2830 // for testing
2931 public SecretManager ( )
@@ -62,53 +64,64 @@ protected virtual void Dispose(bool disposing)
6264 if ( disposing )
6365 {
6466 ( _repository as IDisposable ) ? . Dispose ( ) ;
67+ _hostSecretsLock . Dispose ( ) ;
6568 }
6669 }
6770
6871 public async virtual Task < HostSecretsInfo > GetHostSecretsAsync ( )
6972 {
7073 if ( _hostSecrets == null )
7174 {
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 ( ) ;
8378 try
8479 {
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 > ( ) ;
9681
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+ }
105113
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
107122 {
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+ }
112125 }
113126
114127 return _hostSecrets ;
0 commit comments