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,73 @@
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
+ module Shibkit
19
+ class MetaMeta
20
+
21
+ ## Class to represent the metadata of the organisation owning a Shibboleth entity
22
+ class Organisation < MetadataItem
23
+
24
+ require 'shibkit/meta_meta/metadata_item'
25
+
26
+ ## Element and attribute used to select XML for new objects
27
+ ROOT_ELEMENT = 'Organization'
28
+ TARGET_ATTR = nil
29
+ REQUIRED_QUACKS = [:name]
30
+
31
+ ## The name identifier for the organisation
32
+ attr_accessor :name
33
+
34
+ ## The human-readable display name for the organisation
35
+ attr_accessor :display_name
36
+
37
+ ## The homepage URL for the organisation
38
+ attr_accessor :url
39
+
40
+ ## Try to make a crude unique id for the organisation
41
+ def druid
42
+
43
+ ## Derived, *relatively* unique ID.
44
+ return display_name.strip.downcase.delete " .,-_'"
45
+
46
+ end
47
+
48
+ def to_s
49
+
50
+ return display_name
51
+
52
+ end
53
+
54
+ private
55
+
56
+ def parse_xml
57
+
58
+ @name = @noko.xpath('xmlns:OrganizationName[1]')[0].content.strip
59
+
60
+
61
+ @display_name = @noko.xpath('xmlns:OrganizationDisplayName[1]')[0].content.strip
62
+
63
+
64
+ @url = @noko.xpath('xmlns:OrganizationURL[1]')[0].content.strip
65
+
66
+
67
+ log.debug " Derived organisation #{url} from XML"
68
+
69
+ end
70
+
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,195 @@
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
+ module Shibkit
19
+ class MetaMeta
20
+
21
+ require 'shibkit/meta_meta/metadata_item'
22
+
23
+ ## Class to represent the metadata of a Shibboleth IDP or SP
24
+ class Provider < MetadataItem
25
+
26
+ require 'shibkit/meta_meta/logo'
27
+
28
+ ## Element and attribute used to select XML for new objects
29
+ ROOT_ELEMENT = 'EntityDescriptor'
30
+ TARGET_ATTR = 'entityID'
31
+ REQUIRED_QUACKS = [:entity_uri]
32
+
33
+ MDUI_ROOT = 'NotSpecified'
34
+
35
+ ## The URI of the entity
36
+ attr_accessor :entity_uri
37
+ alias :uri :entity_uri
38
+
39
+ attr_accessor :display_names
40
+
41
+ attr_accessor :descriptions
42
+
43
+ attr_accessor :keyword_sets
44
+
45
+ attr_accessor :info_urls
46
+
47
+ attr_accessor :privacy_urls
48
+
49
+ attr_accessor :ip_blocks
50
+
51
+ attr_accessor :domains
52
+
53
+ attr_accessor :geolocation_urls
54
+
55
+ attr_reader :valid
56
+
57
+ attr_accessor :organisation
58
+
59
+ alias :entity_id :entity_uri
60
+ alias :valid? :valid
61
+
62
+ def to_s
63
+
64
+ return uri
65
+
66
+ end
67
+
68
+ def display_name(lang=:en)
69
+
70
+ return display_names[lang] unless display_names[lang].to_s.empty?
71
+
72
+ if self.kind_of?(Shibkit::MetaMeta::SP) and default_service
73
+ return self.default_service.name[lang] unless default_service.name[lang].to_s.empty?
74
+ end
75
+
76
+ if organisation
77
+ return organisation.display_name unless organisation.display_name.to_s.empty?
78
+ return [organisation.name, "service"].join(' ') unless organisation.name.to_s.empty?
79
+ end
80
+
81
+ return entity_id
82
+
83
+ end
84
+
85
+ def description(lang=:en)
86
+
87
+ return descriptions[lang] unless descriptions[lang].to_s.empty?
88
+
89
+ if self.kind_of?(Shibkit::MetaMeta::SP) and default_service
90
+ return default_service.description(lang) unless default_service.description(lang).to_s.empty?
91
+ end
92
+
93
+ if organisation
94
+ return organisation.display_name unless organisation.display_name.to_s.empty?
95
+ return organisation.name unless organisation.name.to_s.empty?
96
+ end
97
+
98
+ return ""
99
+
100
+ end
101
+
102
+ def keywords(lang=:en)
103
+
104
+ return keyword_sets[lang] || []
105
+
106
+ end
107
+
108
+ def info_url(lang=:en)
109
+
110
+ return info_urls[lang] || nil
111
+
112
+ end
113
+
114
+ def privacy_url(lang=:en)
115
+
116
+ return privacy_urls[lang] || nil
117
+
118
+ end
119
+
120
+ def logos(lang=:en)
121
+
122
+ return logos[lang] || []
123
+
124
+ end
125
+
126
+ def purge_xml(cascade=true)
127
+
128
+ super
129
+
130
+ return unless cascade
131
+
132
+ @logos.values.each do |logo_set|
133
+
134
+ logo_set.each { |logo| logo.purge_xml(cascade)}
135
+
136
+ end
137
+
138
+ end
139
+
140
+ def textify_xml(cascade=true)
141
+
142
+ super
143
+
144
+ return unless cascade
145
+
146
+ @logos.values.each do |logo_set|
147
+
148
+ logo_set.each { |logo| logo.textify_xml(cascade)}
149
+
150
+ end
151
+
152
+ end
153
+
154
+ private
155
+
156
+ def parse_xml
157
+
158
+ self.entity_uri = @noko['entityID']
159
+
160
+ mdui_root = self.class::MDUI_ROOT
161
+
162
+ ## Display names
163
+ @display_names = extract_lang_map_of_strings("xmlns:#{mdui_root}/xmlns:Extensions/mdui:UIInfo/mdui:DisplayName")
164
+
165
+ ## Descriptions
166
+ @descriptions = extract_lang_map_of_strings("xmlns:#{mdui_root}/xmlns:Extensions/mdui:UIInfo/mdui:Description")
167
+
168
+ ## Keywords
169
+ @keyword_sets = extract_lang_map_of_string_lists("xmlns:#{mdui_root}/xmlns:Extensions/mdui:UIInfo/mdui:Keywords")
170
+
171
+ ## Information URLs
172
+ @info_urls = extract_lang_map_of_strings("xmlns:#{mdui_root}/xmlns:Extensions/mdui:UIInfo/mdui:InformationURL")
173
+
174
+ ## Privacy Statement URLs
175
+ @privacy_urls = extract_lang_map_of_strings("xmlns:#{mdui_root}/xmlns:Extensions/mdui:UIInfo/mdui:PrivacyStatementURL")
176
+
177
+ ## Logos
178
+ @logos = extract_lang_map_of_objects("xmlns:#{mdui_root}/xmlns:Extensions/mdui:UIInfo/mdui:Logo",
179
+ Shibkit::MetaMeta::Logo)
180
+
181
+ ## IP Address Ranges
182
+ @ip_blocks = extract_simple_list("xmlns:#{mdui_root}/xmlns:Extensions/mdui:DiscoHints/mdui:IPHint")
183
+
184
+ ## DNS Domain Names
185
+ @domains = extract_simple_list("xmlns:#{mdui_root}/xmlns:Extensions/mdui:DiscoHints/mdui:DomainHint")
186
+
187
+ ## Geolocations
188
+ @geolocations = extract_simple_list("xmlns:#{mdui_root}/xmlns:Extensions/mdui:DiscoHints/mdui:GeolocationHint")
189
+
190
+ end
191
+
192
+ end
193
+
194
+ end
195
+ end
@@ -0,0 +1,33 @@
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
+
19
+ module Shibkit
20
+ class MetaMeta
21
+ module Provisioning
22
+
23
+ ## Abstract base class for provisioning drivers
24
+ class Base
25
+
26
+ initialize
27
+
28
+
29
+
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,29 @@
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
+ module Shibkit
19
+ class MetaMeta
20
+
21
+ ## Class to represent the metadata of the organisation owning a Shibboleth entity
22
+ class RequestedAttribute < Attribute
23
+
24
+ ## Element and attribute used to select XML for new objects
25
+ ROOT_ELEMENT = 'RequestedAttribute'
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,94 @@
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
+ module Shibkit
19
+ class MetaMeta
20
+
21
+ ##
22
+ class Service < MetadataItem
23
+
24
+ require 'shibkit/meta_meta/metadata_item'
25
+
26
+ ## Element and attribute used to select XML for new objects
27
+ ROOT_ELEMENT = 'AttributeConsumingService'
28
+ TARGET_ATTR = 'index'
29
+ REQUIRED_QUACKS = [:index]
30
+
31
+ ##
32
+ attr_accessor :names
33
+
34
+ ##
35
+ attr_accessor :descriptions
36
+
37
+ ##
38
+ attr_accessor :index
39
+
40
+ ##
41
+ attr_accessor :attributes
42
+
43
+ attr_accessor :default
44
+
45
+ alias :default? :default
46
+
47
+ def name(lang=:en)
48
+
49
+ return names[lang]
50
+
51
+ end
52
+
53
+ def description(lang=:en)
54
+
55
+ return descriptions[lang]
56
+
57
+ end
58
+
59
+ def to_s
60
+
61
+ return name(:en)
62
+
63
+ end
64
+
65
+ private
66
+
67
+ def parse_xml
68
+
69
+ @index = @noko['index'].to_i || 0
70
+
71
+ @default = @noko['isDefault'] || 'false'
72
+
73
+ ## Display names
74
+ @names = extract_lang_map_of_strings("xmlns:ServiceName")
75
+
76
+ ## Descriptions
77
+ @descriptions = extract_lang_map_of_strings("xmlns:ServiceDescription")
78
+
79
+ @attributes ||= Array.new
80
+ @noko.xpath('xmlns:RequestedAttribute').each do |ax|
81
+
82
+ attribute = Shibkit::MetaMeta::RequestedAttribute.new(ax).filter
83
+
84
+ @attributes << attribute if attribute
85
+
86
+ end
87
+
88
+ log.debug " Derived service #{name} from XML"
89
+
90
+ end
91
+
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,558 @@
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 'yaml'
19
+ require 'rest_client'
20
+ require 'restclient/components'
21
+ require 'rack/cache'
22
+ require 'rack/commonlogger'
23
+ require 'rbconfig'
24
+ require 'tempfile'
25
+ require 'addressable/uri'
26
+ require 'fileutils'
27
+
28
+ module Shibkit
29
+ class MetaMeta
30
+
31
+ ##
32
+ ##
33
+ class Source
34
+
35
+ require 'shibkit/meta_meta/mixin/cached_downloads'
36
+ require 'shibkit/meta_meta/federation'
37
+
38
+ include Shibkit::MetaMeta::Mixin::CachedDownloads
39
+
40
+ ## @note This class currently lacks the ability to properly validate
41
+ ## metadata.
42
+
43
+ ## Location of default real sources list (contains real-world federation details) # REMOVE
44
+ REAL_SOURCES_FILE = "#{::File.dirname(__FILE__)}/data/real_sources.yml"
45
+
46
+ ## Location of default mock sources list (contains small fictional federations) # REMOVE
47
+ DEV_SOURCES_FILE = "#{::File.dirname(__FILE__)}/data/dev_sources.yml"
48
+
49
+ PERCENTAGE_PATTERN = /(\d+)\s*%/
50
+
51
+ ## Additional namespaces that Nokogiri needs to know about # TODO Move to Config?
52
+ NAMESPACES = {
53
+ 'ukfedlabel' => 'http://ukfederation.org.uk/2006/11/label',
54
+ 'elab' => 'http://eduserv.org.uk/labels',
55
+ 'wayf' => 'http://sdss.ac.uk/2006/06/WAYF',
56
+ 'mdui' => 'urn:oasis:names:tc:SAML:metadata:ui',
57
+ 'saml' => 'urn:oasis:names:tc:SAML:2.0:assertion',
58
+ 'shibmd' => 'urn:mace:shibboleth:metadata:1.0'
59
+ }
60
+
61
+ ## @return [String] the URI identifier for the federation or collection
62
+ attr_accessor :name_uri
63
+ alias :uri :name_uri
64
+
65
+ ## @return [String] the full name of the federation or collection
66
+ attr_accessor :name
67
+
68
+ ## @return [String] the common, friendler name of the federation or collection
69
+ attr_accessor :display_name
70
+
71
+ ## @return [String] :federation for proper federations, :collection for
72
+ ## simple collections of entities.
73
+ attr_accessor :type
74
+
75
+ ## @return [Fixednum] the recommended refresh period for the federation, in seconds
76
+ attr_accessor :refresh_delay
77
+
78
+ ## @return [Array] country codes for areas served by the federation
79
+ attr_accessor :countries
80
+
81
+ ## @return [String] URL or filesystem path of the metadata file to be used
82
+ attr_accessor :metadata_source
83
+ alias :url :metadata_source
84
+
85
+ ## @return [String] URL or filesystem path of the metadata certificate to be used
86
+ attr_accessor :certificate_source
87
+
88
+ ## @return [String, nil] Fingerprint of the federation certificate
89
+ attr_accessor :fingerprint
90
+
91
+ ## @return [String, nil] URL of the federation's Refeds wiki entry
92
+ attr_accessor :refeds_url
93
+ alias :refeds_info :refeds_url
94
+
95
+ ## @return [String] URL of the federation or collection's home page
96
+ attr_accessor :homepage
97
+ alias :homepage_url :homepage
98
+
99
+ ## @return [Array] Array of languages supported by the federation or collection
100
+ attr_accessor :languages
101
+
102
+ ## @return [String] Main contact email address for the federation
103
+ attr_accessor :support_email
104
+
105
+ ## @return [String] Brief description of the federation or collection
106
+ attr_accessor :description
107
+
108
+ ## @return [String] Time the metadata for this federation was last fetched
109
+ ## @note This is not persistent between uses of this class
110
+ attr_reader :fetched_at
111
+
112
+ ## @return [String] Message returned during processing
113
+ ## @deprecated Not actually used at present, not sure if this is needed...
114
+ attr_reader :messages
115
+
116
+ ## @return [String] Status of the source: indicates success of last operation
117
+ attr_reader :status
118
+
119
+ attr_accessor :active
120
+ alias :active? :active
121
+
122
+ attr_reader :created_at
123
+
124
+ private
125
+
126
+ attr_reader :metadata_tmpfile
127
+ attr_reader :certificate_tmpfile
128
+
129
+ public
130
+
131
+ ## New Source object with default values
132
+ ## @return [Source]
133
+ def initialize(&block)
134
+
135
+ @name_uri = ""
136
+ @created_at = Time.new
137
+ @name = "Unnown"
138
+ @refresh_delay = 86400
139
+ @display_name = "Unknown"
140
+ @type = "federation"
141
+ @countries = []
142
+ @metadata_source = nil
143
+ @certificate_source = nil
144
+ @fingerprint = nil
145
+ @refeds_info = nil
146
+ @homepage = nil
147
+ @languages = []
148
+ @support_email = nil
149
+ @description = ""
150
+ @certificate_tmpfile = nil
151
+ @metadata_tmpfile = nil
152
+ @active = true
153
+ @trustiness = 1
154
+ @groups = []
155
+ @tags = []
156
+
157
+ self.instance_eval(&block) if block
158
+
159
+ end
160
+
161
+ def to_s
162
+
163
+ return metadata_source || nil
164
+
165
+ end
166
+
167
+ ## Create a new source from a hash
168
+ def self.from_hash(data, uri=nil)
169
+
170
+ raise "#{data.class} is not a hash" if not data.instance_of? Hash
171
+
172
+ data = data.inject({}){|m,(k,v)| m[k.to_sym] = v; m}
173
+
174
+ new_source = self.new do |source|
175
+
176
+ source.name_uri = data[:uri] || uri
177
+ source.name = data[:name] || uri
178
+ source.refresh_delay = data[:refresh].to_i || 86400
179
+ source.display_name = data[:display_name] || data['name'] || uri
180
+ source.type = data[:type].to_sym || :collection
181
+
182
+ source.metadata_source = data[:metadata]
183
+ source.certificate_source = data[:certificate]
184
+ source.fingerprint = data[:fingerprint]
185
+ source.refeds_url = data[:refeds_info]
186
+ source.homepage = data[:homepage]
187
+
188
+ source.support_email = data[:support_email] || nil
189
+ source.description = data[:description] || ""
190
+ source.trustiness = data[:trustiness].to_f || 1
191
+
192
+ source.languages = data[:languages].inject([]){|m,v| m << v.to_s.downcase.to_sym } || [:en]
193
+ source.countries = data[:countries].inject([]){|m,v| m << v.to_s.downcase.to_sym } || []
194
+ source.groups = data[:groups] || []
195
+ source.tags = data[:tags] || []
196
+
197
+ end
198
+
199
+ return new_source
200
+
201
+ end
202
+
203
+ ## Create a new hash from a source object
204
+ def to_hash
205
+
206
+ data = Hash.new
207
+
208
+ data['uri'] = name_uri.strip
209
+ data['name'] = name
210
+ data['refresh'] = refresh_delay.to_i
211
+ data['display_name'] = display_name
212
+ data['type'] = type.to_s
213
+ data['countries'] = countries
214
+ data['metadata'] = metadata_source
215
+ data['certificate'] = certificate_source
216
+ data['fingerprint'] = fingerprint
217
+ data['refeds_info'] = refeds_url
218
+ data['homepage'] = homepage
219
+ data['languages'] = languages
220
+ data['support_email'] = support_email
221
+ data['description'] = description.strip
222
+ data['trustiness'] = trustiness
223
+ data['groups'] = groups
224
+ data['tags'] = tags
225
+
226
+ return data
227
+
228
+ end
229
+
230
+ ## Build a parsed Federation object containing Entitiess
231
+ ## @return [Shibkit::MetaMeta::Federation]
232
+ def to_federation
233
+
234
+ fx = self.parse_xml
235
+
236
+ federation = ::Shibkit::MetaMeta::Federation.new(fx)
237
+
238
+ ## Pass additional information from source into federation object
239
+ federation.name = name || uri
240
+ federation.display_name = display_name || name
241
+ federation.type = type
242
+ federation.refeds_url = refeds_info
243
+ federation.countries = countries
244
+ federation.languages = languages
245
+ federation.support_email = support_email
246
+ federation.homepage_url = homepage
247
+ federation.description = description
248
+ federation.groups = groups
249
+ federation.tags = tags
250
+ federation.trustiness = trustiness
251
+
252
+ #federation.from_xml(fx)
253
+
254
+ return federation
255
+
256
+ end
257
+
258
+ def groups=(group_names)
259
+
260
+ @groups ||= Array.new
261
+ @groups = [group_names].flatten.inject([]) {|m,v| m << v.to_s.downcase.to_sym }
262
+
263
+ end
264
+
265
+ def groups
266
+
267
+ return @groups
268
+
269
+ end
270
+
271
+ def tags=(tag_names)
272
+
273
+ @tags ||= Array.new
274
+ @tags = [tag_names].flatten.inject([]) {|m,v| m << v.to_s.downcase.to_sym }
275
+
276
+ end
277
+
278
+ def tags
279
+
280
+ return @tags
281
+
282
+ end
283
+
284
+ def trustiness=(level)
285
+
286
+ ## Convert strings
287
+ if level.kind_of? String
288
+
289
+ ## Percentages as strings become decimal fractions, otherwise directly converted.
290
+ level = level.match(PERCENTAGE_PATTERN) ? level.to_f / 100 : level.to_f
291
+
292
+ end
293
+
294
+ case
295
+ when level > 1
296
+ log.warn "Setting trustiness greater than 1 is ambiguous, so storing as 1. Use decimal fraction or percentage."
297
+ @trustiness = 1.0
298
+ when level < 0
299
+ log.warn "Setting trustiness less than 1 is ambiguous, so storing as 0. Use decimal fraction or percentage."
300
+ @trustiness = 0.0
301
+ else
302
+ @trustiness = level.to_f
303
+ end
304
+
305
+ return @trustiness
306
+
307
+ end
308
+
309
+ def trustiness
310
+
311
+ return @trustiness || 1.0 # TODO should be able to configure default trustiness
312
+
313
+ end
314
+
315
+ ## Redownload and revalidate remote files for the source
316
+ ## @return [TrueClass, FalseClass]
317
+ def refresh
318
+
319
+ unless selected?
320
+
321
+ log.info "Content for #{ uri} - skipping refresh as not selected."
322
+ return false
323
+
324
+ end
325
+
326
+ log.info "Content for #{ uri} is being refreshed..."
327
+
328
+ fetch_metadata
329
+ fetch_certificate
330
+
331
+ raise "Validation error" unless valid?
332
+
333
+ return true
334
+
335
+ end
336
+
337
+ ## Fetch remote file and store locally without validation
338
+ ## @return [File] Open filehandle for the local copy of metadata file
339
+ def fetch_metadata
340
+
341
+ @metadata_tmpfile = case metadata_source
342
+ when /^http/
343
+ fetch_remote(metadata_source)
344
+ else
345
+ fetch_local(metadata_source)
346
+ end
347
+
348
+ @fetched_at = Time.new
349
+
350
+ return @metadata_tmpfile
351
+
352
+ end
353
+
354
+ ## Fetch remote file and store locally
355
+ ## @return [File] open filehandle for the local copy of certificate file
356
+ def fetch_certificate
357
+
358
+ @certificate_tmpfile = case certificate_source
359
+ when /^http/
360
+ fetch_remote(certificate_source)
361
+ else
362
+ fetch_local(certificate_source)
363
+ end
364
+
365
+ return @certificate_tmpfile
366
+
367
+ end
368
+
369
+ ## Validates metadata and certificate or raises an exception
370
+ ## @return [TrueClass, FalseClass]
371
+ def validate
372
+
373
+ ## Check that XML is valid
374
+ # ...
375
+
376
+ ## Check that certificate is OK
377
+ # ...
378
+
379
+ ## Check that metadata has been signed OK, prob. Using XMLSecTool?
380
+ # ...
381
+
382
+ return true
383
+
384
+ end
385
+
386
+ ## Checks validity of metadata and certificate without raising exceptions
387
+ ## @return [TrueClass, FalseClass]
388
+ def valid?
389
+
390
+ begin
391
+ return true if validate
392
+ rescue
393
+ return false
394
+ end
395
+
396
+ end
397
+
398
+ ## Has this federation/source been selected by the user?
399
+ def selected?
400
+
401
+ ## If nothing has been specified then everything has been selected.
402
+ return true if ::Shibkit::MetaMeta.config.selected_federation_uris.empty?
403
+
404
+ ## If this source's uri is present in the list, then yup.
405
+ return true if ::Shibkit::MetaMeta.config.selected_federation_uris.include?(uri)
406
+
407
+ return false
408
+
409
+ end
410
+
411
+ ## The content of the certificate associated with the metadata
412
+ ## @return [String, nil]
413
+ def certificate_pem
414
+
415
+ ## Deal with caching locally, downloading, etc
416
+ refresh if ::Shibkit::MetaMeta.config.auto_refresh? and @certificate_tmpfile == nil
417
+
418
+ return IO.read(certificate_tmpfile.path)
419
+
420
+ end
421
+
422
+ ## Return raw source string from the file
423
+ ## @return [String] Metadata XML as text
424
+ def content
425
+
426
+ ## Deal with caching locally, downloading, etc
427
+ refresh if ::Shibkit::MetaMeta.config.auto_refresh? and @metadata_tmpfile == nil
428
+
429
+ raise "No content is available, source has not been downloaded" unless
430
+ metadata_tmpfile.path
431
+
432
+ return IO.read(metadata_tmpfile.path)
433
+
434
+ end
435
+
436
+ ## Return Nokogiri object tree for the metadata
437
+ ## @return [Nokogiri::XML::Document] Nokogiri document
438
+ def parse_xml
439
+
440
+ ## Parse the entire file as an XML document
441
+ doc = Nokogiri::XML.parse(content) do |config|
442
+ #config.strict.noent.dtdvalid
443
+ end
444
+
445
+ ## Select the top node
446
+ xml = doc.root
447
+
448
+ ## Add exotic namespaces to make sure we can deal with all metadata # TODO
449
+ NAMESPACES.each_pair { |label, uri| xml.add_namespace_definition(label,uri) }
450
+
451
+ return xml
452
+
453
+ end
454
+
455
+ def read
456
+
457
+ Nokogiri::XML::Reader(content)
458
+
459
+ end
460
+
461
+ ## Does the source object look sensible?
462
+ ## @return [TrueClass, FalseClass] True or false
463
+ def ok?
464
+
465
+ return false unless metadata_source and metadata_source.size > 1
466
+
467
+ return true
468
+
469
+ end
470
+
471
+ private
472
+
473
+
474
+ ## Logging
475
+ def log
476
+
477
+ return ::Shibkit::MetaMeta.config.logger
478
+
479
+ end
480
+
481
+
482
+ public
483
+
484
+ ##
485
+ ## Class Methods
486
+ ##
487
+
488
+ ## Load a metadata source list from a YAML file
489
+ ## @param [String] source_list Filesystem path of a sources YAML file or
490
+ ## :real for included list of real sources, :dev for mock sources, or
491
+ ## :auto for either :real or :dev, based on environment
492
+ ## @return [Array] Array of metadata source objects
493
+ def self.load(source_list=:auto, *selected_groups)
494
+
495
+ selected_groups = selected_groups.empty? ? ::Shibkit::MetaMeta.config.selected_groups : []
496
+
497
+ Shibkit::MetaMeta.log.debug "Load sources from #{source_list.to_s}"
498
+
499
+ file = self.locate_sources_file(source_list)
500
+
501
+ sources = Array.new
502
+ source_data = YAML::load(File.open(file))
503
+ Shibkit::MetaMeta.log.debug "Source YAML:\n#{source_data.inspect}"
504
+
505
+ ## Load records from the YAML-derived hash rather than directly to process them first
506
+ source_data.each_pair do |id, data|
507
+
508
+ case data
509
+ when Source
510
+ sources << data
511
+ when Hash
512
+ sources << Source.from_hash(data,id)
513
+ else
514
+ raise "Don't know how to convert #{source_data.class} to Source"
515
+ end
516
+ end
517
+
518
+ ## If groups are specified then trim off any non-matching sources
519
+ unless selected_groups.empty? or selected_groups.include? :all
520
+
521
+ Shibkit::MetaMeta.log.info "Filtering source/federations by selected groups"
522
+
523
+ selected_groups.inject([]){|m,v| m << v.to_s.downcase.to_sym }
524
+
525
+ group_set = Set.new selected_groups
526
+ sources = sources.delete_if { |s| group_set.intersection(s.groups).empty? }
527
+
528
+ Shibkit::MetaMeta.log.debug "Rejected sources that aren't in #{selected_groups.join(', ')}"
529
+
530
+ end
531
+
532
+ return sources
533
+
534
+ end
535
+
536
+ ## Return appropriate file path for
537
+ def self.locate_sources_file(source_list)
538
+
539
+ config = ::Shibkit::MetaMeta.config
540
+
541
+ case source_list
542
+ when :auto
543
+ file_path = config.in_production? ? REAL_SOURCES_FILE : DEV_SOURCES_FILE
544
+ when :dev, :test
545
+ file_path = DEV_SOURCES_FILE
546
+ when :real, :prod, :production
547
+ file_path = REAL_SOURCES_FILE
548
+ else
549
+ file_path = source_list
550
+ end
551
+
552
+ return file_path
553
+
554
+ end
555
+
556
+ end
557
+ end
558
+ end