source: TI12-security/trunk/python/ndg_security_server/ndg/security/server/wsgi/openid/provider/authninterface/basic.py @ 5786

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

Updated OpenID AX (Attribute Exchange) interface. Attributes passed over this interface are now stored in the authentication session at the Relying Party.

Line 
1"""NDG Security Basic OpenID Authentication Interface.
2
3A demonstration implementation of an authentication interface for
4OpenIDProviderMiddleware WSGI.  Username/password and OpenId user identifier
5details are read from a config file and passed as keywords.  This class is not
6intended for production use.
7
8NERC DataGrid Project
9
10"""
11__author__ = "P J Kershaw"
12__date__ = "01/08/08"
13__copyright__ = "(C) 2009 Science and Technology Facilities Council"
14__contact__ = "Philip.Kershaw@stfc.ac.uk"
15__revision__ = "$Id$"
16import logging
17log = logging.getLogger(__name__)
18
19from ndg.security.server.wsgi.openid.provider.authninterface import \
20    AbstractAuthNInterface, AuthNInterfaceInvalidCredentials, \
21    AuthNInterfaceRetrieveError, AuthNInterfaceConfigError, \
22    AuthNInterfaceUsername2IdentifierMismatch
23 
24   
25class BasicAuthNInterface(AbstractAuthNInterface):
26    '''Basic Authentication interface class for OpenIDProviderMiddleware
27   
28    it uses username/password details retrieved from config file / keyword
29    entry.  This class is for testing only.  NOT for production use'''
30   
31    IDENTITY_URI_TMPL_KEYNAME = 'identityUriTemplate'
32    USERCREDS_PROPERTY_KEYNAME = 'userCreds'
33    USERCREDS_KEYNAMES = ('password', 'identifiers')
34   
35    propertyKeyNames = (
36        USERCREDS_PROPERTY_KEYNAME
37    )
38   
39    getUserIdentifier = staticmethod(lambda identityURI: 
40                                     identityURI.rsplit('/')[-1])
41   
42    def __init__(self, **prop):
43        """Make any initial settings
44       
45        Settings are held in a dictionary which can be set from **prop,
46        a call to setProperties() or by passing settings in an XML file
47        given by propFilePath
48       
49        @type **prop: dict
50        @param **prop: set properties via keywords
51        @raise AuthNInterfaceConfigError: error with configuration
52        """
53        # Test/Admin username/password set from ini/kw args
54        self._identityUriTemplate = prop.get(
55                                BasicAuthNInterface.IDENTITY_URI_TMPL_KEYNAME)
56        userCredsField = prop.get(
57                                BasicAuthNInterface.USERCREDS_PROPERTY_KEYNAME)
58        if not userCredsField:
59            raise AuthNInterfaceConfigError('No "%s" config option found' %
60                                BasicAuthNInterface.USERCREDS_PROPERTY_KEYNAME)
61
62        self._userCreds = {}
63        for userEntry in userCredsField.split(): 
64            # Split username, password and OpenID name list 
65            userCreds = userEntry.strip().split(':')
66           
67            # Split OpenID name list
68            userCreds[-1] = tuple(userCreds[-1].split(','))
69           
70            # Convert into a dictionary indexed by username
71            userCredsKeys = BasicAuthNInterface.USERCREDS_KEYNAMES
72            self._userCreds[userCreds[0]] = dict(zip(userCredsKeys, 
73                                                     userCreds[1:])) 
74   
75    def logon(self, environ, identityURI, username, password):
76        """Interface login method
77       
78        @type environ: dict
79        @param environ: standard WSGI environ parameter
80
81        @type identityURI: basestring
82        @param identityURI: user's identity URL e.g.
83        'https://joebloggs.somewhere.ac.uk/'
84
85        @type username: basestring
86        @param username: username
87       
88        @type password: basestring
89        @param password: corresponding password for username givens
90       
91        @raise AuthNInterfaceInvalidCredentials: invalid username/password
92        @raise AuthNInterfaceUsername2IdentifierMismatch: no OpenID matching
93        the given username
94        """
95        if self._userCreds.get(username, {}).get('password') != password:
96            raise AuthNInterfaceInvalidCredentials()
97       
98        # Assume identifier is at the end of the URI
99        if identityURI is not None:
100            userIdentifier = BasicAuthNInterface.getUserIdentifier(identityURI)
101       
102            if userIdentifier not in self._userCreds[username]['identifiers']:
103                raise AuthNInterfaceUsername2IdentifierMismatch()
104
105    def logout(self):
106        pass
107       
108    def username2UserIdentifiers(self, environ, username):
109        """Map the login username to an identifier which will become the
110        unique path suffix to the user's OpenID identifier.  The
111        OpenIDProviderMiddleware takes self.urls['id_url'] and adds it to this
112        identifier:
113       
114            identifier = self._authN.username2UserIdentifiers(environ,username)
115            identityURL = self.urls['url_id'] + '/' + identifier
116       
117        @type environ: dict
118        @param environ: standard WSGI environ parameter
119
120        @type username: basestring
121        @param username: user identifier
122       
123        @rtype: tuple
124        @return: identifiers to be used to make OpenID user identity URLs.
125       
126        @raise AuthNInterfaceRetrieveError: error with retrieval of information
127        to identifier e.g. error with database look-up.
128        """
129        try:
130            return self._userCreds[username]['identifiers']
131        except KeyError:
132            raise AuthNInterfaceRetrieveError('No entries for "%s" user' % 
133                                              username)
134
135
136from ndg.security.server.wsgi.utils.sessionmanagerclient import \
137    WSGISessionManagerClient, AuthNServiceInvalidCredentials
138   
139class BasicSessionManagerOpenIDAuthNInterface(BasicAuthNInterface):
140    '''Authentication interface class for OpenIDProviderMiddleware to enable
141    authentication to a Session Manager instance running in the same WSGI
142    stack or via a SOAP call to a remote service.  This is a basic test
143    interface.  See sessionmanager module for a full implementation linking to
144    a database via SQLAlchemy
145    '''
146   
147    def __init__(self, **prop):
148        """Extends BasicAuthNInterface initialising Session Manager Client
149       
150        @type **prop: dict
151        @param **prop: set properties via keywords
152        @raise AuthNInterfaceConfigError: error with configuration
153        """
154        user2Identifier = prop.pop('username2UserIdentifiers')
155        if user2Identifier:
156            self._username2Identifier = {}
157            for i in user2Identifier.split():
158                username, identifierStr = i.strip().split(':')
159                identifiers = tuple(identifierStr.split(','))
160                self._username2Identifier[username] = identifiers
161        else:
162            raise AuthNInterfaceConfigError('No "user2Identifier" config '
163                                            'option found')
164
165        self._client = WSGISessionManagerClient(**prop)
166       
167        # This is set at login
168        self.sessionId = None
169       
170    def logon(self, environ, userIdentifier, username, password):
171        """Interface login method
172       
173        @type environ: dict
174        @param environ: standard WSGI environ parameter
175       
176        @type username: basestring
177        @param username: user identifier
178       
179        @type password: basestring
180        @param password: corresponding password for username givens
181       
182        @raise AuthNInterfaceUsername2IdentifierMismatch: no OpenID
183        identifiers match the given username
184        @raise AuthNInterfaceInvalidCredentials: invalid username/password
185        """       
186        if userIdentifier is not None and \
187           userIdentifier not in self._username2Identifier.get(username):
188            raise AuthNInterfaceUsername2IdentifierMismatch()
189       
190        try:
191            self._client.environ = environ
192            connectResp = self._client.connect(username, passphrase=password)
193            self.sessionId = connectResp[-1]
194            log.debug("Connected to Session Manager with session ID: %s", 
195                      self.sessionId)
196
197        except AuthNServiceInvalidCredentials, e:
198            log.exception(e)
199            raise AuthNInterfaceInvalidCredentials()
200
201    def logout(self):
202        """logout from the Session Manager
203        """
204        try:
205            self._client.disconnect(sessID=self.sessionId)
206           
207        except Exception, e:
208            log.exception(e)
209            raise AuthNInterfaceInvalidCredentials()
Note: See TracBrowser for help on using the repository browser.