Refactoring Authenticated/Identified

View: New views
9 Messages — Rating Filter:   Alert me  

Refactoring Authenticated/Identified

by Steven Grimm :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Continuing this thread from the users list:

> 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, I
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
_______________________________________________
Rife-devel mailing list
Rife-devel@...
http://lists.uwyn.com/mailman/listinfo/rife-devel

Re: Refactoring Authenticated/Identified

by Steven Grimm :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Anyone have any comments on this idea? Is it a sensible approach or
crazy talk? If it's crazy, anyone have a better way to get the result I
want?

-Steve


Steven Grimm wrote:

> Continuing this thread from the users list:
>
>> 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, I
> 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
> _______________________________________________
> Rife-devel mailing list
> Rife-devel@...
> http://lists.uwyn.com/mailman/listinfo/rife-devel
>

_______________________________________________
Rife-devel mailing list
Rife-devel@...
http://lists.uwyn.com/mailman/listinfo/rife-devel

Re: Refactoring Authenticated/Identified

by Geert Bevin :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


> Anyone have any comments on this idea? Is it a sensible approach or  
> crazy talk? If it's crazy, anyone have a better way to get the  
> result I want?

Sorry Steve, haven't had the time yet to read it with the attention  
it deserves. I'll get to it this weekend.

Take care,

Geert

--
Geert Bevin
Uwyn "Use what you need" - http://uwyn.com
RIFE Java application framework - http://rifers.org
Music and words - http://gbevin.com


_______________________________________________
Rife-devel mailing list
Rife-devel@...
http://lists.uwyn.com/mailman/listinfo/rife-devel

Re: Refactoring Authenticated/Identified

by Geert Bevin :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi Steven,

> 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.

I think that this would be a very useful refactoring. Honestly, the  
tight bound between the deployer classes and the actual  
authentication elements has always somewhat bothered me. Actually, I  
can't believe that I never thought of doing this way. Then again, it  
has been awhile since I created custom authentication logic, so I can  
cut myself some slack. ;-)

> 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.

Sorry, but I've still don't feel comfortable with this. As discussed  
before, you still seem to want to use the remember functionality for  
something that it hasn't been designed for. Its purpose really is to  
automate the provision of credentials to the authentication layer. It  
can't really be separated from it because the authentication checks  
still need to be performed every time credentials are retrieved and  
filled in automatically.

Of all the previous suggestions that you've made, I think that the  
fall-through authentication solution seems the best to me (the one  
that doesn't require a form when no authentication session is present  
and no remember cookie can be found). It doesn't solve the  
theoretical use case of people that want to retrieve user credentials  
of accounts that haven't been registered as users to the  
authentication back-end yet, but it does solve your "eternal  
authentication session" problem without introducing an artificial  
embedded element.

Take care,

Geert

--
Geert Bevin
Uwyn "Use what you need" - http://uwyn.com
RIFE Java application framework - http://rifers.org
Music and words - http://gbevin.com


_______________________________________________
Rife-devel mailing list
Rife-devel@...
http://lists.uwyn.com/mailman/listinfo/rife-devel

Re: Refactoring Authenticated/Identified

by Steven Grimm :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Geert Bevin wrote:
> I think that this would be a very useful refactoring. Honestly, the
> tight bound between the deployer classes and the actual authentication
> elements has always somewhat bothered me. Actually, I can't believe
> that I never thought of doing this way. Then again, it has been awhile
> since I created custom authentication logic, so I can cut myself some
> slack. ;-)

Super, glad I'm not totally off the wall here. I have been doing some
custom authentication code on both of my current RIFE projects (totally
different on each, one is just the "remember me" stuff and the other is
a custom Authenticated subclass that sends the user to a third-party
authentication service to log in) so any movement in this direction will
make my life much easier.

