evri_rpx 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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