grosser-rpx_now 0.2 → 0.3

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/CHANGELOG ADDED
@@ -0,0 +1 @@
1
+ 0.3 -- RPXNow::ServerError will be thrown when something is invalid/goes wrong, so watch out (not for invalid tokens in user_data)...
data/README.markdown CHANGED
@@ -36,7 +36,7 @@ Controller
36
36
  ----------
37
37
  # simple: use defaults
38
38
  def rpx_token
39
- data = RPXNew.user_data(params[:token],'YOUR RPX API KEY') # :name=>'John Doe',:email=>'john@doe.com',:identifier=>'blug.google.com/openid/dsdfsdfs3f3'
39
+ data = RPXNow.user_data(params[:token],'YOUR RPX API KEY') # :name=>'John Doe',:email=>'john@doe.com',:identifier=>'blug.google.com/openid/dsdfsdfs3f3'
40
40
  #when no user_data was found, data is empty, you may want to handle that seperatly...
41
41
  #your user model must have an identifier column
42
42
  self.current_user = User.find_by_identifier(data[:identifier]) || User.create!(data)
@@ -44,10 +44,31 @@ Controller
44
44
  end
45
45
 
46
46
  # process the raw response yourself:
47
- RPXNew.user_data(params[:token],'YOUR RPX API KEY'){|raw| {:email=>raw['profile']['verifiedEmail']}}
47
+ RPXNow.user_data(params[:token],'YOUR RPX API KEY'){|raw| {:email=>raw['profile']['verifiedEmail']}}
48
48
 
49
49
  # request extended parameters (most users and APIs do not supply them)
50
- RPXNew.user_data(params[:token],'YOUR RPX API KEY',:extended=>'true'){|raw| ...have a look at the RPX API DOCS...}
50
+ RPXNow.user_data(params[:token],'YOUR RPX API KEY',:extended=>'true'){|raw| ...have a look at the RPX API DOCS...}
51
+
52
+ Advanced: Mappings
53
+ ------------------
54
+ You can map your primary keys (e.g. user.id) to identifiers, so that
55
+ users can login to the same account with multiple identifiers.
56
+ #add a mapping
57
+ RPXNow.map(identifier,primary_key,'YOUR RPX API KEY')
58
+
59
+ #remove a mapping
60
+ RPXNow.unmap(identifier,primary_key,'YOUR RPX API KEY')
61
+
62
+ #show mappings
63
+ RPXNow.mappings(primary_key,'YOUR RPX API KEY') # [identifier1,identifier2,...]
64
+
65
+ After a primary key is mapped to an identifier, when a user logs in with this identifier,
66
+ `RPXNow.user_data` will contain his `primaryKey` as `:id`.
67
+
68
+ TODO
69
+ ====
70
+ - remove activesupport dependency, use something smaller (json gem looks good but: JSON.parse("{x:1}")==BOOM)
71
+ - validate RPXNow.com SSL certificate
51
72
 
52
73
  Author
53
74
  ======
data/Rakefile CHANGED
@@ -10,7 +10,7 @@ end
10
10
 
11
11
  #Gemspec
12
12
  porject_name = 'rpx_now'
13
- Echoe.new(porject_name , '0.2') do |p|
13
+ Echoe.new(porject_name , '0.3') do |p|
14
14
  p.description = "Helper to simplify RPX Now user login/creation"
15
15
  p.url = "http://github.com/grosser/#{porject_name}"
16
16
  p.author = "Michael Grosser"
data/lib/rpx_now.rb CHANGED
@@ -1,14 +1,34 @@
1
1
  require 'activesupport'
2
2
  module RPXNow
3
3
  extend self
4
+
5
+ # retrieve the users data, or return nil when nothing could be read/token was invalid or data was not found
4
6
  def user_data(token,api_key,parameters={})
5
- raise "NO API KEY" if api_key.blank?
6
- data = post('https://rpxnow.com/api/v2/auth_info',{:token=>token,:apiKey=>api_key}.merge(parameters))
7
- data = ActiveSupport::JSON.decode(data)
8
- return if data['err']
7
+ begin
8
+ data = secure_json_post('https://rpxnow.com/api/v2/auth_info',{:token=>token,:apiKey=>api_key}.merge(parameters))
9
+ rescue ServerError
10
+ return nil if $!.to_s =~ /token/ or $!.to_s=~/Data not found/
11
+ raise
12
+ end
9
13
  if block_given? then yield(data) else read_user_data_from_response(data) end
