77using System . IO ;
88using System . Linq ;
99using System . Security . Cryptography ;
10+ using System . Threading ;
1011using System . Threading . Tasks ;
1112using Microsoft . Azure . WebJobs . Script . Config ;
1213using Microsoft . Azure . WebJobs . Script . WebHost . Properties ;
@@ -21,6 +22,7 @@ public class SecretManager : IDisposable, ISecretManager
2122 private readonly ILogger _logger ;
2223 private readonly ISecretsRepository _repository ;
2324 private HostSecretsInfo _hostSecrets ;
25+ private SemaphoreSlim _semaphoreSlim = new SemaphoreSlim ( 1 , 1 ) ;
2426
2527 // for testing
2628 public SecretManager ( )
@@ -65,43 +67,53 @@ public async virtual Task<HostSecretsInfo> GetHostSecretsAsync()
6567 {
6668 if ( _hostSecrets == null )
6769 {
68- HostSecrets hostSecrets = await LoadSecretsAsync < HostSecrets > ( ) ;
69-
70- if ( hostSecrets == null )
71- {
72- // host secrets do not yet exist so generate them
73- _logger . LogDebug ( Resources . TraceHostSecretGeneration ) ;
74- hostSecrets = GenerateHostSecrets ( ) ;
75- await PersistSecretsAsync ( hostSecrets ) ;
76- }
77-
70+ HostSecrets hostSecrets ;
71+ // Allow only one thread to modify the secrets
72+ await _semaphoreSlim . WaitAsync ( ) ;
7873 try
7974 {
80- // Host secrets will be in the original persisted state at this point (e.g. encrypted),
81- // so we read the secrets running them through the appropriate readers
82- hostSecrets = ReadHostSecrets ( hostSecrets ) ;
83- }
84- catch ( CryptographicException )
85- {
86- _logger ? . LogDebug ( Resources . TraceNonDecryptedHostSecretRefresh ) ;
87- await PersistSecretsAsync ( hostSecrets , null , true ) ;
88- await RefreshSecretsAsync ( hostSecrets ) ;
89- }
75+ hostSecrets = await LoadSecretsAsync < HostSecrets > ( ) ;
9076
91- // If the persistence state of any of our secrets is stale (e.g. the encryption key has been rotated), update
92- // the state and persist the secrets
93- if ( hostSecrets . HasStaleKeys )
94- {
95- _logger . LogDebug ( Resources . TraceStaleHostSecretRefresh ) ;
96- await RefreshSecretsAsync ( hostSecrets ) ;
97- }
77+ if ( hostSecrets == null )
78+ {
79+ // host secrets do not yet exist so generate them
80+ _logger . LogDebug ( Resources . TraceHostSecretGeneration ) ;
81+ hostSecrets = GenerateHostSecrets ( ) ;
82+ await PersistSecretsAsync ( hostSecrets ) ;
83+ }
84+
85+ try
86+ {
87+ // Host secrets will be in the original persisted state at this point (e.g. encrypted),
88+ // so we read the secrets running them through the appropriate readers
89+ hostSecrets = ReadHostSecrets ( hostSecrets ) ;
90+ }
91+ catch ( CryptographicException )
92+ {
93+ _logger ? . LogDebug ( Resources . TraceNonDecryptedHostSecretRefresh ) ;
94+ await PersistSecretsAsync ( hostSecrets , null , true ) ;
95+ await RefreshSecretsAsync ( hostSecrets ) ;
96+ }
97+
98+ // If the persistence state of any of our secrets is stale (e.g. the encryption key has been rotated), update
99+ // the state and persist the secrets
100+ if ( hostSecrets . HasStaleKeys )
101+ {
102+ _logger . LogDebug ( Resources . TraceStaleHostSecretRefresh ) ;
103+ await RefreshSecretsAsync ( hostSecrets ) ;
104+ }
98105
99- _hostSecrets = new HostSecretsInfo
106+ _hostSecrets = new HostSecretsInfo
107+ {
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+ } ;
112+ }
113+ finally
100114 {
101- MasterKey = hostSecrets . MasterKey . Value ,
102- FunctionKeys = hostSecrets . FunctionKeys . ToDictionary ( s => s . Name , s => s . Value ) ,
103- SystemKeys = hostSecrets . SystemKeys . ToDictionary ( s => s . Name , s => s . Value )
104- } ;
115+ _semaphoreSlim . Release ( ) ;
116+ }
105117 }
106118
107119 return _hostSecrets ;
0 commit comments