diff --git a/app/credentials-management/doc/images/CredentialsManagement.png b/app/credentials-management/doc/images/CredentialsManagement.png index 0828b9ec63..a76b53e6bd 100644 Binary files a/app/credentials-management/doc/images/CredentialsManagement.png and b/app/credentials-management/doc/images/CredentialsManagement.png differ diff --git a/app/credentials-management/doc/images/CredentialsManagement_Empty.png b/app/credentials-management/doc/images/CredentialsManagement_Empty.png deleted file mode 100644 index 1f36f43bc6..0000000000 Binary files a/app/credentials-management/doc/images/CredentialsManagement_Empty.png and /dev/null differ diff --git a/app/credentials-management/doc/images/CredentialsManagement_empty.png b/app/credentials-management/doc/images/CredentialsManagement_empty.png new file mode 100644 index 0000000000..9f5244c926 Binary files /dev/null and b/app/credentials-management/doc/images/CredentialsManagement_empty.png differ diff --git a/app/credentials-management/doc/images/CredentialsManagement_one_failed.png b/app/credentials-management/doc/images/CredentialsManagement_one_failed.png new file mode 100644 index 0000000000..5d61845fc0 Binary files /dev/null and b/app/credentials-management/doc/images/CredentialsManagement_one_failed.png differ diff --git a/app/credentials-management/doc/index.rst b/app/credentials-management/doc/index.rst index f18b4b9d4f..4236ea59bc 100644 --- a/app/credentials-management/doc/index.rst +++ b/app/credentials-management/doc/index.rst @@ -8,18 +8,25 @@ Further, Phoebus may be configured to store the credentials entered by the user In order to also support an explicit logout capability, the Credentials Management application offers means to remove stored credentials. -In some cases an explicit login procedure can be useful, e.g. login to service for the purpose of storing -user credentials and thereby support automated creation of logbook entries. - The application is launched using the dedicated button in the (bottom) status bar. -The below screen shot shows an example where credentials have been stored for the "logbook" scope, -plus an option to login to the "" scope. User may also choose to "logout" from all scopes, +The below screen shot shows an example where credentials have been stored for the "Logbook" scope, +plus an option to login to the "" scope. User may also choose to "Logout from all" services, i.e. to remove all stored credentials. .. image:: images/CredentialsManagement.png -If no credentials are stored in the credentials store, and if no services supporting authentication have been configured, +If no credentials are stored in the credentials store, and if no services supporting authentication are available, the Credentials Management UI will show a static message: -.. image:: images/CredentialsManagement_Empty.png \ No newline at end of file +.. image:: images/CredentialsManagement_empty.png + +The "Login To All" button can be used to login to all services as a single action. +In this case credentials entered in the text fields of toolbar at the top are used for all services, irrespective of +the credentials (if any) entered in other input fields. + +If login to a service fails (service off-line or invalid credentials), this is indicated in the "Login Result" column. + +.. image:: images/CredentialsManagement_one_failed.png + +If on the other hand login succeeds to a single or all services, the dialog is closed automatically. \ No newline at end of file diff --git a/app/credentials-management/src/main/java/org/phoebus/applications/credentialsmanagement/CredentialsManagementController.java b/app/credentials-management/src/main/java/org/phoebus/applications/credentialsmanagement/CredentialsManagementController.java index 1aa73f5786..c00cfaa0b0 100644 --- a/app/credentials-management/src/main/java/org/phoebus/applications/credentialsmanagement/CredentialsManagementController.java +++ b/app/credentials-management/src/main/java/org/phoebus/applications/credentialsmanagement/CredentialsManagementController.java @@ -19,22 +19,32 @@ package org.phoebus.applications.credentialsmanagement; import javafx.application.Platform; +import javafx.beans.binding.Bindings; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.Node; import javafx.scene.control.Button; +import javafx.scene.control.Label; import javafx.scene.control.PasswordField; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.TextField; +import javafx.scene.control.Tooltip; import javafx.scene.input.KeyCode; import javafx.stage.Stage; import javafx.util.Callback; import org.phoebus.framework.jobs.JobManager; +import org.phoebus.security.authorization.AuthenticationStatus; import org.phoebus.security.authorization.ServiceAuthenticationProvider; import org.phoebus.security.store.SecureStore; import org.phoebus.security.tokens.AuthenticationScope; @@ -53,22 +63,40 @@ */ public class CredentialsManagementController { + @SuppressWarnings("unused") @FXML private Node parent; + @SuppressWarnings("unused") @FXML private TableView tableView; + @SuppressWarnings("unused") @FXML - private TableColumn actionButtonColumn; + private TableColumn actionButtonColumn; + @SuppressWarnings("unused") @FXML - private TableColumn usernameColumn; + private TableColumn usernameColumn; + @SuppressWarnings("unused") @FXML - private TableColumn passwordColumn; + private TableColumn passwordColumn; + @SuppressWarnings("unused") @FXML - private Button clearAllCredentialsButton; - + private TableColumn loginResultColumn; + @SuppressWarnings("unused") + @FXML + private Button loginToAllButton; + @SuppressWarnings("unused") + @FXML + private Button logoutFromAllButton; + @SuppressWarnings("unused") @FXML - private TableColumn scopeColumn; + private TextField loginToAllUsernameTextField; + @SuppressWarnings("unused") + @FXML + private PasswordField loginToAllPasswordTextField; + @SuppressWarnings("unused") + @FXML + private TableColumn scopeColumn; private final SimpleBooleanProperty listEmpty = new SimpleBooleanProperty(true); private final ObservableList serviceItems = @@ -76,102 +104,156 @@ public class CredentialsManagementController { private final SecureStore secureStore; private static final Logger LOGGER = Logger.getLogger(CredentialsManagementController.class.getName()); private final List authenticationProviders; + private final StringProperty loginToAllUsernameProperty = new SimpleStringProperty(); + private final StringProperty loginToAllPasswordProperty = new SimpleStringProperty(); + private final IntegerProperty providerCount = new SimpleIntegerProperty(0); + + /** + * true if user is logged in to at least one service (scope). + */ + private final IntegerProperty loggedInCount = new SimpleIntegerProperty(0); private Stage stage; public CredentialsManagementController(List authenticationProviders, SecureStore secureStore) { this.authenticationProviders = authenticationProviders; this.secureStore = secureStore; + providerCount.set(this.authenticationProviders.size()); } + @SuppressWarnings("unused") @FXML public void initialize() { - tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); - clearAllCredentialsButton.disableProperty().bind(listEmpty); - Callback, TableCell> actionColumnCellFactory = new Callback<>() { + tableView.setSelectionModel(null); + tableView.getStylesheets().add(getClass().getResource("/css/credentials-management-style.css").toExternalForm()); + + usernameColumn.setCellFactory(c -> new UsernameTableCell()); + passwordColumn.setCellFactory(c -> new PasswordTableCell()); + loginResultColumn.setCellFactory(c -> new LoginResultTableCell()); + + loginToAllUsernameTextField.visibleProperty().bind(Bindings.createBooleanBinding(() -> providerCount.get() > 1, providerCount)); + loginToAllPasswordTextField.visibleProperty().bind(Bindings.createBooleanBinding(() -> providerCount.get() > 1, providerCount)); + loginToAllUsernameTextField.textProperty().bindBidirectional(loginToAllUsernameProperty); + loginToAllPasswordTextField.textProperty().bindBidirectional(loginToAllPasswordProperty); + + loginToAllButton.visibleProperty().bind(Bindings.createBooleanBinding(() -> providerCount.get() > 1, providerCount)); + // Login to all button enabled only if non-empty username and password is present + loginToAllButton.disableProperty().bind(Bindings.createBooleanBinding(() -> loginToAllUsernameProperty.get() == null || + loginToAllUsernameProperty.get().isEmpty() || + loginToAllPasswordProperty.get() == null || + loginToAllPasswordProperty.get().isEmpty(), + loginToAllUsernameProperty, loginToAllPasswordProperty)); + + logoutFromAllButton.disableProperty().bind(Bindings.createBooleanBinding(() -> loggedInCount.get() == 0, loggedInCount)); + actionButtonColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue())); + + configureCellFactory(); + + updateTable(); + + // Don't want focus on the username field for "login to all" as that obscures the prompt. + // Let table request focus. + Platform.runLater(() -> tableView.requestFocus()); + + } + + private void configureCellFactory() { + Callback, TableCell> actionColumnCellFactory = new Callback<>() { @Override - public TableCell call(final TableColumn param) { - final TableCell cell = new TableCell<>() { + public TableCell call(final TableColumn param) { + return new TableCell<>() { private final Button btn = new Button(Messages.LogoutButtonText); + { btn.getStyleClass().add("button-style"); btn.setOnAction((ActionEvent event) -> { - ServiceItem serviceItem = getTableView().getItems().get(getIndex()); - if(serviceItem.isLoginAction()){ - login(serviceItem); - } - else{ - logOut(serviceItem.getAuthenticationScope()); + ServiceItem serviceItem = getTableRow().getItem(); + if (serviceItem != null && (serviceItem.authenticationStatus.get().equals(AuthenticationStatus.AUTHENTICATED) || + serviceItem.authenticationStatus.get().equals(AuthenticationStatus.CACHED))) { + logOut(serviceItem); + } else { + login(serviceItem, 1); } }); } @Override - public void updateItem(Void o, boolean empty) { - super.updateItem(o, empty); + public void updateItem(ServiceItem serviceItem, boolean empty) { + super.updateItem(serviceItem, empty); if (empty) { setGraphic(null); } else { - if(getTableRow() != null && getTableRow().getItem() != null){ - btn.setText(getTableRow().getItem().loginAction ? - Messages.LoginButtonText : Messages.LogoutButtonText); - } + btn.textProperty().bind(serviceItem.buttonTextProperty); + btn.disableProperty().bind(Bindings.createBooleanBinding(() -> + serviceItem.username.isNull().get() || serviceItem.username.get().isEmpty() || + serviceItem.password.isNull().get() || serviceItem.password.get().isEmpty(), + serviceItem.username, serviceItem.password)); setGraphic(btn); } } }; - return cell; } }; actionButtonColumn.setCellFactory(actionColumnCellFactory); - usernameColumn.setCellFactory(c -> new UsernameTableCell()); - passwordColumn.setCellFactory(c -> new PasswordTableCell()); - - updateTable(); } + @SuppressWarnings("unused") @FXML - public void logOutFromAll() { + public synchronized void logoutFromAll() { try { - secureStore.deleteAllScopedAuthenticationTokens(); - updateTable(); + tableView.getItems().forEach(s -> logOut(s)); } catch (Exception e) { LOGGER.log(Level.WARNING, "Failed to delete all authentication tokens from key store", e); ExceptionDetailsErrorDialog.openError(parent, Messages.ErrorDialogTitle, Messages.ErrorDialogBody, e); } } + @SuppressWarnings("unused") + @FXML + public synchronized void loginToAll() { + logoutFromAll(); + try { + for (ServiceItem serviceItem : tableView.getItems()) { + serviceItem.username.set(loginToAllUsernameProperty.get()); + serviceItem.password.set(loginToAllPasswordProperty.get()); + login(serviceItem, tableView.getItems().size()); + } + if (loggedInCount.get() == tableView.getItems().size()) { + stage.close(); + } + } catch (Exception e) { + LOGGER.log(Level.WARNING, "Failed to login to all services", e); + } + } + /** - * Attempts to sign in user based on provided credentials. If sign-in succeeds, this method will close the - * associated UI. + * Attempts to sign in user based on provided credentials. + * * @param serviceItem The {@link ServiceItem} defining the scope, and implicitly the authentication service. + * @return true if login succeeds. */ - private void login(ServiceItem serviceItem){ - try { - serviceItem.getServiceAuthenticationProvider().authenticate(serviceItem.getUsername(), serviceItem.getPassword()); - try { - secureStore.setScopedAuthentication(new ScopedAuthenticationToken(serviceItem.getAuthenticationScope(), - serviceItem.getUsername(), - serviceItem.getPassword())); + private synchronized void login(ServiceItem serviceItem, int expectedLoginCount) { + AuthenticationStatus authenticationResult = serviceItem.login(); + if (authenticationResult.equals(AuthenticationStatus.AUTHENTICATED)) { + loggedInCount.set(loggedInCount.get() + 1); + if (expectedLoginCount == loggedInCount.get()) { stage.close(); - } catch (Exception exception) { - LOGGER.log(Level.WARNING, "Failed to store credentials", exception); } - } catch (Exception exception) { - LOGGER.log(Level.WARNING, "Failed to login to service", exception); - ExceptionDetailsErrorDialog.openError(parent, "Login Failure", "Failed to login to service", exception); } } - private void logOut(AuthenticationScope scope) { - try { - secureStore.deleteScopedAuthenticationToken(scope); - updateTable(); - } catch (Exception e) { - LOGGER.log(Level.WARNING, "Failed to logout from scope " + scope, e); - ExceptionDetailsErrorDialog.openError(parent, Messages.ErrorDialogTitle, Messages.ErrorDialogBody, e); + private synchronized void logOut(ServiceItem serviceItem) { + if (serviceItem.authenticationStatus.get().equals(AuthenticationStatus.AUTHENTICATED) + || serviceItem.authenticationStatus.get().equals(AuthenticationStatus.CACHED)) { + try { + serviceItem.logout(); + loggedInCount.set(loggedInCount.get() - 1); + Platform.runLater(() -> tableView.requestFocus()); + } catch (Exception e) { + LOGGER.log(Level.WARNING, "Failed to logout from service " + serviceItem.getDisplayName(), e); + } } } @@ -181,19 +263,21 @@ private void updateTable() { // Match saved tokens with an authentication provider, where applicable List serviceItems = savedTokens.stream().map(token -> { ServiceAuthenticationProvider provider = - authenticationProviders.stream().filter(p-> p.getAuthenticationScope().equals(token.getAuthenticationScope())).findFirst().orElse(null); - return new ServiceItem(provider, token.getUsername(), token.getPassword()); + authenticationProviders.stream().filter(p -> p.getAuthenticationScope().getScope().equals(token.getAuthenticationScope().getScope())).findFirst().orElse(null); + loggedInCount.set(loggedInCount.get() + 1); + return new ServiceItem(provider, AuthenticationStatus.CACHED, token.getUsername(), token.getPassword()); }).collect(Collectors.toList()); // Also need to add ServiceItems for providers not matched with a saved token, i.e. for logged-out services authenticationProviders.forEach(p -> { Optional serviceItem = serviceItems.stream().filter(si -> - p.getAuthenticationScope().equals(si.getAuthenticationScope())).findFirst(); - if(serviceItem.isEmpty()){ - serviceItems.add(new ServiceItem(p)); + p.getAuthenticationScope().getScope().equals(si.getAuthenticationScope().getScope())).findFirst(); + if (serviceItem.isEmpty()) { + serviceItems.add(new ServiceItem(p, AuthenticationStatus.UNDETERMINED, null, null)); } }); - serviceItems.sort(Comparator.comparing(ServiceItem::getAuthenticationScope)); + serviceItems.sort(Comparator.comparing(i -> i.getAuthenticationScope().getDisplayName())); + Platform.runLater(() -> { this.serviceItems.setAll(serviceItems); listEmpty.set(savedTokens.isEmpty()); @@ -205,29 +289,56 @@ private void updateTable() { /** * Model class for the table view */ - public static class ServiceItem { + public class ServiceItem { private final ServiceAuthenticationProvider serviceAuthenticationProvider; - private String username; - private String password; - private boolean loginAction = false; - - public ServiceItem(ServiceAuthenticationProvider serviceAuthenticationProvider, String username, String password) { - this.serviceAuthenticationProvider = serviceAuthenticationProvider; - this.username = username; - this.password = password; - } - - public ServiceItem(ServiceAuthenticationProvider serviceAuthenticationProvider) { + private final StringProperty username = new SimpleStringProperty(); + private final StringProperty password = new SimpleStringProperty(); + private final StringProperty buttonTextProperty = new SimpleStringProperty(); + private final StringProperty loginResultMessage = new SimpleStringProperty(); + private final ObjectProperty authenticationStatus = new SimpleObjectProperty<>(); + + public ServiceItem(ServiceAuthenticationProvider serviceAuthenticationProvider, AuthenticationStatus authenticationResult, String username, String password) { + setupChangeListeners(); this.serviceAuthenticationProvider = serviceAuthenticationProvider; - loginAction = true; + this.username.set(username); + this.password.set(password); + this.authenticationStatus.set(authenticationResult); } - public String getUsername() { - return username; + private void setupChangeListeners() { + this.authenticationStatus.addListener((obs, o, n) -> { + switch (n) { + case UNDETERMINED -> { + loginResultMessage.set(null); + buttonTextProperty.set(Messages.LoginButtonText); + } + case CACHED -> { + loginResultMessage.set(null); + buttonTextProperty.set(Messages.LogoutButtonText); + } + case AUTHENTICATED -> { + loginResultMessage.set("OK"); + buttonTextProperty.set(Messages.LogoutButtonText); + } + case BAD_CREDENTIALS -> { + loginResultMessage.set(Messages.UserNotAuthenticated); + buttonTextProperty.set(Messages.LoginButtonText); + } + case SERVICE_OFFLINE -> { + loginResultMessage.set(Messages.ServiceConnectionFailure); + buttonTextProperty.set(Messages.LoginButtonText); + } + case UNKNOWN_ERROR -> { + loginResultMessage.set(Messages.UnknownError); + buttonTextProperty.set(Messages.LoginButtonText); + } + } + }); } - public void setUsername(String username){ - this.username = username; + @SuppressWarnings("unused") + public StringProperty getLoginResultMessage() { + return loginResultMessage; } public AuthenticationScope getAuthenticationScope() { @@ -239,79 +350,103 @@ public AuthenticationScope getAuthenticationScope() { * @return String representation of the authentication scope. */ @SuppressWarnings("unused") - public String getScope(){ + public String getScope() { return serviceAuthenticationProvider != null ? - serviceAuthenticationProvider.getAuthenticationScope().getName() : ""; + serviceAuthenticationProvider.getAuthenticationScope().getScope() : ""; } - public String getPassword(){ - return password; + @SuppressWarnings("unused") + public String getDisplayName() { + return serviceAuthenticationProvider != null ? + serviceAuthenticationProvider.getAuthenticationScope().getDisplayName() : ""; } - public void setPassword(String password){ - this.password = password; + @SuppressWarnings("unused") + public StringProperty getUsername(){ + return username; } - public ServiceAuthenticationProvider getServiceAuthenticationProvider() { - return serviceAuthenticationProvider; + @SuppressWarnings("unused") + public StringProperty getPassword(){ + return password; } - public boolean isLoginAction(){ - return loginAction; + public void logout() { + serviceAuthenticationProvider.logout(); + authenticationStatus.set(AuthenticationStatus.UNDETERMINED); + username.set(null); + password.set(null); } - } - private class UsernameTableCell extends TableCell{ - private final TextField textField = new TextField(); - public UsernameTableCell(){ - textField.getStyleClass().add("text-field-styling"); - // Update model on key up - textField.setOnKeyReleased(ke -> getTableRow().getItem().setUsername(textField.getText())); + public AuthenticationStatus login() { + AuthenticationStatus authenticationStatus = serviceAuthenticationProvider.authenticate(username.get(), password.get()); + if (authenticationStatus.equals(AuthenticationStatus.AUTHENTICATED)) { + try { + secureStore.setScopedAuthentication(new ScopedAuthenticationToken(getAuthenticationScope(), + username.get(), + password.get())); + } catch (Exception e) { + LOGGER.log(Level.WARNING, "Failed to store user credentials"); + } + } + this.authenticationStatus.set(authenticationStatus); + return authenticationStatus; } + } + + private class UsernameTableCell extends TableCell { @Override - protected void updateItem(String item, final boolean empty) - { + public void updateItem(StringProperty item, final boolean empty) { super.updateItem(item, empty); - if(empty){ + if (empty) { setGraphic(null); - } - else{ - textField.setText(item); - if(getTableRow() != null && getTableRow().getItem() != null){ - // Disable field if user is logged in. - textField.disableProperty().set(!getTableRow().getItem().loginAction); - } + } else { + ServiceItem serviceItem = getTableRow().getItem(); + TextField textField = new TextField(); + textField.getStyleClass().add("text-field-styling"); + textField.textProperty().bindBidirectional(serviceItem.username); + textField.disableProperty().bind(Bindings.createBooleanBinding(() -> serviceItem.authenticationStatus.get().equals(AuthenticationStatus.AUTHENTICATED), + serviceItem.authenticationStatus)); + textField.setOnKeyPressed(keyEvent -> { + if (keyEvent.getCode() == KeyCode.ENTER && + !serviceItem.username.isNull().get() && + !serviceItem.username.get().isEmpty() && + !serviceItem.password.isNull().get() && + !serviceItem.password.get().isEmpty()) { + CredentialsManagementController.this.login(serviceItem, 1); + } + }); setGraphic(textField); } } } - private class PasswordTableCell extends TableCell{ - private final PasswordField passwordField = new PasswordField(); - - public PasswordTableCell(){ - passwordField.getStyleClass().add("text-field-styling"); - // Update model on key up - passwordField.setOnKeyReleased(ke -> getTableRow().getItem().setPassword(passwordField.getText())); - } + private class PasswordTableCell extends TableCell { @Override - protected void updateItem(String item, final boolean empty) - { + protected void updateItem(StringProperty item, final boolean empty) { super.updateItem(item, empty); - if(empty){ + if (empty) { setGraphic(null); - } - else{ - passwordField.setText(item == null ? item : "dummypass"); // Hack to not reveal password length - if(getTableRow() != null && getTableRow().getItem() != null) { - // Disable field if user is logged in. - passwordField.disableProperty().set(!getTableRow().getItem().loginAction); - } + } else { + PasswordField passwordField = new PasswordField(); + passwordField.getStyleClass().add("text-field-styling"); + ServiceItem serviceItem = getTableRow().getItem(); + passwordField.textProperty().bindBidirectional(serviceItem.password); + passwordField.disableProperty().bind(Bindings.createBooleanBinding(() -> + serviceItem.authenticationStatus.get().equals(AuthenticationStatus.AUTHENTICATED) || serviceItem.authenticationStatus.get().equals(AuthenticationStatus.CACHED), + serviceItem.authenticationStatus)); + serviceItem.password.set(serviceItem.authenticationStatus.get().equals(AuthenticationStatus.AUTHENTICATED) || serviceItem.authenticationStatus.get().equals(AuthenticationStatus.CACHED) + ? "dummypass" : null); // Hack to not reveal password length + passwordField.setOnKeyPressed(keyEvent -> { - if (keyEvent.getCode() == KeyCode.ENTER) { - CredentialsManagementController.this.login(getTableRow().getItem()); + if (keyEvent.getCode() == KeyCode.ENTER && + !serviceItem.username.isNull().get() && + !serviceItem.username.get().isEmpty() && + !serviceItem.password.isNull().get() && + !serviceItem.password.get().isEmpty()) { + CredentialsManagementController.this.login(serviceItem, 1); } }); setGraphic(passwordField); @@ -319,7 +454,30 @@ protected void updateItem(String item, final boolean empty) } } - public void setStage(Stage stage){ + private static class LoginResultTableCell extends TableCell { + + @Override + protected void updateItem(StringProperty item, final boolean empty) { + super.updateItem(item, empty); + if (empty) { + setGraphic(null); + } else { + ServiceItem serviceItem = getTableRow().getItem(); + Label label = new Label(); + label.textProperty().bind(serviceItem.loginResultMessage); + serviceItem.authenticationStatus.addListener((obs, o, n) -> { + switch (n){ + case CACHED, AUTHENTICATED -> label.getStyleClass().remove("error"); + default -> label.getStyleClass().add("error"); + } + label.setTooltip(new Tooltip(serviceItem.loginResultMessage.get())); + }); + setGraphic(label); + } + } + } + + public void setStage(Stage stage) { this.stage = stage; } } diff --git a/app/credentials-management/src/main/java/org/phoebus/applications/credentialsmanagement/CredentialsManagementStage.java b/app/credentials-management/src/main/java/org/phoebus/applications/credentialsmanagement/CredentialsManagementStage.java index ef51b00f04..42548efe60 100644 --- a/app/credentials-management/src/main/java/org/phoebus/applications/credentialsmanagement/CredentialsManagementStage.java +++ b/app/credentials-management/src/main/java/org/phoebus/applications/credentialsmanagement/CredentialsManagementStage.java @@ -61,7 +61,6 @@ public CredentialsManagementStage(List authentica try { fxmlLoader.load(); Scene scene = new Scene(fxmlLoader.getRoot()); - scene.getStylesheets().add(getClass().getResource("/css/credentials-management-style.css").toExternalForm()); setTitle(Messages.Title); setScene(scene); } catch (Exception exception) { diff --git a/app/credentials-management/src/main/java/org/phoebus/applications/credentialsmanagement/Messages.java b/app/credentials-management/src/main/java/org/phoebus/applications/credentialsmanagement/Messages.java index ac40d99da5..7cc77f4fc7 100644 --- a/app/credentials-management/src/main/java/org/phoebus/applications/credentialsmanagement/Messages.java +++ b/app/credentials-management/src/main/java/org/phoebus/applications/credentialsmanagement/Messages.java @@ -30,6 +30,9 @@ public class Messages { public static String SecureStoreErrorTitle; public static String SecureStoreErrorBody; public static String Title; + public static String ServiceConnectionFailure; + public static String UnknownError; + public static String UserNotAuthenticated; static { diff --git a/app/credentials-management/src/main/resources/css/credentials-management-style.css b/app/credentials-management/src/main/resources/css/credentials-management-style.css index 5f318d1728..78412d657e 100644 --- a/app/credentials-management/src/main/resources/css/credentials-management-style.css +++ b/app/credentials-management/src/main/resources/css/credentials-management-style.css @@ -1,20 +1,27 @@ .text-field-styling{ -fx-padding: 5px 3px 3px 3px; - -fx-border-insets: 5px 3px 3px 3px; + -fx-border-insets: 5px 3px 3px 2px; -fx-background-insets: 5px 3px 3px 3px; -fx-border-color: #cdcdcd; -fx-border-radius: 3px; } .table-view .table-column{ - -fx-alignment:center; + -fx-alignment:CENTER; } -.table-view .table-cell{ - -fx-font-weight: bold; +.table-view .column-header > .label{ + -fx-alignment: CENTER-LEFT; -fx-font-size: 14px; + -fx-padding: 5px 3px 3px 3px; } .button-style{ -fx-pref-width: 100px; } + +.error{ + -fx-text-fill: red; +} + + diff --git a/app/credentials-management/src/main/resources/org/phoebus/applications/credentialsmanagement/CredentialsManagement.fxml b/app/credentials-management/src/main/resources/org/phoebus/applications/credentialsmanagement/CredentialsManagement.fxml index 3c78f63e53..ec66f94297 100644 --- a/app/credentials-management/src/main/resources/org/phoebus/applications/credentialsmanagement/CredentialsManagement.fxml +++ b/app/credentials-management/src/main/resources/org/phoebus/applications/credentialsmanagement/CredentialsManagement.fxml @@ -22,15 +22,15 @@ - + - + - + + +