Skip to content

Commit ce66c6d

Browse files
committed
Update credentials dialog UI
1 parent 7be1d16 commit ce66c6d

File tree

4 files changed

+104
-56
lines changed

4 files changed

+104
-56
lines changed

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

Lines changed: 82 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919
package org.phoebus.applications.credentialsmanagement;
2020

2121
import javafx.application.Platform;
22+
import javafx.beans.binding.Bindings;
2223
import javafx.beans.property.SimpleBooleanProperty;
24+
import javafx.beans.property.SimpleStringProperty;
25+
import javafx.beans.property.StringProperty;
2326
import javafx.collections.FXCollections;
2427
import javafx.collections.ObservableList;
2528
import javafx.event.ActionEvent;
@@ -62,7 +65,7 @@ public class CredentialsManagementController {
6265
private TableView<ServiceItem> tableView;
6366
@SuppressWarnings("unused")
6467
@FXML
65-
private TableColumn<ServiceItem, Void> actionButtonColumn;
68+
private TableColumn<ServiceItem, ServiceItem> actionButtonColumn;
6669
@SuppressWarnings("unused")
6770
@FXML
6871
private TableColumn<ServiceItem, String> usernameColumn;
@@ -71,7 +74,16 @@ public class CredentialsManagementController {
7174
private TableColumn<ServiceItem, String> passwordColumn;
7275
@SuppressWarnings("unused")
7376
@FXML
74-
private Button clearAllCredentialsButton;
77+
private Button loginToAllButton;
78+
@SuppressWarnings("unused")
79+
@FXML
80+
private Button logoutFromAllButton;
81+
@SuppressWarnings("unused")
82+
@FXML
83+
private TextField loginToAllUsernameTextField;
84+
@SuppressWarnings("unused")
85+
@FXML
86+
private PasswordField loginToAllPasswordTextField;
7587
@SuppressWarnings("unused")
7688
@FXML
7789
private TableColumn<ServiceItem, String> scopeColumn;
@@ -82,6 +94,8 @@ public class CredentialsManagementController {
8294
private final SecureStore secureStore;
8395
private static final Logger LOGGER = Logger.getLogger(CredentialsManagementController.class.getName());
8496
private final List<ServiceAuthenticationProvider> authenticationProviders;
97+
private final StringProperty loginToAllUsernameProperty = new SimpleStringProperty();
98+
private final StringProperty loginToAllPasswordProperty = new SimpleStringProperty();
8599

86100
private Stage stage;
87101

@@ -95,33 +109,34 @@ public CredentialsManagementController(List<ServiceAuthenticationProvider> authe
95109
public void initialize() {
96110

97111
tableView.getStylesheets().add(getClass().getResource("/css/credentials-management-style.css").toExternalForm());
98-
clearAllCredentialsButton.disableProperty().bind(listEmpty);
99-
Callback<TableColumn<ServiceItem, Void>, TableCell<ServiceItem, Void>> actionColumnCellFactory = new Callback<>() {
112+
113+
logoutFromAllButton.disableProperty().bind(listEmpty);
114+
Callback<TableColumn<ServiceItem, ServiceItem>, TableCell<ServiceItem, ServiceItem>> actionColumnCellFactory = new Callback<>() {
100115
@Override
101-
public TableCell<ServiceItem, Void> call(final TableColumn<ServiceItem, Void> param) {
102-
final TableCell<ServiceItem, Void> cell = new TableCell<>() {
116+
public TableCell<ServiceItem, ServiceItem> call(final TableColumn<ServiceItem, ServiceItem> param) {
117+
final TableCell<ServiceItem, ServiceItem> cell = new TableCell<>() {
103118

104119
private final Button btn = new Button(Messages.LogoutButtonText);
120+
105121
{
106122
btn.getStyleClass().add("button-style");
107123
btn.setOnAction((ActionEvent event) -> {
108124
ServiceItem serviceItem = getTableView().getItems().get(getIndex());
109-
if(serviceItem.isLoginAction()){
125+
if (serviceItem.loginAction) {
110126
login(serviceItem);
111-
}
112-
else{
127+
} else {
113128
logOut(serviceItem.getAuthenticationScope());
114129
}
115130
});
116131
}
117132

118133
@Override
119-
public void updateItem(Void o, boolean empty) {
134+
public void updateItem(ServiceItem o, boolean empty) {
120135
super.updateItem(o, empty);
121136
if (empty) {
122137
setGraphic(null);
123138
} else {
124-
if(getTableRow() != null && getTableRow().getItem() != null){
139+
if (getTableRow() != null && getTableRow().getItem() != null) {
125140
btn.setText(getTableRow().getItem().loginAction ?
126141
Messages.LoginButtonText : Messages.LogoutButtonText);
127142
}
@@ -136,14 +151,37 @@ public void updateItem(Void o, boolean empty) {
136151
usernameColumn.setCellFactory(c -> new UsernameTableCell());
137152
passwordColumn.setCellFactory(c -> new PasswordTableCell());
138153

139-
scopeColumn.setStyle( "-fx-alignment: CENTER-LEFT;");
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));
140162

141163
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());
168+
}
169+
170+
@SuppressWarnings("unused")
171+
@FXML
172+
public void logoutFromAll() {
173+
try {
174+
secureStore.deleteAllScopedAuthenticationTokens();
175+
updateTable();
176+
} catch (Exception e) {
177+
LOGGER.log(Level.WARNING, "Failed to delete all authentication tokens from key store", e);
178+
ExceptionDetailsErrorDialog.openError(parent, Messages.ErrorDialogTitle, Messages.ErrorDialogBody, e);
179+
}
142180
}
143181

144182
@SuppressWarnings("unused")
145183
@FXML
146-
public void logOutFromAll() {
184+
public void loginToAll() {
147185
try {
148186
secureStore.deleteAllScopedAuthenticationTokens();
149187
updateTable();
@@ -156,9 +194,10 @@ public void logOutFromAll() {
156194
/**
157195
* Attempts to sign in user based on provided credentials. If sign-in succeeds, this method will close the
158196
* associated UI.
197+
*
159198
* @param serviceItem The {@link ServiceItem} defining the scope, and implicitly the authentication service.
160199
*/
161-
private void login(ServiceItem serviceItem){
200+
private void login(ServiceItem serviceItem) {
162201
try {
163202
serviceItem.getServiceAuthenticationProvider().authenticate(serviceItem.getUsername(), serviceItem.getPassword());
164203
try {
@@ -191,19 +230,20 @@ private void updateTable() {
191230
// Match saved tokens with an authentication provider, where applicable
192231
List<ServiceItem> serviceItems = savedTokens.stream().map(token -> {
193232
ServiceAuthenticationProvider provider =
194-
authenticationProviders.stream().filter(p-> p.getAuthenticationScope().getScope().equals(token.getAuthenticationScope().getScope())).findFirst().orElse(null);
233+
authenticationProviders.stream().filter(p -> p.getAuthenticationScope().getScope().equals(token.getAuthenticationScope().getScope())).findFirst().orElse(null);
195234
return new ServiceItem(provider, token.getUsername(), token.getPassword());
196235
}).collect(Collectors.toList());
197236
// Also need to add ServiceItems for providers not matched with a saved token, i.e. for logged-out services
198237
authenticationProviders.forEach(p -> {
199238
Optional<ServiceItem> serviceItem =
200239
serviceItems.stream().filter(si ->
201240
p.getAuthenticationScope().getScope().equals(si.getAuthenticationScope().getScope())).findFirst();
202-
if(serviceItem.isEmpty()){
241+
if (serviceItem.isEmpty()) {
203242
serviceItems.add(new ServiceItem(p));
204243
}
205244
});
206245
serviceItems.sort(Comparator.comparing(i -> i.getAuthenticationScope().getDisplayName()));
246+
207247
Platform.runLater(() -> {
208248
this.serviceItems.setAll(serviceItems);
209249
listEmpty.set(savedTokens.isEmpty());
@@ -219,7 +259,7 @@ public static class ServiceItem {
219259
private final ServiceAuthenticationProvider serviceAuthenticationProvider;
220260
private String username;
221261
private String password;
222-
private boolean loginAction = false;
262+
private boolean loginAction;
223263

224264
public ServiceItem(ServiceAuthenticationProvider serviceAuthenticationProvider, String username, String password) {
225265
this.serviceAuthenticationProvider = serviceAuthenticationProvider;
@@ -229,14 +269,14 @@ public ServiceItem(ServiceAuthenticationProvider serviceAuthenticationProvider,
229269

230270
public ServiceItem(ServiceAuthenticationProvider serviceAuthenticationProvider) {
231271
this.serviceAuthenticationProvider = serviceAuthenticationProvider;
232-
loginAction = true;
272+
this.loginAction = true;
233273
}
234274

235275
public String getUsername() {
236276
return username;
237277
}
238278

239-
public void setUsername(String username){
279+
public void setUsername(String username) {
240280
this.username = username;
241281
}
242282

@@ -249,53 +289,54 @@ public AuthenticationScope getAuthenticationScope() {
249289
* @return String representation of the authentication scope.
250290
*/
251291
@SuppressWarnings("unused")
252-
public String getScope(){
292+
public String getScope() {
253293
return serviceAuthenticationProvider != null ?
254294
serviceAuthenticationProvider.getAuthenticationScope().getScope() : "";
255295
}
256296

257297
@SuppressWarnings("unused")
258-
public String getDisplayName(){
298+
public String getDisplayName() {
259299
return serviceAuthenticationProvider != null ?
260300
serviceAuthenticationProvider.getAuthenticationScope().getDisplayName() : "";
261301
}
262302

263-
public String getPassword(){
303+
public String getPassword() {
264304
return password;
265305
}
266306

267-
public void setPassword(String password){
307+
public void setPassword(String password) {
268308
this.password = password;
309+
269310
}
270311

271312
public ServiceAuthenticationProvider getServiceAuthenticationProvider() {
272313
return serviceAuthenticationProvider;
273314
}
274315

275-
public boolean isLoginAction(){
316+
public boolean getLoginAction() {
276317
return loginAction;
277318
}
278319
}
279320

280-
private static class UsernameTableCell extends TableCell<ServiceItem, String>{
321+
private static class UsernameTableCell extends TableCell<ServiceItem, String> {
281322
private final TextField textField = new TextField();
282323

283-
public UsernameTableCell(){
324+
public UsernameTableCell() {
284325
textField.getStyleClass().add("text-field-styling");
285326
// Update model on key up
286-
textField.setOnKeyReleased(ke -> getTableRow().getItem().setUsername(textField.getText()));
327+
textField.setOnKeyReleased(ke -> {
328+
getTableRow().getItem().setUsername(textField.getText());
329+
});
287330
}
288331

289332
@Override
290-
protected void updateItem(String item, final boolean empty)
291-
{
333+
protected void updateItem(String item, final boolean empty) {
292334
super.updateItem(item, empty);
293-
if(empty){
335+
if (empty) {
294336
setGraphic(null);
295-
}
296-
else{
337+
} else {
297338
textField.setText(item);
298-
if(getTableRow() != null && getTableRow().getItem() != null){
339+
if (getTableRow() != null && getTableRow().getItem() != null) {
299340
// Disable field if user is logged in.
300341
textField.disableProperty().set(!getTableRow().getItem().loginAction);
301342
}
@@ -304,25 +345,24 @@ protected void updateItem(String item, final boolean empty)
304345
}
305346
}
306347

307-
private class PasswordTableCell extends TableCell<ServiceItem, String>{
348+
private class PasswordTableCell extends TableCell<ServiceItem, String> {
308349
private final PasswordField passwordField = new PasswordField();
309350

310-
public PasswordTableCell(){
351+
public PasswordTableCell() {
311352
passwordField.getStyleClass().add("text-field-styling");
312353
// Update model on key up
313354
passwordField.setOnKeyReleased(ke -> getTableRow().getItem().setPassword(passwordField.getText()));
314355
}
315356

316357
@Override
317-
protected void updateItem(String item, final boolean empty)
318-
{
358+
protected void updateItem(String item, final boolean empty) {
319359
super.updateItem(item, empty);
320-
if(empty){
360+
if (empty) {
321361
setGraphic(null);
322-
}
323-
else{
362+
} else {
324363
passwordField.setText(item == null ? item : "dummypass"); // Hack to not reveal password length
325-
if(getTableRow() != null && getTableRow().getItem() != null) {
364+
365+
if (getTableRow() != null && getTableRow().getItem() != null) {
326366
// Disable field if user is logged in.
327367
passwordField.disableProperty().set(!getTableRow().getItem().loginAction);
328368
}
@@ -336,7 +376,7 @@ protected void updateItem(String item, final boolean empty)
336376
}
337377
}
338378

339-
public void setStage(Stage stage){
379+
public void setStage(Stage stage) {
340380
this.stage = stage;
341381
}
342382
}
Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,32 @@
11
.text-field-styling{
22
-fx-padding: 5px 3px 3px 3px;
3-
-fx-border-insets: 5px 3px 3px 3px;
3+
-fx-border-insets: 5px 3px 3px 2px;
44
-fx-background-insets: 5px 3px 3px 3px;
55
-fx-border-color: #cdcdcd;
66
-fx-border-radius: 3px;
77
}
88

99
.table-view .table-column{
10-
-fx-alignment:center;
10+
-fx-alignment:CENTER;
11+
}
12+
13+
.table-view {
14+
-fx-table-cell-border-color: transparent;
1115
}
1216

1317
.table-view .table-cell{
1418
-fx-font-size: 12px;
19+
-fx-background-color: #FFFFFF;
1520
}
1621

1722
.table-view .column-header > .label{
1823
-fx-alignment: CENTER-LEFT;
24+
-fx-font-size: 14px;
1925
-fx-padding: 5px 3px 3px 3px;
2026
}
2127

2228
.button-style{
2329
-fx-pref-width: 100px;
2430
}
31+
32+

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

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,30 +22,29 @@
2222
<?import javafx.scene.control.cell.*?>
2323
<?import javafx.scene.layout.*?>
2424

25-
<BorderPane id="parent" fx:id="parent" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.phoebus.applications.credentialsmanagement.CredentialsManagementController">
25+
<BorderPane id="parent" fx:id="parent" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/17.0.12" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.phoebus.applications.credentialsmanagement.CredentialsManagementController">
2626
<top>
2727
<ToolBar prefHeight="40.0" prefWidth="200.0" BorderPane.alignment="CENTER">
2828
<items>
29-
<Button fx:id="clearAllCredentialsButton" mnemonicParsing="false" onAction="#logOutFromAll" text="%LogOutFromAll">
30-
<tooltip>
31-
<Tooltip text="%ClearAllCredentials" />
32-
</tooltip>
33-
</Button>
29+
<TextField fx:id="loginToAllUsernameTextField" promptText="%Username" />
30+
<PasswordField fx:id="loginToAllPasswordTextField" promptText="%Password" />
31+
<Button fx:id="loginToAllButton" mnemonicParsing="false" onAction="#loginToAll" prefWidth="120.0" text="%LoginToAll" />
32+
<Button fx:id="logoutFromAllButton" mnemonicParsing="false" onAction="#logoutFromAll" prefWidth="120.0" text="%LogoutFromAll" />
3433
</items>
3534
</ToolBar>
3635
</top>
3736
<center>
38-
<TableView fx:id="tableView" editable="true">
37+
<TableView fx:id="tableView" editable="true" minHeight="-Infinity" minWidth="-Infinity">
3938
<placeholder>
4039
<Label text="%NoCredentialsFound" />
4140
</placeholder>
4241
<columns>
43-
<TableColumn fx:id="scopeColumn" prefWidth="${parent.width * 0.2}" text="%Scope">
42+
<TableColumn fx:id="scopeColumn" prefWidth="${parent.width * 0.2}" style="-fx-alignment: CENTER-lEFT;" text="%Scope">
4443
<cellValueFactory>
4544
<PropertyValueFactory property="displayName" />
4645
</cellValueFactory>
4746
</TableColumn>
48-
<TableColumn fx:id="usernameColumn" editable="true" prefWidth="${parent.width * 0.3}" text="%UserName">
47+
<TableColumn fx:id="usernameColumn" editable="true" prefWidth="${parent.width * 0.3}" text="%Username">
4948
<cellValueFactory>
5049
<PropertyValueFactory property="username" />
5150
</cellValueFactory>

app/credentials-management/src/main/resources/org/phoebus/applications/credentialsmanagement/messages.properties

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@ ErrorDialogTitle=Operation Failed
2222
ErrorDialogBody=Logout failed!
2323
LoginButtonText=Login
2424
LogoutButtonText=Logout
25-
LogOutFromAll=Logout From All
25+
LoginToAll=Login To All
26+
LogoutFromAll=Logout From All
2627
NoCredentialsFound=No saved credentials and no service authentication providers configured
2728
Password=Password
28-
Scope=Scope
29+
Scope=Service
2930
SecureStoreErrorTitle=Application Launch Failed
3031
SecureStoreErrorBody=Failed to instantiate the secure store
3132
Title=Credentials Management
32-
UserName=User Name
33+
Username=Username

0 commit comments

Comments
 (0)