55 */
66package org .elasticsearch .xpack .watcher .notification ;
77
8+ import org .elasticsearch .common .settings .SecureSetting ;
9+ import org .elasticsearch .common .settings .SecureSettings ;
10+ import org .elasticsearch .common .settings .SecureString ;
11+ import org .elasticsearch .common .settings .Setting ;
812import org .elasticsearch .common .settings .Settings ;
913import org .elasticsearch .common .settings .SettingsException ;
1014import org .elasticsearch .test .ESTestCase ;
1115import org .elasticsearch .xpack .watcher .notification .NotificationService ;
1216
17+ import java .io .IOException ;
18+ import java .io .InputStream ;
19+ import java .security .GeneralSecurityException ;
20+ import java .util .Arrays ;
1321import java .util .Collections ;
22+ import java .util .HashMap ;
23+ import java .util .List ;
24+ import java .util .Map ;
25+ import java .util .Set ;
26+ import java .util .concurrent .atomic .AtomicInteger ;
27+ import java .util .concurrent .atomic .AtomicReference ;
28+ import java .util .function .BiConsumer ;
1429
1530import static org .hamcrest .Matchers .anyOf ;
1631import static org .hamcrest .Matchers .is ;
@@ -25,6 +40,7 @@ public void testSingleAccount() {
2540 assertThat (service .getAccount (accountName ), is (accountName ));
2641 // single account, this will also be the default
2742 assertThat (service .getAccount ("non-existing" ), is (accountName ));
43+ assertThat (service .getAccount (null ), is (accountName ));
2844 }
2945
3046 public void testMultipleAccountsWithExistingDefault () {
@@ -80,16 +96,160 @@ public void testAccountDoesNotExist() throws Exception{
8096 is ("no accounts of type [test] configured. Please set up an account using the [xpack.notification.test] settings" ));
8197 }
8298
99+ public void testAccountWithSecureSettings () throws Exception {
100+ final Setting <SecureString > secureSetting1 = SecureSetting .secureString ("xpack.notification.test.account.secure_only" , null );
101+ final Setting <SecureString > secureSetting2 = SecureSetting .secureString ("xpack.notification.test.account.mixed.secure" , null );
102+ final Map <String , char []> secureSettingsMap = new HashMap <>();
103+ secureSettingsMap .put (secureSetting1 .getKey (), "secure_only" .toCharArray ());
104+ secureSettingsMap .put (secureSetting2 .getKey (), "mixed_secure" .toCharArray ());
105+ Settings settings = Settings .builder ()
106+ .put ("xpack.notification.test.account.unsecure_only" , "bar" )
107+ .put ("xpack.notification.test.account.mixed.unsecure" , "mixed_unsecure" )
108+ .setSecureSettings (secureSettingsFromMap (secureSettingsMap ))
109+ .build ();
110+ TestNotificationService service = new TestNotificationService (settings , Arrays .asList (secureSetting1 , secureSetting2 ));
111+ assertThat (service .getAccount ("secure_only" ), is ("secure_only" ));
112+ assertThat (service .getAccount ("unsecure_only" ), is ("unsecure_only" ));
113+ assertThat (service .getAccount ("mixed" ), is ("mixed" ));
114+ assertThat (service .getAccount (null ), anyOf (is ("secure_only" ), is ("unsecure_only" ), is ("mixed" )));
115+ }
116+
117+ public void testAccountCreationCached () {
118+ String accountName = randomAlphaOfLength (10 );
119+ Settings settings = Settings .builder ().put ("xpack.notification.test.account." + accountName , "bar" ).build ();
120+ final AtomicInteger validationInvocationCount = new AtomicInteger (0 );
121+
122+ TestNotificationService service = new TestNotificationService (settings , (String name , Settings accountSettings ) -> {
123+ validationInvocationCount .incrementAndGet ();
124+ });
125+ assertThat (validationInvocationCount .get (), is (0 ));
126+ assertThat (service .getAccount (accountName ), is (accountName ));
127+ assertThat (validationInvocationCount .get (), is (1 ));
128+ if (randomBoolean ()) {
129+ assertThat (service .getAccount (accountName ), is (accountName ));
130+ } else {
131+ assertThat (service .getAccount (null ), is (accountName ));
132+ }
133+ // counter is still 1 because the account is cached
134+ assertThat (validationInvocationCount .get (), is (1 ));
135+ }
136+
137+ public void testAccountUpdateSettings () throws Exception {
138+ final Setting <SecureString > secureSetting = SecureSetting .secureString ("xpack.notification.test.account.x.secure" , null );
139+ final Setting <String > setting = Setting .simpleString ("xpack.notification.test.account.x.dynamic" , Setting .Property .Dynamic ,
140+ Setting .Property .NodeScope );
141+ final AtomicReference <String > secureSettingValue = new AtomicReference <String >(randomAlphaOfLength (4 ));
142+ final AtomicReference <String > settingValue = new AtomicReference <String >(randomAlphaOfLength (4 ));
143+ final Map <String , char []> secureSettingsMap = new HashMap <>();
144+ final AtomicInteger validationInvocationCount = new AtomicInteger (0 );
145+ secureSettingsMap .put (secureSetting .getKey (), secureSettingValue .get ().toCharArray ());
146+ final Settings .Builder settingsBuilder = Settings .builder ()
147+ .put (setting .getKey (), settingValue .get ())
148+ .setSecureSettings (secureSettingsFromMap (secureSettingsMap ));
149+ final TestNotificationService service = new TestNotificationService (settingsBuilder .build (), Arrays .asList (secureSetting ),
150+ (String name , Settings accountSettings ) -> {
151+ assertThat (accountSettings .get ("dynamic" ), is (settingValue .get ()));
152+ assertThat (SecureSetting .secureString ("secure" , null ).get (accountSettings ), is (secureSettingValue .get ()));
153+ validationInvocationCount .incrementAndGet ();
154+ });
155+ assertThat (validationInvocationCount .get (), is (0 ));
156+ service .getAccount (null );
157+ assertThat (validationInvocationCount .get (), is (1 ));
158+ // update secure setting only
159+ updateSecureSetting (secureSettingValue , secureSetting , secureSettingsMap , settingsBuilder , service );
160+ assertThat (validationInvocationCount .get (), is (1 ));
161+ service .getAccount (null );
162+ assertThat (validationInvocationCount .get (), is (2 ));
163+ updateDynamicClusterSetting (settingValue , setting , settingsBuilder , service );
164+ assertThat (validationInvocationCount .get (), is (2 ));
165+ service .getAccount (null );
166+ assertThat (validationInvocationCount .get (), is (3 ));
167+ // update both
168+ if (randomBoolean ()) {
169+ // update secure first
170+ updateSecureSetting (secureSettingValue , secureSetting , secureSettingsMap , settingsBuilder , service );
171+ // update cluster second
172+ updateDynamicClusterSetting (settingValue , setting , settingsBuilder , service );
173+ } else {
174+ // update cluster first
175+ updateDynamicClusterSetting (settingValue , setting , settingsBuilder , service );
176+ // update secure second
177+ updateSecureSetting (secureSettingValue , secureSetting , secureSettingsMap , settingsBuilder , service );
178+ }
179+ assertThat (validationInvocationCount .get (), is (3 ));
180+ service .getAccount (null );
181+ assertThat (validationInvocationCount .get (), is (4 ));
182+ }
183+
184+ private static void updateDynamicClusterSetting (AtomicReference <String > settingValue , Setting <String > setting ,
185+ Settings .Builder settingsBuilder , TestNotificationService service ) {
186+ settingValue .set (randomAlphaOfLength (4 ));
187+ settingsBuilder .put (setting .getKey (), settingValue .get ());
188+ service .clusterSettingsConsumer (settingsBuilder .build ());
189+ }
190+
191+ private static void updateSecureSetting (AtomicReference <String > secureSettingValue , Setting <SecureString > secureSetting ,
192+ Map <String , char []> secureSettingsMap , Settings .Builder settingsBuilder , TestNotificationService service ) {
193+ secureSettingValue .set (randomAlphaOfLength (4 ));
194+ secureSettingsMap .put (secureSetting .getKey (), secureSettingValue .get ().toCharArray ());
195+ service .reload (settingsBuilder .build ());
196+ }
197+
83198 private static class TestNotificationService extends NotificationService <String > {
84199
85- TestNotificationService (Settings settings ) {
86- super ("test" , settings , Collections .emptyList ());
200+ private final BiConsumer <String , Settings > validator ;
201+
202+ TestNotificationService (Settings settings , List <Setting <?>> secureSettings , BiConsumer <String , Settings > validator ) {
203+ super ("test" , settings , secureSettings );
204+ this .validator = validator ;
87205 reload (settings );
88206 }
89207
208+ TestNotificationService (Settings settings , List <Setting <?>> secureSettings ) {
209+ this (settings , secureSettings , (x , y ) -> {});
210+ }
211+
212+ TestNotificationService (Settings settings ) {
213+ this (settings , Collections .emptyList (), (x , y ) -> {});
214+ }
215+
216+ TestNotificationService (Settings settings , BiConsumer <String , Settings > validator ) {
217+ this (settings , Collections .emptyList (), validator );
218+ }
219+
90220 @ Override
91221 protected String createAccount (String name , Settings accountSettings ) {
222+ validator .accept (name , accountSettings );
92223 return name ;
93224 }
94225 }
226+
227+ private static SecureSettings secureSettingsFromMap (Map <String , char []> secureSettingsMap ) {
228+ return new SecureSettings () {
229+
230+ @ Override
231+ public boolean isLoaded () {
232+ return true ;
233+ }
234+
235+ @ Override
236+ public SecureString getString (String setting ) throws GeneralSecurityException {
237+ return new SecureString (secureSettingsMap .get (setting ));
238+ }
239+
240+ @ Override
241+ public Set <String > getSettingNames () {
242+ return secureSettingsMap .keySet ();
243+ }
244+
245+ @ Override
246+ public InputStream getFile (String setting ) throws GeneralSecurityException {
247+ return null ;
248+ }
249+
250+ @ Override
251+ public void close () throws IOException {
252+ }
253+ };
254+ }
95255}
0 commit comments