ruby-openid 2.0.4 → 2.1.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.

Potentially problematic release.


This version of ruby-openid might be problematic. Click here for more details.

Files changed (58) hide show
  1. data/CHANGELOG +65 -28
  2. data/LICENSE +4 -1
  3. data/README +19 -12
  4. data/UPGRADE +5 -0
  5. data/examples/README +8 -22
  6. data/examples/active_record_openid_store/XXX_add_open_id_store_to_db.rb +6 -6
  7. data/examples/active_record_openid_store/lib/association.rb +2 -1
  8. data/examples/active_record_openid_store/lib/openid_ar_store.rb +3 -3
  9. data/examples/rails_openid/app/controllers/consumer_controller.rb +11 -5
  10. data/lib/openid.rb +4 -0
  11. data/lib/openid/association.rb +7 -7
  12. data/lib/openid/consumer/checkid_request.rb +11 -0
  13. data/lib/openid/consumer/discovery.rb +12 -3
  14. data/lib/openid/consumer/idres.rb +35 -43
  15. data/lib/openid/extension.rb +9 -1
  16. data/lib/openid/extensions/pape.rb +22 -25
  17. data/lib/openid/extensions/sreg.rb +1 -0
  18. data/lib/openid/fetchers.rb +25 -5
  19. data/lib/openid/kvform.rb +8 -5
  20. data/lib/openid/kvpost.rb +6 -5
  21. data/lib/openid/message.rb +53 -34
  22. data/lib/openid/server.rb +87 -52
  23. data/lib/openid/trustroot.rb +25 -17
  24. data/lib/openid/util.rb +19 -4
  25. data/lib/openid/yadis/discovery.rb +3 -3
  26. data/lib/openid/yadis/htmltokenizer.rb +8 -5
  27. data/lib/openid/yadis/parsehtml.rb +22 -14
  28. data/lib/openid/yadis/xrds.rb +6 -9
  29. data/test/data/linkparse.txt +1 -1
  30. data/test/data/test1-parsehtml.txt +24 -0
  31. data/test/data/trustroot.txt +8 -2
  32. data/test/test_association.rb +7 -7
  33. data/test/test_associationmanager.rb +1 -1
  34. data/test/test_extension.rb +46 -0
  35. data/test/test_idres.rb +81 -21
  36. data/test/test_kvform.rb +5 -5
  37. data/test/test_message.rb +61 -3
  38. data/test/test_pape.rb +36 -22
  39. data/test/test_server.rb +190 -12
  40. data/test/test_sreg.rb +0 -1
  41. data/test/test_trustroot.rb +1 -0
  42. data/test/test_yadis_discovery.rb +13 -0
  43. metadata +3 -19
  44. data/examples/rails_openid/app/views/consumer/start.rhtml +0 -8
  45. data/examples/rails_openid_login_generator/USAGE +0 -23
  46. data/examples/rails_openid_login_generator/gemspec +0 -13
  47. data/examples/rails_openid_login_generator/openid_login_generator.rb +0 -36
  48. data/examples/rails_openid_login_generator/templates/README +0 -116
  49. data/examples/rails_openid_login_generator/templates/controller.rb +0 -113
  50. data/examples/rails_openid_login_generator/templates/controller_test.rb +0 -0
  51. data/examples/rails_openid_login_generator/templates/helper.rb +0 -2
  52. data/examples/rails_openid_login_generator/templates/openid_login_system.rb +0 -87
  53. data/examples/rails_openid_login_generator/templates/user.rb +0 -14
  54. data/examples/rails_openid_login_generator/templates/user_test.rb +0 -0
  55. data/examples/rails_openid_login_generator/templates/users.yml +0 -0
  56. data/examples/rails_openid_login_generator/templates/view_login.rhtml +0 -15
  57. data/examples/rails_openid_login_generator/templates/view_logout.rhtml +0 -10
  58. data/examples/rails_openid_login_generator/templates/view_welcome.rhtml +0 -9
@@ -19,9 +19,17 @@ module OpenID
19
19
  # message, or create a new message containing only those
20
20
  # arguments. Returns the message with added extension args.
21
21
  def to_message(message = nil)