What I'd love -- and I'm sure you would too -- would be to get to the
point where a reasonably skilled developer could write their own, say,
user database module without having to ever look at the RIFE source
tree. Not that there's anything bad about having the sources to refer
to, of course, but if my experience is typical, right now things are
intertwined tightly enough that someone writing custom auth code has to
be highly aware of the implementation details of the authentication system.

This refactoring, assuming it turns out to be as feasible as it sounds,
will definitely help in that area. I was not looking forward to figuring
out how to explain the current Deployer system on the auth internals
Wiki page. (Which, BTW, I am about to go work on a little more.)

> Of all the previous suggestions that you've made, I think that the
> fall-through authentication solution seems the best to me (the one
> that doesn't require a form when no authentication session is present
> and no remember cookie can be found). It doesn't solve the theoretical
> use case of people that want to retrieve user credentials of accounts
> that haven't been registered as users to the authentication back-end
> yet, but it does solve your "eternal authentication session" problem
> without introducing an artificial embedded element.

I'm not sure I understand that use case. Can you give me an example?

I've just filed RIFE-301 with an initial stab at an implementation of
the fall-through mechanism. Seems to work just fine.

-Steve

_______________________________________________
Rife-devel mailing list
Rife-devel@...
http://lists.uwyn.com/mailman/listinfo/rife-devel

Re: Refactoring Authenticated/Identified

by Geert Bevin :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi Steven,

> What I'd love -- and I'm sure you would too -- would be to get to  
> the point where a reasonably skilled developer could write their  
> own, say, user database module without having to ever look at the  
> RIFE source tree. Not that there's anything bad about having the  
> sources to refer to, of course, but if my experience is typical,  
> right now things are intertwined tightly enough that someone  
> writing custom auth code has to be highly aware of the  
> implementation details of the authentication system.

Indeed that would be awesome. I'm wondering, would you be up for  
making this refactoring happen? It will be quite a while before I can  
look at it. It's really time that I spend as much free time as  
possible on the book(s) and the advancement of continuations.

> This refactoring, assuming it turns out to be as feasible as it  
> sounds, will definitely help in that area. I was not looking  
> forward to figuring out how to explain the current Deployer system  
> on the auth internals Wiki page. (Which, BTW, I am about to go work  
> on a little more.)
>
>> Of all the previous suggestions that you've made, I think that the  
>> fall-through authentication solution seems the best to me (the one  
>> that doesn't require a form when no authentication session is  
>> present and no remember cookie can be found). It doesn't solve the  
>> theoretical use case of people that want to retrieve user  
>> credentials of accounts that haven't been registered as users to  
>> the authentication back-end yet, but it does solve your "eternal  
>> authentication session" problem without introducing an artificial  
>> embedded element.
>
> I'm not sure I understand that use case. Can you give me an example?

I was referring to the one I described in an earlier email:
http://www.nabble.com/Re%3A-%22Remember-me%22-for-identified-%28not- 
authenticated%29-pages--p5592145.html

> I've just filed RIFE-301 with an initial stab at an implementation  
> of the fall-through mechanism. Seems to work just fine.

That looks like a good implementation of it. I would rename  
"allow_anonymous" to "enforce_authenticated", feels more logical and  
general-purpose to me.

To implement the tests for this, I can point you to the related  
files. First, add the required elements to the authentication sites  
that are here: http://rifers.org:8088/browse/rifers/rife/trunk/ 
programs/unittests/config/site

If you want, you can add the individual element XML files here, but I  
don't do that anymore: http://rifers.org:8088/browse/rifers/rife/ 
trunk/programs/unittests/config/element/authentication

Then you add the actual tests to the JUnit suites that are here:
http://rifers.org:8088/browse/rifers/rife/trunk/src/unittests/com/ 
uwyn/rife/authentication/elements

To run only these, I simple comment out the non authentication test  
suites from:
http://rifers.org:8088/browse/rifers/rife/trunk/src/unittests/com/ 
uwyn/rife/TestRifeTests.java?r=trunk

