Below is a first draft of a proposal to cover the features discussed in
this thread. I have bundled a number of loosely related items into the
proposal. I can split them out to separate proposals if that is considered
a better approach.
There are three items which are not resolved in the high level of the
proposal. However, I believe that there is enough detail in the proposal
at the moment to elicit feedback on the Proposal scope and approach.
If this high-level is considered acceptable to the list, I will do an
implementation and submit a detailed proposal to this list, the Wiki and
Launchpad.
Thanks,
Kevin
Proposal
Session Credentials API Enhancements
o Support remote IP restricted logins
o Support domain specific logins
o Encrypt Password
o Application overriding credentials from request
o Tracking of 'logged in' users
Motivation(s)
Require an API for logging in a user automatically. The mailing list
identified a number of other related requirements. Some items were added
or expanded due to usefulness in Zope2.
Problem(s)
Secure authentication scenarios require access to be limited to a single
IP Address or set of IP Addresses. The Session Credential plugin should
take responsibility for extracting and returning the authentication IP
Address.
Some scenarios use a 'domain' and provide the login within that specific
domain (e.g. a company and users belonging to that company).
For security reasons the password should be encrypted while in the session
store.
The application needs an API so that it can set the credentials for a
session programmatically, i.e. as part of the sign up process.
For monitoring purposes, it is useful to know who is currently 'logged
in'. For strict security is important to logout idle users.
Proposal
1. IP Extraction
Extract the IP Address from the credentials and store it. Return the IP
Address in the dictionary from extractCredentials().
The value from request._environ['HTTP_X_FORWARDED_FOR'] will be used if
present. otherwise request._environ['REMOTE_ADDR'].
This requires a change to ISessionCredentials to provide getIP().
2. Domain
The Session Credentials can optionally provide a 'domain' value. Where the
domain is used the domain should be stored in the client using a cookie,
so that it can default.
The domain functionality should be enabled via the UI.
TODO: Need to rename the domain functionality as the term domain is used
for IP specific security in Zope2.
3. Encrypt password
The Session Credentials should use of an encrypter class to encrypt the
password if configured. The class is used to encrypt the password when
saved and decrypts it when returning the password to the application (via
extraCredentials).
The encrypter class shall support the following Interface:
class IEncryptPassword(Interface):
def encrypt(login, password):
"""Return an encrypted version of the password"""
def decrypt(login, encrypted):
"""Return a decrypted version of the password"""
TODO: Determine the mechanism for locating/configuring this class.
TODO: Identify a mechanism for recovering if you use this mechanism with
no users with encrypted passwords.
4. Application Overriding Login Credentials
The ICredentialsPlugin.extractCredentials method will have two new
parameter, overrides and mode.
overrides will provide a dictionary containing ('login', 'password' and
'domain') which will override the same values in the request object if
they are present in the request object.
mode will define what to do with the parameters if session credentials are
already stored:
STORED_REQUEST_MODE:If stored values, used them otherwise use the
request (current functionality)
REQUEST_STORED_MODE:Try for parameters in the request. If they are
not
present use stored values if they are available.
REQUEST_ONLY_MODE: Ignore values if they are stored. Use the
request only.
The class will use a full set of credentials from either the request or
storage. It will not mix them.
An adapter will be provided to make access to the login simpler. This
should be part of zope.app.security. The adapter shall adapt a request
object.
class ILogin(Interface):
"""Provide support for logging in directly"""
def login(overrides, mode=REQUEST_ONLY):
"""Login using credentials provided in the overrides
parameter. {'login':'', 'password': '', 'domain': ''}
If values are not provided in the dictionary, the
request
is searched
"""
def setCredentials(overrides, mode=REQUEST_ONLY):
"""Store the credentials for the curre