Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dialog to help generate Authorization Headers in the Destination STOW Form #226

Merged
merged 4 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -190,7 +187,6 @@ protected DestinationEntity(DestinationType destinationType) {
this.port = 0;
this.useaetdest = Boolean.FALSE;
this.url = "";
this.urlCredentials = "";
this.headers = "";

this.transcodeOnlyUncompressed = false;
Expand All @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -572,7 +559,6 @@ public boolean matchesFilter(String filterText) {
|| contains(hostname, filterText) //
|| equals(port, filterText) //
|| contains(url, filterText) //
|| contains(urlCredentials, filterText) //
|| contains(headers, filterText);
}

Expand All @@ -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="
Expand Down
Original file line number Diff line number Diff line change
@@ -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 = "<key>Authorization</key>";

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<String> 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("<value>");
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("</value>");

this.parentForm.appendToHeaders(sb.toString());
close();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -34,7 +37,7 @@ public class FormSTOW extends VerticalLayout {

private TextField url;

private TextField urlCredentials;
private Button generateAuthorizationHeaderButton;

private TextArea headers;

Expand Down Expand Up @@ -69,20 +72,20 @@ public void init(Binder<DestinationEntity> 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));
Expand All @@ -108,19 +111,33 @@ public void init(Binder<DestinationEntity> 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:\n<key>Authorization</key>\n<value>Bearer 1v1pwxT4Ww4DCFzyaMt0NP</value>");
}
Expand All @@ -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;
}
Expand Down
Loading