moguera-authentication 0.0.1
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.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/.travis.yml +7 -0
- data/Gemfile +18 -0
- data/LICENSE.txt +22 -0
- data/README.md +144 -0
- data/Rakefile +14 -0
- data/lib/moguera/authentication/exception.rb +14 -0
- data/lib/moguera/authentication/request.rb +56 -0
- data/lib/moguera/authentication/version.rb +5 -0
- data/lib/moguera/authentication.rb +78 -0
- data/moguera-authentication.gemspec +24 -0
- data/sample/client.rb +43 -0
- data/sample/server.rb +56 -0
- data/spec/authentication/request_spec.rb +99 -0
- data/spec/authentication_spec.rb +134 -0
- data/spec/shared_context.rb +22 -0
- data/spec/spec_helper.rb +40 -0
- metadata +109 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: fc3dfe649673b10bebc29e516c4ce648b1dd04e1
|
4
|
+
data.tar.gz: 790de69338563c94b9a52eacef08b7c1d60297c1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c3c1a8c8013d6e34740591ca912bb9bbdb8d72089f846b882d9b9dff569a94f6660c82c222a8471b0d3cebffe865a8d7bedbf14862b426a991175d60b0602416
|
7
|
+
data.tar.gz: ecc22756a412c60b51b0885f4e2a12c6bd43ea065da697b5baa320b730c211c338aea75a88d525ccfd3927854767667809cdcc4c6728f34e39e8261bdf5d0275
|
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
service_name: travis-ci
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in moguera-authentication.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
group :test do
|
7
|
+
gem 'timecop'
|
8
|
+
gem 'pry'
|
9
|
+
gem 'pry-byebug'
|
10
|
+
gem 'simplecov'
|
11
|
+
gem 'simplecov-rcov'
|
12
|
+
gem 'coveralls'
|
13
|
+
end
|
14
|
+
|
15
|
+
group :development do
|
16
|
+
gem 'rest-client'
|
17
|
+
gem 'sinatra'
|
18
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 hiro-su
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
# Moguera::Authentication
|
2
|
+
|
3
|
+
Simple REST API Authentication.
|
4
|
+
|
5
|
+
[](https://travis-ci.org/moguera/moguera-authentication)
|
6
|
+
[](https://coveralls.io/r/moguera/moguera-authentication?branch=master)
|
7
|
+
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'moguera-authentication'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install moguera-authentication
|
24
|
+
|
25
|
+
## Authentication Logic
|
26
|
+
Use paramas
|
27
|
+
|
28
|
+
- access_key
|
29
|
+
- secret_access_key
|
30
|
+
- request_pah
|
31
|
+
- request_method
|
32
|
+
- http_date
|
33
|
+
- content_type
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
string_to_isgn = access_key + request_path + request_method + http_date + conetnt_type
|
37
|
+
signature = Baes64.encode64(OpenSSL::HMAC.hexdigest(sha1, secret_access_key, string_to_sign)
|
38
|
+
|
39
|
+
Authorization header
|
40
|
+
Authorization: MOGUERA + " " + access_key + ":" + signature
|
41
|
+
```
|
42
|
+
|
43
|
+
Server check
|
44
|
+
|
45
|
+
1. Same request_token and server_token?
|
46
|
+
2. Expired the request?
|
47
|
+
|
48
|
+
## Usage
|
49
|
+
|
50
|
+
### Server
|
51
|
+
sinatra sample
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
require 'sinatra'
|
55
|
+
require 'moguera/authentication'
|
56
|
+
|
57
|
+
post '/login' do
|
58
|
+
begin
|
59
|
+
request_token = Moguera::Authentication.new(request.env['HTTP_AUTHORIZATION'])
|
60
|
+
user = request_token.authenticate! do |key|
|
61
|
+
key_to_secret = 'secret'
|
62
|
+
|
63
|
+
Moguera::Authentication::Request.new(
|
64
|
+
access_key: key,
|
65
|
+
secret_access_key: key_to_secret,
|
66
|
+
request_path: request.env['REQUEST_PATH'],
|
67
|
+
content_type: request.content_type,
|
68
|
+
http_date: request.env['HTTP_DATE'],
|
69
|
+
request_method: request.request_method
|
70
|
+
)
|
71
|
+
end
|
72
|
+
return user.access_key
|
73
|
+
rescue Moguera::Authentication::ParameterInvalid => e
|
74
|
+
halt 400, "400 Bad Request: #{e.message}\n"
|
75
|
+
rescue Moguera::Authentication::AuthenticationError => e
|
76
|
+
params = %w(
|
77
|
+
token access_key secret_access_key http_date
|
78
|
+
request_method request_path content_type
|
79
|
+
)
|
80
|
+
msg = ["request_tooken: #{e.request_token}"]
|
81
|
+
msg << params.map {|k| "server_#{k}: #{e.server_request.send(k)}" }
|
82
|
+
logger.error msg * "\n"
|
83
|
+
halt 401, "401 Unauthorized: #{e.message}\n"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
```
|
87
|
+
|
88
|
+
### Cilent
|
89
|
+
rest-client sample
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
require 'moguera/authentication'
|
93
|
+
require 'rest-client'
|
94
|
+
require 'time'
|
95
|
+
require 'json'
|
96
|
+
require 'uri'
|
97
|
+
|
98
|
+
url = ARGV[0]
|
99
|
+
abort "Usage: ruby #{__FILE__} http://localhost:4567/login" unless url
|
100
|
+
|
101
|
+
request_path = URI.parse(url).path
|
102
|
+
request_method = 'POST'
|
103
|
+
http_date = Time.now.httpdate
|
104
|
+
content_type = 'application/json'
|
105
|
+
|
106
|
+
request = Moguera::Authentication::Request.new(
|
107
|
+
access_key: 'apikey',
|
108
|
+
secret_access_key: 'secret',
|
109
|
+
request_path: request_path,
|
110
|
+
request_method: request_method,
|
111
|
+
http_date: http_date,
|
112
|
+
content_type: content_type
|
113
|
+
)
|
114
|
+
|
115
|
+
headers = {
|
116
|
+
Authorization: request.token,
|
117
|
+
content_type: content_type,
|
118
|
+
Date: http_date
|
119
|
+
}
|
120
|
+
|
121
|
+
payload = { key: 'value' }.to_json
|
122
|
+
|
123
|
+
begin
|
124
|
+
res = RestClient.post(url, payload, headers)
|
125
|
+
puts res.code
|
126
|
+
puts res.body
|
127
|
+
rescue => e
|
128
|
+
abort e.response
|
129
|
+
end
|
130
|
+
```
|
131
|
+
|
132
|
+
## Contributing
|
133
|
+
|
134
|
+
1. Fork it ( https://github.com/[my-github-username]/moguera-authentication/fork )
|
135
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
136
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
137
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
138
|
+
5. Create a new Pull Request
|
139
|
+
|
140
|
+
## Copyright
|
141
|
+
|
142
|
+
Copyright (c) 2014 hiro-su.
|
143
|
+
|
144
|
+
MIT License
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
begin
|
4
|
+
require "rspec/core/rake_task"
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
7
|
+
t.fail_on_error = true
|
8
|
+
t.rspec_opts = '--format doc'
|
9
|
+
end
|
10
|
+
|
11
|
+
task :default => :spec
|
12
|
+
rescue LoadError
|
13
|
+
# no rspec available
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Moguera
|
2
|
+
class Authentication
|
3
|
+
class AuthenticationError < StandardError
|
4
|
+
attr_accessor :server_request
|
5
|
+
attr_accessor :request_token
|
6
|
+
end
|
7
|
+
|
8
|
+
class ParameterInvalid < StandardError
|
9
|
+
end
|
10
|
+
|
11
|
+
class BlockRequired < StandardError
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Moguera
|
2
|
+
class Authentication
|
3
|
+
class Request
|
4
|
+
attr_accessor :token_prefix
|
5
|
+
|
6
|
+
def initialize(access_key:, secret_access_key:, request_path:, request_method:, http_date:, content_type:)
|
7
|
+
require 'time'
|
8
|
+
require 'openssl'
|
9
|
+
require 'base64'
|
10
|
+
|
11
|
+
@access_key = access_key
|
12
|
+
@secret_access_key = secret_access_key
|
13
|
+
@request_path = request_path
|
14
|
+
@request_method = request_method
|
15
|
+
@http_date = http_date
|
16
|
+
@content_type = content_type
|
17
|
+
|
18
|
+
@token_prefix = token_prefix || 'MOGUERA'
|
19
|
+
|
20
|
+
validate_parameter!
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :access_key, :secret_access_key, :request_path,
|
24
|
+
:request_method, :http_date, :content_type
|
25
|
+
|
26
|
+
def token
|
27
|
+
raise ParameterInvalid, 'Token prefix required.' if @token_prefix.nil?
|
28
|
+
|
29
|
+
@token_prefix + ' ' + @access_key + ':' + signature
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def signature
|
35
|
+
sha1 = OpenSSL::Digest::SHA1.new
|
36
|
+
digest = OpenSSL::HMAC.hexdigest(sha1, @secret_access_key, string_to_sign)
|
37
|
+
Base64.encode64(digest).strip
|
38
|
+
end
|
39
|
+
|
40
|
+
def string_to_sign
|
41
|
+
@access_key + @request_path + @request_method + @http_date + @content_type
|
42
|
+
end
|
43
|
+
|
44
|
+
def validate_parameter!
|
45
|
+
errors = []
|
46
|
+
errors << 'Access Key' unless @access_key
|
47
|
+
errors << 'Secret Access Key' unless @secret_access_key
|
48
|
+
errors << 'Request Path' unless @request_path
|
49
|
+
errors << 'Date Header' unless @http_date
|
50
|
+
errors << 'Content-Type Header' unless @content_type
|
51
|
+
|
52
|
+
raise ParameterInvalid, 'Missing: ' + errors * ', ' unless errors.empty?
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'moguera/authentication/version'
|
2
|
+
require 'moguera/authentication/request'
|
3
|
+
require 'moguera/authentication/exception'
|
4
|
+
|
5
|
+
module Moguera
|
6
|
+
class Authentication
|
7
|
+
attr_accessor :allow_time_interval
|
8
|
+
|
9
|
+
def initialize(request_token = nil)
|
10
|
+
raise AuthenticationError, 'Missing request token.' unless request_token
|
11
|
+
|
12
|
+
@request_token = request_token
|
13
|
+
@allow_time_interval = allow_time_interval || 600
|
14
|
+
end
|
15
|
+
|
16
|
+
def authenticate!(&block)
|
17
|
+
raise BlockRequired, 'Request token required.' unless block_given?
|
18
|
+
|
19
|
+
access_key = extract_access_key_from_token(token: @request_token)
|
20
|
+
server_request = block.call(access_key)
|
21
|
+
|
22
|
+
validate_token!(server_token: server_request.token, request_token: @request_token)
|
23
|
+
validate_time!(request_time: server_request.http_date)
|
24
|
+
|
25
|
+
server_request
|
26
|
+
rescue AuthenticationError => e
|
27
|
+
authentication_error = AuthenticationError.new(e.message)
|
28
|
+
authentication_error.request_token = @request_token
|
29
|
+
authentication_error.server_request = server_request if server_request
|
30
|
+
raise authentication_error
|
31
|
+
end
|
32
|
+
|
33
|
+
def authenticate(&block)
|
34
|
+
authenticate!(&block)
|
35
|
+
|
36
|
+
rescue AuthenticationError
|
37
|
+
false
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def extract_access_key_from_token(token:)
|
43
|
+
begin
|
44
|
+
_, access_key_signature = token.split
|
45
|
+
access_key, _ = access_key_signature.split(':')
|
46
|
+
rescue
|
47
|
+
raise AuthenticationError, 'Invalid token.'
|
48
|
+
end
|
49
|
+
|
50
|
+
access_key
|
51
|
+
end
|
52
|
+
|
53
|
+
def validate_token!(server_token:, request_token:)
|
54
|
+
unless server_token == request_token
|
55
|
+
raise AuthenticationError, 'Mismatch token.'
|
56
|
+
end
|
57
|
+
|
58
|
+
true
|
59
|
+
end
|
60
|
+
|
61
|
+
def validate_time!(request_time:)
|
62
|
+
return true if @allow_time_interval.nil?
|
63
|
+
|
64
|
+
if @allow_time_interval <= 0
|
65
|
+
raise ParameterInvalid, 'Please input a positive value.'
|
66
|
+
end
|
67
|
+
|
68
|
+
rt = Time.parse(request_time).to_i
|
69
|
+
interval = (Time.now.to_i - rt).abs
|
70
|
+
|
71
|
+
if @allow_time_interval <= interval
|
72
|
+
raise AuthenticationError, 'Expired request.'
|
73
|
+
end
|
74
|
+
|
75
|
+
true
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'moguera/authentication/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "moguera-authentication"
|
8
|
+
spec.version = Moguera::Authentication::VERSION
|
9
|
+
spec.authors = ["hiro-su"]
|
10
|
+
spec.email = ["h.sugipon@gmail.com"]
|
11
|
+
spec.summary = %q{Simple REST API Authentication.}
|
12
|
+
spec.description = %q{Simple REST API Authentication.}
|
13
|
+
spec.homepage = "https://github.com/moguera/moguera-authentication"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency "rspec", "~> 3.1.0"
|
24
|
+
end
|
data/sample/client.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
4
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), %w(.. lib)))
|
5
|
+
|
6
|
+
require 'moguera/authentication'
|
7
|
+
require 'rest-client'
|
8
|
+
require 'time'
|
9
|
+
require 'json'
|
10
|
+
require 'uri'
|
11
|
+
|
12
|
+
url = ARGV[0]
|
13
|
+
abort "Usage: ruby #{__FILE__} http://localhost:4567/login" unless url
|
14
|
+
|
15
|
+
request_path = URI.parse(url).path
|
16
|
+
request_method = 'POST'
|
17
|
+
http_date = Time.now.httpdate
|
18
|
+
content_type = 'application/json'
|
19
|
+
|
20
|
+
request = Moguera::Authentication::Request.new(
|
21
|
+
access_key: 'apikey',
|
22
|
+
secret_access_key: 'secret',
|
23
|
+
request_path: request_path,
|
24
|
+
request_method: request_method,
|
25
|
+
http_date: http_date,
|
26
|
+
content_type: content_type
|
27
|
+
)
|
28
|
+
|
29
|
+
headers = {
|
30
|
+
Authorization: request.token,
|
31
|
+
content_type: content_type,
|
32
|
+
Date: http_date
|
33
|
+
}
|
34
|
+
|
35
|
+
payload = { key: 'value' }.to_json
|
36
|
+
|
37
|
+
begin
|
38
|
+
res = RestClient.post(url, payload, headers)
|
39
|
+
puts res.code
|
40
|
+
puts res.body
|
41
|
+
rescue => e
|
42
|
+
abort e.response
|
43
|
+
end
|
data/sample/server.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
4
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), %w(.. lib)))
|
5
|
+
|
6
|
+
require 'sinatra'
|
7
|
+
require 'moguera/authentication'
|
8
|
+
|
9
|
+
post '/login' do
|
10
|
+
begin
|
11
|
+
request_token = Moguera::Authentication.new(request.env['HTTP_AUTHORIZATION'])
|
12
|
+
user = request_token.authenticate! do |key|
|
13
|
+
key_to_secret = 'secret'
|
14
|
+
|
15
|
+
Moguera::Authentication::Request.new(
|
16
|
+
access_key: key,
|
17
|
+
secret_access_key: key_to_secret,
|
18
|
+
request_path: request.env['REQUEST_PATH'],
|
19
|
+
content_type: request.content_type,
|
20
|
+
http_date: request.env['HTTP_DATE'],
|
21
|
+
request_method: request.request_method
|
22
|
+
)
|
23
|
+
end
|
24
|
+
return user.access_key
|
25
|
+
rescue Moguera::Authentication::ParameterInvalid => e
|
26
|
+
halt 400, "400 Bad Request: #{e.message}\n"
|
27
|
+
rescue Moguera::Authentication::AuthenticationError => e
|
28
|
+
params = %w(
|
29
|
+
token access_key secret_access_key http_date
|
30
|
+
request_method request_path content_type
|
31
|
+
)
|
32
|
+
msg = ["request_tooken: #{e.request_token}"]
|
33
|
+
msg << params.map {|k| "server_#{k}: #{e.server_request.send(k)}" }
|
34
|
+
logger.error msg * "\n"
|
35
|
+
halt 401, "401 Unauthorized: #{e.message}\n"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
post '/login2' do
|
40
|
+
request_token = Moguera::Authentication.new(request.env['HTTP_AUTHORIZATION'])
|
41
|
+
user = request_token.authenticate do |key|
|
42
|
+
key_to_secret = 'secret'
|
43
|
+
|
44
|
+
Moguera::Authentication::Request.new(
|
45
|
+
access_key: key,
|
46
|
+
secret_access_key: key_to_secret,
|
47
|
+
request_path: request.env['REQUEST_PATH'],
|
48
|
+
content_type: request.content_type,
|
49
|
+
http_date: request.env['HTTP_DATE'],
|
50
|
+
request_method: request.request_method
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
halt 401, "401 Unauthorized" unless user
|
55
|
+
return user.access_key
|
56
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Moguera::Authentication::Request do
|
4
|
+
include_context 'prepare_auth'
|
5
|
+
|
6
|
+
subject {
|
7
|
+
Moguera::Authentication::Request.new(
|
8
|
+
access_key: access_key,
|
9
|
+
secret_access_key: secret_access_key,
|
10
|
+
request_path: request_path,
|
11
|
+
request_method: request_method,
|
12
|
+
http_date: http_date,
|
13
|
+
content_type: content_type
|
14
|
+
)
|
15
|
+
}
|
16
|
+
|
17
|
+
it 'should be set the token_prefix' do
|
18
|
+
subject.token_prefix = 'TEST'
|
19
|
+
expect(subject.token_prefix).to eq 'TEST'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should be read the token_prefix' do
|
23
|
+
expect(subject.token_prefix).to eq 'MOGUERA'
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should be read the access_key' do
|
27
|
+
expect(subject.access_key).to eq access_key
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should be read the secret_access_key' do
|
31
|
+
expect(subject.secret_access_key).to eq secret_access_key
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should be read the request_path' do
|
35
|
+
expect(subject.request_path).to eq request_path
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should be read the request_method' do
|
39
|
+
expect(subject.request_method).to eq request_method
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should be read the http_date' do
|
43
|
+
expect(subject.http_date).to eq http_date
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should be read the content_type' do
|
47
|
+
expect(subject.content_type).to eq content_type
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#token' do
|
51
|
+
it 'should be display the signature token' do
|
52
|
+
expect(subject.token).to eq request_token
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#signature' do
|
57
|
+
it 'should be create the signature' do
|
58
|
+
_, access_key_signature = subject.token.split
|
59
|
+
_, signature = access_key_signature.split(':')
|
60
|
+
|
61
|
+
expect(subject.send(:signature)).to eq signature
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#string_to_sign' do
|
66
|
+
it 'should be create the string_to_sign' do
|
67
|
+
string_to_sign = access_key + request_path + request_method + http_date + content_type
|
68
|
+
|
69
|
+
expect(subject.send(:string_to_sign)).to eq string_to_sign
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe 'Invalid Parameter' do
|
74
|
+
describe 'request parameter is nil' do
|
75
|
+
it 'should be raise ParameterInvalid Exception' do
|
76
|
+
error = "Missing: Access Key, Secret Access Key, Request Path, Date Header, Content-Type Header"
|
77
|
+
expect {
|
78
|
+
Moguera::Authentication::Request.new(
|
79
|
+
access_key: nil,
|
80
|
+
secret_access_key: nil,
|
81
|
+
request_path: nil,
|
82
|
+
request_method: nil,
|
83
|
+
http_date: nil,
|
84
|
+
content_type: nil
|
85
|
+
)
|
86
|
+
}.to raise_error(Moguera::Authentication::ParameterInvalid, error)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe 'token_prefix is nil' do
|
91
|
+
it 'should be raise ParameterInvalid Exception' do
|
92
|
+
subject.token_prefix = nil
|
93
|
+
expect {
|
94
|
+
subject.token
|
95
|
+
}.to raise_error(Moguera::Authentication::ParameterInvalid, 'Token prefix required.')
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Moguera::Authentication do
|
4
|
+
include_context "prepare_auth"
|
5
|
+
|
6
|
+
subject { Moguera::Authentication.new(request_token) }
|
7
|
+
|
8
|
+
it 'should be set allow_time_interval' do
|
9
|
+
subject.allow_time_interval = 300
|
10
|
+
expect(subject.allow_time_interval).to eq 300
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should be read allow_time_interval' do
|
14
|
+
# default 600s
|
15
|
+
expect(subject.allow_time_interval).to eq 600
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#authenticate!" do
|
19
|
+
it 'should be authenticate' do
|
20
|
+
user = subject.authenticate! do |request_key|
|
21
|
+
Moguera::Authentication::Request.new(
|
22
|
+
access_key: request_key,
|
23
|
+
secret_access_key: secret_access_key,
|
24
|
+
request_path: request_path,
|
25
|
+
request_method: request_method,
|
26
|
+
http_date: http_date,
|
27
|
+
content_type: content_type
|
28
|
+
)
|
29
|
+
end
|
30
|
+
expect(user.access_key).to eq access_key
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'Invalid token' do
|
34
|
+
it 'should be raise AuthenticationError with missing request token message' do
|
35
|
+
expect {
|
36
|
+
Moguera::Authentication.new
|
37
|
+
}.to raise_error(Moguera::Authentication::AuthenticationError, 'Missing request token.')
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should be raise AuthenticationError with invalid token message' do
|
41
|
+
expect {
|
42
|
+
Moguera::Authentication.new('invalid_token').authenticate! { request }
|
43
|
+
}.to raise_error(Moguera::Authentication::AuthenticationError, 'Invalid token.')
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should be raise AuthenticationError with mismatch token message' do
|
47
|
+
expect {
|
48
|
+
Moguera::Authentication.new('Mismatch token:signature').authenticate! { request }
|
49
|
+
}.to raise_error(Moguera::Authentication::AuthenticationError, 'Mismatch token.')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#authenticate' do
|
55
|
+
it 'should be authenticate' do
|
56
|
+
user = subject.authenticate do |request_key|
|
57
|
+
Moguera::Authentication::Request.new(
|
58
|
+
access_key: request_key,
|
59
|
+
secret_access_key: secret_access_key,
|
60
|
+
request_path: request_path,
|
61
|
+
request_method: request_method,
|
62
|
+
http_date: http_date,
|
63
|
+
content_type: content_type
|
64
|
+
)
|
65
|
+
end
|
66
|
+
expect(user.access_key).to eq access_key
|
67
|
+
end
|
68
|
+
|
69
|
+
describe 'Invalid token' do
|
70
|
+
it 'should be return false' do
|
71
|
+
expect(
|
72
|
+
Moguera::Authentication.new('Mismatch token:signature').authenticate { request }
|
73
|
+
).to eq false
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe '#extract_access_key_from_token' do
|
79
|
+
it 'should be return access_key' do
|
80
|
+
expect(
|
81
|
+
subject.send(:extract_access_key_from_token, token: 'TEST access_key:signature')
|
82
|
+
).to eq 'access_key'
|
83
|
+
end
|
84
|
+
|
85
|
+
describe 'Invalid token' do
|
86
|
+
it 'should be raise AuthenticationError with invalid token message' do
|
87
|
+
expect {
|
88
|
+
subject.send(:extract_access_key_from_token, token: 'invalid_token')
|
89
|
+
}.to raise_error(Moguera::Authentication::AuthenticationError, 'Invalid token.')
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe '#validate_token!' do
|
95
|
+
it 'should be validate token' do
|
96
|
+
expect(
|
97
|
+
subject.send(:validate_token!, server_token: 'test_token', request_token: 'test_token')
|
98
|
+
).to eq true
|
99
|
+
end
|
100
|
+
|
101
|
+
describe 'Mismatch token' do
|
102
|
+
it 'should be raise AuthenticationError with mismatch token message' do
|
103
|
+
expect {
|
104
|
+
subject.send(:validate_token!, server_token: 'test1_token', request_token: 'test2_token')
|
105
|
+
}.to raise_error(Moguera::Authentication::AuthenticationError, 'Mismatch token.')
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe '#validate_time!' do
|
111
|
+
it 'should be validate time' do
|
112
|
+
expect(
|
113
|
+
subject.send(:validate_time!, request_time: http_date)
|
114
|
+
).to eq true
|
115
|
+
end
|
116
|
+
|
117
|
+
describe 'Invalid allow_time_interval' do
|
118
|
+
it 'should be raise ParameterInvalid' do
|
119
|
+
subject.allow_time_interval = -1
|
120
|
+
expect {
|
121
|
+
subject.send(:validate_time!, request_time: http_date)
|
122
|
+
}.to raise_error(Moguera::Authentication::ParameterInvalid, 'Please input a positive value.')
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe 'Expired request' do
|
127
|
+
it 'should be raise AuthenticationError' do
|
128
|
+
expect {
|
129
|
+
subject.send(:validate_time!, request_time: (Time.parse(http_date)-1000).httpdate)
|
130
|
+
}.to raise_error(Moguera::Authentication::AuthenticationError, 'Expired request.')
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
RSpec.shared_context 'prepare_auth' do
|
2
|
+
let(:access_key) { 'apikey' }
|
3
|
+
let(:secret_access_key) { 'secret' }
|
4
|
+
let(:request_path) { 'path' }
|
5
|
+
let(:request_method) { 'POST' }
|
6
|
+
let(:content_type) { 'application/json' }
|
7
|
+
let(:now) { Timecop.freeze(Time.now) }
|
8
|
+
let(:http_date) { now.httpdate }
|
9
|
+
let(:request) {
|
10
|
+
Moguera::Authentication::Request.new(
|
11
|
+
access_key: access_key,
|
12
|
+
secret_access_key: secret_access_key,
|
13
|
+
request_path: request_path,
|
14
|
+
request_method: request_method,
|
15
|
+
http_date: http_date,
|
16
|
+
content_type: content_type
|
17
|
+
)
|
18
|
+
}
|
19
|
+
let(:request_token) {
|
20
|
+
request.token
|
21
|
+
}
|
22
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
Bundler.require :test
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
5
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), %w(.. lib)))
|
6
|
+
|
7
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
8
|
+
SimpleCov::Formatter::HTMLFormatter,
|
9
|
+
SimpleCov::Formatter::RcovFormatter,
|
10
|
+
Coveralls::SimpleCov::Formatter
|
11
|
+
]
|
12
|
+
SimpleCov.start { add_filter 'spec' }
|
13
|
+
|
14
|
+
RSpec.configure do |config|
|
15
|
+
|
16
|
+
# Captures the output for analysis later
|
17
|
+
#
|
18
|
+
# @example Capture `$stderr`
|
19
|
+
#
|
20
|
+
# output = capture(:stderr) { $stderr.puts "this is captured" }
|
21
|
+
#
|
22
|
+
# @param [Symbol] stream `:stdout` or `:stderr`
|
23
|
+
# @yield The block to capture stdout/stderr for.
|
24
|
+
# @return [String] The contents of $stdout or $stderr
|
25
|
+
def capture(stream)
|
26
|
+
begin
|
27
|
+
stream = stream.to_s
|
28
|
+
eval "$#{stream} = StringIO.new"
|
29
|
+
yield
|
30
|
+
result = eval("$#{stream}").string
|
31
|
+
ensure
|
32
|
+
eval("$#{stream} = #{stream.upcase}")
|
33
|
+
end
|
34
|
+
|
35
|
+
result
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
require 'moguera/authentication'
|
40
|
+
require 'shared_context'
|
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: moguera-authentication
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- hiro-su
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-11-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 3.1.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.1.0
|
55
|
+
description: Simple REST API Authentication.
|
56
|
+
email:
|
57
|
+
- h.sugipon@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".coveralls.yml"
|
63
|
+
- ".gitignore"
|
64
|
+
- ".rspec"
|
65
|
+
- ".travis.yml"
|
66
|
+
- Gemfile
|
67
|
+
- LICENSE.txt
|
68
|
+
- README.md
|
69
|
+
- Rakefile
|
70
|
+
- lib/moguera/authentication.rb
|
71
|
+
- lib/moguera/authentication/exception.rb
|
72
|
+
- lib/moguera/authentication/request.rb
|
73
|
+
- lib/moguera/authentication/version.rb
|
74
|
+
- moguera-authentication.gemspec
|
75
|
+
- sample/client.rb
|
76
|
+
- sample/server.rb
|
77
|
+
- spec/authentication/request_spec.rb
|
78
|
+
- spec/authentication_spec.rb
|
79
|
+
- spec/shared_context.rb
|
80
|
+
- spec/spec_helper.rb
|
81
|
+
homepage: https://github.com/moguera/moguera-authentication
|
82
|
+
licenses:
|
83
|
+
- MIT
|
84
|
+
metadata: {}
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
requirements: []
|
100
|
+
rubyforge_project:
|
101
|
+
rubygems_version: 2.2.2
|
102
|
+
signing_key:
|
103
|
+
specification_version: 4
|
104
|
+
summary: Simple REST API Authentication.
|
105
|
+
test_files:
|
106
|
+
- spec/authentication/request_spec.rb
|
107
|
+
- spec/authentication_spec.rb
|
108
|
+
- spec/shared_context.rb
|
109
|
+
- spec/spec_helper.rb
|