source: MILK/trunk/milk_server/milk_server/models/ndgSecurity.py @ 4469

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/MILK/trunk/milk_server/milk_server/models/ndgSecurity.py
Revision 4469, 9.9 KB checked in by cbyrom, 12 years ago (diff)

Strip out code not relevant to MILK - mainly WCS and WMS stuff - also including the CSML server code + trackback code
Also tidy up structure of 'public' dir - setting up new 'style' dir and
centralising icons in icons dir + remove all unused icons, javascript and stylesheets.
Also strip out testcase code and populate new test directory structure.

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