danski-ooh-auth 0.1.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/LICENSE +20 -0
- data/Rakefile +58 -0
- data/app/controllers/application.rb +16 -0
- data/app/controllers/authenticating_clients.rb +60 -0
- data/app/controllers/tokens.rb +94 -0
- data/app/helpers/application_helper.rb +64 -0
- data/app/helpers/authenticating_clients_helper.rb +5 -0
- data/app/helpers/authentications_helper.rb +5 -0
- data/app/models/authenticating_client.rb +12 -0
- data/app/models/authenticating_client/dm_authenticating_client.rb +71 -0
- data/app/models/token.rb +12 -0
- data/app/models/token/dm_token.rb +150 -0
- data/app/views/authenticating_clients/_help.html.erb +1 -0
- data/app/views/authenticating_clients/edit.html.erb +27 -0
- data/app/views/authenticating_clients/index.html.erb +24 -0
- data/app/views/authenticating_clients/new.html.erb +47 -0
- data/app/views/authenticating_clients/show.html.erb +40 -0
- data/app/views/layout/ooh_auth.html.erb +23 -0
- data/app/views/tokens/create.html.erb +34 -0
- data/app/views/tokens/edit.html.erb +4 -0
- data/app/views/tokens/new.html.erb +52 -0
- data/app/views/tokens/show.html.erb +1 -0
- data/lib/ooh-auth.rb +103 -0
- data/lib/ooh-auth/authentication_mixin.rb +13 -0
- data/lib/ooh-auth/controller_mixin.rb +38 -0
- data/lib/ooh-auth/key_generators.rb +57 -0
- data/lib/ooh-auth/merbtasks.rb +103 -0
- data/lib/ooh-auth/request_verification_mixin.rb +160 -0
- data/lib/ooh-auth/slicetasks.rb +18 -0
- data/lib/ooh-auth/spectasks.rb +65 -0
- data/lib/ooh-auth/strategies/oauth.rb +16 -0
- data/public/javascripts/master.js +0 -0
- data/public/stylesheets/master.css +2 -0
- data/readme.markdown +43 -0
- data/spec/controllers/application_spec.rb +35 -0
- data/spec/controllers/authenticating_clients_spec.rb +119 -0
- data/spec/controllers/tokens_spec.rb +173 -0
- data/spec/merb-auth-slice-fullfat_spec.rb +41 -0
- data/spec/models/authenticating_client_spec.rb +44 -0
- data/spec/models/oauth_strategy_spec.rb +48 -0
- data/spec/models/request_verification_mixin_spec.rb +121 -0
- data/spec/models/token_spec.rb +139 -0
- data/spec/spec_fixtures.rb +19 -0
- data/spec/spec_helper.rb +107 -0
- data/stubs/app/controllers/application.rb +2 -0
- data/stubs/app/controllers/main.rb +2 -0
- metadata +133 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Dan Glegg
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
|
4
|
+
require 'merb-core'
|
5
|
+
require 'merb-core/tasks/merb'
|
6
|
+
|
7
|
+
require 'spec/rake/spectask'
|
8
|
+
require 'merb-core/test/tasks/spectasks'
|
9
|
+
require 'merb_datamapper/merbtasks'
|
10
|
+
|
11
|
+
desc 'Default: run spec examples'
|
12
|
+
task :default => 'spec'
|
13
|
+
|
14
|
+
|
15
|
+
GEM_NAME = "ooh-auth"
|
16
|
+
AUTHOR = "Dan Glegg"
|
17
|
+
EMAIL = "dan@angryamoeba.co.uk"
|
18
|
+
HOMEPAGE = "http://github.com/danski/ooh-auth"
|
19
|
+
SUMMARY = "Merb Slice that provides RESTful authentication functionality for your application."
|
20
|
+
GEM_VERSION = "0.1.2"
|
21
|
+
|
22
|
+
spec = Gem::Specification.new do |s|
|
23
|
+
s.rubyforge_project = 'merb'
|
24
|
+
s.name = GEM_NAME
|
25
|
+
s.version = GEM_VERSION
|
26
|
+
s.platform = Gem::Platform::RUBY
|
27
|
+
s.has_rdoc = true
|
28
|
+
s.extra_rdoc_files = ["README", "LICENSE", 'TODO']
|
29
|
+
s.summary = SUMMARY
|
30
|
+
s.description = s.summary
|
31
|
+
s.author = AUTHOR
|
32
|
+
s.email = EMAIL
|
33
|
+
s.homepage = HOMEPAGE
|
34
|
+
s.add_dependency('merb-slices', '>= 0.9.10')
|
35
|
+
s.require_path = 'lib'
|
36
|
+
s.files = %w(LICENSE README Rakefile TODO) + Dir.glob("{lib,spec,app,public,stubs}/**/*")
|
37
|
+
end
|
38
|
+
|
39
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
40
|
+
pkg.gem_spec = spec
|
41
|
+
end
|
42
|
+
|
43
|
+
desc "Install the gem"
|
44
|
+
task :install do
|
45
|
+
Merb::RakeHelper.install(GEM_NAME, :version => GEM_VERSION)
|
46
|
+
end
|
47
|
+
|
48
|
+
desc "Uninstall the gem"
|
49
|
+
task :uninstall do
|
50
|
+
Merb::RakeHelper.uninstall(GEM_NAME, :version => GEM_VERSION)
|
51
|
+
end
|
52
|
+
|
53
|
+
desc "Create a gemspec file"
|
54
|
+
task :gemspec do
|
55
|
+
File.open("#{GEM_NAME}.gemspec", "w") do |file|
|
56
|
+
file.puts spec.to_ruby
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class OohAuth::Application < Merb::Controller
|
2
|
+
|
3
|
+
controller_for_slice
|
4
|
+
|
5
|
+
private
|
6
|
+
def user_class
|
7
|
+
Merb::Authentication.user_class
|
8
|
+
end
|
9
|
+
|
10
|
+
# Can be removed once http://merb.lighthouseapp.com/projects/7433/tickets/956-patch-add-message-support
|
11
|
+
# is merged.
|
12
|
+
def message=(arg)
|
13
|
+
@_message = arg
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
class OohAuth::AuthenticatingClients < OohAuth::Application
|
2
|
+
|
3
|
+
before :ensure_authenticated, :exclude=>[:index]
|
4
|
+
only_provides :html
|
5
|
+
|
6
|
+
def index
|
7
|
+
@authenticating_clients = OohAuth::AuthenticatingClient.find_for_user(session.user)
|
8
|
+
render :index
|
9
|
+
end
|
10
|
+
|
11
|
+
def show(id)
|
12
|
+
@authenticating_client = OohAuth::AuthenticatingClient.get(id)
|
13
|
+
raise NotFound unless @authenticating_client and @authenticating_client.editable_by?(session.user)
|
14
|
+
display @authenticating_client, :show
|
15
|
+
end
|
16
|
+
|
17
|
+
def new
|
18
|
+
@authenticating_client = OohAuth::AuthenticatingClient.new
|
19
|
+
display @authenticating_client
|
20
|
+
end
|
21
|
+
|
22
|
+
def create(authenticating_client)
|
23
|
+
@authenticating_client = OohAuth::AuthenticatingClient.new_for_user(session.user, authenticating_client)
|
24
|
+
if @authenticating_client.save
|
25
|
+
headers['Location'] = slice_url(:authenticating_client, @authenticating_client)
|
26
|
+
render :show, :status=>201
|
27
|
+
else
|
28
|
+
message[:error] = "There were problems creating the Application."
|
29
|
+
render :new
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def edit(id)
|
34
|
+
@authenticating_client = OohAuth::AuthenticatingClient.get(id)
|
35
|
+
raise NotFound unless @authenticating_client and @authenticating_client.editable_by?(session.user)
|
36
|
+
display @authenticating_client, :edit
|
37
|
+
end
|
38
|
+
|
39
|
+
def update(id, authenticating_client)
|
40
|
+
@authenticating_client = OohAuth::AuthenticatingClient.get(id)
|
41
|
+
raise NotFound unless @authenticating_client and @authenticating_client.editable_by?(session.user)
|
42
|
+
if @authenticating_client.update_attributes(authenticating_client)
|
43
|
+
message[:success] = "Application updated successfully!"
|
44
|
+
redirect slice_url(:authenticating_client, @authenticating_client)
|
45
|
+
else
|
46
|
+
display @authenticating_client, :edit
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def destroy(id)
|
51
|
+
@authenticating_client = OohAuth::AuthenticatingClient.get(id)
|
52
|
+
raise NotFound unless @authenticating_client and @authenticating_client.editable_by?(session.user)
|
53
|
+
if @authenticating_client.destroy
|
54
|
+
redirect slice_url(:authenticating_clients)
|
55
|
+
else
|
56
|
+
raise InternalServerError
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end # OohAuth::AuthenticatingClients
|
@@ -0,0 +1,94 @@
|
|
1
|
+
=begin
|
2
|
+
OohAuth::Tokens
|
3
|
+
|
4
|
+
This controller is intended to allow applications to authenticate on behalf of a user.
|
5
|
+
Applications follow a set process as shown in authenticating.markdown, in which a number of background
|
6
|
+
requests are made to the host application while the user is run through a short process in which they
|
7
|
+
specify which privileges to give to the authenticating client, and how long to give them for.
|
8
|
+
|
9
|
+
The Authentications controller provides a relatively opaque interface to
|
10
|
+
users and authenticating clients compared to other resource controllers. Most of the views provide only HTML
|
11
|
+
representations as they not only are intended for human interaction, but specifically require it.
|
12
|
+
=end
|
13
|
+
|
14
|
+
require 'net/http'
|
15
|
+
|
16
|
+
class OohAuth::Tokens < OohAuth::Application
|
17
|
+
|
18
|
+
# Define other formats
|
19
|
+
provides :js, :xml, :yaml
|
20
|
+
|
21
|
+
# The index and new actions require a signed request.
|
22
|
+
before :ensure_signed, :only=>[:index]
|
23
|
+
# All other actions require that the user be authenticated directly, rather than through the api.
|
24
|
+
before :forbid_authentication_with_oauth, :exclude=>[:index]
|
25
|
+
|
26
|
+
# Main action used for starting the authorisation process (desktop clients) and finishing it (web clients)
|
27
|
+
def index
|
28
|
+
raise NotAcceptable unless @authenticating_client = request.authenticating_client
|
29
|
+
if @token = request.authentication_token
|
30
|
+
# If client and request key, give the activated token if it was activated.
|
31
|
+
raise NotAcceptable unless @token.authenticating_client == @authenticating_client
|
32
|
+
else
|
33
|
+
# Generate a request key
|
34
|
+
@token = OohAuth::Token.create_request_key(@authenticating_client)
|
35
|
+
end
|
36
|
+
# # Okay, no error raised. Gogo render.
|
37
|
+
display @token, :show, :layout=>false
|
38
|
+
end
|
39
|
+
|
40
|
+
def new
|
41
|
+
only_provides :html
|
42
|
+
unless (@token = OohAuth::Token.first(:token_key=>request.token) and
|
43
|
+
@authenticating_client = @token.authenticating_client)
|
44
|
+
raise NotAcceptable
|
45
|
+
end
|
46
|
+
display @token, :new
|
47
|
+
end
|
48
|
+
|
49
|
+
# Activates an authentication receipt, converting it into a token the authenticating client can use in future requests.
|
50
|
+
def create(token)
|
51
|
+
only_provides :html
|
52
|
+
commit = (params[:commit]=="allow") # Did they click the allow or the deny button? ENQUIRING MINDS NEED TO KNOW!
|
53
|
+
raise NotFound unless @token = OohAuth::Token.get_token(request.token) # The oauth_token is now in the post body.
|
54
|
+
raise NotFound unless @authenticating_client = @token.authenticating_client # Stop right there, criminal scum.
|
55
|
+
|
56
|
+
@activated = @token.activate!(session.user, token[:expires], token[:permissions]) if commit
|
57
|
+
redirect("#{request.callback}#{(request.callback["?"])? "&" : "?"}oauth_token=#{@token.token_key}") if commit and request.callback # the callback is in the post body
|
58
|
+
display @token, :create
|
59
|
+
end
|
60
|
+
|
61
|
+
#def show(id)
|
62
|
+
# @token = ::Authentication.get(id)
|
63
|
+
# raise NotFound unless @token
|
64
|
+
# display @token
|
65
|
+
#end
|
66
|
+
#
|
67
|
+
#def edit(id)
|
68
|
+
# only_provides :html
|
69
|
+
# @token = OohAuth::Token.get(id)
|
70
|
+
# raise NotFound unless @token
|
71
|
+
# display @token
|
72
|
+
#end
|
73
|
+
#
|
74
|
+
#def update(id, token)
|
75
|
+
# @token = OohAuth::Token.get(id)
|
76
|
+
# raise NotFound unless @token
|
77
|
+
# if @token.update_attributes(authentication)
|
78
|
+
# redirect slice_url(:tokens, @token)
|
79
|
+
# else
|
80
|
+
# display @token, :edit
|
81
|
+
# end
|
82
|
+
#end
|
83
|
+
#
|
84
|
+
#def destroy(id)
|
85
|
+
# @token = OohAuth::Token.get(id)
|
86
|
+
# raise NotFound unless @token
|
87
|
+
# if @token.destroy
|
88
|
+
# redirect slice_url(:tokens)
|
89
|
+
# else
|
90
|
+
# raise InternalServerError
|
91
|
+
# end
|
92
|
+
#end
|
93
|
+
|
94
|
+
end # OohAuth::Tokens
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Merb
|
2
|
+
module OohAuth
|
3
|
+
module ApplicationHelper
|
4
|
+
|
5
|
+
# @param *segments<Array[#to_s]> Path segments to append.
|
6
|
+
#
|
7
|
+
# @return <String>
|
8
|
+
# A path relative to the public directory, with added segments.
|
9
|
+
def image_path(*segments)
|
10
|
+
public_path_for(:image, *segments)
|
11
|
+
end
|
12
|
+
|
13
|
+
# @param *segments<Array[#to_s]> Path segments to append.
|
14
|
+
#
|
15
|
+
# @return <String>
|
16
|
+
# A path relative to the public directory, with added segments.
|
17
|
+
def javascript_path(*segments)
|
18
|
+
public_path_for(:javascript, *segments)
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param *segments<Array[#to_s]> Path segments to append.
|
22
|
+
#
|
23
|
+
# @return <String>
|
24
|
+
# A path relative to the public directory, with added segments.
|
25
|
+
def stylesheet_path(*segments)
|
26
|
+
public_path_for(:stylesheet, *segments)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Construct a path relative to the public directory
|
30
|
+
#
|
31
|
+
# @param <Symbol> The type of component.
|
32
|
+
# @param *segments<Array[#to_s]> Path segments to append.
|
33
|
+
#
|
34
|
+
# @return <String>
|
35
|
+
# A path relative to the public directory, with added segments.
|
36
|
+
def public_path_for(type, *segments)
|
37
|
+
::OohAuth.public_path_for(type, *segments)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Construct an app-level path.
|
41
|
+
#
|
42
|
+
# @param <Symbol> The type of component.
|
43
|
+
# @param *segments<Array[#to_s]> Path segments to append.
|
44
|
+
#
|
45
|
+
# @return <String>
|
46
|
+
# A path within the host application, with added segments.
|
47
|
+
def app_path_for(type, *segments)
|
48
|
+
::OohAuth.app_path_for(type, *segments)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Construct a slice-level path.
|
52
|
+
#
|
53
|
+
# @param <Symbol> The type of component.
|
54
|
+
# @param *segments<Array[#to_s]> Path segments to append.
|
55
|
+
#
|
56
|
+
# @return <String>
|
57
|
+
# A path within the slice source (Gem), with added segments.
|
58
|
+
def slice_path_for(type, *segments)
|
59
|
+
::OohAuth.slice_path_for(type, *segments)
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
path = File.expand_path(File.dirname(__FILE__)) / "authenticating_client"
|
2
|
+
if defined?(DataMapper)
|
3
|
+
require path / "dm_authenticating_client"
|
4
|
+
#elsif defined?(ActiveRecord)
|
5
|
+
# require path / "ar_password_reset"
|
6
|
+
#elsif defined?(Sequel)
|
7
|
+
# require path / "sq_password_reset"
|
8
|
+
#elsif defined?(RelaxDB)
|
9
|
+
# require path / "relaxdb_password_reset"
|
10
|
+
else
|
11
|
+
raise RuntimeError, "Datamapper is a dependency of the slice at this time."
|
12
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
=begin
|
2
|
+
OohAuth::AuthenticatingClient
|
3
|
+
========================================================================================
|
4
|
+
An authenticating client is an external application which wants to use your application's public API while authenticated as one of your users.
|
5
|
+
=end
|
6
|
+
|
7
|
+
class OohAuth::AuthenticatingClient
|
8
|
+
include DataMapper::Resource
|
9
|
+
|
10
|
+
# Key it
|
11
|
+
property :id, Serial
|
12
|
+
# The registration will belong to a user, who will be able to edit the client properties.
|
13
|
+
property :user_id, Integer, :writer => :protected
|
14
|
+
# Timestamp it
|
15
|
+
property :created_at, DateTime
|
16
|
+
|
17
|
+
# Used by all type of authenticating apps
|
18
|
+
property :name, String # e.g. "Mobilator PRO"
|
19
|
+
property :web_url, URI # e.g. "http://mobilator.portionator.net"
|
20
|
+
property :api_key, String, :index=>true # the unique key for this application.
|
21
|
+
property :secret, String # the secret which will NEVER be transmitted during the authentication procedure. Used only to sign requests.
|
22
|
+
property :kind, String # e.g "desktop", "web", "mobile"
|
23
|
+
|
24
|
+
validates_present :name, :web_url, :api_key, :secret, :kind
|
25
|
+
validates_is_unique :name
|
26
|
+
validates_is_unique :api_key
|
27
|
+
validates_with_method :kind, :valid_kind?
|
28
|
+
|
29
|
+
before :valid?, :generate_keys_if_not_present
|
30
|
+
|
31
|
+
def self.new_for_user(user, attrs)
|
32
|
+
o = new(attrs)
|
33
|
+
o.user = user
|
34
|
+
return o
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.find_for_user(user)
|
38
|
+
return [] unless user
|
39
|
+
return all(:user_id=>user.id)
|
40
|
+
end
|
41
|
+
|
42
|
+
def is_webapp?
|
43
|
+
self.kind == "web"
|
44
|
+
end
|
45
|
+
|
46
|
+
def generate_keys_if_not_present
|
47
|
+
api_key_length = 15
|
48
|
+
while self.api_key.blank? or self.class.first(:id.not=>id, :api_key=>self.api_key) do
|
49
|
+
self.api_key = OohAuth::KeyGenerators::Alphanum.gen(api_key_length)
|
50
|
+
api_key_length += 1
|
51
|
+
end
|
52
|
+
self.secret = OohAuth::KeyGenerators::Alphanum.gen(40) if secret.blank?
|
53
|
+
end
|
54
|
+
|
55
|
+
def valid_kind?
|
56
|
+
if OohAuth[:client_kinds].include?(self.kind)
|
57
|
+
return true
|
58
|
+
else
|
59
|
+
return false, "illegal kind"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def user=(user)
|
64
|
+
self.user_id = user.id
|
65
|
+
end
|
66
|
+
|
67
|
+
def editable_by?(user)
|
68
|
+
user.id == self.user_id
|
69
|
+
end
|
70
|
+
|
71
|
+
end # OohAuth::AuthenticatingClient
|
data/app/models/token.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
path = File.expand_path(File.dirname(__FILE__)) / "token"
|
2
|
+
if defined?(DataMapper)
|
3
|
+
require path / "dm_token"
|
4
|
+
#elsif defined?(ActiveRecord)
|
5
|
+
# require path / "ar_password_reset"
|
6
|
+
#elsif defined?(Sequel)
|
7
|
+
# require path / "sq_password_reset"
|
8
|
+
#elsif defined?(RelaxDB)
|
9
|
+
# require path / "relaxdb_password_reset"
|
10
|
+
else
|
11
|
+
raise RuntimeError, "Datamapper is a dependency of the slice at this time."
|
12
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
=begin
|
2
|
+
Token model
|
3
|
+
|
4
|
+
A token is a stored authorisation allowing an authenticating client to:
|
5
|
+
|
6
|
+
1. Get a *request key*. This is done by creating an unactivated token belonging to the authenticating client which has a _request key_.
|
7
|
+
2. *Request access*. This is done by directing the user to a URL unique to the given request key, presenting them with a form.
|
8
|
+
The user must be logged in through direct means in order to grant access.
|
9
|
+
3. Getting an *access key* which is a property of the now-activated token.
|
10
|
+
|
11
|
+
=end
|
12
|
+
|
13
|
+
class OohAuth::Token
|
14
|
+
include DataMapper::Resource
|
15
|
+
|
16
|
+
property :id, Serial
|
17
|
+
property :user_id, Integer, :writer=>:protected
|
18
|
+
property :authenticating_client_id, Integer, :writer=>:protected
|
19
|
+
|
20
|
+
# Expiry date will always be respected. You cannot authenticate using an expired token, and nor can you
|
21
|
+
# convert an expired request key into an access key.
|
22
|
+
property :expires, DateTime
|
23
|
+
property :created_at, DateTime
|
24
|
+
property :permissions, String
|
25
|
+
|
26
|
+
property :token_key, String, :writer=>:private, :index=>true
|
27
|
+
property :activated, Boolean, :writer=>:private, :index=>true, :default=>false
|
28
|
+
property :secret, String, :writer=>:private
|
29
|
+
|
30
|
+
validates_is_unique :token_key
|
31
|
+
validates_present :secret
|
32
|
+
validates_present :authenticating_client
|
33
|
+
validates_with_method :permissions, :permissions_valid?
|
34
|
+
|
35
|
+
belongs_to :authenticating_client, :class_name=>"OohAuth::AuthenticatingClient", :child_key=>[:authenticating_client_id]
|
36
|
+
belongs_to :user, :class_name=>Merb::Authentication.user_class.to_s, :child_key=>[:user_id]
|
37
|
+
|
38
|
+
before :valid?, :create_token_key_if_not_present
|
39
|
+
before :valid?, :create_secret_if_not_present
|
40
|
+
|
41
|
+
# Authenticates a client on behalf of a user given the API parameters sent by the client
|
42
|
+
# in the given API request. Returns the user on successful authentication, or false in
|
43
|
+
# the event of a failure to authenticate. If the user was since deleted, NIL will be
|
44
|
+
# returned.
|
45
|
+
def self.authenticate!(consumer_key, access_key)
|
46
|
+
auth = first('authenticating_client.api_key'=>consumer_key, :token_key=>access_key, :activated=>true, :expires.gt=>DateTime.now)
|
47
|
+
return (auth)? auth.user : nil
|
48
|
+
end
|
49
|
+
|
50
|
+
# FIXME the relationship helper should be sorting this. Something to do with the variable class.
|
51
|
+
def user
|
52
|
+
Merb::Authentication.user_class.get(user_id)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Tentatively create a request_key for a given client, not yet tied to a user.
|
56
|
+
def self.create_request_key(authenticating_client, expires=1.hour.since)
|
57
|
+
o = new(:authenticating_client=>authenticating_client, :expires=>expires)
|
58
|
+
o.save or raise RuntimeError, "OAuth request key failed to save with errors: #{o.errors.inspect}"
|
59
|
+
o
|
60
|
+
end
|
61
|
+
|
62
|
+
# Fetch a request_key given the request_key code
|
63
|
+
def self.get_request_key_for_client(client, request_key)
|
64
|
+
first :token_key=>request_key, :authenticating_client_id=>client.id, :expires.gt=>DateTime.now, :activated=>false
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.get_token(token)
|
68
|
+
first :token_key=>token
|
69
|
+
end
|
70
|
+
|
71
|
+
# Make this Authentication object active by generating an access key against it.
|
72
|
+
# You may optionally specify a new expiry date/time for the access key.
|
73
|
+
def activate!(with_user, expire_on=nil, permissions=nil)
|
74
|
+
if authenticating_client and with_user
|
75
|
+
self.activated = true
|
76
|
+
self.expires = (expire_on || 1.year.since)
|
77
|
+
self.permissions = (permissions || OohAuth[:default_permissions])
|
78
|
+
self.user_id = with_user.id
|
79
|
+
generate_token_key!
|
80
|
+
return save
|
81
|
+
else
|
82
|
+
return false
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Checks to see if this Authentication is activated - if there is an access key defined, then
|
87
|
+
# true is returned.
|
88
|
+
#def activated?
|
89
|
+
# ac
|
90
|
+
#end
|
91
|
+
|
92
|
+
# Assigns a valid, unique request_key to the object if one is not already defined.
|
93
|
+
def create_token_key_if_not_present
|
94
|
+
generate_token_key! if token_key.blank?
|
95
|
+
end
|
96
|
+
|
97
|
+
def create_secret_if_not_present
|
98
|
+
self.secret ||= OohAuth::KeyGenerators::Alphanum.gen(30)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Generates a valid, unique access_key which the client can use to authenticate with in future,
|
102
|
+
# and applies it to the object.
|
103
|
+
def generate_token_key!
|
104
|
+
while (token_key.blank? or self.class.first(:token_key=>token_key)) do
|
105
|
+
self.token_key = OohAuth::KeyGenerators::Alphanum.gen(30)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns true if the given user is the owner of this object.
|
110
|
+
def editable_by_user?(user)
|
111
|
+
return user.id == user_id
|
112
|
+
end
|
113
|
+
|
114
|
+
# Returns the permissions for this particular token, or the :default_permissions if not set.
|
115
|
+
def permissions
|
116
|
+
attribute_get(:permissions) or OohAuth[:default_permissions]
|
117
|
+
end
|
118
|
+
|
119
|
+
# Returns true if the set permissions are a valid value according to the keys of the slice's :client_permission_levels hash.
|
120
|
+
def permissions_valid?
|
121
|
+
OohAuth[:client_permission_levels].keys.include?(permissions.to_sym)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Transformation - returns a hash representing this object, ready to be converted to XML, JSON or YAML.
|
125
|
+
def to_hash
|
126
|
+
if activated?
|
127
|
+
{
|
128
|
+
:access_key=>{
|
129
|
+
:token=>token_key,
|
130
|
+
:secret=>secret,
|
131
|
+
:expires=>expires
|
132
|
+
}
|
133
|
+
}
|
134
|
+
else
|
135
|
+
{
|
136
|
+
:request_key=>{
|
137
|
+
:token=>token_key,
|
138
|
+
:secret=>secret,
|
139
|
+
:expires=>expires
|
140
|
+
}
|
141
|
+
}
|
142
|
+
end
|
143
|
+
end
|
144
|
+
# FIXME why is to_xml not available?
|
145
|
+
def to_xml; (activated?)? "<access-key><token>#{token_key}</token><secret>#{secret}</secret><expires>#{expires}</expires></access-key>" : "<request-key><token>#{token_key}</token><secret>#{secret}</secret><expires>#{expires}</expires></request-key>"; end
|
146
|
+
def to_json; to_hash.to_json; end
|
147
|
+
def to_yaml; to_hash.to_yaml; end
|
148
|
+
|
149
|
+
|
150
|
+
end
|