class Shibkit::MetaMeta

Simple library to parse Shibboleth metadata files into Ruby objects

Public Class Methods

add_source(data) click to toggle source

Convenience method to add a source from a hash or object

# File lib/shibkit/meta_meta.rb, line 97
def self.add_source(data)
  
  @additional_sources ||= Hash.new
  
  case data
  when Hash
    source = Source.from_hash(data)
  when ::Shibkit::MetaMeta::Source
    source = data
  else 
    raise "Expected either hash or Source object!"
  end

  log.info "Added a source for #{source.uri}"
  
  @additional_sources[source.uri] = source
  
end
config(&block) click to toggle source
# File lib/shibkit/meta_meta.rb, line 41
def self.config(&block)

  if block
    return ::Shibkit::MetaMeta::Config.instance.configure(&block)
  else
    return ::Shibkit::MetaMeta::Config.instance
  end

end
delete_all_cached_files!() click to toggle source

Delete all cache files

# File lib/shibkit/meta_meta.rb, line 78
def self.delete_all_cached_files!
  
  dir = config.cache_root
  
  if config.can_delete?
    
    log.info "Deleting all files at #{dir}..."
    FileUtils.rm_rf   dir
    FileUtils.mkdir_p dir
    
  else
    
    log.warn "Cannot delete files at #{dir} - check config settings."
    
  end
  
end
entities() click to toggle source

All primary entities from all federations

# File lib/shibkit/meta_meta.rb, line 346
def self.entities
  
  return [] if @entities.nil? and ! config.autoload?
  
  ## Populate memoised array of entities if it's empty
  unless @entities and @entities.size > 0
    
    ## Array for memoising primary entities
    @entities ||= Array.new
    
    ## For keeping track of already processed entities & marking them as primary
    processed = Hash.new 
    
    self.federations.each do |f|
      
      f.entities.each do |e|
        
        ## If we've already found the primary version of the entity
        if processed[e.uri]
          
          ## Add this federation's URI to the primary
          primary = processed[e.uri]              
          primary.other_federation_uris << f.uri
          
          ## Add tags from the non-primary to the primary
          primary.tags << e.tags if config.merge_primary_tags?
          
          next
       
        end
        
        ## Mark this entity as the primary and remember it as already processed.
        e.primary = true
        processed[e.uri] = e
        
        ## Collect entity
        @entities << e 

      end
      
    end

    ## BODGE: Needs a better fix. Issue 14
    #@entities = @entities.compact
    
  end
  
  return @entities
  
end
federations() click to toggle source

Return list of Federations objects (filtered if select_federations is set)

# File lib/shibkit/meta_meta.rb, line 329
def self.federations
  
  return [] if @federations.nil? and ! config.autoload?
  
  self.stockup
   
  if self.filtered_sources?
    
    return @federations.select { |f| self.selected_federation_uris.include? f.uri }
  
  end

  return @federations
  
end
filtered_sources?() click to toggle source

Has a limited subset of federations/sources been selected?

# File lib/shibkit/meta_meta.rb, line 186
def self.filtered_sources?
  
  return self.selected_federation_uris.empty? ? false : true 
  
end
flush() click to toggle source

Clear all loaded entity & federation data

# File lib/shibkit/meta_meta.rb, line 66
def self.flush
  
  log.info "Flushing all loaded objects"
  
  @orgs        = Array.new
  @entities    = Array.new
  @federations = Array.new
  @by_uri      = Hash.new
  
end
from_uri(uri) click to toggle source
# File lib/shibkit/meta_meta.rb, line 441
def self.from_uri(uri)
  
  unless @by_uri and @by_uri.size > 0
    
    @by_uri ||= Hash.new
    
    self.federations.each { |f| @by_uri[f.uri] = f unless @by_uri[f.uri] }
    self.entities.each    { |e| @by_uri[e.uri] = e unless @by_uri[e.uri] }
      
  end
  
  return @by_uri[uri]
  
end
idps() click to toggle source
# File lib/shibkit/meta_meta.rb, line 428
def self.idps
  
  return entities.select { |e| e.idp? }
  
end
load_cache_file(file_or_url, format=:yaml) click to toggle source

Loads federation metadata contents

# File lib/shibkit/meta_meta.rb, line 193
def self.load_cache_file(file_or_url, format=:yaml)
    
    self.reset
    
    log.info "Loading object cache file from #{file_or_url} as #{format} data..."
    
    @federations = case format
    when :yaml
      YAML.load(File.open(file_or_url))
    when :marshal
      Marshal.load(File.open(file_or_url))
    else
      raise "Unexpected cache file format requested! Please use :yaml or :marshal"
    end
    
    self.entities      
    
    log.info "Processing complete."
    
    return true
    
end
load_sources(filename=self.config.sources_file) click to toggle source

Load sources from a YAML file

