Skip to content

Commit cb10531

Browse files
committed
CSS fixes for credentials dialog
1 parent ce66c6d commit cb10531

File tree

3 files changed

+126
-83
lines changed

3 files changed

+126
-83
lines changed

app/credentials-management/src/main/java/org/phoebus/applications/credentialsmanagement/CredentialsManagementController.java

Lines changed: 121 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@
2020

2121
import javafx.application.Platform;
2222
import javafx.beans.binding.Bindings;
23+
import javafx.beans.property.BooleanProperty;
24+
import javafx.beans.property.IntegerProperty;
2325
import javafx.beans.property.SimpleBooleanProperty;
26+
import javafx.beans.property.SimpleIntegerProperty;
27+
import javafx.beans.property.SimpleObjectProperty;
2428
import javafx.beans.property.SimpleStringProperty;
2529
import javafx.beans.property.StringProperty;
2630
import javafx.collections.FXCollections;
@@ -34,9 +38,12 @@
3438
import javafx.scene.control.TableColumn;
3539
import javafx.scene.control.TableView;
3640
import javafx.scene.control.TextField;
41+
import javafx.scene.control.ToolBar;
42+
import javafx.scene.control.cell.TextFieldTableCell;
3743
import javafx.scene.input.KeyCode;
3844
import javafx.stage.Stage;
3945
import javafx.util.Callback;
46+
import javafx.util.StringConverter;
4047
import org.phoebus.framework.jobs.JobManager;
4148
import org.phoebus.security.authorization.ServiceAuthenticationProvider;
4249
import 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());

app/credentials-management/src/main/resources/css/credentials-management-style.css

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,18 @@
1010
-fx-alignment:CENTER;
1111
}
1212

13+
1314
.table-view {
1415
-fx-table-cell-border-color: transparent;
1516
}
1617

1718
.table-view .table-cell{
18-
-fx-font-size: 12px;
1919
-fx-background-color: #FFFFFF;
20+
-fx-text-fill: #000000;
21+
-fx-border-width: 0px;
2022
}
2123

24+
2225
.table-view .column-header > .label{
2326
-fx-alignment: CENTER-LEFT;
2427
-fx-font-size: 14px;

app/credentials-management/src/main/resources/org/phoebus/applications/credentialsmanagement/CredentialsManagement.fxml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
<TextField fx:id="loginToAllUsernameTextField" promptText="%Username" />
3030
<PasswordField fx:id="loginToAllPasswordTextField" promptText="%Password" />
3131
<Button fx:id="loginToAllButton" mnemonicParsing="false" onAction="#loginToAll" prefWidth="120.0" text="%LoginToAll" />
32+
<Label HBox.hgrow="ALWAYS" maxWidth="1.7976931348623157E308" />
3233
<Button fx:id="logoutFromAllButton" mnemonicParsing="false" onAction="#logoutFromAll" prefWidth="120.0" text="%LogoutFromAll" />
3334
</items>
3435
</ToolBar>

0 commit comments

Comments
 (0)