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 cd1af5cada..1aa73f5786 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 @@ -31,6 +31,8 @@ import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.TextField; +import javafx.scene.input.KeyCode; +import javafx.stage.Stage; import javafx.util.Callback; import org.phoebus.framework.jobs.JobManager; import org.phoebus.security.authorization.ServiceAuthenticationProvider; @@ -75,6 +77,8 @@ public class CredentialsManagementController { private static final Logger LOGGER = Logger.getLogger(CredentialsManagementController.class.getName()); private final List authenticationProviders; + private Stage stage; + public CredentialsManagementController(List authenticationProviders, SecureStore secureStore) { this.authenticationProviders = authenticationProviders; this.secureStore = secureStore; @@ -139,6 +143,11 @@ public void logOutFromAll() { } } + /** + * Attempts to sign in user based on provided credentials. If sign-in succeeds, this method will close the + * associated UI. + * @param serviceItem The {@link ServiceItem} defining the scope, and implicitly the authentication service. + */ private void login(ServiceItem serviceItem){ try { serviceItem.getServiceAuthenticationProvider().authenticate(serviceItem.getUsername(), serviceItem.getPassword()); @@ -146,11 +155,10 @@ private void login(ServiceItem serviceItem){ secureStore.setScopedAuthentication(new ScopedAuthenticationToken(serviceItem.getAuthenticationScope(), serviceItem.getUsername(), serviceItem.getPassword())); + stage.close(); } catch (Exception exception) { LOGGER.log(Level.WARNING, "Failed to store credentials", exception); } - updateTable(); - } catch (Exception exception) { LOGGER.log(Level.WARNING, "Failed to login to service", exception); ExceptionDetailsErrorDialog.openError(parent, "Login Failure", "Failed to login to service", exception); @@ -252,7 +260,7 @@ public boolean isLoginAction(){ return loginAction; } } - private static class UsernameTableCell extends TableCell{ + private class UsernameTableCell extends TableCell{ private final TextField textField = new TextField(); public UsernameTableCell(){ @@ -279,7 +287,7 @@ protected void updateItem(String item, final boolean empty) } } - private static class PasswordTableCell extends TableCell{ + private class PasswordTableCell extends TableCell{ private final PasswordField passwordField = new PasswordField(); public PasswordTableCell(){ @@ -301,8 +309,17 @@ protected void updateItem(String item, final boolean empty) // Disable field if user is logged in. passwordField.disableProperty().set(!getTableRow().getItem().loginAction); } + passwordField.setOnKeyPressed(keyEvent -> { + if (keyEvent.getCode() == KeyCode.ENTER) { + CredentialsManagementController.this.login(getTableRow().getItem()); + } + }); setGraphic(passwordField); } } } + + 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 6749e1175e..ef51b00f04 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 @@ -50,6 +50,7 @@ public CredentialsManagementStage(List authentica CredentialsManagementController controller = (CredentialsManagementController) clazz.getConstructor(List.class, SecureStore.class) .newInstance(authenticationProviders, secureStore); + controller.setStage(this); return controller; } catch (Exception e) { diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/client/SaveAndRestoreClient.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/client/SaveAndRestoreClient.java index 600a8e5f9f..a95994721d 100644 --- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/client/SaveAndRestoreClient.java +++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/client/SaveAndRestoreClient.java @@ -151,7 +151,9 @@ public interface SaveAndRestoreClient { SnapshotData getSnapshotData(String uniqueId); - Snapshot saveSnapshot(String parentNodeId, Snapshot snapshot); + Snapshot createSnapshot(String parentNodeId, Snapshot snapshot); + + Snapshot updateSnapshot(Snapshot snapshot); /** * Creates a new {@link CompositeSnapshot}. diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/client/SaveAndRestoreJerseyClient.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/client/SaveAndRestoreJerseyClient.java index aac19ca992..ae9cf53c92 100644 --- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/client/SaveAndRestoreJerseyClient.java +++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/client/SaveAndRestoreJerseyClient.java @@ -102,10 +102,8 @@ private Client getClient() { String password = scopedAuthenticationToken.getPassword(); httpBasicAuthFilter = new HTTPBasicAuthFilter(username, password); client.addFilter(httpBasicAuthFilter); - } else {//if (httpBasicAuthFilter != null) { - //client.removeFilter(httpBasicAuthFilter); - httpBasicAuthFilter = new HTTPBasicAuthFilter(System.getProperty("user.name"), "password"); - client.addFilter(httpBasicAuthFilter); + } else if (httpBasicAuthFilter != null) { + client.removeFilter(httpBasicAuthFilter); } } catch (Exception e) { logger.log(Level.WARNING, "Unable to retrieve credentials from secure store", e); @@ -182,8 +180,7 @@ public List getChildNodes(String uniqueNodeId) throws SaveAndRestoreClient @Override public Node createNewNode(String parentNodeId, Node node) { WebResource webResource = getClient().resource(jmasarServiceUrl + "/node") - .queryParam("parentNodeId", parentNodeId) // Request parameter username is needed in case authorization/authentication is disabled. - .queryParam("username", System.getProperty("user.name")); + .queryParam("parentNodeId", parentNodeId); ClientResponse response = webResource.accept(CONTENT_TYPE_JSON) .entity(node, CONTENT_TYPE_JSON) .put(ClientResponse.class); @@ -207,9 +204,7 @@ public Node updateNode(Node nodeToUpdate) { @Override public Node updateNode(Node nodeToUpdate, boolean customTimeForMigration) { WebResource webResource = getClient().resource(jmasarServiceUrl + "/node") - .queryParam("customTimeForMigration", customTimeForMigration ? "true" : "false") - // Request parameter username is needed in case authorization/authentication is disabled. - .queryParam("username", System.getProperty("user.name")); + .queryParam("customTimeForMigration", customTimeForMigration ? "true" : "false"); ClientResponse response = webResource.accept(CONTENT_TYPE_JSON) .entity(nodeToUpdate, CONTENT_TYPE_JSON) @@ -347,9 +342,7 @@ public ConfigurationData getConfigurationData(String nodeId) { public Configuration createConfiguration(String parentNodeId, Configuration configuration) { WebResource webResource = getClient().resource(jmasarServiceUrl + "/config") - .queryParam("parentNodeId", parentNodeId) - // Request parameter username is needed in case authorization/authentication is disabled. - .queryParam("username", System.getProperty("user.name")); + .queryParam("parentNodeId", parentNodeId); ClientResponse response = webResource.accept(CONTENT_TYPE_JSON) .entity(configuration, CONTENT_TYPE_JSON) .put(ClientResponse.class); @@ -367,9 +360,7 @@ public Configuration createConfiguration(String parentNodeId, Configuration conf @Override public Configuration updateConfiguration(Configuration configuration) { - WebResource webResource = getClient().resource(jmasarServiceUrl + "/config") - // Request parameter username is needed in case authorization/authentication is disabled. - .queryParam("username", System.getProperty("user.name")); + WebResource webResource = getClient().resource(jmasarServiceUrl + "/config"); ClientResponse response = webResource.accept(CONTENT_TYPE_JSON) .entity(configuration, CONTENT_TYPE_JSON) @@ -393,12 +384,10 @@ public SnapshotData getSnapshotData(String nodeId) { } @Override - public Snapshot saveSnapshot(String parentNodeId, Snapshot snapshot) { + public Snapshot createSnapshot(String parentNodeId, Snapshot snapshot) { WebResource webResource = getClient().resource(jmasarServiceUrl + "/snapshot") - .queryParam("parentNodeId", parentNodeId) - // Request parameter username is needed in case authorization/authentication is disabled. - .queryParam("username", System.getProperty("user.name")); + .queryParam("parentNodeId", parentNodeId); ClientResponse response; try { response = webResource.accept(CONTENT_TYPE_JSON) @@ -419,13 +408,36 @@ public Snapshot saveSnapshot(String parentNodeId, Snapshot snapshot) { return response.getEntity(Snapshot.class); } + @Override + public Snapshot updateSnapshot(Snapshot snapshot) { + WebResource webResource = + getClient().resource(jmasarServiceUrl + "/snapshot"); + ClientResponse response; + try { + response = webResource.accept(CONTENT_TYPE_JSON) + .entity(snapshot, CONTENT_TYPE_JSON) + .post(ClientResponse.class); + } catch (UniformInterfaceException e) { + throw new RuntimeException(e); + } + if (response.getStatus() != ClientResponse.Status.OK.getStatusCode()) { + String message = Messages.searchFailed; + try { + message = new String(response.getEntityInputStream().readAllBytes()); + } catch (IOException e) { + logger.log(Level.WARNING, "Unable to parse response", e); + } + throw new SaveAndRestoreClientException(message); + } + return response.getEntity(Snapshot.class); + } + + @Override public CompositeSnapshot createCompositeSnapshot(String parentNodeId, CompositeSnapshot compositeSnapshot) { WebResource webResource = getClient().resource(jmasarServiceUrl + "/composite-snapshot") - .queryParam("parentNodeId", parentNodeId) - // Request parameter username is needed in case authorization/authentication is disabled. - .queryParam("username", System.getProperty("user.name")); + .queryParam("parentNodeId", parentNodeId); ClientResponse response = webResource.accept(CONTENT_TYPE_JSON) .entity(compositeSnapshot, CONTENT_TYPE_JSON) .put(ClientResponse.class); @@ -463,9 +475,7 @@ public List checkCompositeSnapshotConsistency(List snapshotNodeI @Override public CompositeSnapshot updateCompositeSnapshot(CompositeSnapshot compositeSnapshot) { - WebResource webResource = getClient().resource(jmasarServiceUrl + "/composite-snapshot") - // Request parameter username is needed in case authorization/authentication is disabled. - .queryParam("username", System.getProperty("user.name")); + WebResource webResource = getClient().resource(jmasarServiceUrl + "/composite-snapshot"); ClientResponse response = webResource.accept(CONTENT_TYPE_JSON) .entity(compositeSnapshot, CONTENT_TYPE_JSON) @@ -502,9 +512,7 @@ public SearchResult search(MultivaluedMap searchParams) { @Override public Filter saveFilter(Filter filter) { - WebResource webResource = getClient().resource(jmasarServiceUrl + "/filter") - // Request parameter username is needed in case authorization/authentication is disabled. - .queryParam("username", System.getProperty("user.name")); + WebResource webResource = getClient().resource(jmasarServiceUrl + "/filter"); ClientResponse response = webResource.accept(CONTENT_TYPE_JSON) .entity(filter, CONTENT_TYPE_JSON) .put(ClientResponse.class); @@ -566,9 +574,7 @@ public void deleteFilter(String name) { public List addTag(TagData tagData) { WebResource webResource = - getClient().resource(jmasarServiceUrl + "/tags") - // Request parameter username is needed in case authorization/authentication is disabled. - .queryParam("username", System.getProperty("user.name")); + getClient().resource(jmasarServiceUrl + "/tags"); ClientResponse response; try { response = webResource.accept(CONTENT_TYPE_JSON) diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/ContextMenuBase.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/ContextMenuBase.java index 579dabf34e..e904215ee2 100644 --- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/ContextMenuBase.java +++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/ContextMenuBase.java @@ -76,7 +76,7 @@ public ContextMenuBase(SaveAndRestoreController saveAndRestoreController) { deleteNodesMenuItem = new MenuItem(Messages.contextMenuDelete, new ImageView(ImageRepository.DELETE)); deleteNodesMenuItem.setOnAction(ae -> saveAndRestoreController.deleteNodes()); deleteNodesMenuItem.disableProperty().bind(Bindings.createBooleanBinding(() -> - //userIsAuthenticatedProperty.not().get() || + userIsAuthenticatedProperty.not().get() || hasSameParentProperty.not().get(), userIsAuthenticatedProperty, hasSameParentProperty)); diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/ContextMenuSnapshot.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/ContextMenuSnapshot.java index 371e7b8e0e..4a4086b687 100644 --- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/ContextMenuSnapshot.java +++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/ContextMenuSnapshot.java @@ -60,7 +60,7 @@ public ContextMenuSnapshot(SaveAndRestoreController saveAndRestoreController) { tagWithComment = new Menu(Messages.contextMenuTagsWithComment, snapshotTagsWithCommentIconImage); tagWithComment.setOnShowing(event -> saveAndRestoreController.tagWithComment(tagWithComment)); tagWithComment.disableProperty().bind(Bindings.createBooleanBinding(() -> - multipleNodesSelectedProperty.get(), //|| userIsAuthenticatedProperty.not().get(), + multipleNodesSelectedProperty.get() || userIsAuthenticatedProperty.not().get(), multipleNodesSelectedProperty, userIsAuthenticatedProperty)); MenuItem addTagWithCommentMenuItem = TagWidget.AddTagWithCommentMenuItem(); @@ -84,8 +84,8 @@ public ContextMenuSnapshot(SaveAndRestoreController saveAndRestoreController) { tagGoldenMenuItem = new MenuItem(Messages.contextMenuTagAsGolden, new ImageView(ImageRepository.SNAPSHOT)); tagGoldenMenuItem.disableProperty().bind(Bindings.createBooleanBinding(() -> - multipleNodesSelectedProperty.get() || /*userIsAuthenticatedProperty.not().get() ||*/ mayTagOrUntagGoldenProperty.not().get(), - multipleNodesSelectedProperty, /*userIsAuthenticatedProperty,*/ mayTagOrUntagGoldenProperty)); + multipleNodesSelectedProperty.get() || userIsAuthenticatedProperty.not().get() || mayTagOrUntagGoldenProperty.not().get(), + multipleNodesSelectedProperty, userIsAuthenticatedProperty, mayTagOrUntagGoldenProperty)); Image copyIcon = ImageCache.getImage(SaveAndRestoreController.class, "/icons/copy.png"); MenuItem copyMenuItem = new MenuItem(Messages.copy, new ImageView(copyIcon)); diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreService.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreService.java index 37637f018d..4b7ea4c361 100644 --- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreService.java +++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreService.java @@ -235,7 +235,14 @@ public Snapshot saveSnapshot(Node configurationNode, Snapshot snapshot) throws E return snapshotItem; }).collect(Collectors.toList()); snapshot.getSnapshotData().setSnapshotItems(beautifiedItems); - Future future = executor.submit(() -> saveAndRestoreClient.saveSnapshot(configurationNode.getUniqueId(), snapshot)); + Future future = executor.submit(() -> { + if(snapshot.getSnapshotNode().getUniqueId() == null){ + return saveAndRestoreClient.createSnapshot(configurationNode.getUniqueId(), snapshot); + } + else{ + return saveAndRestoreClient.updateSnapshot(snapshot); + } + }); Snapshot updatedSnapshot = future.get(); // Notify listeners as the configuration node has a new child node. notifyNodeChangeListeners(configurationNode); diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotController.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotController.java index 25ad0d710d..20d024e70b 100644 --- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotController.java +++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotController.java @@ -34,8 +34,6 @@ import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreService; import org.phoebus.applications.saveandrestore.ui.VNoData; import org.phoebus.framework.jobs.JobManager; -import org.phoebus.security.store.SecureStore; -import org.phoebus.security.tokens.AuthenticationScope; import org.phoebus.security.tokens.ScopedAuthenticationToken; import org.phoebus.ui.dialog.DialogHelper; import org.phoebus.ui.dialog.ExceptionDetailsErrorDialog; @@ -153,13 +151,22 @@ public void saveSnapshot(ActionEvent actionEvent) { List snapshotItems = snapshotProperty.get().getSnapshotData().getSnapshotItems(); SnapshotData snapshotData = new SnapshotData(); snapshotData.setSnapshotItems(snapshotItems); - Snapshot snapshot = new Snapshot(); + Snapshot snapshot = snapshotProperty.get(); + // Creating new or updating existing (e.g. name change)? + if (snapshot == null) { + snapshot = new Snapshot(); + snapshot.setSnapshotNode(Node.builder().nodeType(NodeType.SNAPSHOT) + .name(snapshotControlsViewController.getSnapshotNameProperty().get()) + .description(snapshotControlsViewController.getSnapshotCommentProperty().get()).build()); + } else { + snapshot.getSnapshotNode().setName(snapshotControlsViewController.getSnapshotNameProperty().get()); + snapshot.getSnapshotNode().setDescription(snapshotControlsViewController.getSnapshotCommentProperty().get()); + } snapshot.setSnapshotData(snapshotData); - snapshot.setSnapshotNode(Node.builder().nodeType(NodeType.SNAPSHOT) - .name(snapshotControlsViewController.getSnapshotNameProperty().get()) - .description(snapshotControlsViewController.getSnapshotCommentProperty().get()).build()); + try { snapshot = SaveAndRestoreService.getInstance().saveSnapshot(configurationNode, snapshot); + snapshotProperty.set(snapshot); Node _snapshotNode = snapshot.getSnapshotNode(); javafx.scene.Node jfxNode = (javafx.scene.Node) actionEvent.getSource(); String userData = (String) jfxNode.getUserData(); @@ -363,7 +370,7 @@ public void addSnapshot(Node snapshotNode) { Snapshot snapshot = getSnapshotFromService(snapshotNode); snapshotTableViewController.addSnapshot(snapshot); } catch (Exception e) { - e.printStackTrace(); + Logger.getLogger(SnapshotController.class.getName()).log(Level.WARNING, "Failed to add snapshot", e); } finally { disabledUi.set(false); } @@ -392,7 +399,7 @@ private Snapshot getSnapshotFromService(Node snapshotNode) throws Exception { } @Override - public void secureStoreChanged(List validTokens){ + public void secureStoreChanged(List validTokens) { snapshotControlsViewController.secureStoreChanged(validTokens); } } \ No newline at end of file diff --git a/app/save-and-restore/app/src/main/resources/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotControlsView.fxml b/app/save-and-restore/app/src/main/resources/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotControlsView.fxml index 9e80e79d3c..d855662974 100644 --- a/app/save-and-restore/app/src/main/resources/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotControlsView.fxml +++ b/app/save-and-restore/app/src/main/resources/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotControlsView.fxml @@ -44,12 +44,13 @@ + + - - + diff --git a/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/security/UserNotAuthorizedException.java b/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/security/UserNotAuthorizedException.java deleted file mode 100644 index b8aafa19e4..0000000000 --- a/app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/security/UserNotAuthorizedException.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2023 European Spallation Source ERIC. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - */ - -package org.phoebus.applications.saveandrestore.model.security; - -public class UserNotAuthorizedException extends RuntimeException{ - - public UserNotAuthorizedException(String message){ - super(message); - } -} diff --git a/docs/source/preference_properties.rst b/docs/source/preference_properties.rst index ad5a30aab8..8a770612aa 100644 --- a/docs/source/preference_properties.rst +++ b/docs/source/preference_properties.rst @@ -24,10 +24,12 @@ File ../../app/alarm/model/src/main/resources/alarm_preferences.properties:: # A file to configure the properites of kafka clients kafka_properties= - # Name of alarm tree root + # Name of alarm tree root. + # Configures the alarm configuration used by the alarm server. + # For the UI, it sets the default alarm configuration. config_name=Accelerator - # Names of selectable alarm configurations + # Names of selectable alarm configurations for UI. # The `config_name` will be used as the default for newly opened tools, # and if `config_names` is empty, it remains the only option. # When one or more comma-separated configurations are listed, @@ -1215,6 +1217,17 @@ File ../../core/pv/src/main/resources/pv_ca_preferences.properties:: # Package org.phoebus.pv.ca # ------------------------- + # By default, we use the following preferences settings, + # but when the System property "jca.use_env" is "true", + # JCA falls back to the EPICS_CA_... environment variables. + # + # Sites that prefer to use the EPICS_CA_... environment variables + # thus need to add + # + # -Djca.use_env=true + # + # to their launcher script. + # Channel Access address list addr_list= diff --git a/services/save-and-restore/doc/index.rst b/services/save-and-restore/doc/index.rst index 0e96b56cb5..8d9641fa88 100644 --- a/services/save-and-restore/doc/index.rst +++ b/services/save-and-restore/doc/index.rst @@ -724,37 +724,46 @@ Body: ] Authentication and Authorization --------------------------------- +================================ All non-GET endpoints are subject to authentication, i.e. clients must send a basic authentication header. The service can be configured to delegate authentication to Active Directory or remote or local LDAP. For demo and test purposes hard coded credentials are found in the ``WebSecurityConfig`` class. See the file ``application.properties`` for information on how to select authentication method. +Two roles are defined, "sar-user" and "sar-admin". The actual name of these roles can be customizable in ``application.properties``, +and must match role/group names in LDAP or Active Directory. + Authorization uses a role-based approach like so: * Unauthenticated users may read data, i.e. access GET endpoints. -* Role "user": - * Create and save configurations - * Create and save snapshots - * Create and save composite snapshots - * Create and save filters +* Save-and-restore role "sar-user": + * Create and update configurations + * Create and update snapshots + * Create and update composite snapshots + * Create and update filters + * Create and update tags, except GOLDEN tag * Update and delete objects if user name matches object's user id and: * Object is a snapshot node and not referenced in a composite snapshot node * Object is a composite snapshot node * Object is configuration or folder node with no child nodes * Object is a filter -* Role "superuser": +perform restore operation -* Role "admin": no restrictions + * Object is a tag +* Save-and-restore role "sar-admin": no restrictions + -Roles must be defined as groups in Active Directory or LDAP. Role/group names can be configured in ``application.properties``. +Enabled authentication, disabled authorization +---------------------------------------------- +The application property ``authorization.permitall`` (default ``true``) can be used to bypass all authorization. In +this case authentication is still required for protected endpoints, but user need not be associated with +a save-and-restore role/group. Migration ---------- +========= -Commit ``48e17a380b660d59b79cec4d2bd908c0d78eeeae`` of the service code base is about changing the persistence -component from a RDB engine to Elasticsearch. Sites using save-and-restore with an RDB engine may migrate +From commit ``48e17a380b660d59b79cec4d2bd908c0d78eeeae`` of the service code base the persistence +layer is moved from RDB engine to Elasticsearch. Sites using save-and-restore with an RDB engine may migrate data using the below procedure. Terminology: "source host" is the host running the legacy service instance using a RDB engine, diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/migration/MigrateRdbToElastic.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/migration/MigrateRdbToElastic.java index c10096264f..c033b64882 100644 --- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/migration/MigrateRdbToElastic.java +++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/migration/MigrateRdbToElastic.java @@ -188,7 +188,7 @@ private void createSnapshot(RestTemplate restTemplate, snapshotData.setUniqueId(legacySnapshotNode.getUniqueId()); snapshot.setSnapshotData(snapshotData); snapshotCount++; - elasticsearchDAO.saveSnapshot(newConfigurationNode.getUniqueId(), snapshot); + elasticsearchDAO.createSnapshot(newConfigurationNode.getUniqueId(), snapshot); } } } diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/persistence/dao/NodeDAO.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/persistence/dao/NodeDAO.java index a9814db4df..0fa5aa4304 100644 --- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/persistence/dao/NodeDAO.java +++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/persistence/dao/NodeDAO.java @@ -134,7 +134,16 @@ public interface NodeDAO { * @param snapshot The {@link Snapshot} data. * @return The persisted {@link Snapshot} data. */ - Snapshot saveSnapshot(String parentNodeId, Snapshot snapshot); + Snapshot createSnapshot(String parentNodeId, Snapshot snapshot); + + /** + * Updates a {@link Snapshot} with respect to name, description/comment. No other properties of the + * node can be modified, but last updated date will be set accordingly. + * + * @param snapshot The {@link Snapshot} subject to update. + * @return The {@link Snapshot} object as read from the persistence implementation. + */ + Snapshot updateSnapshot(Snapshot snapshot); /** * Updates a {@link Node} with respect to name, description/comment and tags. No other properties of the diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/persistence/dao/impl/elasticsearch/ElasticsearchDAO.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/persistence/dao/impl/elasticsearch/ElasticsearchDAO.java index b46afb6d9b..1eecf1845e 100644 --- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/persistence/dao/impl/elasticsearch/ElasticsearchDAO.java +++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/persistence/dao/impl/elasticsearch/ElasticsearchDAO.java @@ -723,7 +723,7 @@ public ConfigurationData getConfigurationData(String uniqueId) { } @Override - public Snapshot saveSnapshot(String parentNodeId, Snapshot snapshot) { + public Snapshot createSnapshot(String parentNodeId, Snapshot snapshot) { SnapshotData sanitizedSnapshotData = removeDuplicateSnapshotItems(snapshot.getSnapshotData()); snapshot.setSnapshotData(sanitizedSnapshotData); @@ -747,6 +747,27 @@ public Snapshot saveSnapshot(String parentNodeId, Snapshot snapshot) { return newSnapshot; } + @Override + public Snapshot updateSnapshot(Snapshot snapshot) { + + SnapshotData sanitizedSnapshotData = removeDuplicateSnapshotItems(snapshot.getSnapshotData()); + snapshot.setSnapshotData(sanitizedSnapshotData); + + snapshot.getSnapshotNode().setNodeType(NodeType.SNAPSHOT); // Force node type + SnapshotData newSnapshotData; + try { + newSnapshotData = snapshotDataRepository.save(snapshot.getSnapshotData()); + } catch (Exception e) { + throw new RuntimeException(e); + } + + Snapshot newSnapshot = new Snapshot(); + newSnapshot.setSnapshotData(newSnapshotData); + newSnapshot.setSnapshotNode(snapshot.getSnapshotNode()); + + return newSnapshot; + } + @Override public SnapshotData getSnapshotData(String uniqueId) { Optional snapshotData = snapshotDataRepository.findById(uniqueId); diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/config/WebSecurityConfig.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/config/WebSecurityConfig.java index bbbee58ddf..1b90f4b65e 100644 --- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/config/WebSecurityConfig.java +++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/config/WebSecurityConfig.java @@ -36,7 +36,7 @@ public class WebSecurityConfig { /** * Authentication implementation. */ - @Value("${auth.impl:none}") + @Value("${auth.impl:demo}") protected String authenitcationImplementation; /** @@ -148,14 +148,8 @@ public WebSecurityCustomizer ignoringCustomizer() { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf().disable(); - if("none".equalsIgnoreCase(authenitcationImplementation.trim())){ - http.authorizeRequests().antMatchers("/**").permitAll(); - } - else{ - http.authorizeRequests().anyRequest().authenticated(); - } + http.authorizeRequests().anyRequest().authenticated(); http.httpBasic(); - return http.build(); } diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/AuthorizationHelper.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/AuthorizationHelper.java index 446987fbb5..087949bfe1 100644 --- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/AuthorizationHelper.java +++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/AuthorizationHelper.java @@ -22,25 +22,15 @@ import org.phoebus.applications.saveandrestore.model.*; import org.phoebus.applications.saveandrestore.model.search.Filter; import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO; -import org.phoebus.service.saveandrestore.web.config.AuthEnabledCondition; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Conditional; -import org.springframework.security.access.expression.SecurityExpressionRoot; import org.springframework.security.access.expression.method.MethodSecurityExpressionOperations; import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.authorization.AuthorizationManager; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.stereotype.Service; -import java.security.Principal; -import java.util.Collection; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; /** * {@link Service} class implementing domain specific authorization rules in order to @@ -62,8 +52,10 @@ public class AuthorizationHelper { @Autowired private String roleUser; - @Value("${authorization.bypass:true}") - public boolean bypassAuthorization; + @Value("${authorization.permitall:true}") + public boolean permitAll; + + private static final String ROLE_PREFIX = "ROLE_"; /** * Checks if all the nodes provided to this method can be deleted by the user. User with admin privileges is always @@ -75,10 +67,10 @@ public class AuthorizationHelper { * @return true only if all if the nodes can be deleted by the user. */ public boolean mayDelete(List nodeIds, MethodSecurityExpressionOperations methodSecurityExpressionOperations) { - if(bypassAuthorization || methodSecurityExpressionOperations.hasAuthority("ROLE_" + roleAdmin)){ + if(permitAll || methodSecurityExpressionOperations.hasAuthority(ROLE_PREFIX + roleAdmin)){ return true; } - if(!methodSecurityExpressionOperations.hasAuthority("ROLE_" + roleUser)){ + if(!methodSecurityExpressionOperations.hasAuthority(ROLE_PREFIX + roleUser)){ return false; } for (String nodeId : nodeIds) { @@ -121,10 +113,10 @@ private boolean mayDelete(String nodeId, String userName) { * @return false if user may not update the {@link Node}. */ public boolean mayUpdate(Node node, MethodSecurityExpressionOperations methodSecurityExpressionOperations) { - if(bypassAuthorization || methodSecurityExpressionOperations.hasAuthority("ROLE_" + roleAdmin)){ + if(permitAll || methodSecurityExpressionOperations.hasAuthority(ROLE_PREFIX + roleAdmin)){ return true; } - if(!methodSecurityExpressionOperations.hasAuthority("ROLE_" + roleUser)){ + if(!methodSecurityExpressionOperations.hasAuthority(ROLE_PREFIX + roleUser)){ return false; } return nodeDAO.getNode(node.getUniqueId()).getUserName() @@ -141,16 +133,57 @@ public boolean mayUpdate(Node node, MethodSecurityExpressionOperations methodSec * @return false if user may not update the {@link CompositeSnapshot}. */ public boolean mayUpdate(CompositeSnapshot compositeSnapshot, MethodSecurityExpressionOperations methodSecurityExpressionOperations) { - if(bypassAuthorization || methodSecurityExpressionOperations.hasAuthority("ROLE_" + roleAdmin)){ + if(permitAll || methodSecurityExpressionOperations.hasAuthority(ROLE_PREFIX + roleAdmin)){ return true; } - if(!methodSecurityExpressionOperations.hasAuthority("ROLE_" + roleUser)){ + if(!methodSecurityExpressionOperations.hasAuthority(ROLE_PREFIX + roleUser)){ return false; } return nodeDAO.getNode(compositeSnapshot.getCompositeSnapshotNode().getUniqueId()).getUserName() .equals(((User)methodSecurityExpressionOperations.getAuthentication().getPrincipal()).getUsername()); } + /** + * An authenticated user may update a configuration if user has admin privileges, or + * if user identity is same as the target {@link Node}'s user id. + * + * @param configuration {@link Configuration} identifying the target of the user's update operation. + * @param methodSecurityExpressionOperations {@link MethodSecurityExpressionOperations} Spring managed object + * queried for authorization. + * @return false if user may not update the {@link Configuration}. + */ + public boolean mayUpdate(Configuration configuration, MethodSecurityExpressionOperations methodSecurityExpressionOperations) { + if(permitAll || methodSecurityExpressionOperations.hasAuthority(ROLE_PREFIX + roleAdmin)){ + return true; + } + if(!methodSecurityExpressionOperations.hasAuthority(ROLE_PREFIX + roleUser)){ + return false; + } + return nodeDAO.getNode(configuration.getConfigurationNode().getUniqueId()).getUserName() + .equals(((User)methodSecurityExpressionOperations.getAuthentication().getPrincipal()).getUsername()); + } + + /** + * An authenticated user may update a snapshot if user has admin privileges, or + * if user identity is same as the target {@link Node}'s user id. + * + * @param snapshot {@link Snapshot} identifying the target of the user's update operation. + * @param methodSecurityExpressionOperations {@link MethodSecurityExpressionOperations} Spring managed object + * queried for authorization. + * @return false if user may not update the {@link Snapshot}. + */ + public boolean mayUpdate(Snapshot snapshot, MethodSecurityExpressionOperations methodSecurityExpressionOperations) { + if(permitAll || methodSecurityExpressionOperations.hasAuthority(ROLE_PREFIX + roleAdmin)){ + return true; + } + if(!methodSecurityExpressionOperations.hasAuthority(ROLE_PREFIX + roleUser)){ + return false; + } + // If snapshot's node has null id, then this is a + return nodeDAO.getNode(snapshot.getSnapshotNode().getUniqueId()).getUserName() + .equals(((User)methodSecurityExpressionOperations.getAuthentication().getPrincipal()).getUsername()); + } + /** * An authenticated user may add or delete {@link Tag}s if user identity is same as the target's * snapshot {@link Node}. However, to add or delete golden tag user must have admin privileges. @@ -167,15 +200,15 @@ public boolean mayAddOrDeleteTag(TagData tagData, MethodSecurityExpressionOperat tagData.getUniqueNodeIds() == null) { throw new IllegalArgumentException("Cannot add tag, data invalid"); } - if(bypassAuthorization || methodSecurityExpressionOperations.hasAuthority("ROLE_" + roleAdmin)){ + if(permitAll || methodSecurityExpressionOperations.hasAuthority(ROLE_PREFIX + roleAdmin)){ return true; } - if(!methodSecurityExpressionOperations.hasAuthority("ROLE_" + roleUser)){ + if(!methodSecurityExpressionOperations.hasAuthority(ROLE_PREFIX + roleUser)){ return false; } Tag tag = tagData.getTag(); if(tag.getName().equals(Tag.GOLDEN)){ - return methodSecurityExpressionOperations.hasAuthority("ROLE_" + roleAdmin); + return methodSecurityExpressionOperations.hasAuthority(ROLE_PREFIX + roleAdmin); } String username = ((User)methodSecurityExpressionOperations.getAuthentication().getPrincipal()).getUsername(); for(String nodeId : tagData.getUniqueNodeIds()){ @@ -200,10 +233,10 @@ public boolean mayAddOrDeleteTag(TagData tagData, MethodSecurityExpressionOperat * @return false if user may not update the {@link Filter}. */ public boolean maySaveOrDeleteFilter(String filterName, MethodSecurityExpressionOperations methodSecurityExpressionOperations) { - if(bypassAuthorization || methodSecurityExpressionOperations.hasAuthority("ROLE_" + roleAdmin)){ + if(permitAll || methodSecurityExpressionOperations.hasAuthority(ROLE_PREFIX + roleAdmin)){ return true; } - if(!methodSecurityExpressionOperations.hasAuthority("ROLE_" + roleUser)){ + if(!methodSecurityExpressionOperations.hasAuthority(ROLE_PREFIX + roleUser)){ return false; } Optional filter1 = @@ -216,4 +249,28 @@ public boolean maySaveOrDeleteFilter(String filterName, MethodSecurityExpression String username = ((User)methodSecurityExpressionOperations.getAuthentication().getPrincipal()).getUsername(); return filter1.map(value -> value.getUser().equals(username)).orElse(true); } + + /** + * Checks if user is allowed to create an object (node, snapshot...). This is the case if authorization + * is disabled or if user has (basic) user role. + * + * @param methodSecurityExpressionOperations {@link MethodSecurityExpressionOperations} Spring managed object + * queried for authorization. + * @return false if user may not create the object, otherwise true. + */ + public boolean mayCreate(MethodSecurityExpressionOperations methodSecurityExpressionOperations){ + return permitAll || methodSecurityExpressionOperations.hasAuthority(ROLE_PREFIX + roleUser); + } + + /** + * Checks if user is allowed to move or copy objects. This is the case if authorization + * is disabled or if user has admin role. + * + * @param methodSecurityExpressionOperations {@link MethodSecurityExpressionOperations} Spring managed object + * queried for authorization. + * @return false if user may not create the object, otherwise true. + */ + public boolean mayMoveOrCopy(MethodSecurityExpressionOperations methodSecurityExpressionOperations) { + return permitAll || methodSecurityExpressionOperations.hasAuthority(ROLE_PREFIX + roleAdmin); + } } diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/BaseController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/BaseController.java index 6d2d8f4ceb..8a025c82bb 100644 --- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/BaseController.java +++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/BaseController.java @@ -17,7 +17,6 @@ */ package org.phoebus.service.saveandrestore.web.controllers; -import org.phoebus.applications.saveandrestore.model.security.UserNotAuthorizedException; import org.phoebus.service.saveandrestore.NodeNotFoundException; import org.phoebus.service.saveandrestore.SnapshotNotFoundException; import org.springframework.beans.factory.annotation.Autowired; @@ -91,12 +90,6 @@ public ResponseEntity handleNodeNotFoundException(HttpServletRequest req return new ResponseEntity<>(exception.getMessage(), HttpStatus.NOT_FOUND); } - @ExceptionHandler(UserNotAuthorizedException.class) - public ResponseEntity handleUserNotAuthorizedException(HttpServletRequest req, UserNotAuthorizedException exception) { - log(exception); - return new ResponseEntity<>(exception.getMessage(), HttpStatus.FORBIDDEN); - } - private void log(Throwable throwable) { logger.log(Level.INFO, "Intercepted " + throwable.getClass().getName(), throwable); } diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotController.java index 359fa67e11..96690cf79b 100644 --- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotController.java +++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotController.java @@ -42,7 +42,7 @@ public class CompositeSnapshotController extends BaseController { private NodeDAO nodeDAO; @PutMapping(value = "/composite-snapshot", produces = JSON) - @PreAuthorize("hasRole(this.roleUser)") + @PreAuthorize("@authorizationHelper.mayCreate(#root)") public CompositeSnapshot createCompositeSnapshot(@RequestParam(value = "parentNodeId") String parentNodeId, @RequestBody CompositeSnapshot compositeSnapshot, Principal principal) { diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationController.java index 462d845e61..81813e3a27 100644 --- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationController.java +++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationController.java @@ -44,7 +44,7 @@ public class ConfigurationController extends BaseController { @SuppressWarnings("unused") @PutMapping(produces = JSON) - @PreAuthorize("hasRole(this.roleUser)") + @PreAuthorize("@authorizationHelper.mayCreate(#root)") public Configuration createConfiguration(@RequestParam(value = "parentNodeId") String parentNodeId, @RequestBody Configuration configuration, Principal principal) { @@ -60,28 +60,10 @@ public ConfigurationData getConfigurationData(@PathVariable String uniqueId) { @SuppressWarnings("unused") @PostMapping(produces = JSON) - @PreAuthorize("hasRole(this.roleAdmin) or (hasRole(this.roleAdmin) or this.mayUpdate(#configuration, #principal))") + @PreAuthorize("@authorizationHelper.mayUpdate(#configuration, #root)") public Configuration updateConfiguration(@RequestBody Configuration configuration, Principal principal) { configuration.getConfigurationNode().setUserName(principal.getName()); - Configuration c = nodeDAO.updateConfiguration(configuration); - return c; - } - - /** - * NOTE: this method MUST be public! - * - *

- * An authenticated user may update a configuration if user identity is same as the target {@link Node}'s user id. - *

- * - * @param configuration {@link Configuration} identifying the target of the user's update operation. - * @param principal Identifies user. - * @return false if user may not update the {@link Node}. - */ - @SuppressWarnings("unused") - public boolean mayUpdate(Configuration configuration, Principal principal){ - Node configNode = nodeDAO.getNode(configuration.getConfigurationNode().getUniqueId()); - return configNode.getUserName().equals(principal.getName()); + return nodeDAO.updateConfiguration(configuration); } } diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/NodeController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/NodeController.java index be62d65422..95ef3471c1 100644 --- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/NodeController.java +++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/NodeController.java @@ -65,7 +65,7 @@ public class NodeController extends BaseController { */ @SuppressWarnings("unused") @PutMapping(value = "/node", produces = JSON) - @PreAuthorize("hasRole(this.roleUser)") + @PreAuthorize("@authorizationHelper.mayCreate(#root)") public Node createNode(@RequestParam(name = "parentNodeId") String parentsUniqueId, @RequestBody final Node node, Principal principal) { diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotController.java index c13da7658a..5654233e3c 100644 --- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotController.java +++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotController.java @@ -47,34 +47,25 @@ public List getAllSnapshots() { } @PutMapping(value = "/snapshot", produces = JSON) - @PreAuthorize("hasRole(this.roleAdmin) or (hasRole(this.roleUser) and this.maySave(#snapshot, #principal))") - public Snapshot saveSnapshot(@RequestParam(value = "parentNodeId") String parentNodeId, + @PreAuthorize("@authorizationHelper.mayCreate(#root)") + public Snapshot createSnapshot(@RequestParam(value = "parentNodeId") String parentNodeId, @RequestBody Snapshot snapshot, Principal principal) { if(!snapshot.getSnapshotNode().getNodeType().equals(NodeType.SNAPSHOT)){ throw new IllegalArgumentException("Snapshot node of wrong type"); } snapshot.getSnapshotNode().setUserName(principal.getName()); - return nodeDAO.saveSnapshot(parentNodeId, snapshot); + return nodeDAO.createSnapshot(parentNodeId, snapshot); } - /** - * NOTE: this method MUST be public! - * - *

- * An authenticated user may save a snapshot, and update if user identity is same as the target's - * snapshot {@link Node}. - *

- * - * @param snapshot {@link Snapshot} identifying the target of the user's update operation. - * @param principal Identifies user. - * @return false if user may not update the {@link Snapshot}. - */ - public boolean maySave(Snapshot snapshot, Principal principal){ - if(snapshot.getSnapshotNode().getUniqueId() == null){ - return true; + @PostMapping(value = "/snapshot", produces = JSON) + @PreAuthorize("@authorizationHelper.mayUpdate(#snapshot, #root)") + public Snapshot updateSnapshot(@RequestBody Snapshot snapshot, + Principal principal) { + if(!snapshot.getSnapshotNode().getNodeType().equals(NodeType.SNAPSHOT)){ + throw new IllegalArgumentException("Snapshot node of wrong type"); } - Node node = nodeDAO.getNode(snapshot.getSnapshotNode().getUniqueId()); - return node.getUserName().equals(principal.getName()); + snapshot.getSnapshotNode().setUserName(principal.getName()); + return nodeDAO.updateSnapshot(snapshot); } } diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/StructureController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/StructureController.java index 91f121e7b8..4bfc3e1cc8 100644 --- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/StructureController.java +++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/StructureController.java @@ -53,7 +53,7 @@ public class StructureController extends BaseController { */ @SuppressWarnings("unused") @PostMapping(value = "/move", produces = JSON) - @PreAuthorize("hasRole(this.roleAdmin)") + @PreAuthorize("@authorizationHelper.mayMoveOrCopy(#root)") public Node moveNodes(@RequestParam(value = "to") String to, @RequestBody List nodes, Principal principal) { @@ -77,6 +77,7 @@ public Node moveNodes(@RequestParam(value = "to") String to, */ @SuppressWarnings("unused") @PostMapping(value = "/copy", produces = JSON) + @PreAuthorize("@authorizationHelper.mayMoveOrCopy(#root)") public Node copyNodes(@RequestParam(value = "to") String to, @RequestBody List nodes, Principal principal) { diff --git a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/TagController.java b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/TagController.java index 45c10ddf71..2c04dac8df 100644 --- a/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/TagController.java +++ b/services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/web/controllers/TagController.java @@ -27,7 +27,6 @@ import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; import java.security.Principal; @@ -55,7 +54,7 @@ public List getTags() { * Adds a {@link Tag} to specified list of target {@link Node}s. The {@link Tag} contained * in tagData must be non-null, and its name must be non-null and non-empty. * - * @param tagData See {@link TagData} + * @param tagData See {@link TagData} * @param principal {@link Principal} of authenticated user. * @return The list of updated {@link Node}s */ diff --git a/services/save-and-restore/src/main/resources/application.properties b/services/save-and-restore/src/main/resources/application.properties index c04f141083..525552efcb 100644 --- a/services/save-and-restore/src/main/resources/application.properties +++ b/services/save-and-restore/src/main/resources/application.properties @@ -84,8 +84,8 @@ demo.readOnly.password=1234 # demo - Hard coded, see WebSecurityConfig class auth.impl = demo -###### Bypass authorization (but not authentication) ####### -auth.bypass = true +###### Bypass authorization (but not authentication!) ####### +authorization.permitall = true ############## Authorization Roles ################ diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/persistence/dao/impl/DAOTestIT.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/persistence/dao/impl/DAOTestIT.java index 2add9a17ee..57cd7446ef 100644 --- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/persistence/dao/impl/DAOTestIT.java +++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/persistence/dao/impl/DAOTestIT.java @@ -271,7 +271,7 @@ public void testGetConfigForSnapshot() { snapshotData.setSnapshotItems(List.of(item1)); snapshot.setSnapshotData(snapshotData); - Node newSnapshot = nodeDAO.saveSnapshot(configuration.getConfigurationNode().getUniqueId(), snapshot).getSnapshotNode(); + Node newSnapshot = nodeDAO.createSnapshot(configuration.getConfigurationNode().getUniqueId(), snapshot).getSnapshotNode(); config = nodeDAO.getParentNode(newSnapshot.getUniqueId()); @@ -306,7 +306,7 @@ public void testDeleteSnapshotReferencedInCompositeSnapshot() { snapshot.setSnapshotData(snapshotData); snapshot.setSnapshotNode(snapshotNode); - snapshot = nodeDAO.saveSnapshot(configNode.getUniqueId(), snapshot); + snapshot = nodeDAO.createSnapshot(configNode.getUniqueId(), snapshot); Node compositeSnapshotNode = Node.builder().name("My composite snapshot").nodeType(NodeType.COMPOSITE_SNAPSHOT).build(); @@ -352,7 +352,7 @@ public void testUpdateCompositeSnapshot() { snapshotData.setSnapshotItems(List.of(SnapshotItem.builder().configPv(ConfigPv.builder().pvName("pv1").build()).build())); snapshot.setSnapshotData(snapshotData); - snapshot = nodeDAO.saveSnapshot(configuration.getConfigurationNode().getUniqueId(), snapshot); + snapshot = nodeDAO.createSnapshot(configuration.getConfigurationNode().getUniqueId(), snapshot); CompositeSnapshot compositeSnapshot = new CompositeSnapshot(); compositeSnapshot.setCompositeSnapshotNode(compositeSnapshotNode); @@ -387,7 +387,7 @@ public void testUpdateCompositeSnapshot() { snapshotData2.setSnapshotItems(List.of(SnapshotItem.builder().configPv(ConfigPv.builder().pvName("pv2").build()).build())); snapshot2.setSnapshotData(snapshotData2); - snapshot2 = nodeDAO.saveSnapshot(configuration2.getConfigurationNode().getUniqueId(), snapshot2); + snapshot2 = nodeDAO.createSnapshot(configuration2.getConfigurationNode().getUniqueId(), snapshot2); compositeSnapshotData = compositeSnapshot.getCompositeSnapshotData(); compositeSnapshotData.setReferencedSnapshotNodes(Arrays.asList(snapshot.getSnapshotNode().getUniqueId(), @@ -443,7 +443,7 @@ public void testGetAllCompositeSnapshotData() { Snapshot snapshot = new Snapshot(); snapshot.setSnapshotNode(snapshotNode); snapshot.setSnapshotData(snapshotData); - nodeDAO.saveSnapshot(configNode.getUniqueId(), snapshot); + nodeDAO.createSnapshot(configNode.getUniqueId(), snapshot); compositeSnapshotData.setReferencedSnapshotNodes(List.of(snapshotNode.getUniqueId())); compositeSnapshot.setCompositeSnapshotData(compositeSnapshotData); @@ -496,7 +496,7 @@ public void testTakeSnapshot() { snapshotData.setSnapshotItems(List.of(item1)); snapshot.setSnapshotData(snapshotData); - snapshot = nodeDAO.saveSnapshot(config.getUniqueId(), snapshot); + snapshot = nodeDAO.createSnapshot(config.getUniqueId(), snapshot); List snapshotItems = snapshot.getSnapshotData().getSnapshotItems(); assertEquals(1, snapshotItems.size()); @@ -514,6 +514,64 @@ public void testTakeSnapshot() { clearAllData(); } + @Test + public void testUpdateSnapshot(){ + Node rootNode = nodeDAO.getRootNode(); + Node folderNode = + Node.builder().name("folder").build(); + + folderNode = nodeDAO.createNode(rootNode.getUniqueId(), folderNode); + + Node config = Node.builder().name("My config 3").nodeType(NodeType.CONFIGURATION).build(); + + Configuration configuration = new Configuration(); + configuration.setConfigurationNode(config); + ConfigurationData configurationData = new ConfigurationData(); + configurationData.setPvList(List.of(ConfigPv.builder() + .pvName("whatever").readbackPvName("readback_whatever").build())); + configuration.setConfigurationData(configurationData); + + configuration = nodeDAO.createConfiguration(folderNode.getUniqueId(), configuration); + + SnapshotItem item1 = SnapshotItem.builder().configPv(configuration.getConfigurationData().getPvList().get(0)) + .value(VDouble.of(7.7, alarm, time, display)).readbackValue(VDouble.of(8.8, alarm, time, display)) + .build(); + + Snapshot snapshot = new Snapshot(); + snapshot.setSnapshotNode(Node.builder() + .name("snapshot name") + .userName("user") + .description("comment") + .build()); + SnapshotData snapshotData = new SnapshotData(); + snapshotData.setSnapshotItems(List.of(item1)); + snapshot.setSnapshotData(snapshotData); + + snapshot = nodeDAO.createSnapshot(config.getUniqueId(), snapshot); + + List snapshotItems = snapshot.getSnapshotData().getSnapshotItems(); + assertEquals(1, snapshotItems.size()); + assertEquals(7.7, ((VDouble) snapshotItems.get(0).getValue()).getValue(), 0.01); + assertEquals(8.8, ((VDouble) snapshotItems.get(0).getReadbackValue()).getValue(), 0.01); + + List snapshots = nodeDAO.getSnapshots(config.getUniqueId()); + assertEquals(1, snapshots.size()); + + Node snapshotNode = snapshot.getSnapshotNode(); + snapshotNode.setName("other snapshot name"); + snapshotNode.setDescription("other comment"); + + snapshot.setSnapshotNode(snapshotNode); + + snapshot = nodeDAO.updateSnapshot(snapshot); + + snapshotNode = snapshot.getSnapshotNode(); + assertEquals("other snapshot name", snapshotNode.getName()); + assertEquals("other comment", snapshotNode.getDescription()); + + clearAllData(); + } + @Test public void testGetSnapshotsNoSnapshots() { assertThrows(NodeNotFoundException.class, @@ -552,7 +610,7 @@ public void testGetSnapshotItemsWithNullPvValues() { snapshotData.setSnapshotItems(List.of(item1)); snapshot.setSnapshotData(snapshotData); - snapshot = nodeDAO.saveSnapshot(config.getUniqueId(), snapshot); + snapshot = nodeDAO.createSnapshot(config.getUniqueId(), snapshot); assertEquals(7.7, ((VDouble) snapshot.getSnapshotData().getSnapshotItems().get(0).getValue()).getValue(), 0.01); assertNull(snapshot.getSnapshotData().getSnapshotItems().get(0).getReadbackValue()); @@ -566,7 +624,7 @@ public void testGetSnapshotItemsWithNullPvValues() { SnapshotData snapshotData1 = new SnapshotData(); snapshot1.setSnapshotData(snapshotData1); - snapshot1 = nodeDAO.saveSnapshot(config.getUniqueId(), snapshot1); + snapshot1 = nodeDAO.createSnapshot(config.getUniqueId(), snapshot1); assertNull(snapshot1.getSnapshotData().getSnapshotItems()); @@ -588,7 +646,7 @@ public void testSnapshotTag() { snapshot.setSnapshotNode(Node.builder().name("testSnapshot").nodeType(NodeType.SNAPSHOT).build()); snapshot.setSnapshotData(new SnapshotData()); - Node snapshotNode = nodeDAO.saveSnapshot(configNode.getUniqueId(), snapshot).getSnapshotNode(); + Node snapshotNode = nodeDAO.createSnapshot(configNode.getUniqueId(), snapshot).getSnapshotNode(); Tag tag = Tag.builder().name("tag1").comment("comment1").userName("testUser1").build(); snapshotNode.addTag(tag); @@ -620,7 +678,7 @@ public void testSnapshotTag() { snapshot2.setSnapshotNode(Node.builder().name("testSnapshot2").nodeType(NodeType.SNAPSHOT).build()); snapshot2.setSnapshotData(new SnapshotData()); - Node snapshotNode2 = nodeDAO.saveSnapshot(configNode.getUniqueId(), snapshot2).getSnapshotNode(); + Node snapshotNode2 = nodeDAO.createSnapshot(configNode.getUniqueId(), snapshot2).getSnapshotNode(); Tag newTag = Tag.builder().name("newtag").comment("comment1").userName("testUser1").build(); @@ -727,7 +785,7 @@ public void testUpdateConfig() throws Exception { snapshotData.setSnapshotItems(Arrays.asList(item1, item2)); snapshot.setSnapshotData(snapshotData); - snapshot = nodeDAO.saveSnapshot(config.getUniqueId(), snapshot); + snapshot = nodeDAO.createSnapshot(config.getUniqueId(), snapshot); // Save another snapshot with same data Snapshot snapshot1 = new Snapshot(); @@ -736,7 +794,7 @@ public void testUpdateConfig() throws Exception { snapshotData1.setSnapshotItems(Arrays.asList(item1, item2)); snapshot1.setSnapshotData(snapshotData1); - nodeDAO.saveSnapshot(config.getUniqueId(), snapshot1); + nodeDAO.createSnapshot(config.getUniqueId(), snapshot1); List snapshotItems = snapshot.getSnapshotData().getSnapshotItems(); @@ -1429,7 +1487,7 @@ public void testCopySnapshotToFolderNotSupported() { snapshotData.setSnapshotItems(List.of(item1)); snapshot.setSnapshotData(snapshotData); - snapshot = nodeDAO.saveSnapshot(configuration.getConfigurationNode().getUniqueId(), snapshot); + snapshot = nodeDAO.createSnapshot(configuration.getConfigurationNode().getUniqueId(), snapshot); String snapshotId = snapshot.getSnapshotNode().getUniqueId(); @@ -1479,7 +1537,7 @@ public void testCopySnapshotToConfiguration() { snapshotData.setSnapshotItems(List.of(item1)); snapshot.setSnapshotData(snapshotData); - snapshot = nodeDAO.saveSnapshot(configuration.getConfigurationNode().getUniqueId(), snapshot); + snapshot = nodeDAO.createSnapshot(configuration.getConfigurationNode().getUniqueId(), snapshot); String snapshotId = snapshot.getSnapshotNode().getUniqueId(); @@ -1527,7 +1585,7 @@ public void testCopySnapshotToConfigurationPvListMismatch() { snapshotData.setSnapshotItems(List.of(item1)); snapshot.setSnapshotData(snapshotData); - snapshot = nodeDAO.saveSnapshot(configuration.getConfigurationNode().getUniqueId(), snapshot); + snapshot = nodeDAO.createSnapshot(configuration.getConfigurationNode().getUniqueId(), snapshot); String snapshotId = snapshot.getSnapshotNode().getUniqueId(); String config2Id = configuration2.getConfigurationNode().getUniqueId(); @@ -1565,7 +1623,7 @@ public void testCopyCompositeSnapshot() { snapshotData.setSnapshotItems(List.of(item1)); snapshot.setSnapshotData(snapshotData); - snapshot = nodeDAO.saveSnapshot(configuration.getConfigurationNode().getUniqueId(), snapshot); + snapshot = nodeDAO.createSnapshot(configuration.getConfigurationNode().getUniqueId(), snapshot); Node folderNode1 = nodeDAO.createNode(rootNode.getUniqueId(), @@ -1625,7 +1683,7 @@ public void testCopyCompositeSnapshotToConfiguration() { snapshotData.setSnapshotItems(List.of(item1)); snapshot.setSnapshotData(snapshotData); - snapshot = nodeDAO.saveSnapshot(configuration.getConfigurationNode().getUniqueId(), snapshot); + snapshot = nodeDAO.createSnapshot(configuration.getConfigurationNode().getUniqueId(), snapshot); Node config2 = Node.builder().name("My config 4").nodeType(NodeType.CONFIGURATION).build(); @@ -1816,7 +1874,7 @@ public void testGetAllSnapshots() { snapshotData.setSnapshotItems(List.of(item1)); snapshot.setSnapshotData(snapshotData); - nodeDAO.saveSnapshot(configuration.getConfigurationNode().getUniqueId(), snapshot); + nodeDAO.createSnapshot(configuration.getConfigurationNode().getUniqueId(), snapshot); List snapshotNodes = nodeDAO.getAllSnapshots(); assertEquals(1, snapshotNodes.size()); @@ -1908,7 +1966,7 @@ public void testCheckForPVNameDuplicates() { SnapshotData snapshotData = new SnapshotData(); snapshotData.setSnapshotItems(Arrays.asList(item1, item2)); snapshot.setSnapshotData(snapshotData); - Node newSnapshot1 = nodeDAO.saveSnapshot(configuration.getConfigurationNode().getUniqueId(), snapshot).getSnapshotNode(); + Node newSnapshot1 = nodeDAO.createSnapshot(configuration.getConfigurationNode().getUniqueId(), snapshot).getSnapshotNode(); //************ End create snapshot1 ************/ //************ Create snapshot2 ************/ @@ -1934,7 +1992,7 @@ public void testCheckForPVNameDuplicates() { SnapshotData snapshotData2 = new SnapshotData(); snapshotData2.setSnapshotItems(Arrays.asList(item12, item22)); snapshot2.setSnapshotData(snapshotData2); - Node newSnapshot2 = nodeDAO.saveSnapshot(configuration2.getConfigurationNode().getUniqueId(), snapshot2).getSnapshotNode(); + Node newSnapshot2 = nodeDAO.createSnapshot(configuration2.getConfigurationNode().getUniqueId(), snapshot2).getSnapshotNode(); //************ End create snapshot2 ************/ List duplicates = nodeDAO.checkForPVNameDuplicates(Arrays.asList(snapshot.getSnapshotNode().getUniqueId(), @@ -1963,7 +2021,7 @@ public void testCheckForPVNameDuplicates() { SnapshotData snapshotData3 = new SnapshotData(); snapshotData3.setSnapshotItems(Collections.singletonList(item13)); snapshot3.setSnapshotData(snapshotData3); - Node newSnapshot3 = nodeDAO.saveSnapshot(configuration3.getConfigurationNode().getUniqueId(), snapshot3).getSnapshotNode(); + Node newSnapshot3 = nodeDAO.createSnapshot(configuration3.getConfigurationNode().getUniqueId(), snapshot3).getSnapshotNode(); //************ End create snapshot3 ************/ duplicates = nodeDAO.checkForPVNameDuplicates(Arrays.asList(snapshot.getSnapshotNode().getUniqueId(), @@ -2030,7 +2088,7 @@ public void testCheckForRejectedReferencedNodesInCompositeSnapshot() { SnapshotData snapshotData = new SnapshotData(); snapshotData.setSnapshotItems(Arrays.asList(item1, item2)); snapshot.setSnapshotData(snapshotData); - Node newSnapshot1 = nodeDAO.saveSnapshot(configuration.getConfigurationNode().getUniqueId(), snapshot).getSnapshotNode(); + Node newSnapshot1 = nodeDAO.createSnapshot(configuration.getConfigurationNode().getUniqueId(), snapshot).getSnapshotNode(); //************ End create snapshot1 ************/ //************ Create snapshot2 ************/ @@ -2056,7 +2114,7 @@ public void testCheckForRejectedReferencedNodesInCompositeSnapshot() { SnapshotData snapshotData2 = new SnapshotData(); snapshotData2.setSnapshotItems(Arrays.asList(item12, item22)); snapshot2.setSnapshotData(snapshotData2); - Node newSnapshot2 = nodeDAO.saveSnapshot(configuration2.getConfigurationNode().getUniqueId(), snapshot2).getSnapshotNode(); + Node newSnapshot2 = nodeDAO.createSnapshot(configuration2.getConfigurationNode().getUniqueId(), snapshot2).getSnapshotNode(); //************ End create snapshot2 ************/ @@ -2116,7 +2174,7 @@ public void testGetSnapshotItemsFromCompositeSnapshot() { SnapshotData snapshotData = new SnapshotData(); snapshotData.setSnapshotItems(Arrays.asList(item1, item2)); snapshot.setSnapshotData(snapshotData); - Node newSnapshot1 = nodeDAO.saveSnapshot(configuration.getConfigurationNode().getUniqueId(), snapshot).getSnapshotNode(); + Node newSnapshot1 = nodeDAO.createSnapshot(configuration.getConfigurationNode().getUniqueId(), snapshot).getSnapshotNode(); //************ End create snapshot1 ************/ //************ Create snapshot2 ************/ @@ -2142,7 +2200,7 @@ public void testGetSnapshotItemsFromCompositeSnapshot() { SnapshotData snapshotData2 = new SnapshotData(); snapshotData2.setSnapshotItems(Arrays.asList(item12, item22)); snapshot2.setSnapshotData(snapshotData2); - Node newSnapshot2 = nodeDAO.saveSnapshot(configuration2.getConfigurationNode().getUniqueId(), snapshot2).getSnapshotNode(); + Node newSnapshot2 = nodeDAO.createSnapshot(configuration2.getConfigurationNode().getUniqueId(), snapshot2).getSnapshotNode(); //************ End create snapshot2 ************/ //************ Create composite snapshot ************/ diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotControllerPermitAllTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotControllerPermitAllTest.java new file mode 100644 index 0000000000..19cec3db71 --- /dev/null +++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotControllerPermitAllTest.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2020 European Spallation Source ERIC. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +package org.phoebus.service.saveandrestore.web.controllers; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.phoebus.applications.saveandrestore.model.CompositeSnapshot; +import org.phoebus.applications.saveandrestore.model.CompositeSnapshotData; +import org.phoebus.applications.saveandrestore.model.Node; +import org.phoebus.applications.saveandrestore.model.NodeType; +import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO; +import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.http.HttpHeaders; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; + +import java.util.List; + +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.when; +import static org.phoebus.service.saveandrestore.web.controllers.BaseController.JSON; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = ControllersTestConfig.class) +@WebMvcTest(CompositeSnapshotController.class) +@TestPropertySource(locations = "classpath:test_application_permit_all.properties") +public class CompositeSnapshotControllerPermitAllTest { + + @Autowired + private String readOnlyAuthorization; + + @Autowired + private NodeDAO nodeDAO; + + @Autowired + private String demoUser; + + @Autowired + private MockMvc mockMvc; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + private static CompositeSnapshot compositeSnapshot; + + @BeforeAll + public static void init() { + compositeSnapshot = new CompositeSnapshot(); + compositeSnapshot.setCompositeSnapshotNode(Node.builder().nodeType(NodeType.COMPOSITE_SNAPSHOT) + .name("name").uniqueId("id").build()); + CompositeSnapshotData compositeSnapshotData = new CompositeSnapshotData(); + compositeSnapshotData.setReferencedSnapshotNodes(List.of("ref")); + compositeSnapshot.setCompositeSnapshotData(compositeSnapshotData); + } + + @Test + public void testCreateCompositeSnapshot() throws Exception { + + when(nodeDAO.createCompositeSnapshot(Mockito.any(String.class), Mockito.any(CompositeSnapshot.class))).thenReturn(compositeSnapshot); + + String compositeSnapshotString = objectMapper.writeValueAsString(compositeSnapshot); + + MockHttpServletRequestBuilder request = put("/composite-snapshot?parentNodeId=id") + .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization) + .contentType(JSON) + .content(compositeSnapshotString); + + MvcResult result = mockMvc.perform(request).andExpect(status().isOk()).andExpect(content().contentType(JSON)) + .andReturn(); + + String s = result.getResponse().getContentAsString(); + // Make sure response contains expected data + objectMapper.readValue(s, CompositeSnapshot.class); + + request = put("/composite-snapshot?parentNodeId=id") + .contentType(JSON) + .content(compositeSnapshotString); + + mockMvc.perform(request).andExpect(status().isUnauthorized()); + reset(nodeDAO); + } + + @Test + public void testUpdateCompositeSnapshot() throws Exception { + + Node node = Node.builder().uniqueId("c").nodeType(NodeType.COMPOSITE_SNAPSHOT).userName(demoUser).build(); + CompositeSnapshot compositeSnapshot1 = new CompositeSnapshot(); + compositeSnapshot1.setCompositeSnapshotNode(node); + + String compositeSnapshotString = objectMapper.writeValueAsString(compositeSnapshot1); + + when(nodeDAO.updateCompositeSnapshot(compositeSnapshot1)).thenReturn(compositeSnapshot1); + when(nodeDAO.getNode("c")).thenReturn(node); + + MockHttpServletRequestBuilder request = post("/composite-snapshot") + .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization) + .contentType(JSON) + .content(compositeSnapshotString); + + MvcResult result = mockMvc.perform(request).andExpect(status().isOk()).andExpect(content().contentType(JSON)) + .andReturn(); + + String s = result.getResponse().getContentAsString(); + // Make sure response contains expected data + objectMapper.readValue(s, CompositeSnapshot.class); + + request = post("/composite-snapshot") + .contentType(JSON) + .content(compositeSnapshotString); + + mockMvc.perform(request).andExpect(status().isUnauthorized()); + + reset(nodeDAO); + } +} diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotControllerTest.java index c5b66238df..8d4162cf24 100644 --- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotControllerTest.java +++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/CompositeSnapshotControllerTest.java @@ -77,7 +77,7 @@ public class CompositeSnapshotControllerTest { @Autowired private MockMvc mockMvc; - private ObjectMapper objectMapper = new ObjectMapper(); + private final ObjectMapper objectMapper = new ObjectMapper(); private static CompositeSnapshot compositeSnapshot; diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationControllerPermitAllTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationControllerPermitAllTest.java new file mode 100644 index 0000000000..502df6b8f3 --- /dev/null +++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationControllerPermitAllTest.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2020 European Spallation Source ERIC. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +package org.phoebus.service.saveandrestore.web.controllers; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.phoebus.applications.saveandrestore.model.Configuration; +import org.phoebus.applications.saveandrestore.model.Node; +import org.phoebus.applications.saveandrestore.model.NodeType; +import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO; +import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.http.HttpHeaders; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; + +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.when; +import static org.phoebus.service.saveandrestore.web.controllers.BaseController.JSON; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = ControllersTestConfig.class) +@WebMvcTest(ConfigurationController.class) +@TestPropertySource(locations = "classpath:test_application_permit_all.properties") +public class ConfigurationControllerPermitAllTest { + + @Autowired + private NodeDAO nodeDAO; + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private String userAuthorization; + + @Autowired + private String readOnlyAuthorization; + + @Autowired + private String demoUser; + + @Test + public void testCreateConfiguration() throws Exception { + + reset(nodeDAO); + + Configuration configuration = new Configuration(); + configuration.setConfigurationNode(Node.builder().build()); + MockHttpServletRequestBuilder request = put("/config?parentNodeId=a") + .header(HttpHeaders.AUTHORIZATION, userAuthorization) + .contentType(JSON).content(objectMapper.writeValueAsString(configuration)); + + mockMvc.perform(request).andExpect(status().isOk()); + + request = put("/config?parentNodeId=a") + .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization) + .contentType(JSON).content(objectMapper.writeValueAsString(configuration)); + + mockMvc.perform(request).andExpect(status().isOk()); + + request = put("/config?parentNodeId=a") + .contentType(JSON).content(objectMapper.writeValueAsString(configuration)); + + mockMvc.perform(request).andExpect(status().isUnauthorized()); + } + + @Test + public void testUpdateConfiguration() throws Exception { + + Node configurationNode = Node.builder().uniqueId("uniqueId").nodeType(NodeType.CONFIGURATION).userName(demoUser).build(); + + Configuration configuration = new Configuration(); + configuration.setConfigurationNode(configurationNode); + + when(nodeDAO.getNode("uniqueId")).thenReturn(configurationNode); + + MockHttpServletRequestBuilder request = post("/config") + .header(HttpHeaders.AUTHORIZATION, userAuthorization) + .contentType(JSON).content(objectMapper.writeValueAsString(configuration)); + + mockMvc.perform(request).andExpect(status().isOk()); + + when(nodeDAO.getNode("uniqueId")).thenReturn(configurationNode); + + request = post("/config") + .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization) + .contentType(JSON).content(objectMapper.writeValueAsString(configuration)); + + mockMvc.perform(request).andExpect(status().isOk()); + + when(nodeDAO.getNode("uniqueId")).thenReturn(configurationNode); + + request = post("/config") + .contentType(JSON).content(objectMapper.writeValueAsString(configuration)); + + mockMvc.perform(request).andExpect(status().isUnauthorized()); + + } +} diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationControllerTest.java new file mode 100644 index 0000000000..4ab5544360 --- /dev/null +++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/ConfigurationControllerTest.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2020 European Spallation Source ERIC. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +package org.phoebus.service.saveandrestore.web.controllers; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.phoebus.applications.saveandrestore.model.Configuration; +import org.phoebus.applications.saveandrestore.model.Node; +import org.phoebus.applications.saveandrestore.model.NodeType; +import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO; +import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.http.HttpHeaders; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; + +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.when; +import static org.phoebus.service.saveandrestore.web.controllers.BaseController.JSON; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = ControllersTestConfig.class) +@WebMvcTest(ConfigurationController.class) +@TestPropertySource(locations = "classpath:test_application.properties") +public class ConfigurationControllerTest { + + @Autowired + private NodeDAO nodeDAO; + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private String userAuthorization; + + @Autowired + private String adminAuthorization; + + @Autowired + private String readOnlyAuthorization; + + @Autowired + private String demoUser; + + @Test + public void testCreateConfiguration() throws Exception { + + reset(nodeDAO); + + Configuration configuration = new Configuration(); + configuration.setConfigurationNode(Node.builder().build()); + MockHttpServletRequestBuilder request = put("/config?parentNodeId=a") + .header(HttpHeaders.AUTHORIZATION, adminAuthorization) + .contentType(JSON).content(objectMapper.writeValueAsString(configuration)); + + mockMvc.perform(request).andExpect(status().isOk()); + + request = put("/config?parentNodeId=a") + .header(HttpHeaders.AUTHORIZATION, userAuthorization) + .contentType(JSON).content(objectMapper.writeValueAsString(configuration)); + + mockMvc.perform(request).andExpect(status().isOk()); + + request = put("/config?parentNodeId=a") + .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization) + .contentType(JSON).content(objectMapper.writeValueAsString(configuration)); + + mockMvc.perform(request).andExpect(status().isForbidden()); + + request = put("/config?parentNodeId=a") + .contentType(JSON).content(objectMapper.writeValueAsString(configuration)); + + mockMvc.perform(request).andExpect(status().isUnauthorized()); + } + + @Test + public void testUpdateConfiguration() throws Exception { + + Node configurationNode = Node.builder().uniqueId("uniqueId").nodeType(NodeType.CONFIGURATION).userName(demoUser).build(); + + Configuration configuration = new Configuration(); + configuration.setConfigurationNode(configurationNode); + + when(nodeDAO.getNode("uniqueId")).thenReturn(configurationNode); + + MockHttpServletRequestBuilder request = post("/config") + .header(HttpHeaders.AUTHORIZATION, userAuthorization) + .contentType(JSON).content(objectMapper.writeValueAsString(configuration)); + + mockMvc.perform(request).andExpect(status().isOk()); + + when(nodeDAO.getNode("uniqueId")).thenReturn(configurationNode); + + request = post("/config") + .header(HttpHeaders.AUTHORIZATION, userAuthorization) + .contentType(JSON).content(objectMapper.writeValueAsString(configuration)); + + mockMvc.perform(request).andExpect(status().isOk()); + + configurationNode = Node.builder().uniqueId("uniqueId").nodeType(NodeType.CONFIGURATION).userName("someUser").build(); + configuration.setConfigurationNode(configurationNode); + + when(nodeDAO.getNode("uniqueId")).thenReturn(configurationNode); + + request = post("/config") + .header(HttpHeaders.AUTHORIZATION, adminAuthorization) + .contentType(JSON).content(objectMapper.writeValueAsString(configuration)); + + mockMvc.perform(request).andExpect(status().isOk()); + + when(nodeDAO.getNode("uniqueId")).thenReturn(configurationNode); + + request = post("/config") + .header(HttpHeaders.AUTHORIZATION, userAuthorization) + .contentType(JSON).content(objectMapper.writeValueAsString(configuration)); + + mockMvc.perform(request).andExpect(status().isForbidden()); + + request = post("/config") + .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization) + .contentType(JSON).content(objectMapper.writeValueAsString(configuration)); + + mockMvc.perform(request).andExpect(status().isForbidden()); + + request = post("/config") + .contentType(JSON).content(objectMapper.writeValueAsString(configuration)); + + mockMvc.perform(request).andExpect(status().isUnauthorized() + ); + + } +} diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/FilterControllerPermitAllTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/FilterControllerPermitAllTest.java new file mode 100644 index 0000000000..0864cf5f2a --- /dev/null +++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/FilterControllerPermitAllTest.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2020 European Spallation Source ERIC. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +package org.phoebus.service.saveandrestore.web.controllers; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.phoebus.applications.saveandrestore.model.search.Filter; +import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO; +import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.http.HttpHeaders; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; + +import java.util.List; + +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.when; +import static org.phoebus.service.saveandrestore.web.controllers.BaseController.JSON; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = ControllersTestConfig.class) +@WebMvcTest(FilterController.class) +@TestPropertySource(locations = "classpath:test_application_permit_all.properties") +public class FilterControllerPermitAllTest { + + @Autowired + private NodeDAO nodeDAO; + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private String userAuthorization; + + @Autowired + private String readOnlyAuthorization; + + @Autowired + private String demoUser; + + @Test + public void testSaveFilter() throws Exception { + + reset(nodeDAO); + + Filter filter = new Filter(); + filter.setName("name"); + filter.setQueryString("query"); + filter.setUser("user"); + + String filterString = objectMapper.writeValueAsString(filter); + + when(nodeDAO.saveFilter(Mockito.any(Filter.class))).thenReturn(filter); + + MockHttpServletRequestBuilder request = put("/filter") + .header(HttpHeaders.AUTHORIZATION, userAuthorization) + .contentType(JSON) + .content(filterString); + + MvcResult result = mockMvc.perform(request).andExpect(status().isOk()).andExpect(content().contentType(JSON)) + .andReturn(); + + String s = result.getResponse().getContentAsString(); + // Make sure response contains expected data + objectMapper.readValue(s, Filter.class); + + request = put("/filter") + .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization) + .contentType(JSON) + .content(filterString); + + mockMvc.perform(request).andExpect(status().isOk()); + + request = put("/filter") + .contentType(JSON) + .content(filterString); + + mockMvc.perform(request).andExpect(status().isUnauthorized()); + } + + @Test + public void testDeleteFilter() throws Exception { + Filter filter = new Filter(); + filter.setName("name"); + filter.setQueryString("query"); + filter.setUser(demoUser); + + when(nodeDAO.getAllFilters()).thenReturn(List.of(filter)); + + MockHttpServletRequestBuilder request = delete("/filter/name") + .header(HttpHeaders.AUTHORIZATION, userAuthorization) + .contentType(JSON); + mockMvc.perform(request).andExpect(status().isOk()); + + request = delete("/filter/name") + .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization) + .contentType(JSON); + mockMvc.perform(request).andExpect(status().isOk()); + + request = delete("/filter/name") + .contentType(JSON); + mockMvc.perform(request).andExpect(status().isUnauthorized()); + } + +} diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerPermitAllTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerPermitAllTest.java new file mode 100644 index 0000000000..1f591c298e --- /dev/null +++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerPermitAllTest.java @@ -0,0 +1,206 @@ +/** + * Copyright (C) 2018 European Spallation Source ERIC. + *

+ * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + *

+ * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package org.phoebus.service.saveandrestore.web.controllers; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.phoebus.applications.saveandrestore.model.Node; +import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO; +import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.http.HttpHeaders; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; + +import java.util.List; + +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.when; +import static org.phoebus.service.saveandrestore.web.controllers.BaseController.JSON; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = ControllersTestConfig.class) +@TestPropertySource(locations = "classpath:test_application_permit_all.properties") +@WebMvcTest(NodeController.class) +/** + * Main purpose of the tests in this class is to verify that REST end points are + * maintained, i.e. that URLs are not changed and that they return the correct + * data. + * + * @author Georg Weiss, European Spallation Source + * + */ +public class NodeControllerPermitAllTest { + + @Autowired + private NodeDAO nodeDAO; + + @Autowired + private MockMvc mockMvc; + + private static Node folderFromClient; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Autowired + private String demoUser; + + @Autowired + private String userAuthorization; + + @Autowired + private String readOnlyAuthorization; + + @BeforeAll + public static void setUp() { + folderFromClient = Node.builder().name("SomeFolder").userName("myusername").uniqueId("11").build(); + } + + @Test + public void testCreateFolder() throws Exception { + + when(nodeDAO.createNode(Mockito.any(String.class), Mockito.any(Node.class))).thenReturn(folderFromClient); + + String content = objectMapper.writeValueAsString(folderFromClient); + + MockHttpServletRequestBuilder request = put("/node?parentNodeId=a") + .header(HttpHeaders.AUTHORIZATION, userAuthorization) + .contentType(JSON) + .content(content); + + MvcResult result = mockMvc.perform(request).andExpect(status().isOk()).andExpect(content().contentType(JSON)) + .andReturn(); + + String s = result.getResponse().getContentAsString(); + // Make sure response contains expected data + objectMapper.readValue(s, Node.class); + + request = put("/node?parentNodeId=a") + .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization) + .contentType(JSON) + .content(content); + mockMvc.perform(request).andExpect(status().isOk()).andExpect(content().contentType(JSON)); + + request = put("/node?parentNodeId=a") + .contentType(JSON) + .content(content); + mockMvc.perform(request).andExpect(status().isUnauthorized()); + } + + @Test + public void testDeleteFolder() throws Exception { + + MockHttpServletRequestBuilder request = + post("/node"); + + mockMvc.perform(request).andExpect(status().isUnauthorized()); + + when(nodeDAO.getNode("a")).thenReturn(Node.builder().uniqueId("a").userName(demoUser).build()); + + request = + delete("/node") + .contentType(JSON).content(objectMapper.writeValueAsString(List.of("a"))) + .header(HttpHeaders.AUTHORIZATION, userAuthorization); + mockMvc.perform(request).andExpect(status().isOk()); + + when(nodeDAO.getNode("a")).thenReturn(Node.builder().uniqueId("a").userName(demoUser).build()); + + request = + delete("/node") + .contentType(JSON).content(objectMapper.writeValueAsString(List.of("a"))) + .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization); + mockMvc.perform(request).andExpect(status().isOk()); + + when(nodeDAO.getNode("a")).thenReturn(Node.builder().uniqueId("a").userName(demoUser).build()); + + request = + delete("/node") + .contentType(JSON).content(objectMapper.writeValueAsString(List.of("a"))) + .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization); + mockMvc.perform(request).andExpect(status().isOk()); + + request = + delete("/node") + .contentType(JSON).content(objectMapper.writeValueAsString(List.of("a"))); + mockMvc.perform(request).andExpect(status().isUnauthorized()); + } + + @Test + public void testGetFolderIllegalArgument() throws Exception { + when(nodeDAO.getNode("a")).thenThrow(IllegalArgumentException.class); + + MockHttpServletRequestBuilder request = get("/node/a"); + + mockMvc.perform(request).andExpect(status().isBadRequest()); + + } + + @Test + public void testUpdateNode() throws Exception { + + reset(nodeDAO); + + Node node = Node.builder().name("foo").uniqueId("a").userName(demoUser).build(); + + when(nodeDAO.getNode("a")).thenReturn(node); + when(nodeDAO.updateNode(Mockito.any(Node.class), Mockito.anyBoolean())).thenReturn(node); + + MockHttpServletRequestBuilder request = post("/node") + .header(HttpHeaders.AUTHORIZATION, userAuthorization) + .param("customTimeForMigration", "false") + .contentType(JSON) + .content(objectMapper.writeValueAsString(node)); + + MvcResult result = mockMvc.perform(request).andExpect(status().isOk()).andExpect(content().contentType(JSON)) + .andReturn(); + + // Make sure response contains expected data + objectMapper.readValue(result.getResponse().getContentAsString(), Node.class); + + node = Node.builder().name("foo").uniqueId("a").userName("notDemoUser").build(); + + when(nodeDAO.getNode("a")).thenReturn(node); + when(nodeDAO.updateNode(Mockito.any(Node.class), Mockito.anyBoolean())).thenReturn(node); + + + request = post("/node") + .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization) + .param("customTimeForMigration", "false") + .contentType(JSON) + .content(objectMapper.writeValueAsString(node)); + mockMvc.perform(request).andExpect(status().isOk()); + + request = post("/node") + .param("customTimeForMigration", "false") + .contentType(JSON) + .content(objectMapper.writeValueAsString(node)); + mockMvc.perform(request).andExpect(status().isUnauthorized()); + } +} diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerTest.java index 41e7ec7878..60bc8075cc 100644 --- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerTest.java +++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/NodeControllerTest.java @@ -24,7 +24,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mockito; -import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.phoebus.applications.saveandrestore.model.Configuration; import org.phoebus.applications.saveandrestore.model.Node; @@ -51,25 +50,21 @@ import static org.mockito.Mockito.reset; import static org.mockito.Mockito.when; import static org.phoebus.service.saveandrestore.web.controllers.BaseController.JSON; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -@ExtendWith(SpringExtension.class) -@ContextConfiguration(classes = ControllersTestConfig.class) -@TestPropertySource(locations = "classpath:test_application.properties") -@WebMvcTest(NodeController.class) /** * Main purpose of the tests in this class is to verify that REST end points are * maintained, i.e. that URLs are not changed and that they return the correct * data. * * @author Georg Weiss, European Spallation Source - * */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = ControllersTestConfig.class) +@TestPropertySource(locations = "classpath:test_application.properties") +@WebMvcTest(NodeController.class) public class NodeControllerTest { @Autowired @@ -82,9 +77,7 @@ public class NodeControllerTest { private static Node config1; - private static Node snapshot; - - private ObjectMapper objectMapper = new ObjectMapper(); + private final ObjectMapper objectMapper = new ObjectMapper(); @Autowired private String demoUser; @@ -108,10 +101,6 @@ public static void setUp() { .userName("myusername").build(); folderFromClient = Node.builder().name("SomeFolder").userName("myusername").uniqueId("11").build(); - - snapshot = Node.builder().nodeType(NodeType.SNAPSHOT).nodeType(NodeType.SNAPSHOT).name("name") - .build(); - } @Test @@ -145,10 +134,10 @@ public void testCreateFolder() throws Exception { .content(content); mockMvc.perform(request).andExpect(status().isForbidden()); - put("/node?parentNodeId=a") + request = put("/node?parentNodeId=a") .contentType(JSON) .content(content); - mockMvc.perform(request).andExpect(status().isForbidden()); + mockMvc.perform(request).andExpect(status().isUnauthorized()); } @Test @@ -176,11 +165,7 @@ public void testCreateConfig() throws Exception { Configuration configuration = new Configuration(); configuration.setConfigurationNode(config); - when(nodeDAO.createConfiguration(Mockito.any(String.class), Mockito.any(Configuration.class))).thenAnswer(new Answer() { - public Configuration answer(InvocationOnMock invocation) { - return configuration; - } - }); + when(nodeDAO.createConfiguration(Mockito.any(String.class), Mockito.any(Configuration.class))).thenAnswer((Answer) invocation -> configuration); MockHttpServletRequestBuilder request = put("/config?parentNodeId=p") .header(HttpHeaders.AUTHORIZATION, userAuthorization) @@ -195,7 +180,7 @@ public Configuration answer(InvocationOnMock invocation) { } @Test - public void testUpdateConfig() throws Exception{ + public void testUpdateConfig() throws Exception { reset(nodeDAO); Node config = Node.builder().nodeType(NodeType.CONFIGURATION).name("config").uniqueId("hhh") @@ -274,11 +259,7 @@ public void testCreateNodeBadRequests() throws Exception { public void testGetChildNodes() throws Exception { reset(nodeDAO); - when(nodeDAO.getChildNodes("p")).thenAnswer(new Answer>() { - public List answer(InvocationOnMock invocation) throws Throwable { - return Arrays.asList(config1); - } - }); + when(nodeDAO.getChildNodes("p")).thenAnswer((Answer>) invocation -> Collections.singletonList(config1)); MockHttpServletRequestBuilder request = get("/node/p/children").contentType(JSON); @@ -286,7 +267,7 @@ public List answer(InvocationOnMock invocation) throws Throwable { .andReturn(); // Make sure response contains expected data - List childNodes = objectMapper.readValue(result.getResponse().getContentAsString(), new TypeReference>() { + List childNodes = objectMapper.readValue(result.getResponse().getContentAsString(), new TypeReference<>() { }); assertEquals(1, childNodes.size()); @@ -322,15 +303,23 @@ public void testGetSnapshotsForNonExistingConfig() throws Exception { mockMvc.perform(request).andExpect(status().isNotFound()); } - /** - * Tests both OK responses and forbidden responses. - * @throws Exception - */ @Test - public void testDeleteFolder() throws Exception { + public void testDeleteSnapshot() throws Exception { + + when(nodeDAO.getNode("a")).thenReturn(Node.builder() + .nodeType(NodeType.SNAPSHOT) + .uniqueId("a").userName(demoUser).build()); + + MockHttpServletRequestBuilder request = + delete("/node") + .contentType(JSON).content(objectMapper.writeValueAsString(List.of("a"))) + .header(HttpHeaders.AUTHORIZATION, userAuthorization); - //.header(HttpHeaders.AUTHORIZATION, userAuthorization) - // .contentType(JSON).content(objectMapper.writeValueAsString(config)); + mockMvc.perform(request).andExpect(status().isOk()); + } + + @Test + public void testDeleteFolder() throws Exception { MockHttpServletRequestBuilder request = post("/node"); @@ -353,6 +342,14 @@ public void testDeleteFolder() throws Exception { .header(HttpHeaders.AUTHORIZATION, userAuthorization); mockMvc.perform(request).andExpect(status().isForbidden()); + when(nodeDAO.getNode("a")).thenReturn(Node.builder().uniqueId("a").userName(demoUser).build()); + + request = + delete("/node") + .contentType(JSON).content(objectMapper.writeValueAsString(List.of("a"))) + .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization); + mockMvc.perform(request).andExpect(status().isForbidden()); + when(nodeDAO.getNode("a")).thenReturn(Node.builder().uniqueId("a").nodeType(NodeType.CONFIGURATION).userName(demoUser).build()); when(nodeDAO.getChildNodes("a")).thenReturn(Collections.emptyList()); @@ -468,79 +465,6 @@ public void testGetNonExistingFolder() throws Exception { } - @Test - public void testMoveNode() throws Exception { - when(nodeDAO.moveNodes(Arrays.asList("a"), "b", demoAdmin)) - .thenReturn(Node.builder().uniqueId("2").uniqueId("a").userName(demoAdmin).build()); - - MockHttpServletRequestBuilder request = post("/move") - .header(HttpHeaders.AUTHORIZATION, adminAuthorization) - .contentType(JSON) - .content(objectMapper.writeValueAsString(Arrays.asList("a"))) - .param("to", "b") - .param("username", "username"); - - MvcResult result = mockMvc.perform(request).andExpect(status().isOk()).andExpect(content().contentType(JSON)) - .andReturn(); - - // Make sure response contains expected data - objectMapper.readValue(result.getResponse().getContentAsString(), Node.class); - - when(nodeDAO.moveNodes(Arrays.asList("a"), "b", demoUser)) - .thenReturn(Node.builder().uniqueId("2").uniqueId("a").userName(demoUser).build()); - - request = post("/move") - .header(HttpHeaders.AUTHORIZATION, userAuthorization) - .contentType(JSON) - .content(objectMapper.writeValueAsString(Arrays.asList("a"))) - .param("to", "b") - .param("username", "username"); - - mockMvc.perform(request).andExpect(status().isForbidden()); - - - request = post("/move") - .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization) - .contentType(JSON) - .content(objectMapper.writeValueAsString(Arrays.asList("a"))) - .param("to", "b") - .param("username", "username"); - - mockMvc.perform(request).andExpect(status().isForbidden()); - - request = post("/move") - .contentType(JSON) - .content(objectMapper.writeValueAsString(Arrays.asList("a"))) - .param("to", "b") - .param("username", "username"); - - mockMvc.perform(request).andExpect(status().isUnauthorized()); - } - - @Test - public void testMoveNodeTargetIdEmpty() throws Exception { - MockHttpServletRequestBuilder request = post("/move") - .header(HttpHeaders.AUTHORIZATION, adminAuthorization) - .contentType(JSON) - .content(objectMapper.writeValueAsString(Arrays.asList("a"))) - .param("to", "") - .param("username", "user"); - - mockMvc.perform(request).andExpect(status().isBadRequest()); - } - - @Test - public void testMoveNodeSourceNodeListEmpty() throws Exception { - MockHttpServletRequestBuilder request = post("/move") - .header(HttpHeaders.AUTHORIZATION, adminAuthorization) - .contentType(JSON) - .content(objectMapper.writeValueAsString(Collections.emptyList())) - .param("to", "targetId") - .param("username", "user"); - - mockMvc.perform(request).andExpect(status().isBadRequest()); - } - @Test public void testGetFolderIllegalArgument() throws Exception { when(nodeDAO.getNode("a")).thenThrow(IllegalArgumentException.class); @@ -605,6 +529,12 @@ public void testUpdateNode() throws Exception { .contentType(JSON) .content(objectMapper.writeValueAsString(node)); mockMvc.perform(request).andExpect(status().isForbidden()); + + request = post("/node") + .param("customTimeForMigration", "false") + .contentType(JSON) + .content(objectMapper.writeValueAsString(node)); + mockMvc.perform(request).andExpect(status().isUnauthorized()); } @Test @@ -617,11 +547,11 @@ public void testGetFromPath() throws Exception { mockMvc.perform(request).andExpect(status().isBadRequest()); Node node = Node.builder().name("name").uniqueId("uniqueId").build(); - when(nodeDAO.getFromPath("/a/b/c")).thenReturn(Arrays.asList(node)); + when(nodeDAO.getFromPath("/a/b/c")).thenReturn(Collections.singletonList(node)); request = get("/path?path=/a/b/c"); MvcResult result = mockMvc.perform(request).andExpect(status().isOk()).andReturn(); - List nodes = objectMapper.readValue(result.getResponse().getContentAsString(), new TypeReference>() { + List nodes = objectMapper.readValue(result.getResponse().getContentAsString(), new TypeReference<>() { }); assertEquals(1, nodes.size()); diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SearchControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SearchControllerTest.java index e5a3948b57..0e6348b93f 100644 --- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SearchControllerTest.java +++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SearchControllerTest.java @@ -36,9 +36,7 @@ import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import javax.ws.rs.core.MultivaluedMap; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -64,7 +62,7 @@ public class SearchControllerTest { private ObjectMapper objectMapper; @Test - public void testSearch() throws Exception{ + public void testSearch() throws Exception { SearchResult searchResult = new SearchResult(); searchResult.setHitCount(1); searchResult.setNodes(List.of(Node.builder().name("node").build())); diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotControllerPermitAllTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotControllerPermitAllTest.java new file mode 100644 index 0000000000..a56b005c04 --- /dev/null +++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotControllerPermitAllTest.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2023 European Spallation Source ERIC. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +package org.phoebus.service.saveandrestore.web.controllers; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.mockito.stubbing.Answer; +import org.phoebus.applications.saveandrestore.model.Node; +import org.phoebus.applications.saveandrestore.model.NodeType; +import org.phoebus.applications.saveandrestore.model.Snapshot; +import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO; +import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.http.HttpHeaders; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; + +import java.util.List; + +import static org.mockito.Mockito.when; +import static org.phoebus.service.saveandrestore.web.controllers.BaseController.JSON; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = ControllersTestConfig.class) +@TestPropertySource(locations = "classpath:test_application_permit_all.properties") +@WebMvcTest(SnapshotController.class) +public class SnapshotControllerPermitAllTest { + + @Autowired + private NodeDAO nodeDAO; + + @Autowired + private String userAuthorization; + + @Autowired + private String readOnlyAuthorization; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Autowired + private MockMvc mockMvc; + + @Autowired + private String demoUser; + + @Test + public void testSaveNewSnapshot() throws Exception { + Node node = Node.builder().uniqueId("uniqueId").nodeType(NodeType.SNAPSHOT).userName(demoUser).build(); + Snapshot snapshot = new Snapshot(); + snapshot.setSnapshotNode(node); + + String snapshotString = objectMapper.writeValueAsString(snapshot); + + when(nodeDAO.getNode("uniqueId")).thenReturn(node); + when(nodeDAO.createSnapshot(Mockito.any(String.class), Mockito.any(Snapshot.class))) + .thenAnswer((Answer) invocation -> snapshot); + + MockHttpServletRequestBuilder request = put("/snapshot?parentNodeId=a") + .header(HttpHeaders.AUTHORIZATION, userAuthorization) + .contentType(JSON) + .content(snapshotString); + + MvcResult result = mockMvc.perform(request).andExpect(status().isOk()).andExpect(content().contentType(JSON)) + .andReturn(); + + // Make sure response contains expected data + objectMapper.readValue(result.getResponse().getContentAsString(), Snapshot.class); + + request = put("/snapshot?parentNodeId=a") + .contentType(JSON) + .content(snapshotString); + mockMvc.perform(request).andExpect(status().isUnauthorized()); + + request = put("/snapshot?parentNodeId=a") + .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization) + .contentType(JSON) + .content(snapshotString); + mockMvc.perform(request).andExpect(status().isOk()); + } + + @Test + public void testDeleteSnapshot() throws Exception{ + + when(nodeDAO.getNode("a")).thenReturn(Node.builder() + .nodeType(NodeType.SNAPSHOT) + .uniqueId("a").userName(demoUser).build()); + + MockHttpServletRequestBuilder request = + delete("/node") + .contentType(JSON).content(objectMapper.writeValueAsString(List.of("a"))) + .header(HttpHeaders.AUTHORIZATION, userAuthorization); + + mockMvc.perform(request).andExpect(status().isOk()); + + request = + delete("/node") + .contentType(JSON).content(objectMapper.writeValueAsString(List.of("a"))) + .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization); + + mockMvc.perform(request).andExpect(status().isOk()); + + request = + delete("/node") + .contentType(JSON).content(objectMapper.writeValueAsString(List.of("a"))); + + mockMvc.perform(request).andExpect(status().isUnauthorized()); + } + + @Test + public void testUpdateSnapshot() throws Exception { + Node node = Node.builder().uniqueId("s").nodeType(NodeType.SNAPSHOT).userName(demoUser).build(); + Snapshot snapshot = new Snapshot(); + snapshot.setSnapshotNode(node); + + String snapshotString = objectMapper.writeValueAsString(snapshot); + + when(nodeDAO.getNode("s")).thenReturn(node); + when(nodeDAO.updateSnapshot(Mockito.any(Snapshot.class))) + .thenAnswer((Answer) invocation -> snapshot); + + MockHttpServletRequestBuilder request = post("/snapshot") + .header(HttpHeaders.AUTHORIZATION, userAuthorization) + .contentType(JSON) + .content(snapshotString); + + MvcResult result = mockMvc.perform(request).andExpect(status().isOk()).andExpect(content().contentType(JSON)) + .andReturn(); + + // Make sure response contains expected data + objectMapper.readValue(result.getResponse().getContentAsString(), Snapshot.class); + + request = put("/snapshot") + .contentType(JSON) + .content(snapshotString); + mockMvc.perform(request).andExpect(status().isUnauthorized()); + + request = post("/snapshot") + .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization) + .contentType(JSON) + .content(snapshotString); + mockMvc.perform(request).andExpect(status().isOk()); + } +} diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotControllerTest.java index b7a8ac4cde..5424ce6965 100644 --- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotControllerTest.java +++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/SnapshotControllerTest.java @@ -39,8 +39,12 @@ import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; +import java.util.List; + import static org.mockito.Mockito.when; import static org.phoebus.service.saveandrestore.web.controllers.BaseController.JSON; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -63,7 +67,7 @@ public class SnapshotControllerTest { @Autowired private String readOnlyAuthorization; - private ObjectMapper objectMapper = new ObjectMapper(); + private final ObjectMapper objectMapper = new ObjectMapper(); @Autowired private MockMvc mockMvc; @@ -74,8 +78,13 @@ public class SnapshotControllerTest { @Test public void testSaveSnapshotWrongNodeType() throws Exception { + + Node node = Node.builder().uniqueId("uniqueId").userName(demoUser).nodeType(NodeType.FOLDER).build(); + Snapshot snapshot = new Snapshot(); - snapshot.setSnapshotNode(Node.builder().nodeType(NodeType.FOLDER).build()); + snapshot.setSnapshotNode(node); + + when(nodeDAO.getNode("uniqueId")).thenReturn(node); MockHttpServletRequestBuilder request = put("/snapshot?parentNodeId=a") .header(HttpHeaders.AUTHORIZATION, userAuthorization) @@ -99,14 +108,15 @@ public void testSaveSnapshotNoParentNodeId() throws Exception { } @Test - public void testSaveNewSnapshot() throws Exception { - Node node = Node.builder().nodeType(NodeType.SNAPSHOT).userName(demoUser).build(); + public void testCreateSnapshot() throws Exception { + Node node = Node.builder().uniqueId("uniqueId").nodeType(NodeType.SNAPSHOT).userName(demoUser).build(); Snapshot snapshot = new Snapshot(); snapshot.setSnapshotNode(node); String snapshotString = objectMapper.writeValueAsString(snapshot); - when(nodeDAO.saveSnapshot(Mockito.any(String.class), Mockito.any(Snapshot.class))) + when(nodeDAO.getNode("uniqueId")).thenReturn(node); + when(nodeDAO.createSnapshot(Mockito.any(String.class), Mockito.any(Snapshot.class))) .thenAnswer((Answer) invocation -> snapshot); MockHttpServletRequestBuilder request = put("/snapshot?parentNodeId=a") @@ -147,10 +157,10 @@ public void testUpdateSnapshot() throws Exception { String snapshotString = objectMapper.writeValueAsString(snapshot); when(nodeDAO.getNode("s")).thenReturn(node); - when(nodeDAO.saveSnapshot(Mockito.any(String.class), Mockito.any(Snapshot.class))) + when(nodeDAO.updateSnapshot(Mockito.any(Snapshot.class))) .thenAnswer((Answer) invocation -> snapshot); - MockHttpServletRequestBuilder request = put("/snapshot?parentNodeId=a") + MockHttpServletRequestBuilder request = post("/snapshot") .header(HttpHeaders.AUTHORIZATION, userAuthorization) .contentType(JSON) .content(snapshotString); @@ -161,21 +171,60 @@ public void testUpdateSnapshot() throws Exception { // Make sure response contains expected data objectMapper.readValue(result.getResponse().getContentAsString(), Snapshot.class); - request = put("/snapshot?parentNodeId=a") + request = put("/snapshot") .contentType(JSON) .content(snapshotString); mockMvc.perform(request).andExpect(status().isUnauthorized()); - request = put("/snapshot?parentNodeId=a") + request = post("/snapshot") .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization) .contentType(JSON) .content(snapshotString); mockMvc.perform(request).andExpect(status().isForbidden()); - request = put("/snapshot?parentNodeId=a") + request = post("/snapshot") .header(HttpHeaders.AUTHORIZATION, adminAuthorization) .contentType(JSON) .content(snapshotString); mockMvc.perform(request).andExpect(status().isOk()); } + + @Test + public void testDeleteSnapshot() throws Exception{ + + when(nodeDAO.getNode("a")).thenReturn(Node.builder() + .nodeType(NodeType.SNAPSHOT) + .uniqueId("a").userName(demoUser).build()); + + MockHttpServletRequestBuilder request = + delete("/node") + .contentType(JSON).content(objectMapper.writeValueAsString(List.of("a"))) + .header(HttpHeaders.AUTHORIZATION, userAuthorization); + + mockMvc.perform(request).andExpect(status().isOk()); + + request = + delete("/node") + .contentType(JSON).content(objectMapper.writeValueAsString(List.of("a"))) + .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization); + + mockMvc.perform(request).andExpect(status().isForbidden()); + + when(nodeDAO.getNode("a")).thenReturn(Node.builder() + .nodeType(NodeType.SNAPSHOT) + .uniqueId("a").userName("otherUser").build()); + + request = + delete("/node") + .contentType(JSON).content(objectMapper.writeValueAsString(List.of("a"))) + .header(HttpHeaders.AUTHORIZATION, adminAuthorization); + + mockMvc.perform(request).andExpect(status().isOk()); + + request = + delete("/node") + .contentType(JSON).content(objectMapper.writeValueAsString(List.of("a"))); + + mockMvc.perform(request).andExpect(status().isUnauthorized()); + } } diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/StructureControllerPermitAllTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/StructureControllerPermitAllTest.java new file mode 100644 index 0000000000..c82b44e5ae --- /dev/null +++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/StructureControllerPermitAllTest.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2023 European Spallation Source ERIC. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +package org.phoebus.service.saveandrestore.web.controllers; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.phoebus.applications.saveandrestore.model.Node; +import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO; +import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.http.HttpHeaders; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; + +import java.util.List; + +import static org.mockito.Mockito.when; +import static org.phoebus.service.saveandrestore.web.controllers.BaseController.JSON; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = ControllersTestConfig.class) +@TestPropertySource(locations = "classpath:test_application_permit_all.properties") +@WebMvcTest(StructureController.class) +public class StructureControllerPermitAllTest { + + @Autowired + private NodeDAO nodeDAO; + + @Autowired + private MockMvc mockMvc; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Autowired + private String demoUser; + + @Autowired + private String demoAdmin; + + @Autowired + private String userAuthorization; + + @Autowired + private String adminAuthorization; + + @Autowired + private String readOnlyAuthorization; + + + @Test + public void testMoveNode() throws Exception { + when(nodeDAO.moveNodes(List.of("a"), "b", demoAdmin)) + .thenReturn(Node.builder().uniqueId("2").uniqueId("a").userName(demoAdmin).build()); + + MockHttpServletRequestBuilder request = post("/move") + .header(HttpHeaders.AUTHORIZATION, adminAuthorization) + .contentType(JSON) + .content(objectMapper.writeValueAsString(List.of("a"))) + .param("to", "b") + .param("username", "username"); + + MvcResult result = mockMvc.perform(request).andExpect(status().isOk()).andExpect(content().contentType(JSON)) + .andReturn(); + + // Make sure response contains expected data + objectMapper.readValue(result.getResponse().getContentAsString(), Node.class); + + when(nodeDAO.moveNodes(List.of("a"), "b", demoUser)) + .thenReturn(Node.builder().uniqueId("2").uniqueId("a").userName(demoUser).build()); + + request = post("/move") + .header(HttpHeaders.AUTHORIZATION, userAuthorization) + .contentType(JSON) + .content(objectMapper.writeValueAsString(List.of("a"))) + .param("to", "b") + .param("username", "username"); + + mockMvc.perform(request).andExpect(status().isOk()); + + + request = post("/move") + .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization) + .contentType(JSON) + .content(objectMapper.writeValueAsString(List.of("a"))) + .param("to", "b") + .param("username", "username"); + + mockMvc.perform(request).andExpect(status().isOk()); + + request = post("/move") + .contentType(JSON) + .content(objectMapper.writeValueAsString(List.of + ("a"))) + .param("to", "b") + .param("username", "username"); + + mockMvc.perform(request).andExpect(status().isUnauthorized()); + } + + @Test + public void testCopyNodes() throws Exception { + MockHttpServletRequestBuilder request = post("/copy") + .header(HttpHeaders.AUTHORIZATION, adminAuthorization) + .contentType(JSON) + .content(objectMapper.writeValueAsString(List.of("a"))) + .param("to", "target"); + mockMvc.perform(request).andExpect(status().isOk()); + + request = post("/copy") + .header(HttpHeaders.AUTHORIZATION, userAuthorization) + .contentType(JSON) + .content(objectMapper.writeValueAsString(List.of("a"))) + .param("to", "target"); + mockMvc.perform(request).andExpect(status().isOk()); + + request = post("/copy") + .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization) + .contentType(JSON) + .content(objectMapper.writeValueAsString(List.of("a"))) + .param("to", "target"); + mockMvc.perform(request).andExpect(status().isOk()); + + request = post("/copy") + .contentType(JSON) + .content(objectMapper.writeValueAsString(List.of("a"))) + .param("to", "target"); + mockMvc.perform(request).andExpect(status().isUnauthorized()); + } +} diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/StructureControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/StructureControllerTest.java new file mode 100644 index 0000000000..df736f8acc --- /dev/null +++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/StructureControllerTest.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2023 European Spallation Source ERIC. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +package org.phoebus.service.saveandrestore.web.controllers; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.phoebus.applications.saveandrestore.model.Node; +import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO; +import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.http.HttpHeaders; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; + +import java.util.Collections; +import java.util.List; + +import static org.mockito.Mockito.when; +import static org.phoebus.service.saveandrestore.web.controllers.BaseController.JSON; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = ControllersTestConfig.class) +@TestPropertySource(locations = "classpath:test_application.properties") +@WebMvcTest(StructureController.class) +public class StructureControllerTest { + + @Autowired + private NodeDAO nodeDAO; + + @Autowired + private MockMvc mockMvc; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Autowired + private String demoUser; + + @Autowired + private String demoAdmin; + + @Autowired + private String userAuthorization; + + @Autowired + private String adminAuthorization; + + @Autowired + private String readOnlyAuthorization; + + + @Test + public void testMoveNode() throws Exception { + when(nodeDAO.moveNodes(List.of("a"), "b", demoAdmin)) + .thenReturn(Node.builder().uniqueId("2").uniqueId("a").userName(demoAdmin).build()); + + MockHttpServletRequestBuilder request = post("/move") + .header(HttpHeaders.AUTHORIZATION, adminAuthorization) + .contentType(JSON) + .content(objectMapper.writeValueAsString(List.of("a"))) + .param("to", "b") + .param("username", "username"); + + MvcResult result = mockMvc.perform(request).andExpect(status().isOk()).andExpect(content().contentType(JSON)) + .andReturn(); + + // Make sure response contains expected data + objectMapper.readValue(result.getResponse().getContentAsString(), Node.class); + + when(nodeDAO.moveNodes(List.of("a"), "b", demoUser)) + .thenReturn(Node.builder().uniqueId("2").uniqueId("a").userName(demoUser).build()); + + request = post("/move") + .header(HttpHeaders.AUTHORIZATION, userAuthorization) + .contentType(JSON) + .content(objectMapper.writeValueAsString(List.of("a"))) + .param("to", "b") + .param("username", "username"); + + mockMvc.perform(request).andExpect(status().isForbidden()); + + + request = post("/move") + .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization) + .contentType(JSON) + .content(objectMapper.writeValueAsString(List.of("a"))) + .param("to", "b") + .param("username", "username"); + + mockMvc.perform(request).andExpect(status().isForbidden()); + + request = post("/move") + .contentType(JSON) + .content(objectMapper.writeValueAsString(List.of("a"))) + .param("to", "b") + .param("username", "username"); + + mockMvc.perform(request).andExpect(status().isUnauthorized()); + } + + @Test + public void testMoveNodeSourceNodeListEmpty() throws Exception { + MockHttpServletRequestBuilder request = post("/move") + .header(HttpHeaders.AUTHORIZATION, adminAuthorization) + .contentType(JSON) + .content(objectMapper.writeValueAsString(Collections.emptyList())) + .param("to", "targetId") + .param("username", "user"); + + mockMvc.perform(request).andExpect(status().isBadRequest()); + } + + @Test + public void testMoveNodeTargetIdEmpty() throws Exception { + MockHttpServletRequestBuilder request = post("/move") + .header(HttpHeaders.AUTHORIZATION, adminAuthorization) + .contentType(JSON) + .content(objectMapper.writeValueAsString(List.of("a"))) + .param("to", "") + .param("username", "user"); + + mockMvc.perform(request).andExpect(status().isBadRequest()); + } + + @Test + public void testCopyNodes() throws Exception { + MockHttpServletRequestBuilder request = post("/copy") + .header(HttpHeaders.AUTHORIZATION, adminAuthorization) + .contentType(JSON) + .content(objectMapper.writeValueAsString(List.of("a"))) + .param("to", "target"); + mockMvc.perform(request).andExpect(status().isOk()); + + request = post("/copy") + .header(HttpHeaders.AUTHORIZATION, userAuthorization) + .contentType(JSON) + .content(objectMapper.writeValueAsString(List.of("a"))) + .param("to", "target"); + mockMvc.perform(request).andExpect(status().isForbidden()); + + request = post("/copy") + .contentType(JSON) + .content(objectMapper.writeValueAsString(List.of("a"))) + .param("to", "target"); + mockMvc.perform(request).andExpect(status().isUnauthorized()); + } + + @Test + public void testCopyNodesBadRequest() throws Exception { + Node node = Node.builder().uniqueId("uniqueId").userName(demoUser).build(); + MockHttpServletRequestBuilder request = post("/copy") + .header(HttpHeaders.AUTHORIZATION, adminAuthorization) + .contentType(JSON) + .content(objectMapper.writeValueAsString(List.of("a"))) + .param("to", ""); + mockMvc.perform(request).andExpect(status().isBadRequest()); + + request = post("/copy") + .header(HttpHeaders.AUTHORIZATION, adminAuthorization) + .contentType(JSON) + .content(objectMapper.writeValueAsString(List.of("a"))); + mockMvc.perform(request).andExpect(status().isBadRequest()); + + request = post("/copy") + .header(HttpHeaders.AUTHORIZATION, adminAuthorization) + .contentType(JSON) + .content(objectMapper.writeValueAsString(Collections.emptyList())) + .param("to", "target"); + mockMvc.perform(request).andExpect(status().isBadRequest()); + } +} diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/TagControllerPermitAllTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/TagControllerPermitAllTest.java new file mode 100644 index 0000000000..bc11cfc137 --- /dev/null +++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/TagControllerPermitAllTest.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2020 European Spallation Source ERIC. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +package org.phoebus.service.saveandrestore.web.controllers; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.phoebus.applications.saveandrestore.model.Node; +import org.phoebus.applications.saveandrestore.model.Tag; +import org.phoebus.applications.saveandrestore.model.TagData; +import org.phoebus.service.saveandrestore.persistence.dao.NodeDAO; +import org.phoebus.service.saveandrestore.web.config.ControllersTestConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.http.HttpHeaders; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; + +import java.util.List; + +import static org.mockito.Mockito.when; +import static org.phoebus.service.saveandrestore.web.controllers.BaseController.JSON; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = ControllersTestConfig.class) +@WebMvcTest(TagController.class) +@TestPropertySource(locations = "classpath:test_application_permit_all.properties") +public class TagControllerPermitAllTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private NodeDAO nodeDAO; + + @Autowired + private String userAuthorization; + + @Autowired + private String readOnlyAuthorization; + + @Autowired + private String demoUser; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + public void testAddTag() throws Exception{ + Tag tag = new Tag(); + tag.setName("tag"); + + Node node = Node.builder().name("name").uniqueId("uniqueId").userName(demoUser).tags(List.of(tag)).build(); + + TagData tagData = new TagData(); + tagData.setTag(tag); + tagData.setUniqueNodeIds(List.of("uniqueId")); + + when(nodeDAO.getNode("uniqueId")).thenReturn(node); + when(nodeDAO.addTag(tagData)).thenReturn(List.of(node)); + + MockHttpServletRequestBuilder request = post("/tags").contentType(JSON) + .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization) + .content(objectMapper.writeValueAsString(tagData)); + MvcResult result = mockMvc.perform(request) + .andExpect(status().isOk()).andExpect(content().contentType(JSON)) + .andReturn(); + + String s = result.getResponse().getContentAsString(); + // Make sure response contains expected data + objectMapper.readValue(s, List.class); + + request = post("/tags").contentType(JSON) + .content(objectMapper.writeValueAsString(tagData)); + mockMvc.perform(request) + .andExpect(status().isUnauthorized()); + } + + @Test + public void testGoldenTag() throws Exception{ + Tag tag = new Tag(); + tag.setName(Tag.GOLDEN); + tag.setUserName(demoUser); + + TagData tagData = new TagData(); + tagData.setTag(tag); + tagData.setUniqueNodeIds(List.of("uniqueId")); + + MockHttpServletRequestBuilder request = post("/tags").contentType(JSON) + .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization) + .content(objectMapper.writeValueAsString(tagData)); + + mockMvc.perform(request).andExpect(status().isOk()); + + request = post("/tags").contentType(JSON) + .header(HttpHeaders.AUTHORIZATION, userAuthorization) + .content(objectMapper.writeValueAsString(tagData)); + + mockMvc.perform(request).andExpect(status().isOk()); + } + + @Test + public void testDeleteTag() throws Exception{ + Tag tag = new Tag(); + tag.setName("tag"); + tag.setUserName(demoUser); + + TagData tagData = new TagData(); + tagData.setTag(tag); + tagData.setUniqueNodeIds(List.of("uniqueId")); + + Node node = Node.builder().name("name").uniqueId("uniqueId").userName("otherUser").tags(List.of(tag)).build(); + + when(nodeDAO.getNode("uniqueId")).thenReturn(node); + + MockHttpServletRequestBuilder request = delete("/tags").contentType(JSON) + .header(HttpHeaders.AUTHORIZATION, userAuthorization) + .content(objectMapper.writeValueAsString(tagData)); + + mockMvc.perform(request) + .andExpect(status().isOk()); + + request = delete("/tags").contentType(JSON) + .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization) + .content(objectMapper.writeValueAsString(tagData)); + + mockMvc.perform(request) + .andExpect(status().isOk()); + + request = delete("/tags").contentType(JSON) + .content(objectMapper.writeValueAsString(tagData)); + + mockMvc.perform(request) + .andExpect(status().isUnauthorized()); + + } +} diff --git a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/TagControllerTest.java b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/TagControllerTest.java index b7bf3a6b1e..b7f1afb71c 100644 --- a/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/TagControllerTest.java +++ b/services/save-and-restore/src/test/java/org/phoebus/service/saveandrestore/web/controllers/TagControllerTest.java @@ -22,7 +22,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mockito; import org.phoebus.applications.saveandrestore.model.Node; import org.phoebus.applications.saveandrestore.model.Tag; import org.phoebus.applications.saveandrestore.model.TagData; @@ -40,11 +39,9 @@ import java.util.List; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.when; import static org.phoebus.service.saveandrestore.web.controllers.BaseController.JSON; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; - -import static org.springframework.test.web.servlet.MockMvc.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -63,13 +60,19 @@ public class TagControllerTest { @Autowired private String userAuthorization; + @Autowired + private String adminAuthorization; + + @Autowired + private String readOnlyAuthorization; + @Autowired private String demoUser; - private ObjectMapper objectMapper = new ObjectMapper(); + private final ObjectMapper objectMapper = new ObjectMapper(); @Test - public void testGetAllTags() throws Exception{ + public void testGetAllTags() throws Exception { Tag tag = new Tag(); tag.setName("tag"); List tags = List.of(tag); @@ -85,7 +88,7 @@ public void testGetAllTags() throws Exception{ } @Test - public void testAddTag() throws Exception{ + public void testAddTag() throws Exception { Tag tag = new Tag(); tag.setName("tag"); @@ -111,7 +114,37 @@ public void testAddTag() throws Exception{ } @Test - public void testAddTagBadData() throws Exception{ + public void testGoldenTag() throws Exception { + Tag tag = new Tag(); + tag.setName(Tag.GOLDEN); + tag.setUserName(demoUser); + + TagData tagData = new TagData(); + tagData.setTag(tag); + tagData.setUniqueNodeIds(List.of("uniqueId")); + + MockHttpServletRequestBuilder request = post("/tags").contentType(JSON) + .header(HttpHeaders.AUTHORIZATION, adminAuthorization) + .content(objectMapper.writeValueAsString(tagData)); + + mockMvc.perform(request).andExpect(status().isOk()); + + request = post("/tags").contentType(JSON) + .header(HttpHeaders.AUTHORIZATION, userAuthorization) + .content(objectMapper.writeValueAsString(tagData)); + + mockMvc.perform(request).andExpect(status().isForbidden()); + + request = post("/tags").contentType(JSON) + .header(HttpHeaders.AUTHORIZATION, readOnlyAuthorization) + .content(objectMapper.writeValueAsString(tagData)); + + mockMvc.perform(request).andExpect(status().isForbidden()); + + } + + @Test + public void testAddTagBadData() throws Exception { TagData tagData = new TagData(); tagData.setUniqueNodeIds(List.of("uniqueId")); @@ -153,7 +186,33 @@ public void testAddTagBadData() throws Exception{ } @Test - public void testDeleteTag() throws Exception{ + public void testDeleteTag() throws Exception { + Tag tag = new Tag(); + tag.setName("tag"); + tag.setUserName(demoUser); + + TagData tagData = new TagData(); + tagData.setTag(tag); + tagData.setUniqueNodeIds(List.of("uniqueId")); + + Node node = Node.builder().name("name").uniqueId("uniqueId").userName("otherUser").tags(List.of(tag)).build(); + + when(nodeDAO.getNode("uniqueId")).thenReturn(node); + + MockHttpServletRequestBuilder request = delete("/tags").contentType(JSON) + .header(HttpHeaders.AUTHORIZATION, userAuthorization) + .content(objectMapper.writeValueAsString(tagData)); + + mockMvc.perform(request) + .andExpect(status().isForbidden()); + + request = delete("/tags").contentType(JSON) + .header(HttpHeaders.AUTHORIZATION, adminAuthorization) + .content(objectMapper.writeValueAsString(tagData)); + + mockMvc.perform(request) + .andExpect(status().isOk() + ); } } diff --git a/services/save-and-restore/src/test/resources/test_application.properties b/services/save-and-restore/src/test/resources/test_application.properties index dc4073e83b..cd97c12504 100644 --- a/services/save-and-restore/src/test/resources/test_application.properties +++ b/services/save-and-restore/src/test/resources/test_application.properties @@ -1,23 +1,2 @@ -logging.level.org.springframework=INFO -app.version=@project.version@ -app.name=@project.name@ - -server.servlet.contextPath=/save-restore - -# Elasticsearch connection parameters -elasticsearch.network.host=localhost -elasticsearch.http.port=9200 - -# Do not change this! -spring.jackson.serialization.write-dates-as-timestamps=false - -# The names of the index to use for save&restore -elasticsearch.tree_node.index=test_saveandrestore_tree -elasticsearch.configuration_node.index=test_saveandrestore_configuration -elasticsearch.snapshot_node.index:test_saveandrestore_snapshot -elasticsearch.composite_snapshot_node.index=test_saveandrestore_composite_snapshot -elasticsearch.filter.index:test_saveandrestore_filter - auth.impl = demo - -authorization.bypass=false +authorization.permitall=false diff --git a/services/save-and-restore/src/test/resources/test_application_permit_all.properties b/services/save-and-restore/src/test/resources/test_application_permit_all.properties new file mode 100644 index 0000000000..7e2f457406 --- /dev/null +++ b/services/save-and-restore/src/test/resources/test_application_permit_all.properties @@ -0,0 +1,2 @@ +auth.impl = demo +authorization.permitall=true