Skip to content
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 5 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Commits listed here are skipped by `git blame` (bulk reformats, etc.).
# Enable locally with: git config blame.ignoreRevsFile .git-blame-ignore-revs

# Reformat with scalafmt 3.11.1
5988da35cc34302c23e39527c8569322308535a9
102 changes: 102 additions & 0 deletions .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//description of properties: https://scalameta.org/scalafmt/docs/configuration.html
version = "3.11.1"
runner.dialect = Scala213Source3
maxColumn = 120

continuationIndent {
defnSite = 2
ctorSite = 2
extendSite = 2
withSiteRelativeToExtends = 2
}

align.preset = none

binPack {
parentConstructors = OnelineIfPrimaryOneline
literalsSingleLine = false
}

newlines {
alwaysBeforeMultilineDef = false
afterCurlyLambda = preserve
implicitParamListModifierPrefer = after
penalizeSingleSelectMultiArgList = false
avoidForSimpleOverflow = [punct, tooLong]
afterInfix = some
}

rewrite.rules = [AvoidInfix, SortModifiers, SortImports, PreferCurlyFors, RedundantParens, RedundantBraces]
rewrite.sortModifiers.order = ["override", "private", "protected", "implicit", "final", "sealed", "abstract", "lazy"]
rewrite.redundantBraces {
stringInterpolation = true
generalExpressions = false
methodBodies = true
includeUnitMethods = false
parensForOneLineApply = true
}

trailingCommas = multiple
importSelectors = singleLine

optIn {
breakChainOnFirstMethodDot = false
# - if newlines.source is missing or keep:
# - if true, will keep existing line breaks around annotations
# - if newlines.source is fold:
# - if true, will break before the entity being annotatated
# - will not force break between consecutive annotations
# - if newlines.source is unfold:
# - if true, will break between consecutive annotations
# - will always break before the entity being annotatated
annotationNewlines = false
}

rewrite.neverInfix.excludeFilters = [
until
to
by
eq
ne
"should.*"
"contain.*"
"must.*"
in
ignore
be
taggedAs
thrownBy
synchronized
have
when
size
only
noneOf
oneElementOf
noElementsOf
atLeastOneElementOf
atMostOneElementOf
allElementsOf
inOrderElementsOf
theSameElementsAs
//below extends default
theSameElementsInOrderAs
like
zip
orElse
getOrElse
matchOpt
map
flatMap
]

