evri_rpx 1.0.0
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.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/CHANGELOG.markdown +10 -0
- data/LICENSE +21 -0
- data/README.markdown +144 -0
- data/Rakefile +73 -0
- data/VERSION +1 -0
- data/certs/cacert.pem +3154 -0
- data/evri_rpx.gemspec +82 -0
- data/lib/evri/rpx.rb +4 -0
- data/lib/evri/rpx/contact.rb +18 -0
- data/lib/evri/rpx/contact_list.rb +27 -0
- data/lib/evri/rpx/credentials.rb +65 -0
- data/lib/evri/rpx/mappings.rb +19 -0
- data/lib/evri/rpx/session.rb +161 -0
- data/lib/evri/rpx/user.rb +105 -0
- data/lib/evri_rpx.rb +13 -0
- data/spec/evri/rpx/contact_list_spec.rb +39 -0
- data/spec/evri/rpx/contact_spec.rb +19 -0
- data/spec/evri/rpx/credentials_spec.rb +98 -0
- data/spec/evri/rpx/mappings_spec.rb +21 -0
- data/spec/evri/rpx/session_spec.rb +154 -0
- data/spec/evri/rpx/user_spec.rb +336 -0
- data/spec/fixtures/contacts/bob_johnson.json +9 -0
- data/spec/fixtures/credentials/dbalatero_facebook.json +32 -0
- data/spec/fixtures/credentials/dbalatero_twitter.json +19 -0
- data/spec/fixtures/credentials/dbalatero_windowslive.json +15 -0
- data/spec/fixtures/mappings/identifiers.json +7 -0
- data/spec/fixtures/session/all_mappings.json +12 -0
- data/spec/fixtures/session/get_contacts.json +63 -0
- data/spec/fixtures/session/map.json +3 -0
- data/spec/fixtures/session/normal_error.json +7 -0
- data/spec/fixtures/session/service_down_error.json +7 -0
- data/spec/fixtures/session/unmap.json +3 -0
- data/spec/fixtures/user/dbalatero_facebook.json +31 -0
- data/spec/fixtures/user/dbalatero_gmail.json +20 -0
- data/spec/fixtures/user/dbalatero_twitter.json +19 -0
- data/spec/fixtures/user/dbalatero_windows_live.json +15 -0
- data/spec/fixtures/user/dbalatero_yahoo.json +17 -0
- data/spec/spec_helper.rb +20 -0
- metadata +101 -0
data/evri_rpx.gemspec
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{evri_rpx}
|
5
|
+
s.version = "1.0.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["David Balatero"]
|
9
|
+
s.date = %q{2009-06-17}
|
10
|
+
s.email = %q{dbalatero@evri.com}
|
11
|
+
s.extra_rdoc_files = [
|
12
|
+
"LICENSE",
|
13
|
+
"README.markdown"
|
14
|
+
]
|
15
|
+
s.files = [
|
16
|
+
".document",
|
17
|
+
".gitignore",
|
18
|
+
"CHANGELOG.markdown",
|
19
|
+
"LICENSE",
|
20
|
+
"README.markdown",
|
21
|
+
"Rakefile",
|
22
|
+
"VERSION",
|
23
|
+
"certs/cacert.pem",
|
24
|
+
"evri_rpx.gemspec",
|
25
|
+
"lib/evri/rpx.rb",
|
26
|
+
"lib/evri/rpx/contact.rb",
|
27
|
+
"lib/evri/rpx/contact_list.rb",
|
28
|
+
"lib/evri/rpx/credentials.rb",
|
29
|
+
"lib/evri/rpx/mappings.rb",
|
30
|
+
"lib/evri/rpx/session.rb",
|
31
|
+
"lib/evri/rpx/user.rb",
|
32
|
+
"lib/evri_rpx.rb",
|
33
|
+
"spec/evri/rpx/contact_list_spec.rb",
|
34
|
+
"spec/evri/rpx/contact_spec.rb",
|
35
|
+
"spec/evri/rpx/credentials_spec.rb",
|
36
|
+
"spec/evri/rpx/mappings_spec.rb",
|
37
|
+
"spec/evri/rpx/session_spec.rb",
|
38
|
+
"spec/evri/rpx/user_spec.rb",
|
39
|
+
"spec/fixtures/contacts/bob_johnson.json",
|
40
|
+
"spec/fixtures/credentials/dbalatero_facebook.json",
|
41
|
+
"spec/fixtures/credentials/dbalatero_twitter.json",
|
42
|
+
"spec/fixtures/credentials/dbalatero_windowslive.json",
|
43
|
+
"spec/fixtures/mappings/identifiers.json",
|
44
|
+
"spec/fixtures/session/all_mappings.json",
|
45
|
+
"spec/fixtures/session/get_contacts.json",
|
46
|
+
"spec/fixtures/session/map.json",
|
47
|
+
"spec/fixtures/session/normal_error.json",
|
48
|
+
"spec/fixtures/session/service_down_error.json",
|
49
|
+
"spec/fixtures/session/unmap.json",
|
50
|
+
"spec/fixtures/user/dbalatero_facebook.json",
|
51
|
+
"spec/fixtures/user/dbalatero_gmail.json",
|
52
|
+
"spec/fixtures/user/dbalatero_twitter.json",
|
53
|
+
"spec/fixtures/user/dbalatero_windows_live.json",
|
54
|
+
"spec/fixtures/user/dbalatero_yahoo.json",
|
55
|
+
"spec/spec_helper.rb"
|
56
|
+
]
|
57
|
+
s.homepage = %q{http://github.com/dbalatero/evri_rpx}
|
58
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
59
|
+
s.require_paths = ["lib"]
|
60
|
+
s.rubyforge_project = %q{evrigems}
|
61
|
+
s.rubygems_version = %q{1.3.4}
|
62
|
+
s.summary = %q{An API wrapper for the RPXNow.com login service.}
|
63
|
+
s.test_files = [
|
64
|
+
"spec/evri/rpx/contact_list_spec.rb",
|
65
|
+
"spec/evri/rpx/contact_spec.rb",
|
66
|
+
"spec/evri/rpx/credentials_spec.rb",
|
67
|
+
"spec/evri/rpx/mappings_spec.rb",
|
68
|
+
"spec/evri/rpx/session_spec.rb",
|
69
|
+
"spec/evri/rpx/user_spec.rb",
|
70
|
+
"spec/spec_helper.rb"
|
71
|
+
]
|
72
|
+
|
73
|
+
if s.respond_to? :specification_version then
|
74
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
75
|
+
s.specification_version = 3
|
76
|
+
|
77
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
78
|
+
else
|
79
|
+
end
|
80
|
+
else
|
81
|
+
end
|
82
|
+
end
|
data/lib/evri/rpx.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module Evri
|
2
|
+
module RPX
|
3
|
+
class Contact
|
4
|
+
# Returns the person's display name, eg "Bob Johnson".
|
5
|
+
attr_reader :display_name
|
6
|
+
|
7
|
+
# Returns an array of emails, eg ['bob@johnson.com']
|
8
|
+
attr_reader :emails
|
9
|
+
|
10
|
+
def initialize(json)
|
11
|
+
@display_name = json['displayName']
|
12
|
+
@emails = json['emails'].map do |email|
|
13
|
+
email['value']
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Evri
|
2
|
+
module RPX
|
3
|
+
class ContactList
|
4
|
+
# Returns the total amount of contacts found for this user.
|
5
|
+
attr_reader :total_results
|
6
|
+
|
7
|
+
# Returns the current pagination index into the contact results
|
8
|
+
attr_reader :start_index
|
9
|
+
|
10
|
+
# Returns the number of contacts returned per page.
|
11
|
+
attr_reader :items_per_page
|
12
|
+
|
13
|
+
# Returns an array of Contact objects.
|
14
|
+
attr_reader :contacts
|
15
|
+
|
16
|
+
def initialize(json)
|
17
|
+
@total_results = json['response']['totalResults'] rescue 0
|
18
|
+
@start_index = json['response']['startIndex'] rescue nil
|
19
|
+
@items_per_page = json['response']['itemsPerPage']
|
20
|
+
|
21
|
+
@contacts = json['response']['entry'].map do |contact_json|
|
22
|
+
Contact.new(contact_json)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Evri
|
2
|
+
module RPX
|
3
|
+
class Credentials
|
4
|
+
def initialize(json)
|
5
|
+
@json = json
|
6
|
+
end
|
7
|
+
|
8
|
+
# Returns the type of credentials:
|
9
|
+
# (Facebook|OAuth|WindowsLive)
|
10
|
+
#
|
11
|
+
# Generally, you should use the helper methods such
|
12
|
+
# as #facebook?, #oauth?, #windows_live?
|
13
|
+
def type
|
14
|
+
@json['type']
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns true if these credentials are Facebook.
|
18
|
+
def facebook?
|
19
|
+
type == 'Facebook'
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the Facebook session key.
|
23
|
+
def facebook_session_key
|
24
|
+
@json['sessionKey']
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns when this Facebook session expires, as a Time
|
28
|
+
# object.
|
29
|
+
def facebook_expires
|
30
|
+
Time.at(@json['expires'].to_i)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns the UID for the authorized Facebook user.
|
34
|
+
def facebook_uid
|
35
|
+
@json['uid']
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns true if these credentials are OAuth.
|
39
|
+
def oauth?
|
40
|
+
type == 'OAuth'
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns the OAuth token.
|
44
|
+
def oauth_token
|
45
|
+
@json['oauthToken']
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns the OAuth token secret.
|
49
|
+
def oauth_token_secret
|
50
|
+
@json['oauthTokenSecret']
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns true if these credentials are for Windows Live
|
54
|
+
def windows_live?
|
55
|
+
type == 'WindowsLive'
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns the Windows Live eact string, which contains the
|
59
|
+
# user's delegated authentication consent token.
|
60
|
+
def windows_live_eact
|
61
|
+
@json['eact']
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Evri
|
2
|
+
module RPX
|
3
|
+
class Mappings
|
4
|
+
# Gives back the raw JSON returned by RPX.
|
5
|
+
attr_reader :json
|
6
|
+
alias :raw :json
|
7
|
+
|
8
|
+
def initialize(json)
|
9
|
+
@json = json
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns a list of identifiers for a user, or an empty
|
13
|
+
# array if there are none.
|
14
|
+
def identifiers
|
15
|
+
@json['identifiers'] || []
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
module Evri
|
2
|
+
module RPX
|
3
|
+
class Session
|
4
|
+
API_VERSION = 'v2'
|
5
|
+
API_HOST = 'rpxnow.com'
|
6
|
+
ROOT_CA_PATH = File.expand_path(File.join(File.dirname(__FILE__),
|
7
|
+
'..', '..', '..', 'certs',
|
8
|
+
'cacert.pem'))
|
9
|
+
|
10
|
+
# This error is raised when the RPX service is unavailable.
|
11
|
+
class ServiceUnavailableError < StandardError; end
|
12
|
+
|
13
|
+
# This error is raised when an API call is misformed,
|
14
|
+
# or there is bad configuration on the other end.
|
15
|
+
class APICallError < StandardError; end
|
16
|
+
|
17
|
+
# Returns the current RPX api_key attached to the session.
|
18
|
+
attr_reader :api_key
|
19
|
+
|
20
|
+
def initialize(api_key)
|
21
|
+
@api_key = api_key
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns an auth_info User response from RPXNow.
|
25
|
+
# Options:
|
26
|
+
# :tokenUrl => 'http://...'
|
27
|
+
# RPX will validate that the tokenUrl that you received
|
28
|
+
# a login redirect from matches the tokenUrl they have on
|
29
|
+
# file. This is extra security to guard against spoofing.
|
30
|
+
#
|
31
|
+
# Default: nil
|
32
|
+
# :extended => (true|false)
|
33
|
+
# If you are a Plus/Pro customer, RPX will return extended
|
34
|
+
# data.
|
35
|
+
#
|
36
|
+
# Default: false
|
37
|
+
def auth_info(token, options = {})
|
38
|
+
params = { 'apiKey' => @api_key,
|
39
|
+
'token' => token }
|
40
|
+
params.merge!(options)
|
41
|
+
|
42
|
+
json = parse_response(get("/api/#{API_VERSION}/auth_info",
|
43
|
+
params))
|
44
|
+
User.new(json)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Get all stored mappings for a particular application.
|
48
|
+
#
|
49
|
+
# Returns a hash in this form:
|
50
|
+
# { 'identifier1' => ['mapping1', 'mapping2'],
|
51
|
+
# 'identifier2' => ['mapping3', 'mapping4'],
|
52
|
+
# ... }
|
53
|
+
def all_mappings
|
54
|
+
json = parse_response(get("/api/#{API_VERSION}/all_mappings",
|
55
|
+
:apiKey => @api_key))
|
56
|
+
json['mappings']
|
57
|
+
end
|
58
|
+
|
59
|
+
# Retrieve a list of contacts for an identifier in the
|
60
|
+
# Portable Contacts format.
|
61
|
+
#
|
62
|
+
# Takes either a string identifier, or a User object
|
63
|
+
# that responds to :identifier.
|
64
|
+
#
|
65
|
+
# This feature is only available for RPX Pro customers.
|
66
|
+
def get_contacts(user_or_identifier)
|
67
|
+
identifier = identifier_param(user_or_identifier)
|
68
|
+
json = parse_response(get("/api/#{API_VERSION}/get_contacts",
|
69
|
+
:apiKey => @api_key,
|
70
|
+
:identifier => identifier))
|
71
|
+
ContactList.new(json)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns the mappings for a given user's primary key, as a
|
75
|
+
# Evri::RPX::Mappings object.
|
76
|
+
#
|
77
|
+
# Takes either a string of the user's primary key:
|
78
|
+
# m = session.mappings('dbalatero')
|
79
|
+
# or a User object with an attached primary key:
|
80
|
+
# user = session.auth_info(params[:token])
|
81
|
+
# m = session.mappings(user)
|
82
|
+
def mappings(user_or_primary_key)
|
83
|
+
params = { 'apiKey' => @api_key }
|
84
|
+
params['primaryKey'] = user_or_primary_key.respond_to?(:primary_key) ?
|
85
|
+
user_or_primary_key.primary_key : user_or_primary_key
|
86
|
+
|
87
|
+
json = parse_response(get("/api/#{API_VERSION}/mappings",
|
88
|
+
params))
|
89
|
+
Mappings.new(json)
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
# Map an OpenID to a primary key. Future logins by this owner of
|
94
|
+
# this OpenID will return the mapped primaryKey in the auth_info
|
95
|
+
# API response, which you may use to sign the user in.
|
96
|
+
#
|
97
|
+
# Returns true.
|
98
|
+
def map(user_or_identifier, primary_key, options = {})
|
99
|
+
params = { 'apiKey' => @api_key,
|
100
|
+
'primaryKey' => primary_key,
|
101
|
+
'overwrite' => true }
|
102
|
+
params.merge!(options)
|
103
|
+
params['identifier'] = identifier_param(user_or_identifier)
|
104
|
+
json = parse_response(get("/api/#{API_VERSION}/map",
|
105
|
+
params))
|
106
|
+
|
107
|
+
json['stat'] == 'ok'
|
108
|
+
end
|
109
|
+
|
110
|
+
# Remove (unmap) an OpenID from a primary key.
|
111
|
+
#
|
112
|
+
# Returns true.
|
113
|
+
def unmap(user_or_identifier, primary_key)
|
114
|
+
params = { 'apiKey' => @api_key,
|
115
|
+
'primaryKey' => primary_key }
|
116
|
+
params['identifier'] = identifier_param(user_or_identifier)
|
117
|
+
|
118
|
+
json = parse_response(get("/api/#{API_VERSION}/unmap",
|
119
|
+
params))
|
120
|
+
|
121
|
+
json['stat'] == 'ok'
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
def identifier_param(user_or_identifier)
|
126
|
+
user_or_identifier.respond_to?(:identifier) ?
|
127
|
+
user_or_identifier.identifier : user_or_identifier
|
128
|
+
end
|
129
|
+
|
130
|
+
def get(resource, params)
|
131
|
+
request = Net::HTTP::Get.new(resource)
|
132
|
+
request.form_data = params
|
133
|
+
make_request(request)
|
134
|
+
end
|
135
|
+
|
136
|
+
def make_request(request)
|
137
|
+
http = Net::HTTP.new(API_HOST, 443)
|
138
|
+
http.use_ssl = true
|
139
|
+
http.ca_file = ROOT_CA_PATH
|
140
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
141
|
+
http.verify_depth = 5
|
142
|
+
http.request(request)
|
143
|
+
end
|
144
|
+
|
145
|
+
def parse_response(response)
|
146
|
+
result = JSON.parse(response.body)
|
147
|
+
|
148
|
+
if result['err']
|
149
|
+
code = result['err']['code']
|
150
|
+
if code == -1
|
151
|
+
raise ServiceUnavailableError, "The RPX service is temporarily unavailable."
|
152
|
+
else
|
153
|
+
raise APICallError, "Got error: #{result['err']['msg']} (code: #{code}), HTTP status: #{response.code}"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
result
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Evri
|
2
|
+
module RPX
|
3
|
+
class User
|
4
|
+
class InvalidUserJsonError < StandardError; end
|
5
|
+
|
6
|
+
# Returns a Credentials object for this user, or nil.
|
7
|
+
attr_reader :credentials
|
8
|
+
|
9
|
+
# Returns the raw JSON returned by RPX, in case you want it.
|
10
|
+
attr_reader :json
|
11
|
+
alias :raw :json
|
12
|
+
|
13
|
+
def initialize(json)
|
14
|
+
if json.nil? or !json['profile']
|
15
|
+
raise InvalidUserJsonError,
|
16
|
+
'The JSON passed in is invalid!'
|
17
|
+
end
|
18
|
+
|
19
|
+
@json = json
|
20
|
+
|
21
|
+
if @json['accessCredentials']
|
22
|
+
@credentials = Credentials.new(@json['accessCredentials'])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns the person's full name (aka David Balatero)
|
27
|
+
def name
|
28
|
+
@json['profile']['name']['formatted'] rescue nil
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns a name that should be displayed for this user.
|
32
|
+
def display_name
|
33
|
+
@json['profile']['displayName'] rescue nil
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns a username for this user.
|
37
|
+
def username
|
38
|
+
@json['profile']['preferredUsername'] rescue nil
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns a unique identifier for this user.
|
42
|
+
def identifier
|
43
|
+
@json['profile']['identifier'] rescue nil
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns the primary key for this user, if they have
|
47
|
+
# been mapped already.
|
48
|
+
def primary_key
|
49
|
+
@json['profile']['primaryKey'] rescue nil
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns a photo URL for a user if they have one.
|
53
|
+
def photo
|
54
|
+
@json['profile']['photo'] rescue nil
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns a URL to a person's profile on the 3rd-party site.
|
58
|
+
def profile_url
|
59
|
+
@json['profile']['url'] rescue nil
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns a user's email.
|
63
|
+
def email
|
64
|
+
@json['profile']['email'] rescue nil
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns the provider name for this user, aka "Twitter", "Google".
|
68
|
+
# Also, see convenience methods such as #google?, #twitter?
|
69
|
+
def provider_name
|
70
|
+
@json['profile']['providerName'] rescue nil
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns true if this is a Twitter login.
|
74
|
+
def twitter?
|
75
|
+
provider_name == 'Twitter'
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns true if this is a Google login.
|
79
|
+
def google?
|
80
|
+
provider_name == 'Google'
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns true if this is a Facebook login.
|
84
|
+
def facebook?
|
85
|
+
provider_name == 'Facebook'
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns true if this is a Yahoo! login.
|
89
|
+
def yahoo?
|
90
|
+
provider_name == 'Yahoo!'
|
91
|
+
end
|
92
|
+
|
93
|
+
# Returns true if this is a Windows Live login.
|
94
|
+
def windows_live?
|
95
|
+
provider_name == 'Windows Live'
|
96
|
+
end
|
97
|
+
|
98
|
+
# Returns true if this provider is unknown / doesn't have
|
99
|
+
# a specific name in the RPX system.
|
100
|
+
def other?
|
101
|
+
provider_name == 'Other'
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|