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
+ 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