Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

initial implementation, ripped from tender_multipass

  • Loading branch information...
commit fa36dc0eeeddade0bd7c58afe6b846ffd676da80 0 parents
@technoweenie technoweenie authored
Showing with 147 additions and 0 deletions.
  1. +20 −0 LICENSE
  2. +12 −0 README
  3. +59 −0 lib/multipass.rb
  4. +56 −0 test/multipass_test.rb
20 LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2009 rick olson
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12 README
@@ -0,0 +1,12 @@
+MultiPass
+=========
+
+Bare bones implementation of encoding and decoding MultiPass values for SSO.
+
+MultiPasses are json hashes encrypted with strong AES encryption. They are typically
+passed as cookie values, URL params, or HTTP header values, depending on how
+the individual service chooses to implement it.
+
+The idea is that if a site wants to automatically create a local user based
+on the login credentials of another site, it will look for a MultiPass. This
+MultiPass can contain the user's email address, name, etc.
59 lib/multipass.rb
@@ -0,0 +1,59 @@
+require 'active_support'
+require 'ezcrypto'
+
+class MultiPass
+ class Invalid < StandardError; end
+ class ExpiredError < Invalid; end
+ class JSONError < Invalid; end
+ class DecryptError < Invalid; end
+
+ def self.encode(site_key, api_key, options = {})
+ new(site_key, api_key).encode(options)
+ end
+
+ def self.decode(site_key, api_key, data)
+ new(site_key, api_key).decode(data)
+ end
+
+ def initialize(site_key, api_key)
+ @site_key = site_key
+ @api_key = api_key
+ @crypto_key = EzCrypto::Key.with_password(@site_key, @api_key)
+ end
+
+ # Encrypts the given hash into a multipass string.
+ def encode(options = {})
+ options[:expires] = case options[:expires]
+ when Fixnum then Time.at(options[:expires]).to_s(:db)
+ when Time, DateTime, Date then options[:expires].to_s(:db)
+ else options[:expires].to_s
+ end
+ @crypto_key.encrypt64(options.to_json)
+ end
+
+ # Decrypts the given multipass string and parses it as JSON. Then, it checks
+ # for a valid expiration date.
+ def decode(data)
+ json = @crypto_key.decrypt64(data)
+
+ if json.nil?
+ raise MultiPass::DecryptError
+ end
+
+ options = ActiveSupport::JSON.decode(json)
+
+ if !options.is_a?(Hash)
+ raise MultiPass::JSONError
+ end
+
+ options.symbolize_keys!
+
+ if options[:expires].blank? || Time.now.utc > Time.parse(options[:expires])
+ raise MultiPass::ExpiredError
+ end
+
+ options
+ rescue ActiveSupport::JSON::ParseError
+ raise MultiPass::JSONError
+ end
+end
56 test/multipass_test.rb
@@ -0,0 +1,56 @@
+$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
+require 'rubygems'
+require 'test/unit'
+require 'multipass'
+
+class MultiPassTest < Test::Unit::TestCase
+ def setup
+ @date = 1234.seconds.from_now
+ @input = {:expires => @date, :email => 'ricky@bobby.com'}
+ @output = @input.merge(:expires => @input[:expires].to_s(:db))
+ @key = EzCrypto::Key.with_password('example', 'abc')
+ @mp = MultiPass.new('example', 'abc')
+ end
+
+ def test_encodes_multipass
+ expected = @key.encrypt64(@output.to_json)
+ assert_equal expected, @mp.encode(@input)
+ end
+
+ def test_encodes_multipass_with_class_method
+ expected = @key.encrypt64(@output.to_json)
+ assert_equal expected, MultiPass.encode('example', 'abc', @input)
+ end
+
+ def test_decodes_multipass
+ encoded = @mp.encode(@input)
+ assert_equal @input, @mp.decode(encoded)
+ end
+
+ def test_decodes_multipass_with_class_method
+ encoded = @mp.encode(@input)
+ assert_equal @input, MultiPass.decode('example', 'abc', encoded)
+ end
+
+ def test_invalidates_bad_string
+ assert_raises MultiPass::DecryptError do
+ @mp.decode("abc")
+ end
+ end
+
+ def test_invalidates_bad_json
+ assert_raises MultiPass::JSONError do
+ @mp.decode(@key.encrypt64("abc"))
+ end
+ assert_raises MultiPass::JSONError do
+ @mp.decode(@key.encrypt64("{a"))
+ end
+ end
+
+ def test_invalidates_old_expiration
+ encrypted = @key.encrypt64(@input.merge(:expires => 1.second.ago).to_json)
+ assert_raises MultiPass::ExpiredError do
+ @mp.decode(encrypted)
+ end
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.