jackdempsey-rpx_now 0.5.8

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,23 @@
1
+ 0.5.0
2
+ ----
3
+ * ApiError is raised when API KEY was invalid (before did only return nil)
4
+ * ServerError is now seperated into ApiError and ServiceUnavailableError
5
+
6
+ 0.4.2
7
+ ----
8
+ * default IDs are strings, not integers, since RPX supports both
9
+ * Added username field to standart parameters
10
+
11
+ 0.4
12
+ ----
13
+ * The RPXNow.api_version= can be set globally
14
+ * Most methods now support :api_version=>'123', so you can specify the api_version on each call
15
+ * Added support for unobtrusive code generation. This will make the gem play nicely with developers creating unobtrusive pages with frameworks such as jQuery;
16
+ * The RPXNOW JSON responses are now parsed by Florian Frank JSON gem (http://json.rubyforge.org/);
17
+ * Removed the dependency of ActiveSupport. However, if present, ActiveSupport will be used due to its convenience methods Array#to_query and Hash#to_query.
18
+ * Added support for multiple versions of the widget. By default, everything will point to version 2;
19
+ * The specs have been updated.
20
+
21
+ 0.3
22
+ ----
23
+ * RPXNow::ServerError will be thrown when something is invalid/goes wrong, so watch out (not for invalid tokens in user_data)...
data/MIGRATION ADDED
@@ -0,0 +1,9 @@
1
+ class AddIdentifierToUsers < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :users, :identifier, :string
4
+ end
5
+
6
+ def self.down
7
+ remove_column :users, :identifier
8
+ end
9
+ end
data/README.markdown ADDED
@@ -0,0 +1,113 @@
1
+ Problem
2
+ =======
3
+ - OpenID is complex, limited and hard to use for users
4
+ - Facebook / Twitter / Myspace / Google / MS-LiveId / AOL connections require different libraries and knowledge
5
+ - Multiple heterogenouse providers are hard to map to a single user
6
+
7
+ Solution
8
+ ========
9
+ - Use [RPX](http://rpxnow.com) for universal and usable user login
10
+ - Use view/controller helpers for easy integration
11
+
12
+ ![Single Interface for all providers](https://rpxnow.com/images/how_diagram.png)
13
+ ![Visitors choose from providers they already have](https://rpxnow.com/images/6providers.png?2)
14
+
15
+ Usage
16
+ =====
17
+ - Get an API key @ [RPX](http://rpxnow.com)
18
+ - run [MIGRATION](http://github.com/grosser/rpx_now/raw/master/MIGRATION)
19
+ - Build login view
20
+ - Communicate with RPX API in controller to create or login User
21
+ - for more advanced features have a look at the [RPX API Docs](https://rpxnow.com/docs)
22
+
23
+ Install
24
+ =======
25
+ - As Rails plugin: `script/plugin install git://github.com/grosser/rpx_now.git `
26
+ - As gem: `sudo gem install grosser-rpx_now --source http://gems.github.com/`
27
+
28
+ Examples
29
+ ========
30
+
31
+ View
32
+ ----
33
+ #'mywebsite' is your subdomain/realm on RPX
34
+ <%=RPXNow.embed_code('mywebsite',rpx_token_sessions_url)%>
35
+ OR
36
+ <%=RPXNow.popup_code('Login here...','mywebsite',rpx_token_sessions_url,:language=>'de')%>
37
+
38
+ `popup_code` can also be called with `:unobstrusive=>true`
39
+
40
+ Environment
41
+ -----------
42
+ Rails::Initializer.run do |config|
43
+ config.gem "grosser-rpx_now", :lib => "rpx_now", :source => "http://gems.github.com/"
44
+ ...
45
+ end
46
+ RPXNow.api_key = "YOU RPX API KEY"
47
+
48
+ Controller
49
+ ----------
50
+ # user_data
51
+ # found: {:name=>'John Doe', :username => 'john', :email=>'john@doe.com', :identifier=>'blug.google.com/openid/dsdfsdfs3f3'}
52
+ # not found: nil (can happen with e.g. invalid tokens)
53
+ def rpx_token
54
+ raise "hackers?" unless data = RPXNow.user_data(params[:token])
55
+ self.current_user = User.find_by_identifier(data[:identifier]) || User.create!(data)
56
+ redirect_to '/'
57
+ end
58
+
59
+ # raw request processing
60
+ RPXNow.user_data(params[:token]){|raw| {:email=>raw['profile']['verifiedEmail']} }
61
+
62
+ # raw request with extended parameters (most users and APIs do not supply them)
63
+ RPXNow.user_data(params[:token], :extended=>'true'){|raw| ...have a look at the RPX API DOCS...}
64
+
65
+ Advanced
66
+ --------
67
+ ###Versions
68
+ RPXNow.api_version = 2
69
+
70
+ ###Mappings
71
+ You can map your primary keys (e.g. user.id) to identifiers, so that
72
+ users can login to the same account with multiple identifiers.
73
+ RPXNow.map(identifier, primary_key) #add a mapping
74
+ RPXNow.unmap(identifier, primary_key) #remove a mapping
75
+ RPXNow.mappings(primary_key) # [identifier1,identifier2,...]
76
+ RPXNow.all_mappings # [["1",['google.com/dsdas','yahoo.com/asdas']], ["2",[...]], ... ]
77
+
78
+ After a primary key is mapped to an identifier, when a user logs in with this identifier,
79
+ `RPXNow.user_data` will contain his `primaryKey` as `:id`.
80
+ A identifyer can only belong to one user (in doubt the last one it was mapped to)
81
+
82
+ ###User integration (e.g. ActiveRecord)
83
+ class User < ActiveRecord::Base
84
+ include RPXNow::UserIntegration
85
+ end
86
+
87
+ user.rpx.identifiers == RPXNow.mappings(user.id)
88
+ user.rpx.map(identifier) == RPXNow.map(identifier, user.id)
89
+ user.rpx.unmap(identifier) == RPXNow.unmap(identifier, user.id)
90
+
91
+ ###Contacts (PRX Pro)
92
+ Retrieve all contacts for a given user:
93
+ RPXNow.contacts(identifier).each {|c| puts "#{c['displayName']}: #{c['emails']}}
94
+
95
+ ###Status updates (PRX Pro)
96
+ Send a status update to provider (a tweet/facebook-status/...) :
97
+ RPXNow.set_status(identifier, "I just registered at yourdomain.com ...")
98
+
99
+ TODO
100
+ ====
101
+ - add provider / credentials helpers ?
102
+
103
+ Author
104
+ ======
105
+ ###Contributors
106
+ - [Amunds](http://github.com/Amunds)
107
+ - [DBA](http://github.com/DBA)
108
+ - [dbalatero](http://github.com/dbalatero)
109
+ - [jackdempsey](http://jackndempsey.blogspot.com/)
110
+
111
+ [Michael Grosser](http://pragmatig.wordpress.com)
112
+ grosser.michael@gmail.com
113
+ Hereby placed under public domain, do what you want, just do not hold me accountable...
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+ require 'echoe'
3
+
4
+ desc "Run all specs in spec directory"
5
+ task :default do |t|
6
+ options = "--colour --format progress --loadby --reverse"
7
+ files = FileList['spec/**/*_spec.rb']
8
+ system("spec #{options} #{files}")
9
+ end
10
+
11
+ #Gemspec
12
+ begin
13
+ require 'jeweler'
14
+ Jeweler::Tasks.new do |gem|
15
+ project_name = 'rpx_now'
16
+ gem.name = project_name
17
+ gem.summary = "Helper to simplify RPX Now user login/creation"
18
+ gem.email = "grosser.michael@gmail.com"
19
+ gem.homepage = "http://github.com/grosser/#{project_name}"
20
+ gem.authors = ["Michael Grosser"]
21
+ gem.add_dependency ['activesupport']
22
+ gem.files = ['**/*']
23
+ end
24
+ rescue LoadError
25
+ puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
26
+ end
27
+
28
+ task :update_gemspec => [:manifest, :build_gemspec]
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :minor: 5
3
+ :patch: 8
4
+ :major: 0
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ #Needed to load rpx_now when used as Rails plugin
2
+ require 'rpx_now'
data/lib/rpx_now.rb ADDED
@@ -0,0 +1,198 @@
1
+ require 'json'
2
+ require 'rpx_now/contacts_collection'
3
+ require 'rpx_now/user_integration'
4
+ require 'rpx_now/user_proxy'
5
+
6
+ module RPXNow
7
+ extend self
8
+
9
+ HOST = 'rpxnow.com'
10
+ SSL_CERT = File.join(File.dirname(__FILE__), '..', 'certs', 'ssl_cert.pem')
11
+
12
+ attr_accessor :api_key
13
+ attr_accessor :api_version
14
+ self.api_version = 2
15
+
16
+ # retrieve the users data, or return nil when nothing could be read/token was invalid
17
+ # or data was not found
18
+ def user_data(token, *args)
19
+ api_key, version, options = extract_key_version_and_options!(args)
20
+ options = {:token=>token,:apiKey=>api_key}.merge options
21
+
22
+ begin
23
+ data = secure_json_post("/api/v#{version}/auth_info", options)
24
+ rescue ServerError
25
+ return nil if $!.to_s=~/Data not found/
26
+ raise
27
+ end
28
+ if block_given? then yield(data) else read_user_data_from_response(data) end
29
+ end
30
+
31
+ # set the users status
32
+ def set_status(identifier, status, *args)
33
+ api_key, version, options = extract_key_version_and_options!(args)
34
+ options = {:identifier => identifier, :status => status, :apiKey => api_key}.merge options
35
+
36
+ begin
37
+ data = secure_json_post("/api/v#{version}/set_status", options)
38
+ rescue ServerError
39
+ return nil if $!.to_s=~/Data not found/
40
+ raise
41
+ end
42
+ end
43
+
44
+ # maps an identifier to an primary-key (e.g. user.id)
45
+ def map(identifier, primary_key, *args)
46
+ api_key, version, options = extract_key_version_and_options!(args)
47
+ options = {:identifier=>identifier,:primaryKey=>primary_key,:apiKey=>api_key}.merge options
48
+ secure_json_post("/api/v#{version}/map", options)
49
+ end
50
+
51
+ # un-maps an identifier to an primary-key (e.g. user.id)
52
+ def unmap(identifier, primary_key, *args)
53
+ api_key, version, options = extract_key_version_and_options!(args)
54
+ options = {:identifier=>identifier,:primaryKey=>primary_key,:apiKey=>api_key}.merge options
55
+ secure_json_post("/api/v#{version}/unmap", options)
56
+ end
57
+
58
+ # returns an array of identifiers which are mapped to one of your primary-keys (e.g. user.id)
59
+ def mappings(primary_key, *args)
60
+ api_key, version, options = extract_key_version_and_options!(args)
61
+ options = {:primaryKey=>primary_key,:apiKey=>api_key}.merge options
62
+ data = secure_json_post("/api/v#{version}/mappings", options)
63
+ data['identifiers']
64
+ end
65
+
66
+ def all_mappings(*args)
67
+ api_key, version, options = extract_key_version_and_options!(args)
68
+ data = secure_json_post("/api/v#{version}/all_mappings", {:apiKey => api_key}.merge(options))
69
+ data['mappings']
70
+ end
71
+
72
+ def contacts(identifier, *args)
73
+ api_key, version, options = extract_key_version_and_options!(args)
74
+ options = {:apiKey => api_key, :identifier=> identifier}.merge(options)
75
+ data = secure_json_post("/api/v#{version}/get_contacts", options)
76
+ RPXNow::ContactsCollection.new(data['response'])
77
+ end
78
+ alias get_contacts contacts
79
+
80
+ def embed_code(subdomain,url,options={})
81
+ options = {:width => '400', :height => '240', :language => 'en'}.merge(options)
82
+ <<EOF
83
+ <iframe src="https://#{subdomain}.#{HOST}/openid/embed?token_url=#{url}&language_preference=#{options[:language]}"
84
+ scrolling="no" frameBorder="no" style="width:#{options[:width]}px;height:#{options[:height]}px;">
85
+ </iframe>
86
+ EOF
87
+ end
88
+
89
+ def popup_code(text, subdomain, url, options = {})
90
+ if options[:unobtrusive]
91
+ unobtrusive_popup_code(text, subdomain, url, options)
92
+ else
93
+ obtrusive_popup_code(text, subdomain, url, options)
94
+ end
95
+ end
96
+
97
+ private
98
+
99
+ def unobtrusive_popup_code(text, subdomain, url, options={})
100
+ version = extract_version! options
101
+ "<a class=\"rpxnow\" href=\"https://#{subdomain}.#{HOST}/openid/v#{version}/signin?token_url=#{url}\">#{text}</a>"
102
+ end
103
+
104
+ def obtrusive_popup_code(text, subdomain, url, options = {})
105
+ version = extract_version! options
106
+ <<EOF
107
+ <a class="rpxnow" onclick="return false;" href="https://#{subdomain}.#{HOST}/openid/v#{version}/signin?token_url=#{url}">
108
+ #{text}
109
+ </a>
110
+ <script src="https://#{HOST}/openid/v#{version}/widget" type="text/javascript"></script>
111
+ <script type="text/javascript">
112
+ //<![CDATA[
113
+ RPXNOW.token_url = "#{url}";
114
+
115
+ RPXNOW.realm = "#{subdomain}";
116
+ RPXNOW.overlay = true;
117
+ RPXNOW.language_preference = '#{options[:language]||'en'}';
118
+ //]]>
119
+ </script>
120
+ EOF
121
+ end
122
+
123
+ def extract_key_version_and_options!(args)
124
+ key, options = extract_key_and_options(args)
125
+ version = extract_version! options
126
+ [key, version, options]
127
+ end
128
+
129
+ # [API_KEY,{options}] or
130
+ # [{options}] or
131
+ # []
132
+ def extract_key_and_options(args)
133
+ if args.length == 2
134
+ [args[0],args[1]]
135
+ elsif args.length==1
136
+ if args[0].is_a? Hash then [@api_key,args[0]] else [args[0],{}] end
137
+ else
138
+ raise "NO Api Key found!" unless @api_key
139
+ [@api_key,{}]
140
+ end
141
+ end
142
+
143
+ def extract_version!(options)
144
+ options.delete(:api_version) || api_version
145
+ end
146
+
147
+ def read_user_data_from_response(response)
148
+ user_data = response['profile']
149
+ data = {}
150
+ data[:identifier] = user_data['identifier']
151
+ data[:email] = user_data['verifiedEmail'] || user_data['email']
152
+ data[:username] = user_data['preferredUsername'] || data[:email].to_s.sub(/@.*/,'')
153
+ data[:name] = user_data['displayName'] || data[:username]
154
+ data[:id] = user_data['primaryKey'] unless user_data['primaryKey'].to_s.empty?
155
+ data
156
+ end
157
+
158
+ def secure_json_post(path, data)
159
+ parse_response(post(path,data))
160
+ end
161
+
162
+ def post(path, data)
163
+ require 'net/http'
164
+ require 'net/https'
165
+ request = Net::HTTP::Get.new(path)
166
+ request.form_data = data.map{|k,v| [k.to_s,v]}#symbol keys -> string because of ruby 1.9.x bug http://redmine.ruby-lang.org/issues/show/1351
167
+ make_request(request)
168
+ end
169
+
170
+ def make_request(request)
171
+ http = Net::HTTP.new(HOST, 443)
172
+ http.use_ssl = true
173
+ http.ca_file = SSL_CERT
174
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
175
+ http.verify_depth = 5
176
+ http.request(request)
177
+ end
178
+
179
+ def parse_response(response)
180
+ if response.code.to_i >= 400
181
+ raise ServiceUnavailableError, "The RPX service is temporarily unavailable. (4XX)"
182
+ else
183
+ result = JSON.parse(response.body)
184
+ return result unless result['err']
185
+
186
+ code = result['err']['code']
187
+ if code == -1
188
+ raise ServiceUnavailableError, "The RPX service is temporarily unavailable."
189
+ else
190
+ raise ApiError, "Got error: #{result['err']['msg']} (code: #{code}), HTTP status: #{response.code}"
191
+ end
192
+ end
193
+ end
194
+
195
+ class ServerError < Exception; end #backwards compatibility / catch all
196
+ class ApiError < ServerError; end
197
+ class ServiceUnavailableError < ServerError; end
198
+ end
@@ -0,0 +1,19 @@
1
+ module RPXNow
2
+ class ContactsCollection < Array
3
+ def initialize(list)
4
+ @raw = list
5
+ @additional_info = list.reject{|k,v|k=='entry'}
6
+ list['entry'].each{|item| self << parse_data(item)}
7
+ end
8
+
9
+ def additional_info;@additional_info;end
10
+ def raw;@raw;end
11
+
12
+ private
13
+
14
+ def parse_data(entry)
15
+ entry['emails'] = entry['emails'].map{|email| email['value']}
16
+ entry
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,7 @@
1
+ module RPXNow
2
+ module UserIntegration
3
+ def rpx
4
+ RPXNow::UserProxy.new(id)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,19 @@
1
+ module RPXNow
2
+ class UserProxy
3
+ def initialize(id)
4
+ @id = id
5
+ end
6
+
7
+ def identifiers
8
+ RPXNow.mappings(@id)
9
+ end
10
+
11
+ def map(identifier)
12
+ RPXNow.map(identifier, @id)
13
+ end
14
+
15
+ def unmap(identifier)
16
+ RPXNow.unmap(identifier, @id)
17
+ end
18
+ end
19
+ end
data/rpx_now.gemspec ADDED
@@ -0,0 +1,56 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{rpx_now}
5
+ s.version = "0.5.8"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Michael Grosser"]
9
+ s.date = %q{2009-06-21}
10
+ s.email = %q{grosser.michael@gmail.com}
11
+ s.extra_rdoc_files = [
12
+ "README.markdown"
13
+ ]
14
+ s.files = [
15
+ "CHANGELOG",
16
+ "MIGRATION",
17
+ "README.markdown",
18
+ "Rakefile",
19
+ "VERSION.yml",
20
+ "init.rb",
21
+ "lib/rpx_now.rb",
22
+ "lib/rpx_now/contacts_collection.rb",
23
+ "lib/rpx_now/user_integration.rb",
24
+ "lib/rpx_now/user_proxy.rb",
25
+ "rpx_now.gemspec",
26
+ "spec/fixtures/get_contacts_response.json",
27
+ "spec/rpx_now/contacts_collection_spec.rb",
28
+ "spec/rpx_now/user_proxy_spec.rb",
29
+ "spec/rpx_now_spec.rb",
30
+ "spec/spec_helper.rb"
31
+ ]
32
+ s.homepage = %q{http://github.com/grosser/rpx_now}
33
+ s.rdoc_options = ["--charset=UTF-8"]
34
+ s.require_paths = ["lib"]
35
+ s.rubygems_version = %q{1.3.5}
36
+ s.summary = %q{Helper to simplify RPX Now user login/creation}
37
+ s.test_files = [
38
+ "spec/rpx_now_spec.rb",
39
+ "spec/rpx_now/contacts_collection_spec.rb",
40
+ "spec/rpx_now/user_proxy_spec.rb",
41
+ "spec/spec_helper.rb"
42
+ ]
43
+
44
+ if s.respond_to? :specification_version then
45
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
46
+ s.specification_version = 3
47
+
48
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
49
+ s.add_runtime_dependency(%q<activesupport>, [">= 0"])
50
+ else
51
+ s.add_dependency(%q<activesupport>, [">= 0"])
52
+ end
53
+ else
54
+ s.add_dependency(%q<activesupport>, [">= 0"])
55
+ end
56
+ end
@@ -0,0 +1,58 @@
1
+ {
2
+ "response": {
3
+ "itemsPerPage": 5,
4
+ "totalResults": 5,
5
+ "entry": [
6
+ {
7
+ "displayName": "Bob Johnson",
8
+ "emails": [
9
+ {
10
+ "type": "other",
11
+ "value": "bob@example.com"
12
+ }
13
+ ]
14
+ },
15
+ {
16
+ "displayName": "Cindy Smith",
17
+ "emails": [
18
+ {
19
+ "type": "other",
20
+ "value": "cindy.smith@example.com"
21
+ }
22
+ ]
23
+ },
24
+ {
25
+ "displayName": "Fred Williams",
26
+ "emails": [
27
+ {
28
+ "type": "other",
29
+ "value": "fred.williams@example.com"
30
+ },
31
+ {
32
+ "type": "other",
33
+ "value": "fred@example.com"
34
+ }
35
+ ]
36
+ },
37
+ {
38
+ "emails": [
39
+ {
40
+ "type": "other",
41
+ "value": "j.lewis@example.com"
42
+ }
43
+ ]
44
+ },
45
+ {
46
+ "displayName": "Mary Jones",
47
+ "emails": [
48
+ {
49
+ "type": "other",
50
+ "value": "mary.jones@example.com"
51
+ }
52
+ ]
53
+ }
54
+ ],
55
+ "startIndex": 1
56
+ },
57
+ "stat": "ok"
58
+ }
@@ -0,0 +1,32 @@
1
+ require File.expand_path("../spec_helper", File.dirname(__FILE__))
2
+
3
+ describe RPXNow::ContactsCollection do
4
+ before do
5
+ data = JSON.parse(File.read('spec/fixtures/get_contacts_response.json'))['response']
6
+ @collection = RPXNow::ContactsCollection.new(data)
7
+ end
8
+
9
+ it "behaves like an array" do
10
+ @collection.size.should == 5
11
+ @collection[0] = "1"
12
+ @collection[0].should == "1"
13
+ end
14
+
15
+ it "parses entry to items" do
16
+ @collection[0]['displayName'].should == "Bob Johnson"
17
+ end
18
+
19
+ it "parses emails to list" do
20
+ @collection[0]['emails'].should == ["bob@example.com"]
21
+ end
22
+
23
+ it "parses emails to list with multiple emails" do
24
+ @collection[2]['emails'].should == ["fred.williams@example.com","fred@example.com"]
25
+ end
26
+
27
+ it "holds additional_info" do
28
+ @collection.additional_info['startIndex'].should == 1
29
+ @collection.additional_info['itemsPerPage'].should == 5
30
+ @collection.additional_info['totalResults'].should == 5
31
+ end
32
+ end
@@ -0,0 +1,31 @@
1
+ require File.expand_path("../spec_helper", File.dirname(__FILE__))
2
+
3
+ class User
4
+ include RPXNow::UserIntegration
5
+
6
+ def id
7
+ 5
8
+ end
9
+ end
10
+
11
+ describe RPXNow::UserProxy do
12
+ before do
13
+ RPXNow.unmap('http://test.myopenid.com', 5)
14
+ end
15
+
16
+ it "has identifiers" do
17
+ RPXNow.map('http://test.myopenid.com', 5)
18
+ User.new.rpx.identifiers.should == ['http://test.myopenid.com']
19
+ end
20
+
21
+ it "can map" do
22
+ User.new.rpx.map('http://test.myopenid.com')
23
+ User.new.rpx.identifiers.should == ['http://test.myopenid.com']
24
+ end
25
+
26
+ it "can unmap" do
27
+ RPXNow.map('http://test.myopenid.com', 5)
28
+ User.new.rpx.unmap('http://test.myopenid.com')
29
+ User.new.rpx.identifiers.should == []
30
+ end
31
+ end
@@ -0,0 +1,278 @@
1
+ require File.expand_path("spec_helper", File.dirname(__FILE__))
2
+
3
+ describe RPXNow do
4
+ describe :api_key= do
5
+ it "stores the api key, so i do not have to supply everytime" do
6
+ RPXNow.api_key='XX'
7
+ RPXNow.expects(:post).with{|x,data|data[:apiKey]=='XX'}.returns mock(:code=>'200', :body=>%Q({"stat":"ok"}))
8
+ RPXNow.mappings(1)
9
+ end
10
+ end
11
+
12
+ describe :api_version= do
13
+ it "can be set to a api_version globally" do
14
+ RPXNow.api_version = 5
15
+ RPXNow.popup_code('x','y','z').should =~ %r(/openid/v5/signin)
16
+ end
17
+ end
18
+
19
+ describe :embed_code do
20
+ it "contains the subdomain" do
21
+ RPXNow.embed_code('xxx','my_url').should =~ /xxx/
22
+ end
23
+
24
+ it "contains the url" do
25
+ RPXNow.embed_code('xxx','my_url').should =~ /token_url=my_url/
26
+ end
27
+
28
+ it "defaults to English" do
29
+ RPXNow.embed_code('xxx', 'my_url').should =~ /language_preference=en/
30
+ end
31
+
32
+ it "has a changeable language" do
33
+ RPXNow.embed_code('xxx', 'my_url', :language => 'es').should =~ /language_preference=es/
34
+ end
35
+
36
+ it "defaults to 400px width" do
37
+ RPXNow.embed_code('xxx', 'my_url').should =~ /width:400px;/
38
+ end
39
+
40
+ it "has a changeable width" do
41
+ RPXNow.embed_code('xxx', 'my_url', :width => '300').should =~ /width:300px;/
42
+ end
43
+
44
+ it "defaults to 240px height" do
45
+ RPXNow.embed_code('xxx', 'my_url').should =~ /height:240px;/
46
+ end
47
+
48
+ it "has a changeable height" do
49
+ RPXNow.embed_code('xxx', 'my_url', :height => '500').should =~ /height:500px;/
50
+ end
51
+ end
52
+
53
+ describe :popup_code do
54
+ it "defaults to obtrusive output" do
55
+ RPXNow.popup_code('sign on', 'subdomain', 'http://fake.domain.com/').should =~ /script src=/
56
+ end
57
+
58
+ it "can build an unobtrusive widget with specific version" do
59
+ expected = %Q(<a class="rpxnow" href="https://subdomain.rpxnow.com/openid/v300/signin?token_url=http://fake.domain.com/">sign on</a>)
60
+ RPXNow.popup_code('sign on', 'subdomain', 'http://fake.domain.com/', { :unobtrusive => true, :api_version => 300 }).should == expected
61
+ end
62
+
63
+ it "allows to specify the version of the widget" do
64
+ RPXNow.popup_code('x','y','z', :api_version => 300).should =~ %r(/openid/v300/signin)
65
+ end
66
+
67
+ it "defaults to widget version 2" do
68
+ RPXNow.popup_code('x','y','z').should =~ %r(/openid/v2/signin)
69
+ end
70
+
71
+ it "defaults to english" do
72
+ RPXNow.popup_code('x','y','z').should =~ /RPXNOW.language_preference = 'en'/
73
+ end
74
+
75
+ it "has a changeable language" do
76
+ RPXNow.popup_code('x','y','z',:language=>'de').should =~ /RPXNOW.language_preference = 'de'/
77
+ end
78
+ end
79
+
80
+ describe :user_data do
81
+ before do
82
+ @response_body = %Q({"profile":{"verifiedEmail":"grosser.michael@googlemail.com","displayName":"Michael Grosser","preferredUsername":"grosser.michael","identifier":"https:\/\/www.google.com\/accounts\/o8\/id?id=AItOawmaOlyYezg_WfbgP_qjaUyHjmqZD9qNIVM","email":"grosser.michael@gmail.com"},"stat":"ok"})
83
+ @fake_user_data = {'profile'=>{}}
84
+ end
85
+
86
+ def fake_response
87
+ mock(:code=>"200",:body=>@response_body)
88
+ end
89
+
90
+ it "raises ApiError when used with an invalid token" do
91
+ lambda{
92
+ RPXNow.user_data('xxxx')
93
+ }.should raise_error(RPXNow::ApiError)
94
+ end
95
+
96
+ it "is empty when used with an unknown token" do
97
+ RPXNow.user_data('60d8c6374f4e9d290a7b55f39da7cc6435aef3d3').should == nil
98
+ end
99
+
100
+ it "parses JSON response to user data" do
101
+ RPXNow.expects(:post).returns fake_response
102
+ RPXNow.user_data('').should == {:name=>'Michael Grosser',:email=>'grosser.michael@googlemail.com',:identifier=>"https://www.google.com/accounts/o8/id?id=AItOawmaOlyYezg_WfbgP_qjaUyHjmqZD9qNIVM", :username => 'grosser.michael'}
103
+ end
104
+
105
+ it "adds a :id when primaryKey was returned" do
106
+ @response_body.sub!(%Q("verifiedEmail"), %Q("primaryKey":"2","verifiedEmail"))
107
+ RPXNow.expects(:post).returns fake_response
108
+ RPXNow.user_data('')[:id].should == '2'
109
+ end
110
+
111
+ it "handles primaryKeys that are not numeric" do
112
+ @response_body.sub!(%Q("verifiedEmail"), %Q("primaryKey":"dbalatero","verifiedEmail"))
113
+ RPXNow.expects(:post).returns fake_response
114
+ RPXNow.user_data('')[:id].should == 'dbalatero'
115
+ end
116
+
117
+ it "hands JSON response to supplied block" do
118
+ RPXNow.expects(:post).returns mock(:code=>'200',:body=>%Q({"x":"1","stat":"ok"}))
119
+ response = nil
120
+ RPXNow.user_data(''){|data| response = data}
121
+ response.should == {"x" => "1", "stat" => "ok"}
122
+ end
123
+
124
+ it "returns what the supplied block returned" do
125
+ RPXNow.expects(:post).returns mock(:code=>'200',:body=>%Q({"x":"1","stat":"ok"}))
126
+ RPXNow.user_data(''){|data| "x"}.should == 'x'
127
+ end
128
+
129
+ it "can send additional parameters" do
130
+ RPXNow.expects(:post).with{|url,data|
131
+ data[:extended].should == 'true'
132
+ }.returns fake_response
133
+ RPXNow.user_data('',:extended=>'true')
134
+ end
135
+
136
+ it "works with api key as 2nd parameter (backwards compatibility)" do
137
+ RPXNow.expects(:secure_json_post).with('/api/v2/auth_info', :apiKey=>'THE KEY', :token=>'id').returns @fake_user_data
138
+ RPXNow.user_data('id', 'THE KEY')
139
+ RPXNow.api_key.should == API_KEY
140
+ end
141
+
142
+ it "works with api key as 2nd parameter and options (backwards compatibility)" do
143
+ RPXNow.expects(:secure_json_post).with('/api/v2/auth_info', :apiKey=>'THE KEY', :extended=>'abc', :token=>'id' ).returns @fake_user_data
144
+ RPXNow.user_data('id', 'THE KEY', :extended=>'abc')
145
+ RPXNow.api_key.should == API_KEY
146
+ end
147
+
148
+ it "works with api version as option (backwards compatibility)" do
149
+ RPXNow.expects(:secure_json_post).with('/api/v123/auth_info', :apiKey=>API_KEY, :token=>'id', :extended=>'abc').returns @fake_user_data
150
+ RPXNow.user_data('id', :extended=>'abc', :api_version=>123)
151
+ RPXNow.api_version.should == API_VERSION
152
+ end
153
+ end
154
+
155
+ describe :set_status do
156
+ before do
157
+ @response_body = %Q({ "stat": "ok" })
158
+ end
159
+
160
+ def fake_response
161
+ mock(:code=>"200",:body=>@response_body)
162
+ end
163
+
164
+ it "parses JSON response to result hash" do
165
+ RPXNow.expects(:post).returns fake_response
166
+ RPXNow.set_status('identifier', 'Chillen...').should == {'stat' => 'ok'}
167
+ end
168
+ end
169
+
170
+ describe :read_user_data_from_response do
171
+ it "reads secondary names" do
172
+ RPXNow.send(:read_user_data_from_response,{'profile'=>{'preferredUsername'=>'1'}})[:name].should == '1'
173
+ end
174
+
175
+ it "parses email when no name is found" do
176
+ RPXNow.send(:read_user_data_from_response,{'profile'=>{'email'=>'1@xxx.com'}})[:name].should == '1'
177
+ end
178
+ end
179
+
180
+ describe :contacts do
181
+ it "finds all contacts" do
182
+ response = JSON.parse(File.read('spec/fixtures/get_contacts_response.json'))
183
+ RPXNow.expects(:secure_json_post).with('/api/v2/get_contacts',:identifier=>'xx', :apiKey=>API_KEY).returns response
184
+ RPXNow.contacts('xx').size.should == 5
185
+ end
186
+ end
187
+
188
+ describe :parse_response do
189
+ it "parses json when status is ok" do
190
+ response = mock(:code=>'200', :body=>%Q({"stat":"ok","data":"xx"}))
191
+ RPXNow.send(:parse_response, response)['data'].should == "xx"
192
+ end
193
+
194
+ it "raises when there is a communication error" do
195
+ response = stub(:code=>'200', :body=>%Q({"err":"wtf","stat":"ok"}))
196
+ lambda{
197
+ RPXNow.send(:parse_response,response)
198
+ }.should raise_error(RPXNow::ApiError)
199
+ end
200
+
201
+ it "raises when service has downtime" do
202
+ response = stub(:code=>'200', :body=>%Q({"err":{"code":-1},"stat":"ok"}))
203
+ lambda{
204
+ RPXNow.send(:parse_response,response)
205
+ }.should raise_error(RPXNow::ServiceUnavailableError)
206
+ end
207
+
208
+ it "raises when service is down" do
209
+ response = stub(:code=>'400',:body=>%Q({"stat":"err"}))
210
+ lambda{
211
+ RPXNow.send(:parse_response,response)
212
+ }.should raise_error(RPXNow::ServiceUnavailableError)
213
+ end
214
+ end
215
+
216
+ describe :mappings do
217
+ it "parses JSON response to unmap data" do
218
+ RPXNow.expects(:post).returns mock(:code=>'200',:body=>%Q({"stat":"ok", "identifiers": ["http://test.myopenid.com/"]}))
219
+ RPXNow.mappings(1, "x").should == ["http://test.myopenid.com/"]
220
+ end
221
+ end
222
+
223
+ describe :map do
224
+ it "adds a mapping" do
225
+ RPXNow.expects(:post).returns mock(:code=>'200',:body=>%Q({"stat":"ok"}))
226
+ RPXNow.map('http://test.myopenid.com',1, API_KEY)
227
+ end
228
+ end
229
+
230
+ describe :unmap do
231
+ it "unmaps a indentifier" do
232
+ RPXNow.expects(:post).returns mock(:code=>'200',:body=>%Q({"stat":"ok"}))
233
+ RPXNow.unmap('http://test.myopenid.com', 1, "x")
234
+ end
235
+
236
+ it "can be called with a specific version" do
237
+ RPXNow.expects(:secure_json_post).with{|a,b|a == "/api/v300/unmap"}
238
+ RPXNow.unmap('http://test.myopenid.com', 1, :api_key=>'xxx', :api_version=>300)
239
+ end
240
+ end
241
+
242
+ describe :mapping_integration do
243
+ before do
244
+ @k1 = 'http://test.myopenid.com'
245
+ RPXNow.unmap(@k1, 1)
246
+ @k2 = 'http://test-2.myopenid.com'
247
+ RPXNow.unmap(@k2, 1)
248
+ end
249
+
250
+ it "has no mappings when nothing was mapped" do
251
+ RPXNow.mappings(1).should == []
252
+ end
253
+
254
+ it "unmaps mapped keys" do
255
+ RPXNow.map(@k2, 1)
256
+ RPXNow.unmap(@k2, 1)
257
+ RPXNow.mappings(1).should == []
258
+ end
259
+
260
+ it "maps keys to a primary key and then retrieves them" do
261
+ RPXNow.map(@k1, 1)
262
+ RPXNow.map(@k2, 1)
263
+ RPXNow.mappings(1).sort.should == [@k2,@k1]
264
+ end
265
+
266
+ it "does not add duplicate mappings" do
267
+ RPXNow.map(@k1, 1)
268
+ RPXNow.map(@k1, 1)
269
+ RPXNow.mappings(1).should == [@k1]
270
+ end
271
+
272
+ it "finds all mappings" do
273
+ RPXNow.map(@k1, 1)
274
+ RPXNow.map(@k2, 2)
275
+ RPXNow.all_mappings.sort.should == [["1", ["http://test.myopenid.com"]], ["2", ["http://test-2.myopenid.com"]]]
276
+ end
277
+ end
278
+ end
@@ -0,0 +1,20 @@
1
+ # ---- requirements
2
+ require 'rubygems'
3
+ require 'spec'
4
+ require 'mocha'
5
+
6
+ $LOAD_PATH << File.expand_path("../lib", File.dirname(__FILE__))
7
+
8
+ # ---- setup environment/plugin
9
+ require File.expand_path("../init", File.dirname(__FILE__))
10
+ API_KEY = '4b339169026742245b754fa338b9b0aebbd0a733'
11
+ API_VERSION = RPXNow.api_version
12
+
13
+ # ---- rspec
14
+ Spec::Runner.configure do |config|
15
+ config.mock_with :mocha
16
+ config.before :each do
17
+ RPXNow.api_key = API_KEY
18
+ RPXNow.api_version = API_VERSION
19
+ end
20
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jackdempsey-rpx_now
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.8
5
+ platform: ruby
6
+ authors:
7
+ - Michael Grosser
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-21 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activesupport
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description:
26
+ email: grosser.michael@gmail.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README.markdown
33
+ files:
34
+ - CHANGELOG
35
+ - MIGRATION
36
+ - README.markdown
37
+ - Rakefile
38
+ - VERSION.yml
39
+ - init.rb
40
+ - lib/rpx_now.rb
41
+ - lib/rpx_now/contacts_collection.rb
42
+ - lib/rpx_now/user_integration.rb
43
+ - lib/rpx_now/user_proxy.rb
44
+ - rpx_now.gemspec
45
+ - spec/fixtures/get_contacts_response.json
46
+ - spec/rpx_now/contacts_collection_spec.rb
47
+ - spec/rpx_now/user_proxy_spec.rb
48
+ - spec/rpx_now_spec.rb
49
+ - spec/spec_helper.rb
50
+ has_rdoc: false
51
+ homepage: http://github.com/grosser/rpx_now
52
+ licenses:
53
+ post_install_message:
54
+ rdoc_options:
55
+ - --charset=UTF-8
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ version:
70
+ requirements: []
71
+
72
+ rubyforge_project:
73
+ rubygems_version: 1.3.5
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: Helper to simplify RPX Now user login/creation
77
+ test_files:
78
+ - spec/rpx_now_spec.rb
79
+ - spec/rpx_now/contacts_collection_spec.rb
80
+ - spec/rpx_now/user_proxy_spec.rb
81
+ - spec/spec_helper.rb