trivialsso 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in trivialsso.gemspec
4
+ gemspec
data/MIT-LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2012 David J. Lee. http://lee.dj/
2
+
3
+ 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:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ 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.
data/README.md ADDED
@@ -0,0 +1,103 @@
1
+ ## Trivialsso
2
+
3
+ A very simple gem to help with creating and reading cookies across multiple sites in a Ruby on Rails application.
4
+
5
+ This allows for a simple single sign on solution among sites within the same domain.
6
+
7
+ This does *not* work across domains.
8
+
9
+
10
+ ## Getting Started
11
+
12
+ Add the gem to your Gemfile
13
+
14
+ gem 'trivialsso'
15
+
16
+ Install the gem
17
+
18
+ bundle install
19
+
20
+ After you've installed the gem, you need to generate a configuration file.
21
+
22
+ rails g trivialsso:install
23
+
24
+ This will create an initializer file with a shared secret. You need to modify this to a big long
25
+ string of characters. Keep this safe from others as they could forge cookies for your sites if they
26
+ get ahold of this string. All sites that use the single sign on must have this same shared secret
27
+ for the cookies to properly interoperate.
28
+
29
+ ## Creating a cookie
30
+
31
+ A cookie is created using a hash of data supplied to it. This must contain a "username" key.
32
+
33
+ When you create the cookie data an expire time is built into the payload. Setting the :expires on the cookie is
34
+ just a convenience to make sure it gets cleared by the browser. The actual expiration date that matters is what is encoded in the cookie.
35
+
36
+
37
+ # Create a hash of data we want to store in the cookie.
38
+ userdata = {"username" => current_user.login, "display" => current_user.display_name, "groups" => current_user.memberof}
39
+
40
+ #Generate the cookie data
41
+ cookie = Trivialsso::Login.cookie(userdata)
42
+
43
+ # Set the cookie
44
+ cookies[:sso_login] = {
45
+ :value => cookie,
46
+ :expires => Trivialsso::Login.expire_date,
47
+ :domain => 'mydomain.com',
48
+ :httponly => true,
49
+ }
50
+
51
+ 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.
52
+
53
+ ## Decoding a cookie
54
+
55
+ Retrieve the contents of the cookie by calling decode_cookie
56
+
57
+ @userdata = Trivialsso::Login.decode_cookie(cookies[:sso_login])
58
+
59
+ This will throw an exception if the cookie has been tampered with, or if the expiration date has passed.
60
+
61
+ ## Sample code for application_controller
62
+
63
+ Here are some methods you can add into your application controller to authenticate against the cookie.
64
+
65
+ # If there is a problem with the cookie, redirect back to our central login server.
66
+ rescue_from Trivialsso::CookieError do |exception|
67
+ redirect_to 'https://login.mydomain.com/'
68
+ end
69
+
70
+ # authorize our users based on the cookie.
71
+ before_filter :auth_user!
72
+
73
+ # authenticate a user and set @current_user
74
+ def auth_user!
75
+ cu = current_user
76
+
77
+ # Check for authorization based on "groups" data that was put in the cookie
78
+ # by the central login application.
79
+ # you could also skip this check and just return true if the cookie is valid.
80
+ if cu['groups'].include? "ALLOWED_GROUP" #all lower case
81
+ @current_user = cu
82
+ return true
83
+ else
84
+ render :file => "#{Rails.root}/public/403", :formats => [:html], :status => 403, :layout => false
85
+ end
86
+
87
+ return false
88
+ end
89
+
90
+ # our current_user decodes the cookie.
91
+ def current_user
92
+ Trivialsso::Login.decode_cookie(cookies[:ophth_login])
93
+ end
94
+
95
+ # Define the name we want to record in paper_trail (if using)
96
+ def user_for_paper_trail
97
+ if @current_user.blank?
98
+ return "anonymous"
99
+ else
100
+ return @current_user['username']
101
+ end
102
+ end
103
+
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require "rake/testtask"
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << 'test'
7
+ end
@@ -0,0 +1,9 @@
1
+ ===============================================================================
2
+
3
+ What you need to do next:
4
+
5
+ Edit config/initalizers/sso_secret.rb to include a secret that will
6
+ be shared between all of the sites. This secret must match for each
7
+ site using the cookies.
8
+
9
+ ===============================================================================
@@ -0,0 +1,5 @@
1
+ # This file is required to define the shared secret used for generating and decoding
2
+ # SSO cookies. This secret *must* be the same across all of your applications.
3
+
4
+ # TODO, figure out how to get app_name
5
+ <%= Rails.application.class.parent_name %>::Application.config.sso_secret = "You-really-need-to-change-this-to-a-nice-long-random-string"
@@ -0,0 +1,20 @@
1
+ module Trivialsso
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("../../templates",__FILE__)
5
+
6
+ desc "Creates a sso_secret initalizer"
7
+
8
+ def copy_intializer
9
+ #template - maybe use that in the future?
10
+ template "sso_secret.rb", "config/initializers/sso_secret.rb"
11
+ end
12
+
13
+
14
+ def show_readme
15
+ readme "README"
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,3 @@
1
+ module Trivialsso
2
+ VERSION = "0.1.0"
3
+ end
data/lib/trivialsso.rb ADDED
@@ -0,0 +1,110 @@
1
+ require "trivialsso/version"
2
+
3
+ module Trivialsso
4
+ class Login
5
+ # signing and un-signing may have to be refactored to use OpenSSL:HMAC...
6
+ # as this will not work well across platforms. (ideally this should work for PHP as well)
7
+
8
+ # create an encrypted and signed cookie containing userdata and an expiry date.
9
+ # userdata should be an array, and at minimum include a 'username' key.
10
+ # using json serializer to hopefully allow future cross version compatibliity
11
+ # (Marshall, the default serializer, is not compatble between versions)
12
+ def self.cookie(userdata, exp_date = expire_date)
13
+ begin
14
+ raise MissingConfig if Rails.configuration.sso_secret.blank?
15
+ rescue NoMethodError
16
+ raise MissingConfig
17
+ end
18
+ raise NoUsernameCookie if userdata['username'].blank?
19
+ enc = ActiveSupport::MessageEncryptor.new(Rails.configuration.sso_secret, :serializer => JSON)
20
+ cookie = enc.encrypt_and_sign([userdata,exp_date.to_i])
21
+ return cookie
22
+ end
23
+
24
+ # Decodes and verifies an encrypted cookie
25
+ # throw a proper exception if a bad or invalid cookie.
26
+ # otherwise, return the username and userdata stored in the cookie
27
+ def self.decode_cookie(cookie)
28
+ begin
29
+ raise MissingConfig if Rails.configuration.sso_secret.blank?
30
+ rescue NoMethodError
31
+ raise MissingConfig
32
+ end
33
+ if cookie.blank?
34
+ raise MissingCookie
35
+ else
36
+ enc = ActiveSupport::MessageEncryptor.new(Rails.configuration.sso_secret, :serializer => JSON)
37
+ begin
38
+ userdata, timestamp = enc.decrypt_and_verify(cookie)
39
+ rescue ActiveSupport::MessageVerifier::InvalidSignature
40
+ #raise our own cookie error instead of passing on invalid signature.
41
+ raise BadCookie
42
+ rescue ActiveSupport::MessageEncryptor::InvalidMessage
43
+ #raise our own cookie error instead of passing on invalid message.
44
+ raise BadCookie
45
+ end
46
+
47
+ # Determine how many seconds our cookie is valid for.
48
+ timeRemain = timestamp - DateTime.now.to_i
49
+
50
+ #make sure current time is not past timestamp.
51
+ if timeRemain > 0
52
+ return userdata
53
+ else
54
+ raise LoginExpired
55
+ end
56
+ end
57
+ end
58
+
59
+ #returns the exipiry date from now. Used for setting an expiry date when creating cookies.
60
+ def self.expire_date
61
+ 9.hours.from_now
62
+ end
63
+ end
64
+
65
+
66
+ #
67
+ # Error Classes
68
+ #
69
+ # General Cookie Error
70
+ class CookieError < RuntimeError
71
+ def to_s
72
+ "There was an error processing the Ophth Cookie"
73
+ end
74
+ end
75
+
76
+ # Cookie can not be verified, data has been altered
77
+ class BadCookie < CookieError
78
+ def to_s
79
+ "Login cookie can not be verified!"
80
+ end
81
+ end
82
+
83
+ # cookie is no longer valid
84
+ class LoginExpired < CookieError
85
+ def to_s
86
+ "Login cookie has expired!"
87
+ end
88
+ end
89
+
90
+ # Cookie is missing
91
+ class MissingCookie < CookieError
92
+ def to_s
93
+ "Login cookie is missing!"
94
+ end
95
+ end
96
+
97
+ # Cookie is lacking a username.
98
+ class NoUsernameCookie < CookieError
99
+ def to_s
100
+ "Need username to create cookie"
101
+ end
102
+ end
103
+
104
+ # Missing configuration value.
105
+ class MissingConfig < CookieError
106
+ def to_s
107
+ "Missing secret configuration for cookie, need to define config.sso_secret"
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,96 @@
1
+ require 'active_support/core_ext'
2
+ require 'rails/all'
3
+
4
+ require 'test/unit'
5
+ require 'mocha' # Need the mocha gem to properly stub out rails configuration.
6
+ require 'trivialsso'
7
+
8
+
9
+ ##
10
+ # Test suite for Trivialsso. Which requires rails (for config values) and active_support for time calculations.
11
+ #
12
+ class TrivialssoTest < Test::Unit::TestCase
13
+
14
+ def setup
15
+ # Stub out our Rails config so we can test things properly.
16
+ Rails.stubs(:configuration).returns(Rails::Application::Configuration.allocate)
17
+ Rails.configuration.sso_secret = "57f236fdb162bd951f2ed15683a9d9d327f26ecdccb1897107161b76223b07a46d34907c3357e66b4eb0ef6e06888a700c0dc"
18
+ end
19
+
20
+
21
+ def test_can_create_cookie
22
+ mycookie = Trivialsso::Login.cookie({'username' => 'testor'})
23
+ assert !mycookie.nil?
24
+ end
25
+
26
+ def test_create_cookie_with_userdata
27
+ mycookie = Trivialsso::Login.cookie({'username' => 'testor', 'data' => 'additional cookie data'})
28
+ assert !mycookie.nil?
29
+ end
30
+
31
+ def test_create_cookie_and_decode_it
32
+ mycookie = Trivialsso::Login.cookie({'username' => 'testor', 'data' => 'additional cookie data'})
33
+ data = Trivialsso::Login.decode_cookie(mycookie)
34
+
35
+ if data['data'] == 'additional cookie data'
36
+ assert true
37
+ else
38
+ assert false
39
+ end
40
+ end
41
+
42
+ def test_throw_exception_on_missing_username
43
+ begin
44
+ mycookie = Trivialsso::Login.cookie("")
45
+ rescue Trivialsso::NoUsernameCookie
46
+ assert true, "Exception thrown due to blank username"
47
+ else
48
+ assert false, "No exception was raised for a blank username"
49
+ end
50
+ end
51
+
52
+
53
+ def test_expire_date_exists
54
+ # in a full rails environment, this will return an ActiveSupport::TimeWithZone
55
+ mydate = Trivialsso::Login.expire_date
56
+ assert mydate.is_a?(Time), "proper Time object not returned"
57
+ end
58
+
59
+ def test_expire_date_is_in_future
60
+ assert (DateTime.now < Trivialsso::Login.expire_date), "Expire date is in the past - cookie will expire immediatly."
61
+ end
62
+
63
+ def test_exception_on_blank_cookie
64
+ begin
65
+ Trivialsso::Login.decode_cookie("")
66
+ rescue Trivialsso::MissingCookie
67
+ assert true
68
+ else
69
+ assert false, "no exception was raised for a blank cookie"
70
+ end
71
+ end
72
+
73
+ def test_exception_on_bad_cookie
74
+ begin
75
+ Trivialsso::Login.decode_cookie("BAhbB0kiC2RqbGVlMgY6BkVUbCsHo17iTg")
76
+ rescue Trivialsso::BadCookie
77
+ assert true
78
+ else
79
+ assert false, "no exception was raised for a malformed cookie"
80
+ end
81
+ end
82
+
83
+ def test_exception_on_expired_cookie
84
+ #create a cookie that is expired.
85
+ temp_cookie = Trivialsso::Login.cookie({'username' => 'testor'}, 2.seconds.ago)
86
+
87
+ begin
88
+ Trivialsso::Login.decode_cookie(temp_cookie)
89
+ rescue Trivialsso::LoginExpired
90
+ assert true
91
+ else
92
+ assert false, "no exception was raised for an expired cookie"
93
+ end
94
+ end
95
+
96
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "trivialsso/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "trivialsso"
7
+ s.version = Trivialsso::VERSION
8
+ s.authors = ["David J. Lee"]
9
+ s.email = ["david@lee.dj"]
10
+ s.homepage = "https://github.com/DavidJLee/trivialsso"
11
+ s.summary = "A simple library to help with Single Sign On cookies"
12
+ s.description = "Used to encode and decode cookies used in a single sign on implementation within the same domain"
13
+
14
+ s.rubyforge_project = "trivialsso"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ # s.add_development_dependency "rspec"
23
+ # s.add_runtime_dependency "rails"
24
+
25
+ # Need at least 3.2.0 to support the JSON serialization.
26
+ s.add_dependency "rails", "~> 3.2.0"
27
+ s.add_dependency "activesupport", "~> 3.2.0"
28
+
29
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: trivialsso
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - David J. Lee
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-08 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: &70357652379460 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 3.2.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70357652379460
25
+ - !ruby/object:Gem::Dependency
26
+ name: activesupport
27
+ requirement: &70357652378960 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 3.2.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70357652378960
36
+ description: Used to encode and decode cookies used in a single sign on implementation
37
+ within the same domain
38
+ email:
39
+ - david@lee.dj
40
+ executables: []
41
+ extensions: []
42
+ extra_rdoc_files: []
43
+ files:
44
+ - .gitignore
45
+ - Gemfile
46
+ - MIT-LICENSE
47
+ - README.md
48
+ - Rakefile
49
+ - lib/generators/templates/README
50
+ - lib/generators/templates/sso_secret.rb
51
+ - lib/generators/trivialsso/install_generator.rb
52
+ - lib/trivialsso.rb
53
+ - lib/trivialsso/version.rb
54
+ - test/test_trivialsso.rb
55
+ - trivialsso.gemspec
56
+ homepage: https://github.com/DavidJLee/trivialsso
57
+ licenses: []
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project: trivialsso
76
+ rubygems_version: 1.8.6
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: A simple library to help with Single Sign On cookies
80
+ test_files:
81
+ - test/test_trivialsso.rb