# File lib/shibkit/meta_meta.rb, line 124
def self.load_sources(filename=self.config.sources_file)
  
  log.info "Loading sources from disk..."
  
  @loaded_sources = Hash.new
  
  Source.load(filename).each do |source|
    
    ## More than one definition for a source is a problem
    raise "Duplicate source for #{source.uri}!" if @loaded_sources[source.uri]
    
    @loaded_sources[source.uri] = source
    
  end 
  
end
loaded_sources?() click to toggle source

Have we loaded any sources?

# File lib/shibkit/meta_meta.rb, line 117
def self.loaded_sources?
  
  return @loaded_sources ? true : false
  
end
orgs() click to toggle source
# File lib/shibkit/meta_meta.rb, line 397
def self.orgs
  
  unless @orgs and @orgs.size > 0
    
    @orgs ||= Array.new
    
    processed = Hash.new
    
    self.entities.each do |e|
       
       org = e.organisation
       
       next unless org
       next if processed[org.druid]
       
       @orgs << org
       
      
       processed[org.druid] = true
       
    end
    
    @orgs.sort! {|a,b| a.druid <=> b.druid }
    
  end
  
  return @orgs
  
end
process_sources() click to toggle source

Parses sources and returns an array of all federation object

# File lib/shibkit/meta_meta.rb, line 248
def self.process_sources
  
  if config.smartcache_active?
    return if self.smartcache_load
  end
  
  log.info "Processing content of sources into objects..."
  
  raise "MetaMeta sources are not an Array! (Should not be a #{self.sources.class})" unless
    self.sources.kind_of? Array
  
  self.flush
  
  self.sources.each do |source|
    
    if self.filtered_sources?
      
      next unless self.selected_federation_uris.include? source.uri
    
    end
    
    start_time = Time.new
      
    federation = source.to_federation
    
    ## Store all federations in array
    @federations << federation
    
    log.info "Loaded #{federation.entities.count} entities from #{federation} metadata file in #{Time.new - start_time} seconds."
    
    
  end
  
  ## Bodge to make sure primary ents are set, multifederation calculated, etc
  #self.entities # Issue 14
  
  log.info "Processing complete. #{@federations.count} sets of metadata have been loaded."
  
  self.smartcache_save if config.smartcache_active?
  
  return @federations
     
end
purge_xml!() click to toggle source
# File lib/shibkit/meta_meta.rb, line 462
def self.purge_xml! 
  
  @federations.each { |f| f.purge_xml(true) }
  
end
refresh(force=false) click to toggle source

Downloads and reprocesses metadata files

# File lib/shibkit/meta_meta.rb, line 293
def self.refresh(force=false)
  
  log.info "Refreshing all selected federations"
  
  ## Reload source lists overwriting previous set
  self.load_sources
  
  ## Reprocess sources to create fresh set of federation and entity objects
  self.process_sources 
  
  return true
  
end
reset() click to toggle source

Flush out all available sources, metadata caches, etc.

# File lib/shibkit/meta_meta.rb, line 52
def self.reset
  
  log.info "Resetting all sources, metadata caches, etc."
  
  ## Clear the source data
  @additional_sources   = Hash.new
  @loaded_sources       = Hash.new
   
  ## Clear federation entity data
  self.flush
  
end
save_cache_file(file_path, format=:yaml) click to toggle source

 Save entity data into a YAML file.

# File lib/shibkit/meta_meta.rb, line 217
def self.save_cache_file(file_path, format=:yaml)
  
  log.info "Saving object cache file to #{file_path} as #{format} data..."
  
  ## Will *not* overwrite the example/default file in gem! TODO: this code is awful.
  gem_data_path = "#{::File.dirname(__FILE__)}/data"
  if file_path.include? gem_data_path 
    raise "Attempt to overwrite gem's default metadata cache! Please specify your own file to save cache in"
  end
  
  self.textify_xml!
  
  ## Write the YAML to disk
  File.open(file_path, 'w') do |out|
    
    case format
    when :yaml
      YAML.dump(@federations, out)
    when :marshal        
      Marshal.dump(@federations, out)
    else
      raise "Unexpected cache file format requested! Please use :yaml or :marshal"
    end
    
  end
    
  return true
    
end
save_sources(filename) click to toggle source

Save all known sources to sources list file

# File lib/shibkit/meta_meta.rb, line 142
def self.save_sources(filename)
  
  log.info "Saving sources to #{filename}..."
  
  src_dump = Hash.new
  self.sources.each { |s| src_dump[s.uri] = s.to_hash }
  
  File.open(filename, 'w') { |out| YAML.dump(src_dump, out) }
    
end
selected_federation_uris() click to toggle source

List of federation/collection uris

# File lib/shibkit/meta_meta.rb, line 179
def self.selected_federation_uris
  
  return self.config.selected_federation_uris
  
end
sources() click to toggle source

List all sources as an array

# File lib/shibkit/meta_meta.rb, line 154
def self.sources
  
  if self.config.autoload? and loaded_sources.size == 0 and additional_sources.size == 0
    
    self.load_sources
  
  end
  
  all_sources_indexed = loaded_sources.merge(additional_sources)

  sources = all_sources_indexed.values

  sources = sources.sort {|a,b| a.created_at <=> b.created_at }

  if self.filtered_sources?
    
    sources = sources.select { |s| self.selected_federation_uris.include? s.uri }
  
  end

  return sources 
  
end
sps() click to toggle source
# File lib/shibkit/meta_meta.rb, line 435
def self.sps
  
  return entities.select { |e| e.sp? }
  
end
stocked?() click to toggle source

Have objects been loaded from metadata?

# File lib/shibkit/meta_meta.rb, line 319
def self.stocked?
  
  return false unless @federations
  return false if @federations.empty? 
  
  return true

end
stockup(force=false) click to toggle source
# File lib/shibkit/meta_meta.rb, line 307
def self.stockup(force=false)
  
  if self.config.autoload?
  
    self.process_sources unless @federations
    self.process_sources if @federations.empty?
    
  end
  
end
textify_xml!() click to toggle source
# File lib/shibkit/meta_meta.rb, line 456
def self.textify_xml! 
  
  @federations.each { |f| f.textify_xml(true) }
  
end

Private Class Methods

additional_sources() click to toggle source

Access to all additional sources

# File lib/shibkit/meta_meta.rb, line 480
def self.additional_sources
  
  @additional_sources ||= Hash.new
  return @additional_sources

end
loaded_sources() click to toggle source

Access to all additional sources

# File lib/shibkit/meta_meta.rb, line 488
def self.loaded_sources
  
  @loaded_sources ||= Hash.new
  return @loaded_sources

end
log() click to toggle source

Logging

# File lib/shibkit/meta_meta.rb, line 471
def self.log

  return ::Shibkit::MetaMeta.config.logger
  
end
smartcache_load() click to toggle source
# File lib/shibkit/meta_meta.rb, line 496
def self.smartcache_load
  
  log.info "Checking smartcache status..."
  
  object_file   = config.smartcache_object_file
  scmd_file     = config.smartcache_info_file
  expiry_period = config.smartcache_expiry
  
  ## Do we even have a file?
  return false unless File.exists? object_file
  return false unless File.exists? scmd_file
  
  start_time = Time.new
  
  ## Make sure the dump metadata is suitable
  info = YAML.load(File.open(scmd_file))
  
  ## Check
  cache_age = (Time.new.to_i - info[:created_at].to_i)
  return false unless cache_age < expiry_period.to_i
  
  return false unless info[:version] == config.version
  
  return false unless info[:platform] == config.platform
  
  return false unless info[:format]  == :marshal
  
  return false unless info[:object_file] == object_file
  
  return false unless info[:purge_xml]  == config.purge_xml?
  return false unless info[:source_xml] == config.remember_source_xml?
  return false unless info[:groups]     == Digest::SHA1.hexdigest(config.selected_groups.join)
  
  log.info "Smartcache is valid: loading objects..."
  
  ## If file does not exist (or is stale) and we have objects, save
  self.load_cache_file(object_file, :marshal)
  
  log.info "Loaded #{@federations.count} federations and #{@entities.count} entities from smartcache in #{Time.new - start_time} seconds."
  
  return true
  
end
smartcache_save() click to toggle source
# File lib/shibkit/meta_meta.rb, line 541
def self.smartcache_save
  
  object_file = config.smartcache_object_file
  scmd_file   = config.smartcache_info_file
  
  log.info "Saving smartcache with #{@federations.count} federations and #{@entities.count} entities..."
  
  ## Save file in fast marsh
  mkdir_p config.cache_root unless File.exists? config.cache_root
  self.save_cache_file(object_file, :marshal) 

  info = {
    :created_at  => Time.new,
    :version     => config.version,
    :platform    => config.platform,
    :object_file => object_file,
    :format      => :marshal,
    :purge_xml   => config.purge_xml?,
    :source_xml  => config.remember_source_xml?,
    :groups      => Digest::SHA1.hexdigest(config.selected_groups.join)
  }

  File.open(scmd_file, 'w') { |out| YAML.dump(info, out) }
  
  log.info "Saved smartcache."
  
  return true
  
end
stats() click to toggle source

Stats

# File lib/shibkit/meta_meta.rb, line 572
def self.stats
  
  stats = Hash.new
  
  stats[:federations] = Hash.new
  
  self.federations.each do |f|
    
    stats[:federations][f.uri] = Hash.new
    stats[:federations][f.uri][:sp_count]  = f.sps.count
    stats[:federations][f.uri][:idp_count] = f.idps.count
    stats[:federations][f.uri][:entities]  = f.entities.count
    stats[:federations][f.uri][:uri_count] = f.entities.collect {|e| e.uri}.uniq.count
    
  end
  
  stats[:federation_count]       = self.federations.count
  stats[:entities_count]         = self.federations.inject(0) {|m,f| m+f.entities.count}
  stats[:primary_entities_count] = self.entities.count
  stats[:organisation_count]     = self.orgs.count
  
  return stats
  
end