rack-couchdb-oauth2 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+ gem 'rack'
6
+ gem 'couchrest'
7
+ gem 'couchrest_model', :git => 'git://github.com/couchrest/couchrest_model.git'
8
+ gem 'activesupport'
9
+ gem 'rack-oauth2'
10
+ gem 'bcrypt-ruby'
11
+
12
+ # Add dependencies to develop your gem here.
13
+ # Include everything needed to run rake, tests, features, etc.
14
+ group :development do
15
+ gem "bundler", "~> 1.0.0"
16
+ gem "jeweler", "~> 1.6.2"
17
+ gem "rcov", ">= 0"
18
+ gem 'rack-test'
19
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,65 @@
1
+ GIT
2
+ remote: git://github.com/couchrest/couchrest_model.git
3
+ revision: 98772ae98a4685d6a69a77a50d30bd2118cc3aad
4
+ specs:
5
+ couchrest_model (1.1.0.rc1)
6
+ activemodel (~> 3.0)
7
+ couchrest (= 1.1.0.pre3)
8
+ mime-types (~> 1.15)
9
+ tzinfo (~> 0.3.22)
10
+
11
+ GEM
12
+ remote: http://rubygems.org/
13
+ specs:
14
+ activemodel (3.0.8)
15
+ activesupport (= 3.0.8)
16
+ builder (~> 2.1.2)
17
+ i18n (~> 0.5.0)
18
+ activesupport (3.0.8)
19
+ attr_required (0.0.3)
20
+ bcrypt-ruby (2.1.4)
21
+ builder (2.1.2)
22
+ couchrest (1.1.0.pre3)
23
+ mime-types (~> 1.15)
24
+ multi_json (~> 1.0.0)
25
+ rest-client (~> 1.6.1)
26
+ git (1.2.5)
27
+ httpclient (2.2.1)
28
+ i18n (0.5.0)
29
+ jeweler (1.6.2)
30
+ bundler (~> 1.0)
31
+ git (>= 1.2.5)
32
+ rake
33
+ json (1.5.2)
34
+ mime-types (1.16)
35
+ multi_json (1.0.3)
36
+ rack (1.3.0)
37
+ rack-oauth2 (0.8.1)
38
+ activesupport (>= 2.3)
39
+ attr_required (>= 0.0.3)
40
+ httpclient (>= 2.2.0.2)
41
+ i18n
42
+ json (>= 1.4.3)
43
+ rack (>= 1.1)
44
+ rack-test (0.6.0)
45
+ rack (>= 1.0)
46
+ rake (0.9.2)
47
+ rcov (0.9.9)
48
+ rest-client (1.6.3)
49
+ mime-types (>= 1.16)
50
+ tzinfo (0.3.28)
51
+
52
+ PLATFORMS
53
+ ruby
54
+
55
+ DEPENDENCIES
56
+ activesupport
57
+ bcrypt-ruby
58
+ bundler (~> 1.0.0)
59
+ couchrest
60
+ couchrest_model!
61
+ jeweler (~> 1.6.2)
62
+ rack
63
+ rack-oauth2
64
+ rack-test
65
+ rcov
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Robin Lu
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,19 @@
1
+ = rack-couchdb-oauth2
2
+
3
+ Rack middleware for OAuth2 Provider Server Based on Couchdb
4
+
5
+ == Contributing to rack-couchdb-oauth2
6
+
7
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
8
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
9
+ * Fork the project
10
+ * Start a feature/bugfix branch
11
+ * Commit and push until you are happy with your contribution
12
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2011 Robin Lu. See LICENSE.txt for
18
+ further details.
19
+
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "rack-couchdb-oauth2"
18
+ gem.homepage = "http://github.com/robin/rack-couchdb-oauth2"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Rack middleware for OAuth2 Provider Server Based on Couchdb}
21
+ gem.description = %Q{Rack middleware for OAuth2 Provider Server Based on Couchdb}
22
+ gem.email = "iamawalrus@gmail.com"
23
+ gem.authors = ["Robin Lu"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+
35
+ require 'rcov/rcovtask'
36
+ Rcov::RcovTask.new do |test|
37
+ test.libs << 'test'
38
+ test.pattern = 'test/**/test_*.rb'
39
+ test.verbose = true
40
+ test.rcov_opts << '--exclude "gems/*"'
41
+ end
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "rack-couchdb-oauth2 #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,31 @@
1
+ class AccessToken < CouchRest::Model::Base
2
+ include CouchdbOAuth2::Model::Base
3
+ include Oauth2Token
4
+ self.default_lifetime = 15.minutes
5
+
6
+ belongs_to :refresh_token
7
+ property :token, String
8
+ property :expires_at, Time
9
+ timestamps!
10
+
11
+ def to_bearer_token(with_refresh_token = false)
12
+ bearer_token = Rack::OAuth2::AccessToken::Bearer.new(
13
+ :access_token => self.token,
14
+ :expires_in => self.expires_in
15
+ )
16
+ if with_refresh_token
17
+ bearer_token.refresh_token = self.refresh_token.token
18
+ end
19
+ bearer_token
20
+ end
21
+
22
+ private
23
+
24
+ def setup
25
+ super
26
+ if self.refresh_token.nil? && self.account
27
+ self.refresh_token = RefreshToken.create(:account => self.account, :client => self.client)
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,74 @@
1
+ require 'bcrypt'
2
+
3
+ class Account < CouchRest::Model::Base
4
+ include CouchdbOAuth2::Model::Base
5
+
6
+ property :email, String
7
+ property :encrypted_password, String
8
+ timestamps!
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
51
+
52
+ # Digests the password using bcrypt.
53
+ def password_digest(password)
54
+ ::BCrypt::Password.create("#{password}#{self.class.pepper}", :cost => self.class.stretches).to_s
55
+ end
56
+
57
+ def self.stretches
58
+ 5
59
+ end
60
+
61
+ def self.pepper
62
+ '5ad96cc293abadd5322908c597a363205b909c99fab13b59895b6e3fc93540f2f276800d6718fa174c9a9720e1148b4da19ee58c779078efe98ca2c76c8cdd40'
63
+ end
64
+
65
+ def self.secure_compare(a, b)
66
+ return false if a.blank? || b.blank? || a.bytesize != b.bytesize
67
+ l = a.unpack "C#{a.bytesize}"
68
+
69
+ res = 0
70
+ b.each_byte { |byte| res |= byte ^ l.shift }
71
+ res == 0
72
+ end
73
+
74
+ end
@@ -0,0 +1,12 @@
1
+ module CouchdbOAuth2
2
+ module Model
3
+ module Base
4
+ def self.included(klass)
5
+ klass.class_eval do
6
+ use_database klass.name.tableize
7
+ end
8
+ end
9
+ end
10
+ end
11
+
12
+ end
@@ -0,0 +1,21 @@
1
+ class Client < CouchRest::Model::Base
2
+ include CouchdbOAuth2::Model::Base
3
+
4
+ property :name, String
5
+ property :redirect_url, String
6
+ property :website, String
7
+ property :secret, String
8
+ timestamps!
9
+
10
+ before_validation :setup, :on => :create
11
+
12
+ def setup
13
+ if self.secret.nil?
14
+ self.secret = ActiveSupport::SecureRandom.base64(64)
15
+ end
16
+ end
17
+
18
+ def identity
19
+ self['_id']
20
+ end
21
+ end
@@ -0,0 +1,52 @@
1
+ module Oauth2Token
2
+ def self.included(klass)
3
+ klass.class_eval do
4
+ cattr_accessor :default_lifetime
5
+ self.default_lifetime = 1.minute
6
+
7
+ belongs_to :account
8
+ belongs_to :client
9
+
10
+ view_by :expires_at
11
+ view_by :account_id
12
+ view_by :client_id
13
+ view_by :token
14
+
15
+ before_validation :setup, :on => :create
16
+ validates :client, :expires_at, :account, :presence => true
17
+ validates :token, :presence => true, :uniqueness => true
18
+ end
19
+ end
20
+
21
+ def expires_in
22
+ (expires_at - Time.now.utc).to_i
23
+ end
24
+
25
+ def expired!
26
+ self.expires_at = Time.now.utc
27
+ self.save!
28
+ end
29
+
30
+ def expired?
31
+ self.expires_at < Time.now.utc
32
+ end
33
+
34
+ def self.valid
35
+ view(:by_expires_at, :startkey => Time.now.utc)
36
+ end
37
+
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
+ private
43
+
44
+ def self.generate(bytes = 64)
45
+ ActiveSupport::SecureRandom.base64(bytes)
46
+ end
47
+
48
+ def setup
49
+ self.token = Oauth2Token.generate
50
+ self.expires_at ||= self.default_lifetime.from_now
51
+ end
52
+ end
@@ -0,0 +1,12 @@
1
+ class RefreshToken < CouchRest::Model::Base
2
+ include CouchdbOAuth2::Model::Base
3
+ include Oauth2Token
4
+ belongs_to :account
5
+ belongs_to :client
6
+ property :token, String
7
+ property :expires_at, Time
8
+ timestamps!
9
+
10
+ self.default_lifetime = 1.month
11
+
12
+ end
@@ -0,0 +1,42 @@
1
+ module Rack
2
+ module CouchdbOAuth2
3
+ class RequireBearerToken < Rack::OAuth2::Server::Resource
4
+ ACCESS_TOKEN = "rack.oauth2.couchdb.access_token"
5
+ def initialize(app)
6
+ super(app, "Rack CouchdbOAuth2 RequireBearerToken") do |req|
7
+ token = req.couchdb_access_token
8
+ if token.nil? || token.expired?
9
+ req.invalid_token!
10
+ else
11
+ token
12
+ end
13
+ end
14
+ end
15
+
16
+ def call(env)
17
+ self.request = Request.new(env).setup!
18
+ self.request.invalid_token! unless self.request.oauth2?
19
+ env[ACCESS_TOKEN] = self.request.couchdb_access_token if self.request.couchdb_access_token
20
+ super(env)
21
+ rescue Rack::OAuth2::Server::Abstract::Error => e
22
+ e.realm ||= realm
23
+ e.finish
24
+ end
25
+
26
+ class Request < Rack::OAuth2::Server::Resource::Bearer::Request
27
+ attr_reader :couchdb_access_token
28
+ def initialize(env)
29
+ super(env)
30
+ end
31
+
32
+ def setup!
33
+ super
34
+ @couchdb_access_token = AccessToken.find_by_token(self.access_token) if self.access_token
35
+ self
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,58 @@
1
+ module Rack
2
+ module CouchdbOAuth2
3
+ class RequireClient
4
+ CLIENT = "rack.oauth2.client"
5
+ def initialize(app, &authenticator)
6
+ @app = app
7
+ @authenticator = authenticator
8
+ end
9
+
10
+ def call(env)
11
+ authenticate!(env)
12
+ @app.call(env)
13
+ rescue Rack::OAuth2::Server::Abstract::Error => e
14
+ e.realm = "Client Not Authorized"
15
+ e.finish
16
+ end
17
+
18
+ private
19
+
20
+ def authenticate!(env)
21
+ request = Request.new(env)
22
+ client = request.client
23
+ request.unauthorized!(:invalid_token) unless client
24
+ if @authenticator
25
+ request.unauthorized! unless @authenticator.call(request)
26
+ end
27
+ env[CLIENT] = client
28
+ end
29
+
30
+ class Request < Rack::Request
31
+ attr_reader :access_token
32
+ attr_reader :client
33
+
34
+ def initialize(env)
35
+ super(env)
36
+ @access_token = env[RequireBearerToken::ACCESS_TOKEN]
37
+ @client = @access_token ? @access_token.client : nil
38
+ end
39
+ end
40
+
41
+ class Unauthorized < Rack::OAuth2::Server::Resource::Unauthorized
42
+ def scheme
43
+ 'client'
44
+ end
45
+ end
46
+
47
+ module ErrorMethods
48
+ include Rack::OAuth2::Server::Resource::ErrorMethods
49
+ def unauthorized!(error = nil, description = nil, options = {})
50
+ raise Unauthorized.new(error, description, options)
51
+ end
52
+ end
53
+
54
+ Request.send :include, ErrorMethods
55
+
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,38 @@
1
+ module Rack
2
+ module CouchdbOAuth2
3
+ class TokenEndpoint
4
+ def call(env)
5
+ authenticator.call(env)
6
+ end
7
+
8
+ private
9
+
10
+ def authenticator
11
+ Rack::OAuth2::Server::Token.new do |req, res|
12
+ client = Client.find(req.client_id) || req.invalid_client!
13
+ client.secret == req.client_secret || req.invalid_client!
14
+ case req.grant_type
15
+ when :authorization_code
16
+ #not implemented
17
+ req.unsupported_grant_type!
18
+ when :password
19
+ # NOTE: password is not hashed in this sample app. Don't do the same on your app.
20
+ account = req.username.nil? ? nil : Account.first_from_view(:by_email, req.username)
21
+ req.invalid_grant! unless account && account.valid_password?(req.password)
22
+ res.access_token = AccessToken.create(:client => client, :account => account).to_bearer_token(:with_refresh_token)
23
+ when :client_credentials
24
+ # NOTE: client is already authenticated here.
25
+ res.access_token = AccessToken.create(:client => client).to_bearer_token
26
+ when :refresh_token
27
+ refresh_token = RefreshToken.find_by_token(req.refresh_token)
28
+ req.invalid_grant! unless refresh_token && !refresh_token.expired? && refresh_token.client && refresh_token.client.id == client.id
29
+ res.access_token = AccessToken.create(:client => client, :account => refresh_token.account, :refresh_token => refresh_token).to_bearer_token
30
+ else
31
+ # NOTE: extended assertion grant_types are not supported yet.
32
+ req.unsupported_grant_type!
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,14 @@
1
+ require 'rack/oauth2'
2
+ require 'couchrest'
3
+ require 'couchrest_model'
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
+
12
+ require 'couchdb_oauth2/token_endpoint'
13
+ require 'couchdb_oauth2/resource/require_bearer_token'
14
+ require 'couchdb_oauth2/resource/require_client'
@@ -0,0 +1,86 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rack-couchdb-oauth2}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Robin Lu"]
12
+ s.date = %q{2011-06-15}
13
+ s.description = %q{Rack middleware for OAuth2 Provider Server Based on Couchdb}
14
+ s.email = %q{iamawalrus@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE.txt",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "lib/couchdb_oauth2/model/access_token.rb",
28
+ "lib/couchdb_oauth2/model/account.rb",
29
+ "lib/couchdb_oauth2/model/base.rb",
30
+ "lib/couchdb_oauth2/model/client.rb",
31
+ "lib/couchdb_oauth2/model/oauth2_token.rb",
32
+ "lib/couchdb_oauth2/model/refresh_token.rb",
33
+ "lib/couchdb_oauth2/resource/require_bearer_token.rb",
34
+ "lib/couchdb_oauth2/resource/require_client.rb",
35
+ "lib/couchdb_oauth2/token_endpoint.rb",
36
+ "lib/rack-couchdb-oauth2.rb",
37
+ "rack-couchdb-oauth2.gemspec",
38
+ "test/helper.rb",
39
+ "test/test_rack-couchdb-oauth2.rb"
40
+ ]
41
+ s.homepage = %q{http://github.com/robin/rack-couchdb-oauth2}
42
+ s.licenses = ["MIT"]
43
+ s.require_paths = ["lib"]
44
+ s.rubygems_version = %q{1.4.2}
45
+ s.summary = %q{Rack middleware for OAuth2 Provider Server Based on Couchdb}
46
+
47
+ if s.respond_to? :specification_version then
48
+ s.specification_version = 3
49
+
50
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
51
+ s.add_runtime_dependency(%q<rack>, [">= 0"])
52
+ s.add_runtime_dependency(%q<couchrest>, [">= 0"])
53
+ s.add_runtime_dependency(%q<couchrest_model>, [">= 0"])
54
+ s.add_runtime_dependency(%q<activesupport>, [">= 0"])
55
+ s.add_runtime_dependency(%q<rack-oauth2>, [">= 0"])
56
+ s.add_runtime_dependency(%q<bcrypt-ruby>, [">= 0"])
57
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
58
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.2"])
59
+ s.add_development_dependency(%q<rcov>, [">= 0"])
60
+ s.add_development_dependency(%q<rack-test>, [">= 0"])
61
+ else
62
+ s.add_dependency(%q<rack>, [">= 0"])
63
+ s.add_dependency(%q<couchrest>, [">= 0"])
64
+ s.add_dependency(%q<couchrest_model>, [">= 0"])
65
+ s.add_dependency(%q<activesupport>, [">= 0"])
66
+ s.add_dependency(%q<rack-oauth2>, [">= 0"])
67
+ s.add_dependency(%q<bcrypt-ruby>, [">= 0"])
68
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
69
+ s.add_dependency(%q<jeweler>, ["~> 1.6.2"])
70
+ s.add_dependency(%q<rcov>, [">= 0"])
71
+ s.add_dependency(%q<rack-test>, [">= 0"])
72
+ end
73
+ else
74
+ s.add_dependency(%q<rack>, [">= 0"])
75
+ s.add_dependency(%q<couchrest>, [">= 0"])
76
+ s.add_dependency(%q<couchrest_model>, [">= 0"])
77
+ s.add_dependency(%q<activesupport>, [">= 0"])
78
+ s.add_dependency(%q<rack-oauth2>, [">= 0"])
79
+ s.add_dependency(%q<bcrypt-ruby>, [">= 0"])
80
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
81
+ s.add_dependency(%q<jeweler>, ["~> 1.6.2"])
82
+ s.add_dependency(%q<rcov>, [">= 0"])
83
+ s.add_dependency(%q<rack-test>, [">= 0"])
84
+ end
85
+ end
86
+
data/test/helper.rb ADDED
@@ -0,0 +1,31 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ require 'rack/test'
4
+ begin
5
+ Bundler.setup(:default, :development)
6
+ rescue Bundler::BundlerError => e
7
+ $stderr.puts e.message
8
+ $stderr.puts "Run `bundle install` to install missing gems"
9
+ exit e.status_code
10
+ end
11
+ require 'test/unit'
12
+
13
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
14
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
15
+ require 'rack-couchdb-oauth2'
16
+
17
+ class Test::Unit::TestCase
18
+ def assert_error_response(response)
19
+ assert !response.ok?
20
+ body = ActiveSupport::JSON.decode(response.body)
21
+ assert_not_nil(body['error'])
22
+ assert_nil(body['access_token'])
23
+ end
24
+ end
25
+
26
+ class TestApp
27
+ BODY = 'done'
28
+ def call(env)
29
+ [200, {"Content-Type" => "text/plain"}, BODY]
30
+ end
31
+ end
@@ -0,0 +1,125 @@
1
+ require 'helper'
2
+
3
+ class TestRackCouchdbOauth2 < Test::Unit::TestCase
4
+ include Rack::Test::Methods
5
+
6
+ def setup
7
+ @client = Client.create(:name => 'test client')
8
+ @account = Account.create(:email => 'test@example.com', :password => 'abc123' )
9
+ end
10
+
11
+ def teardown
12
+ @client.destroy
13
+ @account.destroy
14
+ end
15
+
16
+ def app
17
+ 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 test_oauth2_token_by_password_error
33
+
34
+ post 'oauth2/token', :grant_type => 'password', :client_id => @client.identity, :client_secret =>'wrong secret', :username => @account.email, :password => 'abc123'
35
+ assert_error_response last_response
36
+ assert_equal(401, last_response.status)
37
+
38
+ post 'oauth2/token', :grant_type => 'password', :client_id => 'wrong identity', :client_secret => @client.secret, :username => @account.email, :password => 'abc123'
39
+ assert_error_response last_response
40
+ assert_equal(401, last_response.status)
41
+
42
+ post 'oauth2/token', :grant_type => 'password', :client_id => @client.identity, :client_secret => @client.secret, :username => 'bademail', :password => 'abc123'
43
+ assert_error_response last_response
44
+ assert_equal(400, last_response.status)
45
+
46
+ post 'oauth2/token', :grant_type => 'password', :client_id => @client.identity, :client_secret => @client.secret, :username => @account.email, :password => 'badpassword'
47
+ assert_error_response last_response
48
+ assert_equal(400, last_response.status)
49
+ end
50
+
51
+ def test_oauth2_token_by_password
52
+ header 'AUTHORIZATION', nil
53
+ get 'db'
54
+ assert_error_response last_response
55
+
56
+ post 'oauth2/token', :grant_type => 'password', :client_id => @client.identity, :client_secret => @client.secret, :username => @account.email, :password => 'abc123'
57
+ assert last_response.ok?
58
+ body = ActiveSupport::JSON.decode(last_response.body)
59
+ access_token = body['access_token']
60
+ refresh_token = body['refresh_token']
61
+ assert_not_nil(access_token)
62
+ assert_not_nil(refresh_token)
63
+ assert_equal('bearer', body['token_type'])
64
+
65
+ header 'AUTHORIZATION', nil
66
+ get 'db'
67
+ assert_error_response last_response
68
+ assert_not_equal(TestApp::BODY, last_response.body)
69
+
70
+ header 'AUTHORIZATION', "Bearer badtoken"
71
+ get 'db'
72
+ assert_error_response last_response
73
+ assert_not_equal(TestApp::BODY, last_response.body)
74
+
75
+ header 'AUTHORIZATION', "Bearer #{access_token}"
76
+ get 'db'
77
+ assert last_response.ok?
78
+ assert_equal(TestApp::BODY, last_response.body)
79
+
80
+ AccessToken.find_by_token(access_token).expired!
81
+
82
+ header 'AUTHORIZATION', "Bearer #{access_token}"
83
+ get 'db'
84
+ assert_error_response last_response
85
+ assert_not_equal(TestApp::BODY, last_response.body)
86
+
87
+ header 'AUTHORIZATION', nil
88
+ get 'db'
89
+ assert_error_response last_response
90
+ assert_not_equal(TestApp::BODY, last_response.body)
91
+ end
92
+
93
+ def test_oauth2_token_by_refresh_token
94
+ header 'AUTHORIZATION', nil
95
+ get 'db'
96
+ assert_error_response last_response
97
+
98
+ post 'oauth2/token', :grant_type => 'password', :client_id => @client.identity, :client_secret => @client.secret, :username => @account.email, :password => 'abc123'
99
+ assert last_response.ok?
100
+ body = ActiveSupport::JSON.decode(last_response.body)
101
+ access_token = body['access_token']
102
+ refresh_token = body['refresh_token']
103
+ assert_not_nil(access_token)
104
+ assert_not_nil(refresh_token)
105
+ assert_equal('bearer', body['token_type'])
106
+
107
+ post 'oauth2/token', :grant_type => 'refresh_token', :client_id => @client.identity, :client_secret => @client.secret, :refresh_token => 'badtoken'
108
+ assert_error_response last_response
109
+
110
+ post 'oauth2/token', :grant_type => 'refresh_token', :client_id => 'bad identity', :client_secret => @client.secret, :refresh_token => refresh_token
111
+ assert_error_response last_response
112
+
113
+ post 'oauth2/token', :grant_type => 'refresh_token', :client_id => @client.identity, :client_secret => 'badsecret', :refresh_token => refresh_token
114
+ assert_error_response last_response
115
+
116
+ post 'oauth2/token', :grant_type => 'refresh_token', :client_id => @client.identity, :client_secret => @client.secret, :refresh_token => refresh_token
117
+ assert last_response.ok?
118
+ assert last_response.ok?
119
+ body = ActiveSupport::JSON.decode(last_response.body)
120
+ new_access_token = body['access_token']
121
+ assert_not_nil(new_access_token)
122
+ assert_not_equal(access_token, new_access_token)
123
+
124
+ end
125
+ end
metadata ADDED
@@ -0,0 +1,230 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-couchdb-oauth2
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Robin Lu
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-06-15 00:00:00 +08:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ prerelease: false
23
+ name: rack
24
+ type: :runtime
25
+ version_requirements: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ hash: 3
31
+ segments:
32
+ - 0
33
+ version: "0"
34
+ requirement: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ prerelease: false
37
+ name: couchrest
38
+ type: :runtime
39
+ version_requirements: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ requirement: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ prerelease: false
51
+ name: couchrest_model
52
+ type: :runtime
53
+ version_requirements: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 0
61
+ version: "0"
62
+ requirement: *id003
63
+ - !ruby/object:Gem::Dependency
64
+ prerelease: false
65
+ name: activesupport
66
+ type: :runtime
67
+ version_requirements: &id004 !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ hash: 3
73
+ segments:
74
+ - 0
75
+ version: "0"
76
+ requirement: *id004
77
+ - !ruby/object:Gem::Dependency
78
+ prerelease: false
79
+ name: rack-oauth2
80
+ type: :runtime
81
+ version_requirements: &id005 !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ hash: 3
87
+ segments:
88
+ - 0
89
+ version: "0"
90
+ requirement: *id005
91
+ - !ruby/object:Gem::Dependency
92
+ prerelease: false
93
+ name: bcrypt-ruby
94
+ type: :runtime
95
+ version_requirements: &id006 !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ hash: 3
101
+ segments:
102
+ - 0
103
+ version: "0"
104
+ requirement: *id006
105
+ - !ruby/object:Gem::Dependency
106
+ prerelease: false
107
+ name: bundler
108
+ type: :development
109
+ version_requirements: &id007 !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ~>
113
+ - !ruby/object:Gem::Version
114
+ hash: 23
115
+ segments:
116
+ - 1
117
+ - 0
118
+ - 0
119
+ version: 1.0.0
120
+ requirement: *id007
121
+ - !ruby/object:Gem::Dependency
122
+ prerelease: false
123
+ name: jeweler
124
+ type: :development
125
+ version_requirements: &id008 !ruby/object:Gem::Requirement
126
+ none: false
127
+ requirements:
128
+ - - ~>
129
+ - !ruby/object:Gem::Version
130
+ hash: 11
131
+ segments:
132
+ - 1
133
+ - 6
134
+ - 2
135
+ version: 1.6.2
136
+ requirement: *id008
137
+ - !ruby/object:Gem::Dependency
138
+ prerelease: false
139
+ name: rcov
140
+ type: :development
141
+ version_requirements: &id009 !ruby/object:Gem::Requirement
142
+ none: false
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ hash: 3
147
+ segments:
148
+ - 0
149
+ version: "0"
150
+ requirement: *id009
151
+ - !ruby/object:Gem::Dependency
152
+ prerelease: false
153
+ name: rack-test
154
+ type: :development
155
+ version_requirements: &id010 !ruby/object:Gem::Requirement
156
+ none: false
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ hash: 3
161
+ segments:
162
+ - 0
163
+ version: "0"
164
+ requirement: *id010
165
+ description: Rack middleware for OAuth2 Provider Server Based on Couchdb
166
+ email: iamawalrus@gmail.com
167
+ executables: []
168
+
169
+ extensions: []
170
+
171
+ extra_rdoc_files:
172
+ - LICENSE.txt
173
+ - README.rdoc
174
+ files:
175
+ - .document
176
+ - Gemfile
177
+ - Gemfile.lock
178
+ - LICENSE.txt
179
+ - README.rdoc
180
+ - Rakefile
181
+ - VERSION
182
+ - lib/couchdb_oauth2/model/access_token.rb
183
+ - lib/couchdb_oauth2/model/account.rb
184
+ - lib/couchdb_oauth2/model/base.rb
185
+ - lib/couchdb_oauth2/model/client.rb
186
+ - lib/couchdb_oauth2/model/oauth2_token.rb
187
+ - lib/couchdb_oauth2/model/refresh_token.rb
188
+ - lib/couchdb_oauth2/resource/require_bearer_token.rb
189
+ - lib/couchdb_oauth2/resource/require_client.rb
190
+ - lib/couchdb_oauth2/token_endpoint.rb
191
+ - lib/rack-couchdb-oauth2.rb
192
+ - rack-couchdb-oauth2.gemspec
193
+ - test/helper.rb
194
+ - test/test_rack-couchdb-oauth2.rb
195
+ has_rdoc: true
196
+ homepage: http://github.com/robin/rack-couchdb-oauth2
197
+ licenses:
198
+ - MIT
199
+ post_install_message:
200
+ rdoc_options: []
201
+
202
+ require_paths:
203
+ - lib
204
+ required_ruby_version: !ruby/object:Gem::Requirement
205
+ none: false
206
+ requirements:
207
+ - - ">="
208
+ - !ruby/object:Gem::Version
209
+ hash: 3
210
+ segments:
211
+ - 0
212
+ version: "0"
213
+ required_rubygems_version: !ruby/object:Gem::Requirement
214
+ none: false
215
+ requirements:
216
+ - - ">="
217
+ - !ruby/object:Gem::Version
218
+ hash: 3
219
+ segments:
220
+ - 0
221
+ version: "0"
222
+ requirements: []
223
+
224
+ rubyforge_project:
225
+ rubygems_version: 1.4.2
226
+ signing_key:
227
+ specification_version: 3
228
+ summary: Rack middleware for OAuth2 Provider Server Based on Couchdb
229
+ test_files: []
230
+