Hi all -
This is a progress report on my efforts to equip a WebKit application with
a restricted (and configurable) number of concurrent logins. First I give a
summary, then the details are given below my signature.
Summary: The solution is based on a modification of the
SecurePage/LoginPage example to make use of UserManager, User, and
UserGroup classes. A SecureApplication subclass of Application creates a
UserManager instance for itself with access method
SecureApplication.userManager(), which is needed by SecurePage. I had to
hack AppServer slighly so that its createApplication() method creates a
SecureApplication instead of a plain Application. Doing this by specifying
an ApplicationClass configuration option for AppServer would be insecure,
to say the least, and I couldn't think of a non-hacking approach.
The Session class is used unmodified, but I wish that it had an
isTimedOut() method so that I could test session.isTimedOut() instead of
time.time() - session.lastAccessTime() > session.timeout()
Suggestions and criticisms on any of this are welcome.
The scheme seems to work -- I attempt to view an app page, get presented
with a Login Page, log in as one of my "authorized users", and get to the
application page (subclass of my SecurePage, which is a subclass of my
BasePage) in the expected way.
I notice that if, while viewing the app page, I choose "Refresh" in the
browser's menu or toolbar, the question comes up "Re-post form data?" If I
respond "Yes" then the login page appears, and I have to log in once or
twice to get back to the app page. If the session times out, WebKit
generously allows me to bypass all the security and proceed without a
session. I think both of these problems will be solved with a little more
fiddling with the LoginPage and SecurePage logic.
The long version of what I did and found out (so far) is below. This is
only a progress report, so the details may change considerably in the next
few days as I try to get this ready for release.
Victoria BC Canada
ADDING LOGINS TO A WEBKIT APPLICATION (IN PROGRESS)
My home-made application launcher fixes up sys.path and launches
ThreadedAppServer, which is a subclass of AppServer.
AppServer has been hacked so its createApplication() method creates a
SecureApplication instead of a plain old Application.
SecureApplication is a subclass of Application. Its __init__ method creates
a UserManager for itself, with access method userManager().
SecureApplication does not use the UserManager -- it just makes it
available for the SecurePage applet.
UserKit's UserManager and other classes could be used, but my project needs
stable classes with different features. For project deadline reasons, I
created three very simple classes that do exactly what I need and can be
brought to a final releasable state quickly. These are:
UserManager - loads and saves the users file, doles out Users and
Groups on demand. The authenticate(username, password) method returns a
User object if the login info is correct, None otherwise.
User - has a username, a password (encrypted upon creation, unencrypted
original discarded), an integer security level which allows the app to
decide what the user is allowed to do, and a groupname, which makes him a
member of a particular group or "pool" of concurrent users.
Group - currently has only a groupname and a maxConcurrentUsers
attribute limiting the number of users of that group who can be logged in
All three of these reside in a package (usr) in my application. The "users"
file containing user ID's, encrypted passwords, security levels, and
groupnames, is also kept in a web-inaccessible directory.
My app's license module specifies an overall limit on the total number of
allowed concurrent logins from all groups combined.
Usage example: A site which has five authorized concurrent logins could set
up two groups - say, 'clerks' and 'wizards', where the clerks group has a
maximum of 3 concurrent logins and the wizards group has 2. Ten users
(say) are registered - eight clerks and two wizards. One of the wizards is
given the highest security level, 5, giving him full admin privileges. The
other has level 4. Two clerks have level 3, and all the rest have security
level 2. With this scheme, both of the wizards are always able to log in
any time, but only 3 of the clerks can be logged in at one time.
My version of the SecurePage class relies on the UserManager, User, and
Group classes described above, but a very similar approach would work with
UserKit. When someone tries to log in,
application.userManager().authenticate(username, password) is called. If a
User is returned, all the sessions in application.sessions() are checked to
see whether all of the slots for that user's group are in use. If not, the
User object is stored in the current session as authenticatedUser, and the
user's groupname is stored in the session as groupname. While searching
all the sessions to determine the availability of logins, sessions which
are timed out are not counted. Whenever a secure page attempts to use a
timed out session, it is (supposed to be) immediately logged out. [One
gotcha: application.sessions() is (sort of) dict-like, not list-like, so
you have to iterate through application.sessions().keys()].
The base page class for all the protected app pages is the modified
SecurePage. The original LoginPage is used pretty much as-is, except for
Victoria BC Canada