carpool 0.1.0

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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Brent Kirby
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,17 @@
1
+ = carpool
2
+
3
+ Description goes here.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
13
+ * Send me a pull request. Bonus points for topic branches.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2010 Brent Kirby. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "carpool"
8
+ gem.summary = %Q{Single Sign On solution for Rack-Based applications}
9
+ gem.description = %Q{Carpool is a single sign on solution for Rack-based applications allowing you to persist sessions across domains.}
10
+ gem.email = "dev@kurbmedia.com"
11
+ gem.homepage = "http://github.com/kurbmedia/carpool"
12
+ gem.authors = ["Brent Kirby"]
13
+ gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
14
+ gem.add_dependency "fast-aes"
15
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
+ end
17
+ Jeweler::GemcutterTasks.new
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
20
+ end
21
+
22
+ require 'rake/testtask'
23
+ Rake::TestTask.new(:test) do |test|
24
+ test.libs << 'lib' << 'test'
25
+ test.pattern = 'test/**/test_*.rb'
26
+ test.verbose = true
27
+ end
28
+
29
+ begin
30
+ require 'rcov/rcovtask'
31
+ Rcov::RcovTask.new do |test|
32
+ test.libs << 'test'
33
+ test.pattern = 'test/**/test_*.rb'
34
+ test.verbose = true
35
+ end
36
+ rescue LoadError
37
+ task :rcov do
38
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
39
+ end
40
+ end
41
+
42
+ task :test => :check_dependencies
43
+
44
+ task :default => :test
45
+
46
+ require 'rake/rdoctask'
47
+ Rake::RDocTask.new do |rdoc|
48
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
49
+
50
+ rdoc.rdoc_dir = 'rdoc'
51
+ rdoc.title = "carpool #{version}"
52
+ rdoc.rdoc_files.include('README*')
53
+ rdoc.rdoc_files.include('lib/**/*.rb')
54
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/carpool.gemspec ADDED
@@ -0,0 +1,62 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{carpool}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Brent Kirby"]
12
+ s.date = %q{2010-08-15}
13
+ s.description = %q{Carpool is a single sign on solution for Rack-based applications allowing you to persist sessions across domains.}
14
+ s.email = %q{dev@kurbmedia.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "carpool.gemspec",
27
+ "init.rb",
28
+ "lib/carpool.rb",
29
+ "lib/carpool/driver.rb",
30
+ "lib/carpool/mixins.rb",
31
+ "lib/carpool/passenger.rb",
32
+ "lib/carpool/seatbelt.rb",
33
+ "test/helper.rb",
34
+ "test/test_carpool.rb"
35
+ ]
36
+ s.homepage = %q{http://github.com/kurbmedia/carpool}
37
+ s.rdoc_options = ["--charset=UTF-8"]
38
+ s.require_paths = ["lib"]
39
+ s.rubygems_version = %q{1.3.7}
40
+ s.summary = %q{Single Sign On solution for Rack-Based applications}
41
+ s.test_files = [
42
+ "test/helper.rb",
43
+ "test/test_carpool.rb"
44
+ ]
45
+
46
+ if s.respond_to? :specification_version then
47
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
48
+ s.specification_version = 3
49
+
50
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
51
+ s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
52
+ s.add_runtime_dependency(%q<fast-aes>, [">= 0"])
53
+ else
54
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
55
+ s.add_dependency(%q<fast-aes>, [">= 0"])
56
+ end
57
+ else
58
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
59
+ s.add_dependency(%q<fast-aes>, [">= 0"])
60
+ end
61
+ end
62
+
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'carpool'
@@ -0,0 +1,93 @@
1
+ require 'net/http'
2
+
3
+ module Carpool
4
+
5
+ class Driver
6
+
7
+ include Carpool::Mixins::Core
8
+
9
+ class << self
10
+
11
+ attr_accessor :site_key
12
+ attr_accessor :unauthorized_uri
13
+
14
+ def passengers
15
+ @passengers ||= []
16
+ end
17
+
18
+ def passenger(url, options = {})
19
+ options[:site_key] ||= Carpool.generate_site_key(url)
20
+ options[:secret] ||= Carpool.generate_site_key(url.reverse)
21
+ passengers << { url => options }
22
+ end
23
+
24
+ def site_key
25
+ @site_key ||= Carpool.generate_site_key(@env['HTTP_HOST'])
26
+ end
27
+
28
+ end
29
+
30
+ def initialize(app)
31
+ @app = app
32
+ yield Carpool::Driver if block_given?
33
+ self
34
+ end
35
+
36
+ def call(env)
37
+
38
+ @env = env
39
+ cookies[:scope] = "driver"
40
+
41
+ # Unless we are trying to authenticate a passenger, just continue through the stack.
42
+ return @app.call(env) unless valid_request? && valid_referrer?
43
+
44
+ # Parse the referring site
45
+ referrer = URI.parse(@env['HTTP_REFERER'])
46
+
47
+ # Unless this domain is listed as a potential passenger, issue a 500.
48
+ unless Carpool::Driver.passengers.collect{ |p| p.keys.first.downcase }.include?(referrer.host)
49
+ return [500, {}, 'Unauthorized request.']
50
+ end
51
+
52
+ # Attempt to find an existing driver session.
53
+ # If one is found, redirect back to the passenger site and include our seatbelt
54
+ # The seatbelt includes two parts:
55
+ # 1) The referring uri, so that Carpool::Passenger on the other end can send the user back to their location one authenticated
56
+ # 2) The session payload. This is an AES encrypted hash of whatever attributes you've made available. The encrypted hash is
57
+ # keyed with the site_key and secret of the referring site for extra security.
58
+ #
59
+ unless cookies[:passenger_token]
60
+
61
+ puts "Carpool::Driver: Redirecting to authentication path.."
62
+ Carpool.auth_attempt = true
63
+ cookies[:redirect_to] = referrer
64
+ response = [301, {'Location' => Carpool::Driver.unauthorized_uri}, 'Redirecting unauthorized user...']
65
+
66
+ else
67
+
68
+ puts "Carpool::Driver: Redirecting to passenger site.."
69
+ cookies[:redirect_to] = referrer
70
+ seatbelt = SeatBelt.new(env).create_payload!
71
+
72
+ response = [301, {'Location' => seatbelt}, 'Approved!']
73
+ Carpool.auth_attempt = false
74
+ cookies[:redirect_to] = false
75
+
76
+ end
77
+
78
+ response
79
+
80
+ end
81
+
82
+ private
83
+
84
+ def valid_referrer?
85
+ !(@env['HTTP_REFERER'].nil? or @env['HTTP_REFERER'].blank?)
86
+ end
87
+
88
+ def valid_request?
89
+ @env['PATH_INFO'].downcase == "/sso/authenticate"
90
+ end
91
+
92
+ end
93
+ end
@@ -0,0 +1,21 @@
1
+ module Carpool
2
+ module Mixins
3
+
4
+ module Core
5
+ def self.included(base)
6
+ base.send :include, InstanceMethods
7
+ end
8
+
9
+ module InstanceMethods
10
+ def session
11
+ @env['rack.session']
12
+ end
13
+
14
+ def cookies
15
+ session['carpool.cookies'] ||= {}
16
+ end
17
+ end
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,52 @@
1
+ require 'net/http'
2
+
3
+ module Carpool
4
+ class Passenger
5
+
6
+ include Carpool::Mixins::Core
7
+
8
+ class << self
9
+
10
+ attr_accessor :driver_uri
11
+ attr_accessor :secret
12
+
13
+ end
14
+
15
+ def initialize(app)
16
+ @app = app
17
+ yield Carpool::Passenger if block_given?
18
+ self
19
+ end
20
+
21
+ def call(env)
22
+ @env = env
23
+ cookies[:scope] = "passenger"
24
+
25
+ # If this isn't an authorize request from the driver, just ignore it.
26
+ return @app.call(env) unless valid_request? && valid_referrer?
27
+
28
+ # If we can't find our payload, then we need to abort.
29
+ params = CGI.parse(env['QUERY_STRING'])
30
+ return [500, {}, 'Invalid seatbelt.'] if params['seatbelt'].nil? or params['seatbelt'].blank?
31
+
32
+ # Set a custom HTTP header for our payload and send the request to the user's /sso/authorize handler.
33
+ env['X-CARPOOL-PAYLOAD'] = params['seatbelt']
34
+ return @app.call(env)
35
+
36
+ end
37
+
38
+ private
39
+
40
+ def valid_request?
41
+ @env['PATH_INFO'] == "/sso/authorize"
42
+ end
43
+
44
+ def valid_referrer?
45
+ return false if @env['HTTP_REFERER'].nil? or @env['HTTP_REFERER'].blank?
46
+ referring_uri = URI.parse(@env['HTTP_REFERER'])
47
+ driver_uri = URI.parse(Carpool::Passenger.driver_uri)
48
+ referring_uri.host.to_s.downcase === driver_uri.host.to_s.downcase
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,75 @@
1
+ require 'fast-aes'
2
+ require 'yaml'
3
+
4
+ module Carpool
5
+ class SeatBelt
6
+
7
+ include Carpool::Mixins::Core
8
+
9
+ attr_accessor :env
10
+ attr_accessor :redirect_uri
11
+ attr_accessor :user
12
+
13
+ # SeatBelt instances require access to the rack environment.
14
+ def initialize(env)
15
+ @env = env
16
+ end
17
+
18
+ # 'Attaches' the current user into the session so it can be re-authenticated when
19
+ # a passenger requests it at a later date. We 'fasten' the users seatbelt for the trip back to the
20
+ # referring site.
21
+ # Fasten! returns a url for redirection back to our passenger site including the seatbelt used for authentication
22
+ # on the other end.
23
+ #
24
+ def fasten!(user)
25
+ cookies[:passenger_token] = generate_token(user)
26
+ create_payload!
27
+ end
28
+
29
+ # Restore the user from our payload. We 'remove' their seatbelt because they have arrived!
30
+ def remove!
31
+ payload = env['X-CARPOOL-PAYLOAD']
32
+ payload = payload.flatten.first if payload.is_a?(Array) # TODO: Figure out why our header is an array?
33
+ seatbelt = YAML.load(Base64.decode64(CGI.unescape(payload))).to_hash
34
+ puts "Seatbelt: #{seatbelt.inspect}"
35
+ user = Base64.decode64(seatbelt[:user])
36
+ key = Carpool.generate_site_key(env['SERVER_NAME'])
37
+ secret = Carpool::Passenger.secret
38
+ digest = Digest::SHA256.new
39
+ digest.update("#{key}--#{secret}")
40
+ aes = FastAES.new(digest.digest)
41
+ data = aes.decrypt(user)
42
+ @redirect_uri = seatbelt[:redirect_uri].to_s
43
+ @user = YAML.load(data).to_hash
44
+ self
45
+ end
46
+
47
+ # Create a redirection payload to be sent back to our passenger
48
+ def create_payload!
49
+ seatbelt = self.to_s
50
+ referrer = cookies[:redirect_to]
51
+ new_uri = "#{referrer.scheme}://"
52
+ new_uri << referrer.host
53
+ new_uri << ((referrer.port != 80 && referrer.port != 443) ? ":#{referrer.port}" : "")
54
+ new_uri << "/sso/authorize?seatbelt=#{seatbelt}"
55
+ end
56
+
57
+ def to_s
58
+ CGI.escape(Base64.encode64({:redirect_uri => cookies[:redirect_to].to_s, :user => cookies[:passenger_token] }.to_yaml.to_s).gsub( /\s/, ''))
59
+ end
60
+
61
+ private
62
+
63
+ def generate_token(user)
64
+ referrer = cookies[:redirect_to]
65
+ passenger = Carpool::Driver.passengers.reject{ |p| p.keys.first.downcase != referrer.host }.first.values.first
66
+
67
+ digest = Digest::SHA256.new
68
+ digest.update("#{passenger[:site_key]}--#{passenger[:secret]}")
69
+ aes = FastAES.new(digest.digest)
70
+ Base64.encode64(aes.encrypt(user.encrypted_credentials.to_yaml.to_s)).gsub( /\s/, '')
71
+
72
+ end
73
+
74
+ end
75
+ end
data/lib/carpool.rb ADDED
@@ -0,0 +1,31 @@
1
+ require 'carpool/mixins'
2
+ require 'carpool/driver'
3
+ require 'carpool/passenger'
4
+ require 'carpool/seatbelt'
5
+ require 'base64'
6
+
7
+ module Carpool
8
+
9
+ class << self
10
+
11
+ def auth_attempt=(bool)
12
+ @auth_attempt = bool
13
+ end
14
+
15
+ def auth_attempt?
16
+ @auth_attempt ||= false
17
+ end
18
+
19
+ end
20
+
21
+ def self.generate_site_key(url)
22
+ digest = Digest::SHA256.new
23
+ digest.update(url)
24
+ Base64.encode64(digest.digest).gsub( /\s/, '')
25
+ end
26
+
27
+ def self.unpack_key(key)
28
+ Base64.decode64(key)
29
+ end
30
+
31
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'carpool'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestCarpool < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: carpool
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Brent Kirby
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-08-15 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: thoughtbot-shoulda
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :development
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: fast-aes
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ description: Carpool is a single sign on solution for Rack-based applications allowing you to persist sessions across domains.
50
+ email: dev@kurbmedia.com
51
+ executables: []
52
+
53
+ extensions: []
54
+
55
+ extra_rdoc_files:
56
+ - LICENSE
57
+ - README.rdoc
58
+ files:
59
+ - .document
60
+ - .gitignore
61
+ - LICENSE
62
+ - README.rdoc
63
+ - Rakefile
64
+ - VERSION
65
+ - carpool.gemspec
66
+ - init.rb
67
+ - lib/carpool.rb
68
+ - lib/carpool/driver.rb
69
+ - lib/carpool/mixins.rb
70
+ - lib/carpool/passenger.rb
71
+ - lib/carpool/seatbelt.rb
72
+ - test/helper.rb
73
+ - test/test_carpool.rb
74
+ has_rdoc: true
75
+ homepage: http://github.com/kurbmedia/carpool
76
+ licenses: []
77
+
78
+ post_install_message:
79
+ rdoc_options:
80
+ - --charset=UTF-8
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ hash: 3
89
+ segments:
90
+ - 0
91
+ version: "0"
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ hash: 3
98
+ segments:
99
+ - 0
100
+ version: "0"
101
+ requirements: []
102
+
103
+ rubyforge_project:
104
+ rubygems_version: 1.3.7
105
+ signing_key:
106
+ specification_version: 3
107
+ summary: Single Sign On solution for Rack-Based applications
108
+ test_files:
109
+ - test/helper.rb
110
+ - test/test_carpool.rb