oauth_provider 0.1.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.
@@ -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