source: TI05-delivery/ows_framework/trunk/ows_server/ows_server/lib/security_util.py @ 3019

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

Include organisation name in login info on NDG page bottom panel.

ows_server/ows_server/controllers/login.py: add attCert.issuerName into org key of security session info
ows_server/ows_server/lib/security_util.py: add 'org' to security session keys
ows_server/ows_server/templates/ndgPage.kid: change logged in message to include org.

Line 
1# Copyright (C) 2007 STFC & NERC (Science and Technology Facilities Council).
2# This software may be distributed under the terms of the
3# Q Public License, version 1.0 or later.
4# http://ndg.nerc.ac.uk/public_docs/QPublic_license.txt
5"""
6Utilities for transfor of credentials over login service
7
8@author: Philip Kershaw
9"""
10__revision__ = '$Id:$'
11
12import logging
13log = logging.getLogger(__name__)
14
15import urllib
16from pylons import session, request, g
17
18
19class SecuritySession(object):
20    """Utility for Pylons security session keys enables correct
21    keys to be set
22   
23    @type key: string
24    @cvar key: name of security key in session object
25   
26    @type subKeys: tuple
27    @cvar subKeys: list of valid security keys to set h = session manager
28    address, sid = session ID, u = username, org = organisation where user is
29    logged in, roles = the roles the user is entitled to at org"""
30   
31    key = 'ndgSec'
32    subKeys = ('h', 'sid', 'u', 'org', 'roles')
33
34    def __init__(self, **subKeys):
35        """Update the security key of session object with the
36        input sub keys
37       
38        type **subKeys: dict
39        param **subKeys: set any of the security keywords as contained in
40        SecuritySession.subKeys"""
41       
42        # Set the security session keys from request.params if no keywords
43        # were input
44        if subKeys == {}:
45            subKeys = LoginServiceQuery.decodeRequestParams()
46           
47        # Ensure security key is present
48        if SecuritySession.key not in session:
49            session[SecuritySession.key] = {}
50         
51        # Ensure valid keys are being set   
52        for k in subKeys:
53            if k not in SecuritySession.subKeys:
54                raise KeyError, 'Invalid key Security session dict: "%s"' % k
55       
56        # Update security values
57        session[SecuritySession.key].update(subKeys)           
58        session.save()
59        log.debug("Set security session: %s" % session[SecuritySession.key])
60
61    @classmethod
62    def delete(self):
63        """Delete security key from session object"""
64        if SecuritySession.key in session:
65            del session[SecuritySession.key]
66            session.save()
67        log.debug("Deleted security key to session object: %s" % session)
68           
69setSecuritySession = SecuritySession
70           
71 
72class LoginServiceQuery(object):
73    """Create query string containing security credentials.  This is used by
74    the Identity Provider pass the credentials over a HTTP GET back to the
75    Service Provider
76   
77    @cvar keys: query args to be copied into security session dict
78    @type keys: tuple
79    @cvar roleSep: delimit roles names in URL arg with this symbol
80    @type roleSep: string
81    @cvar argSep: standard arg separator for URLs
82    @type argSep: string"""
83   
84    keys = SecuritySession.subKeys
85    rolesSep = ","
86    argSep = "&"
87       
88    def __str__(self):
89        """Provide convenient short-cut for call to make query string
90
91        @rtype: string
92        @return: URL query string with security args"""
93        return self.makeQueryStr()
94   
95    @classmethod
96    def makeQueryStr(cls):
97        """Create the query string containing the required security
98        credentials to return to the service provider
99       
100        @rtype: string
101        @return: URL query string with security args"""
102       
103        # Make a copy of the security session dict reseting the
104        # roles to a single string ready for passing over URL
105        secDict = session[SecuritySession.key].copy()
106        secDict['roles'] = cls.rolesSep.join(secDict['roles'])
107       
108        # Return the full query as a string
109        return cls.argSep.join(["%s=%s" % (k, secDict[k]) for k in cls.keys])
110
111    @classmethod
112    def stripFromURI(cls, *params):
113        """Make a new query string using Pylons request.params but stripping
114        args relating to security
115       
116        @param params: parameters to remove instead of those contained in keys
117        class variable
118        @type additionalParams: tuple
119        @rtype: string
120        @return: URL query string with security args removed"""
121        keys = params or cls.keys
122        return cls.argSep.join(['%s=%s' % (i, request.params[i]) \
123                                for i in request.params if i not in keys])
124
125    @classmethod
126    def decodeRequestParams(cls):
127        """Get security parameters from request.params received from Login
128        Service (IdP).  Decode parameters where necessary: roles are sent as a
129        comma delimited list - convert into a list type
130       
131        @rtype: dict
132        @return: dictionary of security parameters
133        """
134       
135        try:
136            # request.params is actually a MultiDict type but for the purposes
137            # of this code it can be treated as a regular dict type
138            keys = dict([(k, request.params[k]) for k in cls.keys])
139        except KeyError, e:
140            OwsError, \
141                '%s argument is missing from URL returned by Login Service' %\
142                str(e)
143               
144        # Modify roles from a comma delimited string into a list
145        if 'roles' in keys:
146            keys['roles'] = keys['roles'].split(cls.rolesSep)
147
148        return keys
149
150# TODO: this could be used in the future to replace parts of BaseController.
151# __call__ but leave for the moment as there may be a more modular solution
152def constructURL(pathInfo,
153                 scheme=None,
154                 netloc=None,
155                 altPathInfo='/discovery',
156                 query=None):
157    """Utility for BaseController.  Remove getCredentials calls"""
158 
159    if scheme is None and netloc is None:
160        pathPfx = g.server
161    else:
162        pathPfx = urlunsplit((scheme, netloc, '', '', ''))
163       
164    if 'getCredentials' in pathInfo:
165        logger.debug("Reverting request URL from getCredentials to discovery...")
166        requestURL = pathPfx + altPathInfo       
167    else:
168        requestURL = pathPfx + pathInfo
169        if query is None:
170            query='&'.join(["%s=%s"%item for item in request.params.items()])
171
172        if query:
173            requestURL += '?' + query
174           
175    return requestURL
176
177
178import sys
179
180class SecurityConfigError(Exception):
181    """Handle errors from parsing security config items"""
182       
183class SecurityConfig(object):
184    """Get Security related parameters from the Pylons NDG config file"""
185
186    def __init__(self, cfg=None):
187        '''Get PKI settings for Attribute Authority and Session Manager from
188        the configuration file
189       
190        @type param: pylons config file object
191        @param cfg: reference to NDG configuration file.  If omitted defaults
192        to request.environ['ndgConfig']'''
193       
194        if cfg is None:
195            cfg = request.environ['ndgConfig']
196
197        tracefileExpr = cfg.get('NDG_SECURITY', 'tracefile')
198        if tracefileExpr:
199            self.tracefile = eval(tracefileExpr)
200
201        self.smURI = cfg.get('NDG_SECURITY', 'sessionMgrURI')       
202        self.aaURI = cfg.get('NDG_SECURITY', 'attAuthorityURI')
203       
204        # ... for SSL connections to security web services
205        try:
206            self.sslCACertFilePathList = \
207            cfg.get('NDG_SECURITY', 'sslCACertFilePathList').split()
208               
209        except AttributeError:
210            raise SecurityConfigError, \
211                        'No "sslCACertFilePathList" security setting'
212
213        self.sslPeerCertCN = cfg.get('NDG_SECURITY', 'sslPeerCertCN')
214
215        # ...and for WS-Security digital signature
216        self.wssCertFilePath = cfg.get('NDG_SECURITY', 'wssCertFilePath')
217        self.wssPriKeyFilePath = cfg.get('NDG_SECURITY', 'wssKeyFilePath')
218        self.wssPriKeyPwd = cfg.get('NDG_SECURITY', 'wssKeyPwd')
219
220        try:
221            self.wssCACertFilePathList = \
222                cfg.get('NDG_SECURITY', 'wssCACertFilePathList').split()
223               
224        except AttributeError:
225            raise SecurityConfigError, \
226                                'No "wssCACertFilePathList" security setting'
227
228        # Gatekeeper params
229       
230        # Attribute Certificate Issuer
231        self.acIssuer = cfg.get('NDG_SECURITY', 'acIssuer')
232       
233        # verification of X.509 cert back to CA
234        self.acCACertFilePathList = cfg.get('NDG_SECURITY', 
235                                            'acCACertFilePathList')
236
237             
238    def __repr__(self):
239        return '\n'.join(["%s=%s" % (k,v) for k,v in self.__dict__.items() \
240                if k[:2] != "__"])
Note: See TracBrowser for help on using the repository browser.