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.
Files changed (41) hide show
  1. data/.document +5 -0
  2. data/.gitignore +5 -0
  3. data/CHANGELOG.markdown +10 -0
  4. data/LICENSE +21 -0
  5. data/README.markdown +144 -0
  6. data/Rakefile +73 -0
  7. data/VERSION +1 -0
  8. data/certs/cacert.pem +3154 -0
  9. data/evri_rpx.gemspec +82 -0
  10. data/lib/evri/rpx.rb +4 -0
  11. data/lib/evri/rpx/contact.rb +18 -0
  12. data/lib/evri/rpx/contact_list.rb +27 -0
  13. data/lib/evri/rpx/credentials.rb +65 -0
  14. data/lib/evri/rpx/mappings.rb +19 -0
  15. data/lib/evri/rpx/session.rb +161 -0
  16. data/lib/evri/rpx/user.rb +105 -0
  17. data/lib/evri_rpx.rb +13 -0
  18. data/spec/evri/rpx/contact_list_spec.rb +39 -0
  19. data/spec/evri/rpx/contact_spec.rb +19 -0
  20. data/spec/evri/rpx/credentials_spec.rb +98 -0
  21. data/spec/evri/rpx/mappings_spec.rb +21 -0
  22. data/spec/evri/rpx/session_spec.rb +154 -0
  23. data/spec/evri/rpx/user_spec.rb +336 -0
  24. data/spec/fixtures/contacts/bob_johnson.json +9 -0
  25. data/spec/fixtures/credentials/dbalatero_facebook.json +32 -0
  26. data/spec/fixtures/credentials/dbalatero_twitter.json +19 -0
  27. data/spec/fixtures/credentials/dbalatero_windowslive.json +15 -0
  28. data/spec/fixtures/mappings/identifiers.json +7 -0
  29. data/spec/fixtures/session/all_mappings.json +12 -0
  30. data/spec/fixtures/session/get_contacts.json +63 -0
  31. data/spec/fixtures/session/map.json +3 -0
  32. data/spec/fixtures/session/normal_error.json +7 -0
  33. data/spec/fixtures/session/service_down_error.json +7 -0
  34. data/spec/fixtures/session/unmap.json +3 -0
  35. data/spec/fixtures/user/dbalatero_facebook.json +31 -0
  36. data/spec/fixtures/user/dbalatero_gmail.json +20 -0
  37. data/spec/fixtures/user/dbalatero_twitter.json +19 -0
  38. data/spec/fixtures/user/dbalatero_windows_live.json +15 -0
  39. data/spec/fixtures/user/dbalatero_yahoo.json +17 -0
  40. data/spec/spec_helper.rb +20 -0
  41. 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,4 @@
1
+ module Evri
2
+ module RPX
3
+ end
4
+ end
@@ -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