shibkit-meta_meta 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +21 -0
  4. data/Gemfile.lock +52 -0
  5. data/Icon.png +0 -0
  6. data/LICENSE.txt +177 -0
  7. data/README.md +789 -0
  8. data/Rakefile +38 -0
  9. data/VERSION +1 -0
  10. data/examples/biggest_entity_id.rb +4 -0
  11. data/lib/shibkit/meta_meta.rb +600 -0
  12. data/lib/shibkit/meta_meta/attribute.rb +73 -0
  13. data/lib/shibkit/meta_meta/config.rb +463 -0
  14. data/lib/shibkit/meta_meta/contact.rb +85 -0
  15. data/lib/shibkit/meta_meta/data/default_metadata/example_federation_metadata.xml +168 -0
  16. data/lib/shibkit/meta_meta/data/default_metadata/local_metadata.xml +66 -0
  17. data/lib/shibkit/meta_meta/data/default_metadata/uncommon_federation_metadata.xml +115 -0
  18. data/lib/shibkit/meta_meta/data/default_metadata_cache.yml +166 -0
  19. data/lib/shibkit/meta_meta/data/dev_sources.yml +86 -0
  20. data/lib/shibkit/meta_meta/data/real_sources.yml +163 -0
  21. data/lib/shibkit/meta_meta/entity.rb +219 -0
  22. data/lib/shibkit/meta_meta/federation.rb +161 -0
  23. data/lib/shibkit/meta_meta/idp.rb +81 -0
  24. data/lib/shibkit/meta_meta/logo.rb +216 -0
  25. data/lib/shibkit/meta_meta/metadata_item.rb +244 -0
  26. data/lib/shibkit/meta_meta/mixin/cached_downloads.rb +127 -0
  27. data/lib/shibkit/meta_meta/mixin/xpath_chores.rb +111 -0
  28. data/lib/shibkit/meta_meta/organisation.rb +73 -0
  29. data/lib/shibkit/meta_meta/provider.rb +195 -0
  30. data/lib/shibkit/meta_meta/provisioning/base.rb +33 -0
  31. data/lib/shibkit/meta_meta/requested_attribute.rb +29 -0
  32. data/lib/shibkit/meta_meta/service.rb +94 -0
  33. data/lib/shibkit/meta_meta/source.rb +558 -0
  34. data/lib/shibkit/meta_meta/sp.rb +79 -0
  35. data/shibkit-meta_meta.gemspec +154 -0
  36. data/spec/meta_meta/attribute/token +0 -0
  37. data/spec/meta_meta/config/autoloading_and_refreshing_spec.rb +72 -0
  38. data/spec/meta_meta/config/code_nspec.rb +13 -0
  39. data/spec/meta_meta/config/configuration_spec.rb +30 -0
  40. data/spec/meta_meta/config/creation_spec.rb +43 -0
  41. data/spec/meta_meta/config/downloading_and_caching_settings_spec.rb +216 -0
  42. data/spec/meta_meta/config/env_platform_settings.rb +129 -0
  43. data/spec/meta_meta/config/filtering_settings_spec.rb +123 -0
  44. data/spec/meta_meta/config/init.rb +8 -0
  45. data/spec/meta_meta/config/logger_settings_spec.rb +91 -0
  46. data/spec/meta_meta/config/smartcache_settings_spec.rb +110 -0
  47. data/spec/meta_meta/config/source_file_settings_spec.rb +99 -0
  48. data/spec/meta_meta/config/tagging_settings_spec.rb +81 -0
  49. data/spec/meta_meta/config/working_directory_settings_spec.rb +106 -0
  50. data/spec/meta_meta/config/xml_processing_settings_spec.rb +75 -0
  51. data/spec/meta_meta/contact/contact_oldspec.rb +0 -0
  52. data/spec/meta_meta/entity/entity_oldspec.rb +53 -0
  53. data/spec/meta_meta/federation/federation_oldspec.rb +0 -0
  54. data/spec/meta_meta/idp/token +0 -0
  55. data/spec/meta_meta/logo/token +0 -0
  56. data/spec/meta_meta/meta_meta/cache_example.yaml +141284 -0
  57. data/spec/meta_meta/meta_meta/meta_meta_spec.rb +269 -0
  58. data/spec/meta_meta/meta_meta/saved_sources.yaml +46 -0
  59. data/spec/meta_meta/metadata_item/token +0 -0
  60. data/spec/meta_meta/organisation/organisation_oldspec.rb +0 -0
  61. data/spec/meta_meta/provider/token +0 -0
  62. data/spec/meta_meta/requested_attribute/token +0 -0
  63. data/spec/meta_meta/service/token +0 -0
  64. data/spec/meta_meta/source/application_extras_spec.rb +234 -0
  65. data/spec/meta_meta/source/conversion_spec.rb +75 -0
  66. data/spec/meta_meta/source/creation_spec.rb +0 -0
  67. data/spec/meta_meta/source/downloads_and_caching_spec.rb +0 -0
  68. data/spec/meta_meta/source/federation_information_spec.rb +11 -0
  69. data/spec/meta_meta/source/fixtures.rb +24 -0
  70. data/spec/meta_meta/source/init.rb +1 -0
  71. data/spec/meta_meta/source/loading_and_saving_spec.rb +0 -0
  72. data/spec/meta_meta/source/metadata_details_spec.rb +0 -0
  73. data/spec/meta_meta/source/metadata_integrity_spec.rb +0 -0
  74. data/spec/meta_meta/source/selection_spec.rb +0 -0
  75. data/spec/meta_meta/source/source_oldspec.rb +353 -0
  76. data/spec/meta_meta/source/xml_parsing_spec.rb +0 -0
  77. data/spec/meta_meta/sp/token +0 -0
  78. data/spec/meta_meta/template +2 -0
  79. data/spec/moi/config_spec.rb +0 -0
  80. data/spec/spec.opts +1 -0
  81. data/spec/spec_helper.rb +25 -0
  82. data/spec/support/supply_xml.rb +0 -0
  83. metadata +320 -0
