hadley 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in hadley.gemspec
4
+ gemspec
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,10 @@
1
+ module Hadley::Utils
2
+ extend self
3
+
4
+ def camelize(word, uc_first=true)
5
+ parts = word.split('_')
6
+ assemble = lambda { |head, tail| head + tail.capitalize }
7
+ uc_first ? parts.inject('', &assemble) : parts.inject(&assemble)
8
+ end
9
+
10
+ end
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: []