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
|