Skip to content
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
10 changes: 10 additions & 0 deletions vaadin-spring-tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,16 @@
<artifactId>vaadin-upload-flow</artifactId>
<version>${vaadin.version}</version>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-dialog-flow</artifactId>
<version>${vaadin.version}</version>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-notification-flow</artifactId>
<version>${vaadin.version}</version>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-ordered-layout-flow</artifactId>
Expand Down
12 changes: 12 additions & 0 deletions vaadin-spring-tests/test-spring-security-flow/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,22 @@
<groupId>com.vaadin</groupId>
<artifactId>vaadin-tabs-flow</artifactId>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-text-field-flow</artifactId>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-upload-flow</artifactId>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-dialog-flow</artifactId>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-notification-flow</artifactId>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-login-flow</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ public class SecurityUtils {

public UserDetails getAuthenticatedUser() {
SecurityContext context = SecurityContextHolder.getContext();
if (context == null) {
throw new IllegalStateException("No security context available");
}
if (context.getAuthentication() == null) {
return null;
}
Object principal = context.getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
UserDetails userDetails = (UserDetails) context.getAuthentication()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,25 @@ public class BankService {
private SecurityUtils utils;

public void applyForLoan() {
applyForLoan(10000);
}

public void applyForHugeLoan() {
applyForLoan(1000000);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
}

private void applyForLoan(int amount) {
String name = utils.getAuthenticatedUser().getUsername();
Optional<Account> acc = accountRepository.findByOwner(name);
if (!acc.isPresent()) {
return;
}
Account account = acc.get();
account.setBalance(account.getBalance().add(new BigDecimal("10000")));
account.setBalance(account.getBalance().add(new BigDecimal(amount)));
accountRepository.save(account);
}

Expand All @@ -34,4 +46,5 @@ public BigDecimal getBalance() {
return accountRepository.findByOwner(name).map(Account::getBalance)
.orElse(null);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2000-2021 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.flow.spring.flowsecurity.views;

import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentEvent;
import com.vaadin.flow.component.ComponentEventBus;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.shared.Registration;

public class Broadcaster {

private static Broadcaster instance = new Broadcaster();

private ComponentEventBus router = new ComponentEventBus(new Div());

public static class RefreshEvent extends ComponentEvent<Component> {
public RefreshEvent() {
super(new Div(), false);
}
}

public static void sendMessage() {
instance.router.fireEvent(new RefreshEvent());
}

public static Registration addMessageListener(
ComponentEventListener<RefreshEvent> listener) {
return instance.router.addListener(RefreshEvent.class, listener);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,34 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.concurrent.Executor;

import javax.annotation.security.PermitAll;

import com.vaadin.flow.component.AttachEvent;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.DetachEvent;
import com.vaadin.flow.component.Text;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.html.H4;
import com.vaadin.flow.component.html.Image;
import com.vaadin.flow.component.html.Paragraph;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.upload.Upload;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.StreamResource;
import com.vaadin.flow.shared.Registration;
import com.vaadin.flow.spring.flowsecurity.SecurityUtils;
import com.vaadin.flow.spring.flowsecurity.service.BankService;

import org.springframework.security.concurrent.DelegatingSecurityContextExecutor;

@Route(value = "private", layout = MainView.class)
@PageTitle("Private View")
@PermitAll
Expand All @@ -28,15 +39,27 @@ public class PrivateView extends VerticalLayout {
private BankService bankService;
private Span balanceSpan = new Span();
private SecurityUtils utils;
private DelegatingSecurityContextExecutor executor;
private Registration registration;

public PrivateView(BankService bankService, SecurityUtils utils) {
public PrivateView(BankService bankService, SecurityUtils utils,
Executor executor) {
this.bankService = bankService;
this.utils = utils;
this.executor = new DelegatingSecurityContextExecutor(executor);

updateBalanceText();
balanceSpan.setId("balanceText");
add(balanceSpan);
add(new Button("Apply for a loan", this::applyForLoan));
add(new Button("Apply for a huge loan",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apply for a huge loan button is not used in a tests. Is it for demo only?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it is for manual testing only. At least for now

this::applyForHugeLoanUsingExecutor));

Button globalRefresh = new Button("Send global refresh event",
e -> Broadcaster.sendMessage());
globalRefresh.setId("sendRefresh");
add(globalRefresh);

Upload upload = new Upload();
ByteArrayOutputStream imageStream = new ByteArrayOutputStream();
upload.setReceiver((filename, mimeType) -> {
Expand All @@ -57,16 +80,57 @@ public PrivateView(BankService bankService, SecurityUtils utils) {
add(upload);
}

@Override
protected void onAttach(AttachEvent attachEvent) {
super.onAttach(attachEvent);
attachEvent.getUI().setPollInterval(1000);
registration = Broadcaster.addMessageListener(e -> {
getUI().get().access(() -> this.updateBalanceText());
});
}

@Override
protected void onDetach(DetachEvent detachEvent) {
super.onDetach(detachEvent);
detachEvent.getUI().setPollInterval(-1);
registration.remove();
}

private void updateBalanceText() {
String name = utils.getAuthenticatedUserInfo().getFullName();
BigDecimal balance = bankService.getBalance();
this.balanceSpan.setText(String.format(
"Hello %s, your bank account balance is $%s.", name, balance));

}

private void applyForLoan(ClickEvent<Button> e) {
bankService.applyForLoan();
updateBalanceText();
}

private void applyForHugeLoanUsingExecutor(ClickEvent<Button> e) {
Dialog waitDialog = new Dialog();
waitDialog.add(new Text("Processing loan application..."));
waitDialog.open();
UI ui = getUI().get();
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
bankService.applyForHugeLoan();
} catch (Exception e) {
getUI().get().access(() -> {
Notification
.show("Application failed: " + e.getMessage());
});

}
ui.access(() -> {
updateBalanceText();
waitDialog.close();
});
}
};
executor.execute(runnable);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright 2000-2021 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.flow.spring.flowsecurity;

import com.vaadin.flow.component.login.testbench.LoginFormElement;
import com.vaadin.flow.component.login.testbench.LoginOverlayElement;
import com.vaadin.flow.testutil.ChromeBrowserTest;
import com.vaadin.testbench.TestBenchElement;

import org.junit.After;
import org.junit.Assert;
import org.openqa.selenium.WebDriver;

public abstract class AbstractIT extends ChromeBrowserTest {

private static final String ROOT_PAGE_HEADER_TEXT = "Welcome to the Java Bank of Vaadin";
private static final String ANOTHER_PUBLIC_PAGE_HEADER_TEXT = "Another public view for testing";
private static final int SERVER_PORT = 8888;

@Override
protected int getDeploymentPort() {
return SERVER_PORT;
}

@Override
protected String getRootURL() {
return super.getRootURL(); // + "/context";
}

@After
public void tearDown() {
if (getDriver() != null) {
checkForBrowserErrors();
}
}

private void checkForBrowserErrors() {
checkLogsForErrors(msg -> {
return msg.contains(
"admin-only/secret.txt - Failed to load resource: the server responded with a status of 403");
});
}

protected void open(String path) {
open(path, getDriver());
}

protected void open(String path, WebDriver driver) {
driver.get(getRootURL() + "/" + path);
}

protected void loginUser() {
login("john", "john");
}

protected void loginAdmin() {
login("emma", "emma");
}

protected void login(String username, String password) {
assertLoginViewShown();

LoginFormElement form = $(LoginOverlayElement.class).first()
.getLoginForm();
form.getUsernameField().setValue(username);
form.getPasswordField().setValue(password);
form.submit();
waitUntilNot(driver -> $(LoginOverlayElement.class).exists());
}

protected void assertLoginViewShown() {
assertPathShown("login");
waitUntil(driver -> $(LoginOverlayElement.class).exists());
}

protected void assertRootPageShown() {
waitUntil(drive -> $("h1").attribute("id", "header").exists());
String headerText = $("h1").id("header").getText();
Assert.assertEquals(ROOT_PAGE_HEADER_TEXT, headerText);
}

protected void assertAnotherPublicPageShown() {
waitUntil(drive -> $("h1").attribute("id", "header").exists());
String headerText = $("h1").id("header").getText();
Assert.assertEquals(ANOTHER_PUBLIC_PAGE_HEADER_TEXT, headerText);
}

protected void assertPrivatePageShown(String fullName) {
assertPathShown("private");
waitUntil(driver -> $("span").attribute("id", "balanceText").exists());
String balance = $("span").id("balanceText").getText();
Assert.assertTrue(balance.startsWith(
"Hello " + fullName + ", your bank account balance is $"));
}

protected void assertAdminPageShown(String fullName) {
assertPathShown("admin");
TestBenchElement welcome = waitUntil(driver -> $("*").id("welcome"));
String welcomeText = welcome.getText();
Assert.assertEquals("Welcome to the admin page, " + fullName,
welcomeText);
}

protected void assertPathShown(String path) {
waitUntil(driver -> driver.getCurrentUrl()
.equals(getRootURL() + "/" + path));
}

}
Loading