10
14
  end
11
15
 
16
+ # maps an identifier to an primary-key (e.g. user.id)
17
+ def map(identifier,primary_key,api_key,options={})
18
+ secure_json_post('https://rpxnow.com/api/v2/map',{:identifier=>identifier,:primaryKey=>primary_key,:apiKey=>api_key}.merge(options))
19
+ end
20
+
21
+ # un-maps an identifier to an primary-key (e.g. user.id)
22
+ def unmap(identifier,primary_key,api_key)
23
+ secure_json_post('https://rpxnow.com/api/v2/unmap',{:identifier=>identifier,:primaryKey=>primary_key,:apiKey=>api_key})
24
+ end
25
+
26
+ # returns an array of identifiers which are mapped to one of your primary-keys (e.g. user.id)
27
+ def mappings(primary_key,api_key)
28
+ data = secure_json_post('https://rpxnow.com/api/v2/mappings',{:primaryKey=>primary_key,:apiKey=>api_key})
29
+ data['identifiers']
30
+ end
31
+
12
32
  def embed_code(subdomain,url)
13
33
  <<EOF
14
34
  <iframe src="https://#{subdomain}.rpxnow.com/openid/embed?token_url=#{url}"
@@ -19,7 +39,7 @@ EOF
19
39
 
20
40
  def popup_code(text,subdomain,url,options={})
21
41
  <<EOF
22
- <a class="rpxnow" onclick="return false;" href="https://rathershort.rpxnow.com/openid/v2/signin?token_url=#{url}">
42
+ <a class="rpxnow" onclick="return false;" href="https://#{subdomain}.rpxnow.com/openid/v2/signin?token_url=#{url}">
23
43
  #{text}
24
44
  </a>
25
45
  <script src="https://rpxnow.com/openid/v2/widget" type="text/javascript"></script>
@@ -43,6 +63,14 @@ private
43
63
  data[:identifier] = user_data['identifier']
44
64
  data[:email] = user_data['verifiedEmail'] || user_data['email']
45
65
  data[:name] = user_data['displayName'] || user_data['preferredUsername'] || data[:email].sub(/@.*/,'')
66
+ data[:id] = user_data['primaryKey'].to_i unless user_data['primaryKey'].to_s.empty?
67
+ data
68
+ end
69
+
70
+ def secure_json_post(url,data={})
71
+ data = ActiveSupport::JSON.decode(post(url,data))
72
+ raise ServerError.new(data['err']) if data['err']
73
+ raise ServerError.new(data.inspect) unless data['stat']=='ok'
46
74
  data
47
75
  end
48
76
 
@@ -53,9 +81,18 @@ private
53
81
  if url.scheme == 'https'
54
82
  require 'net/https'
55
83
  http.use_ssl = true
84
+ #TODO do we really want to verify the certificate? http://notetoself.vrensk.com/2008/09/verified-https-in-ruby/
85
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
56
86
  end
57
87
  resp, data = http.post(url.path, data.to_query)
58
88
  raise "POST FAILED:"+resp.inspect unless resp.is_a? Net::HTTPOK
59
- return data
89
+ data
90
+ end
91
+
92
+ class ServerError < Exception
93
+ #to_s returns message(which is a hash...)
94
+ def to_s
95
+ super.to_s
96
+ end
60
97
  end
61
98
  end
data/rpx_now.gemspec CHANGED
@@ -2,15 +2,15 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{rpx_now}
5
- s.version = "0.2"
5
+ s.version = "0.3"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Michael Grosser"]
9
- s.date = %q{2009-01-24}
9
+ s.date = %q{2009-01-28}
10
10
  s.description = %q{Helper to simplify RPX Now user login/creation}
11
11
  s.email = %q{grosser.michael@gmail.com}
12
- s.extra_rdoc_files = ["lib/rpx_now.rb", "README.markdown"]
13
- s.files = ["Manifest", "lib/rpx_now.rb", "init.rb", "Rakefile", "README.markdown", "rpx_now.gemspec"]
12
+ s.extra_rdoc_files = ["CHANGELOG", "lib/rpx_now.rb", "README.markdown"]
13
+ s.files = ["Manifest", "CHANGELOG", "lib/rpx_now.rb", "spec/rpx_now_spec.rb", "spec/spec_helper.rb", "rpx_now.gemspec", "init.rb", "Rakefile", "README.markdown"]
14
14
  s.has_rdoc = true
