2020
2121import javafx .application .Platform ;
2222import javafx .beans .binding .Bindings ;
23+ import javafx .beans .property .BooleanProperty ;
24+ import javafx .beans .property .IntegerProperty ;
2325import javafx .beans .property .SimpleBooleanProperty ;
26+ import javafx .beans .property .SimpleIntegerProperty ;
27+ import javafx .beans .property .SimpleObjectProperty ;
2428import javafx .beans .property .SimpleStringProperty ;
2529import javafx .beans .property .StringProperty ;
2630import javafx .collections .FXCollections ;
3438import javafx .scene .control .TableColumn ;
3539import javafx .scene .control .TableView ;
3640import javafx .scene .control .TextField ;
41+ import javafx .scene .control .ToolBar ;
42+ import javafx .scene .control .cell .TextFieldTableCell ;
3743import javafx .scene .input .KeyCode ;
3844import javafx .stage .Stage ;
3945import javafx .util .Callback ;
46+ import javafx .util .StringConverter ;
4047import org .phoebus .framework .jobs .JobManager ;
4148import org .phoebus .security .authorization .ServiceAuthenticationProvider ;
4249import org .phoebus .security .store .SecureStore ;
@@ -68,10 +75,10 @@ public class CredentialsManagementController {
6875 private TableColumn <ServiceItem , ServiceItem > actionButtonColumn ;
6976 @ SuppressWarnings ("unused" )
7077 @ FXML
71- private TableColumn <ServiceItem , String > usernameColumn ;
78+ private TableColumn <ServiceItem , StringProperty > usernameColumn ;
7279 @ SuppressWarnings ("unused" )
7380 @ FXML
74- private TableColumn <ServiceItem , String > passwordColumn ;
81+ private TableColumn <ServiceItem , StringProperty > passwordColumn ;
7582 @ SuppressWarnings ("unused" )
7683 @ FXML
7784 private Button loginToAllButton ;
@@ -96,12 +103,19 @@ public class CredentialsManagementController {
96103 private final List <ServiceAuthenticationProvider > authenticationProviders ;
97104 private final StringProperty loginToAllUsernameProperty = new SimpleStringProperty ();
98105 private final StringProperty loginToAllPasswordProperty = new SimpleStringProperty ();
106+ private final IntegerProperty providerCount = new SimpleIntegerProperty (0 );
107+
108+ /**
109+ * <code>true</code> if user is logged in to at least one service (scope).
110+ */
111+ private final BooleanProperty loggedInProperty = new SimpleBooleanProperty ();
99112
100113 private Stage stage ;
101114
102115 public CredentialsManagementController (List <ServiceAuthenticationProvider > authenticationProviders , SecureStore secureStore ) {
103116 this .authenticationProviders = authenticationProviders ;
104117 this .secureStore = secureStore ;
118+ providerCount .set (this .authenticationProviders .size ());
105119 }
106120
107121 @ SuppressWarnings ("unused" )
@@ -111,35 +125,67 @@ public void initialize() {
111125 tableView .getStylesheets ().add (getClass ().getResource ("/css/credentials-management-style.css" ).toExternalForm ());
112126
113127 logoutFromAllButton .disableProperty ().bind (listEmpty );
128+
129+ usernameColumn .setCellFactory (c -> new UsernameTableCell ());
130+ passwordColumn .setCellFactory (c -> new PasswordTableCell ());
131+
132+ loginToAllUsernameTextField .visibleProperty ().bind (Bindings .createBooleanBinding (() -> providerCount .get () > 1 , providerCount ));
133+ loginToAllPasswordTextField .visibleProperty ().bind (Bindings .createBooleanBinding (() -> providerCount .get () > 1 , providerCount ));
134+ loginToAllUsernameTextField .textProperty ().bindBidirectional (loginToAllUsernameProperty );
135+ loginToAllPasswordTextField .textProperty ().bindBidirectional (loginToAllPasswordProperty );
136+
137+ loginToAllButton .visibleProperty ().bind (Bindings .createBooleanBinding (() -> providerCount .get () > 1 , providerCount ));
138+ // Login to all button enabled only if non-empty username and password is present
139+ loginToAllButton .disableProperty ().bind (Bindings .createBooleanBinding (() -> loginToAllUsernameProperty .get () == null ||
140+ loginToAllUsernameProperty .get ().isEmpty () ||
141+ loginToAllPasswordProperty .get () == null ||
142+ loginToAllPasswordProperty .get ().isEmpty (),
143+ loginToAllUsernameProperty , loginToAllPasswordProperty ));
144+
145+ logoutFromAllButton .disableProperty ().bind (loggedInProperty .not ());
146+
147+ actionButtonColumn .setCellValueFactory (param -> new SimpleObjectProperty <>(param .getValue ()));
148+
149+ configureCellFactory ();
150+
151+ updateTable ();
152+
153+ // Don't want focus on the username field for "login to all" as that obscures the prompt.
154+ // Let table request focus.
155+ Platform .runLater (() -> tableView .requestFocus ());
156+
157+ }
158+
159+ private void configureCellFactory (){
114160 Callback <TableColumn <ServiceItem , ServiceItem >, TableCell <ServiceItem , ServiceItem >> actionColumnCellFactory = new Callback <>() {
115161 @ Override
116162 public TableCell <ServiceItem , ServiceItem > call (final TableColumn <ServiceItem , ServiceItem > param ) {
117163 final TableCell <ServiceItem , ServiceItem > cell = new TableCell <>() {
118164
119- private final Button btn = new Button (Messages .LogoutButtonText );
120-
121- {
165+ private final Button btn = new Button (Messages .LogoutButtonText );{
122166 btn .getStyleClass ().add ("button-style" );
123167 btn .setOnAction ((ActionEvent event ) -> {
124- ServiceItem serviceItem = getTableView ().getItems (). get ( getIndex () );
125- if (serviceItem . loginAction ) {
126- login (serviceItem );
168+ ServiceItem serviceItem = getTableRow ().getItem ( );
169+ if (serviceItem != null && serviceItem . userLoggedIn . get () ) {
170+ logOut (serviceItem );
127171 } else {
128- logOut (serviceItem . getAuthenticationScope () );
172+ login (serviceItem );
129173 }
130174 });
131175 }
132176
133177 @ Override
134- public void updateItem (ServiceItem o , boolean empty ) {
135- super .updateItem (o , empty );
178+ public void updateItem (ServiceItem serviceItem , boolean empty ) {
179+ super .updateItem (serviceItem , empty );
136180 if (empty ) {
137181 setGraphic (null );
138- } else {
139- if (getTableRow () != null && getTableRow ().getItem () != null ) {
140- btn .setText (getTableRow ().getItem ().loginAction ?
141- Messages .LoginButtonText : Messages .LogoutButtonText );
142- }
182+ }
183+ else {
184+ btn .textProperty ().bind (serviceItem .buttonTextProperty );
185+ btn .disableProperty ().bind (Bindings .createBooleanBinding (() ->
186+ serviceItem .username .isNull ().get () || serviceItem .username .get ().isEmpty () ||
187+ serviceItem .password .isNull ().get () || serviceItem .password .get ().isEmpty (),
188+ serviceItem .username , serviceItem .password ));
143189 setGraphic (btn );
144190 }
145191 }
@@ -148,23 +194,6 @@ public void updateItem(ServiceItem o, boolean empty) {
148194 }
149195 };
150196 actionButtonColumn .setCellFactory (actionColumnCellFactory );
151- usernameColumn .setCellFactory (c -> new UsernameTableCell ());
152- passwordColumn .setCellFactory (c -> new PasswordTableCell ());
153-
154- loginToAllUsernameTextField .textProperty ().bindBidirectional (loginToAllUsernameProperty );
155- loginToAllPasswordTextField .textProperty ().bindBidirectional (loginToAllPasswordProperty );
156-
157- loginToAllButton .disableProperty ().bind (Bindings .createBooleanBinding (() -> loginToAllUsernameProperty .get () == null ||
158- loginToAllUsernameProperty .get ().isEmpty () ||
159- loginToAllPasswordProperty .get () == null ||
160- loginToAllPasswordProperty .get ().isEmpty (),
161- loginToAllUsernameProperty , loginToAllPasswordProperty ));
162-
163- updateTable ();
164-
165- // Don't want focus on the username field for "login to all" as that obscures the prompt.
166- // Let table request focus.
167- Platform .runLater (() -> tableView .requestFocus ());
168197 }
169198
170199 @ SuppressWarnings ("unused" )
@@ -173,6 +202,7 @@ public void logoutFromAll() {
173202 try {
174203 secureStore .deleteAllScopedAuthenticationTokens ();
175204 updateTable ();
205+ loggedInProperty .set (false );
176206 } catch (Exception e ) {
177207 LOGGER .log (Level .WARNING , "Failed to delete all authentication tokens from key store" , e );
178208 ExceptionDetailsErrorDialog .openError (parent , Messages .ErrorDialogTitle , Messages .ErrorDialogBody , e );
@@ -199,12 +229,14 @@ public void loginToAll() {
199229 */
200230 private void login (ServiceItem serviceItem ) {
201231 try {
202- serviceItem .getServiceAuthenticationProvider ().authenticate (serviceItem .getUsername (), serviceItem .getPassword ());
232+ serviceItem .getServiceAuthenticationProvider ().authenticate (serviceItem .getUsername (). get () , serviceItem .getPassword (). get ());
203233 try {
204234 secureStore .setScopedAuthentication (new ScopedAuthenticationToken (serviceItem .getAuthenticationScope (),
205- serviceItem .getUsername (),
206- serviceItem .getPassword ()));
207- stage .close ();
235+ serviceItem .getUsername ().get (),
236+ serviceItem .getPassword ().get ()));
237+ loggedInProperty .set (true );
238+ serviceItem .userLoggedIn .set (true );
239+ //stage.close();
208240 } catch (Exception exception ) {
209241 LOGGER .log (Level .WARNING , "Failed to store credentials" , exception );
210242 }
@@ -214,12 +246,14 @@ private void login(ServiceItem serviceItem) {
214246 }
215247 }
216248
217- private void logOut (AuthenticationScope scope ) {
249+ private void logOut (ServiceItem serviceItem ) {
218250 try {
219- secureStore .deleteScopedAuthenticationToken (scope );
220- updateTable ();
251+ secureStore .deleteScopedAuthenticationToken (serviceItem .getAuthenticationScope ());
252+ serviceItem .setLoggedOut ();
253+ Platform .runLater (() -> tableView .requestFocus ());
254+ //updateTable();
221255 } catch (Exception e ) {
222- LOGGER .log (Level .WARNING , "Failed to logout from scope " + scope , e );
256+ LOGGER .log (Level .WARNING , "Failed to logout from service " + serviceItem . getDisplayName () , e );
223257 ExceptionDetailsErrorDialog .openError (parent , Messages .ErrorDialogTitle , Messages .ErrorDialogBody , e );
224258 }
225259 }
@@ -248,36 +282,42 @@ private void updateTable() {
248282 this .serviceItems .setAll (serviceItems );
249283 listEmpty .set (savedTokens .isEmpty ());
250284 tableView .setItems (this .serviceItems );
285+
251286 });
252287 });
253288 }
254289
255290 /**
256291 * Model class for the table view
257292 */
258- public static class ServiceItem {
293+ public class ServiceItem {
259294 private final ServiceAuthenticationProvider serviceAuthenticationProvider ;
260- private String username ;
261- private String password ;
262- private boolean loginAction ;
295+ private StringProperty username = new SimpleStringProperty ();
296+ private StringProperty password = new SimpleStringProperty ();
297+ private final BooleanProperty userLoggedIn = new SimpleBooleanProperty ();
298+ private final StringProperty buttonTextProperty = new SimpleStringProperty ();
263299
264300 public ServiceItem (ServiceAuthenticationProvider serviceAuthenticationProvider , String username , String password ) {
265301 this .serviceAuthenticationProvider = serviceAuthenticationProvider ;
266- this .username = username ;
267- this .password = password ;
302+ this .username .set (username );
303+ this .password .set (password );
304+ buttonTextProperty .set (Messages .LogoutButtonText );
305+ userLoggedIn .addListener ((obs , o , n ) -> buttonTextProperty .set (n ? Messages .LogoutButtonText : Messages .LoginButtonText ));
268306 }
269307
270308 public ServiceItem (ServiceAuthenticationProvider serviceAuthenticationProvider ) {
271309 this .serviceAuthenticationProvider = serviceAuthenticationProvider ;
272- this .loginAction = true ;
310+ this .userLoggedIn .set (false );
311+ buttonTextProperty .set (Messages .LoginButtonText );
312+ userLoggedIn .addListener ((obs , o , n ) -> buttonTextProperty .set (n ? Messages .LogoutButtonText : Messages .LoginButtonText ));
273313 }
274314
275- public String getUsername () {
315+ public StringProperty getUsername () {
276316 return username ;
277317 }
278318
279319 public void setUsername (String username ) {
280- this .username = username ;
320+ this .username . set ( username ) ;
281321 }
282322
283323 public AuthenticationScope getAuthenticationScope () {
@@ -300,72 +340,71 @@ public String getDisplayName() {
300340 serviceAuthenticationProvider .getAuthenticationScope ().getDisplayName () : "" ;
301341 }
302342
303- public String getPassword () {
343+ public StringProperty getPassword () {
304344 return password ;
305345 }
306346
307347 public void setPassword (String password ) {
308- this .password = password ;
348+ this .password . set ( password ) ;
309349
310350 }
311351
312352 public ServiceAuthenticationProvider getServiceAuthenticationProvider () {
313353 return serviceAuthenticationProvider ;
314354 }
315355
316- public boolean getLoginAction () {
317- return loginAction ;
356+ public BooleanProperty getUserLoggedIn () {
357+ return userLoggedIn ;
318358 }
319- }
320359
321- private static class UsernameTableCell extends TableCell <ServiceItem , String > {
322- private final TextField textField = new TextField ();
360+ public String getButtonTextProperty () {
361+ return buttonTextProperty .get ();
362+ }
323363
324- public UsernameTableCell () {
325- textField .getStyleClass ().add ("text-field-styling" );
326- // Update model on key up
327- textField .setOnKeyReleased (ke -> {
328- getTableRow ().getItem ().setUsername (textField .getText ());
329- });
364+ public StringProperty buttonTextPropertyProperty () {
365+ return buttonTextProperty ;
330366 }
331367
368+ public void setLoggedOut (){
369+ userLoggedIn .set (false );
370+ username .set (null );
371+ password .set (null );
372+ }
373+ }
374+
375+ private class UsernameTableCell extends TableCell <ServiceItem , StringProperty > {
376+
332377 @ Override
333- protected void updateItem (String item , final boolean empty ) {
378+ public void updateItem (StringProperty item , final boolean empty ) {
334379 super .updateItem (item , empty );
335380 if (empty ) {
336381 setGraphic (null );
337382 } else {
338- textField .setText (item );
339- if (getTableRow () != null && getTableRow ().getItem () != null ) {
340- // Disable field if user is logged in.
341- textField .disableProperty ().set (!getTableRow ().getItem ().loginAction );
342- }
383+ TextField textField = new TextField ();
384+ textField .getStyleClass ().add ("text-field-styling" );
385+ textField .textProperty ().bindBidirectional (getTableRow ().getItem ().username );
386+ textField .disableProperty ().bind (getTableRow ().getItem ().userLoggedIn );
343387 setGraphic (textField );
344388 }
345389 }
346390 }
347391
348- private class PasswordTableCell extends TableCell <ServiceItem , String > {
349- private final PasswordField passwordField = new PasswordField ();
350392
351- public PasswordTableCell () {
352- passwordField .getStyleClass ().add ("text-field-styling" );
353- // Update model on key up
354- passwordField .setOnKeyReleased (ke -> getTableRow ().getItem ().setPassword (passwordField .getText ()));
355- }
393+
394+ private class PasswordTableCell extends TableCell <ServiceItem , StringProperty > {
356395
357396 @ Override
358- protected void updateItem (String item , final boolean empty ) {
397+ protected void updateItem (StringProperty item , final boolean empty ) {
359398 super .updateItem (item , empty );
360399 if (empty ) {
361400 setGraphic (null );
362401 } else {
363- passwordField .setText (item == null ? item : "dummypass" ); // Hack to not reveal password length
402+ PasswordField passwordField = new PasswordField ();
403+ passwordField .getStyleClass ().add ("text-field-styling" );
364404
365- if (getTableRow () != null && getTableRow ().getItem () != null ) {
366- // Disable field if user is logged in.
367- passwordField .disableProperty ().set (!getTableRow ().getItem ().loginAction );
368- }
405+ passwordField .setText (item .get () == null ? null : "dummypass" ); // Hack to not reveal password length
406+ passwordField .textProperty ().bindBidirectional (getTableRow ().getItem ().password );
407+ passwordField .disableProperty ().bind (getTableRow ().getItem ().userLoggedIn );
369408 passwordField .setOnKeyPressed (keyEvent -> {
370409 if (keyEvent .getCode () == KeyCode .ENTER ) {
371410 CredentialsManagementController .this .login (getTableRow ().getItem ());
0 commit comments