diff --git a/src/main/java/org/karnak/backend/data/entity/DestinationEntity.java b/src/main/java/org/karnak/backend/data/entity/DestinationEntity.java
index 007143ac..b0a6a238 100644
--- a/src/main/java/org/karnak/backend/data/entity/DestinationEntity.java
+++ b/src/main/java/org/karnak/backend/data/entity/DestinationEntity.java
@@ -141,9 +141,6 @@ public class DestinationEntity implements Serializable {
// mandatory[type=stow]
private String url;
- // credentials of the STOW-RS service (format is "user:password").
- private String urlCredentials;
-
// headers for HTTP request.
private String headers;
@@ -190,7 +187,6 @@ protected DestinationEntity(DestinationType destinationType) {
this.port = 0;
this.useaetdest = Boolean.FALSE;
this.url = "";
- this.urlCredentials = "";
this.headers = "";
this.transcodeOnlyUncompressed = false;
@@ -215,11 +211,10 @@ public static DestinationEntity ofStowEmpty() {
return new DestinationEntity(DestinationType.stow);
}
- public static DestinationEntity ofStow(String description, String url, String urlCredentials, String headers) {
+ public static DestinationEntity ofStow(String description, String url, String headers) {
DestinationEntity destinationEntity = new DestinationEntity(DestinationType.stow);
destinationEntity.setDescription(description);
destinationEntity.setUrl(url);
- destinationEntity.setUrlCredentials(urlCredentials);
destinationEntity.setHeaders(headers);
return destinationEntity;
}
@@ -379,14 +374,6 @@ public void setUrl(String url) {
this.url = url;
}
- public String getUrlCredentials() {
- return urlCredentials;
- }
-
- public void setUrlCredentials(String urlCredentials) {
- this.urlCredentials = urlCredentials;
- }
-
@Size(max = 4096, message = "Headers has more than 4096 characters")
public String getHeaders() {
return headers;
@@ -572,7 +559,6 @@ public boolean matchesFilter(String filterText) {
|| contains(hostname, filterText) //
|| equals(port, filterText) //
|| contains(url, filterText) //
- || contains(urlCredentials, filterText) //
|| contains(headers, filterText);
}
@@ -599,7 +585,7 @@ public String toString() {
+ ", notify=" + notify + ", notifyObjectErrorPrefix=" + notifyObjectErrorPrefix
+ ", notifyObjectPattern=" + notifyObjectPattern + ", notifyObjectValues="
+ notifyObjectValues + ", notifyInterval=" + notifyInterval + ", url=" + url
- + ", urlCredentials=" + urlCredentials + ", headers=" + headers + "]";
+ + ", headers=" + headers + "]";
}
}
return "Destination [id=" + id + ", description=" + description + ", type=" + destinationType + ", notify="
diff --git a/src/main/java/org/karnak/frontend/forwardnode/edit/destination/component/AuthHeadersGenerationDialog.java b/src/main/java/org/karnak/frontend/forwardnode/edit/destination/component/AuthHeadersGenerationDialog.java
new file mode 100644
index 00000000..6037b671
--- /dev/null
+++ b/src/main/java/org/karnak/frontend/forwardnode/edit/destination/component/AuthHeadersGenerationDialog.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2021 Karnak Team and other contributors.
+ *
+ * This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License 2.0 which is available at https://www.eclipse.org/legal/epl-2.0, or the Apache
+ * License, Version 2.0 which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ */
+package org.karnak.frontend.forwardnode.edit.destination.component;
+
+import com.vaadin.flow.component.button.Button;
+import com.vaadin.flow.component.button.ButtonVariant;
+import com.vaadin.flow.component.dialog.Dialog;
+import com.vaadin.flow.component.formlayout.FormLayout;
+import com.vaadin.flow.component.html.Div;
+import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
+import com.vaadin.flow.component.select.Select;
+import com.vaadin.flow.component.textfield.TextField;
+import com.vaadin.flow.dom.Style;
+
+import java.util.Base64;
+
+public class AuthHeadersGenerationDialog extends Dialog {
+
+ public static final String AUTHORIZATION_TAG = "Authorization";
+
+ public static final String TITLE = "Generate Authorization Header";
+
+ private static final String BASIC_AUTH = "Basic Auth";
+
+ private static final String OAUTH2 = "OAuth 2";
+
+ private static final String FIELD_WIDTH = "400px";
+
+ private static final String BUTTON_WIDTH = "200px";
+
+ private Select authTypeSelect;
+
+ private final String[] authTypeSelectValues = { BASIC_AUTH, OAUTH2 };
+
+ private Button cancelButton;
+
+ private Button generateButton;
+
+ private Div divContent;
+
+ private Div divTitle;
+
+ private Div divSelectBox;
+
+ private FormLayout basicForm;
+
+ private TextField basicUsername;
+
+ private TextField basicPassword;
+
+ private FormLayout oauthForm;
+
+ private TextField oauthToken;
+
+ private final FormSTOW parentForm;
+
+ public AuthHeadersGenerationDialog(FormSTOW parentForm) {
+ this.parentForm = parentForm;
+
+ removeAll();
+ setWidth("50%");
+
+ setElement();
+ HorizontalLayout horizontalLayout = new HorizontalLayout(cancelButton, generateButton);
+ horizontalLayout.setWidthFull();
+ horizontalLayout.getStyle().set("justify-content", "flex-end").set("margin-top", "20px");
+ add(divTitle, divSelectBox, divContent, horizontalLayout);
+ }
+
+ private void setElement() {
+ divTitle = new Div();
+ divTitle.setText(TITLE);
+ divTitle.getStyle().set("font-size", "large").set("font-weight", "bolder").set("padding-bottom", "10px");
+
+ divContent = new Div();
+ divSelectBox = new Div();
+ authTypeSelect = new Select<>();
+ authTypeSelect.setItems(authTypeSelectValues);
+ authTypeSelect.setLabel("Authorization Type");
+ authTypeSelect.setErrorMessage("This field is mandatory");
+ authTypeSelect.setWidth(FIELD_WIDTH);
+ authTypeSelect.setEmptySelectionAllowed(false);
+ authTypeSelect.addValueChangeListener(value -> {
+ displayAuthTypeForm(value.getValue());
+ });
+
+ divSelectBox.add(authTypeSelect);
+
+ generateButton = new Button("Generate Headers", event -> {
+ if (validateFields(authTypeSelect.getValue())) {
+ generateAuthHeaders(authTypeSelect.getValue());
+ }
+ });
+ generateButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
+ generateButton.setWidth(BUTTON_WIDTH);
+ cancelButton = new Button("Cancel", event -> close());
+ cancelButton.setWidth(BUTTON_WIDTH);
+
+ buildForms();
+ }
+
+ /**
+ * Display the form according to the Authentication Type selected in the select box
+ * @param value : String corresponding to the Authentication Type chosen in the select box
+ */
+ private void displayAuthTypeForm(String value) {
+ divContent.removeAll();
+ switch(value) {
+ case BASIC_AUTH:
+ divContent.add(basicForm);
+ break;
+ case OAUTH2:
+ divContent.add(oauthForm);
+ break;
+ }
+ }
+
+ /**
+ * Validation method for the authorization type select box, and the fields in the corresponding form
+ * @param authType : String corresponding to the Authorization Type chosen in the select box
+ * @return true if the form is valid, false otherwise
+ */
+ private boolean validateFields(String authType) {
+ // Mark the authorization type select box as invalid if a value is not selected
+ if (authTypeSelect.isEmpty()) {
+ authTypeSelect.setInvalid(true);
+ return false; // set the validation as failed
+ }
+ // Once the authorization type is chosen, ensure that the proper fields are filled for the generation
+ switch (authType) {
+ case BASIC_AUTH:
+ basicUsername.setInvalid(basicUsername.isEmpty());
+ basicPassword.setInvalid(basicPassword.isEmpty());
+ return !(basicUsername.isEmpty() || basicPassword.isEmpty());
+ case OAUTH2:
+ oauthToken.setInvalid(oauthToken.isEmpty());
+ return !(oauthToken.isEmpty());
+ }
+ return false;
+ }
+
+ private void buildForms() {
+ buildBasicAuthForm();
+ buildOAuth2Form();
+ }
+
+ /**
+ * Create the elements necessary to render the Basic Auth form using the basicForm instance
+ */
+ private void buildBasicAuthForm() {
+ basicForm = new FormLayout();
+ basicForm.setWidthFull();
+
+ basicUsername = new TextField();
+ basicUsername.setWidth(FIELD_WIDTH);
+ basicUsername.setRequiredIndicatorVisible(true);
+ basicUsername.setErrorMessage("This field is required");
+ basicUsername.setRequired(true);
+ basicUsername.setLabel("Username");
+
+ basicPassword = new TextField();
+ basicPassword.setWidth(FIELD_WIDTH);
+ basicPassword.setRequiredIndicatorVisible(true);
+ basicPassword.setErrorMessage("This field is required");
+ basicPassword.setRequired(true);
+ basicPassword.setLabel("Password");
+
+ basicForm.add(basicUsername);
+ basicForm.add(basicPassword);
+ }
+
+ /**
+ * Create the elements necessary to render the OAuth 2 form using the oauthForm instance
+ */
+ private void buildOAuth2Form() {
+ oauthForm = new FormLayout();
+ oauthForm.setWidthFull();
+
+ oauthToken = new TextField();
+ oauthToken.setWidth(FIELD_WIDTH);
+ oauthToken.setRequiredIndicatorVisible(true);
+ oauthToken.setErrorMessage("This field is required");
+ oauthToken.setLabel("OAuth 2 Token");
+
+ oauthForm.add(oauthToken);
+ }
+
+ /**
+ * Generate the Authorization Header based on the Authorization type and the data entered.
+ * The headers are appended to the parent form headers field.
+ * @param authType : String corresponding to the Authorization Type chosen in the select box
+ */
+ private void generateAuthHeaders(String authType) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(AUTHORIZATION_TAG);
+ sb.append("\n");
+ sb.append("");
+ switch(authType) {
+ case BASIC_AUTH:
+ String credentials = basicUsername.getValue() + ":" + basicPassword.getValue();
+ sb.append("Basic ");
+ sb.append(Base64.getEncoder().encodeToString(credentials.getBytes()));
+ break;
+ case OAUTH2:
+ sb.append("Bearer ");
+ sb.append(oauthToken.getValue());
+ break;
+ }
+ sb.append("");
+
+ this.parentForm.appendToHeaders(sb.toString());
+ close();
+ }
+}
diff --git a/src/main/java/org/karnak/frontend/forwardnode/edit/destination/component/FormSTOW.java b/src/main/java/org/karnak/frontend/forwardnode/edit/destination/component/FormSTOW.java
index ba6234c0..fc619627 100644
--- a/src/main/java/org/karnak/frontend/forwardnode/edit/destination/component/FormSTOW.java
+++ b/src/main/java/org/karnak/frontend/forwardnode/edit/destination/component/FormSTOW.java
@@ -9,15 +9,18 @@
*/
package org.karnak.frontend.forwardnode.edit.destination.component;
+import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.checkbox.Checkbox;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextArea;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.binder.Binder;
+import com.vaadin.flow.dom.Style;
import org.apache.commons.lang3.StringUtils;
import org.karnak.backend.data.entity.DestinationEntity;
import org.karnak.frontend.component.BoxShadowComponent;
+import org.karnak.frontend.extid.WarningDialog;
import org.karnak.frontend.forwardnode.edit.component.ButtonSaveDeleteCancel;
import org.karnak.frontend.kheops.SwitchingAlbumsView;
import org.karnak.frontend.util.UIS;
@@ -34,7 +37,7 @@ public class FormSTOW extends VerticalLayout {
private TextField url;
- private TextField urlCredentials;
+ private Button generateAuthorizationHeaderButton;
private TextArea headers;
@@ -69,20 +72,20 @@ public void init(Binder binder, ButtonSaveDeleteCancel button
this.tagMorphingComponent.init(this.binder);
this.filterBySOPClassesForm.init(this.binder);
this.destinationCondition.init(binder);
- notificationComponent.init(binder);
- transferSyntaxComponent.init(this.binder);
- transcodeOnlyUncompressedComponent.init(this.binder);
+ this.notificationComponent.init(binder);
+ this.transferSyntaxComponent.init(this.binder);
+ this.transcodeOnlyUncompressedComponent.init(this.binder);
this.description = new TextField("Description");
this.url = new TextField("URL");
- this.urlCredentials = new TextField("URL credentials");
+ this.generateAuthorizationHeaderButton = new Button(AuthHeadersGenerationDialog.TITLE);
this.headers = new TextArea("Headers");
this.switchingAlbumsView = new SwitchingAlbumsView();
this.activate = new Checkbox("Enable destination");
// Define layout
VerticalLayout destinationLayout = new VerticalLayout(UIS.setWidthFull(new HorizontalLayout(description)),
- destinationCondition, UIS.setWidthFull(new HorizontalLayout(url, urlCredentials)),
+ destinationCondition, UIS.setWidthFull(new HorizontalLayout(url, generateAuthorizationHeaderButton)),
UIS.setWidthFull(headers));
VerticalLayout transferLayout = new VerticalLayout(
new HorizontalLayout(transferSyntaxComponent, transcodeOnlyUncompressedComponent));
@@ -108,19 +111,33 @@ public void init(Binder binder, ButtonSaveDeleteCancel button
setElements();
setBinder();
+ configureGenerateHeadersButton();
+ }
+
+ private void configureGenerateHeadersButton() {
+ this.generateAuthorizationHeaderButton.addClickListener(e -> {
+ if (this.headers.getValue().contains(AuthHeadersGenerationDialog.AUTHORIZATION_TAG)) {
+ WarningDialog wd = new WarningDialog("Cannot generate Authorization Header", "The Headers already contain an Authorization tag. Please remove it if you want to generate it.", "Ok");
+ wd.open();
+ } else {
+ AuthHeadersGenerationDialog dialog = new AuthHeadersGenerationDialog(this);
+ dialog.open();
+ }
+ });
}
private void setElements() {
description.setWidth("100%");
- url.setWidth("50%");
+ url.setWidth("70%");
UIS.setTooltip(url, "The destination STOW-RS URL");
- urlCredentials.setWidth("50%");
- UIS.setTooltip(urlCredentials, "Credentials of the STOW-RS service (format is \"user:password\")");
+ generateAuthorizationHeaderButton.setWidth("30%");
+ generateAuthorizationHeaderButton.getStyle().setAlignSelf(Style.AlignSelf.FLEX_END);
headers.setMinHeight("10em");
headers.setWidth("100%");
+ headers.getStyle().set("padding", "0px");
UIS.setTooltip(headers,
"Headers for HTTP request. Example of format:\nAuthorization\nBearer 1v1pwxT4Ww4DCFzyaMt0NP");
}
@@ -136,6 +153,14 @@ private void setBinder() {
binder.bindInstanceFields(this);
}
+ public void appendToHeaders(String value) {
+ String existingHeaders = this.headers.getValue();
+ if (!existingHeaders.isEmpty()) {
+ existingHeaders += "\n";
+ }
+ this.headers.setValue(existingHeaders + value);
+ }
+
public DeIdentificationComponent getDeIdentificationComponent() {
return deIdentificationComponent;
}
diff --git a/src/main/resources/db/changelog/changes/db.changelog-1.4.xml b/src/main/resources/db/changelog/changes/db.changelog-1.4.xml
index 09ce30d7..412ef004 100644
--- a/src/main/resources/db/changelog/changes/db.changelog-1.4.xml
+++ b/src/main/resources/db/changelog/changes/db.changelog-1.4.xml
@@ -18,4 +18,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/java/org/karnak/backend/data/repo/ForwardNodeRepoTest.java b/src/test/java/org/karnak/backend/data/repo/ForwardNodeRepoTest.java
index 6a320e28..0da36901 100644
--- a/src/test/java/org/karnak/backend/data/repo/ForwardNodeRepoTest.java
+++ b/src/test/java/org/karnak/backend/data/repo/ForwardNodeRepoTest.java
@@ -145,7 +145,7 @@ void testInvalidDestinationStow_URL_mandatory() {
ForwardNodeEntity forwardNodeEntity = ForwardNodeEntity.ofEmpty();
forwardNodeEntity.setFwdDescription("description");
forwardNodeEntity.setFwdAeTitle("fwdAeTitle");
- DestinationEntity destinationEntity = DestinationEntity.ofStow("description", null, "urlCredentials",
+ DestinationEntity destinationEntity = DestinationEntity.ofStow("description", null,
"headers");
forwardNodeEntity.addDestination(destinationEntity);
@@ -237,7 +237,7 @@ void testWithDestinationStow() {
ForwardNodeEntity forwardNodeEntity = ForwardNodeEntity.ofEmpty();
forwardNodeEntity.setFwdDescription("description");
forwardNodeEntity.setFwdAeTitle("fwdAeTitle");
- DestinationEntity destinationEntity = DestinationEntity.ofStow("description", "url", "urlCredentials",
+ DestinationEntity destinationEntity = DestinationEntity.ofStow("description", "url",
"headers");
forwardNodeEntity.addDestination(destinationEntity);
entityManager.persistAndFlush(forwardNodeEntity);