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

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@7846
Revision 7846, 20.1 KB checked in by pjkersha, 11 years ago (diff)

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

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