22
+ if message.nil?
23
+ # warnings.warn('Passing None to Extension.toMessage is deprecated. '
24
+ # 'Creating a message assuming you want OpenID 2.',
25
+ # DeprecationWarning, stacklevel=2)
26
+ Message.new(OPENID2_NS)
27
+ end
22
28
  message = Message.new if message.nil?
23
29
 
24
- message.namespaces.add_alias(@ns_uri, @ns_alias)
30
+ implicit = message.is_openid1()
31
+
32
+ message.namespaces.add_alias(@ns_uri, @ns_alias, implicit)
25
33
  # XXX python ignores keyerror if m.ns.getAlias(uri) == alias
26
34
 
27
35
  message.update_args(@ns_uri, get_extension_args)
@@ -14,7 +14,7 @@ module OpenID
14
14
  'http://schemas.openid.net/pape/policies/2007/06/multi-factor'
15
15
  AUTH_PHISHING_RESISTANT =
16
16
  'http://schemas.openid.net/pape/policies/2007/06/phishing-resistant'
17
-
17
+ TIME_VALIDATOR = /\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ/
18
18
  # A Provider Authentication Policy request, sent from a relying
19
19
  # party to a provider
20
20
  class Request < Extension
@@ -86,12 +86,12 @@ module OpenID
86
86
  # A Provider Authentication Policy response, sent from a provider
87
87
  # to a relying party
88
88
  class Response < Extension
89
- attr_accessor :ns_alias, :auth_policies, :auth_age, :nist_auth_level
90
- def initialize(auth_policies=[], auth_age=nil, nist_auth_level=nil)
89
+ attr_accessor :ns_alias, :auth_policies, :auth_time, :nist_auth_level
90
+ def initialize(auth_policies=[], auth_time=nil, nist_auth_level=nil)
91
91
  @ns_alias = 'pape'
92
92
  @ns_uri = NS_URI
93
93
  @auth_policies = auth_policies
94
- @auth_age = auth_age
94
+ @auth_time = auth_time
95
95
  @nist_auth_level = nist_auth_level
96
96
  end
97
97
 
@@ -104,6 +104,7 @@ module OpenID
104
104
  # Create a Response object from an OpenID::Consumer::SuccessResponse
105
105
  def self.from_success_response(success_response)
106
106
  args = success_response.get_signed_ns(NS_URI)
107
+ return nil if args.nil?
107
108
  pape_resp = new
108
109
  pape_resp.parse_extension_args(args)
109
110
  return pape_resp
@@ -115,7 +116,7 @@ module OpenID
115
116
  # encountered
116
117
  def parse_extension_args(args, strict=false)
117
118
  policies_str = args['auth_policies']
118
- if policies_str
119
+ if policies_str and policies_str != 'none'
119
120
  @auth_policies = policies_str.split(' ')
120
121
  end
121
122
 
@@ -138,28 +139,24 @@ module OpenID
138
139
  end
139
140
  end
140
141
 
141
- auth_age_str = args['auth_age']
142
- if auth_age_str
143
- # special handling of zero to handle to_i behavior
144
- if auth_age_str.strip == '0'
145
- auth_age = 0
146
- else
147
- auth_age = auth_age_str.to_i
148
- # if it's zero here we have a bad value
149
- if auth_age == 0
150
- auth_age = nil
151
- end
152
- end
153
- if auth_age and auth_age >= 0
154
- @auth_age = auth_age
142
+ auth_time_str = args['auth_time']
143
+ if auth_time_str
144
+ # validate time string
145
+ if auth_time_str =~ TIME_VALIDATOR
146
+ @auth_time = auth_time_str
155
147
  elsif strict
156
- raise ArgumentError, "auth_age must be a non-negative integer, not #{auth_age_str.inspect}"
148
+ raise ArgumentError, "auth_time must be in RFC3339 format"
157
149
  end
158
150
  end
159
151
  end
160
152
 
161
153
  def get_extension_args
162
- ns_args = {'auth_policies' => @auth_policies.join(' ')}
154
+ ns_args = {}
155
+ if @auth_policies.empty?
156
+ ns_args['auth_policies'] = 'none'
157
+ else
158
+ ns_args['auth_policies'] = @auth_policies.join(' ')
159
+ end
163
160
  if @nist_auth_level
164
161
  unless (0..4).member? @nist_auth_level
165
162
  raise ArgumentError, "nist_auth_level must be an integer 0 through 4, not #{@nist_auth_level.inspect}"
