entp-multipass 1.0.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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 rick olson
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 ADDED
@@ -0,0 +1,12 @@
1
+ MultiPass
2
+ =========
3
+
4
+ Bare bones implementation of encoding and decoding MultiPass values for SSO.
5
+
6
+ MultiPasses are json hashes encrypted with strong AES encryption. They are typically
7
+ passed as cookie values, URL params, or HTTP header values, depending on how
8
+ the individual service chooses to implement it.
9
+
10
+ The idea is that if a site wants to automatically create a local user based
11
+ on the login credentials of another site, it will look for a MultiPass. This
12
+ MultiPass can contain the user's email address, name, etc.
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 1
3
+ :minor: 0
4
+ :patch: 0
data/lib/multipass.rb ADDED
@@ -0,0 +1,94 @@
1
+ require 'ezcrypto'
2
+ require 'time'
3
+
4
+ class MultiPass
5
+ class Invalid < StandardError; end
6
+ class ExpiredError < Invalid; end
7
+ class JSONError < Invalid; end
8
+ class DecryptError < Invalid; end
9
+
10
+ def self.encode(site_key, api_key, options = {})
11
+ new(site_key, api_key).encode(options)
12
+ end
13
+
14
+ def self.decode(site_key, api_key, data)
15
+ new(site_key, api_key).decode(data)
16
+ end
17
+
18
+ def initialize(site_key, api_key)
19
+ @site_key = site_key
20
+ @api_key = api_key
21
+ @crypto_key = EzCrypto::Key.with_password(@site_key, @api_key)
22
+ end
23
+
24
+ # Encrypts the given hash into a multipass string.
25
+ def encode(options = {})
26
+ options[:expires] = case options[:expires]
27
+ when Fixnum then Time.at(options[:expires]).to_s
28
+ when Time, DateTime, Date then options[:expires].to_s
29
+ else options[:expires].to_s
30
+ end
31
+ escape_value @crypto_key.encrypt64(options.to_json)
32
+ end
33
+
34
+ # Decrypts the given multipass string and parses it as JSON. Then, it checks
35
+ # for a valid expiration date.
36
+ def decode(data)
37
+ json = @crypto_key.decrypt64(unescape_value(data))
38
+
39
+ if json.nil?
40
+ raise MultiPass::DecryptError
41
+ end
42
+
43
+ options = decode_json(json)
44
+
45
+ if !options.is_a?(Hash)
46
+ raise MultiPass::JSONError
47
+ end
48
+
49
+ options.keys.each do |key|
50
+ options[key.to_sym] = options.delete(key)
51
+ end
52
+
53
+ if options[:expires].nil? || Time.now.utc > Time.parse(options[:expires])
54
+ raise MultiPass::ExpiredError
55
+ end
56
+
57
+ options
58
+ end
59
+
60
+ private
61
+ if Object.const_defined?(:ActiveSupport)
62
+ def decode_json(s)
63
+ ActiveSupport::JSON.decode(s)
64
+ rescue ActiveSupport::JSON::ParseError
65
+ raise MultiPass::JSONError
66
+ end
67
+ else
68
+ require 'json'
69
+ def decode_json(s)
70
+ JSON.parse(s)
71
+ rescue JSON::ParserError
72
+ raise MultiPass::JSONError
73
+ end
74
+ end
75
+
76
+ if Object.const_defined?(:Rack)
77
+ def escape_value(s)
78
+ Rack::Utils.escape(s)
79
+ end
80
+
81
+ def unescape_value(s)
82
+ Rack::Utils.unescape(s)
83
+ end
84
+ else
85
+ require 'cgi'
86
+ def escape_value(s)
87
+ CGI.escape(s)
88
+ end
89
+
90
+ def unescape_value(s)
91
+ CGI.unescape(s)
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,56 @@
1
+ $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
2
+ require 'rubygems'
3
+ require 'test/unit'
4
+ require 'multipass'
5
+
6
+ class MultiPassTest < Test::Unit::TestCase
7
+ def setup
8
+ @date = Time.now + 1234
9
+ @input = {:expires => @date, :email => 'ricky@bobby.com'}
10
+ @output = @input.merge(:expires => @input[:expires].to_s)
11
+ @key = EzCrypto::Key.with_password('example', 'abc')
12
+ @mp = MultiPass.new('example', 'abc')
13
+ end
14
+
15
+ def test_encodes_multipass
16
+ expected = @key.encrypt64(@output.to_json)
17
+ assert_equal CGI.escape(expected), @mp.encode(@input)
18
+ end
19
+
20
+ def test_encodes_multipass_with_class_method
21
+ expected = @key.encrypt64(@output.to_json)
22
+ assert_equal CGI.escape(expected), MultiPass.encode('example', 'abc', @input)
23
+ end
24
+
25
+ def test_decodes_multipass
26
+ encoded = @mp.encode(@input)
27
+ assert_equal @input, @mp.decode(encoded)
28
+ end
29
+
30
+ def test_decodes_multipass_with_class_method
31
+ encoded = @mp.encode(@input)
32
+ assert_equal @input, MultiPass.decode('example', 'abc', encoded)
33
+ end
34
+
35
+ def test_invalidates_bad_string
36
+ assert_raises MultiPass::DecryptError do
37
+ @mp.decode("abc")
38
+ end
39
+ end
40
+
41
+ def test_invalidates_bad_json
42
+ assert_raises MultiPass::JSONError do
43
+ @mp.decode(CGI.escape(@key.encrypt64("abc")))
44
+ end
45
+ assert_raises MultiPass::JSONError do
46
+ @mp.decode(CGI.escape(@key.encrypt64("{a")))
47
+ end
48
+ end
49
+
50
+ def test_invalidates_old_expiration
51
+ encrypted = CGI.escape @key.encrypt64(@input.merge(:expires => (Time.now - 1)).to_json)
52
+ assert_raises MultiPass::ExpiredError do
53
+ @mp.decode(encrypted)
54
+ end
55
+ end
56
+ end
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: entp-multipass
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - rick
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-19 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: technoweenie@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ - LICENSE
25
+ files:
26
+ - VERSION.yml
27
+ - lib/multipass.rb
28
+ - test/multipass_test.rb
29
+ - README
30
+ - LICENSE
31
+ has_rdoc: false
32
+ homepage: http://github.com/entp/multipass
33
+ post_install_message:
34
+ rdoc_options:
35
+ - --inline-source
36
+ - --charset=UTF-8
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ requirements: []
52
+
53
+ rubyforge_project:
54
+ rubygems_version: 1.2.0
55
+ signing_key:
56
+ specification_version: 3
57
+ summary: TODO
58
+ test_files: []
59
+