and only leave the last bunch of server-side test suites in here:
http://rifers.org:8088/browse/rifers/rife/trunk/src/unittests/com/ 
uwyn/rife/authentication/TestSuiteAuthentication.java?r=trunk

To only use one database for testing, modify this file and prefix the  
"unittests*" names with another character (I use 'd' of disabled):
http://rifers.org:8088/browse/rifers/rife/trunk/programs/unittests/ 
config/rep/unittests_datasources.xml?r=trunk

Hope this is a bit clear, otherwise don't hesitate to ask.

Take care,

Geert

PS.: I know that this would all be much easier if the tests were  
converted to TestNG ... but I'm not really up for that test ;-)
--
Geert Bevin
Uwyn "Use what you need" - http://uwyn.com
RIFE Java application framework - http://rifers.org
Music and words - http://gbevin.com


_______________________________________________
Rife-devel mailing list
Rife-devel@...
http://lists.uwyn.com/mailman/listinfo/rife-devel

Refactoring Purging

by Steven Grimm :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

First a quick update for everyone reading this: I've done a first round
of the Authenticated refactoring, making the selection of the
SessionManager and SessionValidator completely dynamic based on element
configuration. There are no more Mixed element classes since they are
just the database elements with the memory-based session classes
configured in. Application code that uses rife/authenticated/mixed.xml
won't notice the difference.

But now for the real reason I'm writing this: While I was thinking about
what I want to use this newfound configuration for, I had an idea about
how to factor out the Purging stuff too.

What I want to do is put a caching layer on top of the user and session
data so I don't have to hit the database to fetch the data for a user
who's currently logged in. But I *do* want a database sitting under the
cache; whenever there's a cache miss the database should be consulted
and the cache should be populated.

So basically I want to wrap a caching facade object around the
underlying database-capable objects. And it would be great to allow a
stack of these facades, much in the same way we can stack child-trigger
elements if we want to.

Once that general mechanism is in place, purging is just another thing
that can be layered on top of a SessionManager and/or RememberManager.
In fact, the current implementations of PurgingSessionManager and
PurgingRememberManager barely need to change at all; the only difference
is how the system knows to instantiate them and what to wrap them
around. And the purging behavior could be layered on top of, say, my
caching wrapper.

I think the general principle is probably not that controversial. The
interesting question is, what does the configuration look like? And
that's where I'm less certain.

Spring actually has a reasonable syntax for configuring which beans are
wrapped in which others -- you define the underlying bean, then define
the wrapper bean and configure the underlying bean as a property of the
wrapping bean. But since RIFE doesn't have an equivalent configuration
mechanism for low-level objects, I think that might be a bit too
heavyweight of a change to introduce. On the other hand, if we're
willing to bite that bullet, it probably *is* the most flexible way to
go about this.