@@ -167,11 +164,11 @@ module OpenID
167
164
  ns_args['nist_auth_level'] = @nist_auth_level.to_s
168
165
  end
169
166
 
170
- if @auth_age
171
- if @auth_age < 0
172
- raise ArgumentError, "auth_age must be a non-negative integer, not #{@auth_age.inspect}"
167
+ if @auth_time
168
+ unless @auth_time =~ TIME_VALIDATOR
169
+ raise ArgumentError, "auth_time must be in RFC3339 format"
173
170
  end
174
- ns_args['auth_age'] = @auth_age.to_s
171
+ ns_args['auth_time'] = @auth_time
175
172
  end
176
173
  return ns_args
177
174
  end
@@ -246,6 +246,7 @@ module OpenID
246
246
  ns_uri = OpenID::get_sreg_ns(success_response.message)
247
247
  if signed_only
248
248
  args = success_response.get_signed_ns(ns_uri)
249
+ return nil if args.nil? # No signed args, so fail
249
250
  else
250
251
  args = success_response.message.get_args(ns_uri)
251
252
  end
@@ -10,6 +10,8 @@ rescue LoadError
10
10
  require 'net/http'
11
11
  end
12
12
 
13
+ MAX_RESPONSE_KB = 1024
14
+
13
15
  module Net
14
16
  class HTTP
15
17
  def post_connection_check(hostname)
@@ -115,14 +117,17 @@ module OpenID
115
117
  USER_AGENT = "ruby-openid/#{OpenID::VERSION} (#{RUBY_PLATFORM})"
116
118
 
117
119
  REDIRECT_LIMIT = 5
120
+ TIMEOUT = 60
118
121
 
119
122
  attr_accessor :ca_file
123
+ attr_accessor :timeout
120
124
 
121
125
  # I can fetch through a HTTP proxy; arguments are as for Net::HTTP::Proxy.
122
126
  def initialize(proxy_addr=nil, proxy_port=nil,
123
127
  proxy_user=nil, proxy_pass=nil)
124
128
  @ca_file = nil
125
129
  @proxy = Net::HTTP::Proxy(proxy_addr, proxy_port, proxy_user, proxy_pass)
130
+ @timeout = TIMEOUT
126
131
  end
127
132
 
128
133
  def supports_ssl?(conn)
@@ -130,7 +135,10 @@ module OpenID
130
135
  end
131
136
 
132
137
  def make_http(uri)
133
- @proxy.new(uri.host, uri.port)
138
+ http = @proxy.new(uri.host, uri.port)
139
+ http.read_timeout = @timeout
140
+ http.open_timeout = @timeout
141
+ return http
134
142
  end
135
143
 
136
144
  def set_verified(conn, verify)
@@ -173,14 +181,18 @@ module OpenID
173
181
  def fetch(url, body=nil, headers=nil, redirect_limit=REDIRECT_LIMIT)
174
182
  unparsed_url = url.dup
175
183
  url = URI::parse(url)
184
+ if url.nil?
185
+ raise FetchingError, "Invalid URL: #{unparsed_url}"
186
+ end
176
187
 
177
188
  headers ||= {}
178
189
  headers['User-agent'] ||= USER_AGENT
179
-
180
- conn = make_connection(url)
181
- response = nil
190
+ headers['Range'] ||= "0-#{MAX_RESPONSE_KB*1024}"
182
191
 
183
192
  begin
193
+ conn = make_connection(url)
194
+ response = nil
195
+
184
196
  response = conn.start {
185
197
  # Check the certificate against the URL's hostname
186
198
  if supports_ssl?(conn) and conn.use_ssl?
@@ -194,6 +206,8 @@ module OpenID
194
206
  conn.request_post(url.request_uri, body, headers)
195
207
  end
196
208
  }
209
+ rescue RuntimeError => why
210
+ raise why
197
211
  rescue OpenSSL::SSL::SSLError => why
198
212
  raise SSLFetchingError, "Error connecting to SSL URL #{url}: #{why}"
199
213
  rescue FetchingError => why
@@ -210,7 +224,13 @@ module OpenID
210
224
  raise HTTPRedirectLimitReached.new(
211
225
  "Too many redirects, not fetching #{response['location']}")
212
226
  end
