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

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@7842
Revision 7842, 17.6 KB checked in by pjkersha, 9 years ago (diff)

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

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