oauth_provider 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,25 @@
1
+ module OAuthProvider
2
+ class Token
3
+ def self.generate
4
+ new(generate_key(16), generate_key)
5
+ end
6
+
7
+ def self.generate_key(size = 32)
8
+ Base64.encode64(OpenSSL::Random.random_bytes(size)).gsub(/\W/,'')
9
+ end
10
+
11
+ def initialize(shared_key, secret_key)
12
+ @shared_key, @secret_key = shared_key, secret_key
13
+ end
14
+ attr_reader :shared_key, :secret_key
15
+
16
+ def query_string
17
+ OAuth::Token.new(shared_key, secret_key).to_query
18
+ end
19
+
20
+ def ==(token)
21
+ return false unless token.is_a?(Token)
22
+ [shared_key, secret_key].eql?([token.shared_key, token.secret_key])
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module OAuthProvider
2
+ class UserAccess
3
+ def initialize(backend, consumer, request_shared_key, token)
4
+ @backend, @consumer, @request_shared_key, @token = backend, consumer, request_shared_key, token
5
+ end
6
+ attr_reader :consumer, :request_shared_key, :token
7
+
8
+ def query_string
9
+ @token.query_string
10
+ end
11
+
12
+ def shared_key
13
+ @token.shared_key
14
+ end
15
+
16
+ def secret_key
17
+ @token.secret_key
18
+ end
19
+
20
+ def ==(user_access)
21
+ return false unless user_access.is_a?(UserAccess)
22
+ [consumer, request_shared_key, token] == [user_access.consumer, user_access.request_shared_key, user_access.token]
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,46 @@
1
+ module OAuthProvider
2
+ class UserRequest
3
+ def initialize(backend, consumer, authorized, token)
4
+ @backend, @consumer, @authorized, @token = backend, consumer, authorized, token
5
+ end
6
+ attr_reader :consumer, :token
7
+
8
+ def authorized?
9
+ @authorized
10
+ end
11
+
12
+ def authorize
13
+ @authorized = true
14
+ @backend.save_user_request(self)
15
+ end
16
+
17
+ def upgrade(token = nil)
18
+ if authorized?
19
+ @backend.add_user_access(self, token || Token.generate)
20
+ else
21
+ raise UserRequestNotAuthorized.new(self)
22
+ end
23
+ end
24
+
25
+ def callback
26
+ @consumer.callback
27
+ end
28
+
29
+ def query_string
30
+ @token.query_string
31
+ end
32
+
33
+ def shared_key
34
+ @token.shared_key
35
+ end
36
+
37
+ def secret_key
38
+ @token.secret_key
39
+ end
40
+
41
+ def ==(user_request)
42
+ return false unless user_request.is_a?(UserRequest)
43
+ [consumer, authorized?, token] == [user_request.consumer, user_request.authorized?, user_request.token]
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,3 @@
1
+ module OAuthProvider
2
+ VERSION = "0.0.9"
3
+ end
@@ -0,0 +1,3 @@
1
+ describe "A Backend" do
2
+
3
+ end
@@ -0,0 +1,11 @@
1
+ describe "The Abstract backend" do
2
+ %w( create_consumer find_consumer save_consumer destroy_consumer
3
+ create_user_request find_user_request save_user_request destroy_user_request
4
+ create_user_access find_user_access destroy_user_access ).each do |method_name|
5
+ it "does not implement the ##{method_name} method" do
6
+ backend = OAuthProvider::Backends::Abstract.new
7
+ lambda { backend.send(method_name, :arg) }.
8
+ should raise_error(OAuthProvider::NotImplemented, /#{method_name}/)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,65 @@
1
+ describe "A Consumer" do
2
+ describe "issuing a user request" do
3
+ it "saves the request" do
4
+ provider = create_provider
5
+ consumer = provider.add_consumer("http://foo.com")
6
+ user_request = consumer.issue_request
7
+ consumer.find_user_request(user_request.shared_key).should == user_request
8
+ end
9
+
10
+ describe "specifying that it is already authorized" do
11
+ it "is authorized" do
12
+ provider = create_provider
13
+ consumer = provider.add_consumer("http://foo.com")
14
+ user_request = consumer.issue_request(true)
15
+ consumer.find_user_request(user_request.shared_key).should be_authorized
16
+ end
17
+ end
18
+
19
+ describe "specifying that it is not authorized" do
20
+ it "is not authorized" do
21
+ provider = create_provider
22
+ consumer = provider.add_consumer("http://foo.com")
23
+ user_request = consumer.issue_request(false)
24
+ consumer.find_user_request(user_request.shared_key).should_not be_authorized
25
+ end
26
+ end
27
+
28
+ describe "with a custom token" do
29
+ it "uses the provided token" do
30
+ provider = create_provider
31
+ consumer = provider.add_consumer("http://foo.com")
32
+ user_request = consumer.issue_request(false, OAuthProvider::Token.new("shared key", "secret key"))
33
+ consumer.find_user_request("shared key").should == user_request
34
+ user_request.secret_key.should == "secret key"
35
+ end
36
+ end
37
+ end
38
+
39
+ it "can destroy a user request" do
40
+ provider = create_provider
41
+ consumer = provider.add_consumer("http://foo.com")
42
+ user_request = consumer.issue_request
43
+ consumer.find_user_request(user_request.shared_key).should == user_request
44
+ consumer.destroy_user_request(user_request).should be_true
45
+ lambda {consumer.find_user_request(user_request)}.should raise_error(OAuthProvider::UserRequestNotFound)
46
+ end
47
+
48
+ it "finds the same user access for a shared key" do
49
+ provider = create_provider
50
+ consumer = provider.add_consumer("http://foo.com")
51
+ user_request = consumer.issue_request
52
+ user_request.authorize
53
+ user_access = user_request.upgrade
54
+ consumer.find_user_access(user_access.shared_key).should == user_access
55
+ end
56
+
57
+ it "is equal to another consumer when both the callback and token match" do
58
+ provider = create_provider
59
+ token1 = OAuthProvider::Token.new("123", "456")
60
+ token2 = OAuthProvider::Token.new("123", "456")
61
+ consumer1 = OAuthProvider::Consumer.new(nil, provider, "callback", token1)
62
+ consumer2 = OAuthProvider::Consumer.new(nil, provider, "callback", token2)
63
+ consumer1.should == consumer2
64
+ end
65
+ end
@@ -0,0 +1,85 @@
1
+ require 'fileutils'
2
+
3
+ module OAuthBackendHelper
4
+ module InMemory
5
+ def self.create
6
+ OAuthProvider.create(:in_memory)
7
+ end
8
+
9
+ def self.setup; end
10
+ def self.reset; end
11
+ end
12
+
13
+ module DataMapper
14
+ def self.create
15
+ OAuthProvider.create(:data_mapper)
16
+ end
17
+
18
+ def self.setup
19
+ require 'dm-core'
20
+ ::DataMapper.setup(:default, "sqlite3:///tmp/oauth_provider_test.sqlite3")
21
+ end
22
+
23
+ def self.reset
24
+ create
25
+ ::DataMapper.auto_migrate!
26
+ end
27
+ end
28
+
29
+ module Sqlite3
30
+ PATH = "/tmp/oauth_provider_sqlite3_test.sqlite3" unless defined?(PATH)
31
+
32
+ def self.create
33
+ OAuthProvider.create(:sqlite3, PATH)
34
+ end
35
+
36
+ def self.setup; end
37
+
38
+ def self.reset
39
+ FileUtils.rm(PATH) rescue nil
40
+ end
41
+ end
42
+
43
+ module Mysql
44
+ def self.create
45
+ host = ENV['MYSQL_HOST'] || "localhost"
46
+ user = ENV['MYSQL_USER'] || "root"
47
+ password = ENV['MYSQL_PASSWORD'] || ""
48
+ db = ENV['MYSQL_DB'] || "oauth_provider_test"
49
+ port = ENV['MYSQL_PORT'] || 3306
50
+ OAuthProvider.create(:mysql, host, user, password, db, port)
51
+ end
52
+
53
+ def self.setup; end
54
+
55
+ def self.reset
56
+ self.create.backend.clear!
57
+ end
58
+ end
59
+
60
+
61
+ def self.setup
62
+ backend_module.setup
63
+ end
64
+
65
+ def self.reset
66
+ backend_module.reset
67
+ end
68
+
69
+ def self.provider
70
+ backend_module.create
71
+ end
72
+
73
+ def self.backend_module
74
+ klass_name = backend_name.to_s.split('_').map {|e| e.capitalize}.join
75
+ unless const_defined?(klass_name)
76
+ $stderr.puts "There is no backend for #{backend_name.inspect}"
77
+ exit!
78
+ end
79
+ const_get(klass_name)
80
+ end
81
+
82
+ def self.backend_name
83
+ (ENV["BACKEND"] || "in_memory").to_sym
84
+ end
85
+ end
@@ -0,0 +1,107 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "A Provider" do
4
+ describe "adding a consumer" do
5
+ it "saves the consumer" do
6
+ provider = create_provider
7
+ consumer = provider.add_consumer("http://testconsumer.example.org/")
8
+ provider.find_consumer(consumer.shared_key).should == consumer
9
+ end
10
+
11
+ it "disallows a consumer with the same callback" do
12
+ provider = create_provider
13
+ provider.add_consumer("http://testconsumer.example.org/")
14
+ lambda { provider.add_consumer("http://testconsumer.example.org/") }.
15
+ should raise_error(OAuthProvider::DuplicateCallback)
16
+ end
17
+
18
+ describe "with a custom token" do
19
+ it "uses the token" do
20
+ provider = create_provider
21
+ consumer = provider.add_consumer("http://testconsumer.example.org/", OAuthProvider::Token.new("shared key", "secret key"))
22
+ provider.find_consumer("shared key").should == consumer
23
+ consumer.secret_key.should == "secret key"
24
+ end
25
+ end
26
+ end
27
+
28
+ describe "all the consumers" do
29
+ it "returns the complete list" do
30
+ provider = create_provider
31
+ one = provider.add_consumer("http://one.com/")
32
+ two = provider.add_consumer("http://two.com/")
33
+ provider.consumers.should == [one, two]
34
+ end
35
+ end
36
+
37
+ describe "deleting a consumer" do
38
+ it "removes the consumer from the backend" do
39
+ provider = create_provider
40
+ one = provider.add_consumer("http://one.com/")
41
+ provider.destroy_consumer(one)
42
+ provider.consumers.should be_empty
43
+ end
44
+ end
45
+
46
+ describe "finding a consumer" do
47
+ it "removes the consumer from the backend" do
48
+ provider = create_provider
49
+ one = provider.add_consumer("http://one.com/")
50
+ provider.destroy_consumer(one)
51
+ provider.consumers.should be_empty
52
+ end
53
+ end
54
+
55
+ describe "with a consumer" do
56
+ before(:each) do
57
+ @provider = create_provider
58
+ @consumer = @provider.add_consumer("http://testconsumer.example.org/")
59
+ @client = OAuthClient.new(@consumer)
60
+ end
61
+
62
+ it "issues a user request" do
63
+ user_request = @provider.issue_request(@client.request)
64
+ lambda { @provider.confirm_access(@client.request(user_request)) }.
65
+ should raise_error(OAuthProvider::UserAccessNotFound)
66
+ @consumer.find_user_request(user_request.shared_key).should_not be_nil
67
+ end
68
+
69
+ describe "with a user request" do
70
+ before(:each) do
71
+ @user_request = @provider.issue_request(@client.request)
72
+ end
73
+
74
+ it "authorizes the request" do
75
+ @user_request.authorize
76
+ @user_request.should be_authorized
77
+ end
78
+
79
+ describe "which has been authorized" do
80
+ before(:each) do
81
+ @user_request.authorize
82
+ end
83
+
84
+ it "upgrades the request" do
85
+ request = @client.request(@user_request)
86
+ user_access = @provider.upgrade_request(request)
87
+ lambda { @provider.confirm_access(@client.request(user_access)) }.
88
+ should_not raise_error
89
+ end
90
+
91
+ describe "converted to user access" do
92
+ before(:each) do
93
+ request = @client.request(@user_request)
94
+ @access_token = @provider.upgrade_request(request)
95
+ end
96
+
97
+ it "validates the token" do
98
+ request = @client.request(@access_token)
99
+ @provider.confirm_access(request)
100
+ lambda { @provider.confirm_access(request) }.
101
+ should_not raise_error
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,93 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'rack'
4
+ require 'pp'
5
+ require 'oauth/helper'
6
+
7
+ require File.dirname(__FILE__) + '/../lib/oauth_provider'
8
+ require File.dirname(__FILE__) + '/helpers/backend_helper'
9
+
10
+ OAuthBackendHelper.setup
11
+
12
+ module OAuthProviderHelper
13
+ def create_provider
14
+ OAuthBackendHelper.provider
15
+ end
16
+ end
17
+
18
+ Spec::Runner.configure do |config|
19
+ config.include(OAuthProviderHelper)
20
+
21
+ config.before(:each) do
22
+ OAuthBackendHelper.reset
23
+ end
24
+ end
25
+
26
+ require 'oauth/request_proxy/base'
27
+
28
+ module OAuth
29
+ module RequestProxy
30
+ class MockRequest < OAuth::RequestProxy::Base
31
+ proxies Hash
32
+
33
+ def parameters
34
+ @request["parameters"]
35
+ end
36
+
37
+ def method
38
+ @request["method"]
39
+ end
40
+
41
+ def uri
42
+ @request["uri"]
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ class OAuthClient
49
+ def initialize(consumer)
50
+ @consumer = OAuth::Consumer.new(consumer.shared_key, consumer.secret_key)
51
+ end
52
+ attr_reader :consumer
53
+
54
+ def request(token = nil)
55
+ Request.new(@consumer, Time.now.to_i, token).signed_request
56
+ end
57
+
58
+ class Request
59
+ include OAuth::Helper
60
+
61
+ def initialize(consumer, timestamp, token)
62
+ @consumer, @timestamp, @nonce, @token = consumer, timestamp, generate_key, token
63
+ end
64
+
65
+ def signed_request
66
+ r = request
67
+ r["parameters"]["oauth_signature"] = signature
68
+ r
69
+ end
70
+
71
+ def signature
72
+ OAuth::Signature.sign(request) do |token|
73
+ [@token && @token.secret_key, @consumer.secret]
74
+ end
75
+ end
76
+
77
+ def request
78
+ {"parameters" => query_hash,
79
+ "method" => "GET",
80
+ "uri" => "http://example.org/"}
81
+ end
82
+
83
+ def query_hash
84
+ h = {"oauth_nonce" => @nonce,
85
+ "oauth_timestamp" => @timestamp,
86
+ "oauth_signature_method" => "HMAC-SHA1",
87
+ "oauth_consumer_key" => @consumer.key,
88
+ "oauth_version" => "1.0"}
89
+ h["oauth_token"] = @token.shared_key if @token
90
+ h
91
+ end
92
+ end
93
+ end