source: TI12-security/trunk/python/ndg.security.server/ndg/security/server/wsgi/openid/relyingparty/validation.py @ 5491

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

Nearing completion of IdP Validator implementation. This enables OpenID Relying Parties to whitelist OpenID Providers. Code based on ESG equivalent

Line 
1"""NDG Security OpenID Relying Party Provider Validation module
2
3Based on the Earth System Grid IdPValidator interface for restricting
4OpenID Providers that a Relying Party may connect to
5
6An Identity Provider (IdP) is equivalent to an OpenID Provider
7
8NERC DataGrid Project
9"""
10__author__ = "P J Kershaw"
11__date__ = "09/06/2009"
12__copyright__ = "(C) 2009 Science and Technology Facilities Council"
13__license__ = "BSD - see top-level directory for LICENSE file"
14__contact__ = "Philip.Kershaw@stfc.ac.uk"
15__revision__ = "$Id$"
16import logging
17log = logging.getLogger(__name__)
18import os
19
20from elementtree import ElementTree
21from ndg.security.common.utils import getLocalName
22from ndg.security.common.utils.classfactory import instantiateClass
23   
24class _ConfigBase(object):
25    """Base class for IdP Validator and Attribute Provider configuration
26    """
27   
28    def __init__(self):
29        self._className = None
30        self._configFile = None
31        self._parameters = {}
32   
33    def _set_className(self, className):
34        self._className = className
35   
36    def _get_className(self):
37        return self._className
38   
39    className = property(fget=_get_className,
40                         fset=_set_className)
41
42    def _get_configFile(self):
43        return self._configFile
44   
45    def _set_configFile(self, configFile):
46        self._configFile = configFile
47
48    configFile = property(fget=_get_configFile,
49                          fset=_set_configFile)
50   
51    def _get_parameters(self):
52        return self._parameters
53   
54    def _set_parameters(self, parameters):   
55        self._parameters = parameters
56   
57    parameters = property(fget=_get_parameters,
58                          fset=_set_parameters)
59
60class IdPValidatorConfig(_ConfigBase):
61    """Container for IdP validator configuration"""
62   
63class AttributeProviderConfig(_ConfigBase):
64    """Container for Attribute Provider configuration"""
65   
66class XmlConfigReaderError(Exception):
67    """Raise from XmlConfigReader"""
68     
69class XmlConfigReader(object):
70
71    def getValidators(self, filePath): 
72        validators = None
73
74        doc = self.__parseConfigFile(filePath)
75        if doc is not None:
76            validators = extractValidatorConfigs(doc)
77       
78        return validators
79   
80    def getAttrProviders(self, filePath):
81   
82        attrProviders = None
83
84        doc = self.__parseConfigFile(filePath)
85        if doc is not None:
86            attrProviders = self.extractAttrProviderConfigs(doc)
87       
88        return attrProviders
89   
90    def __parseConfigFile(self, filePath):
91        """Read in the XML configuration file
92        @type filePath: basestring
93        @param filePath: XML file to read
94        """
95        elem = ElementTree.parse(source)
96        root = elem.getroot()
97       
98        return root
99
100    def __extractValidatorConfigs(self, root):
101        """Parse Validator configuration from the XML config file
102        @type root: ElementTree.Element
103        @param root: root element of parsed XML config file
104        """
105        validators = []
106        validatorConfig = None
107        parameters = {}
108       
109        for elem in root:
110            if getLocalName(elem).lower() == "validator":   
111                if validatorConfig is not None:
112                    validatorConfig.setParameters(parameters)
113                    validators.append(validatorConfig)
114               
115                validatorConfig = IdPValidatorConfig()
116                validatorConfig.className = elem.attrib["name"]
117                validatorConfig.setClassName(className)
118           
119            elif getLocalName(elem).lower() == "parameter":
120                if elem.attrib["name"] in parameters:
121                    raise XmlConfigReaderError('Duplicate parameter name "%s" '
122                                               'found' % elem.attrib["name"])
123                   
124                parameters[elem.attrib["name"]] = elem.attrib["value"]
125           
126        if validatorConfig is not None:
127            validatorConfig.parameters = parameters
128            validators.append(validatorConfig)
129       
130        return validators
131   
132    def __extractAttrProviderConfigs(self, root):
133        attrProviders = []
134        validatorConfig = None
135        parameters = {}
136
137        for elem in root:
138            if getLocalName(elem).lower() == "attributeprovider":
139                if validatorConfig is not None:
140                    validatorConfig.parameters = parameters
141                    attrProviders.append(validatorConfig)
142               
143                validatorConfig = AttributeProviderConfig()
144                validatorConfig.className(elem.attrib("name"))
145           
146            elif getLocalName(elem).lower() == "parameter":
147                if elem.attrib["name"] in parameters:
148                    raise XmlConfigReaderError('Duplicate parameter name "%s" '
149                                               'found' % elem.attrib["name"])
150           
151                parameters[elem.attrib["name"]] = elem.attrib["value"]
152           
153        if validatorConfig != None:
154            validatorConfig.parameters = parameters
155            attrProviders.append(validatorConfig)
156       
157        return attrProviders
158
159
160class IdPValidatorException(Exception):
161    """Base class for IdPValidator exceptions"""
162   
163class IdPInvalidException(IdPValidatorException):
164    """Raise from IdPValidator.validate if the IdP is not acceptable"""
165
166class ConfigException(IdPValidatorException):
167    """Problem with configuration for the IdP Validator class"""
168 
169
170class IdPValidator(object):
171    '''Interface class for implementing OpenID Provider validators for a
172    Relying Party to call'''
173   
174    def __init__(self):
175        raise NotImplementedError()
176
177    def initialize(self, parameters):
178        '''@raise ConfigException:''' 
179        raise NotImplementedError()
180       
181    def validate(self, idpEndpoint, idpIdentity):
182        '''@raise IdPInvalidException:
183        @raise ConfigException:''' 
184        raise NotImplementedError()
185   
186
187class IdPValidationDriver(object):
188    """Parse an XML Validation configuration containing XML Validators and
189    execute these against the Provider (IdP) input"""   
190   
191    def __init__(self):
192        self._idpValidators = None
193       
194    def _get_idpValidators(self):
195        return self._idpValidators
196   
197    def _set_idpValidators(self, idpValidators):
198        self._idpValidators = idpValidators
199       
200    idpValidators = property(fget=_get_idpValidators,
201                             fset=_set_idpValidators,
202                             doc="list of IdP Validators")
203   
204    def performIdPValidation(self, identifier, discoveries):
205        validators = []
206       
207        idpConfigFilePath = os.environ("IDP_CONFIG_FILE")
208        if idpConfigFilePath is None:
209            log.warning("IdPValidationDriver.performIdPValidation: No IdP "
210                        "Configuration file was set")
211            return discoveries
212       
213        configReader = XmlConfigReader()
214        validatorConfigs = configReader.getValidators(idpConfigFilePath)
215
216        for idpConfig in validatorConfigs:
217       
218            className = idpConfig.className
219            parameters = idpConfig.parameters
220
221            try:
222                validator = instantiateClass(className,
223                                             None, 
224                                             objectType=IdPValidator)
225                validator.initialize(parameters)
226                validators.append(validator)
227           
228            except Exception, e: 
229                log.error("Failed to initialise validator: " + e)
230           
231        if self.idPValidators is not None:
232            validators += self.idPValidators
233
234        log.info(len(validators) + " IdPValidators initialised")
235
236        # validate the discovered endpoints
237        if len(validators) > 0:
238       
239            newDiscoveries = []
240            for validator in validators:   
241                for discoveryInfo in discoveries:
242                    try:                   
243                        validator.validate(discoveryInfo.getOPEndpoint(), 
244                                           identifier.getIdentifier())
245
246                        log.info("Whitelist Validator Accepting endpoint: " + 
247                                 discoveryInfo.getOPEndpoint())
248
249                        newDiscoveries.append(discoveryInfo)
250                   
251                    except Exception, e:       
252                        log.info("Whitelist Validator rejecting endpoint: %s: "
253                                 "%s", discoveryInfo.getOPEndpoint(), e)
254                       
255            if len(newDiscoveries) > 0:
256                discoveries = newDiscoveries
257                log.info("Found %d valid endpoints." % len(discoveries))
258            else:     
259                discoveries = None
260                raise IdPInvalidException("No valid endpoints were found "
261                                          "after validation.")
262 
263        return discoveries
Note: See TracBrowser for help on using the repository browser.