grosser-rpx_now 0.2 → 0.3

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