openid_connect 0.0.31 → 0.0.32

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/.gitignore CHANGED
@@ -14,7 +14,7 @@ tmtags
14
14
  *.swp
15
15
 
16
16
  ## PROJECT::GENERAL
17
- coverage
17
+ coverage*
18
18
  rdoc
19
19
  pkg
20
20
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- openid_connect (0.0.30)
4
+ openid_connect (0.0.31)
5
5
  activemodel (>= 3)
6
6
  attr_required (>= 0.0.3)
7
7
  json (>= 1.4.3)
@@ -15,34 +15,43 @@ PATH
15
15
  GEM
16
16
  remote: http://rubygems.org/
17
17
  specs:
18
- activemodel (3.1.1)
19
- activesupport (= 3.1.1)
18
+ activemodel (3.1.3)
19
+ activesupport (= 3.1.3)
20
20
  builder (~> 3.0.0)
21
21
  i18n (~> 0.6)
22
- activesupport (3.1.1)
22
+ activesupport (3.1.3)
23
23
  multi_json (~> 1.0)
24
24
  addressable (2.2.6)
25
25
  attr_required (0.0.3)
26
26
  bouncy-castle-java (1.5.0146.1)
27
27
  builder (3.0.0)
28
+ configatron (2.8.4)
29
+ yamler (>= 0.1.0)
30
+ cover_me (1.2.0)
31
+ configatron
32
+ hashie
28
33
  crack (0.3.1)
29
34
  diff-lcs (1.1.3)
30
- httpclient (2.2.1)
35
+ hashie (1.2.0)
36
+ httpclient (2.2.4)
31
37
  i18n (0.6.0)
32
38
  jruby-openssl (0.7.4)
33
39
  bouncy-castle-java
34
- json (1.6.1)
35
- json-jwt (0.0.3)
40
+ json (1.6.5)
41
+ json (1.6.5-java)
42
+ json-jwt (0.0.4)
43
+ activesupport (>= 2.3)
44
+ i18n
36
45
  json (>= 1.4.3)
37
46
  url_safe_base64
38
- mail (2.3.0)
47
+ mail (2.4.0)
39
48
  i18n (>= 0.4.0)
40
49
  mime-types (~> 1.16)
41
50
  treetop (~> 1.4.8)
42
- mime-types (1.16)
43
- multi_json (1.0.3)
44
- polyglot (0.3.2)
45
- rack (1.3.4)
51
+ mime-types (1.17.2)
52
+ multi_json (1.0.4)
53
+ polyglot (0.3.3)
54
+ rack (1.4.0)
46
55
  rack-oauth2 (0.11.0)
47
56
  activesupport (>= 2.3)
48
57
  attr_required (>= 0.0.3)
@@ -50,17 +59,15 @@ GEM
50
59
  i18n
51
60
  json (>= 1.4.3)
52
61
  rack (>= 1.1)
53
- rake (0.9.2)
54
- rcov (0.9.11)
55
- rcov (0.9.11-java)
56
- rspec (2.6.0)
57
- rspec-core (~> 2.6.0)
58
- rspec-expectations (~> 2.6.0)
59
- rspec-mocks (~> 2.6.0)
60
- rspec-core (2.6.4)
61
- rspec-expectations (2.6.0)
62
+ rake (0.9.2.2)
63
+ rspec (2.8.0)
64
+ rspec-core (~> 2.8.0)
65
+ rspec-expectations (~> 2.8.0)
66
+ rspec-mocks (~> 2.8.0)
67
+ rspec-core (2.8.0)
68
+ rspec-expectations (2.8.0)
62
69
  diff-lcs (~> 1.1.2)
63
- rspec-mocks (2.6.0)
70
+ rspec-mocks (2.8.0)
64
71
  swd (0.0.7)
65
72
  activesupport (>= 3)
66
73
  attr_required (>= 0.0.3)
@@ -70,25 +77,26 @@ GEM
70
77
  treetop (1.4.10)
71
78
  polyglot
72
79
  polyglot (>= 0.3.1)
73
- tzinfo (0.3.30)
80
+ tzinfo (0.3.31)
74
81
  url_safe_base64 (0.2.1)
