Skip to content

Commit

Permalink
#161: bypass ErrorStateHandler$ExceptionsTrace and throw an informati…
Browse files Browse the repository at this point in the history
…ve AccessDeniedException instead. Fixes #161
  • Loading branch information
mvysny committed Aug 1, 2024
1 parent 7348eef commit 526a114
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ import com.vaadin.flow.component.button.Button
import com.vaadin.flow.component.dialog.Dialog
import com.vaadin.flow.component.notification.Notification
import com.vaadin.flow.component.orderedlayout.VerticalLayout
import com.vaadin.flow.router.AccessDeniedException
import com.vaadin.flow.router.BeforeLeaveEvent
import com.vaadin.flow.router.BeforeLeaveObserver
import com.vaadin.flow.router.NotFoundException
import com.vaadin.flow.router.Route
import com.vaadin.flow.router.RouteNotFoundError
import com.vaadin.flow.server.VaadinRequest
import com.vaadin.flow.server.auth.NavigationAccessControl
import java.io.Serializable
Expand Down Expand Up @@ -234,11 +236,22 @@ internal fun DynaNodeGroup.navigatorTest() {
}
group("user logged in") {
test("when access is rejected, default handler redirects to MockRouteNotFoundError") {
MockVaadin.tearDown()
val routes = Routes().autoDiscoverViews("com.github")
routes.errorRoutes.remove(MockRouteAccessDeniedError::class.java)
MockVaadin.setup(routes)

UI.getCurrent().addBeforeEnterListener(SimpleNavigationAccessControl("admin"))
expectThrows<NotFoundException>("No route found for 'testing': Consider adding one of the following annotations to make the view accessible: @AnonymousAllowed, @PermitAll, @RolesAllowed.") {
navigateTo<TestingView>()
}
}
test("when access is rejected, Karibu's MockRouteAccessDeniedError throws AccessDeniedException") {
UI.getCurrent().addBeforeEnterListener(SimpleNavigationAccessControl("admin"))
expectThrows<AccessDeniedException>("Consider adding one of the following annotations to make the view accessible: @AnonymousAllowed, @PermitAll, @RolesAllowed") {
navigateTo<TestingView>()
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ val allViews: Set<Class<out Component>> = setOf<Class<out Component>>(
NavigationPostponeView::class.java,
PreserveOnRefreshView::class.java
)
val allErrorRoutes: Set<Class<out HasErrorParameter<*>>> = setOf(ErrorView::class.java, MockRouteNotFoundError::class.java)
val allErrorRoutes: Set<Class<out HasErrorParameter<*>>> = setOf(ErrorView::class.java, MockRouteNotFoundError::class.java, MockRouteAccessDeniedError::class.java)

@DynaTestDsl
fun DynaNodeGroup.routesTestBatch() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.vaadin.flow.server.startup.RouteRegistryInitializer
import io.github.classgraph.ClassGraph
import io.github.classgraph.ClassInfo
import io.github.classgraph.ScanResult
import org.slf4j.LoggerFactory
import java.io.Serializable
import java.lang.reflect.Field
import java.util.concurrent.atomic.AtomicReference
Expand Down Expand Up @@ -87,6 +88,9 @@ public data class Routes(
if (errorRoutes.any { it != MockRouteNotFoundError::class.java && it.isRouteNotFound }) {
errorRoutes.remove(MockRouteNotFoundError::class.java)
}
if (errorRoutes.any { it != MockRouteAccessDeniedError::class.java && it.isAccessDenied }) {
errorRoutes.remove(MockRouteAccessDeniedError::class.java)
}

println("Auto-discovered views: $this")
}
Expand Down Expand Up @@ -129,6 +133,36 @@ public open class MockRouteNotFoundError: Component(), HasErrorParameter<NotFoun
}
}

/**
* This route gets registered by default in [Routes], so that Karibu-Testing can catch
* any [AccessDeniedException] and throw it immediately, instead of redirecting to [NotFoundException] as the original [RouteAccessDeniedError]
* handler does. Fixes [#161](https://github.com/mvysny/karibu-testing/issues/161).
*/
@Tag(Tag.DIV)
@AnonymousAllowed
public open class MockRouteAccessDeniedError: Component(), HasErrorParameter<AccessDeniedException> {
override fun setErrorParameter(event: BeforeEnterEvent, parameter: ErrorParameter<AccessDeniedException>): Int {
// don't re-throw caughtException - the stacktrace won't point here.
// try our best to preserve the stacktrace, but bail out for custom exceptions
if (parameter.exception.javaClass == AccessDeniedException::class.java || parameter.exception.javaClass == MockAccessDeniedException::class.java) {
throw MockAccessDeniedException(parameter)
}
log.error("!!!! Karibu-Testing: MockRouteAccessDeniedError caught an exception ${parameter.caughtException}: ${parameter.customMessage}")
throw parameter.caughtException!!
}
public companion object {
@JvmStatic
private val log = LoggerFactory.getLogger(MockRouteAccessDeniedError::class.java)
}
}

public class MockAccessDeniedException(override val message: String, cause: Throwable?) : AccessDeniedException() {
public constructor(param: ErrorParameter<AccessDeniedException>) : this(param.customMessage, param.caughtException)
init {
initCause(cause)
}
}

internal fun RouteData.toPrettyString(): String {
val template = template
val path: String = if (template.isNullOrBlank()) "<root>" else "/$template"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.github.mvysny.fakeservlet.FakeRequest
import com.github.mvysny.fakeservlet.FakeResponse
import com.vaadin.flow.component.UI
import com.vaadin.flow.internal.ReflectTools
import com.vaadin.flow.router.AccessDeniedException
import com.vaadin.flow.router.HasErrorParameter
import com.vaadin.flow.router.NotFoundException
import com.vaadin.flow.server.*
Expand Down Expand Up @@ -113,6 +114,8 @@ internal fun Class<*>.getErrorParameterType(): Class<*>? =

internal val Class<*>.isRouteNotFound: Boolean
get() = getErrorParameterType() == NotFoundException::class.java
internal val Class<*>.isAccessDenied: Boolean
get() = getErrorParameterType() == AccessDeniedException::class.java

public val currentRequest: VaadinRequest
get() = VaadinService.getCurrentRequest()
Expand Down

0 comments on commit 526a114

Please sign in to comment.