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
|
# An implementation of the OpenID Provider Authentication Policy
# Extension 1.0
# see: http://openid.net/specs/
require 'openid/extension'
module OpenID
module PAPE
NS_URI = "http://specs.openid.net/extensions/pape/1.0"
AUTH_MULTI_FACTOR_PHYSICAL =
'http://schemas.openid.net/pape/policies/2007/06/multi-factor-physical'
AUTH_MULTI_FACTOR =
'http://schemas.openid.net/pape/policies/2007/06/multi-factor'
AUTH_PHISHING_RESISTANT =
'http://schemas.openid.net/pape/policies/2007/06/phishing-resistant'
TIME_VALIDATOR = /\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ/
# A Provider Authentication Policy request, sent from a relying
# party to a provider
class Request < Extension
attr_accessor :preferred_auth_policies, :max_auth_age, :ns_alias, :ns_uri
def initialize(preferred_auth_policies=[], max_auth_age=nil)
@ns_alias = 'pape'
@ns_uri = NS_URI
@preferred_auth_policies = preferred_auth_policies
@max_auth_age = max_auth_age
end
# Add an acceptable authentication policy URI to this request
# This method is intended to be used by the relying party to add
# acceptable authentication types to the request.
def add_policy_uri(policy_uri)
unless @preferred_auth_policies.member? policy_uri
@preferred_auth_policies << policy_uri
end
end
def get_extension_args
ns_args = {
'preferred_auth_policies' => @preferred_auth_policies.join(' ')
}
ns_args['max_auth_age'] = @max_auth_age.to_s if @max_auth_age
return ns_args
end
# Instantiate a Request object from the arguments in a
# checkid_* OpenID message
# return nil if the extension was not requested.
def self.from_openid_request(oid_req)
pape_req = new
args = oid_req.message.get_args(NS_URI)
if args == {}
return nil
end
pape_req.parse_extension_args(args)
return pape_req
end
# Set the state of this request to be that expressed in these
# PAPE arguments
def parse_extension_args(args)
@preferred_auth_policies = []
policies_str = args['preferred_auth_policies']
if policies_str
policies_str.split(' ').each{|uri|
add_policy_uri(uri)
}
end
max_auth_age_str = args['max_auth_age']
if max_auth_age_str
@max_auth_age = max_auth_age_str.to_i
else
@max_auth_age = nil
end
end
# Given a list of authentication policy URIs that a provider
# supports, this method returns the subset of those types
# that are preferred by the relying party.
def preferred_types(supported_types)
@preferred_auth_policies.select{|uri| supported_types.member? uri}
end
end
# A Provider Authentication Policy response, sent from a provider
# to a relying party
class Response < Extension
attr_accessor :ns_alias, :auth_policies, :auth_time, :nist_auth_level
def initialize(auth_policies=[], auth_time=nil, nist_auth_level=nil)
@ns_alias = 'pape'
@ns_uri = NS_URI
@auth_policies = auth_policies
@auth_time = auth_time
@nist_auth_level = nist_auth_level
end
# Add a policy URI to the response
# see http://openid.net/specs/openid-provider-authentication-policy-extension-1_0-01.html#auth_policies
def add_policy_uri(policy_uri)
@auth_policies << policy_uri unless @auth_policies.member?(policy_uri)
end
# Create a Response object from an OpenID::Consumer::SuccessResponse
def self.from_success_response(success_response)
args = success_response.get_signed_ns(NS_URI)
return nil if args.nil?
pape_resp = new
pape_resp.parse_extension_args(args)
return pape_resp
end
# parse the provider authentication policy arguments into the
# internal state of this object
# if strict is specified, raise an exception when bad data is
# encountered
def parse_extension_args(args, strict=false)
policies_str = args['auth_policies']
if policies_str and policies_str != 'none'
@auth_policies = policies_str.split(' ')
end
nist_level_str = args['nist_auth_level']
if nist_level_str
# special handling of zero to handle to_i behavior
if nist_level_str.strip == '0'
nist_level = 0
else
nist_level = nist_level_str.to_i
# if it's zero here we have a bad value
if nist_level == 0
nist_level = nil
end
end
if nist_level and nist_level >= 0 and nist_level < 5
@nist_auth_level = nist_level
elsif strict
raise ArgumentError, "nist_auth_level must be an integer 0 through 4, not #{nist_level_str.inspect}"
end
end
auth_time_str = args['auth_time']
if auth_time_str
# validate time string
if auth_time_str =~ TIME_VALIDATOR
@auth_time = auth_time_str
elsif strict
raise ArgumentError, "auth_time must be in RFC3339 format"
end
end
end
def get_extension_args
ns_args = {}
if @auth_policies.empty?
ns_args['auth_policies'] = 'none'
else
ns_args['auth_policies'] = @auth_policies.join(' ')
end
if @nist_auth_level
unless (0..4).member? @nist_auth_level
raise ArgumentError, "nist_auth_level must be an integer 0 through 4, not #{@nist_auth_level.inspect}"
end
ns_args['nist_auth_level'] = @nist_auth_level.to_s
end
if @auth_time
unless @auth_time =~ TIME_VALIDATOR
raise ArgumentError, "auth_time must be in RFC3339 format"
end
ns_args['auth_time'] = @auth_time
end
return ns_args
end
end
end
end
|