saml2 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 90af41e6460d5aa014d4b54d4e597868d005e26b
4
- data.tar.gz: 00833d534d9a8e97c08a78d52becb64b70b82aab
3
+ metadata.gz: a3df3b121cbc505cf89bd09bf2551c4bf169522e
4
+ data.tar.gz: bee5074779df61a8d6decfc85b1137f407f3bafb
5
5
  SHA512:
6
- metadata.gz: 88afb82849c17a10a3b2d0bb580cec358bc2cbdbb26ddb4b0c16d0d236aca871be81b514c6b86b6cb35163fa91b44a13e011df7f1c16d902371ce88657463406
7
- data.tar.gz: 2f7b4cb6fef7079a6dca04a8051e0f7921199962d50ab24123d439e8fa179dcb5903875173f6b791d3c13fb7268d237b323682a4017f6d43491fd2b6e8b387bf
6
+ metadata.gz: f960b992502f20a9a518b53a34e7c50d5d3fae14452d51704f1eb36308accf5f3a48e3f5810d35a31bb3a64cb605ba2a5a49a18e6d01d8f36037a467ab36bf0a
7
+ data.tar.gz: 050953212fed3515aa2a329b24e1adcf1fc7f57bed49d86e700db5d0e47bae8f6c6cc346e1cb02bae366302bcb5c1ce414e32fc7d97adb6b1ad49ee7205a9a3f
@@ -1,23 +1,10 @@
1
+ require 'date'
2
+
1
3
  require 'saml2/base'
2
4
  require 'saml2/namespaces'
3
5
 
4
6
  module SAML2
5
- class AttributeType < Base
6
- attr_accessor :name, :friendly_name, :name_format
7
-
8
- def initialize(name = nil, friendly_name = nil, name_format = nil)
9
- @name, @friendly_name, @name_format = name, friendly_name, name_format
10
- end
11
-
12
- def from_xml(node)
13
- @name = node['Name']
14
- @friendly_name = node['FriendlyName']
15
- @name_format = node['NameFormat']
16
- self
17
- end
18
- end
19
-
20
- class Attribute < AttributeType
7
+ class Attribute < Base
21
8
  module NameFormats
22
9
  BASIC = "urn:oasis:names:tc:SAML:2.0:attrname-format:basic".freeze
23
10
  UNSPECIFIED = "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified".freeze
@@ -38,25 +25,28 @@ module SAML2
38
25
  super unless self == Attribute
39
26
 
40
27
  # look for an appropriate subclass
41
- klass = subclasses.find { |klass| klass.recognizes?(node) }
42
- if klass
43
- klass.from_xml(node)
44
- else
45
- super
46
- end
28
+ klass = class_for(node)
29
+ klass ? klass.from_xml(node) : super
47
30
  end
48
31
 
49
32
  def create(name, value = nil)
50
- klass = subclasses.find { |klass| klass.recognizes?(name) } || self
51
- klass.new(name, value)
33
+
34
+ (class_for(name) || self).new(name, value)
35
+ end
36
+
37
+ protected
38
+
39
+ def class_for(name_or_node)
40
+ subclasses.find do |klass|
41
+ klass.respond_to?(:recognizes?) && klass.recognizes?(name_or_node)
42
+ end
52
43
  end
53
44
  end
54
45
 
55
- attr_accessor :value
46
+ attr_accessor :name, :friendly_name, :name_format, :value
56
47
 
57
48
  def initialize(name = nil, value = nil, friendly_name = nil, name_format = nil)
58
- super(name, friendly_name, name_format)
59
- @value = value
49
+ @name, @value, @friendly_name, @name_format = name, value, friendly_name, name_format
60
50
  end
61
51
 
62
52
  def build(builder)
@@ -73,10 +63,17 @@ module SAML2
73
63
  end
74
64
 
75
65
  def from_xml(node)
76
- @value = node.xpath('saml:AttributeValue', Namespaces::ALL).map do |node|
66
+ @name = node['Name']
67
+ @friendly_name = node['FriendlyName']
68
+ @name_format = node['NameFormat']
69
+ values = node.xpath('saml:AttributeValue', Namespaces::ALL).map do |node|
77
70
  convert_from_xsi(node['xsi:type'], node.content && node.content.strip)
78
71
  end
79
- @value = @value.first if @value.length == 1
72
+ @value = case values.length
73
+ when 0; nil
74
+ when 1; values.first
75
+ else; values
76
+ end
80
77
  super
81
78
  end
82
79
 
@@ -3,9 +3,9 @@ require 'saml2/indexed_object'
3
3
  require 'saml2/namespaces'
4
4
 
5
5
  module SAML2
6
- class RequestedAttribute < AttributeType
6
+ class RequestedAttribute < Attribute
7
7
  def initialize(name = nil, is_required = nil, name_format = nil)