@@ -0,0 +1,38 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "shibkit-meta_meta"
18
+ gem.homepage = "https://github.com/Digital-Identity-Labs/shibkit-meta_meta"
19
+ gem.license = "Apache 2.0"
20
+ gem.summary = %Q{Downloads and parses Shibboleth (SAML2) metadata.}
21
+ gem.description = %Q{Utilities for friendly handling of Shibboleth/SAML2 metadata. Easily download and parse metadata XML into Ruby objects.}
22
+ gem.email = "gems@digitalidentitylabs.com"
23
+ gem.authors = ["Pete Birkinshaw"]
24
+ gem.files.exclude 'lib/scratch_test.rb'
25
+ # dependencies defined in Gemfile
26
+ end
27
+ Jeweler::RubygemsDotOrgTasks.new
28
+
29
+
30
+ require 'rspec/core'
31
+ require 'rspec/core/rake_task'
32
+ RSpec::Core::RakeTask.new(:spec) do |spec|
33
+ ##
34
+ spec.pattern = FileList['spec/**/**/*_spec.rb']
35
+ end
36
+
37
+ task :default => :spec
38
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.2
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+ require 'shibkit/meta_meta'
3
+
4
+ puts Shibkit::MetaMeta.entities.sort!{|a,b| a.uri.size <=> b.uri.size}.last
@@ -0,0 +1,600 @@
1
+ ## @author Pete Birkinshaw (<pete@digitalidentitylabs.com>)
2
+ ## Copyright: Copyright (c) 2011 Digital Identity Ltd.
3
+ ## License: Apache License, Version 2.0
4
+
5
+ ## Licensed under the Apache License, Version 2.0 (the "License");
6
+ ## you may not use this file except in compliance with the License.
7
+ ## You may obtain a copy of the License at
8
+ ##
9
+ ## http://www.apache.org/licenses/LICENSE-2.0
10
+ ##
11
+ ## Unless required by applicable law or agreed to in writing, software
12
+ ## distributed under the License is distributed on an "AS IS" BASIS,
13
+ ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ ## See the License for the specific language governing permissions and
15
+ ## limitations under the License.
16
+ ##
17
+
18
+ require 'rubygems'
19
+
20
+ require 'nokogiri'
21
+ require 'yaml'
22
+ require 'open-uri'
23
+ require 'logger'
24
+ require 'fileutils'
25
+ require 'digest/sha1'
26
+
27
+ require 'shibkit/meta_meta/config'
28
+ require 'shibkit/meta_meta/metadata_item'
29
+ require 'shibkit/meta_meta/contact'
30
+ require 'shibkit/meta_meta/source'
31
+ require 'shibkit/meta_meta/entity'
32
+ require 'shibkit/meta_meta/federation'
33
+ require 'shibkit/meta_meta/organisation'
34
+
35
+ module Shibkit
36
+
37
+ ## Simple library to parse Shibboleth metadata files into Ruby objects
38
+ class MetaMeta
39
+
40
+ ##
41
+ def self.config(&block)
42
+
43
+ if block
44
+ return ::Shibkit::MetaMeta::Config.instance.configure(&block)
45
+ else
46
+ return ::Shibkit::MetaMeta::Config.instance
47
+ end
48
+
49
+ end
50
+
51
+ ## Flush out all available sources, metadata caches, etc.
52
+ def self.reset
53
+
54
+ log.info "Resetting all sources, metadata caches, etc."
55
+
56
+ ## Clear the source data
57
+ @additional_sources = Hash.new
58
+ @loaded_sources = Hash.new
59
+
60
+ ## Clear federation entity data
61
+ self.flush
62
+
63
+ end
64
+
65
+ ## Clear all loaded entity & federation data
66
+ def self.flush
67
+
68
+ log.info "Flushing all loaded objects"
69
+
70
+ @orgs = Array.new
71
+ @entities = Array.new
72
+ @federations = Array.new
73
+ @by_uri = Hash.new
74
+
75
+ end
76
+
77
+ ## Delete all cache files
78
+ def self.delete_all_cached_files!
79
+
80
+ dir = config.cache_root
81
+
82
+ if config.can_delete?
83
+
84
+ log.info "Deleting all files at #{dir}..."
85
+ FileUtils.rm_rf dir
86
+ FileUtils.mkdir_p dir
87
+
88
+ else
89
+
90
+ log.warn "Cannot delete files at #{dir} - check config settings."
91
+
92
+ end
93
+
94
+ end
95
+
96
+ ## Convenience method to add a source from a hash or object
97
+ def self.add_source(data)
98
+
99
+ @additional_sources ||= Hash.new
100
+
101
+ case data
102
+ when Hash
103
+ source = Source.from_hash(data)
104
+ when ::Shibkit::MetaMeta::Source
105
+ source = data
106
+ else
107
+ raise "Expected either hash or Source object!"
108
+ end
109
+
110
+ log.info "Added a source for #{source.uri}"
111
+
112
+ @additional_sources[source.uri] = source
113
+
114
+ end
115
+
116
+ ## Have we loaded any sources?
117
+ def self.loaded_sources?
118
+
119
+ return @loaded_sources ? true : false
120
+
121
+ end
122
+
123
+ ## Load sources from a YAML file
124
+ def self.load_sources(filename=self.config.sources_file)
125
+
126
+ log.info "Loading sources from disk..."
127
+
128
+ @loaded_sources = Hash.new
129
+
130
+ Source.load(filename).each do |source|
131
+
132
+ ## More than one definition for a source is a problem
133
+ raise "Duplicate source for #{source.uri}!" if @loaded_sources[source.uri]
134
+
135
+ @loaded_sources[source.uri] = source
136
+
137
+ end
138
+
139
+ end
140
+
141
+ ## Save all known sources to sources list file
142
+ def self.save_sources(filename)
143
+
144
+ log.info "Saving sources to #{filename}..."
145
+
146
+ src_dump = Hash.new
147
+ self.sources.each { |s| src_dump[s.uri] = s.to_hash }
148
+
149
+ File.open(filename, 'w') { |out| YAML.dump(src_dump, out) }
150
+
151
+ end
152
+
153
+ ## List all sources as an array
154
+ def self.sources
155
+
156
+ if self.config.autoload? and loaded_sources.size == 0 and additional_sources.size == 0
157
+
158
+ self.load_sources
159
+
160
+ end
161
+
162
+ all_sources_indexed = loaded_sources.merge(additional_sources)
163
+
164
+ sources = all_sources_indexed.values
165
+
166
+ sources = sources.sort {|a,b| a.created_at <=> b.created_at }
167
+
168
+ if self.filtered_sources?
169
+
170
+ sources = sources.select { |s| self.selected_federation_uris.include? s.uri }
171
+
172
+ end
173
+
174
+ return sources
175
+
176
+ end
177
+
178
+ ## List of federation/collection uris
179
+ def self.selected_federation_uris
180
+
181
+ return self.config.selected_federation_uris
182
+
183
+ end
184
+
185
+ ## Has a limited subset of federations/sources been selected?
186
+ def self.filtered_sources?
187
+
188
+ return self.selected_federation_uris.empty? ? false : true
189
+
190
+ end
191
+
192
+ ## Loads federation metadata contents
193
+ def self.load_cache_file(file_or_url, format=:yaml)
194
+
195
+ self.reset
196
+
197
+ log.info "Loading object cache file from #{file_or_url} as #{format} data..."
198
+
199
+ @federations = case format
200
+ when :yaml
201
+ YAML.load(File.open(file_or_url))
202
+ when :marshal
203
+ Marshal.load(File.open(file_or_url))
204
+ else
205
+ raise "Unexpected cache file format requested! Please use :yaml or :marshal"
206
+ end
207
+
208
+ self.entities
209
+
210
+ log.info "Processing complete."
211
+
212
+ return true
213
+
214
+ end
215
+
216
+ ## Save entity data into a YAML file.
217
+ def self.save_cache_file(file_path, format=:yaml)
218
+
219
+ log.info "Saving object cache file to #{file_path} as #{format} data..."
220
+
221
+ ## Will *not* overwrite the example/default file in gem! TODO: this code is awful.
222
+ gem_data_path = "#{::File.dirname(__FILE__)}/data"
223
+ if file_path.include? gem_data_path
224
+ raise "Attempt to overwrite gem's default metadata cache! Please specify your own file to save cache in"
225
+ end
226
+
227
+ self.textify_xml!
228
+
229
+ ## Write the YAML to disk
230
+ File.open(file_path, 'w') do |out|
231
+
232
+ case format
233
+ when :yaml
234
+ YAML.dump(@federations, out)
235
+ when :marshal
236
+ Marshal.dump(@federations, out)
237
+ else
238
+ raise "Unexpected cache file format requested! Please use :yaml or :marshal"
239
+ end
240
+
241
+ end
242
+
243
+ return true
244
+
245
+ end
246
+
247
+ ## Parses sources and returns an array of all federation object
248
+ def self.process_sources
249
+
250
+ if config.smartcache_active?
251
+ return if self.smartcache_load
252
+ end
253
+
254
+ log.info "Processing content of sources into objects..."
255
+
256
+ raise "MetaMeta sources are not an Array! (Should not be a #{self.sources.class})" unless
257
+ self.sources.kind_of? Array
258
+
259
+ self.flush
260
+
261
+ self.sources.each do |source|
262
+
263
+ if self.filtered_sources?
264
+
265
+ next unless self.selected_federation_uris.include? source.uri
266
+
267
+ end
268
+
269
+ start_time = Time.new
270
+
271
+ federation = source.to_federation
272
+
273
+ ## Store all federations in array
274
+ @federations << federation
275
+
276
+ log.info "Loaded #{federation.entities.count} entities from #{federation} metadata file in #{Time.new - start_time} seconds."
277
+
278
+
279
+ end
280
+
281
+ ## Bodge to make sure primary ents are set, multifederation calculated, etc
282
+ #self.entities # Issue 14
283
+
284
+ log.info "Processing complete. #{@federations.count} sets of metadata have been loaded."
285
+
286
+ self.smartcache_save if config.smartcache_active?
287
+
288
+ return @federations
289
+
290
+ end
291
+
292
+ ## Downloads and reprocesses metadata files
293
+ def self.refresh(force=false)
294
+
295
+ log.info "Refreshing all selected federations"
296
+
297
+ ## Reload source lists overwriting previous set
298
+ self.load_sources
299
+
300
+ ## Reprocess sources to create fresh set of federation and entity objects
301
+ self.process_sources
302
+
303
+ return true
304
+
305
+ end
306
+
307
+ def self.stockup(force=false)
308
+
309
+ if self.config.autoload?
310
+
311
+ self.process_sources unless @federations
312
+ self.process_sources if @federations.empty?
313
+
314
+ end
315
+
316
+ end
317
+
318
+ ## Have objects been loaded from metadata?
319
+ def self.stocked?
320
+
321
+ return false unless @federations
322
+ return false if @federations.empty?
323
+
324
+ return true
325
+
326
+ end
327
+
328
+ ## Return list of Federations objects (filtered if select_federations is set)
329
+ def self.federations
330
+
331
+ return [] if @federations.nil? and ! config.autoload?
332
+
333
+ self.stockup
334
+
335
+ if self.filtered_sources?
336
+
337
+ return @federations.select { |f| self.selected_federation_uris.include? f.uri }
338
+
339
+ end
340
+
341
+ return @federations
342
+
343
+ end
344
+
345
+ ## All primary entities from all federations
346
+ def self.entities
347
+
348
+ return [] if @entities.nil? and ! config.autoload?
349
+
350
+ ## Populate memoised array of entities if it's empty
351
+ unless @entities and @entities.size > 0
352
+
353
+ ## Array for memoising primary entities
354
+ @entities ||= Array.new
355
+
356
+ ## For keeping track of already processed entities & marking them as primary
357
+ processed = Hash.new
358
+
359
+ self.federations.each do |f|
360
+
361
+ f.entities.each do |e|
362
+
363
+ ## If we've already found the primary version of the entity
364
+ if processed[e.uri]
365
+
366
+ ## Add this federation's URI to the primary
367
+ primary = processed[e.uri]
368
+ primary.other_federation_uris << f.uri
369
+
370
+ ## Add tags from the non-primary to the primary
371
+ primary.tags << e.tags if config.merge_primary_tags?
372
+
373
+ next
374
+
375
+ end
376
+
377
+ ## Mark this entity as the primary and remember it as already processed.
378
+ e.primary = true
379
+ processed[e.uri] = e
380
+
381
+ ## Collect entity
382
+ @entities << e
383
+
384
+ end
385
+
386
+ end
387
+
388
+ ## BODGE: Needs a better fix. Issue 14
389
+ #@entities = @entities.compact
390
+
391
+ end
392
+
393
+ return @entities
394
+
395
+ end
396
+
397
+ def self.orgs
398
+
399
+ unless @orgs and @orgs.size > 0
400
+
401
+ @orgs ||= Array.new
402
+
403
+ processed = Hash.new
404
+
405
+ self.entities.each do |e|
406
+
407
+ org = e.organisation
408
+
409
+ next unless org
410
+ next if processed[org.druid]
411
+
412
+ @orgs << org
413
+
414
+
415
+ processed[org.druid] = true
416
+
417
+ end
418
+
419
+ @orgs.sort! {|a,b| a.druid <=> b.druid }
420
+
421
+ end
422
+
423
+ return @orgs
424
+
425
+ end
426
+
427
+ ##
428
+ def self.idps
429
+
430
+ return entities.select { |e| e.idp? }
431
+
432
+ end
433
+
434
+ ##
435
+ def self.sps
436
+
437
+ return entities.select { |e| e.sp? }
438
+
439
+ end
440
+
441
+ def self.from_uri(uri)
442
+
443
+ unless @by_uri and @by_uri.size > 0
444
+
445
+ @by_uri ||= Hash.new
446
+
447
+ self.federations.each { |f| @by_uri[f.uri] = f unless @by_uri[f.uri] }
448
+ self.entities.each { |e| @by_uri[e.uri] = e unless @by_uri[e.uri] }
449
+
450
+ end
451
+
452
+ return @by_uri[uri]
453
+
454
+ end
455
+
456
+ def self.textify_xml!
457
+
458
+ @federations.each { |f| f.textify_xml(true) }
459
+
460
+ end
461
+
462
+ def self.purge_xml!
463
+
464
+ @federations.each { |f| f.purge_xml(true) }
465
+
466
+ end
467
+
468
+ private
469
+
470
+ ## Logging
471
+ def self.log
472
+
473
+ return ::Shibkit::MetaMeta.config.logger
474
+
475
+ end
476
+
477
+ private
478
+
479
+ ## Access to all additional sources
480
+ def self.additional_sources
481
+
482
+ @additional_sources ||= Hash.new
483
+ return @additional_sources
484
+
485
+ end
486
+
487
+ ## Access to all additional sources
488
+ def self.loaded_sources
489
+
490
+ @loaded_sources ||= Hash.new
491
+ return @loaded_sources
492
+
493
+ end
494
+
495
+ ##
496
+ def self.smartcache_load
497
+
498
+ log.info "Checking smartcache status..."
499
+
500
+ object_file = config.smartcache_object_file
501
+ scmd_file = config.smartcache_info_file
502
+ expiry_period = config.smartcache_expiry
503
+
504
+ ## Do we even have a file?
505
+ return false unless File.exists? object_file
506
+ return false unless File.exists? scmd_file
507
+
508
+ start_time = Time.new
509
+
510
+ ## Make sure the dump metadata is suitable
511
+ info = YAML.load(File.open(scmd_file))
512
+
513
+ ## Check
514
+ cache_age = (Time.new.to_i - info[:created_at].to_i)
515
+ return false unless cache_age < expiry_period.to_i
516
+
517
+ return false unless info[:version] == config.version
518
+
519
+ return false unless info[:platform] == config.platform
520
+
521
+ return false unless info[:format] == :marshal
522
+
523
+ return false unless info[:object_file] == object_file
524
+
525
+ return false unless info[:purge_xml] == config.purge_xml?
526
+ return false unless info[:source_xml] == config.remember_source_xml?
527
+ return false unless info[:groups] == Digest::SHA1.hexdigest(config.selected_groups.join)
528
+
529
+ log.info "Smartcache is valid: loading objects..."
530
+
531
+ ## If file does not exist (or is stale) and we have objects, save
532
+ self.load_cache_file(object_file, :marshal)
533
+
534
+ log.info "Loaded #{@federations.count} federations and #{@entities.count} entities from smartcache in #{Time.new - start_time} seconds."
535
+
536
+ return true
537
+
538
+ end
539
+
540
+ ##
541
+ def self.smartcache_save
542
+
543
+ object_file = config.smartcache_object_file
544
+ scmd_file = config.smartcache_info_file
545
+
546
+ log.info "Saving smartcache with #{@federations.count} federations and #{@entities.count} entities..."
547
+
548
+ ## Save file in fast marsh
549
+ mkdir_p config.cache_root unless File.exists? config.cache_root
550
+ self.save_cache_file(object_file, :marshal)
551
+
552
+ info = {
553
+ :created_at => Time.new,
554
+ :version => config.version,
555
+ :platform => config.platform,
556
+ :object_file => object_file,
557
+ :format => :marshal,
558
+ :purge_xml => config.purge_xml?,
559
+ :source_xml => config.remember_source_xml?,
560
+ :groups => Digest::SHA1.hexdigest(config.selected_groups.join)
561
+ }
562
+
563
+ File.open(scmd_file, 'w') { |out| YAML.dump(info, out) }
564
+
565
+ log.info "Saved smartcache."
566
+
567
+ return true
568
+
569
+ end
570
+
571
+ ## Stats
572
+ def self.stats
573
+
574
+ stats = Hash.new
575
+
576
+ stats[:federations] = Hash.new
577
+
578
+ self.federations.each do |f|
579
+
580
+ stats[:federations][f.uri] = Hash.new
581
+ stats[:federations][f.uri][:sp_count] = f.sps.count
582
+ stats[:federations][f.uri][:idp_count] = f.idps.count
583
+ stats[:federations][f.uri][:entities] = f.entities.count
584
+ stats[:federations][f.uri][:uri_count] = f.entities.collect {|e| e.uri}.uniq.count
585
+
586
+ end
587
+
588
+ stats[:federation_count] = self.federations.count
589
+ stats[:entities_count] = self.federations.inject(0) {|m,f| m+f.entities.count}
590
+ stats[:primary_entities_count] = self.entities.count
591
+ stats[:organisation_count] = self.orgs.count
592
+
593
+ return stats
594
+
595
+ end
596
+
597
+
598
+
599
+ end
600
+ end