source: TI12-security/trunk/WSSecurity/ndg/wssecurity/common/__init__.py @ 6386

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/WSSecurity/ndg/wssecurity/common/__init__.py@6386
Revision 6386, 10.0 KB checked in by pjkersha, 10 years ago (diff)
Line 
1"""NDG Security WS-Security package - contains signature handler and config
2
3NERC DataGrid Project
4"""
5__author__ = "P J Kershaw"
6__date__ = "01/04/08"
7__copyright__ = "(C) 2009 Science and Technology Facilities Council"
8__contact__ = "Philip.Kershaw@stfc.ac.uk"
9__license__ = "BSD - see LICENSE file in top-level directory"
10__contact__ = "Philip.Kershaw@stfc.ac.uk"
11__revision__ = "$Id: $"
12import logging
13log = logging.getLogger(__name__)
14
15import os
16
17from ConfigParser import SafeConfigParser
18from os.path import expandvars as exVar
19import copy
20from ZSI.wstools.Namespaces import OASIS
21
22from ndg.security.common.utils.configfileparsers import \
23    CaseSensitiveConfigParser
24
25class WSSecurityError(Exception):
26    """For WS-Security generic exceptions not covered by other exception
27    classes in this module"""
28    def __init__(self, errorMessage):
29        log.error(errorMessage)
30        super(WSSecurityError, self).__init__(errorMessage)
31       
32class WSSecurityConfigError(WSSecurityError):
33    """Configuration error with WS-Security setting or settings"""
34   
35class WSSecurityConfigOpNotPermitted(WSSecurityConfigError):
36    "Raise for dict methods not allowed in WSSecurityConfig"
37   
38class WSSecurityConfig(dict):
39    """Parser for WS-Security configuration.  Extends dict to enable
40    convenient interface for access to params.
41    """
42    propertyDefaults = dict(
43        reqBinSecTokValType=OASIS.X509TOKEN.X509,
44        verifyingCert=None,
45        verifyingCertFilePath=None,
46        signingCert=None,
47        signingCertFilePath=None, 
48        signingCertChain=[],
49        signingPriKey=None,
50        signingPriKeyFilePath=None, 
51        signingPriKeyPwd=None,
52        caCertDirPath=None,
53        caCertFilePathList=[],
54        addTimestamp=True,
55        timestampClockSkew=0.,
56        timestampMustBeSet=False,
57        createdElemMustBeSet=True,
58        expiresElemMustBeSet=True,
59        applySignatureConfirmation=False,
60        refC14nInclNS=[],
61        signedInfoC14nInclNS=[])
62   
63    def __init__(self, cfg=SafeConfigParser()):
64        '''Initialise settings from an existing config file object or the
65        given path to config file
66       
67        @type cfg: SafeConfigParser or string
68        @param cfg: config object instance or file path to config file to be
69        parsed'''
70       
71        dict.__init__(self)
72       
73        # Initialise parameters from ref in class var
74        self._param = WSSecurityConfig.propertyDefaults.copy()
75       
76        if isinstance(cfg, basestring):
77            # Assume file path to be read
78            self.read(cfg)
79        else:
80            # Assume existing config type object
81            self._cfg = cfg
82       
83
84    def read(self, filePath):
85        '''Read ConfigParser object
86       
87        @type filePath: basestring
88        @param filePath: file to read config from'''
89       
90        # Expand environment variables in file path
91        expandedFilePath = exVar(filePath)
92       
93        # Add 'here' item to enable convenient path substitutions in the config
94        # file
95        defaultItems = dict(here=os.path.dirname(expandedFilePath))
96        self._cfg = CaseSensitiveConfigParser(defaults=defaultItems)
97       
98        readFilePaths = self._cfg.read(expandedFilePath)
99       
100        # Check file was read in OK
101        if len(readFilePaths) == 0:
102            raise IOError('Missing config file: "%s"' % expandedFilePath)
103
104    def parse(self, **kw):
105        '''Extract items from config file and place in dict
106        @type **kw: dict
107        @param **kw: this enables WS-Security params to be set in a config file
108        with other sections e.g. params could be under the section 'wssecurity'
109        '''
110        section = kw.pop('section', 'DEFAULT')
111       
112        # Prefix for option names - optNames = name as they appear in the
113        # config file, self._param are the names used in the code.
114        prefix = kw.pop('prefix', None)
115
116        if prefix:
117            optNames = ["%s.%s" % (prefix, optName) for optName in self._param] 
118        else:
119            optNames = self._param
120           
121        for optName, paramName in zip(optNames, self._param):
122           
123            # Parameters may be omitted and set later
124            if self._cfg.has_option(section, optName):
125                if isinstance(WSSecurityConfig.propertyDefaults[paramName], 
126                              list):
127                    try:
128                        self._param[paramName] = \
129                            exVar(self._cfg.get(section, optName)).split()
130                    except AttributeError:
131                        raise WSSecurityConfigError('Setting "%s"' % paramName)
132                   
133                elif isinstance(WSSecurityConfig.propertyDefaults[paramName], 
134                                bool):           
135                    self._param[paramName] = self._cfg.getboolean(section, 
136                                                                  optName)
137                else:
138                    # Default to None if setting is an empty string.  Settings
139                    # of '' causes problems for M2Crypto parsing
140                    self._param[paramName] = \
141                        exVar(self._cfg.get(section, optName)) or None
142
143    def __len__(self):
144        return len(self._param)
145   
146    def __iter__(self):
147        return self._param.__iter__()
148   
149    def __repr__(self):
150        """Return file properties dictionary as representation"""
151        return repr(self._param)
152
153    def __delitem__(self, key):
154        "Session Manager keys cannot be removed"       
155        raise KeyError('Keys cannot be deleted from ' + \
156                        WSSecurityConfig.__name__)
157
158    def __getitem__(self, key):
159        WSSecurityConfig.__name__ + \
160        """ behaves as data dictionary of WS-Security properties
161        """
162        if key not in WSSecurityConfig.propertyDefaults:
163            raise KeyError("Invalid key '%s'" % key)
164       
165        return self._param[key] 
166   
167    def __setitem__(self, key, item):
168        WSSecurityConfig.__name__ + \
169        """ behaves as data dictionary of WS-Security properties"""
170        if key not in WSSecurityConfig.propertyDefaults:
171            raise KeyError("Parameter key '%s' is not recognised" % key)
172       
173        self._param[key] = item
174
175    def copy(self):
176        wsSecurityConfig = WSSecurityConfig()
177        wsSecurityConfig._param = self._param.copy()
178        return wsSecurityConfig
179   
180    def get(self, key, *arg):
181        return self._param.get(key, *arg)
182
183    def clear(self):
184        raise WSSecurityConfigOpNotPermitted("Data cannot be cleared from "+\
185                                             WSSecurityConfig.__name__)
186   
187    def keys(self):
188        return self._param.keys()
189
190    def items(self):
191        return self._param.items()
192
193    def values(self):
194        return self._param.values()
195
196    def has_key(self, key):
197        return self._param.has_key(key)
198
199    # 'in' operator
200    def __contains__(self, key):
201        return key in self._param
202   
203    def update(self, seq, *arg, **kw):
204
205        # Prefix for option names - optNames = name as they appear in the
206        # config file, self._param are the names used in the code.
207        prefix = kw.pop('prefix', None)
208        if prefix:
209            pfxWithDot = prefix+'.'
210            seqFilt = dict([(k.replace(pfxWithDot, ''), v) 
211                            for k, v in seq.items() 
212                            if k.startswith(pfxWithDot)])
213        else:
214            seqFilt = seq
215       
216        badKeys = []
217        for optName, optVal in seqFilt.items():
218            if optName not in WSSecurityConfig.propertyDefaults:
219                badKeys += [optName]
220               
221            elif isinstance(WSSecurityConfig.propertyDefaults[optName], list):
222                if isinstance(optVal, basestring):
223                    # Parse into a list
224                    seqFilt[optName] = exVar(optVal).split()
225                elif isinstance(optVal, list):
226                    seqFilt[optName] = exVar(optVal)
227                else:
228                    raise WSSecurityConfigError("Expecting list type for "
229                                                'option "%s"' % optName)
230            elif isinstance(WSSecurityConfig.propertyDefaults[optName], bool):
231                if isinstance(optVal, basestring):
232                    # Parse into a boolean
233                    seqFilt[optName] = bool(optVal)
234                   
235                elif isinstance(optVal, bool):
236                    seqFilt[optName] = optVal
237                else:
238                    raise WSSecurityConfigError("Expecting bool type for "
239                                                'option "%s"' % optName)
240            else:
241                # Default to None if setting is an empty string.  Settings
242                # of '' causes problems for M2Crypto parsing
243                if optVal is None:
244                    seqFilt[optName] = optVal
245                else:
246                    seqFilt[optName] = exVar(optVal) or None
247               
248        if len(badKeys) > 0:
249            log.warning("Ignoring unrecognised parameter key(s) for update: "
250                        "%s" % ', '.join(badKeys))
251
252        return self._param.update(seqFilt, *arg)
253   
254    def fromkeys(self, seq):
255        badKeys=[i for i in seq if i not in WSSecurityConfig.propertyDefaults]
256        if badKeys:
257            raise KeyError("Parameter key(s) %s not recognised" % 
258                           ','.join(badKeys))
259        return self._param.fromkeys(seq)
260   
261    def setdefault(self, key, *arg):
262        badKeys=[i for i in arg if i not in WSSecurityConfig.propertyDefaults]
263        if badKeys:
264            raise KeyError("Parameter keys '%s' not recognised" % badKeys)
265        return self._param.setdefault(key, *arg)
266
267    def pop(self, key, *arg):
268        raise WSSecurityConfigOpNotPermitted("Params should not be deleted")
269   
270    def popitem(self):
271        raise WSSecurityConfigOpNotPermitted("Params should not be deleted")
272   
273    def iteritems(self):
274        return self._param.iteritems()
275   
276    def iterkeys(self):
277        return self._param.iterkeys()
278   
279    def itervalues(self):
280        return self._param.itervalues()
281   
Note: See TracBrowser for help on using the repository browser.