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
+ module Backends
3
+ class DataMapper
4
+ class UserRequest
5
+ include ::DataMapper::Resource
6
+
7
+ property :id, Serial
8
+ property :consumer_id, Integer, :nullable => false
9
+ property :authorized, Boolean, :default => false, :nullable => false
10
+ property :shared_key, String, :unique => true, :nullable => false
11
+ property :secret_key, String, :unique => true, :nullable => false
12
+
13
+ belongs_to :consumer , :class_name => '::OAuthProvider::Backends::DataMapper::Consumer'
14
+
15
+ def token
16
+ OAuthProvider::Token.new(shared_key, secret_key)
17
+ end
18
+
19
+ def to_oauth(backend)
20
+ OAuthProvider::UserRequest.new(backend, consumer.to_oauth(backend), authorized, token)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,47 @@
1
+ module OAuthProvider
2
+ module Backends
3
+ class InMemory < Abstract
4
+ def initialize
5
+ @consumers, @user_requests, @user_accesses = [], [], []
6
+ end
7
+ attr_reader :consumers
8
+
9
+ def create_consumer(consumer)
10
+ raise DuplicateCallback.new(consumer) if @consumers.any? {|c| c.callback == consumer.callback}
11
+ @consumers << consumer
12
+ end
13
+
14
+ def find_consumer(shared_key)
15
+ @consumers.find {|x| x.shared_key == shared_key}
16
+ end
17
+
18
+ def destroy_consumer(consumer)
19
+ @consumers.delete(consumer)
20
+ end
21
+
22
+ def create_user_request(user_request)
23
+ @user_requests << user_request
24
+ end
25
+
26
+ def find_user_request(shared_key)
27
+ @user_requests.find {|x| x.shared_key == shared_key}
28
+ end
29
+
30
+ def save_user_request(user_request)
31
+ @user_requests.find {|x| x == user_request} || raise
32
+ end
33
+
34
+ def destroy_user_request(user_request)
35
+ @user_requests.delete(user_request)
36
+ end
37
+
38
+ def create_user_access(user_access)
39
+ @user_accesses << user_access
40
+ end
41
+
42
+ def find_user_access(shared_key)
43
+ @user_accesses.find {|x| x.shared_key == shared_key}
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,90 @@
1
+ begin
2
+ require 'mysql'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ require 'mysql'
6
+ end
7
+
8
+ module OAuthProvider
9
+ module Backends
10
+ class Mysql < OAuthProvider::Backends::Abstract
11
+ def initialize(host, user, password, db, port=nil)
12
+ @db = ::Mysql::real_connect(host, user, password, db, port)
13
+ @db.reconnect = true
14
+ @db.real_query("SET NAMES 'UTF8'")
15
+ # FIXME: The key column widths are perhaps not right. What is the max width of each?
16
+ @db.real_query("CREATE TABLE IF NOT EXISTS consumers (name CHAR(50), shared_key CHAR(43) PRIMARY KEY, secret_key CHAR(43), callback CHAR(255))")
17
+ @db.real_query("CREATE TABLE IF NOT EXISTS request_tokens (shared_key CHAR(22) PRIMARY KEY, secret_key CHAR(43), authorized INT, consumer_shared_key CHAR(43))")
18
+ @db.real_query("CREATE TABLE IF NOT EXISTS access_tokens (shared_key CHAR(22) PRIMARY KEY, secret_key CHAR(43), request_shared_key CHAR(43), consumer_shared_key CHAR(43))")
19
+ end
20
+
21
+ def clear!
22
+ @db.real_query("DROP TABLE IF EXISTS consumers")
23
+ @db.real_query("DROP TABLE IF EXISTS request_tokens")
24
+ @db.real_query("DROP TABLE IF EXISTS access_tokens")
25
+ end
26
+
27
+ def create_consumer(consumer)
28
+ # XXX: Should come up with a better way to store names than forcing the gem user to hack it in manually.
29
+ # Also, OAuthProvider::DuplicateCallback is a bit silly... multiple consumers with the same callback may be useful (especially since callbacks are optional.
30
+ @db.query("SELECT callback FROM consumers WHERE callback='#{consumer.callback}' LIMIT 1").each do
31
+ raise OAuthProvider::DuplicateCallback.new(consumer)
32
+ end
33
+ @db.real_query("INSERT INTO consumers (name, shared_key, secret_key, callback) " \
34
+ "VALUES ('', '#{consumer.shared_key}', '#{consumer.secret_key}', '#{consumer.callback}')")
35
+ end
36
+
37
+ def find_consumer(shared_key)
38
+ @db.query("SELECT name, callback, shared_key, secret_key FROM consumers WHERE shared_key='#{shared_key}' LIMIT 1").each do |row|
39
+ return OAuthProvider::Consumer.new(self, @provider, row[1], OAuthProvider::Token.new(row[2], row[3]))
40
+ end
41
+ nil
42
+ end
43
+
44
+ def consumers
45
+ rtrn = []
46
+ @db.query("SELECT name, callback, shared_key, secret_key FROM consumers").each do |row|
47
+ rtrn << OAuthProvider::Consumer.new(self, @provider, row[1], OAuthProvider::Token.new(row[2], row[3]))
48
+ end
49
+ rtrn
50
+ end
51
+
52
+ def destroy_consumer(consumer)
53
+ @db.real_query("DELETE FROM consumers WHERE shared_key='#{consumer.shared_key}' AND secret_key='#{consumer.secret_key}'")
54
+ end
55
+
56
+ def create_user_request(token)
57
+ @db.real_query("INSERT INTO request_tokens (shared_key, secret_key, authorized, consumer_shared_key) " \
58
+ "VALUES ('#{token.shared_key}','#{token.secret_key}',#{token.authorized? ? 1 : 0},'#{token.consumer.shared_key}')")
59
+ end
60
+
61
+ def find_user_request(shared_key)
62
+ @db.query("SELECT shared_key, secret_key, authorized, consumer_shared_key FROM request_tokens WHERE shared_key = '#{shared_key}' LIMIT 1").each do |row|
63
+ return OAuthProvider::UserRequest.new(self, self.find_consumer(row[3]), row[2].to_i!=0, OAuthProvider::Token.new(row[0], row[1]))
64
+ end
65
+ raise OAuthProvider::UserRequestNotFound.new(shared_key)
66
+ nil
67
+ end
68
+
69
+ def save_user_request(user_request)
70
+ @db.real_query("UPDATE request_tokens SET authorized=#{user_request.authorized? ? '1' : '0'} WHERE shared_key='#{user_request.shared_key}' AND secret_key='#{user_request.secret_key}'")
71
+ end
72
+
73
+ def destroy_user_request(user_request)
74
+ @db.real_query("DELETE FROM request_tokens WHERE shared_key='#{user_request.shared_key}' AND secret_key='#{user_request.secret_key}'")
75
+ end
76
+
77
+ def create_user_access(token)
78
+ @db.real_query("INSERT INTO access_tokens (shared_key, secret_key, consumer_shared_key, request_shared_key) " \
79
+ "VALUES ('#{token.shared_key}','#{token.secret_key}','#{token.consumer.shared_key}', '#{token.request_shared_key}')")
80
+ end
81
+
82
+ def find_user_access(shared_key)
83
+ @db.query("SELECT shared_key, secret_key, request_shared_key, consumer_shared_key FROM access_tokens WHERE shared_key = '#{shared_key}' LIMIT 1").each do |row|
84
+ return OAuthProvider::UserAccess.new(self, self.find_consumer(row[3]), row[2], OAuthProvider::Token.new(row[0], row[1]))
85
+ end
86
+ nil
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,81 @@
1
+ raise "Sequel backend is incomplete"
2
+
3
+ gem 'sequel'
4
+ require 'sequel'
5
+
6
+ module OAuthProvider
7
+ module Backends
8
+ class Sequel
9
+ def initialize(uri)
10
+ @db = Sequel.connect(uri)
11
+ end
12
+
13
+ def save_consumer(consumer)
14
+ @db.execute("INSERT INTO consumers (name, shared, secret, callback) " \
15
+ "VALUES ('#{consumer.name}', '#{consumer.shared}', '#{consumer.secret}', '#{consumer.callback}')")
16
+ end
17
+
18
+ def fetch_consumer(shared)
19
+ @db.execute("SELECT name, shared, secret, callback FROM consumers WHERE shared = '#{shared}' LIMIT 1") do |row|
20
+ return Consumer.new(self, row[0], row[1], row[2], row[3])
21
+ end
22
+ nil
23
+ end
24
+
25
+ def save_request_token(token)
26
+ @db.execute("INSERT INTO request_tokens (shared, secret, authorized, consumer_shared) " \
27
+ "VALUES ('#{token.shared}','#{token.secret}',#{token.authorized ? 1 : 0},'#{token.consumer_shared}')")
28
+ end
29
+
30
+ def fetch_request_token(shared, consumer_shared)
31
+ consumer_shared = "AND consumer_shared='#{consumer_shared}'" if consumer_shared
32
+ @db.execute("SELECT shared, secret, authorized, consumer_shared FROM request_tokens WHERE shared = '#{shared}' #{consumer_shared} LIMIT 1") do |row|
33
+ consumer = fetch_consumer(row[3])
34
+ return RequestToken.new(consumer, row[2], row[0], row[1])
35
+ end
36
+ nil
37
+ end
38
+
39
+ def authorize_request_token(token)
40
+ @db.execute("UPDATE request_tokens SET authorized=1 WHERE shared='#{token.shared}'")
41
+ end
42
+
43
+ def save_access_token(token)
44
+ @db.execute("INSERT INTO access_tokens (shared, secret, consumer_shared, request_shared) " \
45
+ "VALUES ('#{token.shared}','#{token.secret}','#{token.consumer_shared}', '#{token.request_shared}')")
46
+ end
47
+
48
+ def fetch_access_token(shared, consumer_shared)
49
+ consumer_shared = "AND consumer_shared='#{consumer_shared}'" if consumer_shared
50
+ @db.execute("SELECT shared, secret, request_shared, consumer_shared FROM access_tokens WHERE shared = '#{shared}' #{consumer_shared} LIMIT 1") do |row|
51
+ consumer = fetch_consumer(row[3])
52
+ return AccessToken.new(consumer, row[2], row[0], row[1])
53
+ end
54
+ nil
55
+ end
56
+
57
+ private
58
+
59
+ def create_tables
60
+ @db.execute("CREATE TABLE IF NOT EXISTS consumers (name CHAR(50), shared CHAR(32) PRIMARY KEY, secret CHAR(32), callback CHAR(255))")
61
+ @db.execute("CREATE TABLE IF NOT EXISTS request_tokens (shared CHAR(16) PRIMARY KEY, secret CHAR(32), authorized INT, consumer_shared CHAR(32))")
62
+ @db.execute("CREATE TABLE IF NOT EXISTS access_tokens (shared CHAR(16) PRIMARY KEY, secret CHAR(32), request_shared CHAR(32), consumer_shared CHAR(32))")
63
+ unless @db.table_exists?(:consumers)
64
+ @db.create_table :consumers do
65
+ primary_key :id
66
+ text :name, :shared, :secret, :callback, :unique => true, :null => false
67
+ index :shared
68
+ end
69
+ end
70
+
71
+ unless @db.table_exists?(:request_tokens)
72
+ primary_key :id
73
+ text :shared_key, :secret_key, :unique => true, :null => false
74
+ foreign_key :consumer_id, :consumers
75
+ index :shared_key, :consumer_id
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+
@@ -0,0 +1,75 @@
1
+ require 'sqlite3'
2
+
3
+ module OAuthProvider
4
+ module Backends
5
+ class Sqlite3 < Abstract
6
+ def initialize(path)
7
+ @db = SQLite3::Database.new(path)
8
+ @db.execute("CREATE TABLE IF NOT EXISTS consumers (name CHAR(50), shared_key CHAR(43) PRIMARY KEY, secret_key CHAR(43), callback CHAR(255))")
9
+ @db.execute("CREATE TABLE IF NOT EXISTS request_tokens (shared_key CHAR(22) PRIMARY KEY, secret_key CHAR(43), authorized INT, consumer_shared_key CHAR(43))")
10
+ @db.execute("CREATE TABLE IF NOT EXISTS access_tokens (shared_key CHAR(22) PRIMARY KEY, secret_key CHAR(43), request_shared_key CHAR(43), consumer_shared_key CHAR(43))")
11
+ end
12
+
13
+ def create_consumer(consumer)
14
+ # XXX: Should come up with a better way to store names than forcing the gem user to hack it in manually.
15
+ # Also, OAuthProvider::DuplicateCallback is a bit silly... multiple consumers with the same callback may be useful (especially since callbacks are optional.
16
+ @db.execute("SELECT callback FROM consumers WHERE callback='#{consumer.callback}'") do
17
+ raise OAuthProvider::DuplicateCallback.new(consumer)
18
+ end
19
+ @db.execute("INSERT INTO consumers (name, shared_key, secret_key, callback) " \
20
+ "VALUES ('', '#{consumer.shared_key}', '#{consumer.secret_key}', '#{consumer.callback}')")
21
+ end
22
+
23
+ def find_consumer(shared_key)
24
+ @db.execute("SELECT name, callback, shared_key, secret_key FROM consumers WHERE shared_key='#{shared_key}' LIMIT 1") do |row|
25
+ return OAuthProvider::Consumer.new(self, @provider, row[1], OAuthProvider::Token.new(row[2], row[3]))
26
+ end
27
+ nil
28
+ end
29
+
30
+ def consumers
31
+ rtrn = []
32
+ @db.execute("SELECT name, callback, shared_key, secret_key FROM consumers") do |row|
33
+ rtrn << OAuthProvider::Consumer.new(self, @provider, row[1], OAuthProvider::Token.new(row[2], row[3]))
34
+ end
35
+ rtrn
36
+ end
37
+
38
+ def destroy_consumer(consumer)
39
+ @db.execute("DELETE FROM consumers WHERE shared_key='#{consumer.shared_key}' AND secret_key='#{consumer.secret_key}'")
40
+ end
41
+
42
+ def create_user_request(token)
43
+ @db.execute("INSERT INTO request_tokens (shared_key, secret_key, authorized, consumer_shared_key) " \
44
+ "VALUES ('#{token.shared_key}','#{token.secret_key}',#{token.authorized? ? 1 : 0},'#{token.consumer.shared_key}')")
45
+ end
46
+
47
+ def find_user_request(shared_key)
48
+ @db.execute("SELECT shared_key, secret_key, authorized, consumer_shared_key FROM request_tokens WHERE shared_key = '#{shared_key}' LIMIT 1") do |row|
49
+ return OAuthProvider::UserRequest.new(self, self.find_consumer(row[3]), row[2].to_i!=0, OAuthProvider::Token.new(row[0], row[1]))
50
+ end
51
+ nil
52
+ end
53
+
54
+ def save_user_request(user_request)
55
+ @db.execute("UPDATE request_tokens SET authorized=#{user_request.authorized? ? '1' : '0'} WHERE shared_key='#{user_request.shared_key}' AND secret_key='#{user_request.secret_key}'")
56
+ end
57
+
58
+ def destroy_user_request(user_request)
59
+ @db.execute("DELETE FROM request_tokens WHERE shared_key='#{user_request.shared_key}' AND secret_key='#{user_request.secret_key}'")
60
+ end
61
+
62
+ def create_user_access(token)
63
+ @db.execute("INSERT INTO access_tokens (shared_key, secret_key, consumer_shared_key, request_shared_key) " \
64
+ "VALUES ('#{token.shared_key}','#{token.secret_key}','#{token.consumer.shared_key}', '#{token.request_shared_key}')")
65
+ end
66
+
67
+ def find_user_access(shared_key)
68
+ @db.execute("SELECT shared_key, secret_key, request_shared_key, consumer_shared_key FROM access_tokens WHERE shared_key = '#{shared_key}' LIMIT 1") do |row|
69
+ return OAuthProvider::UserAccess.new(self, find_consumer(row[3]), row[2], OAuthProvider::Token.new(row[0], row[1]))
70
+ end
71
+ nil
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,40 @@
1
+ module OAuthProvider
2
+ class Consumer
3
+ def initialize(backend, provider, callback, token)
4
+ @backend, @provider, @callback, @token = backend, provider, callback, token
5
+ end
6
+ attr_reader :provider, :callback, :token
7
+
8
+ def find_user_request(shared_key)
9
+ @backend.find_user_request(shared_key) ||
10
+ raise(UserRequestNotFound.new(shared_key))
11
+ end
12
+
13
+ def destroy_user_request(shared_key)
14
+ @backend.destroy_user_request(shared_key) ||
15
+ raise(UserRequestNotFound.new(shared_key))
16
+ end
17
+
18
+ def find_user_access(shared_key)
19
+ @backend.find_user_access(shared_key) ||
20
+ raise(UserAccessNotFound.new(shared_key))
21
+ end
22
+
23
+ def issue_request(authorized = false, token = nil)
24
+ @backend.add_user_request(self, authorized, token || Token.generate)
25
+ end
26
+
27
+ def shared_key
28
+ @token.shared_key
29
+ end
30
+
31
+ def secret_key
32
+ @token.secret_key
33
+ end
34
+
35
+ def ==(consumer)
36
+ return false unless consumer.is_a?(Consumer)
37
+ [callback, token] == [consumer.callback, consumer.token]
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,8 @@
1
+ # Extend OAuth::Token to be able to create the correct query string form
2
+ module OAuth
3
+ class Token
4
+ def to_query
5
+ "oauth_token=#{escape(token)}&oauth_token_secret=#{escape(secret)}"
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,73 @@
1
+ module OAuthProvider
2
+ class Provider
3
+ def initialize(backend)
4
+ @backend = backend
5
+ end
6
+ attr_reader :backend
7
+
8
+ # Request verification
9
+
10
+ def issue_request(request)
11
+ verify(request, :consumer).issue_request
12
+ end
13
+
14
+ def upgrade_request(request)
15
+ verify(request, :request).upgrade
16
+ end
17
+
18
+ def confirm_access(request)
19
+ verify(request, :access)
20
+ end
21
+
22
+ # Consumer
23
+
24
+ def consumers
25
+ @backend.consumers
26
+ end
27
+
28
+ def add_consumer(callback, token = nil)
29
+ @backend.add_consumer(self, callback, token || Token.generate)
30
+ end
31
+
32
+ def find_consumer(shared_key)
33
+ @backend.find_consumer(shared_key) ||
34
+ raise(ConsumerNotFound.new(shared_key))
35
+ end
36
+
37
+ def save_consumer(consumer)
38
+ @backend.save_consumer(consumer)
39
+ end
40
+
41
+ def destroy_consumer(consumer)
42
+ @backend.destroy_consumer(consumer)
43
+ end
44
+
45
+ private
46
+ def verify(request, type, &block)
47
+ result = nil
48
+
49
+ signature = OAuth::Signature.build(request) do |shared_key,consumer_shared_key|
50
+ consumer = find_consumer(consumer_shared_key)
51
+ case type
52
+ when :request
53
+ result = consumer.find_user_request(shared_key)
54
+ [result.secret_key, consumer.secret_key]
55
+ when :access
56
+ result = consumer.find_user_access(shared_key)
57
+ [result.secret_key, consumer.secret_key]
58
+ when :consumer
59
+ result = consumer
60
+ [nil, consumer.secret_key]
61
+ else
62
+ raise ArgumentError, "Type should be one of :request, :access or :consumer"
63
+ end
64
+ end
65
+
66
+ if signature.verify
67
+ result
68
+ else
69
+ raise VerficationFailed, "Signature verification failed: #{signature.signature} != #{signature.request.signature}"
70
+ end
71
+ end
72
+ end
73
+ end