oauth20 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +40 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/lib/oauth20/access_token.rb +71 -0
- data/lib/oauth20/auth_code.rb +50 -0
- data/lib/oauth20/auth_error.rb +28 -0
- data/lib/oauth20/auth_request.rb +62 -0
- data/lib/oauth20/auth_response.rb +31 -0
- data/lib/oauth20/client.rb +29 -0
- data/lib/oauth20/storage.rb +30 -0
- data/lib/oauth20/storages/mysql_strategy.rb +22 -0
- data/lib/oauth20/storages/redis_strategy.rb +137 -0
- data/lib/oauth20/storages/strategy.rb +55 -0
- data/lib/oauth20/token_request.rb +65 -0
- data/lib/oauth20/token_response.rb +13 -0
- data/lib/oauth20/user.rb +22 -0
- data/lib/oauth20/utils.rb +12 -0
- data/lib/oauth20.rb +15 -0
- data/spec/integration/auth_request_spec.rb +29 -0
- data/spec/oauth2/access_token_spec.rb +87 -0
- data/spec/oauth2/auth_code_spec.rb +60 -0
- data/spec/oauth2/auth_request_spec.rb +84 -0
- data/spec/oauth2/auth_response_spec.rb +33 -0
- data/spec/oauth2/client_spec.rb +44 -0
- data/spec/oauth2/storage_spec.rb +15 -0
- data/spec/oauth2/storages/redis_strategy_spec.rb +174 -0
- data/spec/oauth2/token_request_spec.rb +122 -0
- data/spec/oauth2/token_response_spec.rb +22 -0
- data/spec/oauth2/user_spec.rb +5 -0
- data/spec/oauth2/utils_spec.rb +10 -0
- data/spec/oauth20_spec.rb +0 -0
- data/spec/spec_helper.rb +15 -0
- metadata +275 -0
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.1.2)
|
5
|
+
git (1.2.5)
|
6
|
+
jeweler (1.6.4)
|
7
|
+
bundler (~> 1.0)
|
8
|
+
git (>= 1.2.5)
|
9
|
+
rake
|
10
|
+
json (1.6.1)
|
11
|
+
metaclass (0.0.1)
|
12
|
+
mocha (0.10.0)
|
13
|
+
metaclass (~> 0.0.1)
|
14
|
+
rake (0.8.7)
|
15
|
+
rcov (0.9.9)
|
16
|
+
redis (2.2.2)
|
17
|
+
rspec (2.3.0)
|
18
|
+
rspec-core (~> 2.3.0)
|
19
|
+
rspec-expectations (~> 2.3.0)
|
20
|
+
rspec-mocks (~> 2.3.0)
|
21
|
+
rspec-core (2.3.1)
|
22
|
+
rspec-expectations (2.3.0)
|
23
|
+
diff-lcs (~> 1.1.2)
|
24
|
+
rspec-mocks (2.3.0)
|
25
|
+
shoulda (2.11.3)
|
26
|
+
timecop (0.3.5)
|
27
|
+
|
28
|
+
PLATFORMS
|
29
|
+
ruby
|
30
|
+
|
31
|
+
DEPENDENCIES
|
32
|
+
bundler (~> 1.0.0)
|
33
|
+
jeweler (~> 1.6.4)
|
34
|
+
json
|
35
|
+
mocha
|
36
|
+
rcov
|
37
|
+
redis
|
38
|
+
rspec (~> 2.3.0)
|
39
|
+
shoulda
|
40
|
+
timecop
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Petr Janda
|
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/README.rdoc
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
= oauth20
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Contributing to oauth20
|
6
|
+
|
7
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
8
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
9
|
+
* Fork the project
|
10
|
+
* Start a feature/bugfix branch
|
11
|
+
* Commit and push until you are happy with your contribution
|
12
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
13
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2011 Petr Janda. See LICENSE.txt for
|
18
|
+
further details.
|
19
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "oauth20"
|
18
|
+
gem.homepage = "http://github.com/petrjanda/oauth20"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = "OAuth 2.0"
|
21
|
+
gem.description = "OAuth 2.0"
|
22
|
+
gem.email = "petrjanda@me.com"
|
23
|
+
gem.authors = ["Petr Janda"]
|
24
|
+
gem.add_dependency 'timecop'
|
25
|
+
gem.add_dependency 'json'
|
26
|
+
gem.add_dependency 'redis'
|
27
|
+
# dependencies defined in Gemfile
|
28
|
+
end
|
29
|
+
Jeweler::RubygemsDotOrgTasks.new
|
30
|
+
|
31
|
+
require 'rspec/core'
|
32
|
+
require 'rspec/core/rake_task'
|
33
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
34
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
35
|
+
end
|
36
|
+
|
37
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
38
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
39
|
+
spec.rcov = true
|
40
|
+
end
|
41
|
+
|
42
|
+
task :default => :spec
|
43
|
+
|
44
|
+
require 'rake/rdoctask'
|
45
|
+
Rake::RDocTask.new do |rdoc|
|
46
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
47
|
+
|
48
|
+
rdoc.rdoc_dir = 'rdoc'
|
49
|
+
rdoc.title = "oauth20 #{version}"
|
50
|
+
rdoc.rdoc_files.include('README*')
|
51
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
52
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.1
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module OAuth2
|
2
|
+
|
3
|
+
# Access token represents aceess created on behalf of specific user
|
4
|
+
# which first successfully authenticated with authroization server.
|
5
|
+
# It can be used to access resource server protected resources
|
6
|
+
# until it expires.
|
7
|
+
#
|
8
|
+
class AccessToken
|
9
|
+
attr_reader :client_key, :user_id, :expires_in, :expires_at, :created_at,
|
10
|
+
:scope, :key, :token_type
|
11
|
+
|
12
|
+
# Default timeout in seconds for access token expiration.
|
13
|
+
EXPIRES_IN = 3600
|
14
|
+
|
15
|
+
# Beared token type.
|
16
|
+
TYPE_BEARED = 'Beared'
|
17
|
+
|
18
|
+
# Initialize new access token instance with given attributes.
|
19
|
+
#
|
20
|
+
# @param [OAuth2::Client] Client for which the token is created.
|
21
|
+
# @param [OAuth2::User] User on which behalf token is created.
|
22
|
+
# @param [Hash] Hash of additional options.
|
23
|
+
#
|
24
|
+
def initialize(data)
|
25
|
+
@client_key = data[:client_key]
|
26
|
+
@user_id = data[:user_id]
|
27
|
+
@expires_in = data[:expires_in] || EXPIRES_IN
|
28
|
+
@created_at = data[:created_at] || Time.now
|
29
|
+
@expires_at = data[:expires_at] || Time.now + EXPIRES_IN
|
30
|
+
@scope = data[:scope]
|
31
|
+
@key = data[:key] || OAuth2::Utils.generate_key
|
32
|
+
@token_type = data[:token_type] || TYPE_BEARED
|
33
|
+
end
|
34
|
+
|
35
|
+
# Check if the access token is valid to be used to access protected
|
36
|
+
# resource on the server.
|
37
|
+
#
|
38
|
+
def expired?
|
39
|
+
Time.now >= @expires_at
|
40
|
+
end
|
41
|
+
|
42
|
+
# Revoke the access token. Its no longer valid to be used to access protected
|
43
|
+
# resources.
|
44
|
+
#
|
45
|
+
def revoke!
|
46
|
+
@expires_at = Time.now
|
47
|
+
save
|
48
|
+
end
|
49
|
+
|
50
|
+
# Return the token description data according to oauth protocol specification.
|
51
|
+
# Rest of token attributes is avoided.
|
52
|
+
#
|
53
|
+
def to_json
|
54
|
+
data = {
|
55
|
+
'access_token' => @key,
|
56
|
+
'expires_in' => @expires_in,
|
57
|
+
'token_type' => @token_type
|
58
|
+
}
|
59
|
+
|
60
|
+
data.to_json
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.find_by_key(key)
|
64
|
+
Storage.instance.access_token_find_by_key(key)
|
65
|
+
end
|
66
|
+
|
67
|
+
def save
|
68
|
+
Storage.instance.access_token_save(self)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module OAuth2
|
2
|
+
|
3
|
+
# Authorization code class represents short-lived authorization for user
|
4
|
+
# which should be exchanged for access token until it expires.
|
5
|
+
#
|
6
|
+
class AuthCode
|
7
|
+
attr_reader :client_key, :user_id, :key, :created_at, :expires_at, :access_token
|
8
|
+
|
9
|
+
# Expires in 10 minutes.
|
10
|
+
EXPIRES_IN = 10 * 60
|
11
|
+
|
12
|
+
# Initialize new authorization code.
|
13
|
+
#
|
14
|
+
# @param [OAuth2::User] User which signed authorization request.
|
15
|
+
# @param [OAuth2::Client] Client used to get the auth code.
|
16
|
+
#
|
17
|
+
def initialize(data)
|
18
|
+
@key = data[:key]
|
19
|
+
@created_at = data[:created_at] || Time.now
|
20
|
+
@expires_at = data[:expires_at] || Time.now + EXPIRES_IN
|
21
|
+
@access_token = data[:access_token]
|
22
|
+
@user_id = data[:user_id]
|
23
|
+
@client_key = data[:client_key]
|
24
|
+
end
|
25
|
+
|
26
|
+
# Check if the authorization code is still valid to generate new access
|
27
|
+
# token.
|
28
|
+
#
|
29
|
+
def expired?
|
30
|
+
Time.now > @expires_at
|
31
|
+
end
|
32
|
+
|
33
|
+
def used?
|
34
|
+
not @access_token.nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
def invalidate(access_token)
|
38
|
+
@access_token = access_token.key
|
39
|
+
save
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.find_by_key(key)
|
43
|
+
Storage.instance.auth_code_find_by_key(key)
|
44
|
+
end
|
45
|
+
|
46
|
+
def save
|
47
|
+
Storage.instance.auth_code_save(self)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module OAuth2
|
2
|
+
|
3
|
+
ERROR_UNSUPPORTED_GRANT_TYPE = 'unsupported_grant_type'
|
4
|
+
ERROR_INVALID_CLIENT = 'invalid_client'
|
5
|
+
ERROR_INVALID_GRANT = 'invalid_grant'
|
6
|
+
|
7
|
+
ERROR_INVALID_REQUEST = 'invalid_request'
|
8
|
+
ERROR_UNAUTHORIZED_CLIENT = 'unauthorized_client'
|
9
|
+
ERROR_ACCESS_DENIED = 'access_denied'
|
10
|
+
ERROR_UNSUPPORTED_RESPONSE_TYPE = 'unsupported_response_type'
|
11
|
+
ERROR_INVALID_SCOPE = 'invalid_scope'
|
12
|
+
ERROR_SERVER_ERROR = 'server_error'
|
13
|
+
ERROR_TEMPORARILY_UNAVAILABLE = 'temporarily_unavailable'
|
14
|
+
|
15
|
+
class AuthError < RuntimeError
|
16
|
+
attr_reader :description
|
17
|
+
|
18
|
+
def initialize(message, description = nil)
|
19
|
+
super(message)
|
20
|
+
@description = description
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_error_info
|
24
|
+
desc = "&error_description=#{self.description}" unless description.nil?
|
25
|
+
"error=#{message}#{desc}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module OAuth2
|
2
|
+
|
3
|
+
# Class to represent incoming authorization request.
|
4
|
+
#
|
5
|
+
class AuthRequest
|
6
|
+
attr_reader :user, :client, :client_id, :response_type, :redirect_uri, :scope, :state
|
7
|
+
attr_writer :user
|
8
|
+
|
9
|
+
# Initialize OAuth flow request with given attributes.
|
10
|
+
#
|
11
|
+
# @param [String] Unique client identifier.
|
12
|
+
# @param [String] Type of the response expected.
|
13
|
+
# @param [Hash] Additional hash of commands (recirect_uri, scope, state).
|
14
|
+
#
|
15
|
+
def initialize(client_key, response_type, options = {})
|
16
|
+
@client_id = client_key
|
17
|
+
@response_type = response_type
|
18
|
+
@redirect_uri = options[:redirect_uri] || nil
|
19
|
+
@scope = options[:scope] || nil
|
20
|
+
@state = options[:state] || nil
|
21
|
+
|
22
|
+
validate!
|
23
|
+
end
|
24
|
+
|
25
|
+
# Get the response object. Its gonna raise error unless user was stored
|
26
|
+
# to the request. That should happen after user had used valid credentials
|
27
|
+
# to login to authorization server.
|
28
|
+
#
|
29
|
+
# @throw [AuthError] Exception thrown in case of any error.
|
30
|
+
# @return [AuthResponse] AuthResponse object with all necessary attributes.
|
31
|
+
#
|
32
|
+
def response
|
33
|
+
raise AuthError.new(OAuth2::ERROR_ACCESS_DENIED) unless @user
|
34
|
+
|
35
|
+
AuthResponse.new(self)
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
# Validate if the request parameters match to the protocol specification.
|
40
|
+
# @throw [AuthError] Exception thrown in case of any error.
|
41
|
+
#
|
42
|
+
def validate!
|
43
|
+
unless @response_type && @client_id
|
44
|
+
raise AuthError.new(OAuth2::ERROR_INVALID_REQUEST)
|
45
|
+
end
|
46
|
+
|
47
|
+
@client = Client.find_by_key(@client_id)
|
48
|
+
raise AuthError.new(OAuth2::ERROR_INVALID_CLIENT) unless @client
|
49
|
+
|
50
|
+
#if @redirect_uri && @client.redirect_uri
|
51
|
+
# raise AuthError.new(OAuth2::ERROR_INVALID_REQUEST) unless @redirect_uri == @client.redirect_uri
|
52
|
+
#end
|
53
|
+
|
54
|
+
@redirect_uri = @client.redirect_uri unless @redirect_uri && @client.redirect_uri
|
55
|
+
|
56
|
+
|
57
|
+
unless @response_type == 'code'
|
58
|
+
raise AuthError.new(OAuth2::ERROR_UNSUPPORTED_RESPONSE_TYPE)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module OAuth2
|
2
|
+
# Authorization code response. It contains all the important data
|
3
|
+
# to build response with new authorization code.
|
4
|
+
#
|
5
|
+
class AuthResponse
|
6
|
+
|
7
|
+
# Initialize new authorization response.
|
8
|
+
#
|
9
|
+
# @params [OAuth2::AuthRequest] Request for the auth code.
|
10
|
+
#
|
11
|
+
def initialize(request)
|
12
|
+
@scope = request.scope
|
13
|
+
@state = request.state
|
14
|
+
@redirect_uri = request.redirect_uri
|
15
|
+
|
16
|
+
@code = OAuth2::AuthCode.new({
|
17
|
+
:user_id => request.user.id,
|
18
|
+
:client_key => request.client.key,
|
19
|
+
:key => OAuth2::Utils.generate_key
|
20
|
+
})
|
21
|
+
|
22
|
+
@code.save
|
23
|
+
end
|
24
|
+
|
25
|
+
# Get the redirect URI based on response attributes.
|
26
|
+
#
|
27
|
+
def to_url
|
28
|
+
"#{@redirect_uri}?code=#{@code.key}&state=#{@state}&scope=#{@scope}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module OAuth2
|
2
|
+
class Client
|
3
|
+
attr_reader :name, :key, :secret, :redirect_uri, :client_type
|
4
|
+
|
5
|
+
def initialize(name, options = {})
|
6
|
+
@name = name
|
7
|
+
@client_type = 'confidential'
|
8
|
+
@key = options[:key] || OAuth2::Utils.generate_key
|
9
|
+
@secret = options[:secret] || OAuth2::Utils.generate_key
|
10
|
+
@redirect_uri = options[:redirect_uri] || nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_access_token(user)
|
14
|
+
OAuth2::AccessToken.new({:client_key => key, :user_id => user.id})
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.find_by_key(key)
|
18
|
+
OAuth2::Storage.instance.client_find_by_key(key)
|
19
|
+
end
|
20
|
+
|
21
|
+
def save
|
22
|
+
OAuth2::Storage.instance.client_save(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.all
|
26
|
+
OAuth2::Storage.instance.client_all()
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'singleton'
|
3
|
+
|
4
|
+
module OAuth2
|
5
|
+
class Storage
|
6
|
+
include Singleton
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def_delegators :@strategy,
|
10
|
+
:access_token_save,
|
11
|
+
:access_token_find_by_key,
|
12
|
+
:auth_code_save,
|
13
|
+
:auth_code_find_by_key,
|
14
|
+
:client_save,
|
15
|
+
:client_find_by_key,
|
16
|
+
:client_find_by_secret,
|
17
|
+
:client_all,
|
18
|
+
:user_find_by_email,
|
19
|
+
:user_all,
|
20
|
+
:user_save
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@strategy = OAuth2::Storages::Strategy.new
|
24
|
+
end
|
25
|
+
|
26
|
+
def strategy=(strategy)
|
27
|
+
@strategy = strategy
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#module OAuth2
|
2
|
+
# module Storages
|
3
|
+
# class MySQLStrategy < Strategy
|
4
|
+
# DB = 'oauth20'
|
5
|
+
# USER = 'root'
|
6
|
+
# HOST = 'localhost'
|
7
|
+
#
|
8
|
+
# def client_save(client)
|
9
|
+
# db = Mysql2::Client.new(:database => 'oauth2', :host => HOST, :username => USER)
|
10
|
+
#
|
11
|
+
# name = db.escape(client.name)
|
12
|
+
# key = db.escape(client.key)
|
13
|
+
# secret = db.escape(client.secret)
|
14
|
+
# redirect_uri = db.escape(client.redirect_uri) if client.redirect_uri
|
15
|
+
# client_type = db.escape(client.client_type) if client.client_type
|
16
|
+
#
|
17
|
+
# results = db.query("INSERT INTO clients(`name`, `key`, `secret`, `redirect_uri`, `client_type`) VALUES ('#{name}', '#{key}', '#{secret}', '#{redirect_uri}', '#{client_type}')")
|
18
|
+
# pp results
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require "redis"
|
2
|
+
require 'json'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module OAuth2
|
6
|
+
module Storages
|
7
|
+
class RedisStrategy < Strategy
|
8
|
+
@@redis = nil
|
9
|
+
|
10
|
+
def initialize(uri)
|
11
|
+
@@uri = URI.parse(uri)
|
12
|
+
end
|
13
|
+
|
14
|
+
def redis
|
15
|
+
@@redis ||= Redis.new(:host => @@uri.host, :port => @@uri.port, :password => @@uri.password)
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
# ACCESS TOKEN
|
20
|
+
#
|
21
|
+
|
22
|
+
def access_token_save(access_token)
|
23
|
+
data = {
|
24
|
+
:client_key => access_token.client_key,
|
25
|
+
:user_id => access_token.user_id,
|
26
|
+
:expires_in => access_token.expires_in,
|
27
|
+
:created_at => access_token.created_at.to_i,
|
28
|
+
:expires_at => access_token.expires_at.to_i,
|
29
|
+
:scope => access_token.scope,
|
30
|
+
:key => access_token.key,
|
31
|
+
:token_type => access_token.token_type
|
32
|
+
}
|
33
|
+
|
34
|
+
redis.set "access_token:#{access_token.key}", data.to_json
|
35
|
+
end
|
36
|
+
|
37
|
+
def access_token_find_by_key(key)
|
38
|
+
data = JSON.parse(redis.get("access_token:#{key}"))
|
39
|
+
data['expires_at'] = Time.at(data['expires_at'])
|
40
|
+
data['created_at'] = Time.at(data['created_at'])
|
41
|
+
|
42
|
+
AccessToken.new(symbolize(data))
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# AUTHORIZATION CODE
|
47
|
+
#
|
48
|
+
|
49
|
+
def auth_code_save(auth_code)
|
50
|
+
data = {
|
51
|
+
:client_key => auth_code.client_key,
|
52
|
+
:user_id => auth_code.user_id,
|
53
|
+
:created_at => auth_code.created_at.to_i,
|
54
|
+
:expires_at => auth_code.expires_at.to_i,
|
55
|
+
:access_token => auth_code.access_token,
|
56
|
+
:key => auth_code.key
|
57
|
+
}
|
58
|
+
|
59
|
+
redis.set "auth_code:#{auth_code.key}", data.to_json
|
60
|
+
end
|
61
|
+
|
62
|
+
def auth_code_find_by_key(key)
|
63
|
+
data = JSON.parse(redis.get("auth_code:#{key}"))
|
64
|
+
data['expires_at'] = Time.at(data['expires_at'])
|
65
|
+
data['created_at'] = Time.at(data['created_at'])
|
66
|
+
AuthCode.new(symbolize(data))
|
67
|
+
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# CLIENT
|
71
|
+
#
|
72
|
+
|
73
|
+
def client_save(client)
|
74
|
+
data = {
|
75
|
+
:key => client.key,
|
76
|
+
:secret => client.secret,
|
77
|
+
:redirect_uri => client.redirect_uri,
|
78
|
+
:name => client.name
|
79
|
+
}
|
80
|
+
|
81
|
+
redis.set "client:#{client.key}", data.to_json
|
82
|
+
redis.set "client_secret:#{client.secret}:key", client.key
|
83
|
+
end
|
84
|
+
|
85
|
+
def client_find_by_key(key)
|
86
|
+
begin
|
87
|
+
data = JSON.parse(redis.get("client:#{key}"))
|
88
|
+
Client.new(data.delete('name'), symbolize(data))
|
89
|
+
rescue
|
90
|
+
nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def client_find_by_secret(secret)
|
95
|
+
key = redis.get("client_secret:#{secret}:key")
|
96
|
+
client_find_by_key(key)
|
97
|
+
end
|
98
|
+
|
99
|
+
def client_all
|
100
|
+
redis.keys("client:*").map! do |key|
|
101
|
+
client_find_by_key(key.split(':').last)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def user_save(user)
|
106
|
+
data = {
|
107
|
+
:email => user.email,
|
108
|
+
:password => user.password
|
109
|
+
}
|
110
|
+
|
111
|
+
redis.set "user:#{user.email}", data.to_json
|
112
|
+
end
|
113
|
+
|
114
|
+
def user_find_by_email(email)
|
115
|
+
begin
|
116
|
+
data = JSON.parse(redis.get("user:#{email}"))
|
117
|
+
User.new(symbolize(data))
|
118
|
+
rescue
|
119
|
+
nil
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def user_all
|
124
|
+
redis.keys("user:*").map! do |email|
|
125
|
+
user_find_by_email(email.split(':').last)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
def symbolize(hash)
|
133
|
+
Hash[hash.map{|a| [a.first.to_sym, a.last]}]
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module OAuth2
|
2
|
+
module Storages
|
3
|
+
class Strategy
|
4
|
+
|
5
|
+
# Save new access token.
|
6
|
+
#
|
7
|
+
# @param [Auth2::AccessToken] Access token instance.
|
8
|
+
#
|
9
|
+
def access_token_save(access_token)
|
10
|
+
raise NotImplementedError
|
11
|
+
end
|
12
|
+
|
13
|
+
# Find access token by given key.
|
14
|
+
#
|
15
|
+
# @param [String] Access token key to search.
|
16
|
+
#
|
17
|
+
def access_token_find_by_key(key)
|
18
|
+
raise NotImplementedError
|
19
|
+
end
|
20
|
+
|
21
|
+
# Save new authorization code.
|
22
|
+
#
|
23
|
+
# @param [Auth2::AuthCode] Authorization code instance.
|
24
|
+
#
|
25
|
+
def auth_code_save(auth_code)
|
26
|
+
raise NotImplementedError
|
27
|
+
end
|
28
|
+
|
29
|
+
# Find authorization code by the given key.
|
30
|
+
#
|
31
|
+
# @param [String] Authorization code key.
|
32
|
+
#
|
33
|
+
def auth_code_find_by_key(key)
|
34
|
+
raise NotImplementedError
|
35
|
+
end
|
36
|
+
|
37
|
+
# Save new client.
|
38
|
+
#
|
39
|
+
# @param [Auth2::Client] Client instance.
|
40
|
+
#
|
41
|
+
#
|
42
|
+
def client_save(client)
|
43
|
+
raise NotImplementedError
|
44
|
+
end
|
45
|
+
|
46
|
+
def client_find_by_key(key)
|
47
|
+
raise NotImplementedError
|
48
|
+
end
|
49
|
+
|
50
|
+
def client_find_by_secret(secret)
|
51
|
+
raise NotImplementedError
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|