hadley 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Rakefile +1 -0
- data/config.ru +52 -0
- data/hadley.gemspec +24 -0
- data/lib/hadley.rb +17 -0
- data/lib/hadley/authz.rb +53 -0
- data/lib/hadley/authz/basic.rb +56 -0
- data/lib/hadley/authz/bearer.rb +72 -0
- data/lib/hadley/config.rb +29 -0
- data/lib/hadley/middleware.rb +52 -0
- data/lib/hadley/token_access.rb +27 -0
- data/lib/hadley/utils.rb +10 -0
- metadata +102 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
data/config.ru
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# This file is used by Rack-based servers to start the application.
|
2
|
+
require 'rubygems'
|
3
|
+
require 'hadley'
|
4
|
+
require 'dalli'
|
5
|
+
require 'sinatra/base'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
class ExampleResourceServer < Sinatra::Base
|
9
|
+
include Hadley::Authz
|
10
|
+
|
11
|
+
get '/' do
|
12
|
+
body 'afid-resource-server'
|
13
|
+
end
|
14
|
+
|
15
|
+
get '/v1/current_time' do
|
16
|
+
warden.authenticate!(:afid_user)
|
17
|
+
body({current_time: Time.now.strftime('%Y-%m-%dT%H:%M:%SZ')}.to_json)
|
18
|
+
end
|
19
|
+
|
20
|
+
get '/v1/anonymous_time' do
|
21
|
+
warden.authenticate!(:afid_client)
|
22
|
+
body({current_time: Time.now.strftime('%s')}.to_json)
|
23
|
+
end
|
24
|
+
|
25
|
+
token_store = Hadley::TokenAccess.new(Dalli::Client.new)
|
26
|
+
|
27
|
+
use Rack::Session::Cookie, :secret => 'a8ab10237100f16d12b6c8e574e84b92cc15aecaced04d47251a5f34ffaa0e60'
|
28
|
+
|
29
|
+
use Warden::Manager do |manager|
|
30
|
+
manager.basic(:server) do |basic|
|
31
|
+
basic.hash_credentials = true
|
32
|
+
basic.lookup do |id, secret|
|
33
|
+
[ id, secret] == [
|
34
|
+
'a8ab10237100f16d12b6c8e574e84b92cc15aecaced04d47251a5f34ffaa0e60',
|
35
|
+
'29cd5d3e8f481821422f886055d536c8e395a8aa123700eec74f045b0144e986'
|
36
|
+
] ? id : nil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
manager.bearer(:client) do |bearer|
|
40
|
+
bearer.token_store = token_store
|
41
|
+
bearer.anonymous_allowed = true
|
42
|
+
end
|
43
|
+
manager.bearer(:user) do |bearer|
|
44
|
+
bearer.token_store = token_store
|
45
|
+
bearer.anonymous_allowed = false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
use Hadley::Middleware, token_store: token_store
|
50
|
+
end
|
51
|
+
|
52
|
+
run ExampleResourceServer
|
data/hadley.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'hadley'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'hadley'
|
7
|
+
s.version = Hadley::VERSION
|
8
|
+
s.authors = ['Sean M. Duncan']
|
9
|
+
s.email = ['bitbutcher@gmail.com']
|
10
|
+
s.homepage = 'https://github.com/bitbutcher/hadley'
|
11
|
+
s.summary = 'Rack middleware for AFID(bby-id) resource server implementations'
|
12
|
+
|
13
|
+
s.rubyforge_project = 'hadley'
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = [ 'lib' ]
|
19
|
+
|
20
|
+
s.add_development_dependency 'rspec'
|
21
|
+
s.add_development_dependency 'dalli'
|
22
|
+
s.add_runtime_dependency 'sinatra'
|
23
|
+
s.add_runtime_dependency 'warden'
|
24
|
+
end
|
data/lib/hadley.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
autoload :Rack, 'rack'
|
2
|
+
autoload :Sinatra, 'sinatra/base'
|
3
|
+
autoload :Warden, 'warden'
|
4
|
+
|
5
|
+
module Hadley
|
6
|
+
|
7
|
+
autoload :Authz, 'hadley/authz'
|
8
|
+
autoload :Config, 'hadley/config'
|
9
|
+
autoload :Middleware, 'hadley/middleware'
|
10
|
+
autoload :TokenAccess, 'hadley/token_access'
|
11
|
+
autoload :Utils, 'hadley/utils'
|
12
|
+
|
13
|
+
VERSION = '0.0.1'
|
14
|
+
|
15
|
+
ANONYMOUS_IDENTITY = '0' * 66
|
16
|
+
|
17
|
+
end
|
data/lib/hadley/authz.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
module Hadley::Authz
|
2
|
+
|
3
|
+
autoload :Basic, 'hadley/authz/basic'
|
4
|
+
autoload :Bearer, 'hadley/authz/bearer'
|
5
|
+
|
6
|
+
def warden
|
7
|
+
env['warden']
|
8
|
+
end
|
9
|
+
|
10
|
+
module StrategyBuilder
|
11
|
+
|
12
|
+
def build(name, config)
|
13
|
+
strategy = self.create_strategy(name)
|
14
|
+
self.register_strategy(name, strategy)
|
15
|
+
self.set_config(strategy, config)
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def create_strategy(name)
|
21
|
+
class_name = Hadley::Utils.camelize(name.to_s)
|
22
|
+
if self.const_defined?(class_name)
|
23
|
+
self.const_get(class_name)
|
24
|
+
else
|
25
|
+
self.const_set(class_name, Class.new(self))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def register_strategy(name, strategy)
|
30
|
+
full_name = "afid_#{name}".to_sym
|
31
|
+
if Warden::Strategies[full_name].nil?
|
32
|
+
Warden::Strategies.add(full_name, strategy)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def set_config(strategy, config)
|
37
|
+
strategy.const_set("CONFIG", config) unless strategy.const_defined?("CONFIG")
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
class Strategy < Warden::Strategies::Base
|
43
|
+
extend StrategyBuilder
|
44
|
+
|
45
|
+
def config
|
46
|
+
self.class.const_get("CONFIG")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
Warden::Config.send(:include, Hadley::Authz::Basic::ConfigExtension)
|
51
|
+
Warden::Config.send(:include, Hadley::Authz::Bearer::ConfigExtension)
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Hadley
|
2
|
+
|
3
|
+
module Authz
|
4
|
+
|
5
|
+
module Basic
|
6
|
+
|
7
|
+
class Strategy < Hadley::Authz::Strategy
|
8
|
+
|
9
|
+
def auth
|
10
|
+
@auth ||= Rack::Auth::Basic::Request.new(env)
|
11
|
+
end
|
12
|
+
|
13
|
+
def store?
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
|
+
def authenticate!
|
18
|
+
return unauthorized unless auth.provided? and auth.basic? and auth.credentials
|
19
|
+
credentials = auth.credentials.map do |credential|
|
20
|
+
config.hash_credentials ? Digest::SHA2.new(256).update(credential).to_s : credential
|
21
|
+
end
|
22
|
+
user = config.lookup.call(credentials.first, credentials.last)
|
23
|
+
return user ? success!(auth.credentials.first) : unauthorized
|
24
|
+
end
|
25
|
+
|
26
|
+
def unauthorized
|
27
|
+
custom!(Rack::Response.new([config.fail_message], 401, { 'WWW-Authenticate' => %Q{Basic realm="#{config.realm}"} }))
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
module ConfigExtension
|
33
|
+
|
34
|
+
def basic(name, &block)
|
35
|
+
config = Hadley::Config.new(
|
36
|
+
realm: 'Access Tokens',
|
37
|
+
fail_message: 'Authorization Failed',
|
38
|
+
hash_credentials: false
|
39
|
+
)
|
40
|
+
if block_given?
|
41
|
+
if block.arity == 1
|
42
|
+
yield config
|
43
|
+
else
|
44
|
+
config.instance_eval(&block)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
Hadley::Authz::Basic::Strategy.build(name, config) unless config.lookup.nil?
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Rack::Auth::Bearer
|
2
|
+
|
3
|
+
class Request < Rack::Auth::AbstractRequest
|
4
|
+
|
5
|
+
def bearer?
|
6
|
+
:bearer == scheme
|
7
|
+
end
|
8
|
+
|
9
|
+
def token
|
10
|
+
@token ||= params.split(' ', 2).first
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
module Hadley
|
18
|
+
|
19
|
+
module Authz
|
20
|
+
|
21
|
+
module Bearer
|
22
|
+
|
23
|
+
class Strategy < Hadley::Authz::Strategy
|
24
|
+
|
25
|
+
def auth
|
26
|
+
@auth ||= Rack::Auth::Bearer::Request.new(env)
|
27
|
+
end
|
28
|
+
|
29
|
+
def store?
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
def authenticate!(anonymous_allowed=false)
|
34
|
+
return unauthorized unless auth.provided? and auth.bearer? and auth.token
|
35
|
+
user = config.token_store.get(auth.token)
|
36
|
+
return unauthorized unless user and (!user[:anonymous] or config.anonymous_allowed)
|
37
|
+
success!(user)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def unauthorized
|
43
|
+
custom!(Rack::Response.new([config.fail_message], 401, { 'WWW-Authenticate' => %Q{Bearer realm="#{config.realm}"} }))
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
module ConfigExtension
|
49
|
+
|
50
|
+
def bearer(name, &block)
|
51
|
+
config = Hadley::Config.new(
|
52
|
+
realm: 'Access Tokens',
|
53
|
+
fail_message: 'Authorization Failed',
|
54
|
+
anonymous_allowed: false
|
55
|
+
)
|
56
|
+
if block_given?
|
57
|
+
if block.arity == 1
|
58
|
+
yield config
|
59
|
+
else
|
60
|
+
config.instance_eval(&block)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
Hadley::Authz::Bearer::Strategy.build(name, config) unless config.token_store.nil?
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class Hadley::Config
|
2
|
+
|
3
|
+
def initialize(config={})
|
4
|
+
@config = config
|
5
|
+
end
|
6
|
+
|
7
|
+
def method_missing(name, *args, &block)
|
8
|
+
if block_given?
|
9
|
+
proc(name, &block)
|
10
|
+
elsif name =~ /(.+)=$/
|
11
|
+
set($1, *args, &block)
|
12
|
+
else
|
13
|
+
get(name, &block)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def proc(name, &block)
|
18
|
+
@config[name.to_sym] = block
|
19
|
+
end
|
20
|
+
|
21
|
+
def set(name, *args)
|
22
|
+
@config[name.to_sym] = args.size == 1 ? args.first : args
|
23
|
+
end
|
24
|
+
|
25
|
+
def get(name, &block)
|
26
|
+
@config[name.to_sym]
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
class Hadley::Middleware < Sinatra::Base
|
2
|
+
|
3
|
+
include Hadley::Authz
|
4
|
+
|
5
|
+
attr_reader :confg
|
6
|
+
|
7
|
+
def initialize(app=nil, options={})
|
8
|
+
super(app)
|
9
|
+
@config ||= Hadley::Config.new(options)
|
10
|
+
yield @config if block_given?
|
11
|
+
@tokens = @config.token_store
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
# ------------------------------------------
|
16
|
+
# Routes
|
17
|
+
# ------------------------------------------
|
18
|
+
|
19
|
+
put '/access/tokens/:token' do |token|
|
20
|
+
warden.authenticate!(:afid_server)
|
21
|
+
begin
|
22
|
+
@tokens.put(token, Integer(params.fetch('expires_in')),
|
23
|
+
identity: params.fetch('identity'),
|
24
|
+
client: params.fetch('client')
|
25
|
+
)
|
26
|
+
body 'Token Accepted'
|
27
|
+
rescue => e
|
28
|
+
status 400
|
29
|
+
body e.to_s
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
delete '/access/tokens/:token' do |token|
|
34
|
+
warden.authenticate!(:afid_server)
|
35
|
+
begin
|
36
|
+
@tokens.delete(token)
|
37
|
+
body 'Token Deleted'
|
38
|
+
rescue => e
|
39
|
+
status 400
|
40
|
+
body e.to_s
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# ------------------------------------------
|
45
|
+
# Config
|
46
|
+
# ------------------------------------------
|
47
|
+
|
48
|
+
disable :show_exceptions
|
49
|
+
enable :raise_errors
|
50
|
+
enable :logging
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class Hadley::TokenAccess
|
2
|
+
|
3
|
+
def initialize(store)
|
4
|
+
@store = store
|
5
|
+
end
|
6
|
+
|
7
|
+
def key_for(token)
|
8
|
+
"afid-access-token:#{token}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def get(token)
|
12
|
+
access = @store.get(key_for(token))
|
13
|
+
if access
|
14
|
+
access[:anonymous] = access[:identity] == Hadley::ANONYMOUS_IDENTITY
|
15
|
+
end
|
16
|
+
access
|
17
|
+
end
|
18
|
+
|
19
|
+
def put(token, expires_in, data={})
|
20
|
+
@store.set(key_for(token), data, expires_in)
|
21
|
+
end
|
22
|
+
|
23
|
+
def delete(token)
|
24
|
+
@store.delete(key_for(token))
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/lib/hadley/utils.rb
ADDED
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hadley
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Sean M. Duncan
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-02-12 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &70184778050660 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70184778050660
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: dalli
|
27
|
+
requirement: &70184778049880 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70184778049880
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: sinatra
|
38
|
+
requirement: &70184778065160 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70184778065160
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: warden
|
49
|
+
requirement: &70184778064540 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70184778064540
|
58
|
+
description:
|
59
|
+
email:
|
60
|
+
- bitbutcher@gmail.com
|
61
|
+
executables: []
|
62
|
+
extensions: []
|
63
|
+
extra_rdoc_files: []
|
64
|
+
files:
|
65
|
+
- .gitignore
|
66
|
+
- Gemfile
|
67
|
+
- Rakefile
|
68
|
+
- config.ru
|
69
|
+
- hadley.gemspec
|
70
|
+
- lib/hadley.rb
|
71
|
+
- lib/hadley/authz.rb
|
72
|
+
- lib/hadley/authz/basic.rb
|
73
|
+
- lib/hadley/authz/bearer.rb
|
74
|
+
- lib/hadley/config.rb
|
75
|
+
- lib/hadley/middleware.rb
|
76
|
+
- lib/hadley/token_access.rb
|
77
|
+
- lib/hadley/utils.rb
|
78
|
+
homepage: https://github.com/bitbutcher/hadley
|
79
|
+
licenses: []
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ! '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ! '>='
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
requirements: []
|
97
|
+
rubyforge_project: hadley
|
98
|
+
rubygems_version: 1.8.15
|
99
|
+
signing_key:
|
100
|
+
specification_version: 3
|
101
|
+
summary: Rack middleware for AFID(bby-id) resource server implementations
|
102
|
+
test_files: []
|