75
82
  validate_email (0.1.5)
76
83
  activemodel (>= 3.0)
77
84
  mail (>= 2.2.5)
78
85
  validate_url (0.2.0)
79
86
  activemodel (>= 3.0.0)
80
- webmock (1.7.6)
81
- addressable (> 2.2.5, ~> 2.2)
87
+ webmock (1.7.10)
88
+ addressable (~> 2.2, > 2.2.5)
82
89
  crack (>= 0.1.7)
90
+ yamler (0.1.0)
83
91
 
84
92
  PLATFORMS
85
93
  java
86
94
  ruby
87
95
 
88
96
  DEPENDENCIES
97
+ cover_me (>= 1.2.0)
89
98
  jruby-openssl (>= 0.7)
90
99
  openid_connect!
91
100
  rake (>= 0.8)
92
- rcov (>= 0.9)
93
101
  rspec (>= 2)
94
102
  webmock (>= 1.6.2)
data/README.rdoc CHANGED
@@ -16,12 +16,12 @@ OpenID Connect Server & Client Library
16
16
 
17
17
  === Provider
18
18
 
19
- * Running on Heroku (https://openid-connect.herokuapp.com)
19
+ * Running on Heroku (http://connect-op.heroku.com)
20
20
  * Source on GitHub (https://github.com/nov/openid_connect_sample)
21
21
 
22
22
  === Relying Party
23
23
 
24
- * Running on Heroku (https://openid-connect-rp.herokuapp.com)
24
+ * Running on Heroku (https://connect-rp.heroku.com)
25
25
  * Source on GitHub (https://github.com/nov/openid_connect_sample_rp)
26
26
 
27
27
  == Note on Patches/Pull Requests
data/Rakefile CHANGED
@@ -1,11 +1,19 @@
1
- require 'bundler/gem_tasks'
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
2
3
 
3
4
  require 'rspec/core/rake_task'
4
5
  RSpec::Core::RakeTask.new(:spec)
5
6
 
6
- RSpec::Core::RakeTask.new(:rcov) do |spec|
7
- spec.rcov = true
8
- spec.rcov_opts = ['-Ilib -Ispec --exclude spec,gems']
7
+ if RUBY_VERSION >= '1.9'
8
+ require 'cover_me'
9
+ CoverMe.config do |c|
10
+ c.file_pattern = /(#{CoverMe.config.project.root}\/lib\/.+\.rb)/i
11
+ end
12
+ else
13
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
14
+ spec.rcov = true
15
+ spec.rcov_opts = ['-Ilib -Ispec --exclude spec,gems']
16
+ end
9
17
  end
10
18
 
11
19
  task :default => :spec
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.31
1
+ 0.0.32
@@ -15,6 +15,14 @@ module OpenIDConnect
15
15
  ResponseObject::UserInfo::OpenID.new hash
16
16
  end
17
17
 
18
+ def id_token!
19
+ client.check_id_uri
20
+ hash = resource_request do
21
+ get client.check_id_uri
22
+ end
23
+ ResponseObject::IdToken.new hash
24
+ end
25
+
18
26
  private
19
27
 
20
28
  def resource_request
@@ -0,0 +1,217 @@
1
+ module OpenIDConnect
2
+ class Client
3
+ class Registrar
4
+ include ActiveModel::Validations, AttrRequired, AttrOptional
5
+
6
+ class RegistrationFailed < HttpError; end
7
+
8
+ singular_attributes = [
9
+ :type,
10
+ :client_id,
11
+ :client_secret,
12
+ :access_token,
13
+ :application_type,
14
+ :application_name,
15
+ :logo_url,
16
+ :token_endpoint_auth_type,
17
+ :policy_url,
18
+ :jwk_url,
19
+ :jwk_encryption_url,
20
+ :x509_url,
21
+ :x509_encryption_url,
22
+ :sector_identifier_url,
23
+ :user_id_type,
24
+ :require_signed_request_object,
25
+ ]
26
+ plurar_attributes = [
27
+ :contacts,
28
+ :redirect_uris,
29
+ :userinfo_signed_response_algs,
30
+ :userinfo_encrypted_response_algs,
31
+ :id_token_signed_response_algs,
32
+ :id_token_encrypted_response_algs
33
+ ]
34
+ attr_required :endpoint
35
+ attr_optional *(singular_attributes + plurar_attributes)
36
+
37
+ plurar_attributes.each do |_attr_|
38
+ define_method "#{_attr_}_with_split" do
39
+ self.send("#{_attr_}_without_split").to_s.split(' ')
40
+ end
41
+ alias_method_chain _attr_, :split
42
+ end
43
+
44
+ validates :type, :presence => true
45
+ validates :client_id, :presence => {:if => lambda { |c| c.type.to_s == 'client_update' }}
46
+ validates :sector_identifier_url, :presence => {:if => :sector_identifier_required?}
47
+
48
+ validates :type, :inclusion => {:in => ['client_associate', 'client_update']}
49
+ validates :application_type, :inclusion => {:in => ['native', 'web']}, :allow_nil => true
50
+ validates :user_id_type, :inclusion => {:in => ['pairwise', 'public']}, :allow_nil => true
51
+ validates :token_endpoint_auth_type, :inclusion => {
52
+ :in => ['client_secret_post', 'client_secret_basic', 'client_secret_jwt', 'private_key_jwt']
53
+ }, :allow_nil => true
54
+
55
+ validates(
56
+ :logo_url,
57
+ :policy_url,
58
+ :jwk_url,
59
+ :jwk_encryption_url,
60
+ :x509_url,
61
+ :x509_encryption_url,
62
+ :sector_identifier_url,
63
+ :url => true, :allow_nil => true
64
+ )
65
+
66
+ validate :validate_contacts
67
+ validate :validate_redirect_uris
68
+ validate :validate_key_urls
69
+ validate :validate_signature_algorithms
70
+ validate :validate_encription_algorithms
71
+
72
+ def initialize(endpoint, attributes = {})
73
+ @endpoint = endpoint
74
+ optional_attributes.each do |_attr_|
75
+ self.send "#{_attr_}=", attributes[_attr_].try(:to_s)
76
+ end
77
+ attr_missing!
78
+ end
79
+
80
+ def sector_identifier
81
+ if valid_uri?(sector_identifier_url)
82
+ URI.parse(sector_identifier_url).host
83
+ else
84
+ hosts = Array(redirect_uris).collect do |redirect_uri|
85
+ if valid_uri?(redirect_uri, nil)
86
+ URI.parse(redirect_uri).host
87
+ else
88
+ nil
89
+ end
90
+ end.compact.uniq
91
+ if hosts.size == 1
92
+ hosts.first
93
+ else
94
+ nil
95
+ end
96
+ end
97
+ end
98
+
99
+ def as_json(options = {})
100
+ validate!
101
+ (optional_attributes - [:access_token]).inject({}) do |hash, _attr_|
102
+ value = self.send(_attr_)
103
+ hash.merge! _attr_ => case value
104
+ when Array
105
+ value.collect(&:to_s).join(' ')
106
+ else
107
+ value
108
+ end
109
+ end.delete_if do |key, value|
110
+ value.nil?
111
+ end
112
+ end
113
+
114
+ def associate!
115
+ self.type = 'client_associate'
116
+ post!
117
+ end
118
+
119
+ def update!
120
+ self.type = 'client_update'
121
+ post!
122
+ end
123
+
124
+ def validate!
125
+ valid? or raise ValidationFailed.new(errors)
126
+ end
127
+
128
+ private
129
+
130
+ def sector_identifier_required?
131
+ user_id_type == 'pairwise' &&
132
+ sector_identifier.blank?
133
+ end
134
+
135
+ def valid_uri?(uri, schemes = ['http', 'https'])
136
+ # NOTE: specify nil for schemes to allow any schemes
137
+ URI::regexp(schemes).match(uri).present?
138
+ end
139
+
140
+ def validate_contacts
141
+ contacts.each do |contact|
142
+ EmailValidator.new.validate_each(self, :contacts, contact)
143
+ end
144
+ include_invalid = contacts.any? do |contact|
145
+ begin
146
+ mail = Mail::Address.new(contact)
147
+ mail.address != contact || mail.domain.split(".").length <= 1
148
+ rescue
149
+ :invalid
150
+ end
151
+ end
152
+ errors.add :contacts, 'includes invalid email' if include_invalid
153
+ end
154
+
155
+ def validate_redirect_uris
156
+ include_invalid = redirect_uris.any? do |redirect_uri|
157
+ !valid_uri?(redirect_uri, nil)
158
+ end
159
+ errors.add :redirect_uris, 'includes invalid URL' if include_invalid
160
+ end
161
+
162
+ def validate_key_urls
163
+ # TODO
164
+ end
165
+
166
+ def validate_signature_algorithms
167
+ # TODO
168
+ end
169
+
170
+ def validate_encription_algorithms
171
+ # TODO
172
+ end
173
+
174
+ def post!
175
+ handle_response do
176
+ http_client.post endpoint, as_json
177
+ end
178
+ end
179
+
180
+ def http_client
181
+ case access_token
182
+ when nil
183
+ OpenIDConnect.http_client
184
+ when Rack::OAuth2::AccessToken::Bearer
185
+ access_token
186
+ else
187
+ Rack::OAuth2::AccessToken::Bearer.new(
188
+ :access_token => access_token
189
+ )
190
+ end
191
+ end
192
+
193
+ def handle_response
194
+ response = yield
195
+ case response.status
196
+ when 200..201
197
+ handle_success_response response
198
+ else
199
+ handle_error_response response
200
+ end
201
+ end
202
+
203
+ def handle_success_response(response)
204
+ credentials = JSON.parse(response.body).with_indifferent_access
205
+ Client.new(
206
+ :identifier => credentials[:client_id],
207
+ :secret => credentials[:client_secret],
208
+ :expires_in => credentials[:expires_in]
209
+ )
210
+ end
211
+
212
+ def handle_error_response(response)
213
+ raise RegistrationFailed.new(response.status, 'Client Registration Failed', response)
214
+ end
215
+ end
216
+ end
217
+ end
@@ -42,4 +42,8 @@ module OpenIDConnect
42
42
  raise Exception.new("Unknown Token Type")
43
43
  end
44
44
  end
45
+ end
46
+
47
+ Dir[File.dirname(__FILE__) + '/client/*.rb'].each do |file|
48
+ require file
45
49
  end
@@ -6,18 +6,53 @@ module OpenIDConnect
6
6
  include AttrOptional
7
7
 
8
8
  attr_reader :raw
9
- attr_optional :version, :issuer
10
- attr_optional :authorization_endpoint, :token_endpoint, :user_info_endpoint
11
- attr_optional :check_id_endpoint, :refresh_session_endpoint, :end_session_endpoint
12
- attr_optional :jwk_document, :x509_url, :registration_endpoint
13
- attr_optional :scopes_supported, :flows_supported, :iso29115_supported, :identifiers_supported
9
+ attr_optional(
10
+ :version,
11
+ :issuer,
12
+ :authorization_endpoint,
13
+ :token_endpoint,
14
+ :user_info_endpoint,
15
+ :check_id_endpoint,
16
+ :refresh_session_endpoint,
17
+ :end_session_endpoint,
18
+ :jwk_url,
19
+ :jwk_encryption_url,
20
+ :x509_url,
21
+ :x509_encryption_ur,
22
+ :registration_endpoint,
23
+ :scopes_supported,
24
+ :response_types_supported,
25
+ :acrs_supported,
26
+ :user_id_types_supported,
27
+ :user_info_algs_supported,
28
+ :id_token_algs_supported,
29
+ :request_object_algs_supported,
30
+ :token_endpoint_auth_types_supported,
31
+ :token_endpoint_auth_algs_supported
32
+ )
14
33
 
15
34
  def initialize(hash)
16
35
  optional_attributes.each do |key|
17
36
  self.send "#{key}=", hash[key]
18
37
  end
38
+ @user_info_endpoint ||= hash[:userinfo_endpoint]
39
+ @user_info_algs_supported ||= hash[:userinfo_algs_supported]
40
+ @version ||= '3.0'
19
41
  @raw = hash
20
42
  end
43
+
44
+ def as_json(options = {})
45
+ hash = optional_attributes.inject({}) do |hash, _attr_|
46
+ hash.merge(
47
+ _attr_ => self.send(_attr_)
48
+ )
49
+ end
50
+ hash[:userinfo_endpoint] = hash.delete(:user_info_endpoint)
51
+ hash[:userinfo_algs_supported] = hash.delete(:user_info_algs_supported)
52
+ hash.delete_if do |key, value|
53
+ value.nil?
54
+ end
55
+ end
21
56
  end
22
57
  end
23
58
  end
@@ -1,6 +1,15 @@
1
1
  module OpenIDConnect
2
2
  class Exception < StandardError; end
3
3
 
4
+ class ValidationFailed < Exception
5
+ attr_reader :errors
6
+
7
+ def initialize(errors)
8
+ super errors.full_messages.to_sentence
9
+ @errors = errors
10
+ end
11
+ end
12
+
4
13
  class HttpError < Exception
5
14
  attr_accessor :status, :response
6
15
  def initialize(status, message = nil, response = nil)
@@ -30,38 +30,16 @@ module OpenIDConnect
30
30
  end
31
31
 
32
32
  class << self
33
- def from_jwt(jwt_string, key_or_client)
33
+ def decode(jwt_string, key_or_client)
34
34
  attributes = case key_or_client
35
35
  when Client
36
- resource_request do
37
- http_client.post key_or_client.check_id_uri, :id_token => jwt_string
38
- end
36
+ OpenIDConnect::AccessToken.new(
37
+ :client => key_or_client,
38
+ :access_token => jwt_string
39
+ ).id_token!
39
40
  else
40
- JSON::JWT.decode(jwt_string, key_or_client).with_indifferent_access
41
+ new JSON::JWT.decode(jwt_string, key_or_client)
41
42
  end
42
- new attributes
43
- end
44
-
45
- def resource_request
46
- res = yield
47
- case res.status
48
- when 200
49
- JSON.parse(res.body).with_indifferent_access
50
- when 400
51
- raise BadRequest.new('Check Session Faild', res)
52
- else
53
- raise HttpError.new(res.status, 'Unknown HttpError', res)
54
- end
55
- end
56
-
57
- private
58
-
59
- def http_client
60
- _http_client_ = HTTPClient.new(
61
- :agent_name => "OpenIDConnect (#{VERSION})"
62
- )
63
- _http_client_.request_filter << Debugger::RequestFilter.new if OpenIDConnect.debugging?
64
- _http_client_
65
43
  end
66
44
  end
67
45
  end
@@ -2,28 +2,35 @@ module OpenIDConnect
2
2
  class ResponseObject
3
3
  module UserInfo
4
4
  class OpenID < ResponseObject
5
- attr_optional :user_id, :name, :given_name, :family_name, :middle_name, :nickname
5
+ attr_optional(
6
+ :user_id,
7
+ :name,
8
+ :given_name,
9
+ :family_name,
10
+ :middle_name,
11
+ :nickname,
12
+ :phone_number,
13
+ :verified,
14
+ :gender,
15
+ :zoneinfo,
16
+ :locale,
17
+ :birthday,
18
+ :updated_time,
19
+ :profile,
20
+ :picture,
21
+ :website,
22
+ :email,
23
+ :address
24
+ )
6
25
 
7
- attr_optional :phone_number
8
-
9
- attr_optional :verified, :gender, :zoneinfo, :locale
10
26
  validates :verified, :inclusion => {:in => [true, false]}, :allow_nil => true
11
27
  validates :gender, :inclusion => {:in => ['male', 'female']}, :allow_nil => true
12
28
  validates :zoneinfo, :inclusion => {:in => TZInfo::TimezoneProxy.all.collect(&:name)}, :allow_nil => true
13
- # TODO: validate locale
14
-
15
- attr_optional :birthday, :updated_time
16
-
17
- attr_optional :profile, :picture, :website
18
29
  validates :profile, :picture, :website, :url => true, :allow_nil => true
19
-
20
- attr_optional :email
21
30
  validates :email, :email => true, :allow_nil => true
22
-
23
- attr_optional :address
24
31
  validate :validate_address
25
-
26
32
  validate :require_at_least_one_attributes
33
+ # TODO: validate locale
27
34
 
28
35
  def initialize(attributes = {})
29
36
  super
@@ -33,7 +40,7 @@ module OpenIDConnect
33
40
  end
34
41
 
35
42
  def validate_address
36
- errors.add :address, 'cannot be blank' unless address.blank? || address.valid?
43
+ errors.add :address, address.errors.full_messages.join(', ') if address.present? && !address.valid?
37
44
  end
38
45
 
39
46
  def address=(hash_or_address)
@@ -1,23 +1,7 @@
1
- require 'active_model'
2
- require 'tzinfo'
3
- require 'validate_url'
4
- require 'validate_email'
5
- require 'attr_required'
6
- require 'attr_optional'
7
-
8
1
  module OpenIDConnect
9
2
  class ResponseObject
10
3
  include ActiveModel::Validations, AttrRequired, AttrOptional
11
4
 
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
-
21
5
  def initialize(attributes = {})
22
6
  all_attributes.each do |_attr_|
23
7
  self.send :"#{_attr_}=", attributes[_attr_]
@@ -1,6 +1,12 @@
1
1
  require 'json'
2
2
  require 'logger'
3
3
  require 'swd'
4
+ require 'active_model'
5
+ require 'tzinfo'
6
+ require 'validate_url'
7
+ require 'validate_email'
8
+ require 'attr_required'
9
+ require 'attr_optional'
4
10
  require 'rack/oauth2'
5
11
  require 'rack/oauth2/server/id_token_response'
6
12
 
@@ -43,6 +49,14 @@ module OpenIDConnect
43
49
  self.debugging = original
44
50
  end
45
51
  self.debugging = false
52
+
53
+ def self.http_client
54
+ _http_client_ = HTTPClient.new(
55
+ :agent_name => "OpenIDConnect (#{VERSION})"
56
+ )
57
+ _http_client_.request_filter << Debugger::RequestFilter.new if OpenIDConnect.debugging?
58
+ _http_client_
59
+ end
46
60
  end
47
61
 
48
62
  require 'openid_connect/exception'
@@ -20,7 +20,11 @@ Gem::Specification.new do |s|
20
20
  s.add_runtime_dependency "swd", ">= 0.0.6"
21
21
  s.add_runtime_dependency "rack-oauth2", ">= 0.10.0"
22
22
  s.add_development_dependency "rake", ">= 0.8"
23
- s.add_development_dependency "rcov", ">= 0.9"
23
+ if RUBY_VERSION >= '1.9'
24
+ s.add_development_dependency "cover_me", ">= 1.2.0"
25
+ else
26
+ s.add_development_dependency "rcov", ">= 0.9"
27
+ end
24
28
  s.add_development_dependency "rspec", ">= 2"
25
29
  s.add_development_dependency "webmock", ">= 1.6.2"
26
30
  end
@@ -3,11 +3,11 @@
3
3
  "issuer": "https://connect-op.heroku.com",
4
4
  "authorization_endpoint": "https://connect-op.heroku.com/authorizations/new",
5
5
  "token_endpoint": "https://connect-op.heroku.com/access_tokens",
6
- "user_info_endpoint": "https://connect-op.heroku.com/user_info",
6
+ "userinfo_endpoint": "https://connect-op.heroku.com/user_info",
7
7
  "check_id_endpoint": "https://connect-op.heroku.com/id_token",
8
8
  "registration_endpoint": "https://connect-op.heroku.com/connect/client",
9
- "scopes_supported": ["openid", "profile", "email", "address", "PPID"],
10
- "flows_supported": ["code", "token", "id_token", "code token", "code id_token", "id_token token"],
11
- "identifiers_supported": ["public", "ppid"],
9
+ "scopes_supported": ["openid", "profile", "email", "address"],
10
+ "response_types_supported": ["code", "token", "id_token", "code token", "code id_token", "id_token token"],
11
+ "user_id_types_supported": ["public", "pairwise"],
12
12
  "x509_url": "https://connect-op.heroku.com/cert.pem"
13
13
  }