maromi 0.0.2
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/.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
|
+
|