15
15
  s.homepage = %q{http://github.com/grosser/rpx_now}
16
16
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Rpx_now", "--main", "README.markdown"]
@@ -0,0 +1,135 @@
1
+ require File.expand_path("spec_helper", File.dirname(__FILE__))
2
+
3
+ API_KEY = '4b339169026742245b754fa338b9b0aebbd0a733'
4
+
5
+ describe RPXNow do
6
+ describe :embed_code do
7
+ it "contains the subdomain" do
8
+ RPXNow.embed_code('xxx','my_url').should =~ /xxx/
9
+ end
10
+ it "contains the url" do
11
+ RPXNow.embed_code('xxx','my_url').should =~ /token_url=my_url/
12
+ end
13
+ end
14
+
15
+ describe :popup_code do
16
+ it "defaults to english" do
17
+ RPXNow.popup_code('x','y','z').should =~ /RPXNOW.language_preference = 'en'/
18
+ end
19
+ it "has a changeable language" do
20
+ RPXNow.popup_code('x','y','z',:language=>'de').should =~ /RPXNOW.language_preference = 'de'/
21
+ end
22
+ end
23
+
24
+ describe :user_data do
25
+ def fake_response
26
+ '{"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"}'
27
+ end
28
+ it "is empty when used with an invalid token" do
29
+ RPXNow.user_data('xxxx',API_KEY).should == nil
30
+ end
31
+ it "is empty when used with an unknown token" do
32
+ RPXNow.user_data('60d8c6374f4e9d290a7b55f39da7cc6435aef3d3',API_KEY).should == nil
33
+ end
34
+ it "parses JSON response to user data" do
35
+ RPXNow.expects(:post).returns fake_response
36
+ RPXNow.user_data('','x').should == {:name=>'Michael Grosser',:email=>'grosser.michael@googlemail.com',:identifier=>"https://www.google.com/accounts/o8/id?id=AItOawmaOlyYezg_WfbgP_qjaUyHjmqZD9qNIVM"}
37
+ end
38
+ it "adds a :id when primaryKey was returned" do
39
+ RPXNow.expects(:post).returns fake_response.sub('"verifiedEmail"','primaryKey:"2","verifiedEmail"')
40
+ RPXNow.user_data('','x')[:id].should == 2
41
+ end
42
+ it "hands JSON response to supplied block" do
43
+ RPXNow.expects(:post).returns "{x:1,stat:'ok'}"
44
+ response = nil
45
+ RPXNow.user_data('','x'){|data| response = data}
46
+ response.should == {'x'=>1,'stat'=>'ok'}
47
+ end
48
+ it "returns what the supplied block returned" do
49
+ RPXNow.expects(:post).returns "{x:1,stat:'ok'}"
50
+ RPXNow.user_data('','x'){|data| "x"}.should == 'x'
51
+ end
52
+ it "can send additional parameters" do
53
+ RPXNow.expects(:post).with{|url,data|
54
+ data[:extended].should == 'true'
55
+ }.returns fake_response
56
+ RPXNow.user_data('','x',:extended=>'true')
57
+ end
58
+ end
59
+
60
+ describe :read_user_data_from_response do
61
+ it "reads secondary names" do
62
+ RPXNow.send(:read_user_data_from_response,{'profile'=>{'preferredUsername'=>'1'}})[:name].should == '1'
63
+ end
64
+ it "parses email when no name is found" do
65
+ RPXNow.send(:read_user_data_from_response,{'profile'=>{'email'=>'1@xxx.com'}})[:name].should == '1'
66
+ end
67
+ end
68
+
69
+ describe :secure_json_post do
70
+ it "parses json when status is ok" do
71
+ RPXNow.expects(:post).returns '{stat:"ok",data:"xx"}'
72
+ RPXNow.send(:secure_json_post,'xx')['data'].should == 'xx'
73
+ end
74
+ it "raises when there is a communication error" do
75
+ RPXNow.expects(:post).returns '{"err":"wtf",stat:"ok"}'
76
+ lambda{RPXNow.send(:secure_json_post,'xx')}.should raise_error RPXNow::ServerError
77
+ end
78
+ it "raises when status is not ok" do
79
+ RPXNow.expects(:post).returns '{"stat":"err"}'
80
+ lambda{RPXNow.send(:secure_json_post,'xx')}.should raise_error RPXNow::ServerError
81
+ end
82
+ end
83
+
84
+ describe :mappings do
85
+ it "parses JSON response to unmap data" do
86
+ RPXNow.expects(:post).returns '{"stat":"ok", "identifiers": ["http://test.myopenid.com/"]}'
87
+ RPXNow.mappings(1, "x").should == ["http://test.myopenid.com/"]
88
+ end
89
+ end
90
+
91
+ describe :map do
92
+ it "adds a mapping" do
93
+ RPXNow.expects(:post).returns '{"stat":"ok"}'
94
+ RPXNow.map('http://test.myopenid.com',1, API_KEY)
95
+ end
96
+ end
97
+
98
+ describe :unmap do
99
+ it "unmaps a indentifier" do
100
+ RPXNow.expects(:post).returns '{"stat":"ok"}'
101
+ RPXNow.unmap('http://test.myopenid.com', 1, "x")
102
+ end
103
+ end
104
+
105
+ describe :mapping_integration do
106
+ before do
107
+ @k1 = 'http://test.myopenid.com'
108
+ RPXNow.unmap(@k1, 1, API_KEY)
109
+ @k2 = 'http://test-2.myopenid.com'
110
+ RPXNow.unmap(@k2, 1, API_KEY)
111
+ end
112
+
113
+ it "has no mappings when nothing was mapped" do
114
+ RPXNow.mappings(1,API_KEY).should == []
115
+ end
116
+
117
+ it "unmaps mapped keys" do
118
+ RPXNow.map(@k2, 1, API_KEY)
119
+ RPXNow.unmap(@k2, 1, API_KEY)
120
+ RPXNow.mappings(1, API_KEY).should == []
121
+ end
122
+
123
+ it "maps keys to a primary key and then retrieves them" do
124
+ RPXNow.map(@k1, 1, API_KEY)
125
+ RPXNow.map(@k2, 1, API_KEY)
126
+ RPXNow.mappings(1,API_KEY).sort.should == [@k2,@k1]
127
+ end
128
+
129
+ it "does not add duplicate mappings" do
130
+ RPXNow.map(@k1, 1, API_KEY)
131
+ RPXNow.map(@k1, 1, API_KEY)
132
+ RPXNow.mappings(1,API_KEY).should == [@k1]
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,36 @@
1
+ # ---- requirements
2
+ require 'rubygems'
3
+ require 'spec'
4
+ require 'mocha'
5
+
6
+ $LOAD_PATH << File.expand_path("../lib", File.dirname(__FILE__))
7
+
8
+
9
+ # ---- rspec
10
+ Spec::Runner.configure do |config|
11
+ config.mock_with :mocha
12
+ end
13
+
14
+
15
+ # ---- bugfix
16
+ #`exit?': undefined method `run?' for Test::Unit:Module (NoMethodError)
17
+ #can be solved with require test/unit but this will result in extra test-output
18
+ module Test
19
+ module Unit
20
+ def self.run?
21
+ true
22
+ end
23
+ end
24
+ end
25
+
26
+
27
+ # ---- setup environment/plugin
28
+ require File.expand_path("../init", File.dirname(__FILE__))
29
+
30
+
31
+ # ---- Helpers
32
+ def pending_it(text,&block)
33
+ it text do
34
+ pending(&block)
35
+ end
36
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grosser-rpx_now
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.2"
4
+ version: "0.3"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Grosser
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-01-24 00:00:00 -08:00
12
+ date: 2009-01-28 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -37,15 +37,19 @@ executables: []
37
37
  extensions: []
38
38
 
39
39
  extra_rdoc_files:
40
+ - CHANGELOG
40
41
  - lib/rpx_now.rb
41
42
  - README.markdown
42
43
  files:
43
44
  - Manifest
45
+ - CHANGELOG
44
46
  - lib/rpx_now.rb
47
+ - spec/rpx_now_spec.rb
48
+ - spec/spec_helper.rb
49
+ - rpx_now.gemspec
45
50
  - init.rb
46
51
  - Rakefile
47
52
  - README.markdown
48
- - rpx_now.gemspec
49
53
  has_rdoc: true
50
54
  homepage: http://github.com/grosser/rpx_now
51
55
  post_install_message: