opentoken-newrelic-rails23 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
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,22 @@
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
+ Gemfile.lock
21
+
22
+ ## PROJECT::SPECIFIC
data/CONTRIBUTORS.txt ADDED
@@ -0,0 +1,7 @@
1
+ Ryan Sonnek - Original Author
2
+
3
+ Dan Alvizu - contributed OpenToken encode functionality
4
+
5
+ Complete list of contributors:
6
+ https://github.com/socialcast/opentoken/contributors
7
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in opentoken-newrelic-rails23.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2011 Socialcast, Inc
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # opentoken
2
+
3
+ Parse encrypted opentoken properties
4
+
5
+ see http://www.pingidentity.com/opentoken
6
+
7
+ ## Usage
8
+
9
+ ```ruby
10
+ # configure decryption with shared key
11
+ OpenToken.password = 'shared_secret_to_decrypt'
12
+
13
+ # decrypt opentoken into hash of attributes
14
+ attributes = OpenToken.decode 'opentoken-hashed-string'
15
+
16
+ # encrypt opentoken from hash of attributes
17
+ attributes = { 'subject' => 'foo', 'bar' => 'bak' }
18
+ token = OpenToken.encode attributes, OpenToken::Cipher::AES_128_CBC
19
+ ```
20
+
21
+ ## Contributing
22
+
23
+ * Fork the project
24
+ * Fix the issue
25
+ * Add tests
26
+ * Send me a pull request. Bonus points for topic branches.
27
+
28
+ see CONTRIBUTORS.txt for complete list of contributors.
29
+
30
+ ## Copyright
31
+
32
+ Copyright (c) 2011 Socialcast Inc.
33
+ See LICENSE.txt for details.
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rake/testtask'
5
+ Rake::TestTask.new(:test) do |test|
6
+ test.libs << 'lib' << 'test'
7
+ test.pattern = 'test/**/test_*.rb'
8
+ test.verbose = true
9
+ end
10
+
11
+ task :default => :test
@@ -0,0 +1,182 @@
1
+ require 'base64'
2
+ require 'openssl'
3
+ require 'digest/sha1'
4
+ require 'zlib'
5
+ require 'stringio'
6
+ require 'cgi'
7
+ require 'time'
8
+ require File.join(File.dirname(__FILE__), 'opentoken-newrelic-rails23', 'token')
9
+ require File.join(File.dirname(__FILE__), 'opentoken-newrelic-rails23', 'key_value_serializer')
10
+ require File.join(File.dirname(__FILE__), 'opentoken-newrelic-rails23', 'password_key_generator')
11
+ require File.join(File.dirname(__FILE__), 'opentoken-newrelic-rails23', 'cipher')
12
+
13
+ module OpenToken
14
+ class TokenInvalidError < StandardError; end
15
+
16
+ class << self
17
+ attr_accessor :debug
18
+ def debug?
19
+ !!debug
20
+ end
21
+
22
+ attr_accessor :password
23
+ attr_accessor :token_lifetime
24
+ attr_accessor :renew_until_lifetime
25
+
26
+ def encode(attributes, cipher)
27
+ attributes['not-before'] = Time.now.utc.iso8601.to_s
28
+ attributes['not-on-or-after'] = Time.at(Time.now.to_i + token_lifetime).utc.iso8601.to_s
29
+ attributes['renew-until'] = Time.at(Time.now.to_i + renew_until_lifetime).utc.iso8601.to_s
30
+
31
+ serialized = OpenToken::KeyValueSerializer.serialize(attributes)
32
+ compressed = zip_payload serialized
33
+
34
+ key = cipher.generate_key
35
+ iv = cipher.generate_iv
36
+ encrypted = cipher.encrypt_payload compressed, key, iv
37
+
38
+ mac = []
39
+ mac << "0x01".hex.chr # OTK version
40
+ mac << cipher.suite.chr
41
+ mac << iv
42
+ mac << force_encoding(serialized, 'BINARY')
43
+ hash = OpenSSL::HMAC.digest(OpenToken::PasswordKeyGenerator::SHA1_DIGEST, key, mac.join)
44
+
45
+ token_string = ""
46
+ token_string = "OTK" + 1.chr + cipher.suite.chr
47
+ token_string += hash
48
+ token_string += cipher.iv_length.chr
49
+ token_string += iv
50
+ token_string += 0.chr # key info length
51
+ token_string += ((encrypted.length >> 8) &0xFF ).chr
52
+ token_string += (encrypted.length & 0xFF).chr
53
+ token_string += encrypted
54
+ inspect_binary_string "Unencoded", token_string
55
+ encoded = urlsafe_encode64 token_string
56
+ inspect_binary_string "Encoded", encoded
57
+ encoded
58
+ end
59
+ def decode(opentoken = nil)
60
+ verify opentoken.present?, 'Unable to parse empty token'
61
+ data = urlsafe_decode64(opentoken)
62
+ inspect_binary_string 'DATA', data
63
+
64
+ verify_header data
65
+ verify_version data
66
+
67
+ #cipher suite identifier
68
+ cipher_suite = char_value_of data[4]
69
+ cipher = OpenToken::Cipher.for_suite cipher_suite
70
+
71
+ #SHA-1 HMAC
72
+ payload_hmac = data[5..24]
73
+ inspect_binary_string "PAYLOAD HMAC [5..24]", payload_hmac
74
+
75
+ #Initialization Vector (iv)
76
+ iv_length = char_value_of data[25]
77
+ iv_end = char_value_of [26, 26 + iv_length - 1].max
78
+ iv = data[26..iv_end]
79
+ inspect_binary_string "IV [26..#{iv_end}]", iv
80
+ verify iv_length == cipher.iv_length, "Cipher expects iv length of #{cipher.iv_length} and was: #{iv_length}"
81
+
82
+ #key (not currently used)
83
+ key_length = char_value_of data[iv_end + 1]
84
+ key_end = iv_end + 1
85
+ verify key_length == 0, "Token key embedding is not currently supported. Key length is: #{key_length}"
86
+
87
+ #payload
88
+ payload_length = data[(key_end + 1)..(key_end + 2)].unpack('n').first
89
+ payload_offset = key_end + 3
90
+ encrypted_payload = data[payload_offset..(data.length - 1)]
91
+ verify encrypted_payload.length == payload_length, "Payload length is #{encrypted_payload.length} and was expected to be #{payload_length}"
92
+ inspect_binary_string "ENCRYPTED PAYLOAD [#{payload_offset}..#{data.length - 1}]", encrypted_payload
93
+
94
+ key = cipher.generate_key
95
+ inspect_binary_string 'KEY', key
96
+
97
+ compressed_payload = cipher.decrypt_payload encrypted_payload, key, iv
98
+ inspect_binary_string 'COMPRESSED PAYLOAD', compressed_payload
99
+
100
+ unparsed_payload = unzip_payload compressed_payload
101
+ puts 'EXPANDED PAYLOAD', unparsed_payload if debug?
102
+
103
+ #validate payload hmac
104
+ mac = []
105
+ mac << "0x01".hex.chr
106
+ mac << cipher_suite.chr
107
+ mac << iv
108
+ mac << key if key_length > 0 #key embedding is not currently supported
109
+ mac << unparsed_payload
110
+ hash = OpenSSL::HMAC.digest(OpenToken::PasswordKeyGenerator::SHA1_DIGEST, key, mac.join)
111
+ if (hash <=> payload_hmac) != 0
112
+ verify payload_hmac == hash, "HMAC for payload was #{hash} and expected to be #{payload_hmac}"
113
+ end
114
+
115
+ unescaped_payload = CGI::unescapeHTML(unparsed_payload)
116
+ puts 'UNESCAPED PAYLOAD', unescaped_payload if debug?
117
+ token = OpenToken::KeyValueSerializer.deserialize force_encoding(unescaped_payload, 'UTF-8')
118
+ puts token.inspect if debug?
119
+ token.validate!
120
+ token
121
+ end
122
+
123
+ private
124
+ def char_value_of(character)
125
+ if RUBY_VERSION < "1.9"
126
+ return character
127
+ else
128
+ return character.chr.ord
129
+ end
130
+ end
131
+ def verify_header(data)
132
+ header = data[0..2]
133
+ verify header == 'OTK', "Invalid token header: #{header}"
134
+ end
135
+ def verify_version(data)
136
+ version = char_value_of data[3]
137
+ verify version == 1, "Unsupported token version: '#{version}'"
138
+ end
139
+ #ruby 1.9 has Base64.urlsafe_decode64 which can be used instead of gsubbing '_' and '-'
140
+ def urlsafe_decode64(token)
141
+ string = token.gsub('*', '=').gsub('_', '/').gsub('-', '+')
142
+ data = Base64.decode64(string)
143
+ end
144
+ def urlsafe_encode64(token)
145
+ string = Base64.encode64(token);
146
+ string = string.gsub('=', '*').gsub('/', '_').gsub('+', '-').gsub(10.chr, '').gsub(11.chr, '')
147
+ string
148
+ end
149
+ def verify(assertion, message = 'Invalid Token')
150
+ raise OpenToken::TokenInvalidError.new(message) unless assertion
151
+ end
152
+ #decompress the payload
153
+ #see http://stackoverflow.com/questions/1361892/how-to-decompress-gzip-data-in-ruby
154
+ def unzip_payload(compressed_payload)
155
+ unparsed_payload = begin
156
+ Zlib::Inflate.inflate(compressed_payload)
157
+ rescue Zlib::BufError
158
+ Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate(compressed_payload[2, compressed_payload.size])
159
+ end
160
+ end
161
+ def zip_payload(uncompressed)
162
+ compressed = Zlib::Deflate.deflate(uncompressed, 9)
163
+ compressed
164
+ end
165
+ def inspect_binary_string(header, string)
166
+ return unless debug?
167
+ puts "#{header}:"
168
+ index = 0
169
+ string.each_byte do |b|
170
+ puts "#{index}: #{b} => #{b.chr}"
171
+ index += 1
172
+ end
173
+ end
174
+ def force_encoding(string, encoding)
175
+ string.respond_to?(:force_encoding) ? string.force_encoding(encoding) : string
176
+ end
177
+ end
178
+ end
179
+
180
+ # intialize defaults
181
+ OpenToken.token_lifetime = 300
182
+ OpenToken.renew_until_lifetime = 43200
@@ -0,0 +1,69 @@
1
+ require 'openssl'
2
+
3
+ module OpenToken
4
+ class Cipher
5
+ class InvalidCipherError < StandardError; end
6
+
7
+ attr_reader :algorithm
8
+ attr_reader :iv_length
9
+ attr_reader :key_length
10
+ attr_reader :suite
11
+
12
+ def initialize(attrs = {})
13
+ @suite = attrs[:suite]
14
+ @iv_length = attrs[:iv_length]
15
+ @key_length = attrs[:key_length]
16
+ @algorithm = attrs[:algorithm]
17
+ end
18
+ def self.for_suite(cipher_suite)
19
+ cipher = REGISTERED_CIPHERS.detect {|c| c.suite == cipher_suite }
20
+ raise InvalidCipherError.new("Unknown cipher suite: #{cipher_suite}") unless cipher
21
+ cipher
22
+ end
23
+
24
+ def generate_key
25
+ OpenToken::PasswordKeyGenerator.generate OpenToken.password, self
26
+ end
27
+ def generate_iv
28
+ OpenSSL::Random.random_bytes(iv_length)
29
+ end
30
+
31
+ #see http://snippets.dzone.com/posts/show/4975
32
+ #see http://jdwyah.blogspot.com/2009/12/decrypting-ruby-aes-encryption.html
33
+ #see http://snippets.dzone.com/posts/show/576
34
+ def decrypt_payload(encrypted_payload, key, iv)
35
+ return encrypted_payload unless algorithm
36
+ c = crypt :decrypt, key, iv
37
+ c.update(encrypted_payload) + c.final
38
+ end
39
+ def encrypt_payload(payload, key, iv)
40
+ c = crypt :encrypt, key, iv
41
+ padding = if payload.length % iv_length == 0
42
+ iv_length
43
+ else
44
+ iv_length - (payload.length % iv_length)
45
+ end
46
+ c.update(payload + (padding.chr * padding))
47
+ end
48
+
49
+ private
50
+ def crypt(operation, key, iv)
51
+ crypt = OpenSSL::Cipher::Cipher.new(algorithm)
52
+ crypt.send operation
53
+ crypt.key = key
54
+ crypt.iv = iv
55
+ crypt
56
+ end
57
+
58
+ NULL = Cipher.new(:suite => 0, :iv_length => 0)
59
+ AES_256_CBC = Cipher.new(:suite => 1, :iv_length => 32, :key_length => 256, :algorithm => 'aes-256-cbc')
60
+ AES_128_CBC = Cipher.new(:suite => 2, :iv_length => 16, :key_length => 128, :algorithm => 'aes-128-cbc')
61
+ DES3_168_CBC = Cipher.new(:suite => 3, :iv_length => 8, :key_length => 168, :algorithm => 'des-cbc')
62
+
63
+ REGISTERED_CIPHERS = []
64
+ REGISTERED_CIPHERS << NULL
65
+ REGISTERED_CIPHERS << AES_256_CBC
66
+ REGISTERED_CIPHERS << AES_128_CBC
67
+ REGISTERED_CIPHERS << DES3_168_CBC
68
+ end
69
+ end
@@ -0,0 +1,142 @@
1
+ module OpenToken
2
+ class KeyValueSerializer
3
+ LINE_START = 0
4
+ EMPTY_SPACE = 1
5
+ VALUE_START = 2
6
+ LINE_END = 3
7
+ IN_KEY = 4
8
+ IN_VALUE = 5
9
+ IN_QUOTED_VALUE = 6
10
+
11
+ class << self
12
+ def serialize(hashmap)
13
+ result = String.new
14
+ count = 0;
15
+ hashmap.each_pair do |key,value|
16
+ if (count != 0)
17
+ result = result + "\n"
18
+ end
19
+ count +=1
20
+ result += key + "="
21
+ result += escape_value(value)
22
+ end
23
+ result
24
+ end
25
+ def deserialize(string)
26
+ result = OpenToken::Token.new
27
+ state = LINE_START
28
+ open_quote_char = 0.chr
29
+ currkey = ""
30
+ token = ""
31
+ nextval = ""
32
+
33
+ string.split(//).each do |c|
34
+ nextval = c
35
+
36
+ case c
37
+ when "\t"
38
+ if state == IN_KEY
39
+ # key ends
40
+ currkey = token
41
+ token = ""
42
+ state = EMPTY_SPACE
43
+ elsif state == IN_VALUE
44
+ # non-quoted value ends
45
+ result[currkey] = self.deserialize(token)
46
+ token = ""
47
+ state = LINE_END
48
+ elsif state == IN_QUOTED_VALUE
49
+ token += c
50
+ end
51
+ when " "
52
+ if state == IN_KEY
53
+ # key ends
54
+ currkey = token
55
+ token = ""
56
+ state = EMPTY_SPACE
57
+ elsif state == IN_VALUE
58
+ # non-quoted value ends
59
+ result[currkey] = self.deserialize(token)
60
+ token = ""
61
+ state = LINE_END
62
+ elsif state == IN_QUOTED_VALUE
63
+ token += c
64
+ end
65
+ when "\n"
66
+ # newline
67
+ if (state == IN_VALUE) || (state == VALUE_START)
68
+ result[currkey] = unescape_value(token)
69
+ token = ""
70
+ state = LINE_START
71
+ elsif state == LINE_END
72
+ token = ""
73
+ state = LINE_START
74
+ elsif state == IN_QUOTED_VALUE
75
+ token += c
76
+ end
77
+ when "="
78
+ if state == IN_KEY
79
+ currkey = token
80
+ token = ""
81
+ state = VALUE_START
82
+ elsif (state == IN_QUOTED_VALUE) || (state == IN_VALUE)
83
+ token += c
84
+ end
85
+ when "\""
86
+ if state == IN_QUOTED_VALUE
87
+ if (c == open_quote_char) && (token[token.size-1] != "\\"[0])
88
+ result[currkey] = unescape_value(token)
89
+ token = ""
90
+ state = LINE_END
91
+ else
92
+ token += c
93
+ end
94
+ elsif state == VALUE_START
95
+ state = IN_QUOTED_VALUE
96
+ open_quote_char = c
97
+ end
98
+ when "'"
99
+ if state == IN_QUOTED_VALUE
100
+ if (c == open_quote_char) && (token[token.size-1] != "\\"[0])
101
+ result[currkey] = unescape_value(token)
102
+ token = ""
103
+ state = LINE_END
104
+ else
105
+ token += c
106
+ end
107
+ else state == VALUE_START
108
+ state = IN_QUOTED_VALUE
109
+ open_quote_char = c
110
+ end
111
+ else
112
+ if state == LINE_START
113
+ state = IN_KEY
114
+ elsif state == VALUE_START
115
+ state = IN_VALUE
116
+ end
117
+ token += c
118
+ end
119
+
120
+ if (state == IN_QUOTED_VALUE) || (state == IN_VALUE)
121
+ result[currkey] = unescape_value(token)
122
+ end
123
+ end
124
+ result
125
+ end
126
+ private
127
+ def unescape_value(value)
128
+ value.gsub("\\\"", "\"").gsub("\\\'", "'")
129
+ end
130
+ def escape_value(value)
131
+ value.each_byte do |b|
132
+ c = b.chr
133
+ if c == "\n" or c == "\t" or c == " " or c == "'" or c == "\""
134
+ value = "'" + value.gsub("'", "\'").gsub("\"", "\\\"") + "'"
135
+ break
136
+ end
137
+ end
138
+ value
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,65 @@
1
+ module OpenToken
2
+ class PasswordKeyGenerator
3
+ SHA1_DIGEST = OpenSSL::Digest::Digest.new('sha1')
4
+
5
+ class << self
6
+ def generate(password, cipher)
7
+ salt = 0.chr * 8
8
+ generate_impl(password, cipher, salt, 1000)
9
+ end
10
+
11
+ private
12
+ def generate_block(password, salt, count, index)
13
+ mac = salt
14
+ mac += [index].pack("N")
15
+
16
+ result = OpenSSL::HMAC.digest(SHA1_DIGEST, password, mac)
17
+ cur = result
18
+
19
+ i_count = 1
20
+ while i_count < count
21
+ i_count +=1
22
+
23
+ cur = OpenSSL::HMAC.digest(SHA1_DIGEST, password, cur)
24
+
25
+ 20.times do |i|
26
+ if RUBY_VERSION < "1.9"
27
+ result[i] = result[i] ^ cur[i]
28
+ else
29
+ result[i] = (result[i].chr.ord ^ cur[i].chr.ord).chr
30
+ end
31
+ end
32
+ end
33
+
34
+ return result
35
+ end
36
+
37
+ def generate_impl(password, cipher, salt, iterations)
38
+ return unless cipher.algorithm
39
+
40
+ key_size = cipher.key_length / 8
41
+ numblocks = key_size / 20
42
+ numblocks += 1 if (key_size % 20) > 0
43
+
44
+ # Generate the appropriate number of blocks and write their output to
45
+ # the key bytes; note that it's important to start from 1 (vs. 0) as the
46
+ # initial block number affects the hash. It's not clear that this fact
47
+ # is stated explicitly anywhere, but without this approach, the generated
48
+ # keys will not match up with test cases defined in RFC 3962.
49
+ key_buffer_index = 0
50
+ key = ""
51
+
52
+ numblocks.times do |i|
53
+ i+=1 # Previously zero based, needs to be 1 based
54
+ block = generate_block(password, salt, iterations, i)
55
+ len = [20, (key_size - key_buffer_index)].min
56
+ key += block[0, len]
57
+ key_buffer_index += len
58
+ end
59
+
60
+ return key
61
+ end
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,33 @@
1
+ require 'time'
2
+ require 'active_support'
3
+
4
+ module OpenToken
5
+ class TokenExpiredError < StandardError; end
6
+
7
+ class Token < HashWithIndifferentAccess
8
+ def validate!
9
+ raise OpenToken::TokenExpiredError.new("#{Time.now.utc} is not within token duration: #{self.start_at} - #{self.end_at}") if self.expired?
10
+ end
11
+ #verify that the current time is between the not-before and not-on-or-after values
12
+ def valid?
13
+ start_at.past? && end_at.future?
14
+ end
15
+ def expired?
16
+ !valid?
17
+ end
18
+ def start_at
19
+ payload_date('not-before')
20
+ end
21
+ def end_at
22
+ payload_date('not-on-or-after')
23
+ end
24
+ def valid_until
25
+ payload_date('renew-until')
26
+ end
27
+
28
+ private
29
+ def payload_date(key)
30
+ Time.iso8601(self[key])
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ module OpenToken
2
+ VERSION = '1.2.2'
3
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "opentoken-newrelic-rails23/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "opentoken-newrelic-rails23"
7
+ s.version = OpenToken::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Ryan Sonnek"]
10
+ s.email = ["ryan@socialcast.com"]
11
+ s.homepage = "http://github.com/newrelic/opentoken-newrelic-rails23"
12
+ s.summary = %q{ruby implementation of the opentoken specification, forked for Rails 2.3 compatibility. Use the non-forked version for Rails 3+}
13
+ s.description = %q{parse opentoken properties passed for Single Signon requests}
14
+
15
+ s.rubyforge_project = "opentoken-newrelic-rails23"
16
+
17
+ # If you are on a newer version of Rails, you should be using the opentoken gem, not our fork.
18
+ s.add_runtime_dependency(%q<activesupport>, ["~> 2.3.14"])
19
+ s.add_runtime_dependency(%q<i18n>, [">= 0"])
20
+ s.add_development_dependency(%q<shoulda>, ["2.11.3"])
21
+ s.add_development_dependency(%q<timecop>, ["0.3.5"])
22
+ s.add_development_dependency(%q<rake>, ["0.9.2"])
23
+
24
+ s.files = `git ls-files`.split("\n")
25
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
26
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
27
+ s.require_paths = ["lib"]
28
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+ require 'shoulda'
12
+ require 'timecop'
13
+
14
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
15
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
16
+ require 'opentoken-newrelic-rails23'
17
+
18
+ class Test::Unit::TestCase
19
+ end
@@ -0,0 +1,105 @@
1
+ require 'helper'
2
+
3
+ class TestOpentoken < Test::Unit::TestCase
4
+ # OpenToken.debug = true
5
+
6
+ #"renew-until"=>"2010-03-05T07:19:15Z"
7
+ #"not-before"=>"2010-03-04T19:19:15Z"
8
+ #"not-on-or-after"=>"2010-03-04T19:24:15Z"
9
+ context "aes-128-cbc token with subject attribute" do
10
+ setup do
11
+ @opentoken = "T1RLAQJ0Ca97sl6MLJAZDa_hdFzMlicMQBDjqUzrXl0EOXKmpj5oo7L5AACgaWoW8fZizrsLbtxb_F00aTdFmhw8flGy4iGqPWPtqYpdIzQZzg5WvrvYH8Rnq7ckJpYk2YPZw6yNyA4ohG-BgFdTHc0U7CwZTFmodg1MuO0cTh7T98s2RXiTcaZa21MNO0yuXKm2Q10cbrWhnB5yHJUhSHx6JLxlgMTZ0oE0DoUOB6JmoLMYHcyL9hKRiPTh62ky_QmXRaifDNOdl4sH2w**"
12
+ @password = 'Test123'
13
+ OpenToken.password = @password
14
+ end
15
+ context "decoding token between expiration dates" do
16
+ setup do
17
+ Timecop.travel(Time.iso8601('2010-03-04T19:20:10Z')) do
18
+ assert_nothing_raised do
19
+ @token = OpenToken.decode @opentoken
20
+ end
21
+ end
22
+ end
23
+ should "decrypt subject from token payload" do
24
+ assert_equal 'john@example.com', @token[:subject]
25
+ end
26
+ should "decrypt subject using string or symbol" do
27
+ assert_equal 'john@example.com', @token['subject']
28
+ end
29
+ should "parse 'renew-until' date" do
30
+ assert_equal Time.iso8601('2010-03-05T07:19:15Z'), @token.valid_until
31
+ end
32
+ end
33
+
34
+ context "decoding token when current time is before expiration date" do
35
+ should "raise TokenExpiredError" do
36
+ Timecop.travel(Time.iso8601('2010-03-04T19:19:10Z')) do
37
+ assert_raises OpenToken::TokenExpiredError do
38
+ @token = OpenToken.decode @opentoken
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ context "decoding token when current time is equal to expiration date" do
45
+ should "raise TokenExpiredError" do
46
+ Timecop.travel(Time.iso8601('2010-03-04T19:24:15Z')) do
47
+ assert_raises OpenToken::TokenExpiredError do
48
+ @token = OpenToken.decode @opentoken
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ context "decoding token with attribute value containing apostrophe" do
55
+ setup do
56
+ Timecop.travel(Time.iso8601('2011-01-13T11:08:01Z')) do
57
+ @opentoken = "T1RLAQLIjiqgexqi1PQcEKCetvGoSYR2jhDFSIfE5ctlSBxEnq3S1ydjAADQUNRIKJx6_14aE3MQZnDABupGJrKNfoJHFS5VOnKexjMtboeOgst31Hf-D9CZBrpB7Jv0KBwnQ7DN3HizecPT76oX3UGtq_Vi5j5bKYCeObYm9W6h7NY-VzcZY5TTqIuulc2Jit381usAWZ2Sv1c_CWwhrH4hw-x7vUQMSjErvXK1qvsrFCpfNr7XlArx0HjI6kT5XEaHgQNdC0zrLw9cZ4rewoEisR3H5oM7B6gMaP82wTSFVBXvpn5r0KT-Iuc3JuG2en1zVh3GNf110oQCKQ**"
58
+ @token = OpenToken.decode @opentoken
59
+ end
60
+ end
61
+ should 'preserve apostrophe in attribute payload' do
62
+ assert_equal "D'angelo", @token[:last_name]
63
+ end
64
+ end
65
+
66
+ should 'raise invalid token error parsing nil token' do
67
+ assert_raises OpenToken::TokenInvalidError do
68
+ OpenToken.decode nil
69
+ end
70
+ end
71
+ end
72
+
73
+ context "encoding token" do
74
+ setup do
75
+ OpenToken.password = "Password1"
76
+ end
77
+ context "with aes-128-cbc and subject attribute" do
78
+ setup do
79
+ @attributesIn = { "subject" => "john", "email" => "john@example.com"}
80
+ @token = OpenToken.encode @attributesIn, OpenToken::Cipher::AES_128_CBC
81
+ end
82
+ should "be decodable" do
83
+ @attributesOut = OpenToken.decode @token
84
+ assert_equal @attributesIn, @attributesOut
85
+ end
86
+ end
87
+ end
88
+
89
+ context "encoding token with utf-8 values" do
90
+ setup do
91
+ OpenToken.password = "Password1"
92
+ end
93
+ context "with aes-128-cbc and subject attribute" do
94
+ setup do
95
+ @subject = OpenToken.send(:force_encoding, "Andr\xC3\xA9", 'UTF-8')
96
+ @attributesIn = { "subject" => @subject, "email" => "john@example.com"}
97
+ @token = OpenToken.encode @attributesIn, OpenToken::Cipher::AES_128_CBC
98
+ end
99
+ should "be decodable" do
100
+ @attributesOut = OpenToken.decode @token
101
+ assert_equal @attributesIn, @attributesOut
102
+ end
103
+ end
104
+ end
105
+ end
metadata ADDED
@@ -0,0 +1,159 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: opentoken-newrelic-rails23
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 2
9
+ - 2
10
+ version: 1.2.2
11
+ platform: ruby
12
+ authors:
13
+ - Ryan Sonnek
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-05-09 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: activesupport
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 31
29
+ segments:
30
+ - 2
31
+ - 3
32
+ - 14
33
+ version: 2.3.14
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: i18n
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ type: :runtime
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: shoulda
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - "="
57
+ - !ruby/object:Gem::Version
58
+ hash: 37
59
+ segments:
60
+ - 2
61
+ - 11
62
+ - 3
63
+ version: 2.11.3
64
+ type: :development
65
+ version_requirements: *id003
66
+ - !ruby/object:Gem::Dependency
67
+ name: timecop
68
+ prerelease: false
69
+ requirement: &id004 !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - "="
73
+ - !ruby/object:Gem::Version
74
+ hash: 25
75
+ segments:
76
+ - 0
77
+ - 3
78
+ - 5
79
+ version: 0.3.5
80
+ type: :development
81
+ version_requirements: *id004
82
+ - !ruby/object:Gem::Dependency
83
+ name: rake
84
+ prerelease: false
85
+ requirement: &id005 !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - "="
89
+ - !ruby/object:Gem::Version
90
+ hash: 63
91
+ segments:
92
+ - 0
93
+ - 9
94
+ - 2
95
+ version: 0.9.2
96
+ type: :development
97
+ version_requirements: *id005
98
+ description: parse opentoken properties passed for Single Signon requests
99
+ email:
100
+ - ryan@socialcast.com
101
+ executables: []
102
+
103
+ extensions: []
104
+
105
+ extra_rdoc_files: []
106
+
107
+ files:
108
+ - .document
109
+ - .gitignore
110
+ - CONTRIBUTORS.txt
111
+ - Gemfile
112
+ - LICENSE.txt
113
+ - README.md
114
+ - Rakefile
115
+ - lib/opentoken-newrelic-rails23.rb
116
+ - lib/opentoken-newrelic-rails23/cipher.rb
117
+ - lib/opentoken-newrelic-rails23/key_value_serializer.rb
118
+ - lib/opentoken-newrelic-rails23/password_key_generator.rb
119
+ - lib/opentoken-newrelic-rails23/token.rb
120
+ - lib/opentoken-newrelic-rails23/version.rb
121
+ - opentoken-newrelic-rails23.gemspec
122
+ - test/helper.rb
123
+ - test/test_opentoken.rb
124
+ homepage: http://github.com/newrelic/opentoken-newrelic-rails23
125
+ licenses: []
126
+
127
+ post_install_message:
128
+ rdoc_options: []
129
+
130
+ require_paths:
131
+ - lib
132
+ required_ruby_version: !ruby/object:Gem::Requirement
133
+ none: false
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ hash: 3
138
+ segments:
139
+ - 0
140
+ version: "0"
141
+ required_rubygems_version: !ruby/object:Gem::Requirement
142
+ none: false
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ hash: 3
147
+ segments:
148
+ - 0
149
+ version: "0"
150
+ requirements: []
151
+
152
+ rubyforge_project: opentoken-newrelic-rails23
153
+ rubygems_version: 1.8.10
154
+ signing_key:
155
+ specification_version: 3
156
+ summary: ruby implementation of the opentoken specification, forked for Rails 2.3 compatibility. Use the non-forked version for Rails 3+
157
+ test_files:
158
+ - test/helper.rb
159
+ - test/test_opentoken.rb