[Just reviving an old topic - thanks to Matt, Barney and Dave for setting
some things straight.]

Now that I've had some time to consider things (and head down about 50
dead-ends) I think I've got something good.  I just wanted to run it by
folks to get some opinions:

My goal is to create a security system that can be applied somewhat
painlessly to my web applications (it will, in the end, be an optional part
of my personal implementation framework).  The goals were to:

1)       Build only the logical and persistence elements of the system (no
presentation).  That way I could customize the interface how I wanted per
application.

2)       Build a CFC-based (e.g object rather procedural) system.  This was
also to allow the system to extend several pre-built utility components I've
already done.

3)       The security system would not secure itself.  Perhaps this is
wrong, but I didn't want to make it that complicated.  Instead the interface
would use  the security system to security "admin" type functionality - in
other words such entitlement checking would be done outside the system.

4)       The system should aggressively cache information for performance,
but should err on the side of logical correctness rather than performance
gains.

5)       The system should allow for basic roles/groups entitlements.

6)       The system should be flexible enough to add (later!) common
security needs ("three strikes and you're out" authentications, blocked
users, multiple log ins, etc)

7)       The system should allow for admin/entitlement changes to be
reflected immediately upon current authentications (i.e. banning somebody
would take effect immediately not upon the next log in).

Conceptually I've defined a few things:

Group: An umbrella label for protected things/function (or sets of
things/function) such as "Adminstrator", "Member", "Editor", etc.

User: A virtual concept representing a person (or, I guess, something else)
using the system.  Each user has an associated Credential, Profile and
Entitlement.

Credential: User information specific to the security system (username,
password hash, last login date, etc).

Profile: User information not specific to the security system (name, phone
number, address, etc).

Entitlement: A list of groups that the user can access.

Authentication: A representation of a "current log in".  Contains references
to the user, the session which authenticated and times of access.



Considering that, this is what I've come up with (I'm only going to go over
the basic components - each extends other components and has
persistence-broker sub components and so forth, but I won't get into that).

DP_SecurityConfiguration.CFC

            This CFC just contains basic installation and uninstallation
routines (creation of data tables, scheduled tasks, etc).  It's only used
for installation (and installation).

DP_Security.CFC

The main entry point of the whole shebang.  Contains system settings,
creation/deletion methods (for users and groups), and authentication
methods.  Also contains the Authentications collection and the mediator
(caching) components (described below).

DP_Authentications.CFC

A collection component for holding/managing authentications.  Contains
methods for retrieving specific authentications (using sessionkey) and
expiring timed-out sessions.

DP_Authentication.CFC

            Represents a current authentication and is keyed from SessionID.
Contains the UserKey (which links an authentication to a user) and the
ability to fetch and return that user's Credential, Profile or Entitlement
from the mediators.  Also contains the time of last access (used to control
authentication time-outs).

            The authentication component could also be extended to manage a
"three-strikes and you're out" rule by limited any specific session to three
login attempts.

DP_Credential.CFC

            Represents a credential.  Takes a UserKey and has methods for
loading and saving Credential data from the database.  All system users have
to have a Credential.

DP_Profile.CFC

            Represents a profile.  Takes a UserKey and has methods for
loading and saving Profile data from the database.  Profiles are optional
(not all system users have to have a profile).  So a "blank" profile can be
returned.

DP_Entitlement.CFC

            Represents an entitlement.  Takes a UserKey and has methods for
loading and saving Entitlement data from the database.  Entitlements are
optional (since not all system user have to have access to something).  This
also what's called to check entitlements (as in isEntitled(grouplist) ).

DP_CredentialMediator.CFC

            All requests for Credentials will go through the
CredentialMediator.  It will determine if the requested Credential exists
(returning an error if it doesn't) and return a reference to the component
instance.  The component instance will be placed in a collection cache.  In
this way requests for the same credential will also result in the passing of
the same instance reference meaning that any changes made will be instantly
applicable to all users of the instance.  It will also have methods to
manage this cache (clearCache(), expireCache() and so forth )

DP_ProfileMediator.CFC

            Same as the CredentialMediator only for profiles.  Also if a
Profile is requested that doesn't exist this will first ensure that a
credential exists before sending a blank profile (otherwise an error will be
thrown).

DP_EntitlementMediator.CFC

            Same as the ProfileMediator, except managing Entitlements.



Considering all of that here's my idea of an authentication life cycle:

1)       The application instantiates the DP_Security system.  The system
sets up the authentication cache and the mediator caches.

