def identify(self, arguments, **httpkwargs): responseDate = zuluTime() if arguments.keys() != ['verb']: additionalMessage = 'Argument(s) %s is/are illegal.' % ", ".join('"%s"' % key for key in arguments.keys() if key != 'verb') yield oaiError('badArgument', additionalMessage=additionalMessage, arguments=arguments, requestUrl=self._repository.requestUrl(**httpkwargs), **httpkwargs) return repositoryIdentifier = self._repository.identifier descriptionRepositoryIdentifier = '' if not repositoryIdentifier else DESCRIPTION_REPOSITORY_IDENTIFIER % {'repositoryIdentifier': escapeXml(repositoryIdentifier)} values = { 'repositoryName': escapeXml(self._repository.name), 'baseURL': escapeXml(self._repository.requestUrl(**httpkwargs)), 'adminEmails': ''.join([ADMIN_EMAIL % escapeXml(email) for email in [self._repository.adminEmail]]), 'deletedRecord': self.call.getDeletedRecordType(), } values.update(hardcoded_values) yield oaiHeader(self, responseDate) yield oaiRequestArgs(arguments, requestUrl=self._repository.requestUrl(**httpkwargs), **httpkwargs) yield '<Identify>' yield IDENTIFY % values yield descriptionRepositoryIdentifier yield TOOLKIT_DESCRIPTION yield self.all.description() yield '</Identify>' yield oaiFooter()
def description(self): yield """<description xmlns="http://www.openarchives.org/OAI/2.0/">""" yield """<branding xmlns="http://www.openarchives.org/OAI/2.0/branding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/branding/ http://www.openarchives.org/OAI/2.0/branding.xsd">""" yield "<collectionIcon>" yield " <url>%s</url>" % escapeXml(self._url) yield " <link>%s</link>" % escapeXml(self._link) if self._link else "" yield " <title>%s</title>" % self._title if self._title else "" yield "</collectionIcon>" yield "</branding>" yield "</description>"
def _getHumanStartPage(self, lxmlNode): didl_hsp_item = lxmlNode.xpath( '//didl:Item/didl:Item[didl:Descriptor/didl:Statement/rdf:type/@rdf:resource="info:eu-repo/semantics/humanStartPage"]', namespaces=self._nsMap) if len(didl_hsp_item) == 0: didl_hsp_item = lxmlNode.xpath( '//didl:Item/didl:Item[didl:Descriptor/didl:Statement/rdf:type/@resource="info:eu-repo/semantics/humanStartPage"]', namespaces=self._nsMap) if len(didl_hsp_item) > 0: self.do.logMsg(self._uploadid, LOGGER9, prefix=STR_DIDL) if len(didl_hsp_item) == 0: didl_hsp_item = lxmlNode.xpath( '//didl:Item/didl:Item[didl:Descriptor/didl:Statement/dip:ObjectType/text()="info:eu-repo/semantics/humanStartPage"]', namespaces=self._nsMap) if len(didl_hsp_item) > 0: self.do.logMsg(self._uploadid, LOGGER10, prefix=STR_DIDL) if len(didl_hsp_item) == 0: self.do.logMsg(self._uploadid, LOGGER11, prefix=STR_DIDL) return "" uriref = didl_hsp_item[0].xpath( 'self::didl:Item/didl:Component/didl:Resource/@ref', namespaces=self._nsMap) mimetype = didl_hsp_item[0].xpath( 'self::didl:Item/didl:Component/didl:Resource/@mimeType', namespaces=self._nsMap) if len(mimetype) == 0: self.do.logMsg(self._uploadid, LOGGER13, prefix=STR_DIDL) if len(mimetype) > 0 and not comm.isMimeType(mimetype[0]): self.do.logMsg(self._uploadid, LOGGER12 + mimetype[0], prefix=STR_DIDL) if len(uriref) == 0 or not comm.isURL(uriref[0]): raise ValidateException( formatExceptionLine(EXCEPTION11, prefix=STR_DIDL)) return """<didl:Item> <didl:Descriptor> <didl:Statement mimeType="application/xml"> <rdf:type rdf:resource="info:eu-repo/semantics/humanStartPage"/> </didl:Statement> </didl:Descriptor> <didl:Component> <didl:Resource ref="%s" mimeType="%s"/> </didl:Component> </didl:Item>""" % (escapeXml(comm.urlQuote( uriref[0].strip())), escapeXml(mimetype[0]))
def description(self): yield """<description xmlns="http://www.openarchives.org/OAI/2.0/">""" yield """<branding xmlns="http://www.openarchives.org/OAI/2.0/branding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/branding/ http://www.openarchives.org/OAI/2.0/branding.xsd">""" yield "<collectionIcon>" yield " <url>%s</url>" % escapeXml(self._url) yield " <link>%s</link>" % escapeXml( self._link) if self._link else "" yield " <title>%s</title>" % self._title if self._title else "" yield "</collectionIcon>" yield "</branding>" yield "</description>"
def handleRequest(self, Body=b'', **kwargs): contentLength = kwargs['Headers'].get('Content-Length') if contentLength: Body = Body[:int(contentLength)] yield '\r\n'.join([ 'HTTP/1.0 200 Ok', 'Content-Type: text/xml; charset=utf-8\r\n', '' ]) try: updateRequest = XML(Body) recordId = xpathFirst(updateRequest, 'ucp:recordIdentifier/text()') action = xpathFirst(updateRequest, 'ucp:action/text()') if self._allInvalid and action == "info:srw/action/1/replace": if 'oai:record:02' in recordId: raise InvalidDataException() raise InvalidDataException('Invalid data') if recordId in self._raiseExceptionOnIds: raise Exception("ERROR") self._number += 1 filename = '%05d_%s.updateRequest' % (self._number, action.rsplit('/')[-1]) with open(join(self._dumpdir, filename), 'w') as f: stdout.flush() f.write(lxmltostring(updateRequest, pretty_print=True)) answer = RESPONSE_XML % { "operationStatus": "success", "diagnostics": "" } except InvalidDataException as e: answer = RESPONSE_XML % { "operationStatus": "fail", "diagnostics": DIAGNOSTIC_XML % { 'uri': 'info:srw/diagnostic/12/12', 'details': escapeXml(str(e)), 'message': 'Invalid data: record rejected' } } except Exception as e: answer = RESPONSE_XML % { "operationStatus": "fail", "diagnostics": DIAGNOSTIC_XML % { 'uri': 'info:srw/diagnostic/12/1', 'details': escapeXml(format_exc()), 'message': 'Invalid component: record rejected' } } import sys sys.stdout.flush() yield answer
def _deleteRecord(self, recordId): print 'DELETING', recordId body = """<ucp:updateRequest xmlns:ucp="info:lc/xmlns/update-v1"> <srw:version xmlns:srw="http://www.loc.gov/zing/srw/">1.0</srw:version> <ucp:action>info:srw/action/1/delete</ucp:action> <ucp:recordIdentifier>%s</ucp:recordIdentifier> </ucp:updateRequest>""" % escapeXml(recordId) s = socket() s.connect(self._sruUpdateHostAndPort) s.send("POST /internal/update HTTP/1.0\r\n") s.send("Content-Type: text/xml\r\n") s.send("Content-Length: %s\r\n" % len(body)) s.send("\r\n") s.sendall(body) # Receive the answer from the server and print it to stdout. response = s.recv(1024) while True: part = s.recv(1024) if part == "": break response += part s.close() if 'success' in response: return raise ValueError(response)
def handleRequest(self, Body='', **kwargs): yield '\r\n'.join(['HTTP/1.0 200 Ok', 'Content-Type: text/xml; charset=utf-8\r\n', '']) try: updateRequest = XML(Body) recordId = xpathFirst(updateRequest, 'ucp:recordIdentifier/text()') action = xpathFirst(updateRequest, 'ucp:action/text()') if self._allInvalid and action == "info:srw/action/1/replace": if 'oai:record:02' in recordId: raise InvalidDataException() raise InvalidDataException('Invalid data') if recordId in self._raiseExceptionOnIds: raise Exception("ERROR") self._number +=1 filename = '%05d_%s.updateRequest' %(self._number, action.rsplit('/')[-1]) with open(join(self._dumpdir, filename), 'w') as f: stdout.flush() f.write(lxmltostring(updateRequest, pretty_print=True)) answer = RESPONSE_XML % { "operationStatus": "success", "diagnostics": ""} except InvalidDataException, e: answer = RESPONSE_XML % { "operationStatus": "fail", "diagnostics": DIAGNOSTIC_XML % { 'uri': 'info:srw/diagnostic/12/12', 'details': escapeXml(str(e)), 'message': 'Invalid data: record rejected'}}
def identify(self, arguments, **httpkwargs): responseDate = zuluTime() if list(arguments.keys()) != ['verb']: allArguments = sorted(list(arguments.keys())) additionalMessage = 'Argument(s) %s is/are illegal.' % ", ".join( '"%s"' % key for key in allArguments if key != 'verb') yield oaiError( 'badArgument', additionalMessage=additionalMessage, arguments=arguments, requestUrl=self._repository.requestUrl(**httpkwargs), **httpkwargs) return repositoryIdentifier = self._repository.identifier descriptionRepositoryIdentifier = '' if not repositoryIdentifier else DESCRIPTION_REPOSITORY_IDENTIFIER % { 'repositoryIdentifier': escapeXml(repositoryIdentifier) } values = { 'repositoryName': escapeXml(self._repository.name), 'baseURL': escapeXml(self._repository.requestUrl(**httpkwargs)), 'adminEmails': ''.join([ ADMIN_EMAIL % escapeXml(email) for email in [self._repository.adminEmail] ]), 'deletedRecord': self.call.getDeletedRecordType(), } values.update(hardcoded_values) yield oaiHeader(self, responseDate) yield oaiRequestArgs( arguments, requestUrl=self._repository.requestUrl(**httpkwargs), **httpkwargs) yield '<Identify>' yield IDENTIFY % values yield descriptionRepositoryIdentifier yield TOOLKIT_DESCRIPTION yield self.all.description() yield '</Identify>' yield oaiFooter()
def _generateXml(fields): for (key, value) in fields: tags = key.split('.') for tag in tags: if not correctNameRe.match(tag): raise Fields2XmlException('Invalid key: "%s"' % key) yield ''.join("<%s>" % tag for tag in tags) yield escapeXml(value) yield ''.join("</%s>" % tag for tag in reversed(tags))
def _getIdentifierDescriptor(self, lxmlNode): # Check geldige Identifier (feitelijk verplicht, hoewel vaak niet geimplemeteerd...) idee = lxmlNode.xpath( 'self::didl:Item/didl:Descriptor/didl:Statement/dii:Identifier/text()', namespaces=self._nsMap) if len(idee) > 0: return descr_templ % ('<dii:Identifier>' + escapeXml( idee[0].strip()) + '</dii:Identifier>') else: return ''
def _findAndBindFirst(self, node, template, *xpaths): ## Will bind only the FIRST (xpath match/record) to the template. It will never return more than one templated element... items = [] for p in xpaths: items += node.xpath(p, namespaces=self._nsMap) if len(items) > 1: break for item in items: return template % escapeXml(item) return ''
def _openSearchDescription(self, **kwargs): server = self._host if str(self._port) == "80" else "{}:{}".format(self._host, self._port) yield okXml yield """<?xml version="1.0" encoding="UTF-8"?>""" yield """<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"> <ShortName>%(shortname)s</ShortName> <Description>%(description)s</Description> <Url type="text/xml" method="get" template="http://%(server)s%(xmlTemplateQuery)s"/> %(htmlUrl)s <Url type="%(contentTypeSuggest)s" template="http://%(server)s%(path)s?%(templateQueryForSuggest)s"/> </OpenSearchDescription>""" % { 'contentTypeSuggest': CONTENT_TYPE_JSON_SUGGESTIONS, 'shortname': self._shortname, 'description': self._description, 'server': server, 'path': self._path, 'xmlTemplateQuery': escapeXml(self._xmlTemplateQuery), 'htmlUrl': '<Url type="text/html" method="get" template="http://{server}{template}"/>'.format(server=server, template=escapeXml(self._htmlTemplateQuery)) if self._htmlTemplateQuery else '', 'templateQueryForSuggest': escapeXml(self._templateQueryForSuggest()), }
def _openSearchDescription(self, **kwargs): server = self._host if str(self._port) == "80" else "{}:{}".format(self._host, self._port) yield okXml yield """<?xml version="1.0" encoding="UTF-8"?>""" yield """<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"> <ShortName>%(shortname)s</ShortName> <Description>%(description)s</Description> <Url type="text/xml" method="get" template="http://%(server)s%(xmlTemplateQuery)s"/> %(htmlUrl)s <Url type="%(contentTypeSuggest)s" template="http://%(server)s%(path)s?%(templateQueryForSuggest)s"/> </OpenSearchDescription>""" % { "contentTypeSuggest": CONTENT_TYPE_JSON_SUGGESTIONS, "shortname": self._shortname, "description": self._description, "server": server, "path": self._path, "xmlTemplateQuery": escapeXml(self._xmlTemplateQuery), "htmlUrl": '<Url type="text/html" method="get" template="http://{server}{template}"/>'.format( server=server, template=escapeXml(self._htmlTemplateQuery) ) if self._htmlTemplateQuery else "", "templateQueryForSuggest": escapeXml(self._templateQueryForSuggest()), }
def description(self): yield """<description xmlns="http://www.openarchives.org/OAI/2.0/">""" yield """<Service xmlns="https://www.openaire.eu/cerif-profile/1.1/">""" yield """<Compatibility xmlns="https://www.openaire.eu/cerif-profile/vocab/OpenAIRE_Service_Compatibility">https://www.openaire.eu/cerif-profile/vocab/OpenAIRE_Service_Compatibility#1.1</Compatibility>""" yield "<Acronym>%s</Acronym>" % escapeXml(self._acronym) yield """<Name xml:lang="en">%s</Name>""" % escapeXml(self._name) yield """<Description xml:lang="en">%s</Description>""" % escapeXml( self._description) yield "<WebsiteURL>%s</WebsiteURL>" % escapeXml(self._website) yield "<OAIPMHBaseURL>%s</OAIPMHBaseURL>" % escapeXml(self._baseurl) yield "<SubjectHeadingsURL>%s</SubjectHeadingsURL>" % escapeXml( self._subjectheading) yield "<Owner>" yield """ <OrgUnit id="%s">""" % escapeXml(self._orgunitid) yield " <Acronym>%s</Acronym>" % escapeXml(self._owneracronym) yield " </OrgUnit>" yield "</Owner>" yield "</Service>" yield "</description>"
def main(reactor, port, directory): dumpdir = join(directory, 'dump') isdir(dumpdir) or makedirs(dumpdir) dump = Dump(dumpdir) oaiStorage = MultiSequentialStorage(join(directory, 'storage')) oaiJazz = OaiJazz(join(directory, 'oai')) server = be( (Observable(), (ObservableHttpServer(reactor, port), (PathFilter("/dump"), (dump, )), (PathFilter("/control"), ( Control(), (dump, ), (Log(), ), )), (PathFilter('/oai'), (Log(), ( OaiPmh(repositoryName="Oai Test Server", adminEmail="*****@*****.**", batchSize=10), (oaiStorage, ), (oaiJazz, ), ))), (PathFilter('/badoai'), (Log(), (BadOai(), ))), (PathFilter("/log"), (RetrieveLog(), (Log(), ))), (PathFilter("/ready"), (StringServer('yes', ContentTypePlainText), ))))) list(compose(server.once.observer_init())) oaiJazz.updateMetadataFormat( prefix="oai_dc", schema="http://www.openarchives.org/OAI/2.0/oai_dc.xsd", namespace="http://www.openarchives.org/OAI/2.0/oai_dc/") for i in range(1, 16): if i == 2: identifier = 'oai:record:02/&gkn' else: identifier = 'oai:record:%02d' % i oaiStorage.addData( identifier=identifier, name='oai_dc', data=bytes( '''<oai_dc:dc xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dc="http://purl.org/dc/elements/1.1/" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd"><dc:identifier>%s</dc:identifier><dc:title>Title is √</dc:title></oai_dc:dc>''' % escapeXml(identifier), encoding='utf-8')) oaiJazz.addOaiRecord(identifier=identifier, metadataPrefixes=['oai_dc']) if i in [3, 6]: list(compose(oaiJazz.delete(identifier=identifier)))
def asXml(self): return """<{username}>{xml}</{username}>""".format( username=self.username, xml=''.join("<{tag}>{value}</{tag}>".format( tag=tag, value=escapeXml(getattr(self, tag, ''))) for tag in self.ATTRIBUTES))
def main(reactor, port, directory): dumpdir = join(directory, 'dump') isdir(dumpdir) or makedirs(dumpdir) dump = Dump(dumpdir) oaiStorage = MultiSequentialStorage(join(directory, 'storage')) oaiJazz = OaiJazz(join(directory, 'oai')) server = be( (Observable(), (ObservableHttpServer(reactor, port), (PathFilter("/dump"), (dump,) ), (PathFilter("/control"), (Control(), (dump,), (Log(),), ) ), (PathFilter('/oai'), (Log(), (OaiPmh(repositoryName="Oai Test Server", adminEmail="*****@*****.**", batchSize=10), (oaiStorage,), (oaiJazz,), ) ) ), (PathFilter("/log"), (RetrieveLog(), (Log(),) ) ), (PathFilter("/ready"), (StringServer('yes', ContentTypePlainText),) ) ) ) ) list(compose(server.once.observer_init())) for i in range(1,16): if i == 2: identifier = 'oai:record:02/&gkn' else: identifier = 'oai:record:%02d' % i oaiStorage.addData(identifier=identifier, name='oai_dc', data='''<oai_dc:dc xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dc="http://purl.org/dc/elements/1.1/" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd"><dc:identifier>%s</dc:identifier></oai_dc:dc>''' % escapeXml(identifier)) oaiJazz.addOaiRecord(identifier=identifier, metadataFormats=[('oai_dc', 'http://www.openarchives.org/OAI/2.0/oai_dc.xsd', 'http://www.openarchives.org/OAI/2.0/oai_dc/')]) if i in [3,6]: list(compose(oaiJazz.delete(identifier=identifier)))
else: raise ValueError("action value should refer to either 'create', 'replace' or 'delete'.") yield self._respond() except ValidateException, e: localLogCollector["invalid"] = recordId self._log(Body, e, localLogCollector=localLogCollector) yield self._respond( diagnosticUri="info:srw/diagnostic/12/12", details=escapeXml(str(e)), message="Invalid data: record rejected", ) except Exception, e: self._log(Body, e, localLogCollector=localLogCollector) yield self._respond( diagnosticUri="info:srw/diagnostic/12/1", details=escapeXml(format_exc()), message="Invalid component: record rejected", ) finally: self._collectLogForScope(sruRecordUpdate=localLogCollector) def _respond(self, diagnosticUri=None, details="", message=""): operationStatus = "success" diagnostics = "" if diagnosticUri: operationStatus = "fail" uri = diagnosticUri diagnostics = DIAGNOSTIC_XML % locals() yield RESPONSE_XML % locals() def _log(self, data, error=None, localLogCollector=None):
def _escapeXml(value): return escapeXml(value).replace('"', """)
def delete(self, identifier): path = self._path('update') yield self._send(path=path, body="<delete><id>%s</id></delete>" % escapeXml(identifier))
def _getObjectfiles(self, lxmlNode): of_container = '' objectfiles = lxmlNode.xpath( '//didl:DIDL/didl:Item/didl:Item[didl:Descriptor/didl:Statement/rdf:type/@rdf:resource="info:eu-repo/semantics/objectFile"]', namespaces=self._nsMap) if len(objectfiles) == 0: objectfiles = lxmlNode.xpath( '//didl:DIDL/didl:Item/didl:Item[didl:Descriptor/didl:Statement/rdf:type/@resource="info:eu-repo/semantics/objectFile"]', namespaces=self._nsMap) if len(objectfiles) > 0: self.do.logMsg(self._uploadid, LOGGER6, prefix=STR_DIDL) if len(objectfiles) == 0: objectfiles = lxmlNode.xpath( '//didl:DIDL/didl:Item/didl:Item[didl:Descriptor/didl:Statement/dip:ObjectType/text()="info:eu-repo/semantics/objectFile"]', namespaces=self._nsMap) if len(objectfiles) > 0: self.do.logMsg(self._uploadid, LOGGER7, prefix=STR_DIDL) for objectfile in objectfiles: #1:Define correct ObjectFile descriptor: of_container += '<didl:Item><didl:Descriptor><didl:Statement mimeType="application/xml"><rdf:type rdf:resource="info:eu-repo/semantics/objectFile"/></didl:Statement></didl:Descriptor>' #2: Check geldige Identifier (feitelijk verplicht, hoewel vaak niet geimplemeteerd...) pi = objectfile.xpath( 'self::didl:Item/didl:Descriptor/didl:Statement/dii:Identifier/text()', namespaces=self._nsMap) if len(pi) > 0: of_container += descr_templ % ('<dii:Identifier>' + escapeXml( pi[0].strip()) + '</dii:Identifier>') #3: Check op geldige AccessRights: arights = objectfile.xpath( 'self::didl:Item/didl:Descriptor/didl:Statement/dcterms:accessRights/text()', namespaces=self._nsMap) if len(arights) > 0: for key, value in accessRights.iteritems(): if arights[0].strip().lower().find(key) >= 0: of_container += descr_templ % ( '<dcterms:accessRights>' + value + '</dcterms:accessRights>') break else: raise ValidateException( formatExceptionLine(arights[0] + EXCEPTION12, prefix=STR_DIDL)) else: raise ValidateException( formatExceptionLine(EXCEPTION8, prefix=STR_DIDL)) #4: Check geldige datemodified (feitelijk verplicht, hoewel vaak niet geimplemeteerd...) modified = objectfile.xpath( 'self::didl:Item/didl:Descriptor/didl:Statement/dcterms:modified/text()', namespaces=self._nsMap) if len(modified) > 0 and comm.isISO8601(modified[0]): of_container += descr_templ % ('<dcterms:modified>' + modified[0].strip() + '</dcterms:modified>') #5: Check for 'file' description: descr = objectfile.xpath( 'self::didl:Item/didl:Descriptor/didl:Statement/dc:description/text()', namespaces=self._nsMap) if len(descr) > 0: of_container += descr_templ % ('<dc:description>' + escapeXml( descr[0].strip()) + '</dc:description>') ## SKIPPING: Not in EduStandaard. #6.0: Check for embargo: # embargo = objectfile.xpath('self::didl:Item/didl:Descriptor/didl:Statement/dcterms:available/text()', namespaces=self._nsMap) # if len(embargo) > 0 and comm.isISO8601(embargo[0]): # of_container += descr_templ % ('<dcterms:available>'+embargo[0].strip()+'</dcterms:available>') ## SKIPPING: Not in EduStandaard. #6.1: Check for dateSubmitted: # dembargo = objectfile.xpath('self::didl:Item/didl:Descriptor/didl:Statement/dcterms:dateSubmitted/text()', namespaces=self._nsMap) # if len(dembargo) > 0 and comm.isISO8601(dembargo[0]): # of_container += descr_templ % ('<dcterms:dateSubmitted>'+dembargo[0].strip()+'</dcterms:dateSubmitted>') # else: # #6.2: Check for issued (depricated, normalize to dateSubmitted): # issued = objectfile.xpath('self::didl:Item/didl:Descriptor/didl:Statement/dcterms:issued/text()', namespaces=self._nsMap) # if len(issued) > 0 and comm.isISO8601(issued[0]): # of_container += descr_templ % ('<dcterms:dateSubmitted>'+issued[0].strip()+'</dcterms:dateSubmitted>') #7: Check for published version(author/publisher): pubVersion = objectfile.xpath( 'self::didl:Item/didl:Descriptor/didl:Statement/rdf:type/@rdf:resource', namespaces=self._nsMap) if len( pubVersion ) > 0: ## Both (author/publisher) may be available: we'll take the first one... for key, value in pubVersions.iteritems(): if pubVersion[0].strip().lower().find(key) >= 0: of_container += descr_templ % ( '<rdf:type rdf:resource="' + value + '"/>') break #8:Check for MANDATORY resources and mimetypes: didl_resources = objectfile.xpath( 'self::didl:Item/didl:Component/didl:Resource[@mimeType and @ref]', namespaces=self._nsMap) resources = '' _url_list = [] for resource in didl_resources: mimeType = resource.xpath('self::didl:Resource/@mimeType', namespaces=self._nsMap) uri = resource.xpath('self::didl:Resource/@ref', namespaces=self._nsMap) ## We need both mimeType and URI: (MIMETYPE is required by DIDL schema, @ref not). if len(mimeType) > 0 and len(uri) > 0: if not comm.isMimeType(mimeType[0]): self.do.logMsg(self._uploadid, LOGGER8 + mimeType[0], prefix=STR_DIDL) if comm.isURL(uri[0].strip()): resources += """<didl:Resource mimeType="%s" ref="%s"/>""" % ( escapeXml(mimeType[0].strip()), escapeXml(comm.urlQuote(uri[0].strip()))) _url_list.append( """<didl:Resource mimeType="%s" ref="%s"/>""" % (escapeXml(mimeType[0].strip()), escapeXml(comm.urlQuote(uri[0].strip())))) else: raise ValidateException( formatExceptionLine(EXCEPTION9 + uri[0], prefix=STR_DIDL)) if resources != '': of_container += """<didl:Component> %s </didl:Component>""" % (resources) else: raise ValidateException( formatExceptionLine(EXCEPTION10, prefix=STR_DIDL)) of_container += '</didl:Item>' return of_container
yield self.all.delete(identifier=recordId) else: raise ValueError( "action value should refer to either 'create', 'replace' or 'delete'." ) yield self._respond() except ValidateException, e: localLogCollector['invalid'] = recordId self._log(Body, e, localLogCollector=localLogCollector) yield self._respond(diagnosticUri='info:srw/diagnostic/12/12', details=escapeXml(str(e)), message='Invalid data: record rejected') except Exception, e: self._log(Body, e, localLogCollector=localLogCollector) yield self._respond(diagnosticUri='info:srw/diagnostic/12/1', details=escapeXml(format_exc()), message='Invalid component: record rejected') finally: self._collectLogForScope(sruRecordUpdate=localLogCollector) def _respond(self, diagnosticUri=None, details='', message=''): operationStatus = "success" diagnostics = "" if diagnosticUri: operationStatus = "fail" uri = diagnosticUri diagnostics = DIAGNOSTIC_XML % locals() yield RESPONSE_XML % locals() def _log(self, data, error=None, localLogCollector=None): if not error is None:
def _convertNODRecord2smods(self, lxmlNode): penvoerder, penvoerder_en, abstract, abstract_en, locatie, status, knaw_long = '', '', '', '', '', '', None # ORGANISATIE: if lxmlNode.xpath("boolean(count(//org:organisatie))", namespaces=namespaceMap): title = self._findAndBind(lxmlNode, '\n<titleInfo><title>%s</title></titleInfo>', '//org:naam_nl/text()') title_en = self._findAndBind(lxmlNode, '\n<titleInfo xml:lang="en"><title>%s</title></titleInfo>', '//org:naam_en/text()') taak = lxmlNode.xpath("//org:taak_nl/text()", namespaces=namespaceMap) if taak: abstract = '\n<abstract>%s</abstract>' % escapeXml(taak[0][:self._truncate_chars]) taak = lxmlNode.xpath("//org:taak_en/text()", namespaces=namespaceMap) if taak: abstract_en = '\n<abstract xml:lang="en">%s</abstract>' % escapeXml(taak[0][:self._truncate_chars]) # Locatie: In Dutch (NL) only! location = lxmlNode.xpath("//org:locatie/text()", namespaces=namespaceMap) if location: locatie = '\n<locatie>%s</locatie>' % escapeXml(location[0]) knaw_long = '''<knaw_long xmlns="http://www.knaw.nl/narcis/1.0/long/"><metadata>%s%s<genre>organisation</genre>%s%s%s</metadata></knaw_long>''' % ( title, title_en, locatie, abstract, abstract_en) # ACTIVITEIT: elif lxmlNode.xpath("boolean(count(//ond:activiteit))", namespaces=namespaceMap): title = self._findAndBind(lxmlNode, '\n<titleInfo><title>%s</title></titleInfo>', '//ond:title_nl/text()') title_en = self._findAndBind(lxmlNode, '\n<titleInfo xml:lang="en"><title>%s</title></titleInfo>', '//ond:title_en/text()') # Samenvatting: taak = lxmlNode.xpath("//ond:summary_nl/text()", namespaces=namespaceMap) if taak: abstract = '\n<abstract>%s</abstract>' % escapeXml(taak[0][:self._truncate_chars]) taak = lxmlNode.xpath("//ond:summary_en/text()", namespaces=namespaceMap) if taak: abstract_en = '\n<abstract xml:lang="en">%s</abstract>' % escapeXml(taak[0][:self._truncate_chars]) # Penvoerder: taak = lxmlNode.xpath("//ond:penvoerder/ond:naam[@xml:lang='nl']/text()", namespaces=namespaceMap) if taak: penvoerder = '\n<penvoerder xml:lang="nl">%s</penvoerder>' % escapeXml(taak[0]) taak = lxmlNode.xpath("//ond:penvoerder/ond:naam[@xml:lang='en']/text()", namespaces=namespaceMap) if taak: penvoerder_en = '\n<penvoerder xml:lang="en">%s</penvoerder>' % escapeXml(taak[0]) # Status onderzoek (C/D): taak = lxmlNode.xpath("//ond:status/text()", namespaces=namespaceMap) if taak: status = '\n<status>%s</status>' % escapeXml(taak[0]) knaw_long = '''<knaw_long xmlns="http://www.knaw.nl/narcis/1.0/long/"><metadata>%s%s<genre>research</genre>%s%s%s%s%s</metadata></knaw_long>''' % ( title, title_en, penvoerder, penvoerder_en, abstract, abstract_en, status) # PERSOON: elif lxmlNode.xpath("boolean(count(//prs:persoon))", namespaces=namespaceMap): # TODO: Moeten de expertise keywords wel in de abstract verschijnen? fullName = lxmlNode.xpath("//prs:fullName/text()", namespaces=namespaceMap) if not fullName: fullName.append('n.a.') else: fullName[0] = escapeXml(fullName[0]) title = ('\n<titleInfo><title>%s</title></titleInfo>' % fullName[0]) title_en = ('\n<titleInfo xml:lang="en"><title>%s</title></titleInfo>' % fullName[0]) taak = lxmlNode.xpath("//prs:expertise_nl/text()", namespaces=namespaceMap) if taak: abstract = '\n<abstract>%s</abstract>' % escapeXml(taak[0][:self._truncate_chars]) taak = lxmlNode.xpath("//prs:expertise_en/text()", namespaces=namespaceMap) if taak: abstract_en = '\n<abstract xml:lang="en">%s</abstract>' % escapeXml(taak[0][:self._truncate_chars]) # Copy ALL nameIdentifiers + OLD <dai> tag ######## str_name = '\n<name><type>personal</type><name>' + fullName[0] + '</name>' # Kijk of er een 'oude' dai-only tag aanwezig is: dai = self._findAndBind(lxmlNode, '\n<dai>info:eu-repo/dai/nl/%s</dai>', '//prs:persoon/prs:dai/text()') if dai: str_name += dai nids = lxmlNode.xpath("//prs:persoon/prs:nameIdentifier", namespaces=namespaceMap) for nid in nids: # serialize complete tag and remove default namespace... nid_type = nid.xpath('self::prs:nameIdentifier/@type', namespaces=namespaceMap) nid_val = nid.xpath('self::prs:nameIdentifier/text()', namespaces=namespaceMap) if len(nid_type) > 0 and len(nid_val) > 0: str_name += '''<nameIdentifier type="%s">%s</nameIdentifier>''' % (nid_type[0], nid_val[0]) str_name += '</name>' # Einde nameIdentifier ######## knaw_long = '''<knaw_long xmlns="http://www.knaw.nl/narcis/1.0/long/"><metadata>%s%s%s<genre>person</genre>%s%s</metadata></knaw_long>''' % ( title, title_en, str_name, abstract, abstract_en) # We'll return a Lxml node here: parser = etree.XMLParser(remove_blank_text=True) if knaw_long is not None: try: return parse(StringIO(knaw_long), parser) except: print 'Error while parsing', knaw_long raise return knaw_long
def delete(self, identifier): path = self._path("update") yield self._send(path=path, body="<delete><id>%s</id></delete>" % escapeXml(identifier))
class SruRecordUpdate(Observable): def __init__(self, name=None, stderr=stderr, sendRecordData=True, logErrors=True, enableCollectLog=False): Observable.__init__(self, name=name) self._stderr = stderr self._logErrors = logErrors self._sendRecordData = sendRecordData self._collectLogForScope = lambda **kwargs: None if enableCollectLog: self._collectLogForScope = collectLogForScope def handleRequest(self, Body="", **kwargs): yield okXml if not Body: yield self._respond( diagnosticUri='info:srw/diagnostic/12/9', details='Update request lacks a record in its body.', message='Missing mandatory element: record rejected') return localLogCollector = dict() try: try: lxmlNode = parse(StringIO(Body)) except XMLSyntaxError, e: self._log(Body, localLogCollector=localLogCollector) raise updateRequest = xpathFirst(lxmlNode, '/*[local-name()="updateRequest"]') recordId = xpathFirst(updateRequest, 'ucp:recordIdentifier/text()') if recordId is None or recordId.strip() == '': raise ValueError("recordIdentifier is mandatory.") recordId = str(recordId) action = xpathFirst(updateRequest, 'ucp:action/text()') action = action.partition("info:srw/action/1/")[-1] if action in ['create', 'replace']: record = xpathFirst(updateRequest, 'srw:record') lxmlNode = record if self._sendRecordData: lxmlNode = xpathFirst(record, 'srw:recordData/child::*') recordSchema = xpathFirst(record, 'srw:recordSchema/text()') localLogCollector['add'] = recordId yield self.all.add( identifier=recordId, partname=recordSchema, lxmlNode=ElementTree(lxmlElementUntail(lxmlNode)), ) elif action == 'delete': localLogCollector['delete'] = recordId yield self.all.delete(identifier=recordId) else: raise ValueError( "action value should refer to either 'create', 'replace' or 'delete'." ) yield self._respond() except ValidateException, e: localLogCollector['invalid'] = recordId self._log(Body, e, localLogCollector=localLogCollector) yield self._respond(diagnosticUri='info:srw/diagnostic/12/12', details=escapeXml(str(e)), message='Invalid data: record rejected')
def _getTopItem(self, lxmlNode): ## Wrappers: pid, modified, mimetype, pidlocation = '', '', "application/xml", '' #1: Get persistentIdentifier: pidlist = lxmlNode.xpath( '//didl:DIDL/didl:Item/didl:Descriptor/didl:Statement/dii:Identifier/text()', namespaces=self._nsMap) if len(pidlist) > 0: pid = pidlist[0].strip() if not comm.isURNNBN(pid): raise ValidateException( formatExceptionLine(EXCEPTION0 + pid, prefix=STR_DIDL)) else: raise ValidateException( formatExceptionLine(EXCEPTION1, prefix=STR_DIDL)) #2: Get toplevel modificationDate: comm.isISO8601() tl_modified = lxmlNode.xpath( '//didl:DIDL/didl:Item/didl:Descriptor/didl:Statement/dcterms:modified/text()', namespaces=self._nsMap) ## Check op geldig/aanwezigheid tlModified, anders exception: if len(tl_modified) > 0 and not comm.isISO8601(tl_modified[0]): raise ValidateException( formatExceptionLine(EXCEPTION2 + tl_modified[0], prefix=STR_DIDL)) elif len(tl_modified) == 0: raise ValidateException( formatExceptionLine(EXCEPTION3, prefix=STR_DIDL)) ## Get all modified dates: all_modified = lxmlNode.xpath( '//didl:Item/didl:Descriptor/didl:Statement/dcterms:modified/text()', namespaces=self._nsMap) ## Get most recent date from all items, to add to toplevelItem: if len(all_modified) > 0: datedict = {} for date in all_modified: if comm.isISO8601(date.strip()): #datedict[parseDate(date.strip())] = date.strip() pd = parseDate(date.strip()) datedict["%s %s" % (str(pd.date()), str(pd.time()))] = date.strip() ## Get first sorted key: for key in reversed(sorted(datedict.iterkeys())): modified = datedict[key] break if not tl_modified[0].strip() == modified: self.do.logMsg(self._uploadid, LOGGER1, prefix=STR_DIDL) #3: Get PidResourceMimetype mimetypelist = lxmlNode.xpath( '//didl:DIDL/didl:Item/didl:Component/didl:Resource/@mimeType', namespaces=self._nsMap) if len(mimetypelist) > 0: mimetype = mimetypelist[0].strip() if not comm.isMimeType(mimetype): self.do.logMsg(self._uploadid, LOGGER2 + mimetype, prefix=STR_DIDL) #4: Get PidResourceLocation: pidlocation = self._findAndBindFirst( lxmlNode, '%s', '//didl:DIDL/didl:Item/didl:Component/didl:Resource/@ref', '//didl:DIDL/didl:Item/didl:Component/didl:Resource/text()' '//didl:Item/didl:Item[didl:Descriptor/didl:Statement/rdf:type/@rdf:resource="info:eu-repo/semantics/humanStartPage"]/didl:Component/didl:Resource/@ref', #DIDL 3.0 '//didl:Item/didl:Item[didl:Descriptor/didl:Statement/rdf:type/@resource="info:eu-repo/semantics/humanStartPage"]/didl:Component/didl:Resource/@ref', #DIDL 3.0, without @rdf:resource '//didl:Item/didl:Item[didl:Descriptor/didl:Statement/dip:ObjectType/text()="info:eu-repo/semantics/humanStartPage"]/didl:Component/didl:Resource/@ref', #fallback DIDL 2.3.1 '//didl:Item/didl:Item[didl:Descriptor/didl:Statement/rdf:type/@rdf:resource="info:eu-repo/semantics/objectFile"]/didl:Component/didl:Resource/@ref', #fallback DIDL 3.0 '//didl:Item/didl:Item[didl:Descriptor/didl:Statement/rdf:type/@resource="info:eu-repo/semantics/objectFile"]/didl:Component/didl:Resource/@ref', #fallback DIDL 3.0, without @rdf:resource '//didl:Item/didl:Item[didl:Descriptor/didl:Statement/dip:ObjectType/text()="info:eu-repo/semantics/objectFile"]/didl:Component/didl:Resource/@ref' #fallback DIDL 2.3.1 ).strip() if pidlocation == '': raise ValidateException( formatExceptionLine(EXCEPTION4, prefix=STR_DIDL)) if not comm.isURL(pidlocation): raise ValidateException( formatExceptionLine(EXCEPTION5 + pidlocation, prefix=STR_DIDL)) return """<didl:Item> <didl:Descriptor><didl:Statement mimeType="application/xml"><dii:Identifier>%s</dii:Identifier></didl:Statement></didl:Descriptor> <didl:Descriptor><didl:Statement mimeType="application/xml"><dcterms:modified>%s</dcterms:modified></didl:Statement></didl:Descriptor> <didl:Component><didl:Resource mimeType="%s" ref="%s"/></didl:Component>""" % ( escapeXml(pid), modified, escapeXml(mimetype), comm.urlQuote(pidlocation))
def _findAndBind(self, node, template, *xpaths): items = [] for p in xpaths: items += node.xpath(p, namespaces=namespaceMap) return '\n'.join(template % escapeXml(item) for item in items)
def fieldStatement(key, value): return '<field name="%s">%s</field>' % (escapeXml(key), escapeXml(value))
def oaiRecord(record, metadataPrefix, fetchedRecords=None): yield '<mock:record xmlns:mock="uri:mock">%s/%s</mock:record>' % (escapeXml(record.identifier), escapeXml(metadataPrefix))
def handleRequest(self, Body="", **kwargs): if type(Body) is str: Body = bytes(Body, encoding="utf-8") yield okXml if not Body: yield self._respond( diagnosticUri='info:srw/diagnostic/12/9', details='Update request lacks a record in its body.', message='Missing mandatory element: record rejected') return localLogCollector = dict() try: try: lxmlNode = parse(BytesIO(Body)) except XMLSyntaxError as e: self._log(Body, localLogCollector=localLogCollector) raise updateRequest = xpathFirst(lxmlNode, '/*[local-name()="updateRequest"]') recordId = xpathFirst(updateRequest, 'ucp:recordIdentifier/text()') if recordId is None or recordId.strip() == '': raise ValueError("recordIdentifier is mandatory.") recordId = str(recordId) action = xpathFirst(updateRequest, 'ucp:action/text()') action = action.partition("info:srw/action/1/")[-1] if action in ['create', 'replace']: record = xpathFirst(updateRequest, 'srw:record') lxmlNode = record if self._sendRecordData: lxmlNode = xpathFirst(record, 'srw:recordData/child::*') recordSchema = xpathFirst(record, 'srw:recordSchema/text()') localLogCollector['add'] = recordId yield self.all.add( identifier=recordId, partname=recordSchema, lxmlNode=ElementTree(lxmlElementUntail(lxmlNode)), ) elif action == 'delete': localLogCollector['delete'] = recordId if self._supportDeleteRecord: yield self.all.deleteRecord(identifier=recordId, record=xpathFirst( updateRequest, 'srw:record')) else: yield self.all.delete(identifier=recordId) else: raise ValueError( "action value should refer to either 'create', 'replace' or 'delete'." ) yield self._respond() except ValidateException as e: localLogCollector['invalid'] = recordId self._log(Body, e, localLogCollector=localLogCollector) yield self._respond(diagnosticUri='info:srw/diagnostic/12/12', details=escapeXml(str(e)), message='Invalid data: record rejected') except Exception as e: self._log(Body, e, localLogCollector=localLogCollector) yield self._respond(diagnosticUri='info:srw/diagnostic/12/1', details=escapeXml(format_exc()), message='Invalid component: record rejected') finally: self._collectLogForScope(sruRecordUpdate=localLogCollector)
def builder(sp, authnsign=False, wsign=False, valid_until=None, cache_duration=None, contacts=None, organization=None): """ Builds the metadata of the SP :param sp: The SP data :type sp: string :param authnsign: authnRequestsSigned attribute :type authnsign: string :param wsign: wantAssertionsSigned attribute :type wsign: string :param valid_until: Metadata's expiry date :type valid_until: string|DateTime|Timestamp :param cache_duration: Duration of the cache in seconds :type cache_duration: int|string :param contacts: Contacts info :type contacts: dict :param organization: Organization info :type organization: dict """ if valid_until is None: valid_until = int(time()) + OneLogin_Saml2_Metadata.TIME_VALID if not isinstance(valid_until, basestring): if isinstance(valid_until, datetimeClass): valid_until_time = valid_until.timetuple() else: valid_until_time = gmtime(valid_until) valid_until_str = strftime(r'%Y-%m-%dT%H:%M:%SZ', valid_until_time) else: valid_until_str = valid_until if cache_duration is None: cache_duration = OneLogin_Saml2_Metadata.TIME_CACHED if not isinstance(cache_duration, basestring): cache_duration_str = 'PT%sS' % cache_duration # 'P'eriod of 'T'ime x 'S'econds else: cache_duration_str = cache_duration if contacts is None: contacts = {} if organization is None: organization = {} str_attribute_consuming_service = '' if 'attributeConsumingService' in sp and len( sp['attributeConsumingService']): attr_cs_desc_str = '' if "serviceDescription" in sp['attributeConsumingService']: attr_cs_desc_str = """ <md:ServiceDescription xml:lang="en">%s</md:ServiceDescription> """ % sp['attributeConsumingService']['serviceDescription'] requested_attribute_data = [] for req_attribs in sp['attributeConsumingService'][ 'requestedAttributes']: req_attr_nameformat_str = req_attr_friendlyname_str = req_attr_isrequired_str = '' req_attr_aux_str = ' />' if 'nameFormat' in req_attribs.keys( ) and req_attribs['nameFormat']: req_attr_nameformat_str = " NameFormat=\"%s\"" % req_attribs[ 'nameFormat'] if 'friendlyName' in req_attribs.keys( ) and req_attribs['friendlyName']: req_attr_nameformat_str = " FriendlyName=\"%s\"" % req_attribs[ 'friendlyName'] if 'isRequired' in req_attribs.keys( ) and req_attribs['isRequired']: req_attr_isrequired_str = " isRequired=\"%s\"" % 'true' if req_attribs[ 'isRequired'] else 'false' if 'attributeValue' in req_attribs.keys( ) and req_attribs['attributeValue']: if isinstance(req_attribs['attributeValue'], basestring): req_attribs['attributeValue'] = [ req_attribs['attributeValue'] ] req_attr_aux_str = ">" for attrValue in req_attribs['attributeValue']: req_attr_aux_str += """ <saml:AttributeValue xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">%(attributeValue)s</saml:AttributeValue>""" % \ { 'attributeValue': attrValue } req_attr_aux_str += """ </md:RequestedAttribute>""" requested_attribute = """ <md:RequestedAttribute Name="%(req_attr_name)s"%(req_attr_nameformat_str)s%(req_attr_friendlyname_str)s%(req_attr_isrequired_str)s%(req_attr_aux_str)s""" % \ { 'req_attr_name': req_attribs['name'], 'req_attr_nameformat_str': req_attr_nameformat_str, 'req_attr_friendlyname_str': req_attr_friendlyname_str, 'req_attr_isrequired_str': req_attr_isrequired_str, 'req_attr_aux_str': req_attr_aux_str } requested_attribute_data.append(requested_attribute) str_attribute_consuming_service = """ <md:AttributeConsumingService index="1"> <md:ServiceName xml:lang="en">%(service_name)s</md:ServiceName> %(attr_cs_desc)s%(requested_attribute_str)s </md:AttributeConsumingService> """ % \ { 'service_name': sp['attributeConsumingService']['serviceName'], 'attr_cs_desc': attr_cs_desc_str, 'requested_attribute_str': '\n'.join(requested_attribute_data) } sls = '' if 'singleLogoutService' in sp and 'url' in sp['singleLogoutService']: sls = """ <md:SingleLogoutService Binding="%(binding)s" Location="%(location)s" />\n""" % \ { 'binding': sp['singleLogoutService']['binding'], 'location': sp['singleLogoutService']['url'], } str_authnsign = 'true' if authnsign else 'false' str_wsign = 'true' if wsign else 'false' str_organization = '' if len(organization) > 0: organization_names = [] organization_displaynames = [] organization_urls = [] for (lang, info) in organization.items(): organization_names.append( """ <md:OrganizationName xml:lang="%s">%s</md:OrganizationName>""" % (lang, info['name'])) organization_displaynames.append( """ <md:OrganizationDisplayName xml:lang="%s">%s</md:OrganizationDisplayName>""" % (lang, info['displayname'])) organization_urls.append( """ <md:OrganizationURL xml:lang="%s">%s</md:OrganizationURL>""" % (lang, info['url'])) org_data = '\n'.join(organization_names) + '\n' + '\n'.join( organization_displaynames) + '\n' + '\n'.join( organization_urls) str_organization = """ <md:Organization> %(org)s </md:Organization>\n""" % { 'org': org_data } str_contacts = '' if len(contacts) > 0: contacts_info = [] for (ctype, info) in contacts.items(): surName = info.get('surName') surNameXml = '' if surName: surNameXml = '\n <md:SurName>%s</md:SurName>' % escapeXml( surName) contact = """ <md:ContactPerson contactType="%(type)s"> <md:GivenName>%(name)s</md:GivenName>%(surNameXml)s <md:EmailAddress>%(email)s</md:EmailAddress> </md:ContactPerson>""" % \ { 'type': ctype, 'name': info['givenName'], 'surNameXml': surNameXml, 'email': info['emailAddress'], } contacts_info.append(contact) str_contacts = '\n'.join(contacts_info) + '\n' metadata = u"""<?xml version="1.0"?> <md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" %(valid)s %(cache)s entityID="%(entity_id)s"> <md:SPSSODescriptor AuthnRequestsSigned="%(authnsign)s" WantAssertionsSigned="%(wsign)s" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> %(sls)s <md:NameIDFormat>%(name_id_format)s</md:NameIDFormat> <md:AssertionConsumerService Binding="%(binding)s" Location="%(location)s" index="1" /> %(attribute_consuming_service)s </md:SPSSODescriptor> %(organization)s%(contacts)s</md:EntityDescriptor>""" % \ { 'valid': ('validUntil="%s"' % valid_until_str) if valid_until_str else '', 'cache': ('cacheDuration="%s"' % cache_duration_str) if cache_duration_str else '', 'entity_id': sp['entityId'], 'authnsign': str_authnsign, 'wsign': str_wsign, 'name_id_format': sp['NameIDFormat'], 'binding': sp['assertionConsumerService']['binding'], 'location': sp['assertionConsumerService']['url'], 'sls': sls, 'organization': str_organization, 'contacts': str_contacts, 'attribute_consuming_service': str_attribute_consuming_service } return metadata