Guard

io.eezo.auth.Guard
See theGuard companion object
final class Guard[U]

What identifies the user behind a request, for one way of signing in: a password.

Built from two functions, a URL and how long a sign in should last, and nothing else. find turns the key in the session into the application's own user, and credentials turns an email into the key and the stored hash that go with it. Both are the application's, which is what keeps modules/auth off modules/db: the guard never learns that a table exists, and examples/blog supplies two one-line closures over its own read. Constructing one therefore touches nothing, so a boot that never serves opens no connection.

credentials says where the hash is and stops there; comparing it against what was typed is the guard's, in submitted and nowhere else. An application that did its own comparison had to be trusted to answer an unknown email as slowly as a wrong password, which is not a thing a one-line lookup does by accident, and the plain text had to travel out to it to be compared.

The login it carries is unthrottled, and deliberately: every attempt that decodes, right or wrong, known email or not, costs one bcrypt at Password's shipped strength, so a few hundred guesses a second is also a few hundred quarter seconds of this server's CPU a second. A body that will not decode, an empty password or one past the 72 bytes bcrypt reads, never gets that far. Refusing an address after so many tries is the reverse proxy's job, where the addresses and the rest of the application's traffic already are, and not something a guard can do honestly from inside one process.

One guard per user model. required, only and except are how it becomes a Guarded, and every declaration it makes carries carries, the same three route instances, so an application that guards anything has mounted the page it redirects to without writing a line.

Attributes

Companion
object
Source
Guard.scala
Graph
Supertypes
class Object
trait Matchable
class Any

Members list

Value members

Concrete methods

def current(request: Request): U

Who the guard says is behind this request.

Who the guard says is behind this request.

Throws rather than answering None, because a handler behind a guarded route has already been told there is somebody: the wrapper redirected everyone else before this handler ran. Reaching here with nobody means the route was not guarded, which is a mistake in the route table and not a thing to branch on. A public page that wants to greet a user if there is one is a separate question, and it stays unanswered until a page actually asks it.

A java.lang.IllegalStateException and not one of eezo's HTTP failures, because the mistake is in the route table rather than in the request: no request could have made this handler behave, so there is no status that describes the caller's part in it. It reaches the boundary as anything else outside the sealed set does and becomes a 500, whose detail a developer reads in dev and nobody reads in production.

Attributes

Throws
java.lang.IllegalStateException

when the session names nobody, or names a row that is no longer there.

Source
Guard.scala
def except[A](actions: Action*): GuardedBy[A, U]

Everything but these needs one.

Everything but these needs one.

Attributes

Source
Guard.scala
def logoutForm(request: Request, action: Url = ...): Html

The sign out form, for a page with somewhere to put one, and nothing at all when the browser is not signed in.

The sign out form, for a page with somewhere to put one, and nothing at all when the browser is not signed in.

A form rather than a link, because signing out changes something and Csrf.protect verifies every unsafe verb: the hidden token is the field whose absence is a 403, and it is rendered here so that no application hand builds the one input the logout route will not run without. The empty answer is the other half, and it is why this is a Html rather than a pair of methods: a page splices the result and never asks who is there, which keeps current's rule intact, that asking who is behind a request is for a handler the guard let through.

action defaults to the guard's own logout route, a Url.Mounted that travels with the routes, so a page inside the mount takes the default and names no prefix. A page outside the mount, which is where examples/blog has one to show, points in with a Url.Absolute, the same way every other link from outside a mount does.

Attributes

Source
Guard.scala
def only[A](actions: Action*): GuardedBy[A, U]

Exactly these routes need one; the rest are open.

Exactly these routes need one; the rest are open.

Attributes

Source
Guard.scala
def required[A]: GuardedBy[A, U]

Every route of the thing this declares needs a signed in user.

Every route of the thing this declares needs a signed in user.

A GuardedBy rather than a bare Guarded, which is a subtype and so changes nothing a caller wrote: it is what lets .owning be chained on, because ownership needs the one thing a Guarded deliberately does not carry, a way back to who is signed in. The model type has to be written when the chain continues, required[Post].owning(_.author), because a selector's parameter type cannot be inferred from a chain the expected type reaches only at its end.

Attributes

Source
Guard.scala