Instead, though, maybe something simpler is in order. Something like
this (using some of the new properties I've introduced in my
refactoring; hopefully it's easy enough to follow.) This is a definition
for an element that uses a purging wrapper around a caching wrapper
around a database session manager.

First, database.xml (minus the stuff not related to session managers,
for the sake of example):

<element extends="rife/authenticated/authenticated.xml">
    <property
name="sessionmanagerfactory_class">DatabaseSessionsFactory</property>
    <property name="datasource"></property>
    <property name="sessionmanager_wrap_prefix">db_</property>
</element>

This is just like a normal (post-refactoring) session manager
declaration: a factory class and the parameters it needs to instantiate
its objects. The new thing is the last property; the easiest way to
explain it is by proceeding to the next element, databasecaching.xml:

<element extends="rife/authenticated/database.xml">
    <property
name="db_sessionmanagerfactory_class">SessionManagerWrapperFactory</property>
    <property
name="db_sessionmanager_class">CachingSessionManagerWrapper</property>
    <property name="db_cache_size">1M</property>
    <property name="db_cache_expiration">LRU</property>
    <property name="db_sessionmanager_wrap_prefix">cached_</property>
</element>

Here all the properties are prefixed with the wrap prefix defined by the
element we're wrapping. This prevents namespace collisions. All the
classes involved will be able to take prefix arguments and will know to
strip off their prefixes when looking up their properties. With that in
mind, this looks similar to the initial element: we define a factory
class (in this case, one that knows how to produce wrapper classes; it
will pass the underlying database session manager to the wrapper's
constructor) and tell the factory what underlying class to make. That
underlying class has some properties. Then we define the next prefix in
case there's another element in the chain, which in this case is in
databasecachingpurging.xml:

<element extends="rife/authenticated/databasecaching.xml">
    <property
name="cached_sessionmanagerfactory_class">SessionManagerWrapperFactory</property>
    <property
name="cached_sessionmanager_class">PurgingSessionManagerWrapper</property>
    <property name="cached_purge_frequency">...</property>
    <property name="cached_sessionmanager_wrap_prefix">purged_</property>
</element>

Another wrapper as before, which also defines a prefix for the next link
in the chain. But this time there isn't one. The session manager setup
code will look for a "purged_sessionmanagerfactory_class" property on
the current element, not see one, and will return the
PurgingSessionManagerWrapper object to the caller.

I admit the notion of using prefixes is a little wacky; you really
*should* be able to use the same properties at each level so you don't
have to change your element's properties just to wrap a different
parent. But (as I just confirmed with Geert on IRC) the namespace of
element properties is flat, so you pretty much have to do something like
this to represent an arbitrary chain of parameterized objects.

As with my current round of refactoring, this new flexibility is
completely hidden from applications that just want to use the pre-canned
"databaseunpurged.xml" or whatever. They continue to extend those XML
files as before, blissfully unaware that stuff has gotten more dynamic
under the covers.

What do you guys think?

-Steve
_______________________________________________
Rife-devel mailing list
Rife-devel@...
http://lists.uwyn.com/mailman/listinfo/rife-devel

Re: Refactoring Purging

by Geert Bevin :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi Steven,

I think this should maybe better be solved by adding a single  
"sessionmanager_filter" property that can be injected through IoC.  
The objects should implement the SessionManagerFilter interface,  
which would then in turn extend the SessionManager interface.

Instances of the filter classes are then simply setup for IoC in  
participants. One of the standard implementations can take a  
collection of filters that are processed sequentially.

I'm thinking of maybe adding a setDelegate(SessionManager) method to  
SessionManagerFilter which will always be called to set the instance  
of the manager that is being wrapped. It would then be up to the  
filter to call the corresponding method on the wrapped manager if  
that's needed or to not do so if that's appropriate.

I think that setting things up like this will be a lot easier that  
adding a whole bunch of properties with prefixes.

What do you think?

Geert

On 07 Sep 2006, at 21:18, Steven Grimm wrote:

> First a quick update for everyone reading this: I've done a first  
> round of the Authenticated refactoring, making the selection of the  
> SessionManager and SessionValidator completely dynamic based on  
> element configuration. There are no more Mixed element classes  
> since they are just the database elements with the memory-based  
> session classes configured in. Application code that uses rife/
> authenticated/mixed.xml won't notice the difference.
>
> But now for the real reason I'm writing this: While I was thinking  
> about what I want to use this newfound configuration for, I had an  
> idea about how to factor out the Purging stuff too.
>
> What I want to do is put a caching layer on top of the user and  
> session data so I don't have to hit the database to fetch the data  
> for a user who's currently logged in. But I *do* want a database  
> sitting under the cache; whenever there's a cache miss the database  
> should be consulted and the cache should be populated.
>
> So basically I want to wrap a caching facade object around the  
> underlying database-capable objects. And it would be great to allow  
> a stack of these facades, much in the same way we can stack child-
> trigger elements if we want to.
>
> Once that general mechanism is in place, purging is just another  
> thing that can be layered on top of a SessionManager and/or  
> RememberManager. In fact, the current implementations of  
> PurgingSessionManager and PurgingRememberManager barely need to  
> change at all; the only difference is how the system knows to  
> instantiate them and what to wrap them around. And the purging  
> behavior could be layered on top of, say, my caching wrapper.
>
> I think the general principle is probably not that controversial.  
> The interesting question is, what does the configuration look like?  
> And that's where I'm less certain.
>
> Spring actually has a reasonable syntax for configuring which beans  
> are wrapped in which others -- you define the underlying bean, then  
> define the wrapper bean and configure the underlying bean as a  
> property of the wrapping bean. But since RIFE doesn't have an  
> equivalent configuration mechanism for low-level objects, I think  
> that might be a bit too heavyweight of a change to introduce. On  
> the other hand, if we're willing to bite that bullet, it probably  
> *is* the most flexible way to go about this.
>
> Instead, though, maybe something simpler is in order. Something  
> like this (using some of the new properties I've introduced in my  
> refactoring; hopefully it's easy enough to follow.) This is a  
> definition for an element that uses a purging wrapper around a  
> caching wrapper around a database session manager.
>
> First, database.xml (minus the stuff not related to session  
> managers, for the sake of example):
>
> <element extends="rife/authenticated/authenticated.xml">
>    <property  
> name="sessionmanagerfactory_class">DatabaseSessionsFactory</property>
>    <property name="datasource"></property>
>    <property name="sessionmanager_wrap_prefix">db_</property>
> </element>
>
> This is just like a normal (post-refactoring) session manager  
> declaration: a factory class and the parameters it needs to  
> instantiate its objects. The new thing is the last property; the  
> easiest way to explain it is by proceeding to the next element,  
> databasecaching.xml:
>
> <element extends="rife/authenticated/database.xml">
>    <property  
> name="db_sessionmanagerfactory_class">SessionManagerWrapperFactory</
> property>
>    <property  
> name="db_sessionmanager_class">CachingSessionManagerWrapper</property>
>    <property name="db_cache_size">1M</property>
>    <property name="db_cache_expiration">LRU</property>
>    <property name="db_sessionmanager_wrap_prefix">cached_</property>
> </element>
>
> Here all the properties are prefixed with the wrap prefix defined  
> by the element we're wrapping. This prevents namespace collisions.  
> All the classes involved will be able to take prefix arguments and  
> will know to strip off their prefixes when looking up their  
> properties. With that in mind, this looks similar to the initial  
> element: we define a factory class (in this case, one that knows  
> how to produce wrapper classes; it will pass the underlying  
> database session manager to the wrapper's constructor) and tell the  
> factory what underlying class to make. That underlying class has  
> some properties. Then we define the next prefix in case there's  
> another element in the chain, which in this case is in  
> databasecachingpurging.xml:
>
> <element extends="rife/authenticated/databasecaching.xml">
>    <property  
> name="cached_sessionmanagerfactory_class">SessionManagerWrapperFactory
> </property>
>    <property  
> name="cached_sessionmanager_class">PurgingSessionManagerWrapper</
> property>
>    <property name="cached_purge_frequency">...</property>
>    <property name="cached_sessionmanager_wrap_prefix">purged_</
> property>
> </element>
>
> Another wrapper as before, which also defines a prefix for the next  
> link in the chain. But this time there isn't one. The session  
> manager setup code will look for a  
> "purged_sessionmanagerfactory_class" property on the current  
> element, not see one, and will return the  
> PurgingSessionManagerWrapper object to the caller.
>
> I admit the notion of using prefixes is a little wacky; you really  
> *should* be able to use the same properties at each level so you  
> don't have to change your element's properties just to wrap a  
> different parent. But (as I just confirmed with Geert on IRC) the  
> namespace of element properties is flat, so you pretty much have to  
> do something like this to represent an arbitrary chain of  
> parameterized objects.
>
> As with my current round of refactoring, this new flexibility is  
> completely hidden from applications that just want to use the pre-
> canned "databaseunpurged.xml" or whatever. They continue to extend  
> those XML files as before, blissfully unaware that stuff has gotten  
> more dynamic under the covers.
>
> What do you guys think?
>
> -Steve
> _______________________________________________
> Rife-devel mailing list
> Rife-devel@...
> http://lists.uwyn.com/mailman/listinfo/rife-devel
>

--
Geert Bevin
Uwyn "Use what you need" - http://uwyn.com
RIFE Java application framework - http://rifers.org
Music and words - http://gbevin.com


_______________________________________________
Rife-devel mailing list
Rife-devel@...
http://lists.uwyn.com/mailman/listinfo/rife-devel

Re: Refactoring Authenticated/Identified

by Steven Grimm :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

The refactoring I described in the original message of this thread is now done and checked into the "authrefactor" branch in svn. Feel free to check it out! Unless your application is directly messing with the authentication elements (e.g. you have your own subclass of DatabaseAuthenticated) this should be a completely invisible refactoring.

You can now substitute your own classes for any of RIFE's authentication-related ones without having to write your own Authenticated element. If you crack open the element XML files such as authenticated/database.xml, you'll see a bunch of new config options that control which classes are used. Namely:
  • sessionmanagerfactory_class - this class should implement SessionManagerFactory. Built-in implementations are DatabaseSessionsFactory and SimpleSessionManagerFactory (more on the latter below).
  • sessionvalidatorfactory_class - this class should implement SessionValidatorFactory. Built-in implementations are DatabaseSessionValidatorFactory and SimpleSessionValidatorFactory.
  • remembermanagerfactory_class - this class should implement RememberManagerFactory. One built-in implementation: DatabaseRememberFactory ("remember me" doesn't make sense for simple in-memory implementations).
  • credentialsmanagerfactory_class - this class should implement CredentialsManagerFactory. Built-in implementations are DatabaseUsersFactory and MemoryUsersFactory.
The SimpleSessionManagerFactory and SimpleSessionValidatorFactory classes implement a simple cache of singletons based on ID/class-name pairs. If you have an implementation that doesn't need to read any configuration from the authentication element, these are appropriate to use; that will very often be the case with session validators even if you are using your own custom session manager. These factory classes expect element properties to control which classes to instantiate:
  • sessionmanager_class - implements SessionManager. Built-in implementation is MemorySessions (there is also still DatabaseSessions but you can't instantiate that with the simple factory.)
  • sessionmanager_id - a string identifier. This means the same thing it used to in the MemorySessions configuration, but now it can apply to other session manager implementations too.
  • sessionvalidator_class - implements SessionValidator. Built-in implementation is BasicSessionValidator, which just calls the associated session and remember managers without any optimization. This will be appropriate in many cases. (There is also DatabaseSessionValidator but you can't instantiate it with the simple factory.)
This refactoring completely gets rid of the storage-medium-based subclasses of Authenticated. There is no more DatabaseAuthenticated or MixedAuthenticated -- these are all just configurations of a single class that looks at the properties. That should make it significantly easier for people who want to alter the behavior of the authentication system; previously if you wanted to override something in Authenticated, you had to subclass all the different leaf nodes of Authenticated's class hierarchy. And more importantly, if all you want to do is substitute your own user database or whatever, you no longer have to subclass Authenticated at all. Just override the credentials manager factory class in your element's configuration and you're good to go.

Comments and problem reports welcome.

There will be one more round of refactoring before I consider this done: the purging vs. non-purging distinction is still hardwired into the class hierarchy. See the "Refactoring Purging" thread on this list for some idea of what I'd like to do with that.

-Steve



Steven Grimm wrote:
Continuing this thread from the users list:

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, I 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
_______________________________________________
Rife-devel mailing list
Rife-devel@...
http://lists.uwyn.com/mailman/listinfo/rife-devel



_______________________________________________
Rife-devel mailing list
Rife-devel@...
http://lists.uwyn.com/mailman/listinfo/rife-devel
 
 
 
Google
rifers.org web