source: TI12-security/trunk/python/ndg_security_server/ndg/security/server/wsgi/openid/provider/axinterface/csv.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/axinterface/csv.py@5786
Revision 5786, 7.6 KB checked in by pjkersha, 10 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 OpenID Provider AX Interface for CSV file based attribute store
2
3For test purposes only
4
5NERC DataGrid Project
6"""
7__author__ = "P J Kershaw"
8__date__ = "30/09/09"
9__copyright__ = "(C) 2009 Science and Technology Facilities Council"
10__license__ = "BSD - see LICENSE file in top-level directory"
11__contact__ = "Philip.Kershaw@stfc.ac.uk"
12__revision__ = "$Id$"
13import logging
14log = logging.getLogger(__name__)
15
16import re
17
18from ndg.security.server.wsgi.openid.provider.axinterface import AXInterface, \
19    AXInterfaceConfigError, MissingRequiredAttrs
20from ndg.security.server.wsgi.openid.provider import AbstractAuthNInterface, \
21    OpenIDProviderMiddleware
22
23class CSVFileAXInterface(AXInterface):
24    """OpenID Provider Attribute Exchange Interface based on a Comma Separated
25    Variable file containing user identities and associated attributes. 
26    For test/development purposes
27    only.
28   
29    The expected file format is:
30   
31    <OpenID>, <attr 1>, <attr 2>, ... <attr N>
32    """
33    __slots__ = (
34        "csvFilePath",
35        "attributeNames",
36        "attributeMap",
37    )
38    IDENTITY_URI_SESSION_KEYNAME = \
39                        OpenIDProviderMiddleware.IDENTITY_URI_SESSION_KEYNAME
40   
41    def __init__(self, **properties):
42        """
43        @param properties: file path to Comma Separated file
44        containing user ids and roles
45        @type properties: dict
46        """
47        self.__csvFilePath = None
48        self.__attributeNames = []
49        self.__attributeMap = {}
50       
51        self.setProperties(**properties)
52        if self.csvFilePath is not None:
53            self.read()
54
55    def _getAttributeNames(self):
56        return self.__attributeNames
57
58    def _setAttributeNames(self, value):
59        """@param value: if a string, it will be parsed into a list delimiting
60        elements by whitespace
61        @type value: basestring/tuple or list
62        """
63        if isinstance(value, (list, tuple)):
64            self.__attributeNames = list(value)
65           
66        elif isinstance(value, basestring):
67            self.__attributeNames = value.split() 
68        else:
69            raise TypeError('Expecting string, list or tuple type for '
70                            '"attributeNames"; got %r' % type(value))
71       
72    attributeNames = property(fget=_getAttributeNames, 
73                              fset=_setAttributeNames, 
74                              doc="list of attribute names supported.  The "
75                                  "order of the names is important and "
76                                  "determines the order in which they will be "
77                                  "assigned from the columns in the CSV file")
78
79    def setProperties(self, **properties):
80        for name, val in properties.items():
81            setattr(self, name, val)
82   
83    def read(self):
84        csvFile = open(self.csvFilePath)
85        lines = csvFile.readlines()
86       
87        nAttributes = len(self.attributeNames)
88        for line in lines:
89            fields = re.split(',\W*', line.strip())
90           
91            # Dictionary keyed by user ID with each val itself a dict keyed
92            # by attribute name
93            self.attributeMap[fields[0]] = dict(zip(self.attributeNames,
94                                                    fields[1:nAttributes+1]))
95               
96    def _getCsvFilePath(self):
97        return self.__csvFilePath
98
99    def _setCsvFilePath(self, value):
100        if not isinstance(value, basestring):
101            raise TypeError('Expecting string type for "csvFilePath"; got %r'
102                            % type(value))
103        self.__csvFilePath = value
104
105    csvFilePath = property(fget=_getCsvFilePath, 
106                           fset=_setCsvFilePath, 
107                           doc="file path to Comma Separated Variable format "
108                               "file containing user IDs and attributes")
109   
110    def _getAttributeMap(self):
111        return self.__attributeMap
112
113    def _setAttributeMap(self, value):
114        self.__attributeMap = value
115
116    attributeMap = property(fget=_getAttributeMap, 
117                            fset=_setAttributeMap, 
118                            doc="Dictionary of attributes keyed by user ID")
119   
120    def __call__(self, ax_req, ax_resp, authnInterface, authnCtx):
121        """Add the attributes to the ax_resp object requested in the ax_req
122        object.  If it is not possible to return them, raise
123        MissingRequiredAttrs error
124       
125        @type ax_req: openid.extensions.ax.FetchRequest
126        @param ax_req: attribute exchange request object.  To find out what
127        attributes the Relying Party has requested for example, call
128        ax_req.getRequiredAttrs()
129        @type ax_resp: openid.extensions.ax.FetchResponse
130        @param ax_resp: attribute exchange response object.  This method should
131        update the settings in this object.  Use addValue and setValues methods
132        @type authnInterface: AbstractAuthNInterface
133        @param authnInterface: custom authentication interface set at login. 
134        See ndg.security.server.openid.provider.AbstractAuthNInterface for more
135        information
136        @type authnCtx: dict like
137        @param authnCtx: session containing authentication context information
138        such as username and OpenID user identifier URI snippet
139        """
140        log.debug('CSVFileAXInterface.__call__  ...')
141       
142        identityURI = authnCtx.get(
143                                CSVFileAXInterface.IDENTITY_URI_SESSION_KEYNAME)
144        if identityURI is None:
145            raise AXInterfaceConfigError("No OpenID user identifier set in "
146                                         "session context")
147       
148        requiredAttributeURIs = ax_req.getRequiredAttrs()
149           
150        userAttributeMap = self.attributeMap.get(identityURI)
151        if userAttributeMap is None:
152            raise AXInterfaceConfigError("No attribute entry for user [%s] " %
153                                         identityURI)
154                                     
155        missingAttributeURIs = [
156            requiredAttributeURI
157            for requiredAttributeURI in requiredAttributeURIs
158            if requiredAttributeURI not in self.attributeNames
159        ]
160        if len(missingAttributeURIs) > 0:
161            raise MissingRequiredAttrs("OpenID Provider does not support "
162                                       "release of these attributes required "
163                                       "by the Relying Party: %s" %
164                                       ', '.join(missingAttributeURIs))
165       
166        # Add the required attributes
167        for requiredAttributeURI in requiredAttributeURIs:
168            log.info("Adding required AX parameter %s=%s ...", 
169                     requiredAttributeURI,
170                     userAttributeMap[requiredAttributeURI])
171           
172            ax_resp.addValue(requiredAttributeURI,
173                             userAttributeMap[requiredAttributeURI])
174           
175        # Append requested attribute if available
176        for requestedAttributeURI in ax_req.requested_attributes.keys():
177            if requestedAttributeURI in self.attributeNames:
178                log.info("Adding requested AX parameter %s=%s ...", 
179                         requestedAttributeURI,
180                         userAttributeMap[requestedAttributeURI])
181               
182                ax_resp.addValue(requestedAttributeURI,
183                                 userAttributeMap[requestedAttributeURI])
184            else:
185                log.info("Skipping Relying Party requested AX parameter %s: "
186                         "this parameter is not available", 
187                         requestedAttributeURI)
188               
Note: See TracBrowser for help on using the repository browser.