rack-couchdb-oauth2 0.1.0 → 0.2.0
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/Gemfile.lock +6 -6
- data/VERSION +1 -1
- data/lib/couchdb_oauth2/configuration.rb +28 -0
- data/lib/couchdb_oauth2/model/access_token.rb +1 -3
- data/lib/couchdb_oauth2/model/account.rb +87 -69
- data/lib/couchdb_oauth2/model/base.rb +12 -11
- data/lib/couchdb_oauth2/model/client.rb +11 -2
- data/lib/couchdb_oauth2/model/oauth2_token.rb +24 -10
- data/lib/couchdb_oauth2/model/refresh_token.rb +1 -5
- data/lib/couchdb_oauth2/resource/require_client.rb +1 -1
- data/lib/couchdb_oauth2/token_endpoint.rb +2 -1
- data/lib/rack-couchdb-oauth2.rb +19 -9
- data/rack-couchdb-oauth2.gemspec +9 -4
- data/test/helper.rb +31 -0
- data/test/test_account.rb +26 -0
- data/test/test_client.rb +50 -0
- data/test/test_rack-couchdb-oauth2.rb +4 -5
- data/test/test_require_client.rb +130 -0
- data/test/test_token.rb +62 -0
- metadata +40 -35
data/Gemfile.lock
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
GIT
|
2
2
|
remote: git://github.com/couchrest/couchrest_model.git
|
3
|
-
revision:
|
3
|
+
revision: dca47ac3b2b6fd5a4f3ee131083dde4a5cd2b8db
|
4
4
|
specs:
|
5
5
|
couchrest_model (1.1.0.rc1)
|
6
6
|
activemodel (~> 3.0)
|
@@ -11,11 +11,11 @@ GIT
|
|
11
11
|
GEM
|
12
12
|
remote: http://rubygems.org/
|
13
13
|
specs:
|
14
|
-
activemodel (3.0.
|
15
|
-
activesupport (= 3.0.
|
14
|
+
activemodel (3.0.9)
|
15
|
+
activesupport (= 3.0.9)
|
16
16
|
builder (~> 2.1.2)
|
17
17
|
i18n (~> 0.5.0)
|
18
|
-
activesupport (3.0.
|
18
|
+
activesupport (3.0.9)
|
19
19
|
attr_required (0.0.3)
|
20
20
|
bcrypt-ruby (2.1.4)
|
21
21
|
builder (2.1.2)
|
@@ -30,11 +30,11 @@ GEM
|
|
30
30
|
bundler (~> 1.0)
|
31
31
|
git (>= 1.2.5)
|
32
32
|
rake
|
33
|
-
json (1.5.
|
33
|
+
json (1.5.3)
|
34
34
|
mime-types (1.16)
|
35
35
|
multi_json (1.0.3)
|
36
36
|
rack (1.3.0)
|
37
|
-
rack-oauth2 (0.8.
|
37
|
+
rack-oauth2 (0.8.2)
|
38
38
|
activesupport (>= 2.3)
|
39
39
|
attr_required (>= 0.0.3)
|
40
40
|
httpclient (>= 2.2.0.2)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Rack
|
2
|
+
module CouchdbOAuth2
|
3
|
+
class Configuration
|
4
|
+
def self.add_config(name)
|
5
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
6
|
+
def self.#{name}(value=nil)
|
7
|
+
@#{name} = value if value
|
8
|
+
return @#{name} if self.object_id == #{self.object_id} || defined?(@#{name})
|
9
|
+
name = superclass.#{name}
|
10
|
+
return nil if name.nil? && !instance_variable_defined?("@#{name}")
|
11
|
+
@#{name} = name && !name.is_a?(Module) && !name.is_a?(Symbol) && !name.is_a?(Numeric) && !name.is_a?(TrueClass) && !name.is_a?(FalseClass) ? name.dup : name
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.#{name}=(value)
|
15
|
+
@#{name} = value
|
16
|
+
end
|
17
|
+
|
18
|
+
def #{name}
|
19
|
+
self.class.#{name}
|
20
|
+
end
|
21
|
+
RUBY
|
22
|
+
end
|
23
|
+
|
24
|
+
add_config :account_class
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,11 +1,9 @@
|
|
1
1
|
class AccessToken < CouchRest::Model::Base
|
2
|
-
include CouchdbOAuth2::Model::Base
|
2
|
+
include Rack::CouchdbOAuth2::Model::Base
|
3
3
|
include Oauth2Token
|
4
4
|
self.default_lifetime = 15.minutes
|
5
5
|
|
6
6
|
belongs_to :refresh_token
|
7
|
-
property :token, String
|
8
|
-
property :expires_at, Time
|
9
7
|
timestamps!
|
10
8
|
|
11
9
|
def to_bearer_token(with_refresh_token = false)
|
@@ -1,74 +1,92 @@
|
|
1
1
|
require 'bcrypt'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
view_by :email
|
11
|
-
|
12
|
-
validates_uniqueness_of :email
|
13
|
-
|
14
|
-
attr_reader :password
|
15
|
-
|
16
|
-
def password=(new_password)
|
17
|
-
@password = new_password
|
18
|
-
self.encrypted_password = password_digest(@password) if @password.present?
|
19
|
-
end
|
20
|
-
|
21
|
-
def valid_password?(password)
|
22
|
-
return false if encrypted_password.blank?
|
23
|
-
bcrypt = ::BCrypt::Password.new(self.encrypted_password)
|
24
|
-
password = ::BCrypt::Engine.hash_secret("#{password}#{self.class.pepper}", bcrypt.salt)
|
25
|
-
Account.secure_compare(password, self.encrypted_password)
|
26
|
-
end
|
27
|
-
|
28
|
-
def clean_up_passwords
|
29
|
-
self.password = self.password_confirmation = ""
|
30
|
-
end
|
31
|
-
|
32
|
-
# A reliable way to expose the salt regardless of the implementation.
|
33
|
-
def authenticatable_salt
|
34
|
-
self.encrypted_password[0,29] if self.encrypted_password
|
35
|
-
end
|
36
|
-
|
37
|
-
def access_tokens
|
38
|
-
AccessToken.view(:by_account_id, :key => self['_id'])
|
39
|
-
end
|
40
|
-
|
41
|
-
def refresh_tokens
|
42
|
-
RefreshToken.view(:by_account_id, :key => self['_id'])
|
43
|
-
end
|
44
|
-
|
45
|
-
protected
|
46
|
-
|
47
|
-
# Downcase case-insensitive keys
|
48
|
-
def downcase_keys
|
49
|
-
(self.class.case_insensitive_keys || []).each { |k| self[k].try(:downcase!) }
|
50
|
-
end
|
3
|
+
module Rack
|
4
|
+
module CouchdbOAuth2
|
5
|
+
module Model
|
6
|
+
module Account
|
7
|
+
def self.included(klass)
|
8
|
+
klass.class_eval do
|
51
9
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
10
|
+
property :email, String
|
11
|
+
property :encrypted_password, String, :protected => true
|
12
|
+
property :pepper, String, :protected => true
|
13
|
+
|
14
|
+
view_by :email
|
15
|
+
|
16
|
+
validates_presence_of :email
|
17
|
+
validates_uniqueness_of :email
|
18
|
+
validates_confirmation_of :password
|
19
|
+
validates_presence_of :encrypted_password, :message => 'password should not be empty'
|
20
|
+
validates_presence_of :password_confirmation, :if => :password_changed?
|
21
|
+
|
22
|
+
attr_reader :password
|
23
|
+
|
24
|
+
def self.stretches
|
25
|
+
5
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.secure_compare(a, b)
|
29
|
+
return false if a.blank? || b.blank? || a.bytesize != b.bytesize
|
30
|
+
l = a.unpack "C#{a.bytesize}"
|
31
|
+
|
32
|
+
res = 0
|
33
|
+
b.each_byte { |byte| res |= byte ^ l.shift }
|
34
|
+
res == 0
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.find_account(identity)
|
38
|
+
raise 'implement me'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def password=(new_password)
|
44
|
+
@password = new_password
|
45
|
+
self.pepper = BCrypt::Engine.generate_salt
|
46
|
+
self.encrypted_password = password_digest(@password) if @password.present?
|
47
|
+
end
|
48
|
+
|
49
|
+
def password_changed?
|
50
|
+
self.encrypted_password_changed?
|
51
|
+
end
|
52
|
+
|
53
|
+
def valid_password?(password)
|
54
|
+
return false if encrypted_password.blank?
|
55
|
+
bcrypt = ::BCrypt::Password.new(self.encrypted_password)
|
56
|
+
password = ::BCrypt::Engine.hash_secret("#{password}#{self.pepper}", bcrypt.salt)
|
57
|
+
self.class.secure_compare(password, self.encrypted_password)
|
58
|
+
end
|
59
|
+
|
60
|
+
def clean_up_passwords
|
61
|
+
self.password = self.password_confirmation = ""
|
62
|
+
end
|
63
|
+
|
64
|
+
# A reliable way to expose the salt regardless of the implementation.
|
65
|
+
def authenticatable_salt
|
66
|
+
self.encrypted_password[0,29] if self.encrypted_password
|
67
|
+
end
|
68
|
+
|
69
|
+
def access_tokens
|
70
|
+
AccessToken.view(:by_account_id, :key => self['_id'])
|
71
|
+
end
|
72
|
+
|
73
|
+
def refresh_tokens
|
74
|
+
RefreshToken.view(:by_account_id, :key => self['_id'])
|
75
|
+
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
|
79
|
+
# Downcase case-insensitive keys
|
80
|
+
def downcase_keys
|
81
|
+
(self.class.case_insensitive_keys || []).each { |k| self[k].try(:downcase!) }
|
82
|
+
end
|
83
|
+
|
84
|
+
# Digests the password using bcrypt.
|
85
|
+
def password_digest(password)
|
86
|
+
::BCrypt::Password.create("#{password}#{self.pepper}", :cost => self.class.stretches).to_s
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
72
91
|
end
|
73
|
-
|
74
92
|
end
|
@@ -1,12 +1,13 @@
|
|
1
|
-
module
|
2
|
-
module
|
3
|
-
module
|
4
|
-
|
5
|
-
klass
|
6
|
-
|
7
|
-
|
8
|
-
|
1
|
+
module Rack
|
2
|
+
module CouchdbOAuth2
|
3
|
+
module Model
|
4
|
+
module Base
|
5
|
+
def self.included(klass)
|
6
|
+
klass.class_eval do
|
7
|
+
use_database klass.name.tableize
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
9
11
|
end
|
10
|
-
end
|
11
|
-
|
12
|
-
end
|
12
|
+
end
|
13
|
+
end
|
@@ -1,13 +1,16 @@
|
|
1
1
|
class Client < CouchRest::Model::Base
|
2
|
-
include CouchdbOAuth2::Model::Base
|
2
|
+
include Rack::CouchdbOAuth2::Model::Base
|
3
3
|
|
4
4
|
property :name, String
|
5
5
|
property :redirect_url, String
|
6
6
|
property :website, String
|
7
|
-
property :secret, String
|
7
|
+
property :secret, String, :protected => true
|
8
8
|
timestamps!
|
9
9
|
|
10
10
|
before_validation :setup, :on => :create
|
11
|
+
validates_uniqueness_of :name
|
12
|
+
validates_presence_of :name
|
13
|
+
validates_presence_of :secret
|
11
14
|
|
12
15
|
def setup
|
13
16
|
if self.secret.nil?
|
@@ -18,4 +21,10 @@ class Client < CouchRest::Model::Base
|
|
18
21
|
def identity
|
19
22
|
self['_id']
|
20
23
|
end
|
24
|
+
|
25
|
+
def self.find_by_env(env)
|
26
|
+
request = Rack::OAuth2::Server::Token::Request.new(env)
|
27
|
+
client = Client.find(request.client_id)
|
28
|
+
client if client && client.secret == request.client_secret
|
29
|
+
end
|
21
30
|
end
|
@@ -4,9 +4,12 @@ module Oauth2Token
|
|
4
4
|
cattr_accessor :default_lifetime
|
5
5
|
self.default_lifetime = 1.minute
|
6
6
|
|
7
|
-
belongs_to :account
|
7
|
+
belongs_to :account, :class_name => Rack::CouchdbOAuth2::Configuration.account_class.to_s
|
8
8
|
belongs_to :client
|
9
9
|
|
10
|
+
property :token, String
|
11
|
+
property :expires_at, Time
|
12
|
+
|
10
13
|
view_by :expires_at
|
11
14
|
view_by :account_id
|
12
15
|
view_by :client_id
|
@@ -15,6 +18,25 @@ module Oauth2Token
|
|
15
18
|
before_validation :setup, :on => :create
|
16
19
|
validates :client, :expires_at, :account, :presence => true
|
17
20
|
validates :token, :presence => true, :uniqueness => true
|
21
|
+
|
22
|
+
def self.find_by_env(env)
|
23
|
+
token = find_by_token(env[Rack::OAuth2::Server::Resource::ACCESS_TOKEN])
|
24
|
+
if token.nil? || token.expired?
|
25
|
+
nil
|
26
|
+
else
|
27
|
+
token
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.valid
|
32
|
+
view(:by_expires_at, :startkey => Time.now.utc)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.find_by_token(token)
|
36
|
+
return nil if token.nil? || token.empty?
|
37
|
+
self.first_from_view(:by_token, token)
|
38
|
+
end
|
39
|
+
|
18
40
|
end
|
19
41
|
end
|
20
42
|
|
@@ -29,16 +51,8 @@ module Oauth2Token
|
|
29
51
|
|
30
52
|
def expired?
|
31
53
|
self.expires_at < Time.now.utc
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.valid
|
35
|
-
view(:by_expires_at, :startkey => Time.now.utc)
|
36
|
-
end
|
54
|
+
end
|
37
55
|
|
38
|
-
def self.find_by_token(token)
|
39
|
-
return nil if token.nil? || token.empty?
|
40
|
-
self.first_from_view(:by_token, token)
|
41
|
-
end
|
42
56
|
private
|
43
57
|
|
44
58
|
def self.generate(bytes = 64)
|
@@ -1,10 +1,6 @@
|
|
1
1
|
class RefreshToken < CouchRest::Model::Base
|
2
|
-
include CouchdbOAuth2::Model::Base
|
2
|
+
include Rack::CouchdbOAuth2::Model::Base
|
3
3
|
include Oauth2Token
|
4
|
-
belongs_to :account
|
5
|
-
belongs_to :client
|
6
|
-
property :token, String
|
7
|
-
property :expires_at, Time
|
8
4
|
timestamps!
|
9
5
|
|
10
6
|
self.default_lifetime = 1.month
|
@@ -22,7 +22,7 @@ module Rack
|
|
22
22
|
client = request.client
|
23
23
|
request.unauthorized!(:invalid_token) unless client
|
24
24
|
if @authenticator
|
25
|
-
request.unauthorized! unless @authenticator.call(request)
|
25
|
+
request.unauthorized!(:invalid_token) unless @authenticator.call(request)
|
26
26
|
end
|
27
27
|
env[CLIENT] = client
|
28
28
|
end
|
@@ -17,7 +17,8 @@ module Rack
|
|
17
17
|
req.unsupported_grant_type!
|
18
18
|
when :password
|
19
19
|
# NOTE: password is not hashed in this sample app. Don't do the same on your app.
|
20
|
-
|
20
|
+
account_class = Rack::CouchdbOAuth2::Configuration.account_class
|
21
|
+
account = req.username.nil? ? nil : account_class.find_account(req.username)
|
21
22
|
req.invalid_grant! unless account && account.valid_password?(req.password)
|
22
23
|
res.access_token = AccessToken.create(:client => client, :account => account).to_bearer_token(:with_refresh_token)
|
23
24
|
when :client_credentials
|
data/lib/rack-couchdb-oauth2.rb
CHANGED
@@ -2,13 +2,23 @@ require 'rack/oauth2'
|
|
2
2
|
require 'couchrest'
|
3
3
|
require 'couchrest_model'
|
4
4
|
require 'active_support'
|
5
|
-
require 'couchdb_oauth2/model/base'
|
6
|
-
require 'couchdb_oauth2/model/oauth2_token'
|
7
|
-
require 'couchdb_oauth2/model/access_token'
|
8
|
-
require 'couchdb_oauth2/model/account'
|
9
|
-
require 'couchdb_oauth2/model/client'
|
10
|
-
require 'couchdb_oauth2/model/refresh_token'
|
11
5
|
|
12
|
-
|
13
|
-
|
14
|
-
|
6
|
+
module Rack
|
7
|
+
module CouchdbOAuth2
|
8
|
+
autoload :Configuration, 'couchdb_oauth2/configuration'
|
9
|
+
autoload :TokenEndpoint, 'couchdb_oauth2/token_endpoint'
|
10
|
+
|
11
|
+
autoload :RequireBearerToken, 'couchdb_oauth2/resource/require_bearer_token'
|
12
|
+
autoload :RequireClient, 'couchdb_oauth2/resource/require_client'
|
13
|
+
|
14
|
+
module Model
|
15
|
+
autoload :Base, 'couchdb_oauth2/model/base'
|
16
|
+
autoload :Account, 'couchdb_oauth2/model/account'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
autoload :Oauth2Token, 'couchdb_oauth2/model/oauth2_token'
|
21
|
+
autoload :AccessToken, 'couchdb_oauth2/model/access_token'
|
22
|
+
autoload :Client, 'couchdb_oauth2/model/client'
|
23
|
+
autoload :RefreshToken, 'couchdb_oauth2/model/refresh_token'
|
24
|
+
|
data/rack-couchdb-oauth2.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{rack-couchdb-oauth2}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Robin Lu"]
|
12
|
-
s.date = %q{2011-06-
|
12
|
+
s.date = %q{2011-06-27}
|
13
13
|
s.description = %q{Rack middleware for OAuth2 Provider Server Based on Couchdb}
|
14
14
|
s.email = %q{iamawalrus@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -24,6 +24,7 @@ Gem::Specification.new do |s|
|
|
24
24
|
"README.rdoc",
|
25
25
|
"Rakefile",
|
26
26
|
"VERSION",
|
27
|
+
"lib/couchdb_oauth2/configuration.rb",
|
27
28
|
"lib/couchdb_oauth2/model/access_token.rb",
|
28
29
|
"lib/couchdb_oauth2/model/account.rb",
|
29
30
|
"lib/couchdb_oauth2/model/base.rb",
|
@@ -36,12 +37,16 @@ Gem::Specification.new do |s|
|
|
36
37
|
"lib/rack-couchdb-oauth2.rb",
|
37
38
|
"rack-couchdb-oauth2.gemspec",
|
38
39
|
"test/helper.rb",
|
39
|
-
"test/
|
40
|
+
"test/test_account.rb",
|
41
|
+
"test/test_client.rb",
|
42
|
+
"test/test_rack-couchdb-oauth2.rb",
|
43
|
+
"test/test_require_client.rb",
|
44
|
+
"test/test_token.rb"
|
40
45
|
]
|
41
46
|
s.homepage = %q{http://github.com/robin/rack-couchdb-oauth2}
|
42
47
|
s.licenses = ["MIT"]
|
43
48
|
s.require_paths = ["lib"]
|
44
|
-
s.rubygems_version = %q{1.
|
49
|
+
s.rubygems_version = %q{1.6.2}
|
45
50
|
s.summary = %q{Rack middleware for OAuth2 Provider Server Based on Couchdb}
|
46
51
|
|
47
52
|
if s.respond_to? :specification_version then
|
data/test/helper.rb
CHANGED
@@ -14,6 +14,21 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
14
14
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
15
15
|
require 'rack-couchdb-oauth2'
|
16
16
|
|
17
|
+
class User < CouchRest::Model::Base
|
18
|
+
use_database 'users'
|
19
|
+
include Rack::CouchdbOAuth2::Model::Account
|
20
|
+
|
21
|
+
def self.pepper
|
22
|
+
'pepper'
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.find_account(identity)
|
26
|
+
first_from_view(:by_email, identity)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
Rack::CouchdbOAuth2::Configuration.account_class = ::User
|
31
|
+
|
17
32
|
class Test::Unit::TestCase
|
18
33
|
def assert_error_response(response)
|
19
34
|
assert !response.ok?
|
@@ -21,6 +36,22 @@ class Test::Unit::TestCase
|
|
21
36
|
assert_not_nil(body['error'])
|
22
37
|
assert_nil(body['access_token'])
|
23
38
|
end
|
39
|
+
|
40
|
+
def create_client
|
41
|
+
@client = Client.create(:name => 'test client')
|
42
|
+
end
|
43
|
+
|
44
|
+
def create_account
|
45
|
+
@account = User.create(:email => 'test@example.com', :password => 'abc123', :password_confirmation => 'abc123' )
|
46
|
+
end
|
47
|
+
|
48
|
+
def destroy_client
|
49
|
+
@client.destroy
|
50
|
+
end
|
51
|
+
|
52
|
+
def destroy_account
|
53
|
+
@account.destroy
|
54
|
+
end
|
24
55
|
end
|
25
56
|
|
26
57
|
class TestApp
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class TestRequireClient < Test::Unit::TestCase
|
4
|
+
include Rack::Test::Methods
|
5
|
+
|
6
|
+
def test_presents
|
7
|
+
a = User.new()
|
8
|
+
assert(!a.save, "Failure message.")
|
9
|
+
a.email = 'abc@example.com'
|
10
|
+
assert(!a.save, "Failure message.")
|
11
|
+
a.password = 'abcdef'
|
12
|
+
assert(!a.save, "Failure message.")
|
13
|
+
a.password_confirmation = 'wrong'
|
14
|
+
assert(!a.save, "Failure message.")
|
15
|
+
a.password_confirmation = 'abcdef'
|
16
|
+
assert(a.save, "Failure message.")
|
17
|
+
a.destroy
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_attr_protected
|
21
|
+
a = User.new(:email => 'aaa@example.com', :encrypted_password => "pwd", :pepper => "pepper")
|
22
|
+
assert_equal('aaa@example.com', a.email)
|
23
|
+
assert_nil(a.encrypted_password)
|
24
|
+
assert_nil(a.pepper)
|
25
|
+
end
|
26
|
+
end
|
data/test/test_client.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class TestClient < Test::Unit::TestCase
|
4
|
+
include Rack::Test::Methods
|
5
|
+
|
6
|
+
def setup
|
7
|
+
create_client
|
8
|
+
end
|
9
|
+
|
10
|
+
def teardown
|
11
|
+
destroy_client
|
12
|
+
end
|
13
|
+
|
14
|
+
def app
|
15
|
+
Rack::Builder.new do
|
16
|
+
run proc {|env|
|
17
|
+
client = Client.find_by_env(env)
|
18
|
+
raise 'no client' if client.nil?
|
19
|
+
[200, {}, 'OK']
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_presents
|
25
|
+
c = Client.new()
|
26
|
+
assert(!c.save, "Failure message.")
|
27
|
+
c.name = 'abc'
|
28
|
+
assert(c.save, "Failure message.")
|
29
|
+
|
30
|
+
c2 = Client.new :name => 'abc', :secret => 's'
|
31
|
+
assert_nil(c2.secret)
|
32
|
+
assert(!c2.save, "Failure message.")
|
33
|
+
c.destroy
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_find_by_env
|
37
|
+
assert_raise(RuntimeError) {
|
38
|
+
get '/'
|
39
|
+
}
|
40
|
+
assert_raise(RuntimeError) {
|
41
|
+
get '/', :client_id => @client.identity, :client_secret =>'wrong secret'
|
42
|
+
}
|
43
|
+
assert_nothing_raised(RuntimeError) {
|
44
|
+
get '/', :client_id => @client.identity, :client_secret =>@client.secret
|
45
|
+
}
|
46
|
+
assert_raise(RuntimeError) {
|
47
|
+
get '/', :client_id => "wrong identity", :client_secret =>'wrong secret'
|
48
|
+
}
|
49
|
+
end
|
50
|
+
end
|
@@ -4,20 +4,19 @@ class TestRackCouchdbOauth2 < Test::Unit::TestCase
|
|
4
4
|
include Rack::Test::Methods
|
5
5
|
|
6
6
|
def setup
|
7
|
-
|
8
|
-
|
7
|
+
create_client
|
8
|
+
create_account
|
9
9
|
end
|
10
10
|
|
11
11
|
def teardown
|
12
|
-
|
13
|
-
|
12
|
+
destroy_client
|
13
|
+
destroy_account
|
14
14
|
end
|
15
15
|
|
16
16
|
def app
|
17
17
|
Rack::Builder.new do
|
18
18
|
map '/db' do
|
19
19
|
use Rack::CouchdbOAuth2::RequireBearerToken
|
20
|
-
use Rack::CouchdbOAuth2::RequireClient
|
21
20
|
run TestApp.new
|
22
21
|
end
|
23
22
|
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class TestRequireClient < Test::Unit::TestCase
|
4
|
+
include Rack::Test::Methods
|
5
|
+
|
6
|
+
def setup
|
7
|
+
create_client
|
8
|
+
create_account
|
9
|
+
end
|
10
|
+
|
11
|
+
def teardown
|
12
|
+
destroy_client
|
13
|
+
destroy_account
|
14
|
+
end
|
15
|
+
|
16
|
+
def app
|
17
|
+
@app ||= Rack::Builder.new do
|
18
|
+
map '/db' do
|
19
|
+
use Rack::CouchdbOAuth2::RequireBearerToken
|
20
|
+
use Rack::CouchdbOAuth2::RequireClient
|
21
|
+
run TestApp.new
|
22
|
+
end
|
23
|
+
|
24
|
+
map '/oauth2' do
|
25
|
+
map '/token' do
|
26
|
+
run Rack::CouchdbOAuth2::TokenEndpoint.new
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def change_to_false_client_id
|
33
|
+
@app ||= Rack::Builder.new do
|
34
|
+
map '/db' do
|
35
|
+
use Rack::CouchdbOAuth2::RequireBearerToken
|
36
|
+
use Rack::CouchdbOAuth2::RequireClient do |req|
|
37
|
+
req.client.identity == 'false id'
|
38
|
+
end
|
39
|
+
run TestApp.new
|
40
|
+
end
|
41
|
+
|
42
|
+
map '/oauth2' do
|
43
|
+
map '/token' do
|
44
|
+
run Rack::CouchdbOAuth2::TokenEndpoint.new
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def change_to_use_client_id
|
51
|
+
identity = @client.identity
|
52
|
+
@app ||= Rack::Builder.new do
|
53
|
+
map '/db' do
|
54
|
+
use Rack::CouchdbOAuth2::RequireBearerToken
|
55
|
+
use Rack::CouchdbOAuth2::RequireClient do |req|
|
56
|
+
req.client.identity == identity
|
57
|
+
end
|
58
|
+
run TestApp.new
|
59
|
+
end
|
60
|
+
|
61
|
+
map '/oauth2' do
|
62
|
+
map '/token' do
|
63
|
+
run Rack::CouchdbOAuth2::TokenEndpoint.new
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_require_client
|
70
|
+
header 'AUTHORIZATION', nil
|
71
|
+
get 'db'
|
72
|
+
assert_error_response last_response
|
73
|
+
|
74
|
+
post 'oauth2/token', :grant_type => 'password', :client_id => @client.identity, :client_secret => @client.secret, :username => @account.email, :password => 'abc123'
|
75
|
+
assert last_response.ok?
|
76
|
+
body = ActiveSupport::JSON.decode(last_response.body)
|
77
|
+
access_token = body['access_token']
|
78
|
+
refresh_token = body['refresh_token']
|
79
|
+
assert_not_nil(access_token)
|
80
|
+
assert_not_nil(refresh_token)
|
81
|
+
assert_equal('bearer', body['token_type'])
|
82
|
+
|
83
|
+
header 'AUTHORIZATION', "Bearer #{access_token}"
|
84
|
+
get 'db'
|
85
|
+
assert last_response.ok?
|
86
|
+
assert_equal(TestApp::BODY, last_response.body)
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_require_client_with_wrong_id
|
90
|
+
change_to_false_client_id
|
91
|
+
header 'AUTHORIZATION', nil
|
92
|
+
get 'db'
|
93
|
+
assert_error_response last_response
|
94
|
+
|
95
|
+
post 'oauth2/token', :grant_type => 'password', :client_id => @client.identity, :client_secret => @client.secret, :username => @account.email, :password => 'abc123'
|
96
|
+
assert last_response.ok?
|
97
|
+
body = ActiveSupport::JSON.decode(last_response.body)
|
98
|
+
access_token = body['access_token']
|
99
|
+
refresh_token = body['refresh_token']
|
100
|
+
assert_not_nil(access_token)
|
101
|
+
assert_not_nil(refresh_token)
|
102
|
+
assert_equal('bearer', body['token_type'])
|
103
|
+
|
104
|
+
header 'AUTHORIZATION', "Bearer #{access_token}"
|
105
|
+
get 'db'
|
106
|
+
assert_error_response last_response
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_require_client_with_correct_id
|
110
|
+
change_to_use_client_id
|
111
|
+
header 'AUTHORIZATION', nil
|
112
|
+
get 'db'
|
113
|
+
assert_error_response last_response
|
114
|
+
|
115
|
+
post 'oauth2/token', :grant_type => 'password', :client_id => @client.identity, :client_secret => @client.secret, :username => @account.email, :password => 'abc123'
|
116
|
+
assert last_response.ok?
|
117
|
+
body = ActiveSupport::JSON.decode(last_response.body)
|
118
|
+
access_token = body['access_token']
|
119
|
+
refresh_token = body['refresh_token']
|
120
|
+
assert_not_nil(access_token)
|
121
|
+
assert_not_nil(refresh_token)
|
122
|
+
assert_equal('bearer', body['token_type'])
|
123
|
+
|
124
|
+
header 'AUTHORIZATION', "Bearer #{access_token}"
|
125
|
+
get 'db'
|
126
|
+
assert last_response.ok?
|
127
|
+
assert_equal(TestApp::BODY, last_response.body)
|
128
|
+
|
129
|
+
end
|
130
|
+
end
|
data/test/test_token.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestToken < Test::Unit::TestCase
|
4
|
+
include Rack::Test::Methods
|
5
|
+
|
6
|
+
def setup
|
7
|
+
create_client
|
8
|
+
create_account
|
9
|
+
end
|
10
|
+
|
11
|
+
def teardown
|
12
|
+
destroy_client
|
13
|
+
destroy_account
|
14
|
+
end
|
15
|
+
|
16
|
+
def app
|
17
|
+
Rack::Builder.new do
|
18
|
+
map '/oauth2' do
|
19
|
+
map '/token' do
|
20
|
+
run Rack::CouchdbOAuth2::TokenEndpoint.new
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
map '/db' do
|
25
|
+
use Rack::OAuth2::Server::Resource::Bearer, 'test' do |req|
|
26
|
+
|
27
|
+
end
|
28
|
+
run proc {|env|
|
29
|
+
token = AccessToken.find_by_env(env)
|
30
|
+
raise 'no client' if token.nil?
|
31
|
+
[200, {}, 'OK']
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_find_by_env
|
38
|
+
post 'oauth2/token', :grant_type => 'password', :client_id => @client.identity, :client_secret => @client.secret, :username => @account.email, :password => 'abc123'
|
39
|
+
assert last_response.ok?
|
40
|
+
body = ActiveSupport::JSON.decode(last_response.body)
|
41
|
+
access_token = body['access_token']
|
42
|
+
refresh_token = body['refresh_token']
|
43
|
+
assert_not_nil(access_token)
|
44
|
+
assert_not_nil(refresh_token)
|
45
|
+
assert_equal('bearer', body['token_type'])
|
46
|
+
|
47
|
+
assert_raise(RuntimeError) {
|
48
|
+
header 'AUTHORIZATION', nil
|
49
|
+
get 'db'
|
50
|
+
}
|
51
|
+
|
52
|
+
assert_nothing_raised(RuntimeError) {
|
53
|
+
header 'AUTHORIZATION', "Bearer #{access_token}"
|
54
|
+
get 'db'
|
55
|
+
}
|
56
|
+
|
57
|
+
assert_raise(RuntimeError) {
|
58
|
+
header 'AUTHORIZATION', nil
|
59
|
+
get 'db'
|
60
|
+
}
|
61
|
+
end
|
62
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-couchdb-oauth2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 2
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Robin Lu
|
@@ -15,14 +15,13 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-06-
|
18
|
+
date: 2011-06-27 00:00:00 +08:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
22
|
prerelease: false
|
23
|
-
name: rack
|
24
23
|
type: :runtime
|
25
|
-
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
26
25
|
none: false
|
27
26
|
requirements:
|
28
27
|
- - ">="
|
@@ -31,12 +30,12 @@ dependencies:
|
|
31
30
|
segments:
|
32
31
|
- 0
|
33
32
|
version: "0"
|
34
|
-
|
33
|
+
name: rack
|
34
|
+
version_requirements: *id001
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
36
|
prerelease: false
|
37
|
-
name: couchrest
|
38
37
|
type: :runtime
|
39
|
-
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
39
|
none: false
|
41
40
|
requirements:
|
42
41
|
- - ">="
|
@@ -45,12 +44,12 @@ dependencies:
|
|
45
44
|
segments:
|
46
45
|
- 0
|
47
46
|
version: "0"
|
48
|
-
|
47
|
+
name: couchrest
|
48
|
+
version_requirements: *id002
|
49
49
|
- !ruby/object:Gem::Dependency
|
50
50
|
prerelease: false
|
51
|
-
name: couchrest_model
|
52
51
|
type: :runtime
|
53
|
-
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
54
53
|
none: false
|
55
54
|
requirements:
|
56
55
|
- - ">="
|
@@ -59,12 +58,12 @@ dependencies:
|
|
59
58
|
segments:
|
60
59
|
- 0
|
61
60
|
version: "0"
|
62
|
-
|
61
|
+
name: couchrest_model
|
62
|
+
version_requirements: *id003
|
63
63
|
- !ruby/object:Gem::Dependency
|
64
64
|
prerelease: false
|
65
|
-
name: activesupport
|
66
65
|
type: :runtime
|
67
|
-
|
66
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
68
67
|
none: false
|
69
68
|
requirements:
|
70
69
|
- - ">="
|
@@ -73,12 +72,12 @@ dependencies:
|
|
73
72
|
segments:
|
74
73
|
- 0
|
75
74
|
version: "0"
|
76
|
-
|
75
|
+
name: activesupport
|
76
|
+
version_requirements: *id004
|
77
77
|
- !ruby/object:Gem::Dependency
|
78
78
|
prerelease: false
|
79
|
-
name: rack-oauth2
|
80
79
|
type: :runtime
|
81
|
-
|
80
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
82
81
|
none: false
|
83
82
|
requirements:
|
84
83
|
- - ">="
|
@@ -87,12 +86,12 @@ dependencies:
|
|
87
86
|
segments:
|
88
87
|
- 0
|
89
88
|
version: "0"
|
90
|
-
|
89
|
+
name: rack-oauth2
|
90
|
+
version_requirements: *id005
|
91
91
|
- !ruby/object:Gem::Dependency
|
92
92
|
prerelease: false
|
93
|
-
name: bcrypt-ruby
|
94
93
|
type: :runtime
|
95
|
-
|
94
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
96
95
|
none: false
|
97
96
|
requirements:
|
98
97
|
- - ">="
|
@@ -101,12 +100,12 @@ dependencies:
|
|
101
100
|
segments:
|
102
101
|
- 0
|
103
102
|
version: "0"
|
104
|
-
|
103
|
+
name: bcrypt-ruby
|
104
|
+
version_requirements: *id006
|
105
105
|
- !ruby/object:Gem::Dependency
|
106
106
|
prerelease: false
|
107
|
-
name: bundler
|
108
107
|
type: :development
|
109
|
-
|
108
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
110
109
|
none: false
|
111
110
|
requirements:
|
112
111
|
- - ~>
|
@@ -117,12 +116,12 @@ dependencies:
|
|
117
116
|
- 0
|
118
117
|
- 0
|
119
118
|
version: 1.0.0
|
120
|
-
|
119
|
+
name: bundler
|
120
|
+
version_requirements: *id007
|
121
121
|
- !ruby/object:Gem::Dependency
|
122
122
|
prerelease: false
|
123
|
-
name: jeweler
|
124
123
|
type: :development
|
125
|
-
|
124
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
126
125
|
none: false
|
127
126
|
requirements:
|
128
127
|
- - ~>
|
@@ -133,12 +132,12 @@ dependencies:
|
|
133
132
|
- 6
|
134
133
|
- 2
|
135
134
|
version: 1.6.2
|
136
|
-
|
135
|
+
name: jeweler
|
136
|
+
version_requirements: *id008
|
137
137
|
- !ruby/object:Gem::Dependency
|
138
138
|
prerelease: false
|
139
|
-
name: rcov
|
140
139
|
type: :development
|
141
|
-
|
140
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
142
141
|
none: false
|
143
142
|
requirements:
|
144
143
|
- - ">="
|
@@ -147,12 +146,12 @@ dependencies:
|
|
147
146
|
segments:
|
148
147
|
- 0
|
149
148
|
version: "0"
|
150
|
-
|
149
|
+
name: rcov
|
150
|
+
version_requirements: *id009
|
151
151
|
- !ruby/object:Gem::Dependency
|
152
152
|
prerelease: false
|
153
|
-
name: rack-test
|
154
153
|
type: :development
|
155
|
-
|
154
|
+
requirement: &id010 !ruby/object:Gem::Requirement
|
156
155
|
none: false
|
157
156
|
requirements:
|
158
157
|
- - ">="
|
@@ -161,7 +160,8 @@ dependencies:
|
|
161
160
|
segments:
|
162
161
|
- 0
|
163
162
|
version: "0"
|
164
|
-
|
163
|
+
name: rack-test
|
164
|
+
version_requirements: *id010
|
165
165
|
description: Rack middleware for OAuth2 Provider Server Based on Couchdb
|
166
166
|
email: iamawalrus@gmail.com
|
167
167
|
executables: []
|
@@ -179,6 +179,7 @@ files:
|
|
179
179
|
- README.rdoc
|
180
180
|
- Rakefile
|
181
181
|
- VERSION
|
182
|
+
- lib/couchdb_oauth2/configuration.rb
|
182
183
|
- lib/couchdb_oauth2/model/access_token.rb
|
183
184
|
- lib/couchdb_oauth2/model/account.rb
|
184
185
|
- lib/couchdb_oauth2/model/base.rb
|
@@ -191,7 +192,11 @@ files:
|
|
191
192
|
- lib/rack-couchdb-oauth2.rb
|
192
193
|
- rack-couchdb-oauth2.gemspec
|
193
194
|
- test/helper.rb
|
195
|
+
- test/test_account.rb
|
196
|
+
- test/test_client.rb
|
194
197
|
- test/test_rack-couchdb-oauth2.rb
|
198
|
+
- test/test_require_client.rb
|
199
|
+
- test/test_token.rb
|
195
200
|
has_rdoc: true
|
196
201
|
homepage: http://github.com/robin/rack-couchdb-oauth2
|
197
202
|
licenses:
|
@@ -222,7 +227,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
222
227
|
requirements: []
|
223
228
|
|
224
229
|
rubyforge_project:
|
225
|
-
rubygems_version: 1.
|
230
|
+
rubygems_version: 1.6.2
|
226
231
|
signing_key:
|
227
232
|
specification_version: 3
|
228
233
|
summary: Rack middleware for OAuth2 Provider Server Based on Couchdb
|