source: TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test/integration/authz_lite/securityservices.ini @ 6276

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg-security/TI12-security/trunk/NDGSecurity/python/ndg_security_test/ndg/security/test/integration/authz_lite/securityservices.ini@6276
Revision 6276, 20.6 KB checked in by pjkersha, 11 years ago (diff)
  • ndg.security.server.wsgi.NDGSecurityMiddlewareBase: change code to leave trailing space in path info attribute
  • ndg.security.server.wsgi.openid.provider.OpenIDProviderMiddleware:
    • added properties with type checking
    • added trustedRelyingParties attribute - a list of sites trusted by the Provider for which no decide page interface is invoked.
  • ndg.security.server.wsgi.openid.provider.axinterface.csv: fixed adding of AX attributes for CSV class. Required attributes were being set twice.
  • ndg.security.server.wsgi.openid.relyingparty.OpenIDRelyingParty:
    • changed whitelisting to use ndg.security.server.wsgi.openid.relyingparty.validation.SSLIdPValidationDriver class. FIXME: Fix required in SSL verify callback needed to check only the server certificate in the verification chain and ignore CA certificates.
  • ndg.security.server.wsgi.openid.relyingparty.validation.SSLIdPValidationDriver: changed to init to include keyword option for installing default urllib2 opener and defaulting to OMIT it.
