trivial_sso 4.0.0.3 → 4.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -0
- data/README.md +81 -65
- data/lib/trivial_sso/api.rb +19 -0
- data/lib/trivial_sso/error.rb +65 -11
- data/lib/trivial_sso/login.rb +64 -51
- data/lib/trivial_sso/struct/secret.rb +14 -0
- data/lib/trivial_sso/struct/un_wrap.rb +54 -0
- data/lib/trivial_sso/struct/wrap.rb +31 -0
- data/lib/trivial_sso/version.rb +1 -1
- data/lib/trivial_sso.rb +5 -0
- data/spec/lib/trivial_sso/api_spec.rb +34 -0
- data/spec/lib/trivial_sso/login_spec.rb +106 -0
- data/spec/spec_helper.rb +0 -9
- metadata +33 -15
- data/spec/models/trivial_sso_spec.rb +0 -66
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 936981875ed9f86e1f13252e3b2e98f86280b32e
|
4
|
+
data.tar.gz: 5c59ef3e7aec280fdba6918b50095ff354600ef0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 39c154dbe5a77da77ee19f011022384858d912831261c6f632f287e37598a1ec79c21a6da827ccd6d789e55f32143f9b22b24255948cad946c4be5f5766b9d00
|
7
|
+
data.tar.gz: 68e0396f2c4aab07a10084c3c482a1464274c19a851eb26930e66ad68658783fe76ec9a2706b41e025896707dff1942a941fa194f2c6c3526767c30c8770d087
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
[![Code Climate](https://codeclimate.com/github/nacengineer/trivialsso.png)](https://codeclimate.com/github/nacengineer/trivialsso)
|
2
|
+
|
1
3
|
## TrivialSso
|
2
4
|
|
3
5
|
A very simple gem to help with creating and reading cookies across multiple sites in a Ruby on Rails application.
|
@@ -26,84 +28,98 @@ After you've installed the gem, you need to generate a configuration file.
|
|
26
28
|
|
27
29
|
This will create an initializer file with a shared secret. You need to modify this to a big long string of characters. Keep this safe from others as they could forge cookies for your sites if they get ahold of this string. All sites that use the single sign on must have this same shared secret for the cookies to properly interoperate.
|
28
30
|
|
29
|
-
## Creating
|
31
|
+
## Creating an encrpyted string
|
32
|
+
### Uses [ActiveSupport::MessageEncryptor](http://api.rubyonrails.org/classes/ActiveSupport/MessageEncryptor.html)
|
33
|
+
|
34
|
+
A encrypted and signed string is created using of data supplied to it via a [ruby Hash](http://www.ruby-doc.org/core-2.0/Hash.html).
|
35
|
+
|
36
|
+
This only requirement is that the Hash must contain a **username** key.
|
37
|
+
|
38
|
+
When you create the encrypted string an expire time is added to the payload. You have the option of either providing one, or a default of 9 hours from the current time will be used.
|
30
39
|
|
31
|
-
|
40
|
+
Note: Setting the [ActionDispatch#expires](http://api.rubyonrails.org/classes/ActionDispatch/Cookies.html) on the cookie is just a convenience to make sure it gets cleared by the browser.
|
32
41
|
|
33
|
-
|
42
|
+
The actual expiration date that is enforced by the application is what is encoded in the cookie.4
|
34
43
|
|
35
|
-
|
36
|
-
# Create a hash of data we want to store in the cookie.
|
37
|
-
userdata = {
|
38
|
-
"username" => current_user.login,
|
39
|
-
"display" => current_user.display_name,
|
40
|
-
"groups" => current_user.memberof
|
41
|
-
}
|
44
|
+
Essentiall it is all boiled down to the follwoing...
|
42
45
|
|
43
|
-
|
44
|
-
cookie = TrivialSso::Login.cookie(userdata)
|
46
|
+
### create a userdata hash
|
45
47
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
48
|
+
```ruby
|
49
|
+
# Create a hash of data we want to store in the cookie.
|
50
|
+
userdata = {
|
51
|
+
"username" => current_user.login,
|
52
|
+
"display" => current_user.display_name,
|
53
|
+
"groups" => current_user.memberof
|
54
|
+
}
|
55
|
+
|
56
|
+
#### Generate the cookie data with Api.encode
|
57
|
+
cookie = TrivialSso::Api.encode(userdata)
|
58
|
+
|
59
|
+
#### Set the cookie
|
60
|
+
cookies[:sso_login] = {
|
61
|
+
:value => cookie,
|
62
|
+
:expires => TrivialSso::Login.default_expire,
|
63
|
+
:domain => 'mydomain.com',
|
64
|
+
:httponly => true
|
65
|
+
}
|
66
|
+
```
|
54
67
|
|
55
68
|
The above code creates a hash of data we will be putting in the cookie, generates the cookie, and then sets the cookie in the browser.
|
56
69
|
|
57
|
-
|
70
|
+
### Decoding a cookie
|
58
71
|
|
59
|
-
Retrieve the contents of the cookie by calling
|
72
|
+
Retrieve the contents of the cookie by calling Api.decode
|
60
73
|
|
61
|
-
|
74
|
+
```ruby
|
75
|
+
@userdata = TrivialSso::Api.decode(cookies[:sso_login])
|
76
|
+
```
|
62
77
|
|
63
|
-
This will
|
78
|
+
This will return the originally encoded data as a __Hash__ with string keys.
|
79
|
+
|
80
|
+
NOTE: This will throw an exception if the cookie has been tampered with, or if the expiration date has passed.
|
64
81
|
|
65
82
|
## Sample code for application_controller
|
66
83
|
|
67
84
|
Here are some methods you can add into your application controller to authenticate against the cookie.
|
68
85
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
86
|
+
```ruby
|
87
|
+
# If there is a problem with the cookie, redirect back to our central login server.
|
88
|
+
rescue_from TrivialSso::CookieError do |exception|
|
89
|
+
redirect_to 'https://login.mydomain.com/'
|
90
|
+
end
|
91
|
+
|
92
|
+
# authorize our users based on the cookie.
|
93
|
+
before_filter :auth_user!
|
94
|
+
|
95
|
+
# authenticate a user and set @current_user
|
96
|
+
def auth_user!
|
97
|
+
cu = current_user
|
98
|
+
|
99
|
+
# Check for authorization based on "groups" data that was put in the cookie
|
100
|
+
# by the central login application.
|
101
|
+
# you could also skip this check and just return true if the cookie is valid.
|
102
|
+
if cu['groups'].include? "ALLOWED_GROUP" #all lower case
|
103
|
+
@current_user = cu
|
104
|
+
true
|
105
|
+
else
|
106
|
+
render :file => "#{Rails.root}/public/403", :formats => [:html], :status => 403, :layout => false
|
107
|
+
end
|
108
|
+
|
109
|
+
false
|
110
|
+
end
|
111
|
+
|
112
|
+
# our current_user decodes the cookie.
|
113
|
+
def current_user
|
114
|
+
TrivialSso::Api.decode(cookies[:sso_login])
|
115
|
+
end
|
116
|
+
|
117
|
+
# Define the name we want to record in paper_trail (if using)
|
118
|
+
def user_for_paper_trail
|
119
|
+
if @current_user.blank?
|
120
|
+
"anonymous"
|
121
|
+
else
|
122
|
+
@current_user['username']
|
123
|
+
end
|
124
|
+
end
|
125
|
+
```
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module TrivialSso
|
2
|
+
class Api
|
3
|
+
|
4
|
+
class <<self
|
5
|
+
|
6
|
+
def encode(data, expire_time = nil, secret = nil)
|
7
|
+
TrivialSso::Login.new(
|
8
|
+
{data: data, expire_time: expire_time, secret: secret}
|
9
|
+
).wrap
|
10
|
+
end
|
11
|
+
|
12
|
+
def decode(data, secret = nil)
|
13
|
+
TrivialSso::Login.new({data: data, secret: secret}).unwrap
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
data/lib/trivial_sso/error.rb
CHANGED
@@ -8,13 +8,46 @@ module TrivialSso
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
|
12
|
-
class BadCookie < CookieError
|
11
|
+
class MissingConfig < RuntimeError
|
13
12
|
def to_s
|
14
|
-
"
|
13
|
+
"There is something wrong with the config of this application"
|
15
14
|
end
|
16
15
|
end
|
17
16
|
|
17
|
+
# Cookie can not be verified, data has been altered
|
18
|
+
module BadData
|
19
|
+
|
20
|
+
class Signature < CookieError
|
21
|
+
def to_s
|
22
|
+
"The data signature supplied was not valid."
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Cookie can not be verified, data has been altered
|
27
|
+
class Message < CookieError
|
28
|
+
def to_s
|
29
|
+
"The data supplied was not valid. i.e. bad cookie data given"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Given < CookieError
|
34
|
+
def to_s
|
35
|
+
<<-HERE
|
36
|
+
The data supplied is useless to me. i.e. Can't Wrap or Unwrap.
|
37
|
+
Try passing me some good data.
|
38
|
+
HERE
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Cookie is missing
|
43
|
+
class Missing < CookieError
|
44
|
+
def to_s
|
45
|
+
"No data was given."
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
18
51
|
# cookie is no longer valid
|
19
52
|
class LoginExpired < CookieError
|
20
53
|
def to_s
|
@@ -22,24 +55,27 @@ module TrivialSso
|
|
22
55
|
end
|
23
56
|
end
|
24
57
|
|
25
|
-
# Cookie is
|
26
|
-
class
|
58
|
+
# Cookie is lacking a username.
|
59
|
+
class NoUsernameData < CookieError
|
27
60
|
def to_s
|
28
|
-
"
|
61
|
+
"Need username to create cookie"
|
29
62
|
end
|
30
63
|
end
|
31
64
|
|
32
|
-
#
|
33
|
-
class
|
65
|
+
# Missing configuration value.
|
66
|
+
class MissingRailsConfig < CookieError
|
34
67
|
def to_s
|
35
|
-
|
68
|
+
<<-HERE
|
69
|
+
Missing secret configuration for cookie, need to define
|
70
|
+
config.sso_secret
|
71
|
+
HERE
|
36
72
|
end
|
37
73
|
end
|
38
74
|
|
39
75
|
# Missing configuration value.
|
40
|
-
class
|
76
|
+
class MissingSecret < CookieError
|
41
77
|
def to_s
|
42
|
-
"Missing secret
|
78
|
+
"Missing secret, need to define sso_secret"
|
43
79
|
end
|
44
80
|
end
|
45
81
|
|
@@ -50,5 +86,23 @@ module TrivialSso
|
|
50
86
|
end
|
51
87
|
end
|
52
88
|
|
89
|
+
|
90
|
+
class BadExpireTime < CookieError
|
91
|
+
def to_s
|
92
|
+
<<-HERE
|
93
|
+
The expire_time is useless to me. i.e. its not an Time object
|
94
|
+
transormed to integer time. Please retry with good data.
|
95
|
+
HERE
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
module Mode
|
100
|
+
class Wrong < CookieError
|
101
|
+
def to_s
|
102
|
+
"Can't use same object to decode and encode"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
53
107
|
end
|
54
108
|
end
|
data/lib/trivial_sso/login.rb
CHANGED
@@ -1,76 +1,89 @@
|
|
1
1
|
module TrivialSso
|
2
2
|
class Login
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
userdata
|
16
|
-
rescue NoMethodError
|
17
|
-
raise TrivialSso::Error::MissingConfig
|
18
|
-
rescue ActiveSupport::MessageVerifier::InvalidSignature ||
|
19
|
-
ActiveSupport::MessageEncryptor::InvalidMessage
|
20
|
-
raise TrivialSso::Error::BadCookie
|
21
|
-
end
|
4
|
+
attr_accessor :data, :secret, :expire_time, :struct
|
5
|
+
attr_reader :wrap, :unwrap, :mode, :secret, :default_expire
|
6
|
+
|
7
|
+
def initialize(opts = {})
|
8
|
+
opts[:expire_time] = default_expire unless opts[:expire_time]
|
9
|
+
opts[:secret] = default_secret unless opts[:secret]
|
10
|
+
opts.each {|k,v| send("#{k}=".to_sym, v) if available_opts.include?(k.to_sym)}
|
11
|
+
end
|
12
|
+
|
13
|
+
def expire_time=(value)
|
14
|
+
@expire_time = time_or_default(value) if time_value_good?(value)
|
22
15
|
end
|
23
16
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
raise
|
31
|
-
raise TrivialSso::Error::NoUsernameCookie if check_username(userdata)
|
32
|
-
enc.encrypt_and_sign([userdata, expire_date])
|
33
|
-
rescue NoMethodError
|
34
|
-
raise TrivialSso::Error::MissingConfig
|
17
|
+
def data=(value)
|
18
|
+
if value.is_a?(String)
|
19
|
+
@mode, @data = :unwrap, value
|
20
|
+
elsif value.is_a?(Hash)
|
21
|
+
@mode, @data = :wrap, OpenStruct.new(value)
|
22
|
+
else
|
23
|
+
raise Error::BadData::Given
|
35
24
|
end
|
36
25
|
end
|
37
26
|
|
38
|
-
|
27
|
+
def wrap
|
28
|
+
check_mode_set
|
29
|
+
raise Error::Mode::Wrong unless mode == __method__
|
30
|
+
TrivialSso::Wrap.new(data, sso_secret, expire_time).wrap
|
31
|
+
end
|
32
|
+
|
33
|
+
def unwrap
|
34
|
+
check_mode_set
|
35
|
+
raise Error::Mode::Wrong unless mode == __method__
|
36
|
+
response = TrivialSso::UnWrap.new(data, sso_secret).unwrap
|
37
|
+
struct ? OpenStruct.new(response) : response
|
38
|
+
end
|
39
39
|
|
40
|
-
def
|
41
|
-
|
42
|
-
userdata.empty? ||
|
43
|
-
! userdata.has_key?('username') ||
|
44
|
-
userdata['username'].empty?
|
40
|
+
def sso_secret
|
41
|
+
TrivialSso::Secret.new(secret).sso_secret
|
45
42
|
end
|
46
43
|
|
47
|
-
def
|
48
|
-
|
44
|
+
def default_expire
|
45
|
+
(Time.now + 32400).to_i # 9 hours from now
|
49
46
|
end
|
50
47
|
|
51
|
-
|
52
|
-
|
48
|
+
private
|
49
|
+
|
50
|
+
def default_secret
|
51
|
+
get_defined_sso_secret || SecureRandom.hex(64)
|
52
|
+
end
|
53
|
+
|
54
|
+
def get_defined_sso_secret
|
55
|
+
if rails_sso_secret_exists?
|
56
|
+
Rails.configuration.sso_secret
|
57
|
+
elsif defined? Rails
|
58
|
+
raise Error::MissingRailsConfig
|
59
|
+
else
|
60
|
+
false
|
61
|
+
end
|
53
62
|
end
|
54
63
|
|
55
|
-
def
|
56
|
-
(
|
64
|
+
def rails_sso_secret_exists?
|
65
|
+
defined?(Rails) && Rails.configuration.sso_secret
|
57
66
|
end
|
58
67
|
|
59
|
-
def
|
60
|
-
raise
|
61
|
-
ActiveSupport::MessageEncryptor.new(sso_secret, serializer: JSON)
|
68
|
+
def check_mode_set
|
69
|
+
raise Error::BadData::Given if mode.nil? || mode.empty?
|
62
70
|
end
|
63
71
|
|
64
|
-
def
|
65
|
-
|
66
|
-
|
67
|
-
|
72
|
+
def time_value_good?(value)
|
73
|
+
if (value.is_a?(Integer) || value.is_a?(String)) && value.to_s.length == 10
|
74
|
+
true
|
75
|
+
else
|
76
|
+
raise Error::BadExpireTime
|
68
77
|
end
|
69
|
-
Rails.configuration.sso_secret
|
70
78
|
end
|
71
79
|
|
72
|
-
def
|
73
|
-
|
80
|
+
def time_or_default(value)
|
81
|
+
value.to_i.nonzero? ? value.to_i : default_expire
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
def available_opts
|
86
|
+
[:data, :secret, :expire_time]
|
74
87
|
end
|
75
88
|
|
76
89
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
TrivialSso::Secret = Struct.new :sso_secret do
|
2
|
+
|
3
|
+
def sso_secret=(value)
|
4
|
+
# TODO Add something to verify value
|
5
|
+
@sso_secret = value if check_sso_secret(value)
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def check_sso_secret(value)
|
11
|
+
!value.nil? || !value.empty? || (raise TrivialSso::Error::MissingSecret)
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
TrivialSso::UnWrap = Struct.new :data, :sso_secret do
|
2
|
+
|
3
|
+
attr_reader :userdata, :timestamp
|
4
|
+
|
5
|
+
def unwrap
|
6
|
+
decode_data_and_timestamp
|
7
|
+
userdata
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def userdata=(value)
|
13
|
+
@userdata = (value.has_key?('table') ? value['table'] : value)
|
14
|
+
end
|
15
|
+
|
16
|
+
def timestamp=(time_as_int)
|
17
|
+
if (time_as_int - Time.now.to_i) <= 0
|
18
|
+
# TODO check if we can raise Error::LoginExpired
|
19
|
+
raise TrivialSso::Error::LoginExpired
|
20
|
+
else
|
21
|
+
@timestamp = time_as_int
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def decode_data_and_timestamp
|
26
|
+
begin
|
27
|
+
has_data?
|
28
|
+
self.userdata, self.timestamp = \
|
29
|
+
encrypted_message.decrypt_and_verify(data)
|
30
|
+
rescue NoMethodError
|
31
|
+
raise TrivialSso::Error::MissingConfig
|
32
|
+
rescue ActiveSupport::MessageVerifier::InvalidSignature
|
33
|
+
raise TrivialSso::Error::BadData::Signature
|
34
|
+
rescue ActiveSupport::MessageEncryptor::InvalidMessage
|
35
|
+
raise TrivialSso::Error::BadData::Message
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def has_data?
|
40
|
+
if data.nil? || data.empty?
|
41
|
+
raise TrivialSso::Error::BadData::Missing
|
42
|
+
else
|
43
|
+
true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def encrypted_message
|
48
|
+
unless defined? Rails
|
49
|
+
raise TrivialSso::Error::MissingRails
|
50
|
+
end
|
51
|
+
ActiveSupport::MessageEncryptor.new(sso_secret, serializer: JSON)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
TrivialSso::Wrap = Struct.new :data, :sso_secret, :expire_time do
|
2
|
+
|
3
|
+
attr_reader :wrap
|
4
|
+
|
5
|
+
def wrap
|
6
|
+
begin
|
7
|
+
encryptor.encrypt_and_sign([data, expire_time]) if sanity_check
|
8
|
+
rescue NoMethodError
|
9
|
+
raise TrivialSso::Error::MissingConfig
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def sanity_check
|
16
|
+
sso_secret && check_username || false
|
17
|
+
end
|
18
|
+
|
19
|
+
def encryptor
|
20
|
+
ActiveSupport::MessageEncryptor.new(sso_secret, serializer: JSON)
|
21
|
+
end
|
22
|
+
|
23
|
+
def check_username
|
24
|
+
has_data? || (raise TrivialSso::Error::NoUsernameData)
|
25
|
+
end
|
26
|
+
|
27
|
+
def has_data?
|
28
|
+
data && !(data.username.nil? || data.username.empty?)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
data/lib/trivial_sso/version.rb
CHANGED
data/lib/trivial_sso.rb
CHANGED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'trivial_sso'
|
3
|
+
require 'forgery'
|
4
|
+
|
5
|
+
# mock out rails app for sso_secret
|
6
|
+
module TrivialSso
|
7
|
+
class Application < Rails::Application
|
8
|
+
config.sso_secret = SecureRandom.hex(64)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe TrivialSso::Api do
|
13
|
+
let(:sso_secret) {SecureRandom.hex(64)}
|
14
|
+
let(:expire_time) {(Time.now - 1000).to_i}
|
15
|
+
let(:userdata) {
|
16
|
+
{
|
17
|
+
'username' => Forgery::Internet.user_name,
|
18
|
+
'groups' => ['one', 'two']
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
|
23
|
+
it "gives cookie" do
|
24
|
+
TrivialSso::Api.encode(userdata).should be_kind_of String
|
25
|
+
end
|
26
|
+
|
27
|
+
it "gives data" do
|
28
|
+
data = TrivialSso::Api.decode(TrivialSso::Api.encode(userdata))
|
29
|
+
data['username'].should eq userdata['username']
|
30
|
+
data['groups'].should eq userdata['groups']
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'active_support/core_ext'
|
2
|
+
require 'trivial_sso'
|
3
|
+
require 'forgery'
|
4
|
+
require 'securerandom'
|
5
|
+
|
6
|
+
require 'rails'
|
7
|
+
|
8
|
+
# mock out rails app for sso_secret
|
9
|
+
module TrivialSso
|
10
|
+
class Application < Rails::Application
|
11
|
+
config.sso_secret = SecureRandom.hex(64)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe TrivialSso::Login do
|
16
|
+
let(:sso_secret) {SecureRandom.hex(64)}
|
17
|
+
let(:userdata) {
|
18
|
+
{
|
19
|
+
'username' => Forgery::Internet.user_name,
|
20
|
+
'groups' => ['one', 'two']
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
let(:encode) {
|
25
|
+
TrivialSso::Login.new({secret: sso_secret, data: userdata})
|
26
|
+
}
|
27
|
+
|
28
|
+
let(:decode) {
|
29
|
+
TrivialSso::Login.new({secret: sso_secret, data: encode.wrap})
|
30
|
+
}
|
31
|
+
|
32
|
+
let(:expired_encode) {
|
33
|
+
TrivialSso::Login.new(
|
34
|
+
{data: userdata, secret: sso_secret, expire_time: (Time.now - 1000).to_i}
|
35
|
+
)
|
36
|
+
}
|
37
|
+
|
38
|
+
it "does set the data" do
|
39
|
+
encode.mode.should eq :wrap
|
40
|
+
encode.data.should be_kind_of OpenStruct
|
41
|
+
encode.data.username.should eq userdata['username']
|
42
|
+
encode.data.groups.should eq userdata['groups']
|
43
|
+
end
|
44
|
+
|
45
|
+
it "does create cookie with userdata" do
|
46
|
+
encode.wrap.should be_kind_of String
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should encode and decode" do
|
50
|
+
decode.unwrap.should be_kind_of Hash
|
51
|
+
decode.unwrap.should eq userdata
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should encode and decode as OpenStruct" do
|
55
|
+
decode.struct = true
|
56
|
+
decode.unwrap.should be_kind_of OpenStruct
|
57
|
+
decode.unwrap.should eq OpenStruct.new userdata
|
58
|
+
end
|
59
|
+
|
60
|
+
it "throw exception on missing username" do
|
61
|
+
expect {
|
62
|
+
encode.data = {username: ""}
|
63
|
+
encode.wrap
|
64
|
+
}.to raise_error TrivialSso::Error::NoUsernameData
|
65
|
+
end
|
66
|
+
|
67
|
+
it "throw exception on blank cookie" do
|
68
|
+
expect {
|
69
|
+
decode.data = ""
|
70
|
+
decode.unwrap
|
71
|
+
}.to raise_error TrivialSso::Error::BadData::Missing
|
72
|
+
end
|
73
|
+
|
74
|
+
it "raise exception bad signature when given cookie w/o signature as data source" do
|
75
|
+
expect {
|
76
|
+
decode.data = "BAhbB0kiC2RqbGVlMgY6BkVUbCsHo17iTg"
|
77
|
+
decode.unwrap
|
78
|
+
}.to raise_error TrivialSso::Error::BadData::Signature
|
79
|
+
end
|
80
|
+
|
81
|
+
it "raise exception bad message when given cookie w/o encryped data" do
|
82
|
+
expect {
|
83
|
+
decode.data = "--5b3164f6d1f09fb00d6905d073b18bc45a859b50"
|
84
|
+
decode.unwrap
|
85
|
+
}.to raise_error TrivialSso::Error::BadData::Signature
|
86
|
+
end
|
87
|
+
|
88
|
+
it "raise exception on expired cookie" do
|
89
|
+
expect {
|
90
|
+
decode.data = expired_encode.wrap
|
91
|
+
decode.unwrap
|
92
|
+
}.to raise_error TrivialSso::Error::LoginExpired
|
93
|
+
end
|
94
|
+
|
95
|
+
it "raises if expire_time not valid" do
|
96
|
+
expect {
|
97
|
+
encode.expire_time = %w(this is bad data)
|
98
|
+
}.to raise_error TrivialSso::Error::BadExpireTime
|
99
|
+
end
|
100
|
+
|
101
|
+
it "uses rails sso if Rails.defined" do
|
102
|
+
encode.secret = Rails.configuration.sso_secret
|
103
|
+
encode.sso_secret.should eq Rails.configuration.sso_secret
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -18,7 +18,6 @@ Spork.prefork do
|
|
18
18
|
require 'rspec/autorun'
|
19
19
|
require 'rspec/mocks'
|
20
20
|
require 'factory_girl'
|
21
|
-
require 'perftools'
|
22
21
|
|
23
22
|
# Requires supporting ruby files with custom matchers and macros, etc,
|
24
23
|
# in spec/support/ and its subdirectories.
|
@@ -31,14 +30,6 @@ Spork.prefork do
|
|
31
30
|
|
32
31
|
config.order = "random"
|
33
32
|
|
34
|
-
config.before :suite do
|
35
|
-
PerfTools::CpuProfiler.start("#{profile_directory}/rspec_profile")
|
36
|
-
end
|
37
|
-
|
38
|
-
config.after :suite do
|
39
|
-
PerfTools::CpuProfiler.stop
|
40
|
-
end
|
41
|
-
|
42
33
|
def profile_directory
|
43
34
|
directory = "/tmp/trivial_sso"
|
44
35
|
system("mkdir #{directory}") unless Dir.exist? directory
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trivial_sso
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0.
|
4
|
+
version: 4.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David J. Lee
|
@@ -9,34 +9,46 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2015-02-26 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- -
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '4.0'
|
21
|
+
- - ">="
|
19
22
|
- !ruby/object:Gem::Version
|
20
23
|
version: 4.0.0
|
21
24
|
type: :runtime
|
22
25
|
prerelease: false
|
23
26
|
version_requirements: !ruby/object:Gem::Requirement
|
24
27
|
requirements:
|
25
|
-
- -
|
28
|
+
- - "~>"
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '4.0'
|
31
|
+
- - ">="
|
26
32
|
- !ruby/object:Gem::Version
|
27
33
|
version: 4.0.0
|
28
34
|
- !ruby/object:Gem::Dependency
|
29
35
|
name: activesupport
|
30
36
|
requirement: !ruby/object:Gem::Requirement
|
31
37
|
requirements:
|
32
|
-
- -
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4.0'
|
41
|
+
- - ">="
|
33
42
|
- !ruby/object:Gem::Version
|
34
43
|
version: 4.0.0
|
35
44
|
type: :runtime
|
36
45
|
prerelease: false
|
37
46
|
version_requirements: !ruby/object:Gem::Requirement
|
38
47
|
requirements:
|
39
|
-
- -
|
48
|
+
- - "~>"
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '4.0'
|
51
|
+
- - ">="
|
40
52
|
- !ruby/object:Gem::Version
|
41
53
|
version: 4.0.0
|
42
54
|
description: |-
|
@@ -49,17 +61,22 @@ executables: []
|
|
49
61
|
extensions: []
|
50
62
|
extra_rdoc_files: []
|
51
63
|
files:
|
64
|
+
- CHANGELOG.md
|
65
|
+
- MIT-LICENSE
|
66
|
+
- README.md
|
52
67
|
- lib/generators/templates/README
|
53
68
|
- lib/generators/templates/sso_secret.rb
|
54
69
|
- lib/generators/trivial_sso/install_generator.rb
|
70
|
+
- lib/trivial_sso.rb
|
71
|
+
- lib/trivial_sso/api.rb
|
55
72
|
- lib/trivial_sso/error.rb
|
56
73
|
- lib/trivial_sso/login.rb
|
74
|
+
- lib/trivial_sso/struct/secret.rb
|
75
|
+
- lib/trivial_sso/struct/un_wrap.rb
|
76
|
+
- lib/trivial_sso/struct/wrap.rb
|
57
77
|
- lib/trivial_sso/version.rb
|
58
|
-
- lib/trivial_sso.rb
|
59
|
-
-
|
60
|
-
- README.md
|
61
|
-
- CHANGELOG.md
|
62
|
-
- spec/models/trivial_sso_spec.rb
|
78
|
+
- spec/lib/trivial_sso/api_spec.rb
|
79
|
+
- spec/lib/trivial_sso/login_spec.rb
|
63
80
|
- spec/spec_helper.rb
|
64
81
|
- spec/support/deferred_garbage_collection.rb
|
65
82
|
homepage: https://github.com/nacengineer/trivial_sso
|
@@ -72,21 +89,22 @@ require_paths:
|
|
72
89
|
- lib
|
73
90
|
required_ruby_version: !ruby/object:Gem::Requirement
|
74
91
|
requirements:
|
75
|
-
- -
|
92
|
+
- - ">="
|
76
93
|
- !ruby/object:Gem::Version
|
77
94
|
version: 1.9.3
|
78
95
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
96
|
requirements:
|
80
|
-
- -
|
97
|
+
- - ">="
|
81
98
|
- !ruby/object:Gem::Version
|
82
99
|
version: '0'
|
83
100
|
requirements: []
|
84
101
|
rubyforge_project:
|
85
|
-
rubygems_version: 2.
|
102
|
+
rubygems_version: 2.4.5
|
86
103
|
signing_key:
|
87
104
|
specification_version: 4
|
88
105
|
summary: A simple library to help with Single Sign On cookies
|
89
106
|
test_files:
|
90
|
-
- spec/
|
107
|
+
- spec/lib/trivial_sso/api_spec.rb
|
108
|
+
- spec/lib/trivial_sso/login_spec.rb
|
91
109
|
- spec/spec_helper.rb
|
92
110
|
- spec/support/deferred_garbage_collection.rb
|
@@ -1,66 +0,0 @@
|
|
1
|
-
require 'active_support/core_ext'
|
2
|
-
|
3
|
-
require 'trivial_sso'
|
4
|
-
require 'forgery'
|
5
|
-
require 'securerandom'
|
6
|
-
|
7
|
-
describe TrivialSso do
|
8
|
-
|
9
|
-
def before
|
10
|
-
# Stub out our Rails config so we can test things properly.
|
11
|
-
Rails.stubs(:configuration).returns(Rails::Application::Configuration.allocate)
|
12
|
-
Rails.configuration.sso_secret = SecureRandom.hex(64)
|
13
|
-
@user_name = Forgery::Internet.user_name
|
14
|
-
@data = Forgery::LoremIpsum.words(20)
|
15
|
-
@userdata = {'username' => @user_name, 'data' => @data}
|
16
|
-
@expired_cookie = TrivialSso::Login.cookie(
|
17
|
-
{'username' => 'testor'}, 2.seconds.ago
|
18
|
-
)
|
19
|
-
end
|
20
|
-
|
21
|
-
it "does create cookie with userdata" do
|
22
|
-
TrivialSso::Login.cookie(@userdata).should_not be_nil
|
23
|
-
end
|
24
|
-
|
25
|
-
# def test_create_cookie_and_decode_it
|
26
|
-
# mycookie = TrivialSso::Login.cookie(@userdata)
|
27
|
-
# data = TrivialSso::Login.decode_cookie(mycookie)
|
28
|
-
# assert_equal data['data'], @data
|
29
|
-
# end
|
30
|
-
|
31
|
-
# def test_throw_exception_on_missing_username
|
32
|
-
# assert_raise TrivialSso::Error::NoUsernameCookie do
|
33
|
-
# mycookie = TrivialSso::Login.cookie("")
|
34
|
-
# end
|
35
|
-
# end
|
36
|
-
|
37
|
-
# def test_expire_date_exists
|
38
|
-
# # in a full rails environment, this will return an ActiveSupport::TimeWithZone
|
39
|
-
# assert TrivialSso::Login.expire_date.is_a?(Time),
|
40
|
-
# "proper Time object not returned"
|
41
|
-
# end
|
42
|
-
|
43
|
-
# def test_expire_date_is_in_future
|
44
|
-
# assert (DateTime.now < TrivialSso::Login.expire_date),
|
45
|
-
# "Expire date is in the past - cookie will expire immediatly."
|
46
|
-
# end
|
47
|
-
|
48
|
-
# def test_raise_exception_on_blank_cookie
|
49
|
-
# assert_raise TrivialSso::Error::MissingCookie do
|
50
|
-
# TrivialSso::Login.decode_cookie("")
|
51
|
-
# end
|
52
|
-
# end
|
53
|
-
|
54
|
-
# def test_raise_exception_on_bad_cookie
|
55
|
-
# assert_raise TrivialSso::Error::BadCookie do
|
56
|
-
# TrivialSso::Login.decode_cookie("BAhbB0kiC2RqbGVlMgY6BkVUbCsHo17iTg")
|
57
|
-
# end
|
58
|
-
# end
|
59
|
-
|
60
|
-
# def test_raise_exception_on_expired_cookie
|
61
|
-
# assert_raise TrivialSso::Error::LoginExpired do
|
62
|
-
# TrivialSso::Login.decode_cookie(@expired_cookie)
|
63
|
-
# end
|
64
|
-
# end
|
65
|
-
|
66
|
-
end
|