xmlLiterals.assumeFormatted = true
project.git = true
danglingParentheses.exclude = [
// "class", //with that, trailling comma don't work in class definition
"trait"
]
verticalMultiline {
atDefnSite = true
newlineAfterOpenParen = true
}
10 changes: 6 additions & 4 deletions auth/.js/src/main/scala/io/udash/auth/AuthApplication.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ object AuthApplication {
implicit final class ApplicationAuthExt[HierarchyRoot >: Null <: GState[HierarchyRoot]](
private val application: Application[HierarchyRoot]
) extends AnyVal {
/**
* Adds the default listener of authorization failure in routing (redirects to provided state).

/** Adds the default listener of authorization failure in routing (redirects to provided state).
*
* @param authFailedRedirectState application will redirect user to this state after auth fail
* @param authFailedRedirectState
* application will redirect user to this state after auth fail
*/
def withDefaultRoutingFailureListener(authFailedRedirectState: HierarchyRoot): Application[HierarchyRoot] = {
application.onRoutingFailure {
case _: UnauthorizedException | _: UnauthenticatedException if application.currentState != authFailedRedirectState =>
case _: UnauthorizedException | _: UnauthenticatedException
if application.currentState != authFailedRedirectState =>
application.goTo(authFailedRedirectState)
}
application
Expand Down
18 changes: 11 additions & 7 deletions auth/.js/src/main/scala/io/udash/auth/AuthPresenter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ package io.udash.auth

import io.udash._

/**
* Presenter which check user access in `handleState` method.
/** Presenter which check user access in `handleState` method.
*
* @param permission PermissionCombinator verified against provided `userCtx`.
* @param requireAuthenticated If `true`, the presenter requires `userCtx` to don't be Unauthenticated subclass.
* @param permission
* PermissionCombinator verified against provided `userCtx`.
* @param requireAuthenticated
* If `true`, the presenter requires `userCtx` to don't be Unauthenticated subclass.
*/
abstract class AuthPresenter[S <: State](permission: PermissionCombinator, requireAuthenticated: Boolean = false)
(implicit userCtx: UserCtx)
extends Presenter[S] with AuthRequires {
abstract class AuthPresenter[S <: State](
permission: PermissionCombinator,
requireAuthenticated: Boolean = false,
)(implicit userCtx: UserCtx
) extends Presenter[S]
with AuthRequires {

override def handleState(state: S): Unit = {
require(permission, requireAuthenticated)
Expand Down
20 changes: 17 additions & 3 deletions auth/.js/src/main/scala/io/udash/auth/AuthView.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,31 @@ import io.udash.bindings.modifiers.EmptyModifier
import scalatags.JsDom.all._

trait AuthView {

/** Renders provided `view` only if user context has required permissions. */
def require(permission: PermissionCombinator, requireAuthenticated: Boolean = false)(view: => Modifier)(implicit userCtx: UserCtx): Modifier =
def require(
permission: PermissionCombinator,
requireAuthenticated: Boolean = false,
)(
view: => Modifier
)(implicit userCtx: UserCtx
): Modifier =
requireWithFallback(permission, requireAuthenticated)(view)()

/** Renders provided `view` only if user is authenticated. */
def requireAuthenticated(view: => Modifier)(implicit userCtx: UserCtx): Modifier =
require(AllowAll, requireAuthenticated = true)(view)

/** Renders provided `view` if user context has required permissions or `fallback` view otherwise. */
def requireWithFallback(permission: PermissionCombinator, requireAuthenticated: Boolean = false)
(view: => Modifier)(fallback: => Modifier = new EmptyModifier())(implicit userCtx: UserCtx): Modifier =
def requireWithFallback(
permission: PermissionCombinator,
requireAuthenticated: Boolean = false,
)(
view: => Modifier
)(
fallback: => Modifier = new EmptyModifier()
)(implicit userCtx: UserCtx
): Modifier =
if ((!requireAuthenticated || userCtx.isAuthenticated) && permission.check(userCtx)) view else fallback

/** Renders provided `view` if user is authenticated or `fallback` view otherwise. */
Expand Down
35 changes: 19 additions & 16 deletions auth/.js/src/test/scala/io/udash/auth/AuthApplicationTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,22 @@ import io.udash.core._
import io.udash.routing.RoutingRegistry
import io.udash.testing.AsyncUdashFrontendTest


class AuthApplicationTest extends AsyncUdashFrontendTest with AuthTestUtils with AuthFrontendTestUtils {

import AuthApplication.ApplicationAuthExt
import PermissionCombinator.AllowAll

class TestVF(p: Presenter[TestStates]) extends ViewFactory[TestStates] {
override def create(): (View, Presenter[TestStates]) =
(new View {
(
new View {

import scalatags.JsDom.all._
import scalatags.JsDom.all._

override def getTemplate: Modifier = div().render
}, p)
override def getTemplate: Modifier = div().render
},
p,
)
}

val rr: RoutingRegistry[TestStates] = new RoutingRegistry[TestStates] {
Expand All @@ -40,23 +42,24 @@ class AuthApplicationTest extends AsyncUdashFrontendTest with AuthTestUtils with
state match {
case SomeState => new TestVF(new AuthPresenter[TestStates](P1) {})
case SecondState => new TestVF(new AuthPresenter[TestStates](AllowAll, requireAuthenticated = true) {})
case ThirdState => new TestVF(new Presenter[TestStates] {
override def handleState(state: TestStates): Unit = {}
})
case ThirdState =>
new TestVF(new Presenter[TestStates] {
override def handleState(state: TestStates): Unit = {}
})
}
}

val root = scalatags.JsDom.all.div().render
val app = new Application[TestStates](rr, vfr).withDefaultRoutingFailureListener(ThirdState)
app.run(root)
for {
_ <- retrying { app.currentState should be(ThirdState) }
_ <- retrying(app.currentState should be(ThirdState))
_ = app.goTo(SecondState)
_ <- retrying { app.currentState should be(ThirdState) }
_ <- retrying(app.currentState should be(ThirdState))
_ = app.goTo(SomeState)
_ <- retrying { app.currentState should be(ThirdState) }
_ <- retrying(app.currentState should be(ThirdState))
_ = app.goTo(ThirdState)
r <- retrying { app.currentState should be(ThirdState) }
r <- retrying(app.currentState should be(ThirdState))
} yield r
}

Expand All @@ -75,13 +78,13 @@ class AuthApplicationTest extends AsyncUdashFrontendTest with AuthTestUtils with
val app = new Application[TestStates](rr, vfr).withDefaultRoutingFailureListener(ThirdState)
app.run(root)
for {
_ <- retrying { app.currentState should be(ThirdState) }
_ <- retrying(app.currentState should be(ThirdState))
_ = app.goTo(SecondState)
_ <- retrying { app.currentState should be(ThirdState) }
_ <- retrying(app.currentState should be(ThirdState))
_ = app.goTo(SomeState)
_ <- retrying { app.currentState should be(ThirdState) }
_ <- retrying(app.currentState should be(ThirdState))
_ = app.goTo(ThirdState)
r <- retrying { app.currentState should be(ThirdState) }
r <- retrying(app.currentState should be(ThirdState))
} yield r
}
}
Expand Down
6 changes: 4 additions & 2 deletions auth/.js/src/test/scala/io/udash/auth/AuthPresenterTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ class AuthPresenterTest extends UdashFrontendTest with AuthTestUtils with AuthFr

"AuthPresenter" should {
"throw an exception if user is not authenticated" in {
class SomePresenter extends AuthPresenter[SomeState.type](AllowAll, requireAuthenticated = false)(UnauthenticatedUser)
class SomePresenter
extends AuthPresenter[SomeState.type](AllowAll, requireAuthenticated = false)(UnauthenticatedUser)

val p = new SomePresenter
p.handleState(SomeState)

class SomePresenter2 extends AuthPresenter[SomeState.type](AllowAll, requireAuthenticated = true)(UnauthenticatedUser)
class SomePresenter2
extends AuthPresenter[SomeState.type](AllowAll, requireAuthenticated = true)(UnauthenticatedUser)

val p2 = new SomePresenter2
intercept[UnauthenticatedException] {
Expand Down
37 changes: 18 additions & 19 deletions auth/.js/src/test/scala/io/udash/auth/AuthViewTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ class AuthViewTest extends UdashFrontendTest with AuthTestUtils {
implicit val user = User(Set(P1, P2))

val el = div(
require(P1.and(P2)) { "1" },
require(P1.and(P3)) { "2" },
require(P1.or(P3)) { "3" },
require(P3.or(P1)) { "4" },
require(P1) { "5" },
require(P3) { "6" },
require(P3.and(P1.or(P2))) { "7" },
require(P1.and(P3.or(P2))) { "8" },
require(P1.and(P2.or(P3))) { "9" }
require(P1.and(P2))("1"),
require(P1.and(P3))("2"),
require(P1.or(P3))("3"),
require(P3.or(P1))("4"),
require(P1)("5"),
require(P3)("6"),
require(P3.and(P1.or(P2)))("7"),
require(P1.and(P3.or(P2)))("8"),
require(P1.and(P2.or(P3)))("9"),
).render

el.textContent should be("134589")
Expand All @@ -33,15 +33,15 @@ class AuthViewTest extends UdashFrontendTest with AuthTestUtils {
implicit val user = UnauthenticatedUser

val el = div(
require(AllowAll, requireAuthenticated = true) { "1" },
require(AllowAll) { "2" },
require(AllowAll, requireAuthenticated = true) { "3" },
require(AllowAll) { "4" },
require(AllowAll, requireAuthenticated = true) { "5" },
require(P1) { "6" },
require(AllowAll, requireAuthenticated = true) { "7" },
require(AllowAll) { "8" },
require(AllowAll, requireAuthenticated = true) { "9" }
require(AllowAll, requireAuthenticated = true)("1"),
require(AllowAll)("2"),
require(AllowAll, requireAuthenticated = true)("3"),
require(AllowAll)("4"),
require(AllowAll, requireAuthenticated = true)("5"),
require(P1)("6"),
require(AllowAll, requireAuthenticated = true)("7"),
require(AllowAll)("8"),
require(AllowAll, requireAuthenticated = true)("9"),
).render

el.textContent should be("248")
Expand All @@ -52,7 +52,6 @@ class AuthViewTest extends UdashFrontendTest with AuthTestUtils {
import PermissionCombinator.AllowAll
import scalatags.JsDom.all._


implicit val user = User(Set(P1, P2))

val el = div(
Expand Down
4 changes: 3 additions & 1 deletion auth/src/main/scala/io/udash/auth/AuthRequires.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package io.udash.auth

trait AuthRequires {

/** Checks if user context has required permissions. */
def require(permission: PermissionCombinator, requireAuthenticated: Boolean = false)(implicit userCtx: UserCtx): Unit = {
def require(permission: PermissionCombinator, requireAuthenticated: Boolean = false)(implicit userCtx: UserCtx)
: Unit = {
if (requireAuthenticated && !userCtx.isAuthenticated) throw new UnauthenticatedException()
if (!permission.check(userCtx)) throw new UnauthorizedException()
}
Expand Down
6 changes: 3 additions & 3 deletions auth/src/main/scala/io/udash/auth/Permission.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.udash.auth

/**
* Base class for permissions used with PermissionControl. Permissions are compared by ID.
/** Base class for permissions used with PermissionControl. Permissions are compared by ID.
*/
trait Permission {
def id: PermissionId
Expand All @@ -19,6 +18,7 @@ trait Permission {
}

object Permission {

/** Single permission as a combinator resolved implicitly. */
implicit final class Single(private val permission: Permission) extends AnyVal with PermissionCombinator {
override def check(ctx: UserCtx): Boolean =
Expand All @@ -27,4 +27,4 @@ object Permission {
override def toString: String =
permission.toString
}
}
}
Loading
Loading