source: TI05-delivery/ows_framework/trunk/ows_server/ows_server/models/ndgSecurity.py @ 3018

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI05-delivery/ows_framework/trunk/ows_server/ows_server/models/ndgSecurity.py@3018
Revision 3018, 9.8 KB checked in by pjkersha, 12 years ago (diff)

More complete logging info access request - #854

ows_server/models/ndgSecurity.py:

  • additional uri arg corresponding to data granule ID
  • access granted log message now includes the above

ows_server/lib/ndgInterface.py: modified HandleSecurity? call to include data granule ID.

Line 
1import sys # tracefile config param may be set to e.g. sys.stderr
2import urllib2
3import socket
4
5from ows_server.lib.base import *
6from pylons import request
7import logging
8log = logging.getLogger(__name__)
9
10from ows_common.exception_report import OwsError
11from ows_server.lib.security_util import SecuritySession
12from ndg.security.common.SessionMgr import SessionMgrClient, SessionNotFound,\
13    SessionCertTimeError, SessionExpired, InvalidSession, \
14    AttributeRequestDenied
15   
16def HandleSecurity(*args):
17    return SecurityHandler(*args)()
18
19class URLCannotBeOpened(Exception):
20    """Raise from canURLBeOpened SecurityHandler class method
21    if URL is invalid - this method is used to check the AA
22    service"""
23
24class SecurityHandler(object):
25    """Make access control decision based on CSML constraint and user security
26    token"""
27   
28    AccessAllowedMsg = "Access Allowed"
29    InvalidAttributeCertificate = \
30            "The certificate containing your authorisation roles is invalid"
31    NotLoggedInMsg = 'Not Logged in'
32    SessionExpiredMsg = 'Session has expired.  Please re-login'
33    InvalidSessionMsg = 'Session is invalid.  Please try re-login'
34    InvalidSecurityCondition = 'Invalid Security Condition'
35
36    def __init__(self, uri, securityElement, securityTokens):
37        """Initialise settings for WS-Security and SSL for SOAP
38        call to Session Manager
39       
40        @type uri: string
41        @param uri: URI corresponding to data granule ID
42       
43        @type securityElement: ElementTree Element
44        @param securityElement: MOLES security constraint containing role and
45        Attribute Authority URI. In xml, could look like:
46        <moles:effect>allow</moles:effect>
47            <moles:simpleCondition>
48            <moles:dgAttributeAuthority>https://glue.badc.rl.ac.uk/AttributeAuthority</moles:dgAttributeAuthority>
49            <moles:attrauthRole>coapec</moles:attrauthRole>
50        </moles:simpleCondition>
51        NB: xmlns:moles="http://ndg.nerc.ac.uk/moles
52       
53        @type: pylons.session
54        @param securityTokens: dict-like session object containing security
55        tokens"""
56       
57        self.uri = uri
58        self.securityElement = securityElement
59        self.securityTokens = securityTokens
60
61
62    def __call__(self, **kw):
63        """Convenience wrapper for checkAccess"""
64        return self.checkAccess(**kw)
65
66
67    def checkAccess(self, 
68                    uri=None, 
69                    securityElement=None, 
70                    securityTokens=None):
71        """Make an access control decision based on whether the user is
72        authenticated and has the required roles
73       
74        @type uri: string
75        @param uri: URI corresponding to data granule ID
76       
77        @type: ElementTree Element
78        @param securityElement: MOES security constraint containing role and
79        Attribute Authority URI. In xml, could look like:
80        <moles:effect>allow</moles:effect>
81            <moles:simpleCondition>
82            <moles:dgAttributeAuthority>https://glue.badc.rl.ac.uk/AttributeAuthority</moles:dgAttributeAuthority>
83            <moles:attrauthRole>coapec</moles:attrauthRole>
84        </moles:simpleCondition>
85        NB: xmlns:moles="http://ndg.nerc.ac.uk/moles"
86       
87        @type: pylons.session
88        @param securityTokens: dict-like session object containing security
89        tokens.  Resets equivalent object attribute."""
90         
91        # tokens and element may be set from __init__ or as args to this
92        # method.  If the latter copy them into self 
93        if uri:
94            self.uri = uri
95           
96        if securityTokens:
97            self.securityTokens = securityTokens
98                           
99        if securityElement:
100            self.securityElement=securityElement
101     
102        # Check self.securityTokens - if not set then the user mustn't be
103        # logged in.  This situation is possible if a user has been denied
104        # access to data and then tried to logout - after log out they are
105        # redirected back to the page where they tried accessing data but this
106        # time they will have no security credential set
107        if not self.securityTokens:
108            # Try to recover and do something sensible
109            #
110            # TODO: this adds insult to injury if the person has just been
111            # denied access to data.  Instead do a redirect back to the
112            # discovery page?
113            # P J Kershaw 10/08/07
114            log.info("Exiting from Gatekeeper: user is not logged in")
115            return False, self.__class__.NotLoggedInMsg
116           
117        xpathr='{http://ndg.nerc.ac.uk/moles}simpleCondition/{http://ndg.nerc.ac.uk/moles}attrauthRole'
118        xpathaa='{http://ndg.nerc.ac.uk/moles}simpleCondition/{http://ndg.nerc.ac.uk/moles}dgAttributeAuthority'
119        roleE,aaE=self.securityElement.find(xpathr),self.securityElement.find(xpathaa)
120        if roleE is None:
121            log.error("Gatekeeper: role not found in dataset element: %s" % \
122                      self.securityElement)
123            return False, self.__class__.InvalidSecurityCondition
124       
125        self.reqRole=roleE.text
126       
127        # Check Attribute Authority address
128        try:
129            SecurityHandler.urlCanBeOpened(aaE.text)
130        except (URLCannotBeOpened, AttributeError):
131            # Catch situation where either Attribute Authority address in the
132            # data invalid or none was set.  In this situation verify
133            # against the Attribute Authority set in the config
134            log.info('Gatekeeper: Attribute Authority address is invalid ' + \
135                     'in data "%s" - defaulting to config file setting' % \
136                     self.securityElement)
137            self.reqAAURI = g.securityCfg.aaURI
138   
139        # Create Session Manager client
140        self.smClnt = SessionMgrClient(uri=self.securityTokens['h'],
141                    sslCACertFilePathList=g.securityCfg.sslCACertFilePathList,
142                    sslPeerCertCN=g.securityCfg.sslPeerCertCN,
143                    signingCertFilePath=g.securityCfg.wssCertFilePath,
144                    signingPriKeyFilePath=g.securityCfg.wssPriKeyFilePath,
145                    signingPriKeyPwd=g.securityCfg.wssPriKeyPwd,
146                    caCertFilePathList=g.securityCfg.wssCACertFilePathList,
147                    tracefile=g.securityCfg.tracefile)       
148       
149        return self.__checkAttCert()
150           
151
152   
153    def __checkAttCert(self):
154        """Check to see if the Session Manager can deliver an Attribute
155        Certificate with the required role to gain access to the resource
156        in question"""
157           
158        try:
159            # Make request for attribute certificate
160            attCert = self.smClnt.getAttCert(attAuthorityURI=self.reqAAURI,
161                                         sessID=self.securityTokens['sid'],
162                                         reqRole=self.reqRole)
163        except AttributeRequestDenied, e:
164            log.info(\
165                "Gatekeeper - request for attribute certificate denied: %s"%e)
166            return False, str(e)
167       
168        except SessionNotFound, e:
169            # Clear the security details from the session object
170            SecuritySession.delete()
171            log.info("Gatekeeper - no session found: %s" % e)
172            return False, self.__class__.NotLoggedInMsg
173
174        except SessionExpired, e:
175            # Clear the security details from the session object
176            SecuritySession.delete()
177            log.info("Gatekeeper - session expired: %s" % e)
178            return False, self.__class__.SessionExpiredMsg
179
180        except SessionCertTimeError, e:
181            # Clear the security details from the session object
182            SecuritySession.delete()
183            log.info("Gatekeeper - session cert. time error: %s" % e)
184            return False, self.__class__.InvalidSessionMsg
185           
186        except InvalidSession, e:
187            SecuritySession.delete()
188            log.info("Gatekeeper - invalid user session: %s" % e)
189            return False, self.__class__.InvalidSessionMsg
190
191        except Exception, e:
192            raise OwsError, "Gatekeeper request for attribute certificate: "+\
193                            str(e)
194                           
195        # Check attribute certificate is valid
196        attCert.certFilePathList = g.securityCfg.acCACertFilePathList
197        attCert.isValid(raiseExcep=True)
198           
199        # Check it's issuer is as expected
200        if attCert.issuer != g.securityCfg.acIssuer:
201            log.info('Gatekeeper - access denied: Attribute Certificate ' + \
202                'issuer DN, "%s" ' % attCert.issuer + \
203                'must match this data provider\'s Attribute Authority ' + \
204                'DN: "%s"' % g.securityCfg.acIssuer)
205            return False, self.__class__.InvalidAttributeCertificate
206       
207        log.info('Gatekeeper - access granted for user "%s" '%attCert.userId+\
208                 'to "%s" secured with role "%s" ' % (self.uri,self.reqRole)+\
209                 'using attribute certificate:\n\n%s' % attCert)
210                     
211        return True, self.__class__.AccessAllowedMsg
212
213    @classmethod
214    def urlCanBeOpened(cls, url, timeout=5, raiseExcep=True):
215       """Check url can be opened - adapted from
216       http://mail.python.org/pipermail/python-list/2004-October/289601.html
217       """
218   
219       found = False
220       defTimeOut = socket.getdefaulttimeout()
221       try:
222           socket.setdefaulttimeout(timeout)
223
224           try:
225               urllib2.urlopen(url)
226           except (urllib2.HTTPError, urllib2.URLError,
227                   socket.error, socket.sslerror):
228               if raiseExcep:
229                   raise URLCannotBeOpened
230           
231           found = True
232         
233       finally:
234           socket.setdefaulttimeout(defTimeOut)
235           
236       return found       
Note: See TracBrowser for help on using the repository browser.