source: TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/paster_templates/template.py @ 7847

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/paster_templates/template.py@7847
Revision 7847, 21.0 KB checked in by pjkersha, 11 years ago (diff)

Incomplete - task 16: NDG Security 2.x.x - incl. updated Paster templates

  • fixes to the ndgsecurity_services template
  • Property svn:executable set to *
  • Property svn:keywords set to Id
Line 
1"""NDG Security Paster template classes
2
3NERC DataGrid Project
4"""
5__author__ = "P J Kershaw"
6__date__ = "20/10/2010"
7__copyright__ = "(C) 2010 Science and Technology Facilities Council"
8__license__ = "BSD - see top-level directory for LICENSE file"
9__contact__ = "Philip.Kershaw@stfc.ac.uk"
10__revision__ = "$Id$"
11
12import os
13import socket
14import base64
15import string
16from urlparse import urlunsplit, urlparse
17from paste.script.templates import Template, var
18
19_hostTuple = socket.gethostbyaddr(socket.gethostname())
20try:
21    # Get first alias from list if present
22    _hostname = _hostTuple[1][0]
23except IndexError:
24    # ... or default to hostname
25    _hostname = _hostTuple[0]
26   
27from ndg.saml.saml2.core import Issuer   
28
29
30class DoublePercentTemplate(string.Template):
31    """Alternative template uses '%%' instead of '$' to denote template
32    variables.  This is used because some NDG Security templates contain
33    '$' variables used for other purposes."""
34    delimiter = "%%"
35   
36   
37class TemplateBase(Template):
38    """Base Paste Template class sets a custom renderer"""
39   
40    def template_renderer(self, content, vars, filename=None):
41        """Alternative renderer defined to enable use of '%%' prefix for template
42        variables.  NDG Security ini files already use '$' for other variables
43       
44        @param content: template content
45        @type content: string
46        @param vars: variables to substituted into the template
47        @type vars: dict
48        @return: content with all variables substituted for
49        @rtype: string
50        """
51        tmpl = DoublePercentTemplate(content)
52        return tmpl.substitute(**vars)
53
54    def pre(self, command, output_dir, vars):
55        '''Extend to fix log file path setting in ini file
56       
57        @param command: command to create template
58        @type command:
59        @param output_dir: output directory for template file(s)
60        @type output_dir: string
61        @param vars: variables to be substituted into template
62        @type vars: dict
63        ''' 
64        vars['outputDir'] = os.path.abspath(output_dir)
65       
66       
67"""@var _MYPROXY_SERVER_LOCALID_XRD_ENTRY_TMPL: Yadis XRDS entry for a MyProxy
68server endpoint.  This entry also include a localID $user_url which the OpenID
69Provider application code will fill out at runtime.
70@type _MYPROXY_SERVER_LOCALID_XRD_ENTRY_TMPL: ndg.security.server.paster_templates.template.DoublePercentTemplate
71"""
72_MYPROXY_SERVER_LOCALID_XRD_ENTRY_TMPL = DoublePercentTemplate(
73"""        <Service priority="10">
74            <Type>urn:esg:security:myproxy-service</Type>
75            <URI>%%{myproxyServerURI}</URI>
76            <LocalID>$user_url</LocalID>
77        </Service>
78""")
79
80"""@var _ATTRIBUTE_SERVICE_LOCALID_XRD_ENTRY_TMPL: Yadis XRDS entry for an
81Attribute Service endpoint.  This entry also include a localID $user_url which
82the OpenID Provider application code will fill out at runtime.
83@type _ATTRIBUTE_SERVICE_LOCALID_XRD_ENTRY_TMPL: ndg.security.server.paster_templates.template.DoublePercentTemplate
84"""
85_ATTRIBUTE_SERVICE_LOCALID_XRD_ENTRY_TMPL = DoublePercentTemplate(
86"""<Service priority="20">
87            <Type>urn:esg:security:attribute-service</Type>
88            <Type>urn:esg:security:attribute-service</Type>
89            <URI>%%{attributeServiceURI}</URI>
90            <LocalID>$user_url</LocalID>
91        </Service>
92""")
93
94"""@var _MYPROXY_SERVER_NONLOCALID_XRD_ENTRY_TMPL: Yadis XRDS entry for a
95MyProxy server endpoint.  No localID entry is included as this template is for
96use with the serveryadis.xml_tmpl which applies to requests where the specific
97identity is not provided.
98@type _MYPROXY_SERVER_NONLOCALID_XRD_ENTRY_TMPL: ndg.security.server.paster_templates.template.DoublePercentTemplate
99"""
100_MYPROXY_SERVER_NONLOCALID_XRD_ENTRY_TMPL = DoublePercentTemplate(
101"""        <Service priority="10">
102            <Type>urn:esg:security:myproxy-service</Type>
103            <URI>%%{myproxyServerURI}</URI>
104        </Service>
105""")
106
107"""@var _ATTRIBUTE_SERVICE_NONLOCALID_XRD_ENTRY_TMPL: Yadis XRDS entry for an
108Attribute Service endpoint.  No localID entry is included as this template is
109for use with the serveryadis.xml_tmpl which applies to requests where the
110specific identity is not provided.
111@type _ATTRIBUTE_SERVICE_NONLOCALID_XRD_ENTRY_TMPL: ndg.security.server.paster_templates.template.DoublePercentTemplate
112"""
113_ATTRIBUTE_SERVICE_NONLOCALID_XRD_ENTRY_TMPL = DoublePercentTemplate(
114"""<Service priority="20">
115            <Type>urn:esg:security:attribute-service</Type>
116            <URI>%%{attributeServiceURI}</URI>
117        </Service>
118""")
119
120
121class ServicesTemplate(TemplateBase):
122    """Make a template containing all the Security Services available with
123    NDG Security.  These are provided together in one template but deployers
124    should consider adapting this and dividing up into separate WSGI apps
125    to suit
126    """
127    DEFAULT_URI = urlunsplit(('https', _hostname, '', None, None))
128   
129    ATTRIBUTE_SERVICE_DEFAULT_MOUNT_PATH = '/AttributeService'
130    ATTRIBUTE_SERVICE_DEFAULT_ISSUER_NAME = '/O=Site A/CN=Attribute Authority'
131    ATTRIBUTE_SERVICE_DEFAULT_ISSUER_FORMAT = Issuer.X509_SUBJECT
132   
133    AUTHORISATION_SERVICE_DEFAULT_ISSUER_NAME = \
134        '/O=Site A/CN=Authorisation Service'
135    AUTHORISATION_SERVICE_DEFAULT_ISSUER_FORMAT = Issuer.X509_SUBJECT
136    AUTHORISATION_SERVICE_DEFAULT_MOUNT_PATH = '/AuthorisationService'   
137   
138    _template_dir = 'services'
139    summary = ('NDG Security services full deployment template '
140               'including the SAML Attribute and Authorisation Services, '
141               'OpenID Provider application, OpenID Relying Party and SSL '
142               'client authentication services')
143    vars = [
144        var('baseURI',
145            'Base URI for the service(s) [with no trailing slash]',
146            default=DEFAULT_URI),
147           
148        var('attributeServiceMountPath',
149            'Mount path for Attribute Service',
150            ATTRIBUTE_SERVICE_DEFAULT_MOUNT_PATH),
151           
152        var('authorisationServiceMountPath',
153            'Mount path for Authorisation Service',
154            AUTHORISATION_SERVICE_DEFAULT_MOUNT_PATH),
155           
156        var('attributeServiceIssuerName',
157            'SAML Issuer Name field for Attribute Service SAML responses',
158            ATTRIBUTE_SERVICE_DEFAULT_ISSUER_NAME),
159           
160        var('attributeServiceIssuerFormat',
161            'SAML Issuer Name field for Attribute Service SAML responses',
162            ATTRIBUTE_SERVICE_DEFAULT_ISSUER_FORMAT),
163           
164        var('authorisationServiceIssuerName',
165            'SAML Issuer Name field for Authorisation Service SAML responses',
166            AUTHORISATION_SERVICE_DEFAULT_ISSUER_NAME),
167           
168        var('authorisationServiceIssuerFormat',
169            'SAML Issuer Name field for Authorisation Service SAML responses',
170            AUTHORISATION_SERVICE_DEFAULT_ISSUER_FORMAT),
171
172        var('authkitCookieSecret', 
173            ('Cookie secret for AuthKit authentication middleware.  This value '
174             '*MUST* agree with the one used for the ini file of the '
175             'application to be secured - see ndgsecurity_securedapp template'),
176            default=base64.b64encode(os.urandom(32))[:32]),
177
178        var('beakerSessionCookieSecret', 
179            'Secret for securing the OpenID Provider and SSL Client '
180            'authentication session cookie',
181            default=base64.b64encode(os.urandom(32))[:32]),
182           
183        var('openidRelyingPartyCookieSecret',
184            'Secret for securing OpenID Relying Party session cookie',
185            default=base64.b64encode(os.urandom(32))[:32]),
186           
187        var('myproxyServerURI',
188            'MyProxy Server address to advertise in OpenID Provider Yadis '
189            'document - defaults to omit this entry',
190            default=''),
191           
192        var('includeAttributeServiceInYadis',
193            'Include Attribute Service address in OpenID Provider Yadis '
194            'document',
195            default=True)
196        ]
197   
198    def pre(self, command, output_dir, vars):
199        '''Extend to enable substitutions for OpenID Provider Yadis templates,
200        port number and fix log file path setting
201       
202        @param command: command to create template
203        @type command:
204        @param output_dir: output directory for template file(s)
205        @type output_dir: string
206        @param vars: variables to be substituted into template
207        @type vars: dict
208        ''' 
209       
210        # Fix for baseURI in case trailing slash was included.  In THIS template
211        # it should not be there
212        if vars['baseURI'].endswith('/'):
213            vars['baseURI'] = vars['baseURI'].rstrip('/')
214
215        # Fix for mount paths in case leading slash was omitted.
216        if not vars['attributeServiceMountPath'].startswith('/'):
217            vars['attributeServiceMountPath'] = '/' + vars[
218                                                'attributeServiceMountPath']
219
220        if not vars['authorisationServiceMountPath'].startswith('/'):
221            vars['authorisationServiceMountPath'] = '/' + vars[
222                                            'authorisationServiceMountPath']
223           
224        # Cut out port number from base URI
225        uriParts = urlparse(vars['baseURI'])
226        netlocLastElem = uriParts.netloc.split(':')[-1]
227        if netlocLastElem.isdigit():
228            vars['portNumber'] = netlocLastElem
229        else:
230            vars['portNumber'] = ''
231           
232        vars['yadisExtraServiceEndpoints'] = ''
233        vars['serveryadisExtraServiceEndpoints'] = ''
234       
235        attributeServiceURI = vars['baseURI'] + vars[
236                                'attributeServiceMountPath']
237       
238        # Attribute Service entry added if flag was set
239        if vars['includeAttributeServiceInYadis']:
240            # yadis.xml_tmpl entry
241            vars['yadisExtraServiceEndpoints'
242                 ] += _ATTRIBUTE_SERVICE_LOCALID_XRD_ENTRY_TMPL.substitute(
243                        attributeServiceURI=attributeServiceURI)
244
245            # serveryadis.xml_tmpl entry
246            vars['serveryadisExtraServiceEndpoints'
247                 ] += _ATTRIBUTE_SERVICE_NONLOCALID_XRD_ENTRY_TMPL.substitute(
248                        attributeServiceURI=attributeServiceURI)
249
250        del vars['includeAttributeServiceInYadis']
251       
252        # MyProxy Server entry added if an endpoint was specified
253        if vars['myproxyServerURI']:
254            # yadis.xml_tmpl entry
255            vars['yadisExtraServiceEndpoints'
256                 ] += _MYPROXY_SERVER_LOCALID_XRD_ENTRY_TMPL.substitute(
257                            myproxyServerURI=vars['myproxyServerURI'])       
258           
259            vars['serveryadisExtraServiceEndpoints'
260                 ] += _MYPROXY_SERVER_NONLOCALID_XRD_ENTRY_TMPL.substitute(
261                        myproxyServerURI=vars['myproxyServerURI'])
262        del vars['myproxyServerURI']   
263       
264        # This sets the log file path
265        super(ServicesTemplate, self).pre(command, output_dir, vars)
266
267       
268class SecuredAppTemplate(TemplateBase):
269    """Create a template for a secured application with authentication and
270    authorisation filters"""
271    DEFAULT_URI = 'http://localhost:7080/'
272    DEFAULT_AUTHN_REDIRECT_URI = 'https://localhost:7443/verify'
273    DEFAULT_AUTHZ_SERVICE_URI = 'https://localhost:7443/AuthorisationService'
274    DEFAULT_ISSUER_NAME = 'O=NDG, OU=Security, CN=localhost'
275    DEFAULT_ISSUER_FORMAT = Issuer.X509_SUBJECT
276    DEFAULT_ACCESS_DENIED_HEADING = 'Access Denied'
277   
278    _template_dir = 'securedapp'
279    summary = (
280        'NDG Security template for securing an application with '
281        'authentication and authorisation filters.  Use in conjunction with '
282        'the ndgsecurity_services template')
283   
284    vars = [
285        var('baseURI',
286            'Base URI for the service [sets default return to address '
287            'following logout]',
288            default=DEFAULT_URI),
289
290        var('authkitCookieSecret', 
291            ('Cookie secret for AuthKit authentication middleware [this value '
292             '*MUST* agree with the one set in the authentication service\'s '
293             'ini file]'),
294            default=base64.b64encode(os.urandom(32))[:32]),
295
296        var('beakerSessionCookieSecret', 
297            'Cookie secret for keeping security session state',
298            default=base64.b64encode(os.urandom(32))[:32]),
299
300        var('authnRedirectURI', 
301            ('endpoint hosting OpenID Relying Party and/or SSL authentication '
302             'interface'),
303            default=DEFAULT_AUTHN_REDIRECT_URI),
304
305        var('accessDeniedPageHeading',
306            'Heading for access denied HTML page',
307            default=DEFAULT_ACCESS_DENIED_HEADING),
308           
309        var('authzServiceURI', 
310            ('endpoint authorisation service which this app is secured with'),
311            default=DEFAULT_AUTHZ_SERVICE_URI),
312           
313        var('authzDecisionQueryIssuerName', 
314            ('ID of this service used in SAML authorisation queries'),
315            default=DEFAULT_ISSUER_NAME),
316
317        var('authzDecisionQueryIssuerFormat', 
318            ('Format of authzDecisionQueryIssuerName string; if using the '
319             'default, ensure that the issuerName value is a correctly '
320             'formatted X.509 Subject Name'),
321            default=DEFAULT_ISSUER_FORMAT)
322    ]
323
324    def pre(self, command, output_dir, vars):
325        '''Extend to enable substitutions for port number and fix log file path
326        setting
327       
328        @param command: command to create template
329        @type command:
330        @param output_dir: output directory for template file(s)
331        @type output_dir: string
332        @param vars: variables to be substituted into template
333        @type vars: dict
334        ''' 
335        # Cut out port number from base URI
336        uriParts = urlparse(vars['baseURI'])
337        netlocLastElem = uriParts.netloc.split(':')[-1]
338        if netlocLastElem.isdigit():
339            vars['portNumber'] = netlocLastElem
340        else:
341            vars['portNumber'] = ''
342       
343        # Fix for baseURI in case trailing slash was omitted.
344        if not vars['baseURI'].endswith('/'):
345            vars['baseURI'] += '/'
346                       
347        # This sets the log file path
348        super(SecuredAppTemplate, self).pre(command, output_dir, vars)
349           
350
351class AttributeServiceTemplate(TemplateBase):
352    """Paster template for the SAML attribute service"""
353   
354    DEFAULT_PORT = 5000
355    DEFAULT_MOUNT_PATH = '/AttributeService'
356    DEFAULT_ISSUER_NAME = 'O=NDG, OU=Security, CN=localhost'
357    DEFAULT_ISSUER_FORMAT = Issuer.X509_SUBJECT
358   
359    _template_dir = 'attributeservice'
360    summary = 'NDG Security SAML Attribute Service template'
361    vars = [
362        var('portNumber',
363            'Port number for service to listen on [applies to running with '
364            'paster ONLY]',
365            default=DEFAULT_PORT),
366           
367        var('mountPath', 
368            ('URI path to mount service i.e. "https://myhost/<mountPath>" ['
369             'Nb. for mod_wsgi path may be e.g. "https://myhost/<script alias '
370             'path><mountPath>" !]'),
371            default=DEFAULT_MOUNT_PATH),
372
373        var('issuerName', 
374            ('ID of this service used in SAML queries and responses'),
375            default=DEFAULT_ISSUER_NAME),
376
377        var('issuerFormat', 
378            ('Format of issuerName string; if using the default, ensure that '
379             'the issuerName value is a correctly formatted X.509 Subject '
380             'Name'),
381            default=DEFAULT_ISSUER_FORMAT)
382    ]
383
384    def pre(self, command, output_dir, vars):
385        '''Extend to fix log file path setting and check mount point setting
386       
387        @param command: command to create template
388        @type command:
389        @param output_dir: output directory for template file(s)
390        @type output_dir: string
391        @param vars: variables to be substituted into template
392        @type vars: dict
393        ''' 
394        # Fix for mount point in case leading slash was omitted.
395        if not vars['mountPath'].startswith('/'):
396            vars['mountPath'] = '/' + vars['mountPath']
397           
398        super(AttributeServiceTemplate, self).pre(command, output_dir, vars)
399           
400
401class AuthorisationServiceTemplate(TemplateBase):
402    """Paster template for the SAML authorisation service"""
403   
404    DEFAULT_PORT = 5100
405    DEFAULT_MOUNT_PATH = '/AuthorisationService'
406    DEFAULT_ISSUER_NAME = 'O=NDG, OU=Security, CN=localhost'
407    DEFAULT_ISSUER_FORMAT = Issuer.X509_SUBJECT
408   
409    _template_dir = 'authorisationservice'
410    summary = 'NDG Security Authorisation Service template'
411   
412    vars = [
413        var('portNumber',
414            'Port number for service to listen on [applies to running with '
415            'paster ONLY]',
416            default=DEFAULT_PORT),
417
418        var('mountPath', 
419            ('URI path to mount service i.e. "https://myhost/<mountPath>" ['
420             'Nb. for mod_wsgi path may be e.g. "https://myhost/<script alias '
421             'path><mountPath>" !]'),
422            default=DEFAULT_MOUNT_PATH),
423
424        var('issuerName', 
425            ('ID of this service used in SAML queries and responses'),
426            default=DEFAULT_ISSUER_NAME),
427
428        var('issuerFormat', 
429            ('Format of issuerName string; if using the default, ensure that '
430             'the issuerName value is a correctly formatted X.509 Subject '
431             'Name'),
432            default=DEFAULT_ISSUER_FORMAT)
433    ]
434
435    def pre(self, command, output_dir, vars):
436        '''Extend to fix log file path setting and check mount point setting
437       
438        @param command: command to create template
439        @type command:
440        @param output_dir: output directory for template file(s)
441        @type output_dir: string
442        @param vars: variables to be substituted into template
443        @type vars: dict
444        ''' 
445        vars['outputDir'] = os.path.abspath(output_dir)
446       
447        # Fix for mount point in case leading slash was omitted.
448        if not vars['mountPath'].startswith('/'):
449            vars['mountPath'] = '/' + vars['mountPath']
450               
451
452class OpenIDProviderTemplate(TemplateBase):
453    """Paster template for OpenID Provider service"""
454    _template_dir = 'openidprovider'
455    summary = 'NDG Security OpenID Provider template'
456   
457    DEFAULT_URI = urlunsplit(('https', _hostname, '', None, None))
458   
459    vars = [
460        var('baseURI',
461            'Base URI for the service [with no trailing slash]',
462            default=DEFAULT_URI),
463
464        var('beakerSessionCookieSecret', 
465            'Secret for securing the OpenID Provider and SSL Client '
466            'authentication session cookie',
467            default=base64.b64encode(os.urandom(32))[:32]),
468           
469        var('myproxyServerURI',
470            'MyProxy Server address to advertise in OpenID Provider Yadis '
471            'document - defaults to omit this entry',
472            default=''),
473           
474        var('attributeServiceURI',
475            'Attribute Service address to advertise in OpenID Provider Yadis '
476            'document - defaults to omit this entry',
477            default='')
478        ]
479
480    def pre(self, command, output_dir, vars):
481        '''Extend to enable substitutions for OpenID Provider Yadis templates,
482        port number and fix log file path setting
483       
484        @param command: command to create template
485        @type command:
486        @param output_dir: output directory for template file(s)
487        @type output_dir: string
488        @param vars: variables to be substituted into template
489        @type vars: dict
490        ''' 
491        # Cut out port number from base URI
492        uriParts = urlparse(vars['baseURI'])
493        netlocLastElem = uriParts.netloc.split(':')[-1]
494        if netlocLastElem.isdigit():
495            vars['portNumber'] = netlocLastElem
496        else:
497            vars['portNumber'] = ''
498
499        # Set Yadis XRDS entries
500        vars['yadisExtraServiceEndpoints'] = ''
501        vars['serveryadisExtraServiceEndpoints'] = ''
502       
503        # Attribute Service entry added if an endpoint was specified
504        if vars['attributeServiceURI']:
505            # yadis.xml_tmpl entry
506            vars['yadisExtraServiceEndpoints'
507                 ] += _ATTRIBUTE_SERVICE_LOCALID_XRD_ENTRY_TMPL.substitute(
508                        attributeServiceURI=vars['attributeServiceURI'])
509
510            # serveryadis.xml_tmpl entry
511            vars['serveryadisExtraServiceEndpoints'
512                 ] += _ATTRIBUTE_SERVICE_NONLOCALID_XRD_ENTRY_TMPL.substitute(
513                        attributeServiceURI=vars['attributeServiceURI'])
514
515        del vars['attributeServiceURI']
516       
517        if vars['myproxyServerURI']:
518            # yadis.xml_tmpl entry
519            vars['yadisExtraServiceEndpoints'
520                 ] += _MYPROXY_SERVER_LOCALID_XRD_ENTRY_TMPL.substitute(
521                            myproxyServerURI=vars['myproxyServerURI'])       
522           
523            vars['serveryadisExtraServiceEndpoints'
524                 ] += _MYPROXY_SERVER_NONLOCALID_XRD_ENTRY_TMPL.substitute(
525                        myproxyServerURI=vars['myproxyServerURI'])
526                         
527        del vars['myproxyServerURI'] 
528         
529        super(OpenIDProviderTemplate, self).pre(command, output_dir, vars)
530
531
Note: See TracBrowser for help on using the repository browser.