Line 
1#
2# NERC DataGrid Security
3#
4# Paste configuration for combined Attribute Authority, OpenID Relying Party
5# and Provider services
6#
7# The %(here)s variable will be replaced with the parent directory of this file
8#
9# Author: P J Kershaw
10# date: 01/07/09
11# Copyright: (C) 2009 Science and Technology Facilities Council
12# license: BSD - see LICENSE file in top-level directory
13# Contact: Philip.Kershaw@stfc.ac.uk
14# Revision: $Id:$
15
16[DEFAULT]
17portNum = 7443
18hostname = localhost
19scheme = https
20baseURI = %(scheme)s://%(hostname)s:%(portNum)s
21openIDProviderIDBase = /openid
22openIDProviderIDSelectURI = %(baseURI)s%(openIDProviderIDBase)s
23testConfigDir = %(here)s/../../config
24beakerSessionKeyName = beaker.session.ndg.security.services
25
26# Global Attribute Authority Settings
27attributeAuthorityEnvironKeyName = ndg.security.server.attributeauthority.AttributeAuthority
28attributeQueryInterfaceEnvironKeyName = ndg.security.server.attributeauthority.attributeQueryInterface
29
30dbConnectionString = sqlite:///%(testConfigDir)s/user.db
31
32[server:main]
33use = egg:Paste#http
34host = 0.0.0.0
35port = %(portNum)s
36
37# Provider borrows content from RP static content dir so the cascade is not
38# needed(!)
39#[filter-app:OpenIDProviderFilterApp]
40#use = egg:Paste#httpexceptions
41#next = cascade
42#
43## Composite for OpenID Provider to enable settings for picking up static
44## content
45#[composit:cascade]
46#use = egg:Paste#cascade
47#app1 = OpenIDProviderStaticContent
48#app2 = OpenIDProviderApp
49#catch = 404
50#
51#[app:OpenIDProviderStaticContent]
52#use = egg:Paste#static
53#document_root = %(here)s/openidprovider
54
55# Ordering of filters and app is critical
56[pipeline:main]
57pipeline = wsseSignatureVerificationFilter
58                   AttributeAuthorityFilter
59                   AttributeAuthorityWsdlSoapBindingFilter
60           wsseSignatureFilter
61           AttributeAuthoritySamlSoapBindingFilter
62                   SessionMiddlewareFilter
63                   SSLCientAuthKitFilter
64                   SSLClientAuthenticationFilter
65                   SSLCientAuthnRedirectResponseFilter
66                   OpenIDRelyingPartyFilter
67                   OpenIDProviderApp
68
69#______________________________________________________________________________
70# Beaker Session Middleware (used by OpenID Provider Filter)
71[filter:SessionMiddlewareFilter]
72paste.filter_app_factory=beaker.middleware:SessionMiddleware
73beaker.session.key = openid
74beaker.session.secret = qKEdQdCr33NE087dRUWX3qUv5r7AsuQU
75
76# If you'd like to fine-tune the individual locations of the cache data dirs
77# for the Cache data, or the Session saves, un-comment the desired settings
78# here:
79beaker.cache.data_dir = %(here)s/openidprovider/beaker/cache
80beaker.session.data_dir = %(here)s/openidprovider/beaker/sessions
81beaker.session.cookie_expires = True
82
83# Key name for keying into environ dictionary
84environ_key = %(beakerSessionKeyName)s
85
86[filter:SSLCientAuthKitFilter]
87paste.filter_app_factory = authkit.authenticate:middleware
88
89# AuthKit Set-up
90setup.method=cookie
91
92# This cookie name and secret MUST agree with the name used by the
93# Authentication Filter used to secure a given app
94cookie.name=ndg.security.auth
95
96cookie.secret=9wvZObs9anUEhSIAnJNoY2iJq59FfYZr
97cookie.signoutpath = /logout
98
99# Disable inclusion of client IP address from cookie signature due to
100# suspected problem with AuthKit setting it when a HTTP Proxy is in place
101cookie.includeip = False
102
103# SSL Client Certificate based authentication is invoked if the client passed
104# a certificate with request.  This bypasses OpenID based authn.
105[filter:SSLClientAuthenticationFilter]
106paste.filter_app_factory = ndg.security.server.wsgi.ssl:AuthKitSSLAuthnMiddleware
107prefix = ssl.
108ssl.caCertFilePathList = %(testConfigDir)s/ca/ndg-test-ca.crt
109#ssl.clientCertDNMatchList = /O=NDG/OU=BADC/CN=mytest /O=gabriel/OU=BADC/CN=test /O=NDG/OU=BADC/CN=test
110
111# 'HTTP_' prefix is set when passed through a proxy
112ssl.sslKeyName = HTTP_HTTPS
113ssl.sslClientCertKeyName = HTTP_SSL_CLIENT_CERT
114
115# Set the URI pattern match here to interrupt a redirect to the OpenID Relying
116# Party from the service running over HTTP and see if a client certificate has
117# been set
118ssl.rePathMatchList = ^/verify.*
119
120[filter:OpenIDRelyingPartyFilter]
121paste.filter_app_factory = 
122        ndg.security.server.wsgi.openid.relyingparty:OpenIDRelyingPartyMiddleware.filter_app_factory
123
124openid.relyingparty.baseURL = %(authkit.openid.baseurl)s
125openid.relyingparty.idpWhitelistConfigFilePath = %(here)s/openidrelyingparty/ssl-idp-validator.xml
126openid.relyingparty.signinInterfaceMiddlewareClass = ndg.security.server.wsgi.openid.relyingparty.signin_interface.genshi.GenshiSigninTemplate
127#openid.relyingparty.signinInterface.staticContentRootDir = %(here)s/openidrelyingparty/public
128openid.relyingparty.signinInterface.baseURL = %(openid.relyingparty.baseURL)s
129openid.relyingparty.signinInterface.initialOpenID = %(openIDProviderIDSelectURI)s
130openid.relyingparty.signinInterface.heading = OpenID Sign-in
131#openid.relyingparty.signinInterface.leftLogo = %(openid.relyingparty.signinInterface.baseURL)s/layout/NERC_Logo.gif
132#openid.relyingparty.signinInterface.leftAlt = Natural Environment Research Council
133#openid.relyingparty.signinInterface.leftLink = http://ndg.nerc.ac.uk/
134#openid.relyingparty.signinInterface.leftImage = %(openid.relyingparty.signinInterface.baseURL)s/layout/ndg_logo_circle.gif
135openid.relyingparty.signinInterface.footerText = This site is for test purposes only.   <a class="FooterLink" href="http://openid.net/what/" target="_blank"><small>What is OpenID?</small></a>
136openid.relyingparty.signinInterface.rightLink = http://ceda.ac.uk/
137openid.relyingparty.signinInterface.rightImage = %(openid.relyingparty.signinInterface.baseURL)s/layout/CEDA_RightButton60.png
138openid.relyingparty.signinInterface.rightAlt = Centre for Environmental Data Archival
139openid.relyingparty.signinInterface.helpIcon = %(openid.relyingparty.signinInterface.baseURL)s/layout/icons/help.png
140
141cache_dir = %(here)s/data
142
143# AuthKit Set-up
144authkit.setup.method=openid, cookie
145
146# This cookie name and secret MUST agree with the name used by the
147# Authentication Filter used to secure a given app
148authkit.cookie.name=ndg.security.auth
149
150authkit.cookie.secret=9wvZObs9anUEhSIAnJNoY2iJq59FfYZr
151authkit.cookie.signoutpath = /logout
152
153# Disable inclusion of client IP address from cookie signature due to
154# suspected problem with AuthKit setting it when a HTTP Proxy is in place
155authkit.cookie.includeip = False
156
157authkit.openid.path.signedin=/
158authkit.openid.store.type=file
159authkit.openid.store.config=%(here)s/openidrelyingparty/store
160authkit.openid.session.key = authkit_openid
161authkit.openid.session.secret = random string
162
163# Key name for dereferencing beaker.session object held in environ
164authkit.openid.session.middleware = %(beakerSessionKeyName)s
165
166authkit.openid.baseurl = %(baseURI)s
167
168# Template for signin
169#authkit.openid.template.obj =
170
171# Handler for parsing OpenID and creating a session from it
172#authkit.openid.urltouser =
173
174# Attribute Exchange - all are optional unless the relevant ax.required.<name>
175# is set to True.  The alias defers to the parameter name given unless explicity
176# specified - see commented out entry for firstName below.  The number of
177# attributes for each attribute name defaults to 1 unless otherwise set
178authkit.openid.ax.typeuri.firstName=http://openid.net/schema/namePerson/first
179authkit.openid.ax.alias.firstName=firstName
180#authkit.openid.ax.count.firstName=1
181#authkit.openid.ax.required.firstName=True
182authkit.openid.ax.typeuri.lastName=http://openid.net/schema/namePerson/last
183authkit.openid.ax.alias.lastName=lastName
184authkit.openid.ax.required.lastName=True
185authkit.openid.ax.typeuri.emailAddress=http://openid.net/schema/contact/internet/email
186authkit.openid.ax.alias.emailAddress=emailAddress
187authkit.openid.ax.required.emailAddress=True
188
189
190[filter:SSLCientAuthnRedirectResponseFilter]
191# Redirect to original requested URI following SSL Client Authentication.  This
192# filter must be placed AFTER the AuthKit cookie setting middleware.  In this
193# case its configured in the OpenIDRelyingPartyMiddleware filter.  If the
194# OpenID Relying Party filter is removed, a separate AuthKit middleware entry
195# would need to be made so that this redirect filter can still function
196paste.filter_app_factory = ndg.security.server.wsgi.authn:AuthKitRedirectResponseMiddleware
197prefix = ssl.
198ssl.sessionKey = %(beakerSessionKeyName)s
199
200#______________________________________________________________________________
201# OpenID Provider WSGI Settings
202[app:OpenIDProviderApp]
203paste.app_factory=ndg.security.server.wsgi.openid.provider:OpenIDProviderMiddleware.app_factory
204
205openid.provider.path.openidserver=/OpenID/Provider/server
206openid.provider.path.login=/OpenID/Provider/login
207openid.provider.path.loginsubmit=/OpenID/Provider/loginsubmit
208
209# Yadis based discovery only - the 'id' path is configured may be set to page
210# with <link rel="openid.server" href="..."> and Yadis
211# <meta http-equiv="x-xrds-location" content="..."> links if required but in
212# this implementation it set to return 404 not found - see
213# ndg.security.server.wsgi.openid.provider.renderinginterface.genshi.GenshiRendering
214# class
215openid.provider.path.id=/OpenID/Provider/id/${userIdentifier}
216openid.provider.path.yadis=%(openIDProviderIDBase)s/${userIdentifier}
217
218# Yadis based discovery for idselect mode - this is where the user has entered
219# a URI at the Relying Party which identifies their Provider only and not their
220# full ID URI.  e.g. https://badc.nerc.ac.uk instead of
221# https://badc.nerc.ac.uk/John
222openid.provider.path.serveryadis=%(openIDProviderIDBase)s
223openid.provider.path.allow=/OpenID/Provider/allow
224openid.provider.path.decide=/OpenID/Provider/decide
225openid.provider.path.mainpage=/OpenID/Provider/home
226
227openid.provider.session_middleware=%(beakerSessionKeyName)s
228openid.provider.base_url=%(baseURI)s
229
230# Enable login to construct an identity URI if IDSelect mode was chosen and
231# no identity URI was passed from the Relying Party.  This value should
232# match openid.provider.path.id and/or openid.provider.path.yadis - see above
233identityUriTemplate=%(baseURI)s%(openIDProviderIDBase)s/${userIdentifier}
234
235openid.provider.trace=False
236openid.provider.consumer_store_dirpath=%(here)s/openidprovider
237openid.provider.renderingClass=ndg.security.server.wsgi.openid.provider.renderinginterface.genshi.GenshiRendering
238#openid.provider.renderingClass=ndg.security.server.wsgi.openid.provider.DemoRenderingInterface
239
240# Layout
241openid.provider.rendering.baseURL = %(openid.provider.base_url)s
242#openid.provider.rendering.leftLogo = %(openid.provider.rendering.baseURL)s/layout/NERC_Logo.gif
243#openid.provider.rendering.leftAlt = Natural Environment Research Council
244#openid.provider.rendering.leftLink = http://ndg.nerc.ac.uk/
245#openid.provider.rendering.leftImage = %(openid.provider.rendering.baseURL)s/layout/ndg_logo_circle.gif
246openid.provider.rendering.helpIcon = %(openid.provider.rendering.baseURL)s/layout/icons/help.png
247openid.provider.rendering.footerText = This site is for test purposes only.
248openid.provider.rendering.rightLink = http://ceda.ac.uk/
249openid.provider.rendering.rightImage = %(openid.provider.rendering.baseURL)s/layout/CEDA_RightButton60.png
250openid.provider.rendering.rightAlt = Centre for Environmental Data Archival
251
252# Basic Authentication interface to demonstrate capabilities
253#openid.provider.authNInterface=ndg.security.server.wsgi.openid.provider.authninterface.basic.BasicAuthNInterface
254openid.provider.authNInterface=ndg.security.server.wsgi.openid.provider.authninterface.sqlalchemy_authn.SQLAlchemyAuthnInterface
255openid.provider.authN.connectionString=%(dbConnectionString)s
256openid.provider.authN.logonSqlQuery=select count(*) from users where username = '${username}' and md5password = '${password}'
257openid.provider.authN.username2UserIdentifierSqlQuery=select openid_identifier from users where username = '${username}'
258openid.provider.authN.isMD5EncodedPwd=True
259
260# user login details format is:
261# <username>:<password>:<OpenID name>, ... <OpenID name N> <username>:... etc
262# Each user entry is delimited by a space. username, password and OpenID name
263# list are delimited by a colon.  The list of OpenID names are delimited by
264# commas.  The OpenID name represents the unique part of the OpenID URL for the
265# individual user.  Each username may have more than one OpenID alias but only
266# alias at a time may be registered with a given Attribute Authority
267openid.provider.authN.userCreds=pjk:testpassword:PhilipKershaw,P.J.Kershaw another:testpassword:A.N.Other
268
269# Basic authentication for testing/admin - comma delimited list of
270# <username>:<password> pairs
271#openid.provider.usercreds=pjk:test
272
273# Attribute Exchange interface
274#openid.provider.axResponse.class=ndg.security.server.wsgi.openid.provider.axinterface.csv.CSVFileAXInterface
275#openid.provider.axResponse.csvFilePath=%(here)s/openidprovider/attributeexchange.csv
276openid.provider.axResponse.class=ndg.security.server.wsgi.openid.provider.axinterface.sqlalchemy_ax.SQLAlchemyAXInterface
277openid.provider.axResponse.connectionString=%(dbConnectionString)s
278openid.provider.axResponse.sqlQuery = select firstname, lastname, emailaddress from users where username = '${username}'
279openid.provider.axResponse.attributeNames=http://openid.net/schema/namePerson/first
280    http://openid.net/schema/namePerson/last
281    http://openid.net/schema/contact/internet/email
282   
283openid.provider.trustedRelyingParties=https://localhost:7443, https://ndg.somewhere.ac.uk,
284        https://badc.somewhere.ac.uk
285
286#______________________________________________________________________________
287# Attribute Authority WSGI settings
288#
289[filter:AttributeAuthorityFilter]
290# This filter publishes an Attribute Authority instance as a key in environ
291# to enable other middleware to access it
292paste.filter_app_factory = ndg.security.server.wsgi.attributeauthority:AttributeAuthorityMiddleware.filter_app_factory
293prefix = attributeAuthority.
294
295# Key name by which the WSDL SOAP based interface may reference this
296# service
297attributeAuthority.environKeyName = %(attributeAuthorityEnvironKeyName)s
298
299# Key name for the SAML SOAP binding based interface to reference this
300# service's attribute query method
301attributeAuthority.environKeyNameAttributeQueryInterface: %(attributeQueryInterfaceEnvironKeyName)s
302
303# Attribute Authority settings
304# 'name' setting MUST agree with map config file 'thisHost' name attribute
305attributeAuthority.name: Site A
306
307# Lifetime is measured in seconds
308attributeAuthority.attCertLifetime: 28800 
309
310# Allow an offset for clock skew between servers running
311# security services. NB, measured in seconds - use a minus sign for time in the
312# past
313attributeAuthority.attCertNotBeforeOff: 0
314
315# All Attribute Certificates issued are recorded in this dir
316attributeAuthority.attCertDir: %(testConfigDir)s/attributeauthority/sitea/attributeCertificateLog
317
318# Files in attCertDir are stored using a rotating file handler
319# attCertFileLogCnt sets the max number of files created before the first is
320# overwritten
321attributeAuthority.attCertFileName: ac.xml
322attributeAuthority.attCertFileLogCnt: 16
323attributeAuthority.dnSeparator:/
324
325# Location of role mapping file
326attributeAuthority.mapConfigFilePath: %(testConfigDir)s/attributeauthority/sitea/siteAMapConfig.xml
327
328# Settings for custom AttributeInterface derived class to get user roles for given
329# user ID
330#attributeAuthority.attributeInterface.modFilePath: %(testConfigDir)s/attributeauthority/sitea
331#attributeAuthority.attributeInterface.modName: siteAUserRoles
332#attributeAuthority.attributeInterface.className: TestUserRoles
333
334# SQLAlchemy Attribute Interface
335attributeAuthority.attributeInterface.connectionString: %(dbConnectionString)s
336attributeAuthority.attributeInterface.modName: ndg.security.server.attributeauthority
337attributeAuthority.attributeInterface.className: SQLAlchemyAttributeInterface
338attributeAuthority.attributeInterface.issuerName = /O=Site A/CN=Attribute Authority
339attributeAuthority.attributeInterface.samlSubjectSqlQuery = select count(*) from users where openid = '${userId}'
340attributeAuthority.attributeInterface.samlAttribute2SqlQuery.1 = "urn:esg:first:name" "select firstname from users where openid = '${userId}'"
341attributeAuthority.attributeInterface.samlAttribute2SqlQuery.lastName = "urn:esg:last:name" "select lastname from users where openid = '${userId}'"
342attributeAuthority.attributeInterface.samlAttribute2SqlQuery.emailAddress = "urn:esg:email:address" "select emailaddress from users where openid = '${userId}'"
343attributeAuthority.attributeInterface.samlAttribute2SqlQuery.4 = "urn:siteA:security:authz:1.0:attr" "select attributename from attributes where openid = '${userId}'"
344attributeAuthority.attributeInterface.samlValidRequestorDNs = /O=Site A/CN=Authorisation Service,/O=Site A/CN=Attribute Authority,
345                                                           /O=Site B/CN=Authorisation Service,
346                                                           /CN=test/O=NDG/OU=BADC
347
348# Config for XML signature of Attribute Certificate
349attributeAuthority.signingPriKeyFilePath: %(testConfigDir)s/attributeauthority/sitea/siteA-aa.key
350attributeAuthority.signingCertFilePath: %(testConfigDir)s/attributeauthority/sitea/siteA-aa.crt
351attributeAuthority.caCertFilePathList: %(testConfigDir)s/ca/ndg-test-ca.crt
352
353
354# SOAP WSDL Based Binding to the Attribute Authority
355[filter:AttributeAuthorityWsdlSoapBindingFilter]
356paste.filter_app_factory = ndg.security.server.wsgi.attributeauthority:AttributeAuthoritySOAPBindingMiddleware.filter_app_factory
357prefix = service.soap.binding.
358attributeAuthoritySOAPBindingPrefix = attributeauthority.service.soap.binding.
359
360service.soap.binding.referencedFilters = filter:wsseSignatureVerificationFilter
361service.soap.binding.path = /AttributeAuthority
362service.soap.binding.enableWSDLQuery = True
363service.soap.binding.charset = utf-8
364service.soap.binding.serviceSOAPBindingEnvironKeyName = ndg.security.server.wsgi.attributeauthority.AttributeAuthoritySOAPBindingMiddleware
365
366attributeauthority.service.soap.binding.attributeAuthorityEnvironKeyName = %(attributeAuthorityEnvironKeyName)s
367attributeauthority.service.soap.binding.wsseSignatureVerificationFilterID = filter:wsseSignatureVerificationFilter
368
369
370# SAML SOAP Binding to the Attribute Authority
371[filter:AttributeAuthoritySamlSoapBindingFilter]
372paste.filter_app_factory = ndg.security.server.wsgi.saml:SOAPAttributeInterfaceMiddleware.filter_app_factory
373prefix = saml.soapbinding.
374
375saml.soapbinding.pathMatchList = /AttributeAuthority/saml
376saml.soapbinding.queryInterfaceKeyName = %(attributeQueryInterfaceEnvironKeyName)s
377
378
379#______________________________________________________________________________
380# WS-Security Signature Verification
381[filter:wsseSignatureVerificationFilter]
382paste.filter_app_factory = ndg.security.server.wsgi.wssecurity:SignatureVerificationFilter.filter_app_factory
383filterID = %(__name__)s
384
385# Settings for WS-Security SignatureHandler class used by this filter
386wsseCfgFilePrefix = wssecurity
387
388# Verify against known CAs - Provide a space separated list of file paths
389wssecurity.caCertFilePathList=%(testConfigDir)s/ca/ndg-test-ca.crt
390
391
392#______________________________________________________________________________
393# Apply WS-Security Signature
394[filter:wsseSignatureFilter]
395paste.filter_app_factory = ndg.security.server.wsgi.wssecurity:ApplySignatureFilter.filter_app_factory
396
397# Reference the verification filter in order to be able to apply signature
398# confirmation
399referencedFilters = filter:wsseSignatureVerificationFilter
400wsseSignatureVerificationFilterID = filter:wsseSignatureVerificationFilter
401
402# Last filter in chain of SOAP handlers writes the response
403writeResponse = True
404
405# Settings for WS-Security SignatureHandler class used by this filter
406wsseCfgFilePrefix = wssecurity
407
408# Certificate associated with private key used to sign a message.  The sign
409# method will add this to the BinarySecurityToken element of the WSSE header. 
410wssecurity.signingCertFilePath=%(testConfigDir)s/pki/wsse-server.crt
411
412# PEM encoded private key file
413wssecurity.signingPriKeyFilePath=%(testConfigDir)s/pki/wsse-server.key
414
415# Set the ValueType for the BinarySecurityToken added to the WSSE header for a
416# signed message.  See __setReqBinSecTokValType method and binSecTokValType
417# class variable for options - it may be one of X509, X509v3, X509PKIPathv1 or
418# give full namespace to alternative - see
419# ZSI.wstools.Namespaces.OASIS.X509TOKEN
420#
421# binSecTokValType determines whether signingCert or signingCertChain
422# attributes will be used.
423wssecurity.reqBinSecTokValType=X509v3
424
425# Add a timestamp element to an outbound message
426wssecurity.addTimestamp=True
427
428# For WSSE 1.1 - service returns signature confirmation containing signature
429# value sent by client
430wssecurity.applySignatureConfirmation=True
431
432# Logging configuration
433[loggers]
434keys = root, ndg
435
436[handlers]
437keys = console
438
439[formatters]
440keys = generic
441
442[logger_root]
443level = INFO
444handlers = console
445
446[logger_ndg]
447level = DEBUG
448handlers =
449qualname = ndg
450
451[handler_console]
452class = StreamHandler
453args = (sys.stderr,)
454level = NOTSET
455formatter = generic
456
457[formatter_generic]
458format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s:%(lineno)s] %(message)s
459datefmt = %Y-%m-%d %H:%M:%S
460
Note: See TracBrowser for help on using the repository browser.