source: TI12-security/trunk/python/ndg.security.common/ndg/security/common/authz/pdp/browse.py @ 3894

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg.security.common/ndg/security/common/authz/pdp/browse.py@3894
Revision 3894, 13.0 KB checked in by pjkersha, 13 years ago (diff)

Renamed moles Policy Decision Point code to browse as it deals with CSML also.

Line 
1"""NDG Policy Decision Point for NDG Browse - access constraints for a
2resource are determined from MOLES access constraints in the data
3
4NERC Data Grid Project
5"""
6__author__ = "P J Kershaw"
7__date__ = "04/04/08"
8__copyright__ = "(C) 2008 STFC & NERC"
9__contact__ = "P.J.Kershaw@rl.ac.uk"
10__license__ = \
11"""This software may be distributed under the terms of the Q Public
12License, version 1.0 or later."""
13__contact__ = "P.J.Kershaw@rl.ac.uk"
14__revision__ = "$Id:gatekeeper.py 3079 2007-11-30 09:39:46Z pjkersha $"
15
16import logging
17log = logging.getLogger(__name__)
18
19import sys # tracefile config param may be set to e.g. sys.stderr
20import urllib2
21import socket
22from ConfigParser import SafeConfigParser
23
24# For parsing of properties file
25from os.path import expandvars as expVars
26
27from ndg.security.common.authz.pdp import PDPInterface, PDPError, \
28    PDPUserAccessDenied, PDPUserNotLoggedIn, PDPMissingResourceConstraints
29   
30from ndg.security.common.SessionMgr import SessionMgrClient, SessionNotFound,\
31    SessionCertTimeError, SessionExpired, InvalidSession, \
32    AttributeRequestDenied                   
33   
34from ndg.security.common.X509 import X500DN               
35
36class InvalidAttributeCertificate(PDPError):
37    "The certificate containing authorisation roles is invalid"
38    def __init__(self, msg=None):
39        PDPError.__init__(self, msg or InvalidAttributeCertificate.__doc__)
40   
41class SessionExpiredMsg(PDPError):
42    'Session has expired.  Please re-login'
43    def __init__(self, msg=None):
44        PDPError.__init__(self, msg or SessionExpiredMsg.__doc__)
45
46class InvalidSessionMsg(PDPError):
47    'Session is invalid.  Please try re-login'
48    def __init__(self, msg=None):
49        PDPError.__init__(self, msg or InvalidSessionMsg.__doc__)
50
51class InitSessionCtxError(PDPError):
52    'A problem occured initialising a session connection'
53    def __init__(self, msg=None):
54        PDPError.__init__(self, msg or InitSessionCtxError.__doc__)
55
56class AttributeCertificateRequestError(PDPError):
57    'A problem occured requesting a certificate containing authorisation roles'
58    def __init__(self, msg=None):
59        PDPError.__init__(self,msg or AttributeCertificateRequestError.__doc__)
60
61class URLCannotBeOpened(PDPError):
62    """Raise from canURLBeOpened PullModelHandler class method
63    if URL is invalid - this method is used to check the AA
64    service"""
65
66
67class MolesPDP(PDPInterface):
68    """Make access control decision based on a MOLES access constraint
69    (applies to CSML too) and user security token"""
70
71    molesXPathQueryPfx = \
72'{http://ndg.nerc.ac.uk/moles}simpleCondition/{http://ndg.nerc.ac.uk/moles}'
73    roleXPathQuery = molesXPathQueryPfx + 'attrauthRole'
74    roleXPathQuery = molesXPathQueryPfx + 'dgAttributeAuthority'
75
76    defParam = {'aaURI': '',
77                'sslCACertFilePathList': [],
78                'tracefile': '',
79                'acCACertFilePathList': [], 
80                'acIssuer': ''}
81           
82   
83    def __init__(self, cfgFilePath=None, **cfgKw):
84        """Initialise settings for WS-Security and SSL for SOAP
85        call to Session Manager
86       
87        @type uri: string
88        @param uri: URI corresponding to data granule ID
89       
90        @type securityElement: ElementTree Element
91        @param securityElement: MOLES security constraint containing role and
92        Attribute Authority URI. In xml, could look like:
93        <moles:effect>allow</moles:effect>
94            <moles:simpleCondition>
95            <moles:dgAttributeAuthority>https://glue.badc.rl.ac.uk/AttributeAuthority</moles:dgAttributeAuthority>
96            <moles:attrauthRole>coapec</moles:attrauthRole>
97        </moles:simpleCondition>
98        NB: xmlns:moles="http://ndg.nerc.ac.uk/moles
99        """
100       
101        self.cfgFilePath = cfgFilePath
102        self.resrcURI = None
103        self.securityElement = None
104        self.userHandle = None
105       
106        # Set from config file
107        if cfgFilePath:
108            self._readConfig()
109           
110        # Separate keywords into PDP and WS-Security specific items
111        paramNames = cfgKw.keys()
112        for paramName in paramNames:
113            if paramName in MolesPDP.defParam:
114                # Keywords are deleted as they are set
115                setattr(self, paramName, cfgKw.pop('paramName'))
116               
117        # Remaining keys must be for WS-Security config
118        self.wssCfg = cfgKw   
119
120           
121    def _readConfig(self, section='DEFAULT'):
122        '''Read PDP configuration file'''
123        cfg = SafeConfigParser()
124        cfg.read(self.cfgFilePath)
125       
126        # Copy directly into attribute of this object
127        for paramName, paramVal in MolesPDP.defParam.items():
128            if isinstance(paramVal, list):
129                paramListVal = expVars(cfg.get(section, paramName)).split()
130                setattr(self, paramName, paramListVal)
131            else:
132                setattr(self, paramName, expVars(cfg.get(section, paramName)))           
133
134
135    def accessPermitted(self, resrcHandle, userHandle, accessType=None):
136        """Make an access control decision based on whether the user is
137        authenticated and has the required roles
138       
139        @type resrcHandle: dict
140        @param resrcHandle: dict 'uri' = resource URI, 'securityElement' =
141        ElementTree type MOLES security Element
142       
143        @type userHandle: dict
144        @param userHandle: dict with keys 'sid' = user session ID,
145        'h' = Session Manager URI
146       
147        @type accessType: -
148        @param accessType: not implemented - logs a warning if set
149       
150        @rtype: bool
151        @return: True if access permitted; False if denied or else raise
152        an Exception
153       
154        @type uri: string
155        @param uri: URI corresponding to data granule ID
156       
157        @type: ElementTree Element
158        @param securityElement: MOES security constraint containing role and
159        Attribute Authority URI. In xml, could look like:
160        <moles:effect>allow</moles:effect>
161            <moles:simpleCondition>
162            <moles:dgAttributeAuthority>https://glue.badc.rl.ac.uk/AttributeAuthority</moles:dgAttributeAuthority>
163            <moles:attrauthRole>coapec</moles:attrauthRole>
164        </moles:simpleCondition>
165        NB: xmlns:moles="http://ndg.nerc.ac.uk/moles"
166       
167        @type: pylons.session
168        @param userHandle: dict-like session object containing security
169        tokens.  Resets equivalent object attribute."""
170         
171        # Resource handle contains URI and ElementTree resource security
172        # element
173        try:
174            self.resrcURI = resrcHandle['uri']
175            self.securityElement = resrcHandle['securityElement'] 
176        except KeyError, e:
177            log.error("Resource handle missing key %s" % e)
178            raise PDPMissingResourceConstraints()
179       
180        # User handle contains 'h' = Session Manager URI and 'sid' user
181        # Session ID
182        try:
183            self.smURI = userHandle['h']
184            self.userSessID = userHandle['sid']
185        except KeyError, e:
186            log.error("User handle missing key %s" % e)
187            raise PDPUserNotLoggedIn()
188
189           
190        roleElem = self.securityElement.find(MolesPDP.roleXPathQuery)
191        if roleElem is None or not roleElem.text:
192            log.error("PDP: role not set in MOLES security " + \
193                      "constraints")
194            raise PDPMissingResourceConstraints()
195       
196        self.reqRole = roleElem.text
197
198        aaElem = self.securityElement.find(MolesPDP.aaXPathQuery)
199       
200        # Sanity check on Attribute Authority URI
201        if aaElem and aaElem.text:
202            aaURI = aaElem.text
203           
204            # Check Attribute Authority address
205            try:
206                MolesPDP.urlCanBeOpened(aaURI)
207            except URLCannotBeOpened, e:
208                # Catch situation where either Attribute Authority address in the
209                # data invalid or none was set.  In this situation verify
210                # against the Attribute Authority set in the config
211   
212                log.warning('PDP: MOLES security constraint ' + \
213                            'Attribute Authority address is invalid - ' + \
214                            'defaulting to config file setting: %s; ' % \
215                            self.aaURI + \
216                            'error message is: %s' % e)
217                aaURI = self.aaURI
218        else:
219            log.warning("PDP: Attribute Authority element not " + \
220                        "set in MOLES security constraints - defaulting " + \
221                        "to config file setting: %s" % self.aaURI)
222            aaURI = self.aaURI
223   
224        # Retrieve Attirbute Certificate from user's session held by
225        # Session Manager
226        attCert = self._pullUserSessionAttCert(aaURI)
227       
228        # Check its validity
229        self._checkAttCert(attCert)
230                   
231        log.info('PDP - access granted for user "%s" ' % \
232                 attCert.userId + \
233                 'to "%s" secured with role "%s" ' % \
234                 (self.resrcURI, self.reqRole) + \
235                 'using attribute certificate:\n\n%s' % attCert)
236           
237       
238    def _pullUserSessionAttCert(self, aaURI):
239        """Check to see if the Session Manager can deliver an Attribute
240        Certificate with the required role to gain access to the resource
241        in question
242       
243        @type aaURI: string
244        @param aaURI: address of Attribute Authority that the Session Manager
245        will call in order to request an AC on behalf of the user"""
246       
247        try:
248            # Create Session Manager client
249            self.smClnt = SessionMgrClient(uri=self.smURI,
250                            cfgFilePath=self.cfgFilePath,
251                            cfgFileSection='WS-Security',
252                            sslCACertFilePathList=self.sslCACertFilePathList,
253                            tracefile=self.tracefile,
254                            **self.wssCfg) 
255        except Exception, e:
256            log.error("PDP: creating Session Manager client: %s"%e)
257            raise InitSessionCtxError()
258       
259                 
260        try:
261            # Make request for attribute certificate
262            attCert = self.smClnt.getAttCert(attAuthorityURI=aaURI,
263                                             sessID=self.userSessID,
264                                             reqRole=self.reqRole)
265            return attCert
266       
267        except AttributeRequestDenied, e:
268            log.info(\
269            "PDP -request for attribute certificate denied: %s" % e)
270            raise PDPUserAccessDenied()
271       
272        except SessionNotFound, e:
273            log.info("PDP -no session found: %s" % e)
274            raise PDPUserNotLoggedIn()
275
276        except SessionExpired, e:
277            log.info("PDP -session expired: %s" % e)
278            raise InvalidSessionMsg()
279
280        except SessionCertTimeError, e:
281            log.info("PDP -session cert. time error: %s" % e)
282            raise InvalidSessionMsg()
283           
284        except InvalidSession, e:
285            log.info("PDP -invalid user session: %s" % e)
286            raise InvalidSessionMsg()
287
288        except Exception, e:
289            log.error("PDP request for attribute certificate: %s" % e)
290            raise AttributeCertificateRequestError()
291       
292
293    def _checkAttCert(self, attCert):
294        '''Check attribute certificate is valid
295       
296        @type attCert: ndg.security.common.AttCert.AttCert
297        @param attCert: attribute certificate to be check for validity'''
298        attCert.certFilePathList = self.acCACertFilePathList
299        try:
300            attCert.isValid(raiseExcep=True)
301        except Exception, e:
302            log.error("Attribute Certificate: %s" % e)
303            raise InvalidAttributeCertificate() 
304         
305        # Check it's issuer is as expected - Convert to X500DN to do equality
306        # test
307        acIssuerDN = X500DN(self.acIssuer)
308        if attCert.issuerDN != acIssuerDN:
309            log.info('PDP -access denied: Attribute Certificate ' + \
310                'issuer DN, "%s" ' % attCert.issuerDN + \
311                'must match this data provider\'s Attribute Authority ' + \
312                'DN: "%s"' % acIssuerDN)
313            raise InvalidAttributeCertificate()
314
315
316    @classmethod
317    def urlCanBeOpened(cls, url, timeout=5, raiseExcep=True):
318       """Check url can be opened - adapted from
319       http://mail.python.org/pipermail/python-list/2004-October/289601.html
320       """
321   
322       found = False
323       defTimeOut = socket.getdefaulttimeout()
324       try:
325           socket.setdefaulttimeout(timeout)
326
327           try:
328               urllib2.urlopen(url)
329           except (urllib2.HTTPError, urllib2.URLError,
330                   socket.error, socket.sslerror, AttributeError):
331               if raiseExcep:
332                   raise URLCannotBeOpened()
333           
334           found = True
335         
336       finally:
337           socket.setdefaulttimeout(defTimeOut)
338           
339       return found
340     
341   
342def makeDecision(resrcHandle, userHandle, accessType=None, **kw):
343    '''One call Wrapper interface to PDP'''
344    return MolesPDP(**kw)(resrcHandle, userHandle)
345
346 
Note: See TracBrowser for help on using the repository browser.