213
- return fetch(response['location'], body, headers, redirect_limit - 1)
227
+ begin
228
+ return fetch(response['location'], body, headers, redirect_limit - 1)
229
+ rescue HTTPRedirectLimitReached => e
230
+ raise e
231
+ rescue FetchingError => why
232
+ raise FetchingError, "Error encountered in redirect from #{url}: #{why}"
233
+ end
214
234
  else
215
235
  return HTTPResponse._from_net_response(response, unparsed_url)
216
236
  end
data/lib/openid/kvform.rb CHANGED
@@ -1,6 +1,9 @@
1
1
 
2
2
  module OpenID
3
3
 
4
+ class KVFormError < Exception
5
+ end
6
+
4
7
  module Util
5
8
 
6
9
  def Util.seq_to_kv(seq, strict=false)
@@ -13,7 +16,7 @@ module OpenID
13
16
  err = lambda { |msg|
14
17
  msg = "seq_to_kv warning: #{msg}: #{seq.inspect}"
15
18
  if strict
16
- raise ArgumentError, msg
19
+ raise KVFormError, msg
17
20
  else
18
21
  Util.log(msg)
19
22
  end
@@ -27,11 +30,11 @@ module OpenID
27
30
  end
28
31
 
29
32
  if !k.index("\n").nil?
30
- raise ArgumentError, "Invalid input for seq_to_kv: key contains newline: #{k.inspect}"
33
+ raise KVFormError, "Invalid input for seq_to_kv: key contains newline: #{k.inspect}"
31
34
  end
32
35
 
33
36
  if !k.index(":").nil?
34
- raise ArgumentError, "Invalid input for seq_to_kv: key contains colon: #{k.inspect}"
37
+ raise KVFormError, "Invalid input for seq_to_kv: key contains colon: #{k.inspect}"
35
38
  end
36
39
 
37
40
  if k.strip() != k
@@ -44,7 +47,7 @@ module OpenID
44
47
  end
45
48
 
46
49
  if !v.index("\n").nil?
47
- raise ArgumentError, "Invalid input for seq_to_kv: value contains newline: #{v.inspect}"
50
+ raise KVFormError, "Invalid input for seq_to_kv: value contains newline: #{v.inspect}"
48
51
  end
49
52
 
50
53
  if v.strip() != v
@@ -66,7 +69,7 @@ module OpenID
66
69
  err = lambda { |msg|
67
70
  msg = "kv_to_seq warning: #{msg}: #{data.inspect}"
68
71
  if strict
69
- raise ArgumentError, msg
72
+ raise KVFormError, msg
70
73
  else
71
74
  Util.log(msg)
72
75
  end
data/lib/openid/kvpost.rb CHANGED
@@ -7,19 +7,18 @@ module OpenID
7
7
  class ServerError < OpenIDError
8
8
  attr_reader :error_text, :error_code, :message
9
9
 
10
- def initialize(error_text, error_code, message, server_url)
10
+ def initialize(error_text, error_code, message)
11
11
  super(error_text)
12
12
  @error_text = error_text
13
13
  @error_code = error_code
14
- @server_url = server_url
15
14
  @message = message
16
15
  end
17
16
 
18
- def self.from_message(msg, server_url)
17
+ def self.from_message(msg)
19
18
  error_text = msg.get_arg(OPENID_NS, 'error',
20
19
  '<no error message supplied>')
21
20
  error_code = msg.get_arg(OPENID_NS, 'error_code')
22
- return self.new(error_text, error_code, msg, server_url)
21
+ return self.new(error_text, error_code, msg)
23
22
  end
24
23
  end
25
24
 
@@ -34,8 +33,10 @@ module OpenID
34
33
  case response.code.to_i
35
34
  when 200
36
35
  return msg
36
+ when 206
37
+ return msg
37
38
  when 400
38
- raise ServerError.from_message(msg, server_url)
39
+ raise ServerError.from_message(msg)
39
40
  else
40
41
  error_message = "bad status code from server #{server_url}: "\
41
42
  "#{response.code}"
@@ -9,8 +9,10 @@ module OpenID
9
9
  # OpenID 1.x extension, and so a special case.
10
10
  SREG_URI = 'http://openid.net/sreg/1.0'
11
11
 
12
- # The OpenID 1.x namespace URI
12
+ # The OpenID 1.x namespace URIs
13
13
  OPENID1_NS = 'http://openid.net/signon/1.0'
14
+ OPENID11_NS = 'http://openid.net/signon/1.1'
15
+ OPENID1_NAMESPACES = [OPENID1_NS, OPENID11_NS]
14
16
 
15
17
  # The OpenID 2.0 namespace URI
16
18
  OPENID2_NS = 'http://specs.openid.net/auth/2.0'
@@ -54,6 +56,10 @@ module OpenID
54
56
  # Raised when an alias or namespace URI has already been registered.
55
57
  class NamespaceAliasRegistrationError < Exception; end
56
58
 
59
+ # Raised if openid.ns is not a recognized value.
60
+ # See Message class variable @@allowed_openid_namespaces
61
+ class InvalidOpenIDNamespace < Exception; end
62
+
57
63
  class Message
58
64
  attr_reader :namespaces
59
65
 
@@ -87,19 +93,24 @@ module OpenID
87
93
  @@registered_aliases[alias_] = namespace_uri
88
94
  end
89
95
 
90
- @@allowed_openid_namespaces = [OPENID1_NS, OPENID2_NS]
96
+ @@allowed_openid_namespaces = [OPENID1_NS, OPENID2_NS, OPENID11_NS]
91
97
 
92
- def initialize(openid_namspace=nil)
98
+ # Raises InvalidNamespaceError if you try to instantiate a Message
99
+ # with a namespace not in the above allowed list
100
+ def initialize(openid_namespace=nil)
93
101
  @args = {}
94
102
  @namespaces = NamespaceMap.new
95
- if openid_namspace
96
- self.set_openid_namespace(openid_namspace)
103
+ if openid_namespace
104
+ implicit = OPENID1_NAMESPACES.member? openid_namespace
105
+ self.set_openid_namespace(openid_namespace, implicit)
97
106
  else
98
107
  @openid_ns_uri = nil
99
108
  end
100
109
  end
101
110
 
102
111
  # Construct a Message containing a set of POST arguments.
112
+ # Raises InvalidNamespaceError if you try to instantiate a Message
113
+ # with a namespace not in the above allowed list
103
114
  def Message.from_post_args(args)
104
115
  m = Message.new
105
116
  openid_args = {}
@@ -123,12 +134,16 @@ module OpenID
123
134
  end
124
135
 
125
136
  # Construct a Message from a parsed KVForm message.
137
+ # Raises InvalidNamespaceError if you try to instantiate a Message
138
+ # with a namespace not in the above allowed list
126
139
  def Message.from_openid_args(openid_args)
127
140
  m = Message.new
128
141
  m._from_openid_args(openid_args)
129
142
  return m
130
143
  end
131
144
 
145
+ # Raises InvalidNamespaceError if you try to instantiate a Message
146
+ # with a namespace not in the above allowed list
132
147
  def _from_openid_args(openid_args)
133
148
  ns_args = []
134
149
 
@@ -143,47 +158,46 @@ module OpenID
143
158
  if ns_alias == 'ns'
144
159
  @namespaces.add_alias(value, ns_key)
145
160
  elsif ns_alias == NULL_NAMESPACE and ns_key == 'ns'
146
- @namespaces.add_alias(value, NULL_NAMESPACE)
161
+ set_openid_namespace(value, false)
147
162
  else
148
163
  ns_args << [ns_alias, ns_key, value]
149
164
  end
150
165
  }
151
166
 
152
- # ensure that there is an OpenID namespace definition
153
- openid_ns_uri = @namespaces.get_namespace_uri(NULL_NAMESPACE)
154
- openid_ns_uri = OPENID1_NS unless openid_ns_uri
155
-
156
- self.set_openid_namespace(openid_ns_uri)
167
+ # implicitly set an OpenID 1 namespace
168
+ unless get_openid_namespace
169
+ set_openid_namespace(OPENID1_NS, true)
170
+ end
157
171
 
158
172
  # put the pairs into the appropriate namespaces
159
173
  ns_args.each { |ns_alias, ns_key, value|
160
174
  ns_uri = @namespaces.get_namespace_uri(ns_alias)
161
175
  unless ns_uri
162
- # only try to map an alias to a default if it's an
163
- # OpenID 1.x namespace
164
- @@registered_aliases.each { |_alias, _uri|
165
- if _alias == ns_alias
166
- ns_uri = _uri
167
- break
168
- end
169
- }
170
-
176
+ ns_uri = _get_default_namespace(ns_alias)
171
177
  unless ns_uri
172
- ns_uri = openid_ns_uri
178
+ ns_uri = get_openid_namespace
173
179
  ns_key = "#{ns_alias}.#{ns_key}"
174
180
  else
175
- @namespaces.add_alias(ns_uri, ns_alias)
181
+ @namespaces.add_alias(ns_uri, ns_alias, true)
176
182
  end
177
183
  end
178
184
  self.set_arg(ns_uri, ns_key, value)
179
185
  }
180
186
  end
181
187
 
182
- def set_openid_namespace(openid_ns_uri)
188
+ def _get_default_namespace(mystery_alias)
189
+ # only try to map an alias to a default if it's an
190
+ # OpenID 1.x namespace
191
+ if is_openid1
192
+ @@registered_aliases[mystery_alias]
193
+ end
194
+ end
195
+
196
+ def set_openid_namespace(openid_ns_uri, implicit)
183
197
  if !@@allowed_openid_namespaces.include?(openid_ns_uri)
184
- raise ArgumentError, "Invalid null namespace: #{openid_ns_uri}"
198
+ raise InvalidOpenIDNamespace, "Invalid null namespace: #{openid_ns_uri}"
185
199
  end
186
- @namespaces.add_alias(openid_ns_uri, NULL_NAMESPACE)
200
+ @namespaces.add_alias(openid_ns_uri, NULL_NAMESPACE, implicit)
187
201
  @openid_ns_uri = openid_ns_uri
188
202
  end
189
203
 
@@ -192,7 +206,7 @@ module OpenID
192
206
  end
193
207
 
194
208
  def is_openid1
195
- return @openid_ns_uri == OPENID1_NS
209
+ return OPENID1_NAMESPACES.member?(@openid_ns_uri)
196
210
  end
197
211
 
198
212
  def is_openid2
@@ -214,16 +228,15 @@ module OpenID
214
228
 
215
229
  # add namespace defs to the output
216
230
  @namespaces.each { |ns_uri, ns_alias|
231
+ if @namespaces.implicit?(ns_uri)
232
+ next
233
+ end
217
234
  if ns_alias == NULL_NAMESPACE
218
- if ns_uri != OPENID1_NS
219
- args['openid.ns'] = ns_uri
220
- end
235
+ ns_key = 'openid.ns'
221
236
  else
222
- if get_openid_namespace != OPENID1_NS
223
- ns_key = 'openid.ns.' + ns_alias
224
- args[ns_key] = ns_uri
225
- end
237
+ ns_key = 'openid.ns.' + ns_alias
226
238
  end
239
+ args[ns_key] = ns_uri
227
240
  }
228
241
 
229
242
  @args.each { |k, value|
@@ -447,6 +460,7 @@ module OpenID
447
460
  def initialize
448
461
  @alias_to_namespace = {}
449
462
  @namespace_to_alias = {}
463
+ @implicit_namespaces = []
450
464
  end
451
465
 
452
466
  def get_alias(namespace_uri)
@@ -458,7 +472,7 @@ module OpenID
458
472
  end
459
473
 
460
474
  # Add an alias from this namespace URI to the alias.
461
- def add_alias(namespace_uri, desired_alias)
475
+ def add_alias(namespace_uri, desired_alias, implicit=false)
462
476
  # Check that desired_alias is not an openid protocol field as
463
477
  # per the spec.
464
478
  Util.assert(!OPENID_PROTOCOL_FIELDS.include?(desired_alias),
@@ -487,6 +501,7 @@ module OpenID
487
501
 
488
502
  @alias_to_namespace[desired_alias] = namespace_uri
489
503
  @namespace_to_alias[namespace_uri] = desired_alias
504
+ @implicit_namespaces << namespace_uri if implicit
490
505
  return desired_alias
491
506
  end
492
507
 
@@ -526,6 +541,10 @@ module OpenID
526
541
  return @namespace_to_alias.keys()
527
542
  end
528
543
 
544
+ def implicit?(namespace_uri)
545
+ return @implicit_namespaces.member?(namespace_uri)
546
+ end
547
+
529
548
  def aliases
530
549
  # Return an iterator over the aliases
531
550
  return @alias_to_namespace.keys()