source: TI12-security/trunk/python/ndg.security.server/ndg/security/server/wsgi/openid/provider/authninterface/sessionmanager.py @ 5168

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg.security.server/ndg/security/server/wsgi/openid/provider/authninterface/sessionmanager.py@5168
Revision 5168, 7.5 KB checked in by pjkersha, 12 years ago (diff)

Added new access control interface and functionality to OpenID Provider to enable a custom context object to be passed between login and logout calls.

Line 
1"""NDG Security OpenID Authentication Interface to a Session Manager.
2
3This enables an OpenID Provider's signin to link to a Session Manager running
4in the same WSGI stack or else running as a separate service via the Session
5Manager SOAP interface
6
7NERC DataGrid Project
8"""
9__author__ = "P J Kershaw"
10__date__ = "01/08/08"
11__copyright__ = "(C) 2009 Science and Technology Facilities Council"
12__license__ = "BSD - see LICENSE file in top-level directory"
13__contact__ = "Philip.Kershaw@stfc.ac.uk"
14__revision__ = "$Id$"
15import logging
16log = logging.getLogger(__name__)
17from string import Template
18from sqlalchemy import create_engine
19
20from ndg.security.server.wsgi.openid.provider import AbstractAuthNInterface, \
21    AuthNInterfaceConfigError, AuthNInterfaceInvalidCredentials, \
22    AuthNInterfaceUsername2IdentifierMismatch, AuthNInterfaceCtx
23   
24from ndg.security.server.wsgi.utils.sessionmanagerclient import \
25    WSGISessionManagerClient, AuthNServiceInvalidCredentials
26   
27
28class SessionManagerAuthNCtx(AuthNInterfaceCtx):
29    """Authentication Context class for passing session Id and Session Manager
30    URI to logout method AXResponse instance
31    """
32    def __init__(self, uri=None, environKeyName=None, sessionId=None):
33        self.uri = uri
34        self.environKeyName = environKeyName
35        self.sessionId = sessionId
36
37class SessionManagerOpenIDAuthNInterface(AbstractAuthNInterface):
38    '''Authentication interface class for OpenIDProviderMiddleware to enable
39    authentication to a Session Manager instance running in the same WSGI
40    stack or via a SOAP call to a remote service
41   
42    @type dbParamNames: tuple
43    @cvar dbParamNames: permitted config keywords.  Nb. SQL queries takes
44    String Template style '$' substitutions for username, password and OpenID
45    identifier'''
46   
47    dbParamNames = (
48        'connectionString',
49        'logonSQLQuery', 
50        'userIdentifiersSQLQuery')
51   
52    def __init__(self, **prop):
53        """Make any initial settings
54       
55        Settings are held in a dictionary which can be set from **prop,
56        a call to setProperties() or by passing settings in an XML file
57        given by propFilePath
58       
59        @type **prop: dict
60        @param **prop: set properties via keywords
61        @raise AuthNInterfaceConfigError: error with configuration
62        """
63        try:
64            for name in SessionManagerOpenIDAuthNInterface.dbParamNames:
65                setattr(self, name, prop.pop(name))
66               
67        except KeyError, e:
68            raise AuthNInterfaceConfigError("Missing property setting for "
69                                            "database connection: %s" % e)
70
71        self._client = WSGISessionManagerClient(**prop)
72       
73       
74    def logon(self, environ, userIdentifier, username, password):
75        """Interface login method
76       
77        @type environ: dict
78        @param environ: standard WSGI environ parameter
79       
80        @type username: basestring
81        @param username: user identifier
82       
83        @type password: basestring
84        @param password: corresponding password for username givens
85       
86        @raise AuthNInterfaceUsername2IdentifierMismatch: no OpenID identifiers
87        match the given username
88        @raise AuthNInterfaceInvalidCredentials: invalid username/password
89        """
90        if userIdentifier is not None:
91            # Check for a match between the OpenID user identifier and the
92            # username
93            try:
94                dbEngine = create_engine(self.connectionString)
95                connection = dbEngine.connect()
96            except Exception, e:
97                log.error('Connecting database for user logon query : %s' % e)
98                raise
99           
100            try:
101                try:
102                    queryInputs = dict(username=username,
103                                       userIdentifier=userIdentifier)
104                    query=Template(self.logonSQLQuery).substitute(queryInputs)
105                    result = connection.execute(query)
106                except Exception, e:
107                    log.error('Connecting database for user logon query : %s' %
108                              e)
109                    raise
110               
111                if not result.rowcount:
112                    raise AuthNInterfaceUsername2IdentifierMismatch()
113            finally:
114                connection.close()
115       
116        try:
117            authNCtx = SessionManagerAuthNCtx(uri=self._client.uri,
118                                    environKeyName=self._client._environKey)
119            self._client.environ = environ
120            authNCtx.sessionId = self._client.connect(username, 
121                                                      passphrase=password)[-1]
122           
123        except AuthNServiceInvalidCredentials, e:
124            log.exception(e)
125            raise AuthNInterfaceInvalidCredentials()
126       
127        return authNCtx
128       
129   
130    def username2UserIdentifiers(self, environ, username):
131        """Map the login username to an identifier which will become the
132        unique path suffix to the user's OpenID identifier.  The
133        OpenIDProviderMiddleware takes the ID URL template and adds it to this
134        identifier e.g.
135       
136            identifier = self._authN.username2UserIdentifiers(username)
137            identityURL = http://mysite/openid/${userIdentifier}
138       
139        @type environ: dict
140        @param environ: standard WSGI environ parameter
141       
142        @type username: basestring
143        @param username: user identifier
144       
145        @rtype: tuple
146        @return: identifiers to be used to make OpenID user identity URLs.
147       
148        @raise AuthNInterfaceRetrieveError: error with retrieval of information
149        to identifier e.g. error with database look-up.
150        """
151        try:
152            dbEngine = create_engine(self.connectionString)
153            connection = dbEngine.connect()
154        except Exception, e:
155            log.error('Connecting database for user identifiers query : %s'%e)
156            raise
157           
158        try:
159            try:
160                tmpl = Template(self.userIdentifiersSQLQuery)
161                sqlQuery = tmpl.substitute(dict(username=username))
162                result = connection.execute(sqlQuery)
163                if not result.rowcount:
164                    raise AuthNInterfaceRetrieveError()
165               
166                userIdentifiers = tuple([row.values()[0] for row in result])
167            except Exception, e:
168                log.error('Querying database for user identifiers for user '
169                          '"%s": %s' (username, e))
170                raise
171        finally:
172            connection.close()
173           
174        return userIdentifiers
175
176    def logout(self, authNCtx):
177        """
178        @type authNCtx: SessionManagerAuthNCtx
179        @param authNCtx: authentication context object returned from
180        login method
181        """
182        if not isinstance(authNCtx, SessionManagerAuthNCtx):
183            log.error("Expecting SessionManagerAuthNCtx type for authNCtx; "
184                      "got: %s" % authNCtx.__class__.__name__)
185            raise AuthNInterfaceConfigError("Expecting SessionManagerAuthNCtx "
186                                            "type for authNCtx; got: %s" % 
187                                            authNCtx.__class__.__name__)
188       
189        try:
190            self._client.environ = environ
191            self._client.disconnect(sessID=authNCtx.sessionId)
192           
193        except Exception, e:
194            log.exception(e)
195            raise AuthNInterfaceInvalidCredentials()
Note: See TracBrowser for help on using the repository browser.