2)       To log in the application would pass a unique sessionKey (something
to identify the session - either a UUID or the CFTOKEN_CFID or something
similair), a username and a password to the authenticate() method of
DP_Security.  An authentication component is instantiated and added to the
cache (using this you could then track unsuccessful login attempts from the
session).  This method returns "true" if the authentication succeeds and
"false" if it does not.

3)       If successful the SessionKey and UserKey are added to the
authentication component.  The log in time is noted.

4)       The application can then call the isAuthenticated() method (passing
the sessionkey) and get a "true" or "false".

5)       The application can call the getAuthentication() method (passing
the sessionkey) to get the authentication object.  It can then use that to
get the Credential, Profile or Entitlement for the user.  It CAN'T get the
UserKey however - that's private.

6)       Once it has the Credential, Profile or Entitlement it can edit them
and save them (which will then affect anybody else access/using the same
instances).  Note again that the system doesn't protect itself
automatically: if the interface allos the user to change enititlements then
they can (and sometimes you may want this: for example allowing users to
sign-up interactively for newsletters).

7)       The Authentication will end if a request to it is made after the
specified timeout.  It can also be destroyed in a scheduled cache clean-up.
Lastly it can also be preemptively destroyed by an interface action
("logging out").

All in all I'm very pleased with this whole thing idea.  The mediators allow
for extremely aggressive caching with (I think) no issues with stale data.
By separating the entitlements from the "user" I've opened the door to
eventually adding much more complex entitlement checks.  By separating the
"Profile" from the "Credential" I've made it simpler to deal with personal
information apart from security information (for example an interface could
be easily built that allowed somebody to edit other people's Profiles, but
not touch their security settings).

Also this should reduce the overhead since it's only rarely (normally) that
you need either the Credential or Profile information in a request (although
you generally need the entitlement information every time).

It's nice (I think) that the end user doesn't actually know anything they're
not told: things are linked via the sessionkey instead of putting the
UserKey or a User component into the session.  The session always has to ask
for information (although I'll admit that there is the potential for caching
references to the low-level system objects: but you shouldn't do that
anyway).



If you're still with me (and thank you very much if you are) I still have
some questions I'd like some opinions on:

1) The security system can obviously be broken in many ways by bad UI
implementations (allow access to admin functions, caching references they
shouldn't, etc).  My thinking is that this is fine: if the system wants good
security they'll follow the recommendations.  I don't think that this is
unusual, is it?  What do you think?  

2) I'm not really sure about some of my access points.  Instead of returning
the Authentication component should the system rather expose methods like
"getCredential()", "getProfile()" and so forth in the topmost security
instance?  This would add more that this (which is already quite large) and
completely abstract the authentication component.  Is that a good thing?

3) Although I'm truly pleased with the end-user side of things I'm not so
sure yet about the admin side of things.  How do I (as an admin) load a
Credential to edit somebody else's log information?  Do I add
"getAllUsers()" method to the Security.CFC or should I expose (for admin use
only) the "getAll" type methods in the Mediator's?

I'm leaning towards the latter (to remove complexity from the already
complex Security CFC) but am unsure about allowing such "deep" access to the
system.  Right now all of the public methods in the Security.CFC work off of
a sessionKey - I'm confused about best to expose access to date outside of
an Authentication (but I know that I want to information to come through the
mediators).

Sorry for writing a book, but this has had me in knots for several weeks and
I finally feel like I'm getting a handle on it.

Criticisms or requests for details welcome.  I'm also happy to share the
code with anybody that wants it (especially if they want to help!).

Jim Davis
[Todays Threads] [This Message] [Subscription] [Fast Unsubscribe] [User Settings] [Donations and Support]

Reply via email to