From a2b07392fcf7b1a7da78c3e7849bff043a03653f Mon Sep 17 00:00:00 2001 From: Tulio Garcia Date: Tue, 12 Feb 2019 16:46:15 +0200 Subject: [PATCH] Different approach to fix secured issue (#734) * Created BakeryUI with listener * Added test for access denied view --- .../bakery/app/security/SecurityUtils.java | 16 +++++++- .../vaadin/starter/bakery/ui/BakeryUI.java | 27 +++++++++++++ .../vaadin/starter/bakery/ui/MainView.java | 35 ++++++---------- .../ui/views/errors/AccessDeniedView.java | 2 + src/main/resources/application.properties | 2 +- .../src/views/errors/access-denied-view.html | 1 + .../starter/bakery/testbench/UsersViewIT.java | 40 +++++++++++++++---- 7 files changed, 89 insertions(+), 34 deletions(-) create mode 100644 src/main/java/com/vaadin/starter/bakery/ui/BakeryUI.java diff --git a/src/main/java/com/vaadin/starter/bakery/app/security/SecurityUtils.java b/src/main/java/com/vaadin/starter/bakery/app/security/SecurityUtils.java index 7e28194ab..e3f35f8ec 100644 --- a/src/main/java/com/vaadin/starter/bakery/app/security/SecurityUtils.java +++ b/src/main/java/com/vaadin/starter/bakery/app/security/SecurityUtils.java @@ -17,6 +17,9 @@ import com.vaadin.flow.server.ServletHelper.RequestType; import com.vaadin.flow.shared.ApplicationConstants; +import com.vaadin.starter.bakery.ui.views.errors.AccessDeniedView; +import com.vaadin.starter.bakery.ui.views.errors.CustomRouteNotFoundError; +import com.vaadin.starter.bakery.ui.views.login.LoginView; /** * SecurityUtils takes care of all such static operations that have to do with @@ -50,13 +53,22 @@ public static String getUsername() { * Checks if access is granted for the current user for the given secured view, * defined by the view class. * - * @param securedClass + * @param securedClass View class * @return true if access is granted, false otherwise. */ public static boolean isAccessGranted(Class securedClass) { + final boolean publicView = LoginView.class.equals(securedClass) + || AccessDeniedView.class.equals(securedClass) + || CustomRouteNotFoundError.class.equals(securedClass); + + // Always allow access to public views + if (publicView) { + return true; + } + Authentication userAuthentication = SecurityContextHolder.getContext().getAuthentication(); - // All views require authentication + // All other views require authentication if (!isUserLoggedIn(userAuthentication)) { return false; } diff --git a/src/main/java/com/vaadin/starter/bakery/ui/BakeryUI.java b/src/main/java/com/vaadin/starter/bakery/ui/BakeryUI.java new file mode 100644 index 000000000..422878876 --- /dev/null +++ b/src/main/java/com/vaadin/starter/bakery/ui/BakeryUI.java @@ -0,0 +1,27 @@ +package com.vaadin.starter.bakery.ui; + +import com.vaadin.flow.component.UI; +import com.vaadin.flow.server.VaadinRequest; +import com.vaadin.starter.bakery.app.security.SecurityUtils; +import com.vaadin.starter.bakery.ui.components.OfflineBanner; +import com.vaadin.starter.bakery.ui.exceptions.AccessDeniedException; +import com.vaadin.starter.bakery.ui.views.login.LoginView; + +public class BakeryUI extends UI { + + protected void init(VaadinRequest request) { + add(new OfflineBanner()); + addBeforeEnterListener(event -> { + final boolean accessGranted = + SecurityUtils.isAccessGranted(event.getNavigationTarget()); + if (!accessGranted) { + if (SecurityUtils.isUserLoggedIn()) { + event.rerouteToError(AccessDeniedException.class); + } + else { + event.rerouteTo(LoginView.class); + } + } + }); + } +} diff --git a/src/main/java/com/vaadin/starter/bakery/ui/MainView.java b/src/main/java/com/vaadin/starter/bakery/ui/MainView.java index 3b719e31b..6ae884a14 100644 --- a/src/main/java/com/vaadin/starter/bakery/ui/MainView.java +++ b/src/main/java/com/vaadin/starter/bakery/ui/MainView.java @@ -1,5 +1,16 @@ package com.vaadin.starter.bakery.ui; +import static com.vaadin.starter.bakery.ui.utils.BakeryConst.PAGE_DASHBOARD; +import static com.vaadin.starter.bakery.ui.utils.BakeryConst.PAGE_PRODUCTS; +import static com.vaadin.starter.bakery.ui.utils.BakeryConst.PAGE_STOREFRONT; +import static com.vaadin.starter.bakery.ui.utils.BakeryConst.PAGE_USERS; +import static com.vaadin.starter.bakery.ui.utils.BakeryConst.TITLE_DASHBOARD; +import static com.vaadin.starter.bakery.ui.utils.BakeryConst.TITLE_LOGOUT; +import static com.vaadin.starter.bakery.ui.utils.BakeryConst.TITLE_PRODUCTS; +import static com.vaadin.starter.bakery.ui.utils.BakeryConst.TITLE_STOREFRONT; +import static com.vaadin.starter.bakery.ui.utils.BakeryConst.TITLE_USERS; +import static com.vaadin.starter.bakery.ui.utils.BakeryConst.VIEWPORT; + import com.vaadin.flow.component.HasElement; import com.vaadin.flow.component.UI; import com.vaadin.flow.component.applayout.AbstractAppRouterLayout; @@ -10,19 +21,12 @@ import com.vaadin.flow.component.html.Span; import com.vaadin.flow.component.icon.VaadinIcon; import com.vaadin.flow.component.page.Viewport; -import com.vaadin.flow.router.BeforeEnterEvent; -import com.vaadin.flow.router.BeforeEnterObserver; import com.vaadin.flow.server.PWA; import com.vaadin.starter.bakery.app.security.SecurityUtils; import com.vaadin.starter.bakery.ui.components.BakeryCookieConsent; -import com.vaadin.starter.bakery.ui.components.OfflineBanner; -import com.vaadin.starter.bakery.ui.exceptions.AccessDeniedException; import com.vaadin.starter.bakery.ui.views.HasConfirmation; import com.vaadin.starter.bakery.ui.views.admin.products.ProductsView; import com.vaadin.starter.bakery.ui.views.admin.users.UsersView; -import com.vaadin.starter.bakery.ui.views.login.LoginView; - -import static com.vaadin.starter.bakery.ui.utils.BakeryConst.*; @Viewport(VIEWPORT) @@ -31,7 +35,7 @@ backgroundColor = "#227aef", themeColor = "#227aef", offlinePath = "offline-page.html", offlineResources = {"images/offline-login-banner.jpg"}) -public class MainView extends AbstractAppRouterLayout implements BeforeEnterObserver { +public class MainView extends AbstractAppRouterLayout { private final ConfirmDialog confirmDialog; @@ -43,7 +47,6 @@ public MainView() { getElement().appendChild(confirmDialog.getElement()); getElement().appendChild(new BakeryCookieConsent().getElement()); - getElement().addAttachListener(e -> UI.getCurrent().add(new OfflineBanner())); } @Override @@ -78,20 +81,6 @@ private void setMenuItem(AppLayoutMenu menu, AppLayoutMenuItem menuItem) { menu.addMenuItem(menuItem); } - @Override - public void beforeEnter(BeforeEnterEvent event) { - final boolean accessGranted = - SecurityUtils.isAccessGranted(event.getNavigationTarget()); - if (!accessGranted) { - if (SecurityUtils.isUserLoggedIn()) { - event.rerouteToError(AccessDeniedException.class); - } - else { - event.rerouteTo(LoginView.class); - } - } - } - @Override public void showRouterLayoutContent(HasElement content) { super.showRouterLayoutContent(content); diff --git a/src/main/java/com/vaadin/starter/bakery/ui/views/errors/AccessDeniedView.java b/src/main/java/com/vaadin/starter/bakery/ui/views/errors/AccessDeniedView.java index 241d406e4..9c3855ba6 100644 --- a/src/main/java/com/vaadin/starter/bakery/ui/views/errors/AccessDeniedView.java +++ b/src/main/java/com/vaadin/starter/bakery/ui/views/errors/AccessDeniedView.java @@ -10,6 +10,7 @@ import com.vaadin.flow.router.HasErrorParameter; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.ParentLayout; +import com.vaadin.flow.router.Route; import com.vaadin.flow.templatemodel.TemplateModel; import com.vaadin.starter.bakery.ui.MainView; import com.vaadin.starter.bakery.ui.exceptions.AccessDeniedException; @@ -19,6 +20,7 @@ @HtmlImport("src/views/errors/access-denied-view.html") @ParentLayout(MainView.class) @PageTitle(BakeryConst.TITLE_ACCESS_DENIED) +@Route public class AccessDeniedView extends PolymerTemplate implements HasErrorParameter { @Override diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index f093674ac..9ea76fa58 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -3,7 +3,7 @@ server.compression.mime-types=application/json,application/xml,text/html,text/xm security.basic.enabled=false server.tomcat.uri-encoding=UTF-8 spring.jackson.serialization.write_dates_as_timestamps=false - +server.servlet.context-parameters.UI=com.vaadin.starter.bakery.ui.BakeryUI # Comment out if using anything else than H2 (e.g. MySQL or PostgreSQL) spring.jpa.database-platform=org.hibernate.dialect.H2Dialect diff --git a/src/main/resources/static/frontend/src/views/errors/access-denied-view.html b/src/main/resources/static/frontend/src/views/errors/access-denied-view.html index b5acb9f3e..58b4ba75f 100644 --- a/src/main/resources/static/frontend/src/views/errors/access-denied-view.html +++ b/src/main/resources/static/frontend/src/views/errors/access-denied-view.html @@ -1,4 +1,5 @@ +