Steven Grimm
2006-08-01 18:17:18 UTC
Your suggestion of making authentication always 'fall through' might
be workable, but I'm not entirely sure that it's really what is needed.
Now that we're on the same page about what I'm trying to achieve, Ibe workable, but I'm not entirely sure that it's really what is needed.
suspect you'll be able to come up with something cleaner than that. It
still feels to me like I want to be somewhere between Identified and
Authenticated, but given that the user data needs to be stored somewhere
on the back end, you kind of need the hierarchy of backend storage types
that the Authenticated subclasses give you (memory vs. db, purged vs.
not, etc.)
However, maybe that's a sign that the stuff in the Authenticated
subclasses isn't actually in the right place to begin with. Consider it
this way: Authenticated provides the logic to do different things
depending on whether the user has a valid authenticated session, and the
logic to manage that session (cookie management, etc.) That is its core
purpose. The user and session data stores are really *inputs* to the
authentication logic (or services it uses, if you prefer). Subclasses of
Authenticated should be about changing the authentication logic, not
changing which persistence service holds the user and session data. The
persistence service is only loosely coupled to the authentication logic.
The loose coupling I'm talking about is almost there already, in the
form of the *Deployer classes, but the problem is that the selection of
a Deployer is compiled into the Authentication class hierarchy rather
than determined as an independent configuration setting. That means you
can't subclass the authentication logic without making a parallel
hierarchy of subclasses for the different combinations of storage types.
So what I'm driving at is that the selection of the Deployer class
should be purely a runtime configuration thing, and Authenticated should
have none of its existing subclasses.
I would add one aspect to the Deployer setup, though: the various stores
should be configured separately rather than tied together. That is,
there should be a SessionDeployer, CredentialsDeployer, and
RememberDeployer interfaces, or whatever names make sense. The "mixed"
case would no longer be its own concrete class, but just a configuration
with a memory-based SessionDeployer and DB-based CredentialsDeployer and
RememberDeployers. Then it'd be trivial for someone to substitute their
own backend for one of those without touching the other one. For
example, I will probably want a DB-based credentials store and my own
session store that runs on a clustered memory cache.
Vanilla RIFE sites won't know the difference: they will keep extending
rife/authenticated/database.xml or whatever, and they never need to know
that that file now has additional config parameters that cause stuff to
be dynamically created on the backend.
Once that refactoring is complete, it then becomes an almost trivial
matter to do a subclass of Identified that knows how to pull the data
for a user whose identity is known but who isn't currently
authenticated. The subclass just uses the same Deployers as the
authentication element and the rest happens for free.
Before I said I might do it as a class in between Identified and
Authenticated, but now I think I might do it as a superclass of
Identified. I realize this is not your (Geert's) favorite way of
thinking about the problem, but bear with me and maybe it'll make sense:
RememberedSession (restores sessions from remember-me)
Identified (looks up user ID from session ID)
Authenticated (matches username/password)
RoleUserAuthenticated (or should this logic be injected into
Authenticated too?)
The "remember me" logic (both creating and reading the cookie) moves
into RememberedSession. If a session is restored from a cookie, the
remember-me attribute is set just as today. The "prohibit remember"
logic gets moved there too, so sites that want today's Identified
semantics can still have them; if that flag is set, RememberedSession
simply refrains from marking the request as part of a session.
Identified and Authenticated will just see it as a request that doesn't
have a valid session, no need for any special logic.
That feels architecturally clean to me: it separates the three distinct
questions that get asked on each page hit (is this a previously known
user who wants to be remembered, is this a user we know about, is this
user really who he claims to be) into separate classes. I think this
will make the authentication system not only more flexible, but also
easier to understand. YMMV on that though.
-Steve