maromi 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +33 -0
- data/Rakefile +2 -0
- data/lib/maromi.rb +185 -0
- data/lib/maromi/helpers.rb +10 -0
- data/lib/maromi/helpers/lazy_object.rb +66 -0
- data/lib/maromi/helpers/parameter_list.rb +30 -0
- data/lib/maromi/helpers/sinatra.rb +34 -0
- data/lib/maromi/helpers/smart_authorizer_marshal.rb +36 -0
- data/lib/maromi/helpers/token.rb +15 -0
- data/lib/maromi/models.rb +24 -0
- data/lib/maromi/models/authorization.rb +15 -0
- data/lib/maromi/models/consumer.rb +26 -0
- data/lib/maromi/models/request.rb +32 -0
- data/lib/maromi/proxies.rb +7 -0
- data/lib/maromi/proxies/consumer.rb +25 -0
- data/lib/maromi/proxies/consumer_authorization.rb +24 -0
- data/lib/maromi/proxies/consumer_request.rb +70 -0
- data/lib/maromi/version.rb +3 -0
- data/maromi.gemspec +26 -0
- metadata +161 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
maromi (0.0.1)
|
5
|
+
dm-core
|
6
|
+
dm-migrations
|
7
|
+
dm-validations
|
8
|
+
oauth
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: http://rubygems.org/
|
12
|
+
specs:
|
13
|
+
addressable (2.2.3)
|
14
|
+
dm-core (1.0.2)
|
15
|
+
addressable (~> 2.2)
|
16
|
+
extlib (~> 0.9.15)
|
17
|
+
dm-migrations (1.0.2)
|
18
|
+
dm-core (~> 1.0.2)
|
19
|
+
dm-validations (1.0.2)
|
20
|
+
dm-core (~> 1.0.2)
|
21
|
+
extlib (0.9.15)
|
22
|
+
oauth (0.4.4)
|
23
|
+
|
24
|
+
PLATFORMS
|
25
|
+
ruby
|
26
|
+
|
27
|
+
DEPENDENCIES
|
28
|
+
bundler (>= 1.0.0)
|
29
|
+
dm-core
|
30
|
+
dm-migrations
|
31
|
+
dm-validations
|
32
|
+
maromi!
|
33
|
+
oauth
|
data/Rakefile
ADDED
data/lib/maromi.rb
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
require 'dm-migrations'
|
3
|
+
require 'dm-validations'
|
4
|
+
require 'oauth'
|
5
|
+
require 'oauth/request_proxy/rack_request'
|
6
|
+
require 'uri'
|
7
|
+
|
8
|
+
$: << File.dirname(__FILE__)
|
9
|
+
|
10
|
+
# The Maromi Rack Middleware
|
11
|
+
class Maromi
|
12
|
+
|
13
|
+
REQUIRED_OAUTH_PARAMETERS = %w( oauth_signature_method oauth_timestamp
|
14
|
+
oauth_nonce oauth_consumer_key
|
15
|
+
oauth_signature )
|
16
|
+
|
17
|
+
|
18
|
+
autoload :Proxies, 'maromi/proxies'
|
19
|
+
autoload :Helpers, 'maromi/helpers'
|
20
|
+
require 'maromi/models'
|
21
|
+
|
22
|
+
|
23
|
+
cattr_accessor :authorized_request
|
24
|
+
|
25
|
+
# In most cases, you should not call this method directly, but call Rack's
|
26
|
+
# use method
|
27
|
+
# @api public
|
28
|
+
# @example use Maromi, :database => 'database_dev', :adapter => 'mysql',
|
29
|
+
# :user => 'root', :password => ''
|
30
|
+
# @example use Maromi, 'mysql://root:pass@localhost/database_dev'
|
31
|
+
# @param [#call] app The downstream Rack application
|
32
|
+
# @param [String, Hash] connection_path Either a connection string or a
|
33
|
+
# hash of options
|
34
|
+
def initialize(app, connection_path = nil)
|
35
|
+
@app = app
|
36
|
+
auto_attach_helpers_to app
|
37
|
+
connect_to_datamapper! connection_path unless
|
38
|
+
DataMapper::Repository.adapters[:default]
|
39
|
+
DataMapper.auto_upgrade!
|
40
|
+
end
|
41
|
+
|
42
|
+
# Performs the request through maromi
|
43
|
+
# @api protected
|
44
|
+
# @param [Rack::Environment] env The Rack Environment
|
45
|
+
# @return [Array] The Rack Response
|
46
|
+
def call(env)
|
47
|
+
setup env
|
48
|
+
if is_terminal_request?
|
49
|
+
respond_with terminal_response
|
50
|
+
else
|
51
|
+
add_headers
|
52
|
+
with_lazy_authentication { respond_with @app.call(@env) }
|
53
|
+
perform_required_redirects
|
54
|
+
end
|
55
|
+
rescue Unauthorized => e
|
56
|
+
respond_with [
|
57
|
+
401, {'Content-Type' => 'text/html'},
|
58
|
+
['<h1>Unauthorized.</h1>' + e.message]
|
59
|
+
]
|
60
|
+
rescue Invalid => e
|
61
|
+
respond_with [
|
62
|
+
400, {'Content-Type' => 'text/html'},
|
63
|
+
['<h1>Invalid</h1>' + e.message]
|
64
|
+
]
|
65
|
+
ensure
|
66
|
+
return @response
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# Builds the request environment for Maromi to use
|
72
|
+
# @api private
|
73
|
+
# @param [Rack::Environment] env The Rack Environment
|
74
|
+
# @return nil
|
75
|
+
def setup(env)
|
76
|
+
@env = env
|
77
|
+
@request = Rack::Request.new(env)
|
78
|
+
end
|
79
|
+
|
80
|
+
def is_terminal_request?
|
81
|
+
['/oauth/request_token', '/oauth/access_token'].include? @request.path
|
82
|
+
end
|
83
|
+
|
84
|
+
def add_headers
|
85
|
+
@env['maromi.version'] = VERSION
|
86
|
+
@env['maromi.injected'] = true
|
87
|
+
@env['maromi.helper'] = Helpers::LazyObject.new(@request)
|
88
|
+
end
|
89
|
+
|
90
|
+
def perform_required_redirects
|
91
|
+
if request = Maromi.authorized_request and request.callback_url != 'oob'
|
92
|
+
uri = URI.parse(request.callback_url)
|
93
|
+
uri.send(:set_query, [uri.query, 'oauth_verifier=' + request.verifier].reject(&:nil?).join('&'))
|
94
|
+
respond_with [301, {'Location' => uri.to_s}, []]
|
95
|
+
Maromi.authorized_request = nil
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def terminal_response
|
100
|
+
return generate_request_token if @request.path == '/oauth/request_token'
|
101
|
+
return generate_access_token if @request.path == '/oauth/access_token'
|
102
|
+
end
|
103
|
+
|
104
|
+
def generate_access_token
|
105
|
+
request, r, c = OAuth::RequestProxy.proxy(@request)
|
106
|
+
|
107
|
+
if REQUIRED_OAUTH_PARAMETERS.any? {|s| !request.parameters[s]}
|
108
|
+
raise Invalid, required.join(', ') + ' are required.'
|
109
|
+
|
110
|
+
elsif !(r = Request.first(:token => request.parameters['oauth_token'],
|
111
|
+
:consumer => c=Consumer.get(request.parameters['oauth_consumer_key'])))
|
112
|
+
raise Unauthorized, 'Unable to find a request with token ' +
|
113
|
+
request.parameters['oauth_token']
|
114
|
+
|
115
|
+
elsif r.verified && r.verifier == request.parameters['oauth_verifier'] && OAuth::Signature.verify(request) {|_| [r.secret, c.secret] }
|
116
|
+
authorization = r.upgrade!
|
117
|
+
parameters = Helpers::ParameterList.new(:oauth_token => authorization.token, :oauth_token_secret => authorization.secret)
|
118
|
+
|
119
|
+
[200, {'Content-Type' => 'application/x-www-form-urlencoded'}, [parameters.urlencoded]]
|
120
|
+
|
121
|
+
else
|
122
|
+
raise Unauthorized
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def generate_request_token
|
127
|
+
request, c = OAuth::RequestProxy.proxy(@request)
|
128
|
+
if REQUIRED_OAUTH_PARAMETERS.any? {|s| !request.parameters[s] }
|
129
|
+
raise Invalid, required.join(', ') + ' are required.'
|
130
|
+
elsif OAuth::Signature.verify(request) {|r| [ nil, (c=Consumer.get!(r.parameters['oauth_consumer_key'])).secret]}
|
131
|
+
r = Request.new(:consumer => c)
|
132
|
+
r.callback_url = request.parameters['oauth_callback'] || r.consumer.callback_url
|
133
|
+
r.token = Helpers::Token.new(16)
|
134
|
+
r.secret = Helpers::Token.new
|
135
|
+
r.save
|
136
|
+
p = Helpers::ParameterList.new(:oauth_token => r.token, :oauth_token_secret => r.secret, :oauth_callback_confirmed => 'true')
|
137
|
+
[200, {'Content-Type' => 'application/x-www-form-urlencoded'}, [p.urlencoded]]
|
138
|
+
elsif [:oauth_consumer_key]
|
139
|
+
raise Unauthorized, 'Bad Signature.'
|
140
|
+
end
|
141
|
+
rescue OAuth::Signature::UnknownSignatureMethod => e
|
142
|
+
raise Invalid, 'Unknown signature method ' + e.message
|
143
|
+
rescue DataMapper::ObjectNotFoundError
|
144
|
+
raise Unauthorized, 'Could not find a consumer with token ' +
|
145
|
+
request.parameters['oauth_consumer_key']
|
146
|
+
end
|
147
|
+
|
148
|
+
def respond_with(response)
|
149
|
+
@response = response
|
150
|
+
end
|
151
|
+
|
152
|
+
def auto_attach_helpers_to(app)
|
153
|
+
app.send(:helpers, Maromi::Helpers::Sinatra) if
|
154
|
+
defined? Sinatra and app.is_a? Sinatra::Application
|
155
|
+
end
|
156
|
+
|
157
|
+
def connect_to_datamapper!(connection_path)
|
158
|
+
connection_path = (ENV['DATABASE_URL'] || connection_path ||
|
159
|
+
(ActiveRecord::Base.configurations[ENV['RACK_ENV']] if
|
160
|
+
defined? ActiveRecord))
|
161
|
+
if connection_path
|
162
|
+
connection_path = Mash.new(connection_path) and
|
163
|
+
connection_path[:adapter].sub!('postgresql', 'postgres') if
|
164
|
+
connection_path.is_a? Hash
|
165
|
+
DataMapper.setup(:default, connection_path)
|
166
|
+
DataMapper::Logger.new($stdout, :error)
|
167
|
+
else
|
168
|
+
raise "Maromi couldn't find a database to connect to. " +
|
169
|
+
"You should provide it with what it needs."
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def with_lazy_authentication
|
174
|
+
authorized = false
|
175
|
+
catch :unauthorized do
|
176
|
+
yield
|
177
|
+
authorized = true
|
178
|
+
end
|
179
|
+
raise Unauthorized unless authorized
|
180
|
+
end
|
181
|
+
|
182
|
+
class Unauthorized < Exception; end
|
183
|
+
class Invalid < Exception; end
|
184
|
+
|
185
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class Maromi
|
2
|
+
# A collection of utilities in use in Maromi
|
3
|
+
module Helpers
|
4
|
+
autoload :Sinatra, 'maromi/helpers/sinatra'
|
5
|
+
autoload :LazyObject, 'maromi/helpers/lazy_object'
|
6
|
+
autoload :Token, 'maromi/helpers/token'
|
7
|
+
autoload :ParameterList, 'maromi/helpers/parameter_list'
|
8
|
+
autoload :SmartAuthorizerMarshal, 'maromi/helpers/smart_authorizer_marshal'
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
class Maromi
|
2
|
+
module Helpers
|
3
|
+
# The Lazy Object is automatically attached to the Rack ENV as maromi.helper
|
4
|
+
# This basically allows you to interact with Maromi. For the most part, the
|
5
|
+
# interface is built up in a particular framework's set of helpers, such as
|
6
|
+
# in {Maromi::Helpers::Sinatra}, though direct use of the LazyObject is permitted.
|
7
|
+
class LazyObject
|
8
|
+
|
9
|
+
# @api private
|
10
|
+
def initialize(request)
|
11
|
+
@request = request
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
def request_for_authorization
|
16
|
+
@request_for_authorization ||= Request.get!(@request.params['oauth_token'])
|
17
|
+
rescue DataMapper::ObjectNotFoundError
|
18
|
+
@request_for_authorization = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
def consumer
|
23
|
+
return @consumer unless @consumer.nil?
|
24
|
+
if is_authorization_request? && request_for_authorization
|
25
|
+
return @consumer = Proxies::ConsumerRequest.new(request_for_authorization)
|
26
|
+
elsif oauth_authenticated?
|
27
|
+
return @consumer
|
28
|
+
end
|
29
|
+
return @consumer
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [Boolean] whether or not the current request should use temporary credentials
|
33
|
+
def is_authorization_request?
|
34
|
+
@request.path == '/oauth/authorize'
|
35
|
+
end
|
36
|
+
|
37
|
+
# Fires up the whole thing. Makes sure that the current request is authenticated and
|
38
|
+
# causes Rack to return a 401 Unauthorized immediately if it is not.
|
39
|
+
def require_oauth_authentication!
|
40
|
+
throw :unauthorized unless oauth_authenticated?
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [Boolean] whether or not the current request is authenticated by oauth.
|
44
|
+
def oauth_authenticated?
|
45
|
+
request, consumer, authorization = OAuth::RequestProxy.proxy(@request)
|
46
|
+
if OAuth::Signature.verify(request) do |r|
|
47
|
+
raise 'Bad Consumer Key' unless consumer = Consumer.get!(r.parameters['oauth_consumer_key'])
|
48
|
+
raise 'Bad Token' unless authorization = Authorization.first(:consumer => consumer, :token => r.parameters['oauth_token']) || Request.first(:consumer => consumer, :token => r.parameters['oauth_token'])
|
49
|
+
[authorization.secret, consumer.secret]
|
50
|
+
end
|
51
|
+
@consumer = Proxies::ConsumerAuthorization.new(authorization, consumer) if authorization.is_a? Authorization
|
52
|
+
@consumer = Proxies::ConsumerRequest.new(authorization, consumer) if authorization.is_a? Request
|
53
|
+
return true if authorization.is_a? Authorization
|
54
|
+
end
|
55
|
+
rescue Exception => e
|
56
|
+
return false
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Maromi::Consumer]a new consumer
|
60
|
+
def new_consumer(params={})
|
61
|
+
consumer = Consumer.new(:secret => params[:secret] || Helpers::Token.new, :token => params[:token] || Helpers::Token.new(16), :callback_url => params[:callback], :name => params[:name])
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Maromi
|
2
|
+
module Helpers
|
3
|
+
class ParameterList
|
4
|
+
def initialize(hash={})
|
5
|
+
@parameters = hash
|
6
|
+
end
|
7
|
+
|
8
|
+
def []=(key, value)
|
9
|
+
@parameters[key] = value
|
10
|
+
end
|
11
|
+
|
12
|
+
def [](key)
|
13
|
+
@parameters[key]
|
14
|
+
end
|
15
|
+
|
16
|
+
def urlencoded
|
17
|
+
@parameters.map{|key,value| "#{key}=#{value}"}.join('&')
|
18
|
+
end
|
19
|
+
|
20
|
+
def method_missing(symbol, arguments)
|
21
|
+
method = symbol.to_s
|
22
|
+
if method[-1] == '='
|
23
|
+
self[method[0..(method.length-1)].intern] = arguments[0]
|
24
|
+
else
|
25
|
+
self[symbol]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class Maromi
|
2
|
+
module Helpers
|
3
|
+
module Sinatra
|
4
|
+
|
5
|
+
# @return [LazyObject] the {LazyObject} attached to this request
|
6
|
+
def maromi
|
7
|
+
env['maromi.helper']
|
8
|
+
end
|
9
|
+
|
10
|
+
# @return [String] the oauth_token attached to this request
|
11
|
+
def oauth_token
|
12
|
+
params[:oauth_token]
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [Proxies::Consumer, Proxies::ConsumerRequest, Proxies::ConsumerAuthorization] the consumer proxy which corresponds to the level of access attached to this request
|
16
|
+
def consumer
|
17
|
+
maromi.consumer
|
18
|
+
end
|
19
|
+
|
20
|
+
def require_oauth_authentication!
|
21
|
+
maromi.require_oauth_authentication!
|
22
|
+
end
|
23
|
+
|
24
|
+
def temporary_consumer
|
25
|
+
maromi.request_for_authorization
|
26
|
+
end
|
27
|
+
|
28
|
+
def new_consumer(*args)
|
29
|
+
maromi.new_consumer(*args)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class Maromi
|
2
|
+
module Helpers
|
3
|
+
# A little helper for storing authorizers on requests and authorizations
|
4
|
+
# that knows how to deal with AR and DM objects
|
5
|
+
module SmartAuthorizerMarshal
|
6
|
+
|
7
|
+
# Sets Datamapper attributes properly in the case that obj is an AR or DM object
|
8
|
+
# @param [Object] object the value to attach to the datamapper resource
|
9
|
+
def authorizer=(obj)
|
10
|
+
if defined? ActiveRecord and obj.is_a? ActiveRecord::Base
|
11
|
+
attribute_set(:authorizer, Marshal.dump(obj.send(obj.class.primary_key)))
|
12
|
+
attribute_set(:authorizer_class, obj.class)
|
13
|
+
return obj
|
14
|
+
elsif obj.class.included_modules.include? DataMapper::Resource
|
15
|
+
attribute_set(:authorizer, Marshal.dump(obj.key[0]))
|
16
|
+
attribute_set(:authorizer_class, obj.class)
|
17
|
+
else
|
18
|
+
attribute_set(:authorizer, Marshal.dump(obj))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Intelligently plucks objects stored using SAM.authorizer=(obj).
|
23
|
+
# @return [Object] object previously stored in the datamapper resource
|
24
|
+
def authorizer
|
25
|
+
authorizer, klass = attribute_get(:authorizer), attribute_get(:authorizer_class)
|
26
|
+
return nil if authorizer.nil?
|
27
|
+
return authorizer if authorizer = Marshal.load(attribute_get(:authorizer)) and ( klass.nil? || klass == '' )
|
28
|
+
if defined? ActiveRecord and (klass = eval(klass)).ancestors.include? ActiveRecord::Base
|
29
|
+
return klass.find(authorizer)
|
30
|
+
else
|
31
|
+
return klass.get!(authorizer)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Maromi
|
2
|
+
module Helpers
|
3
|
+
# There was going to be more to this at one point, but all it does is to
|
4
|
+
# generate a random string suitable for use as a token or secret.
|
5
|
+
module Token
|
6
|
+
|
7
|
+
# Generates a pseudorandom string suitable for use as a token or secret.
|
8
|
+
# @return [String] a pseudorandom string
|
9
|
+
# @param [Integer] size the maximum length of the string
|
10
|
+
def self.new(size=32)
|
11
|
+
Base64.encode64(OpenSSL::Random.random_bytes(size)).gsub(/\W/,'')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Maromi
|
2
|
+
# A wrapper for the DataMapper models used by Maromi
|
3
|
+
module Models
|
4
|
+
|
5
|
+
# proxy to Maromi::Consumer
|
6
|
+
def Consumer
|
7
|
+
::Maromi::Consumer
|
8
|
+
end
|
9
|
+
|
10
|
+
# proxy to Maromi::Authorization
|
11
|
+
def Authorization
|
12
|
+
::Maromi::Authorization
|
13
|
+
end
|
14
|
+
|
15
|
+
#proxy to Maromi::Request
|
16
|
+
def Request
|
17
|
+
::Maromi::Request
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'maromi/models/consumer'
|
23
|
+
require 'maromi/models/authorization'
|
24
|
+
require 'maromi/models/request'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Maromi
|
2
|
+
class Authorization
|
3
|
+
include DataMapper::Resource
|
4
|
+
|
5
|
+
property :token, String, :key => true
|
6
|
+
property :secret, String
|
7
|
+
property :authorizer, Object
|
8
|
+
property :authorizer_class, String
|
9
|
+
property :scopes, String
|
10
|
+
|
11
|
+
belongs_to :consumer
|
12
|
+
|
13
|
+
include Helpers::SmartAuthorizerMarshal
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Maromi
|
2
|
+
class Consumer
|
3
|
+
include DataMapper::Resource
|
4
|
+
|
5
|
+
property :token, String, :key => true
|
6
|
+
|
7
|
+
property :secret, String
|
8
|
+
property :name, String
|
9
|
+
property :callback_url, String,
|
10
|
+
:format => %r{https?://([-\w\.]+)+(:\d+)?(/([\w/_\.]*(\?\S+)?)?)?},
|
11
|
+
:required => true,
|
12
|
+
:unique => true,
|
13
|
+
:messages => {
|
14
|
+
:is_unique => 'The callback url is already in use',
|
15
|
+
:format => 'The callback url does not appear to be valid'
|
16
|
+
}
|
17
|
+
|
18
|
+
|
19
|
+
has n, :requests
|
20
|
+
has n, :authorizations
|
21
|
+
|
22
|
+
def callback_url=(callback)
|
23
|
+
attribute_set(:callback_url, callback.respond_to?(:downcase) ? callback.downcase : callback)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class Maromi
|
2
|
+
class Request
|
3
|
+
include DataMapper::Resource
|
4
|
+
|
5
|
+
property :token, String, :key => true
|
6
|
+
property :secret, String, :required => true
|
7
|
+
property :authorizer, Object
|
8
|
+
property :authorizer_class, String
|
9
|
+
property :scopes, String
|
10
|
+
property :verifier, String
|
11
|
+
property :verified, Boolean
|
12
|
+
property :callback_url, String, :required => true
|
13
|
+
|
14
|
+
belongs_to :consumer
|
15
|
+
|
16
|
+
include Helpers::SmartAuthorizerMarshal
|
17
|
+
|
18
|
+
def upgrade!
|
19
|
+
authorization = Authorization.create(:token => Helpers::Token.new(16),
|
20
|
+
:secret => Helpers::Token.new,
|
21
|
+
:authorizer => authorizer,
|
22
|
+
:scopes => scopes,
|
23
|
+
:consumer => consumer)
|
24
|
+
destroy!
|
25
|
+
return authorization
|
26
|
+
rescue Exception => e
|
27
|
+
p e
|
28
|
+
p e.message
|
29
|
+
p e.backtrace
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Maromi
|
2
|
+
module Proxies
|
3
|
+
class Consumer
|
4
|
+
def initialize(consumer)
|
5
|
+
@consumer = consumer
|
6
|
+
end
|
7
|
+
|
8
|
+
def callback
|
9
|
+
@consumer.callback_url
|
10
|
+
end
|
11
|
+
|
12
|
+
def name
|
13
|
+
@consumer.nickname
|
14
|
+
end
|
15
|
+
|
16
|
+
def secret
|
17
|
+
@consumer.secret
|
18
|
+
end
|
19
|
+
|
20
|
+
def token
|
21
|
+
@consumer.token
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Maromi
|
2
|
+
module Proxies
|
3
|
+
class ConsumerAuthorization
|
4
|
+
|
5
|
+
def initialize(access, consumer = nil)
|
6
|
+
@access, @consumer = access, consumer
|
7
|
+
end
|
8
|
+
|
9
|
+
def authorizer
|
10
|
+
@authorizer ||= access.authorizer
|
11
|
+
end
|
12
|
+
alias_method :user, :authorizer
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
attr_reader :access
|
17
|
+
|
18
|
+
def consumer
|
19
|
+
@consumer ||= access.consumer
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
class Maromi
|
2
|
+
module Proxies
|
3
|
+
class ConsumerRequest
|
4
|
+
|
5
|
+
def initialize(request, consumer = nil)
|
6
|
+
@request, @consumer = request, consumer
|
7
|
+
end
|
8
|
+
|
9
|
+
def authorized!(params = {})
|
10
|
+
request.tap do |r|
|
11
|
+
r.authorizer = params[:by] if params[:by]
|
12
|
+
r.scopes = params[:to] if params[:to]
|
13
|
+
r.verified = true
|
14
|
+
r.verifier = (request.callback_url == 'oob' ? rand(9999) : Helpers::Token.new)
|
15
|
+
r.save!
|
16
|
+
end
|
17
|
+
Maromi.authorized_request = request
|
18
|
+
end
|
19
|
+
|
20
|
+
def authorizer
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
alias_method :user, :authorizer
|
24
|
+
|
25
|
+
def name
|
26
|
+
consumer.name
|
27
|
+
end
|
28
|
+
|
29
|
+
def callback
|
30
|
+
return request.callback_url unless request.callback_url == 'oob'
|
31
|
+
return consumer.callback_url
|
32
|
+
end
|
33
|
+
|
34
|
+
def requested_redirect?
|
35
|
+
request.callback_url != 'oob'
|
36
|
+
end
|
37
|
+
|
38
|
+
def verifier
|
39
|
+
request.verifier
|
40
|
+
end
|
41
|
+
alias_method :pin, :verifier
|
42
|
+
|
43
|
+
def to_s
|
44
|
+
name || callback
|
45
|
+
end
|
46
|
+
|
47
|
+
def token
|
48
|
+
consumer.token
|
49
|
+
end
|
50
|
+
|
51
|
+
def authorize_button(button_text = 'Authorize')
|
52
|
+
<<-EOF.gsub(' ', '')
|
53
|
+
<form action="/oauth/authorize" method="post">
|
54
|
+
<input type="hidden" name="oauth_token" value="#{request.token}" />
|
55
|
+
<button type="submit">#{button_text}</button>
|
56
|
+
</form>
|
57
|
+
EOF
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
attr_reader :request
|
63
|
+
|
64
|
+
def consumer
|
65
|
+
@consumer ||= request.consumer
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/maromi.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path("../lib/maromi/version", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "maromi"
|
6
|
+
s.version = Maromi::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = []
|
9
|
+
s.email = []
|
10
|
+
s.homepage = "http://rubygems.org/gems/maromi"
|
11
|
+
s.summary = "Maromi's a (Naive) Rack OAuth Middleware"
|
12
|
+
s.description = "Maromi sits between you and your app and handles OAuth stuff. Dependencies abound."
|
13
|
+
|
14
|
+
s.required_rubygems_version = ">= 1.3.6"
|
15
|
+
s.rubyforge_project = "maromi"
|
16
|
+
|
17
|
+
%w{dm-core dm-migrations dm-validations oauth}.each do |rubygem|
|
18
|
+
s.add_dependency rubygem
|
19
|
+
end
|
20
|
+
|
21
|
+
s.add_development_dependency "bundler", ">= 1.0.0"
|
22
|
+
|
23
|
+
s.files = `git ls-files`.split("\n")
|
24
|
+
s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
|
25
|
+
s.require_path = 'lib'
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: maromi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
|
+
platform: ruby
|
12
|
+
authors: []
|
13
|
+
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-01-24 00:00:00 -05:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: dm-core
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: dm-migrations
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: dm-validations
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :runtime
|
62
|
+
version_requirements: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: oauth
|
65
|
+
prerelease: false
|
66
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
hash: 3
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
type: :runtime
|
76
|
+
version_requirements: *id004
|
77
|
+
- !ruby/object:Gem::Dependency
|
78
|
+
name: bundler
|
79
|
+
prerelease: false
|
80
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
hash: 23
|
86
|
+
segments:
|
87
|
+
- 1
|
88
|
+
- 0
|
89
|
+
- 0
|
90
|
+
version: 1.0.0
|
91
|
+
type: :development
|
92
|
+
version_requirements: *id005
|
93
|
+
description: Maromi sits between you and your app and handles OAuth stuff. Dependencies abound.
|
94
|
+
email: []
|
95
|
+
|
96
|
+
executables: []
|
97
|
+
|
98
|
+
extensions: []
|
99
|
+
|
100
|
+
extra_rdoc_files: []
|
101
|
+
|
102
|
+
files:
|
103
|
+
- .gitignore
|
104
|
+
- Gemfile
|
105
|
+
- Gemfile.lock
|
106
|
+
- Rakefile
|
107
|
+
- lib/maromi.rb
|
108
|
+
- lib/maromi/helpers.rb
|
109
|
+
- lib/maromi/helpers/lazy_object.rb
|
110
|
+
- lib/maromi/helpers/parameter_list.rb
|
111
|
+
- lib/maromi/helpers/sinatra.rb
|
112
|
+
- lib/maromi/helpers/smart_authorizer_marshal.rb
|
113
|
+
- lib/maromi/helpers/token.rb
|
114
|
+
- lib/maromi/models.rb
|
115
|
+
- lib/maromi/models/authorization.rb
|
116
|
+
- lib/maromi/models/consumer.rb
|
117
|
+
- lib/maromi/models/request.rb
|
118
|
+
- lib/maromi/proxies.rb
|
119
|
+
- lib/maromi/proxies/consumer.rb
|
120
|
+
- lib/maromi/proxies/consumer_authorization.rb
|
121
|
+
- lib/maromi/proxies/consumer_request.rb
|
122
|
+
- lib/maromi/version.rb
|
123
|
+
- maromi.gemspec
|
124
|
+
has_rdoc: true
|
125
|
+
homepage: http://rubygems.org/gems/maromi
|
126
|
+
licenses: []
|
127
|
+
|
128
|
+
post_install_message:
|
129
|
+
rdoc_options: []
|
130
|
+
|
131
|
+
require_paths:
|
132
|
+
- lib
|
133
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
134
|
+
none: false
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
hash: 3
|
139
|
+
segments:
|
140
|
+
- 0
|
141
|
+
version: "0"
|
142
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
|
+
none: false
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
hash: 23
|
148
|
+
segments:
|
149
|
+
- 1
|
150
|
+
- 3
|
151
|
+
- 6
|
152
|
+
version: 1.3.6
|
153
|
+
requirements: []
|
154
|
+
|
155
|
+
rubyforge_project: maromi
|
156
|
+
rubygems_version: 1.4.1
|
157
|
+
signing_key:
|
158
|
+
specification_version: 3
|
159
|
+
summary: Maromi's a (Naive) Rack OAuth Middleware
|
160
|
+
test_files: []
|
161
|
+
|