openid_connect 0.0.11 → 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- openid_connect (0.0.10)
4
+ openid_connect (0.0.11)
5
5
  activemodel (>= 3)
6
6
  attr_required (>= 0.0.3)
7
7
  json (>= 1.4.3)
@@ -62,8 +62,8 @@ GEM
62
62
  mail (>= 2.2.5)
63
63
  validate_url (0.2.0)
64
64
  activemodel (>= 3.0.0)
65
- webmock (1.7.2)
66
- addressable (> 2.2.5, ~> 2.2)
65
+ webmock (1.7.4)
66
+ addressable (~> 2.2, > 2.2.5)
67
67
  crack (>= 0.1.7)
68
68
 
69
69
  PLATFORMS
data/README.rdoc CHANGED
@@ -8,8 +8,9 @@ OpenID Connect Server & Client Library
8
8
 
9
9
  == Resources
10
10
 
11
- * View Source on GitHub (http://github.com/nov/openid_connect)
12
- * Report Issues on GitHub (http://github.com/nov/openid_connect/issues)
11
+ * View Source on GitHub (https://github.com/nov/openid_connect)
12
+ * Report Issues on GitHub (https://github.com/nov/openid_connect/issues)
13
+ * Subscribe Update Info (https://www.facebook.com/pages/OpenID-Connect/134681459957370)
13
14
 
14
15
  == Examples
15
16
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.11
1
+ 0.0.12
@@ -9,36 +9,44 @@ module OpenIDConnect
9
9
  class ResponseObject
10
10
  include ActiveModel::Validations, AttrRequired, AttrOptional
11
11
 
12
+ class ValidationFailed < Exception
13
+ attr_reader :errors
14
+
15
+ def initialize(errors)
16
+ super errors.full_messages.to_sentence
17
+ @errors = errors
18
+ end
19
+ end
20
+
12
21
  def initialize(attributes = {})
13
- all_attriutes.each do |_attr_|
22
+ all_attributes.each do |_attr_|
14
23
  self.send :"#{_attr_}=", attributes[_attr_]
15
24
  end
16
25
  attr_missing!
17
26
  end
18
27
 
19
- def all_attriutes
28
+ def all_attributes
20
29
  required_attributes + optional_attributes
21
30
  end
22
31
 
23
32
  def require_at_least_one_attributes
24
- all_blank = all_attriutes.all? do |key|
33
+ all_blank = all_attributes.all? do |key|
25
34
  self.send(key).blank?
26
35
  end
27
36
  errors.add :base, 'At least one attribute is required' if all_blank
28
37
  end
29
38
 
30
39
  def as_json(options = {})
31
- (all_attriutes - Array(hidden_attributes)).inject({}) do |hash, _attr_|
40
+ validate!
41
+ all_attributes.inject({}) do |hash, _attr_|
32
42
  hash.merge! _attr_ => self.send(_attr_)
33
43
  end.delete_if do |key, value|
34
44
  value.nil?
35
45
  end
36
46
  end
37
47
 
38
- private
39
-
40
- def hidden_attributes
41
- nil
48
+ def validate!
49
+ raise ValidationFailed.new(errors) unless valid?
42
50
  end
43
51
  end
44
52
  end
@@ -6,10 +6,13 @@ module OpenIDConnect
6
6
  class InvalidToken < Exception; end
7
7
 
8
8
  attr_required :iss, :user_id, :aud, :exp
9
- attr_optional :iso29115, :nonce, :issued_to, :secret
9
+ attr_optional :iso29115, :nonce, :issued_to
10
10
 
11
11
  def initialize(attributes = {})
12
12
  super
13
+ (all_attributes - [:exp]).each do |key|
14
+ self.send "#{key}=", self.send(key).try(:to_s)
15
+ end
13
16
  @exp = @exp.to_i
14
17
  end
15
18
 
@@ -18,19 +21,12 @@ module OpenIDConnect
18
21
  raise InvalidToken.new('Invalid audience or expired')
19
22
  end
20
23
 
21
- def to_jwt
22
- raise Exception.new('Secret Required') unless secret
23
- JWT.encode as_json, secret
24
+ def to_jwt(key, algorithm = 'RS256')
25
+ JWT.encode as_json, key, algorithm
24
26
  end
25
27
 
26
- def self.from_jwt(jwt, secret)
27
- new JWT.decode(jwt, secret).with_indifferent_access.merge(:secret => secret)
28
- end
29
-
30
- private
31
-
32
- def hidden_attributes
33
- :secret
28
+ def self.from_jwt(jwt_string, key)
29
+ new JWT.decode(jwt_string, key).with_indifferent_access
34
30
  end
35
31
  end
36
32
  end
@@ -7,9 +7,9 @@ module OpenIDConnect
7
7
  attr_optional :phone_number
8
8
 
9
9
  attr_optional :verified, :gender, :zoneinfo, :locale
10
- validates_inclusion_of :verified, :in => [true, false], :allow_nil => true
11
- validates_inclusion_of :gender, :in => [:male, :female], :allow_nil => true
12
- validates_inclusion_of :zoneinfo, :in => TZInfo::TimezoneProxy.all.collect(&:name), :allow_nil => true
10
+ validates :verified, :inclusion => {:in => [true, false]}, :allow_nil => true
11
+ validates :gender, :inclusion => {:in => ['male', 'female']}, :allow_nil => true
12
+ validates :zoneinfo, :inclusion => {:in => TZInfo::TimezoneProxy.all.collect(&:name)}, :allow_nil => true
13
13
  # TODO: validate locale
14
14
 
15
15
  attr_optional :birthday, :updated_time
@@ -25,6 +25,13 @@ module OpenIDConnect
25
25
 
26
26
  validate :require_at_least_one_attributes
27
27
 
28
+ def initialize(attributes = {})
29
+ super
30
+ (all_attributes - [:verified, :address]).each do |key|
31
+ self.send "#{key}=", self.send(key).try(:to_s)
32
+ end
33
+ end
34
+
28
35
  def validate_address
29
36
  errors.add :address, 'cannot be blank' unless address.blank? || address.valid?
30
37
  end
@@ -1,11 +1,21 @@
1
1
  module Rack::OAuth2::Server
2
2
  module IdTokenResponse
3
3
  def self.included(klass)
4
- klass.send :attr_optional, :id_token
4
+ klass.send :attr_optional, :id_token, :private_key
5
5
  klass.class_eval do
6
+ def jwt_string
7
+ case id_token
8
+ when String
9
+ id_token
10
+ when OpenIDConnect::ResponseObject::IdToken
11
+ raise AttrRequired::AttrMissing.new('private_key is required') unless private_key
12
+ id_token.to_jwt private_key
13
+ end
14
+ end
15
+
6
16
  def protocol_params_with_id_token
7
17
  protocol_params_without_id_token.merge(
8
- :id_token => id_token.try(:to_jwt)
18
+ :id_token => jwt_string
9
19
  )
10
20
  end
11
21
  alias_method_chain :protocol_params, :id_token
@@ -1,10 +1,10 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe OpenIDConnect::ResponseObject::IdToken do
4
- let(:klass) { OpenIDConnect::ResponseObject::IdToken }
5
- let(:id_token) { klass.new attributes }
6
- let(:attributes) { required_attributes }
7
- let(:ext) { 10.minutes.from_now }
4
+ let(:klass) { OpenIDConnect::ResponseObject::IdToken }
5
+ let(:id_token) { klass.new attributes }
6
+ let(:attributes) { required_attributes }
7
+ let(:ext) { 10.minutes.from_now }
8
8
  let :required_attributes do
9
9
  {
10
10
  :iss => 'https://server.example.com',
@@ -17,7 +17,7 @@ describe OpenIDConnect::ResponseObject::IdToken do
17
17
  describe 'attributes' do
18
18
  subject { klass }
19
19
  its(:required_attributes) { should == [:iss, :user_id, :aud, :exp] }
20
- its(:optional_attributes) { should == [:iso29115, :nonce, :issued_to, :secret] }
20
+ its(:optional_attributes) { should == [:iso29115, :nonce, :issued_to] }
21
21
  end
22
22
 
23
23
  describe '#verify!' do
@@ -40,31 +40,21 @@ describe OpenIDConnect::ResponseObject::IdToken do
40
40
  end
41
41
 
42
42
  describe '#to_jwt' do
43
- subject { id_token.to_jwt }
44
-
45
- context 'when secret is given' do
46
- let(:attributes) { required_attributes.merge(:secret => 'secret') }
47
- it { should be_a String }
48
- end
49
-
50
- context 'otherwise' do
51
- it do
52
- expect { id_token.to_jwt }.should raise_error OpenIDConnect::Exception, 'Secret Required'
53
- end
54
- end
43
+ subject { id_token.to_jwt private_key }
44
+ it { should be_a String }
55
45
  end
56
46
 
57
47
  describe '#as_json' do
58
48
  subject { id_token.as_json }
59
- let(:attributes) { required_attributes.merge(:secret => 'secret') }
49
+ let(:attributes) { required_attributes }
60
50
  it { should_not include :secret }
61
51
  end
62
52
 
63
53
  describe '.from_jwt' do
64
- subject { klass.from_jwt id_token.to_jwt, 'secret' }
65
- let(:attributes) { required_attributes.merge(:secret => 'secret') }
54
+ subject { klass.from_jwt id_token.to_jwt(private_key), public_key }
55
+ let(:attributes) { required_attributes }
66
56
  it { should be_a klass }
67
- [:iss, :user_id, :aud, :secret].each do |key|
57
+ [:iss, :user_id, :aud].each do |key|
68
58
  its(key) { should == attributes[key] }
69
59
  end
70
60
  its(:exp) { should == attributes[:exp].to_i }
@@ -4,16 +4,17 @@ describe OpenIDConnect::ResponseObject do
4
4
  class OpenIDConnect::ResponseObject::SubClass < OpenIDConnect::ResponseObject
5
5
  attr_required :required
6
6
  attr_optional :optional
7
+ validates :required, :inclusion => {:in => ['Required', 'required']}, :length => 1..10
7
8
  end
8
9
 
9
- let(:klass) { OpenIDConnect::ResponseObject::SubClass }
10
10
  subject { klass.new attributes }
11
+ let(:klass) { OpenIDConnect::ResponseObject::SubClass }
12
+ let :attributes do
13
+ {:required => 'Required', :optional => 'Optional'}
14
+ end
11
15
 
12
16
  context 'when required attributes are given' do
13
17
  context 'when optional attributes are given' do
14
- let :attributes do
15
- {:required => 'Required', :optional => 'Optional'}
16
- end
17
18
  its(:required) { should == 'Required' }
18
19
  its(:optional) { should == 'Optional' }
19
20
  end
@@ -45,11 +46,24 @@ describe OpenIDConnect::ResponseObject do
45
46
  end
46
47
 
47
48
  describe '#as_json' do
48
- let :attributes do
49
- {:required => 'Required', :optional => 'Optional'}
50
- end
51
49
  its(:as_json) do
52
50
  should == {:required => 'Required', :optional => 'Optional'}
53
51
  end
54
52
  end
53
+
54
+ describe '#validate!' do
55
+ let(:invalid) do
56
+ instance = klass.new attributes
57
+ instance.required = 'Out of List and Too Long'
58
+ instance
59
+ end
60
+
61
+ it 'should raise OpenIDConnect::ResponseObject::ValidationFailed with ActiveModel::Errors' do
62
+ expect { invalid.validate! }.should raise_error(OpenIDConnect::ResponseObject::ValidationFailed) { |e|
63
+ e.message.should include 'Required is not included in the list'
64
+ e.message.should include 'Required is too long (maximum is 10 characters)'
65
+ e.errors.should be_a ActiveModel::Errors
66
+ }
67
+ end
68
+ end
55
69
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper.rb'
2
2
 
3
3
  describe OpenIDConnect::Server::IdToken do
4
- let(:request) { Rack::MockRequest.new app }
4
+ let(:request) { Rack::MockRequest.new app }
5
5
  let :app do
6
6
  OpenIDConnect::Server::IdToken.new do |req, res|
7
7
  res.id_token = id_token
@@ -14,7 +14,7 @@ describe OpenIDConnect::Server::IdToken do
14
14
  )
15
15
  end
16
16
  let :params do
17
- {:id_token => id_token.to_jwt}
17
+ {:id_token => id_token.to_jwt(private_key) }
18
18
  end
19
19
  let :id_token do
20
20
  OpenIDConnect::ResponseObject::IdToken.new(
@@ -24,11 +24,32 @@ describe Rack::OAuth2::Server::Authorize::Extension::CodeAndToken do
24
24
  response.code = 'code'
25
25
  response.access_token = bearer_token
26
26
  response.id_token = id_token
27
+ response.private_key = private_key
27
28
  response.approve!
28
29
  end
29
30
  end
30
31
  its(:status) { should == 302 }
31
- its(:location) { should == "#{redirect_uri}?code=code#access_token=access_token&id_token=#{id_token.to_jwt}&token_type=bearer" }
32
+ its(:location) { should == "#{redirect_uri}?code=code#access_token=access_token&id_token=#{id_token.to_jwt(private_key)}&token_type=bearer" }
33
+
34
+ context 'when id_token is String' do
35
+ let(:id_token) { 'id_token' }
36
+ its(:location) { should == "#{redirect_uri}?code=code#access_token=access_token&id_token=id_token&token_type=bearer" }
37
+ end
38
+
39
+ context 'when private_key is missing' do
40
+ let :app do
41
+ Rack::OAuth2::Server::Authorize.new do |request, response|
42
+ response.redirect_uri = redirect_uri
43
+ response.code = 'code'
44
+ response.access_token = bearer_token
45
+ response.id_token = id_token
46
+ response.approve!
47
+ end
48
+ end
49
+ it do
50
+ expect { response }.should raise_error AttrRequired::AttrMissing, 'private_key is required'
51
+ end
52
+ end
32
53
  end
33
54
 
34
55
  context "otherwise" do
@@ -22,11 +22,31 @@ describe Rack::OAuth2::Server::Authorize::Token do
22
22
  response.redirect_uri = redirect_uri
23
23
  response.access_token = bearer_token
24
24
  response.id_token = id_token
25
+ response.private_key = private_key
25
26
  response.approve!
26
27
  end
27
28
  end
28
29
  its(:status) { should == 302 }
29
- its(:location) { should == "#{redirect_uri}#access_token=access_token&id_token=#{id_token.to_jwt}&token_type=bearer" }
30
+ its(:location) { should == "#{redirect_uri}#access_token=access_token&id_token=#{id_token.to_jwt(private_key)}&token_type=bearer" }
31
+
32
+ context 'when id_token is String' do
33
+ let(:id_token) { 'id_token' }
34
+ its(:location) { should == "#{redirect_uri}#access_token=access_token&id_token=id_token&token_type=bearer" }
35
+ end
36
+
37
+ context 'when private_key is missing' do
38
+ let :app do
39
+ Rack::OAuth2::Server::Authorize.new do |request, response|
40
+ response.redirect_uri = redirect_uri
41
+ response.access_token = bearer_token
42
+ response.id_token = id_token
43
+ response.approve!
44
+ end
45
+ end
46
+ it do
47
+ expect { response }.should raise_error AttrRequired::AttrMissing, 'private_key is required'
48
+ end
49
+ end
30
50
  end
31
51
 
32
52
  context "otherwise" do
@@ -26,10 +26,28 @@ describe Rack::OAuth2::Server::Token::AuthorizationCode do
26
26
  Rack::OAuth2::Server::Token.new do |request, response|
27
27
  response.access_token = Rack::OAuth2::AccessToken::Bearer.new(:access_token => 'access_token')
28
28
  response.id_token = id_token
29
+ response.private_key = private_key
29
30
  end
30
31
  end
31
32
  its(:status) { should == 200 }
32
- its(:body) { should include "\"id_token\":\"#{id_token.to_jwt}\"" }
33
+ its(:body) { should include "\"id_token\":\"#{id_token.to_jwt(private_key)}\"" }
34
+
35
+ context 'when id_token is String' do
36
+ let(:id_token) { 'id_token' }
37
+ its(:body) { should include "\"id_token\":\"id_token\"" }
38
+ end
39
+
40
+ context 'when private_key is missing' do
41
+ let :app do
42
+ Rack::OAuth2::Server::Token.new do |request, response|
43
+ response.access_token = Rack::OAuth2::AccessToken::Bearer.new(:access_token => 'access_token')
44
+ response.id_token = id_token
45
+ end
46
+ end
47
+ it do
48
+ expect { response }.should raise_error AttrRequired::AttrMissing, 'private_key is required'
49
+ end
50
+ end
33
51
  end
34
52
 
35
53
  context "otherwise" do
@@ -25,10 +25,28 @@ describe Rack::OAuth2::Server::Token::RefreshToken do
25
25
  Rack::OAuth2::Server::Token.new do |request, response|
26
26
  response.access_token = Rack::OAuth2::AccessToken::Bearer.new(:access_token => 'access_token')
27
27
  response.id_token = id_token
28
+ response.private_key = private_key
28
29
  end
29
30
  end
30
31
  its(:status) { should == 200 }
31
- its(:body) { should include "\"id_token\":\"#{id_token.to_jwt}\"" }
32
+ its(:body) { should include "\"id_token\":\"#{id_token.to_jwt(private_key)}\"" }
33
+
34
+ context 'when id_token is String' do
35
+ let(:id_token) { 'id_token' }
36
+ its(:body) { should include "\"id_token\":\"id_token\"" }
37
+ end
38
+
39
+ context 'when private_key is missing' do
40
+ let :app do
41
+ Rack::OAuth2::Server::Token.new do |request, response|
42
+ response.access_token = Rack::OAuth2::AccessToken::Bearer.new(:access_token => 'access_token')
43
+ response.id_token = id_token
44
+ end
45
+ end
46
+ it do
47
+ expect { response }.should raise_error AttrRequired::AttrMissing, 'private_key is required'
48
+ end
49
+ end
32
50
  end
33
51
 
34
52
  context "otherwise" do
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,16 @@
1
1
  require 'rspec'
2
2
  require 'openid_connect'
3
3
 
4
- require 'helpers/webmock_helper'
4
+ require 'helpers/webmock_helper'
5
+
6
+ def rsa
7
+ @rsa ||= OpenSSL::PKey::RSA.generate 2048
8
+ end
9
+
10
+ def public_key
11
+ @public_key ||= rsa.public_key
12
+ end
13
+
14
+ def private_key
15
+ @private_key ||= OpenSSL::PKey::RSA.new rsa.export(OpenSSL::Cipher::Cipher.new('aes256'), 'pass-phrase'), 'pass-phrase'
16
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openid_connect
3
3
  version: !ruby/object:Gem::Version
4
- hash: 9
4
+ hash: 7
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 11
10
- version: 0.0.11
9
+ - 12
10
+ version: 0.0.12
11
11
  platform: ruby
12
12
  authors:
13
13
  - nov matake
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-08-17 00:00:00 Z
18
+ date: 2011-08-18 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: activemodel