<commit_before>package aaf.fr.metadata

import java.text.SimpleDateFormat
import org.springframework.transaction.annotation.*
import org.springframework.beans.factory.InitializingBean
import aaf.fr.foundation.*

/**
 * Provides methods to generate Metadata for the entire federation or specific components.
 *
 * @author <NAME>
 */
class MetadataGenerationService implements InitializingBean {
  
  def grailsApplication

  def registrationAuthority
  def registrationPolicy
  def registrationPolicyLang

  def hasRegistrationAuthority
  def hasRegistrationPolicy

  def void afterPropertiesSet() {
      // initialize registration info from grailsApplication.config
      registrationAuthority = grailsApplication.config.aaf.fr.metadata.registrationAuthority
      registrationPolicy = grailsApplication.config.aaf.fr.metadata.registrationPolicy
      registrationPolicyLang = grailsApplication.config.aaf.fr.metadata.registrationPolicyLang

      hasRegistrationAuthority = registrationAuthority && registrationAuthority // trick to convert to boolean
      hasRegistrationPolicy = grailsApplication.config.aaf.fr.metadata.registrationPolicy && grailsApplication.config.aaf.fr.metadata.registrationPolicyLang
  }

  def populateSchema() {
    def namespaces = ["xmlns":"urn:oasis:names:tc:SAML:2.0:metadata", "xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance", 'xmlns:saml':'urn:oasis:names:tc:SAML:2.0:assertion', 'xmlns:shibmd':'urn:mace:shibboleth:metadata:1.0',
      'xmlns:ds':'http://www.w3.org/2000/09/xmldsig#', "xsi:schemaLocation":"urn:oasis:names:tc:SAML:2.0:metadata saml-schema-metadata-2.0.xsd urn:mace:shibboleth:metadata:1.0 shibboleth-metadata-1.0.xsd http://www.w3.org/2000/09/xmldsig# xmldsig-core-schema.xsd"]
    if (hasRegistrationAuthority || hasRegistrationPolicy) namespaces.put('xmlns:mdrpi','urn:oasis:names:tc:SAML:metadata:rpi')
    return namespaces
  }
  
  def localizedName(builder, type, lang, content) {
    builder."$type"('xml:lang':lang, content)
  }

  def localizedUri(builder, type, lang, content) {
    builder."$type"('xml:lang':lang, content)
  }
  
  def organization(builder, organization) {
    builder.Organization() {
      localizedName(builder, "OrganizationName", organization.lang, organization.name)
      localizedName(builder, "OrganizationDisplayName", organization.lang, organization.displayName)
      localizedUri(builder, "OrganizationURL", organization.lang, organization.url)
    }
  }
  
  def contactPerson(builder, contactPerson) {
    // Ignores non SAML standard contact types and contacts marked as 'other'
    def contactType
    if(["technical", "support", "administrative", "billing"].contains(contactPerson.type.name)) {
      
      builder.ContactPerson(contactType:contactPerson.type.name) {
        if(contactPerson.contact.organization)
          Company(contactPerson.contact.organization.displayName)
        GivenName(contactPerson.contact.givenName)
        SurName(contactPerson.contact.surname)
        EmailAddress("mailto:${contactPerson.contact.email}")
        if(contactPerson.contact.workPhone)
          TelephoneNumber(contactPerson.contact.workPhone)
        if(contactPerson.contact.homePhone)
          TelephoneNumber(contactPerson.contact.homePhone)
        if(contactPerson.contact.mobilePhone)
          TelephoneNumber(contactPerson.contact.mobilePhone)
      }
    }
  }

  private String processCertificateData(String certData) {
    def data = certData.trim().normalize()
    if(data.startsWith('-----BEGIN CERTIFICATE-----'))
      data = data.replace('-----BEGIN CERTIFICATE-----', '')
    if(data.endsWith('-----END CERTIFICATE-----'))
      data = data.replace('-----END CERTIFICATE-----', '')

    // Ensure any hanging new lines are gone and make XML neat with
    // leading and trailing newlines.
    "\n${data.trim()}\n"  
  }
  
  def caKeyInfo(builder, keyInfo) {
    builder.'ds:KeyInfo'('xmlns:ds':'http://www.w3.org/2000/09/xmldsig#') {
      if(keyInfo.keyName)
        'ds:KeyName'(keyInfo.keyName)
      'ds:X509Data'() {
        def data = processCertificateData(keyInfo.certificate.data)
        'ds:X509Certificate'(data)
      }
    }
  }
  
  def keyInfo(builder, keyInfo) {
    builder.'ds:KeyInfo'('xmlns:ds':'http://www.w3.org/2000/09/xmldsig#') {
      if(keyInfo.keyName)
        'ds:KeyName'(keyInfo.keyName)
      'ds:X509Data'() {
        def data = processCertificateData(keyInfo.certificate.data)
        'ds:X509Certificate'(data)
      }
    }
  }
  
  def keyDescriptor(builder, keyDescriptor) {
    if(!keyDescriptor.disabled) {
      builder.KeyDescriptor(use: keyDescriptor.keyType) {
        keyInfo(builder, keyDescriptor.keyInfo)
        if(keyDescriptor.encryptionMethod) {
          EncryptionMethod(Algorithm:keyDescriptor.encryptionMethod.algorithm) {
            if(keyDescriptor.encryptionMethod.keySize)
              'xenc:KeySize'('xmlns:xenc':'http://www.w3.org/2001/04/xmlenc#', keyDescriptor.encryptionMethod.keySize)
            if(keyDescriptor.encryptionMethod.oaeParams)
            'xenc:OAEPparams'('xmlns:xenc':'http://www.w3.org/2001/04/xmlenc#', keyDescriptor.encryptionMethod.oaeParams)
          }
        }
      }
    }
  }
  
  @Transactional(readOnly = true)
  def entitiesDescriptor(builder, all, minimal, roleExtensions, entitiesDescriptor, validUntil, certificateAuthorities) {
    
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
    sdf.setTimeZone(TimeZone.getTimeZone("UTC"))

    SimpleDateFormat idf = new SimpleDateFormat("_yyyyMMdd'T'HHmmss'Z'")
    idf.setTimeZone(TimeZone.getTimeZone("UTC"))

    def currently = new Date()
    
    def params = [:]
    params.putAll(populateSchema())
    params.ID =  idf.format(currently)
    params.validUntil = sdf.format(validUntil)
    params.Name = entitiesDescriptor.name
      
    builder.EntitiesDescriptor(params) {
      if(certificateAuthorities && certificateAuthorities.size() != 0) {
        builder.Extensions() {
          builder."shibmd:KeyAuthority"("xmlns:shibmd":"urn:mace:shibboleth:metadata:1.0", VerifyDepth: 5) {
            certificateAuthorities?.sort{it.id}.each { ca ->
              caKeyInfo(builder, ca)
            }
          }
        }
      }
      entitiesDescriptor.entitiesDescriptors?.sort{it.id}?.each { eds ->
        this.entitiesDescriptor(builder, all, minimal, roleExtensions, eds)
      }
      entitiesDescriptor.entityDescriptors?.sort{it.entityID}.each { ed ->
        entityDescriptor(builder, all, minimal, roleExtensions, ed, false)
      }
    }
  }
  
  @Transactional(readOnly = true)
  def entitiesDescriptor(builder, all, minimal, roleExtensions, entitiesDescriptor) {
    builder.EntitiesDescriptor() {
      entitiesDescriptor.entitiesDescriptors?.sort{it.id}?.each { eds ->
        this.entitiesDescriptor(builder, all, minimal, roleExtensions, eds)
      }
      entitiesDescriptor.entityDescriptors?.sort{it.entityID}.each { ed ->
        entityDescriptor(builder, all, minimal, roleExtensions, ed, false)
      }
    }
  }
  