8
- super(name, name_format)
8
+ super(name, nil, nil, name_format)
9
9
  @is_required = is_required
10
10
  end
11
11
 
@@ -28,6 +28,17 @@ module SAML2
28
28
  end
29
29
  end
30
30
 
31
+ class InvalidAttributeValue < RuntimeError
32
+ attr_reader :requested_attribute, :provided_value
33
+
34
+ def initialize(requested_attribute, provided_value)
35
+ super("Attribute #{requested_attribute.name} is provided value " \
36
+ "#{provided_value.inspect}, but only allows " \
37
+ "#{Array(requested_attribute.value).inspect}")
38
+ @requested_attribute, @provided_value = requested_attribute, provided_value
39
+ end
40
+ end
41
+
31
42
  class AttributeConsumingService < Base
32
43
  include IndexedObject
33
44
 
@@ -64,9 +75,20 @@ module SAML2
64
75
  attr ||= attributes_hash[[requested_attr.name, nil]]
65
76
  end
66
77
  if attr
78
+ if requested_attr.value &&
79
+ !Array(requested_attr.value).include?(attr.value)
80
+ raise InvalidAttributeValue.new(requested_attr, attr.value)
81
+ end
67
82
  attributes << attr
68
83
  elsif requested_attr.required?
69
- raise RequiredAttributeMissing.new(requested_attr)
84
+ # if the metadata includes only one possible value, helpfully set
85
+ # that value
86
+ if requested_attr.value && !requested_attr.value.is_a?(::Array)
87
+ attributes << Attribute.create(requested_attr.name,
88
+ requested_attr.value)
89
+ else
90
+ raise RequiredAttributeMissing.new(requested_attr)
91
+ end
70
92
  end
71
93
  end
72
94
  return nil if attributes.empty?
@@ -1,3 +1,3 @@
1
1
  module SAML2
2
- VERSION = '1.0.3'
2
+ VERSION = '1.0.4'
3
3
  end
@@ -69,6 +69,59 @@ module SAML2
69
69
  stmt.attributes.first.name.must_equal 'name'
70
70
  stmt.attributes.first.value.must_equal 'cody'
71
71
  end
72
+
73
+ it "requires that provided attributes match a single default" do
74
+ acs.requested_attributes.clear
75
+ attr = RequestedAttribute.new('attr')
76
+ attr.value = 'value'
77
+ acs.requested_attributes << attr
78
+ -> { acs.create_statement('attr' => 'something') }.must_raise InvalidAttributeValue
79
+ stmt = acs.create_statement('attr' => 'value')
80
+ stmt.attributes.length.must_equal 1
81
+ stmt.attributes.first.name.must_equal 'attr'
82
+ stmt.attributes.first.value.must_equal 'value'
83
+ end
84
+
85
+ it "requires that provided attributes be from allowed enumeration" do
86
+ acs.requested_attributes.clear
87
+ attr = RequestedAttribute.new('attr')
88
+ attr.value = ['value1', 'value2']
89
+ acs.requested_attributes << attr
90
+ -> { acs.create_statement('attr' => 'something') }.must_raise InvalidAttributeValue
91
+ stmt = acs.create_statement('attr' => 'value1')
92
+ stmt.attributes.length.must_equal 1
93
+ stmt.attributes.first.name.must_equal 'attr'
94
+ stmt.attributes.first.value.must_equal 'value1'
95
+ end
96
+
97
+ it "auto-provides missing required attribute with a default" do
98
+ acs.requested_attributes.clear
99
+ attr = RequestedAttribute.new('attr', true)
100
+ attr.value = 'value'
101
+ acs.requested_attributes << attr
102
+ stmt = acs.create_statement({})
103
+ stmt.attributes.length.must_equal 1
104
+ stmt.attributes.first.name.must_equal 'attr'
105
+ stmt.attributes.first.value.must_equal 'value'
106
+ end
107
+
108
+ it "doesn't auto-provide missing required attribute with an enumeration" do
109
+ acs.requested_attributes.clear
110
+ attr = RequestedAttribute.new('attr', true)
111
+ attr.value = ['value1', 'value2']
112
+ acs.requested_attributes << attr
113
+ -> { acs.create_statement({}) }.must_raise RequiredAttributeMissing
114
+ end
115
+
116
+ it "doesn't auto-provide missing non-required attribute with a default" do
117
+ acs.requested_attributes.clear
118
+ attr = RequestedAttribute.new('attr')
119
+ attr.value = 'value'
120
+ acs.requested_attributes << attr
121
+ stmt = acs.create_statement({})
122
+ stmt.must_equal nil
123
+ end
124
+
72
125
  end
73
126
  end
74
127
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: saml2
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cody Cutrer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-25 00:00:00.000000000 Z
11
+ date: 2015-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri