Sessions are objects linked to an authenticated user. The Session.new_cnx method returns a new Connection linked to that session.
Connections provide the .execute method to query the data sources.
There are two kinds of connections.
Normal connections are typically named _cw in most appobjects or sometimes just session.
Internal connections are available from the Repository object and are to be used like this:
with self.repo.internal_cnx() as cnx:
do_stuff_with(cnx)
cnx.commit()
Connections should always be used as context managers, to avoid leaks.
The authentication process is a ballet involving a few dancers:
Sometimes CubicWeb’s out-of-the-box authentication schemes (cookie and http) are not sufficient. Nowadays there is a plethora of such schemes and the framework cannot provide them all, but as the sequence above shows, it is extensible.
Two levels have to be considered when writing an authentication plugin: the web client and the repository.
We invented a scenario where it makes sense to have a new plugin in each side: some middleware will do pre-authentication and under the right circumstances add a new HTTP x-foo-user header to the query before it reaches the CubicWeb instance. For a concrete example of this, see the trustedauth cube.
On the repository side, it is possible to register a source authentifier using the following kind of code:
from cubicweb.server.sources import native
class FooAuthentifier(native.LoginPasswordAuthentifier):
""" a source authentifier plugin
if 'foo' in authentication information, no need to check
password
"""
auth_rql = 'Any X WHERE X is CWUser, X login %(login)s'
def authenticate(self, session, login, **kwargs):
"""return CWUser eid for the given login
if this account is defined in this source,
else raise `AuthenticationError`
"""
session.debug('authentication by %s', self.__class__.__name__)
if 'foo' not in kwargs:
return super(FooAuthentifier, self).authenticate(session, login, **kwargs)
try:
rset = session.execute(self.auth_rql, {'login': login})
return rset[0][0]
except Exception, exc:
session.debug('authentication failure (%s)', exc)
raise AuthenticationError('foo user is unknown to us')
Since repository authentifiers are not appobjects, we have to register them through a server_startup hook.
class ServerStartupHook(hook.Hook):
""" register the foo authenticator """
__regid__ = 'fooauthenticatorregisterer'
events = ('server_startup',)
def __call__(self):
self.debug('registering foo authentifier')
self.repo.system_source.add_authentifier(FooAuthentifier())
class XFooUserRetriever(authentication.LoginPasswordRetriever):
""" authenticate by the x-foo-user http header
or just do normal login/password authentication
"""
__regid__ = 'x-foo-user'
order = 0
def authentication_information(self, req):
"""retrieve authentication information from the given request, raise
NoAuthInfo if expected information is not found
"""
self.debug('web authenticator building auth info')
try:
login = req.get_header('x-foo-user')
if login:
return login, {'foo': True}
else:
return super(XFooUserRetriever, self).authentication_information(self, req)
except Exception, exc:
self.debug('web authenticator failed (%s)', exc)
raise authentication.NoAuthInfo()
def authenticated(self, retriever, req, cnx, login, authinfo):
"""callback when return authentication information have opened a
repository connection successfully. Take care req has no session
attached yet, hence req.execute isn't available.
Here we set a flag on the request to indicate that the user is
foo-authenticated. Can be used by a selector
"""
self.debug('web authenticator running post authentication callback')
cnx.foo_user = authinfo.get('foo')
In the authenticated method we add (in an admitedly slightly hackish way) an attribute to the connection object. This, in turn, can be used to build a selector dispatching on the fact that the user was preauthenticated or not.
@objectify_selector
def foo_authenticated(cls, req, rset=None, **kwargs):
if hasattr(req.cnx, 'foo_user') and req.foo_user:
return 1
return 0
Repository user session
About session storage / transactions
Here is a description of internal session attributes. Besides data and transaction_data, you should not have to use attributes described here but higher level APIs.
data is a dictionary containing shared data, used to communicate extra information between the client and the repository
_cnxs is a dictionary of Connection instance, one for each running connection. The key is the connection id. By default the connection id is the thread name but it can be otherwise (per dbapi cursor for instance, or per thread name from another process).
__threaddata is a thread local storage whose cnx attribute refers to the proper instance of Connection according to the connection.
You should not have to use neither _cnx nor __threaddata, simply access connection data transparently through the _cnx property. Also, you usually don’t have to access it directly since current connection’s data may be accessed/modified through properties / methods:
connection_data, similarly to data, is a dictionary containing some shared data that should be cleared at the end of the connection. Hooks and operations may put arbitrary data in there, and this may also be used as a communication channel between the client and the repository.
return value associated to key in session data
set value associated to key in session data
return True if the entity of the given eid is being created in the current transaction
return True if the entity of the given eid is being deleted in the current transaction
Connection state information:
running_dbapi_query, boolean flag telling if the executing query is coming from a dbapi connection or is a query from within the repository
cnxset, the connections set to use to execute queries on sources. During a transaction, the connection set may be freed so that is may be used by another session as long as no writing is done. This means we can have multiple sessions with a reasonably low connections set pool size.
- set_cnxset()¶
the session need a connections set to execute some queries
- free_cnxset(*args, **kwargs)¶
mode, string telling the connections set handling mode, may be one of ‘read’ (connections set may be freed), ‘write’ (some write was done in the connections set, it can’t be freed before end of the transaction), ‘transaction’ (we want to keep the connections set during all the transaction, with or without writing)
pending_operations, ordered list of operations to be processed on commit/rollback
commit_state, describing the transaction commit state, may be one of None (not yet committing), ‘precommit’ (calling precommit event on operations), ‘postcommit’ (calling postcommit event on operations), ‘uncommitable’ (some ValidationError or Unauthorized error has been raised during the transaction and so it must be rolled back).
Security level Management:
read_security and write_security, boolean flags telling if read/write security is currently activated.
Hooks Management:
hooks_mode, may be either HOOKS_ALLOW_ALL or HOOKS_DENY_ALL.
enabled_hook_categories, when hooks_mode is HOOKS_DENY_ALL, this set contains hooks categories that are enabled.
disabled_hook_categories, when hooks_mode is HOOKS_ALLOW_ALL, this set contains hooks categories that are disabled.
return a boolean telling if the given category is currently activated or not
return a boolean telling if the given hook class is currently activated or not
Data manipulation:
provide direct access to the repository method to add a relation.
This is equivalent to the following rql query:
SET X rtype Y WHERE X eid fromeid, T eid toeid
without read security check but also all the burden of rql execution. You may use this in hooks when you know both eids of the relation you want to add.
set many relation using a shortcut similar to the one in add_relation
relations is a list of 2-uples, the first element of each 2-uple is the rtype, and the second is a list of (fromeid, toeid) tuples
provide direct access to the repository method to delete a relation.
This is equivalent to the following rql query:
DELETE X rtype Y WHERE X eid fromeid, T eid toeid
without read security check but also all the burden of rql execution. You may use this in hooks when you know both eids of the relation you want to delete.
Other:
Repository Connection
Holds all connection related data
Database connection resources:
running_dbapi_query, boolean flag telling if the executing query is coming from a dbapi connection or is a query from within the repository
cnxset, the connections set to use to execute queries on sources. If the transaction is read only, the connection set may be freed between actual queries. This allows multiple connections with a reasonably low connection set pool size. Control mechanism is detailed below.
mode, string telling the connections set handling mode, may be one of ‘read’ (connections set may be freed), ‘write’ (some write was done in the connections set, it can’t be freed before end of the transaction), ‘transaction’ (we want to keep the connections set during all the transaction, with or without writing)
Internal transaction data:
data is a dictionary containing some shared data cleared at the end of the transaction. Hooks and operations may put arbitrary data in there, and this may also be used as a communication channel between the client and the repository.
pending_operations, ordered list of operations to be processed on commit/rollback
commit_state, describing the transaction commit state, may be one of None (not yet committing), ‘precommit’ (calling precommit event on operations), ‘postcommit’ (calling postcommit event on operations), ‘uncommitable’ (some ValidationError or Unauthorized error has been raised during the transaction and so it must be rolled back).
Hooks controls:
hooks_mode, may be either HOOKS_ALLOW_ALL or HOOKS_DENY_ALL.
enabled_hook_cats, when hooks_mode is HOOKS_DENY_ALL, this set contains hooks categories that are enabled.
disabled_hook_cats, when hooks_mode is HOOKS_ALLOW_ALL, this set contains hooks categories that are disabled.
Security level Management:
read_security and write_security, boolean flags telling if read/write security is currently activated.