  @Transactional(readOnly = true)
  def entityDescriptor(builder, all, minimal, roleExtensions, entityDescriptor, schema) {
    if(all || entityDescriptor.functioning() ) {
      if(!all && entityDescriptor.empty()) {
        log.warn "Not rendering $entityDescriptor no children nodes detected"
      }
      else {
        def params = [:]
        params.entityID = entityDescriptor.entityID
        if(schema) params.putAll(populateSchema()) 
          
        builder.EntityDescriptor(params) {
          if (hasRegistrationAuthority) {
            builder.Extensions() {
              def rpiparams = [:]
              SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
              sdf.setTimeZone(TimeZone.getTimeZone("UTC"))<commit_msg>Metadata: RegistrationInfo: honour minimal and ext<commit_after>package aaf.fr.metadata

import java.text.SimpleDateFormat
import org.springframework.transaction.annotation.*
import org.springframework.beans.factory.InitializingBean
import aaf.fr.foundation.*

/**
 * Provides methods to generate Metadata for the entire federation or specific components.
 *
 * @author <NAME>
 */
class MetadataGenerationService implements InitializingBean {
  
  def grailsApplication

  def registrationAuthority
  def registrationPolicy
  def registrationPolicyLang

  def hasRegistrationAuthority
  def hasRegistrationPolicy

  def void afterPropertiesSet() {
      // initialize registration info from grailsApplication.config
      registrationAuthority = grailsApplication.config.aaf.fr.metadata.registrationAuthority
      registrationPolicy = grailsApplication.config.aaf.fr.metadata.registrationPolicy
      registrationPolicyLang = grailsApplication.config.aaf.fr.metadata.registrationPolicyLang

      hasRegistrationAuthority = registrationAuthority && registrationAuthority // trick to convert to boolean
      hasRegistrationPolicy = grailsApplication.config.aaf.fr.metadata.registrationPolicy && grailsApplication.config.aaf.fr.metadata.registrationPolicyLang
  }

  def populateSchema(minimal, roleExtensions) {
    def namespaces = ["xmlns":"urn:oasis:names:tc:SAML:2.0:metadata", "xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance", 'xmlns:saml':'urn:oasis:names:tc:SAML:2.0:assertion', 'xmlns:shibmd':'urn:mace:shibboleth:metadata:1.0',
      'xmlns:ds':'http://www.w3.org/2000/09/xmldsig#', "xsi:schemaLocation":"urn:oasis:names:tc:SAML:2.0:metadata saml-schema-metadata-2.0.xsd urn:mace:shibboleth:metadata:1.0 shibboleth-metadata-1.0.xsd http://www.w3.org/2000/09/xmldsig# xmldsig-core-schema.xsd"]
    if (hasRegistrationAuthority && !minimal && roleExtensions) namespaces.put('xmlns:mdrpi','urn:oasis:names:tc:SAML:metadata:rpi')
    return namespaces
  }
  
  def localizedName(builder, type, lang, content) {
    builder."$type"('xml:lang':lang, content)
  }

  def localizedUri(builder, type, lang, content) {
    builder."$type"('xml:lang':lang, content)
  }
  
  def organization(builder, organization) {
    builder.Organization() {
      localizedName(builder, "OrganizationName", organization.lang, organization.name)
      localizedName(builder, "OrganizationDisplayName", organization.lang, organization.displayName)
      localizedUri(builder, "OrganizationURL", organization.lang, organization.url)
    }
  }
  
  def contactPerson(builder, contactPerson) {
    // Ignores non SAML standard contact types and contacts marked as 'other'
    def contactType
    if(["technical", "support", "administrative", "billing"].contains(contactPerson.type.name)) {
      
      builder.ContactPerson(contactType:contactPerson.type.name) {
        if(contactPerson.contact.organization)
          Company(contactPerson.contact.organization.displayName)
        GivenName(contactPerson.contact.givenName)
        SurName(contactPerson.contact.surname)
        EmailAddress("mailto:${contactPerson.contact.email}")
        if(contactPerson.contact.workPhone)
          TelephoneNumber(contactPerson.contact.workPhone)
        if(contactPerson.contact.homePhone)
          TelephoneNumber(contactPerson.contact.homePhone)
        if(contactPerson.contact.mobilePhone)
          TelephoneNumber(contactPerson.contact.mobilePhone)
      }
    }
  }

  private String processCertificateData(String certData) {
    def data = certData.trim().normalize()
    if(data.startsWith('-----BEGIN CERTIFICATE-----'))
      data = data.replace('-----BEGIN CERTIFICATE-----', '')
    if(data.endsWith('-----END CERTIFICATE-----'))
      data = data.replace('-----END CERTIFICATE-----', '')

    // Ensure any hanging new lines are gone and make XML neat with
    // leading and trailing newlines.
    "\n${data.trim()}\n"  
  }
  
  def caKeyInfo(builder, keyInfo) {
    builder.'ds:KeyInfo'('xmlns:ds':'http://www.w3.org/2000/09/xmldsig#') {
      if(keyInfo.keyName)
        'ds:KeyName'(keyInfo.keyName)
      'ds:X509Data'() {
        def data = processCertificateData(keyInfo.certificate.data)
        'ds:X509Certificate'(data)
      }
    }
  }
  
  def keyInfo(builder, keyInfo) {
    builder.'ds:KeyInfo'('xmlns:ds':'http://www.w3.org/2000/09/xmldsig#') {
      if(keyInfo.keyName)
        'ds:KeyName'(keyInfo.keyName)
      'ds:X509Data'() {
        def data = processCertificateData(keyInfo.certificate.data)
        'ds:X509Certificate'(data)
      }
    }
  }
  
  def keyDescriptor(builder, keyDescriptor) {
    if(!keyDescriptor.disabled) {
      builder.KeyDescriptor(use: keyDescriptor.keyType) {
        keyInfo(builder, keyDescriptor.keyInfo)
        if(keyDescriptor.encryptionMethod) {
          EncryptionMethod(Algorithm:keyDescriptor.encryptionMethod.algorithm) {
            if(keyDescriptor.encryptionMethod.keySize)
              'xenc:KeySize'('xmlns:xenc':'http://www.w3.org/2001/04/xmlenc#', keyDescriptor.encryptionMethod.keySize)
            if(keyDescriptor.encryptionMethod.oaeParams)
            'xenc:OAEPparams'('xmlns:xenc':'http://www.w3.org/2001/04/xmlenc#', keyDescriptor.encryptionMethod.oaeParams)
          }
        }
      }
    }
  }
  
  @Transactional(readOnly = true)
  def entitiesDescriptor(builder, all, minimal, roleExtensions, entitiesDescriptor, validUntil, certificateAuthorities) {
    
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
    sdf.setTimeZone(TimeZone.getTimeZone("UTC"))

    SimpleDateFormat idf = new SimpleDateFormat("_yyyyMMdd'T'HHmmss'Z'")
    idf.setTimeZone(TimeZone.getTimeZone("UTC"))

    def currently = new Date()
    
    def params = [:]
    params.putAll(populateSchema(minimal, roleExtensions))
    params.ID =  idf.format(currently)
    params.validUntil = sdf.format(validUntil)
    params.Name = entitiesDescriptor.name
      
    builder.EntitiesDescriptor(params) {
      if(certificateAuthorities && certificateAuthorities.size() != 0) {
        builder.Extensions() {
          builder."shibmd:KeyAuthority"("xmlns:shibmd":"urn:mace:shibboleth:metadata:1.0", VerifyDepth: 5) {
            certificateAuthorities?.sort{it.id}.each { ca ->
              caKeyInfo(builder, ca)
            }
          }
        }
      }
      entitiesDescriptor.entitiesDescriptors?.sort{it.id}?.each { eds ->
        this.entitiesDescriptor(builder, all, minimal, roleExtensions, eds)
      }
      entitiesDescriptor.entityDescriptors?.sort{it.entityID}.each { ed ->
        entityDescriptor(builder, all, minimal, roleExtensions, ed, false)
      }
    }
  }
  
  @Transactional(readOnly = true)
  def entitiesDescriptor(builder, all, minimal, roleExtensions, entitiesDescriptor) {
    builder.EntitiesDescriptor() {
      entitiesDescriptor.entitiesDescriptors?.sort{it.id}?.each { eds ->
        this.entitiesDescriptor(builder, all, minimal, roleExtensions, eds)
      }
      entitiesDescriptor.entityDescriptors?.sort{it.entityID}.each { ed ->
        entityDescriptor(builder, all, minimal, roleExtensions, ed, false)
      }
    }
  }
  
  @Transactional(readOnly = true)
  def entityDescriptor(builder, all, minimal, roleExtensions, entityDescriptor, schema) {
    if(all || entityDescriptor.functioning() ) {
      if(!all && entityDescriptor.empty()) {
        log.warn "Not rendering $entityDescriptor no children nodes detected"
      }
      else {
        def params = [:]
        params.entityID = entityDescriptor.entityID
        if(schema) params.putAll(populateSchema(minimal, roleExtensions)) 
          
        builder.EntityDescriptor(params) {
          if (hasRegistrationAuthority && !minimal && roleExtensions) {
            builder.Extensions() {
              def rpiparams = [:]
              SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
              sdf.setTimeZone(TimeZone.getTimeZone("UTC"))
{'fraction_non_alphanumeric': 0.12457997844417676, 'fraction_numerical': 0.0097635199391365, 'mean_word_length': 3.555010106843777, 'pattern_counts': {'":': 8, '<': 5, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 14, 'xml': 40}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6893704', 'n_tokens_mistral': 4836, 'n_tokens_neox': 4667, 'n_words': 696}