« Return to Thread: Refactoring Authenticated/Identified

Re: Refactoring Authenticated/Identified

by Steven Grimm :: Rate this Message:

Reply to Author | View in Thread

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

 « Return to Thread: Refactoring Authenticated/Identified

 
 
 
Google
rifers.org web