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.
data/README ADDED
@@ -0,0 +1,151 @@
1
+ --------------------------------------------------------------------------------
2
+ - OAuth Provider library in Ruby
3
+ --------------------------------------------------------------------------------
4
+
5
+ --------------------------------------------------------------------------------
6
+ - 1) Getting the library setup
7
+ - 2) Creating a provider
8
+ - 3) Adding a consumer
9
+ - 4) Issuing a request token
10
+ - 5) Authorizing a request token
11
+ - 6) Upgrading a request token to an access token
12
+ - 7) Confirming access for an access token
13
+ --------------------------------------------------------------------------------
14
+
15
+ --------------------------------------------------------------------------------
16
+ - 1) Getting the library setup
17
+ --------------------------------------------------------------------------------
18
+
19
+ You can currently only download the source and build a gem.
20
+ It will be put on rubyforge once it is more feature-some.
21
+
22
+ # git clone git://github.com/halorgium/oauth_provider.git
23
+ # rake package
24
+
25
+ --------------------------------------------------------------------------------
26
+ - 2) Getting the library setup
27
+ --------------------------------------------------------------------------------
28
+
29
+ Create a provider to allow you to interact issue request tokens etc.
30
+ There are several backends to allow you to use this for real and in testing.
31
+
32
+ The in-memory backend is best for testing, it allows you to not have the
33
+ overhead of a database.
34
+
35
+ # provider = OAuthProvider.create(:in_memory)
36
+
37
+ The DataMapper backend is currently the only real backend, you can provide a
38
+ repository which will allow you to use a different database connection.
39
+
40
+ # provider = OAuthProvider.create(:data_mapper, :some_oauth_repository)
41
+
42
+ --------------------------------------------------------------------------------
43
+ - 3) Adding a consumer
44
+ --------------------------------------------------------------------------------
45
+
46
+ To add a consumer to the provider, you need to provide a callback URL.
47
+
48
+ # consumer = provider.add_consumer("http://myconsumer.com/token")
49
+
50
+ You should store the consumer shared key in your database so you can associate
51
+ your users with the tokens they own.
52
+
53
+ # Consumer.create("My Consumer", consumer.shared_key)
54
+
55
+ --------------------------------------------------------------------------------
56
+ - 4) Issuing a request token
57
+ --------------------------------------------------------------------------------
58
+
59
+ Now you can issue a request token, this will save the token for later access.
60
+ You need to pass in the raw request object which your web framework uses and
61
+ require the correct request-proxy.
62
+
63
+ Rails (ActionController):
64
+ # require 'oauth/request_proxy/action_controller_request'
65
+ XMPP4R:
66
+ # require 'oauth/request_proxy/jabber_request'
67
+ Net::HTTP:
68
+ # require 'oauth/request_proxy/net_http'
69
+ Sinatra/Merb (Rack):
70
+ # require 'oauth/request_proxy/rack_request'
71
+
72
+ Once that file is required, you can ask the provider to issue a token.
73
+
74
+ # user_request = provider.issue_request(request)
75
+
76
+ You should save this token in your database to connect this token with a
77
+ particular user.
78
+
79
+ # current_user.tokens.create(:consumer_shared_key => user_request.consumer.shared_key,
80
+ # :shared_key => user_request.shared_key)
81
+
82
+ This object allows you to access the query_string which should be returned
83
+ to the consumer.
84
+ This is the form: oauth_token=ABCDE&oauth_token_secret=SECRET123
85
+
86
+ # user_request.query_string
87
+
88
+ Now it is up to the consumer to redirect the user to your authorization
89
+ screen. To locate the token which corresponds with the shared key (usually
90
+ the 'oauth_token' parameter in the request) you need to
91
+
92
+ --------------------------------------------------------------------------------
93
+ - 5) Authorizing a request token
94
+ --------------------------------------------------------------------------------
95
+
96
+ Once you have determined that the user wishes to authorize the request. You
97
+ should display the consumer information to the user.
98
+
99
+ An example ERB view might be:
100
+
101
+ # <p>You are about to authorize <%= token.consumer.name %> to access your account %></p>
102
+ # <p>Do you want this to happen?</p>
103
+ # <p><a href="/authorize?oauth_token=<%= token.shared_key %>Authorize it</a>
104
+
105
+ At this point, you can also store any access control information to allow this
106
+ consumer to perhaps only have read-access to the user's information.
107
+
108
+ Then in the 'authorize' action you would tell the provider to authorize this
109
+ request token and redirect back to the consumer callback URL.
110
+
111
+ # user_request.authorize
112
+ # redirect_to user_request.callback
113
+
114
+ --------------------------------------------------------------------------------
115
+ - 6) Upgrading a request token to an access token
116
+ --------------------------------------------------------------------------------
117
+
118
+ Now that the request token is authorized by the user, the consumer can upgrade
119
+ this token to an access token.
120
+
121
+ # user_access = provider.upgrade_request(request)
122
+
123
+ If the request token is not yet authorized, an exception will be raised. The
124
+ exception class is 'OAuthProvider::UserRequestNotAuthorized'.
125
+
126
+ If the request token is authorized, the request token will be destroyed and
127
+ a access token will be generated and returned.
128
+
129
+ Now you can save this into your database.
130
+
131
+ # token = current_user.tokens.find_by_shared_key(user_access.request_shared_key)
132
+ # token.update_attributes(:access => true, :shared_key => user_access.shared_key)
133
+
134
+ And return the query string back to the consumer
135
+
136
+ # user_access.query_string
137
+
138
+ --------------------------------------------------------------------------------
139
+ - 7) Confirming access for an access token
140
+ --------------------------------------------------------------------------------
141
+
142
+ At this point, the consumer should have a valid access token and can make API
143
+ requests. You can ask the provider to confirm that the access token is valid.
144
+
145
+ # user_access = provider.confirm_access(request)
146
+
147
+ Now you can find the user token which corresponds to the shared_key.
148
+
149
+ # token = current_user.tokens.first(:access => true, :shared_key => user_access.shared_key)
150
+
151
+ You are now ready to respond to the API request as needed!
@@ -0,0 +1,52 @@
1
+ require 'oauth'
2
+ require 'oauth/server'
3
+ require 'oauth/signature'
4
+
5
+ module OAuthProvider
6
+ class Error < StandardError; end
7
+ class NotImplemented < Error; end
8
+ class ConsumerNotFound < Error
9
+ def initialize(shared_key)
10
+ super("No Consumer with shared key: #{shared_key.inspect}")
11
+ end
12
+ end
13
+ class UserRequestNotFound < Error
14
+ def initialize(shared_key)
15
+ super("No User Request with shared key: #{shared_key.inspect}")
16
+ end
17
+ end
18
+ class UserAccessNotFound < Error
19
+ def initialize(shared_key)
20
+ super("No User Access with shared key: #{shared_key.inspect}")
21
+ end
22
+ end
23
+ class UserRequestNotAuthorized < Error
24
+ def initialize(user_request)
25
+ super("The User Request is not yet authorized by the User: #{user_request.shared_key.inspect}")
26
+ end
27
+ end
28
+ class VerficationFailed < Error; end
29
+ class IncompleteToken < Error; end
30
+
31
+ class DuplicateCallback < Error
32
+ def initialize(consumer)
33
+ super("The callback #{consumer.callback.inspect} is already used by another consumer")
34
+ end
35
+ end
36
+
37
+ def self.create(backend_type, *args)
38
+ Backends.for(backend_type, *args).provider
39
+ end
40
+ end
41
+
42
+ $:.unshift File.dirname(__FILE__)
43
+
44
+ require 'oauth_provider/fixes'
45
+ require 'oauth_provider/token'
46
+ require 'oauth_provider/provider'
47
+ require 'oauth_provider/consumer'
48
+ require 'oauth_provider/user_request'
49
+ require 'oauth_provider/user_access'
50
+
51
+ require 'oauth_provider/backends'
52
+ require 'oauth_provider/backends/abstract'
@@ -0,0 +1,9 @@
1
+ module OAuthProvider
2
+ module Backends
3
+ def self.for(type, *args)
4
+ require "oauth_provider/backends/#{type}"
5
+ klass_name = type.to_s.split('_').map {|e| e.capitalize}.join
6
+ const_get(klass_name).new(*args)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,75 @@
1
+ module OAuthProvider
2
+ module Backends
3
+ class Abstract
4
+ def provider
5
+ @provider ||= Provider.new(self)
6
+ end
7
+
8
+ def add_consumer(provider, callback, token)
9
+ consumer = Consumer.new(self, provider, callback, token)
10
+ create_consumer(consumer)
11
+ consumer
12
+ end
13
+
14
+ def create_consumer(consumer)
15
+ raise NotImplemented, "Implement #create_consumer in #{self.class}"
16
+ end
17
+ protected :create_consumer
18
+
19
+ def find_consumer(shared_key)
20
+ raise NotImplemented, "Implement #find_consumer in #{self.class}"
21
+ end
22
+
23
+ def save_consumer(shared_key)
24
+ raise NotImplemented, "Implement #save_consumer in #{self.class}"
25
+ end
26
+
27
+ def destroy_consumer(shared_key)
28
+ raise NotImplemented, "Implement #destroy_consumer in #{self.class}"
29
+ end
30
+
31
+ def add_user_request(consumer, authorized, token)
32
+ user_request = UserRequest.new(self, consumer, authorized, token)
33
+ create_user_request(user_request)
34
+ user_request
35
+ end
36
+
37
+ def create_user_request(user_request)
38
+ raise NotImplemented, "Implement #create_user_request in #{self.class}"
39
+ end
40
+ protected :create_user_request
41
+
42
+ def find_user_request(shared_key)
43
+ raise NotImplemented, "Implement #find_user_request in #{self.class}"
44
+ end
45
+
46
+ def save_user_request(user_request)
47
+ raise NotImplemented, "Implement #save_user_request in #{self.class}"
48
+ end
49
+
50
+ def destroy_user_request(user_request)
51
+ raise NotImplemented, "Implement #destroy_user_request in #{self.class}"
52
+ end
53
+
54
+ def add_user_access(user_request, token)
55
+ user_access = UserAccess.new(self, user_request.consumer, user_request.shared_key, token)
56
+ create_user_access(user_access)
57
+ destroy_user_request(user_request)
58
+ user_access
59
+ end
60
+
61
+ def create_user_access(user_access)
62
+ raise NotImplemented, "Implement #create_user_access in #{self.class}"
63
+ end
64
+ protected :create_user_access
65
+
66
+ def find_user_access(shared_key)
67
+ raise NotImplemented, "Implement #find_user_access in #{self.class}"
68
+ end
69
+
70
+ def destroy_user_access(user_access)
71
+ raise NotImplemented, "Implement #destroy_user_access in #{self.class}"
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,115 @@
1
+ require 'dm-core'
2
+ require 'dm-validations'
3
+
4
+ module OAuthProvider
5
+ module Backends
6
+ class DataMapper < Abstract
7
+ def initialize(repository = :default)
8
+ @repository = repository
9
+ end
10
+
11
+ def consumers
12
+ with_repository do
13
+ Consumer.all.map do |c|
14
+ c.to_oauth(self)
15
+ end
16
+ end
17
+ end
18
+
19
+ def create_consumer(consumer)
20
+ with_repository do
21
+ raise DuplicateCallback.new(consumer) if Consumer.first(:callback => consumer.callback)
22
+ model = Consumer.new(:callback => consumer.callback,
23
+ :shared_key => consumer.shared_key,
24
+ :secret_key => consumer.secret_key)
25
+ model.save || raise("Failed to create Consumer: #{model.inspect}, #{model.errors.inspect}")
26
+ end
27
+ end
28
+
29
+ def find_consumer(shared_key)
30
+ consumer = consumer_for(shared_key)
31
+ consumer && consumer.to_oauth(self)
32
+ end
33
+
34
+ def destroy_consumer(consumer)
35
+ consumer = consumer_for(consumer.shared_key)
36
+ consumer && consumer.destroy
37
+ end
38
+
39
+ def create_user_request(user_request)
40
+ with_repository do
41
+ if consumer = consumer_for(user_request.consumer.shared_key)
42
+ consumer.user_requests.create(:shared_key => user_request.shared_key,
43
+ :secret_key => user_request.secret_key,
44
+ :authorized => user_request.authorized?)
45
+ end
46
+ end
47
+ end
48
+
49
+ def find_user_request(shared_key)
50
+ user_request = user_request_for(shared_key)
51
+ user_request && user_request.to_oauth(self)
52
+ end
53
+
54
+ def save_user_request(user_request)
55
+ if model = user_request_for(user_request.shared_key)
56
+ model.authorized = user_request.authorized?
57
+ model.save || raise("Failed to save UserRequest: #{user_request.shared_key}, #{model.errors.inspect}")
58
+ end
59
+ end
60
+
61
+ def destroy_user_request(user_request)
62
+ user_request = user_request_for(user_request.shared_key)
63
+ user_request && user_request.destroy
64
+ end
65
+
66
+ def create_user_access(user_access)
67
+ with_repository do
68
+ if consumer = consumer_for(user_access.consumer.shared_key)
69
+ u = consumer.user_accesses.create(:request_shared_key => user_access.request_shared_key,
70
+ :shared_key => user_access.shared_key,
71
+ :secret_key => user_access.secret_key)
72
+ else
73
+ raise ConsumerNotFound.new(user_access.consumer.shared_key)
74
+ end
75
+ end
76
+ end
77
+
78
+ def find_user_access(shared_key)
79
+ with_repository do
80
+ user_access = UserAccess.first(:shared_key => shared_key)
81
+ user_access && user_access.to_oauth(self)
82
+ end
83
+ end
84
+
85
+ private
86
+ def with_repository(&block)
87
+ ::DataMapper.repository(@repository) do
88
+ yield
89
+ end
90
+ end
91
+
92
+ def consumer_for(shared_key)
93
+ with_repository do
94
+ Consumer.first(:shared_key => shared_key)
95
+ end
96
+ end
97
+
98
+ def user_request_for(shared_key)
99
+ with_repository do
100
+ UserRequest.first(:shared_key => shared_key)
101
+ end
102
+ end
103
+
104
+ def user_access_for(shared_key)
105
+ with_repository do
106
+ UserAccess.first(:shared_key => shared_key)
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ require 'oauth_provider/backends/data_mapper/consumer'
114
+ require 'oauth_provider/backends/data_mapper/user_request'
115
+ require 'oauth_provider/backends/data_mapper/user_access'
@@ -0,0 +1,25 @@
1
+ module OAuthProvider
2
+ module Backends
3
+ class DataMapper
4
+ class Consumer
5
+ include ::DataMapper::Resource
6
+
7
+ property :id, Serial
8
+ property :callback, String, :unique => true, :nullable => false
9
+ property :shared_key, String, :unique => true, :nullable => false
10
+ property :secret_key, String, :unique => true, :nullable => false
11
+
12
+ has n, :user_requests, :class_name => '::OAuthProvider::Backends::DataMapper::UserRequest'
13
+ has n, :user_accesses, :class_name => '::OAuthProvider::Backends::DataMapper::UserAccess'
14
+
15
+ def token
16
+ OAuthProvider::Token.new(shared_key, secret_key)
17
+ end
18
+
19
+ def to_oauth(backend)
20
+ OAuthProvider::Consumer.new(backend, backend.provider, callback, token)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module OAuthProvider
2
+ module Backends
3
+ class DataMapper
4
+ class UserAccess
5
+ include ::DataMapper::Resource
6
+
7
+ property :id, Serial
8
+ property :consumer_id, Integer, :nullable => false
9
+ property :request_shared_key, String, :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::UserAccess.new(backend, consumer.to_oauth(backend), request_shared_key, token)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end