sreg.rb
277 lines
| 9.4 KiB
| text/x-ruby
|
RubyLexer
|
r2376 | 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 | ||||