hadley 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.
- 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: []
|