File: sreg.rb

package info (click to toggle)
ruby-openid 2.5.0debian-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 1,980 kB
  • ctags: 2,219
  • sloc: ruby: 16,737; xml: 219; sh: 24; makefile: 2
file content (277 lines) | stat: -rw-r--r-- 9,598 bytes parent folder | download | duplicates (8)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
require 'openid/extension'
require 'openid/util'
require 'openid/message'

module OpenID
  module SReg
    DATA_FIELDS = {
      'fullname'=>'Full Name',
      'nickname'=>'Nickname',
      'dob'=>'Date of Birth',
      'email'=>'E-mail Address',
      'gender'=>'Gender',
      'postcode'=>'Postal Code',
      'country'=>'Country',
      'language'=>'Language',
      'timezone'=>'Time Zone',
    }

    NS_URI_1_0 = 'http://openid.net/sreg/1.0'
    NS_URI_1_1 = 'http://openid.net/extensions/sreg/1.1'
    NS_URI = NS_URI_1_1

    begin
      Message.register_namespace_alias(NS_URI_1_1, 'sreg')
    rescue NamespaceAliasRegistrationError => e
      Util.log(e)
    end

    # raise ArgumentError if fieldname is not in the defined sreg fields
    def OpenID.check_sreg_field_name(fieldname)
      unless DATA_FIELDS.member? fieldname
        raise ArgumentError, "#{fieldname} is not a defined simple registration field"
      end
    end

    # Does the given endpoint advertise support for simple registration?
    def OpenID.supports_sreg?(endpoint)
      endpoint.uses_extension(NS_URI_1_1) || endpoint.uses_extension(NS_URI_1_0)
    end

    # Extract the simple registration namespace URI from the given
    # OpenID message. Handles OpenID 1 and 2, as well as both sreg
    # namespace URIs found in the wild, as well as missing namespace
    # definitions (for OpenID 1)
    def OpenID.get_sreg_ns(message)
      [NS_URI_1_1, NS_URI_1_0].each{|ns|
        if message.namespaces.get_alias(ns)
          return ns
        end
      }
      # try to add an alias, since we didn't find one
      ns = NS_URI_1_1
      begin
        message.namespaces.add_alias(ns, 'sreg')
      rescue IndexError
        raise NamespaceError
      end
      return ns
    end

    # The simple registration namespace was not found and could not
    # be created using the expected name (there's another extension
    # using the name 'sreg')
    #
    # This is not <em>illegal</em>, for OpenID 2, although it probably
    # indicates a problem, since it's not expected that other extensions
    # will re-use the alias that is in use for OpenID 1.
    #
    # If this is an OpenID 1 request, then there is no recourse. This
    # should not happen unless some code has modified the namespaces for
    # the message that is being processed.
    class NamespaceError < ArgumentError
    end

    # An object to hold the state of a simple registration request.
    class Request < Extension
      attr_reader :optional, :required, :ns_uri
      attr_accessor :policy_url
      def initialize(required = nil, optional = nil, policy_url = nil, ns_uri = NS_URI)
        super()

        @policy_url = policy_url
        @ns_uri = ns_uri
        @ns_alias = 'sreg'
        @required = []
        @optional = []

        if required
          request_fields(required, true, true)
        end
        if optional
          request_fields(optional, false, true)
        end
      end

      # Create a simple registration request that contains the
      # fields that were requested in the OpenID request with the
      # given arguments
      # Takes an OpenID::CheckIDRequest, returns an OpenID::Sreg::Request
      # return nil if the extension was not requested.
      def self.from_openid_request(request)
        # Since we're going to mess with namespace URI mapping, don't
        # mutate the object that was passed in.
        message = request.message.copy
        ns_uri = OpenID::get_sreg_ns(message)
        args = message.get_args(ns_uri)
        return nil if args == {}
        req = new(nil,nil,nil,ns_uri)
        req.parse_extension_args(args)
        return req
      end

      # Parse the unqualified simple registration request
      # parameters and add them to this object.
      #
      # This method is essentially the inverse of
      # getExtensionArgs. This method restores the serialized simple
      # registration request fields.
      #
      # If you are extracting arguments from a standard OpenID
      # checkid_* request, you probably want to use fromOpenIDRequest,
      # which will extract the sreg namespace and arguments from the
      # OpenID request. This method is intended for cases where the
      # OpenID server needs more control over how the arguments are
      # parsed than that method provides.
      def parse_extension_args(args, strict = false)
        required_items = args['required']
        unless required_items.nil? or required_items.empty?
          required_items.split(',').each{|field_name|
            begin
              request_field(field_name, true, strict)
            rescue ArgumentError
              raise if strict
            end
          }
        end

        optional_items = args['optional']
        unless optional_items.nil? or optional_items.empty?
          optional_items.split(',').each{|field_name|
            begin
              request_field(field_name, false, strict)
            rescue ArgumentError
              raise if strict
            end
          }
        end
        @policy_url = args['policy_url']
      end

      # A list of all of the simple registration fields that were
      # requested, whether they were required or optional.
      def all_requested_fields
        @required + @optional
      end

      # Have any simple registration fields been requested?
      def were_fields_requested?
        !all_requested_fields.empty?
      end

      # Request the specified field from the OpenID user
      # field_name: the unqualified simple registration field name
      # required: whether the given field should be presented
      #        to the user as being a required to successfully complete
      #        the request
      # strict: whether to raise an exception when a field is
      #        added to a request more than once
      # Raises ArgumentError if the field_name is not a simple registration
      # field, or if strict is set and a field is added more than once
      def request_field(field_name, required=false, strict=false)
        OpenID::check_sreg_field_name(field_name)

        if strict
          if (@required + @optional).member? field_name
            raise ArgumentError, 'That field has already been requested'
          end
        else
          return if @required.member? field_name
          if @optional.member? field_name
            if required
              @optional.delete field_name
            else
              return
            end
          end
        end
        if required
          @required << field_name
        else
          @optional << field_name
        end
      end

      # Add the given list of fields to the request.
      def request_fields(field_names, required = false, strict = false)
        raise ArgumentError unless field_names.respond_to?(:each) and
                                   field_names[0].is_a?(String)
        field_names.each{|fn|request_field(fn, required, strict)}
      end

      # Get a hash of unqualified simple registration arguments
      # representing this request.
      # This method is essentially the inverse of parse_extension_args.
      # This method serializes the simple registration request fields.
      def get_extension_args
        args = {}
        args['required'] = @required.join(',') unless @required.empty?
        args['optional'] = @optional.join(',') unless @optional.empty?
        args['policy_url'] = @policy_url unless @policy_url.nil?
        return args
      end

      def member?(field_name)
        all_requested_fields.member?(field_name)
      end

    end

    # Represents the data returned in a simple registration response
    # inside of an OpenID id_res response. This object will be
    # created by the OpenID server, added to the id_res response
    # object, and then extracted from the id_res message by the Consumer.
    class Response < Extension
      attr_reader :ns_uri, :data

      def initialize(data = {}, ns_uri=NS_URI)
        @ns_alias = 'sreg'
        @data = data
        @ns_uri = ns_uri
      end

      # Take a Request and a hash of simple registration
      # values and create a Response object containing that data.
      def self.extract_response(request, data)
        arf = request.all_requested_fields
        resp_data = data.reject{|k,v| !arf.member?(k) || v.nil? }
        new(resp_data, request.ns_uri)
      end

      # Create an Response object from an
      # OpenID::Consumer::SuccessResponse from consumer.complete
      # If you set the signed_only parameter to false, unsigned data from
      # the id_res message from the server will be processed.
      def self.from_success_response(success_response, signed_only = true)
        ns_uri = OpenID::get_sreg_ns(success_response.message)
        if signed_only
          args = success_response.get_signed_ns(ns_uri)
          return nil if args.nil? # No signed args, so fail
        else
          args = success_response.message.get_args(ns_uri)
        end
        args.reject!{|k,v| !DATA_FIELDS.member?(k) }
        new(args, ns_uri)
      end

      # Get the fields to put in the simple registration namespace
      # when adding them to an id_res message.
      def get_extension_args
        return @data
      end

      # Read-only hashlike interface.
      # Raises an exception if the field name is bad
      def [](field_name)
        OpenID::check_sreg_field_name(field_name)
        data[field_name]
      end

      def empty?
        @data.empty?
      end
      # XXX is there more to a hashlike interface I should add?
    end
  end
end