shibbolite 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +108 -0
  3. data/Rakefile +21 -0
  4. data/app/concerns/shibbolite/filters.rb +44 -0
  5. data/app/concerns/shibbolite/helpers.rb +48 -0
  6. data/app/concerns/shibbolite/user.rb +21 -0
  7. data/app/controllers/shibbolite/shibboleth_controller.rb +53 -0
  8. data/app/views/layouts/shibbolite/shibboleth.html.erb +32 -0
  9. data/app/views/shibbolite/shibboleth/access_denied.html.erb +7 -0
  10. data/app/views/shibbolite/shibboleth/logout_message.html.erb +7 -0
  11. data/config/routes.rb +6 -0
  12. data/lib/generators/shibbolite/install_generator.rb +17 -0
  13. data/lib/generators/shibbolite/migration_generator.rb +14 -0
  14. data/lib/generators/templates/shibbolite_config.rb +51 -0
  15. data/lib/shibbolite/engine.rb +13 -0
  16. data/lib/shibbolite/version.rb +3 -0
  17. data/lib/shibbolite.rb +58 -0
  18. data/spec/controllers/filters_test_controller_spec.rb +146 -0
  19. data/spec/controllers/helpers_test_controller_spec.rb +146 -0
  20. data/spec/controllers/shibbolite/shibboleth_controller_spec.rb +114 -0
  21. data/spec/controllers/static_controller_spec.rb +5 -0
  22. data/spec/dummy/Rakefile +6 -0
  23. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  24. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  25. data/spec/dummy/app/assets/stylesheets/scaffold.css +56 -0
  26. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  27. data/spec/dummy/app/controllers/filters_test_controller.rb +37 -0
  28. data/spec/dummy/app/controllers/helpers_test_controller.rb +44 -0
  29. data/spec/dummy/app/controllers/static_controller.rb +21 -0
  30. data/spec/dummy/app/models/user.rb +3 -0
  31. data/spec/dummy/app/views/filters_test/dummy.html.erb +0 -0
  32. data/spec/dummy/app/views/helpers_test/dummy.html.erb +0 -0
  33. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  34. data/spec/dummy/app/views/static/admin_resource.html.erb +3 -0
  35. data/spec/dummy/app/views/static/home.html.erb +35 -0
  36. data/spec/dummy/app/views/static/user_resource.html.erb +5 -0
  37. data/spec/dummy/bin/bundle +3 -0
  38. data/spec/dummy/bin/rails +4 -0
  39. data/spec/dummy/bin/rake +4 -0
  40. data/spec/dummy/config/application.rb +17 -0
  41. data/spec/dummy/config/boot.rb +5 -0
  42. data/spec/dummy/config/database.yml +25 -0
  43. data/spec/dummy/config/environment.rb +5 -0
  44. data/spec/dummy/config/environments/development.rb +29 -0
  45. data/spec/dummy/config/environments/production.rb +80 -0
  46. data/spec/dummy/config/environments/test.rb +36 -0
  47. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  48. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  49. data/spec/dummy/config/initializers/session_store.rb +3 -0
  50. data/spec/dummy/config/initializers/shibbolite_config.rb +51 -0
  51. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  52. data/spec/dummy/config/routes.rb +6 -0
  53. data/spec/dummy/config.ru +4 -0
  54. data/spec/dummy/db/development.sqlite3 +0 -0
  55. data/spec/dummy/db/migrate/20140404162119_create_users.rb +9 -0
  56. data/spec/dummy/db/migrate/20140414172304_add_shibboleth_attributes_to_users.rb +12 -0
  57. data/spec/dummy/db/schema.rb +30 -0
  58. data/spec/dummy/db/test.sqlite3 +0 -0
  59. data/spec/dummy/log/development.log +19082 -0
  60. data/spec/dummy/log/test.log +46778 -0
  61. data/spec/dummy/public/404.html +58 -0
  62. data/spec/dummy/public/422.html +58 -0
  63. data/spec/dummy/public/500.html +57 -0
  64. data/spec/dummy/public/favicon.ico +0 -0
  65. data/spec/dummy/tmp/cache/assets/development/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
  66. data/spec/dummy/tmp/cache/assets/development/sprockets/1be2f6345400afa38cfd6c919f2cf297 +0 -0
  67. data/spec/dummy/tmp/cache/assets/development/sprockets/27c12eb9977c123bfb2ef83640964c02 +0 -0
  68. data/spec/dummy/tmp/cache/assets/development/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
  69. data/spec/dummy/tmp/cache/assets/development/sprockets/35634bbec2c7419d3efa1a72c23db7e0 +0 -0
  70. data/spec/dummy/tmp/cache/assets/development/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
  71. data/spec/dummy/tmp/cache/assets/development/sprockets/368329c663775348c7db5500ff959f80 +0 -0
  72. data/spec/dummy/tmp/cache/assets/development/sprockets/371bf96e99717688ed7313a0c53f4212 +0 -0
  73. data/spec/dummy/tmp/cache/assets/development/sprockets/510da110ae528e2d22533be39ff696c5 +0 -0
  74. data/spec/dummy/tmp/cache/assets/development/sprockets/6fc757c2c8329244ca95d6909865bbc2 +0 -0
  75. data/spec/dummy/tmp/cache/assets/development/sprockets/83de9c3d672e9a3420dd8a36aaaab517 +0 -0
  76. data/spec/dummy/tmp/cache/assets/development/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
  77. data/spec/dummy/tmp/cache/assets/development/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
  78. data/spec/dummy/tmp/cache/assets/development/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
  79. data/spec/dummy/tmp/cache/assets/test/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
  80. data/spec/dummy/tmp/cache/assets/test/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
  81. data/spec/dummy/tmp/cache/assets/test/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
  82. data/spec/dummy/tmp/cache/assets/test/sprockets/371bf96e99717688ed7313a0c53f4212 +0 -0
  83. data/spec/dummy/tmp/cache/assets/test/sprockets/6fc757c2c8329244ca95d6909865bbc2 +0 -0
  84. data/spec/dummy/tmp/cache/assets/test/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
  85. data/spec/dummy/tmp/cache/assets/test/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
  86. data/spec/dummy/tmp/cache/assets/test/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
  87. data/spec/factories/shibboleth_attributes.rb +28 -0
  88. data/spec/factories/users.rb +13 -0
  89. data/spec/models/user_spec.rb +46 -0
  90. data/spec/spec_helper.rb +32 -0
  91. metadata +320 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2014 David Bassett
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.rdoc ADDED
@@ -0,0 +1,108 @@
1
+ = Shibbolite
2
+
3
+ Are you running Rails apps in a Shibboleth environment? Do you want to use all those sweet Shibboleth provided attributes for super simple access control? Shibbolite is the gem for you.
4
+
5
+ == Caveats
6
+ So far Shibbolite has been tested in one environment (mine). That is:
7
+
8
+ * Apache 2.2
9
+ * Shibboleth NativeSP 2.5 (older versions will not work due to URL redirect options not existing before this)
10
+ * Phusion Passenger
11
+ * Rails 4.0.* (older won't work)
12
+
13
+ Shibbolite should work fine in any environment that provides the NativeSP attributes as environment variables. As of this writing that includes all versions of Apache by default, and that's pretty much it. Some quick googling tells me that Nginx support can be enabled with FastCGI, and possibly Mongrel as well, but it looks like this will provide the attributes as request headers, which will not work out of the box.
14
+
15
+ == Install
16
+ === Before you start...
17
+ Shibbolite expects that you already
18
+ 1. Have root set to something
19
+ 2. Created a User model at some point
20
+
21
+ === OK let's go
22
+ In your Gemfile
23
+ gem 'shibbolite'
24
+ Then run bundle to install it. Now run the handy generator
25
+ rails generate shibbolite:install
26
+ ...which will copy shibbolite_config.rb to your app's initializer folder. Take a look over the various options. You'll have to add your SP's attributes to the configuration, as well as specify the name of your user class if it isn't "User". You can also configure the list of valid group types you can assign to your users.
27
+
28
+ If your SP handler locations are configured differently from the common defaults you'll have to specify those as well. See your SP's /Status handler more information, as well as the {Shibboleth documentation}[https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPSessions].
29
+
30
+ Now generate the migration
31
+ rails generate shibbolite:migration
32
+ This will create a migration for your User model that adds all of the Shibbolith attributes. Take a look at it then run
33
+ rake db:migrate
34
+
35
+ Go to your User model and add
36
+ include Shibbolite::User
37
+
38
+ Go to your ApplicationController and add
39
+ include Shibbolite::Filters
40
+
41
+ Restart your application
42
+
43
+ == Usage
44
+ Shibbolite::User adds some validations as well as .find_user, which returns the user that has the supplied :primary_user_id
45
+
46
+ Shibbolite::Filters gives you a gaggle of before filters to enable access control and a handful of helpers for views. Below are some examples, see the source for a complete list.
47
+
48
+ === Filters
49
+ before_action :require_login
50
+
51
+ In your ApplicationController will require that users have a valid Shibboleth session before they can launch any action
52
+
53
+ before_action { |c| c.require_group :admin }
54
+
55
+ will require that the user belong to the admin group
56
+
57
+ before_action { |c| c.require_group :admin, :user }
58
+
59
+ will allow anyone who has the admin or the user group
60
+
61
+ before_action { |c| c.require_group_or_id :admin, some_user.id }
62
+
63
+ is useful for things like profile pages that are accessible to both an owner and anyone in the admin group
64
+
65
+ before_action :use_attributes_if_available, only: :public
66
+
67
+ will attempt to load a Shibboleth session if one is available but do nothing otherwise. Use this if you have a public page but still want to make use of attributes for users that have logged in.
68
+
69
+ === Helpers
70
+ These helpers will be available to views of any controllers that include Shibbolite::Filters. See the source for a complete list.
71
+
72
+ current_user
73
+
74
+ will return the User object of the Shibboleth authenticated user or nil if no one is authenticated, or the user isn't registered in the database. NOTE: current_user can still return nil after the user authenticates if one of the above filters isn't used before the controller action.
75
+
76
+ Since current_user relies on a database lookup, you'll have to register (ie persist) your users before they can interact with most of the features of Shibbolite. How you want to handle user registration is left up to you.
77
+
78
+ logged_in?
79
+
80
+ checks to see that a username is set in the session
81
+
82
+ user_in_group? (:admin)
83
+
84
+ will return true if the current user has the admin group
85
+
86
+
87
+
88
+ === Actions
89
+ A couple of actions are available from Shibbolite::ShibbolethController
90
+
91
+ shibbolite.login_path #=> '/shibbolite/login'
92
+
93
+ will attempt to load a Shibboleth session, or redirect to the Shibbolite.session_initiator for authentication if there is none. After authentication the user is brought back to the root_path.
94
+
95
+ shibbolite.logout_path #=> 'shibbolite/logout
96
+
97
+ likewise redirects the client to the Shibbolite.logout_initiator
98
+
99
+ == Contribute
100
+ 1. Fork the repository
101
+ 2. Make sure the tests pass
102
+ 3. Write a test for your change and make it pass
103
+ 4. Push the changes to your Fork
104
+ 5. Submit a pull request
105
+
106
+ == License
107
+
108
+ Shibbolite is released under the MIT License
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
8
+ load 'rails/tasks/engine.rake'
9
+
10
+ Bundler::GemHelper.install_tasks
11
+
12
+ Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each {|f| load f }
13
+
14
+ require 'rspec/core'
15
+ require 'rspec/core/rake_task'
16
+
17
+ desc "Run all specs in spec directory (excluding plugin specs)"
18
+ RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')
19
+
20
+ task :default => :spec
21
+
@@ -0,0 +1,44 @@
1
+ # controller filters for access control
2
+ module Shibbolite
3
+ module Filters
4
+ extend ActiveSupport::Concern
5
+
6
+ include Shibbolite::Helpers
7
+
8
+ def require_login
9
+ redirect_to login_or_access_denied unless logged_in?
10
+ end
11
+
12
+ def require_registered
13
+ redirect_to login_or_access_denied unless registered_user?
14
+ end
15
+
16
+ def require_group(*groups)
17
+ in_group = false
18
+ groups.flatten.each { |group| in_group ||= user_in_group?(group) }
19
+ redirect_to login_or_access_denied unless in_group
20
+ end
21
+
22
+ def require_id(id)
23
+ redirect_to login_or_access_denied unless user_has_id?(id)
24
+ end
25
+
26
+ def require_group_or_id(*groups, id)
27
+ unless user_has_id?(id)
28
+ require_group(groups)
29
+ end
30
+ end
31
+
32
+ def use_attributes_if_available
33
+ if request.env[Shibbolite.pid.to_s] and not logged_in?
34
+ redirect_to login_or_access_denied
35
+ end
36
+ end
37
+
38
+ # a handy redirect target
39
+ def login_or_access_denied
40
+ session[:requested_url] = request.fullpath
41
+ logged_in? ? shibbolite.access_denied_url : shibbolite.login_url
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,48 @@
1
+ module Shibbolite
2
+ module Helpers
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ helper_method :logged_in?, :current_user, :anonymous_user?, :guest_user?,
7
+ :registered_user?, :user_in_group?, :user_has_id?
8
+ end
9
+
10
+ # true when the primary_user_id has been saved in session
11
+ def logged_in?
12
+ session[Shibbolite.pid]
13
+ end
14
+
15
+ # sets current user from the session user id
16
+ def current_user
17
+ logged_in? ? @current_user ||= Shibbolite.user_class.find_user(session[Shibbolite.pid]) : nil
18
+ end
19
+
20
+ # true when user hasn't signed in to SSO
21
+ def anonymous_user?
22
+ not logged_in?
23
+ end
24
+
25
+ # true when the user signed in to the SSO
26
+ # but they haven't been registered with the app
27
+ def guest_user?
28
+ current_user.nil? and logged_in?
29
+ end
30
+
31
+ # true when the user exists in the database
32
+ def registered_user?
33
+ current_user
34
+ end
35
+
36
+ def user_in_group?(group, user = nil)
37
+ user ||= current_user
38
+ return false unless user
39
+ user.group == group.to_s
40
+ end
41
+
42
+ def user_has_id?(id, user = nil)
43
+ user ||= current_user
44
+ return false unless user
45
+ user.id == id
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,21 @@
1
+ module Shibbolite
2
+ module User
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ unless Shibbolite.skip_validations
7
+ validates Shibbolite.primary_user_id, :group, presence: true
8
+ validates Shibbolite.primary_user_id, uniqueness: true
9
+ validates :group, inclusion: { in: Shibbolite.groups.map(&:to_s),
10
+ message: "%{value} is not included in Shibbolite.groups" }
11
+ end
12
+ end
13
+
14
+ module ClassMethods
15
+ # gets the user who matches the shibboleth primary key
16
+ def find_user(pid)
17
+ find_by(Shibbolite.primary_user_id => pid)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,53 @@
1
+ module Shibbolite
2
+ class ShibbolethController < ActionController::Base
3
+
4
+ include Shibbolite::Helpers
5
+
6
+ def access_denied
7
+ @requested_url = session.delete(:requested_url)
8
+ end
9
+
10
+ def login
11
+ session[:requested_url] ||= main_app.root_path
12
+ load_session
13
+ redirect_to logged_in? ? session.delete(:requested_url) : sp_login_url
14
+ end
15
+
16
+ def logout
17
+ session.delete(Shibbolite.pid)
18
+ redirect_to sp_logout_url
19
+ end
20
+
21
+ # required to prevent displaying default shibboleth logout message
22
+ def logout_message ; end
23
+
24
+ private
25
+
26
+ # loads the session data created by shibboleth
27
+ # ensures that the user's id is set in session
28
+ # and updates the user's shibboleth attributes
29
+ def load_session
30
+ unless logged_in?
31
+ session[Shibbolite.pid] = request.env[Shibbolite.pid.to_s]
32
+ current_user.update(get_attributes) if registered_user?
33
+ end
34
+ end
35
+
36
+ # reads shibboleth attributes from environment
37
+ def get_attributes
38
+ request.env.with_indifferent_access.slice(*Shibbolite.attributes)
39
+ end
40
+
41
+ def sp_login_url
42
+ request.protocol + request.host +
43
+ Shibbolite.handler_url + Shibbolite.session_initiator +
44
+ '?' + URI.encode_www_form(target: login_url)
45
+ end
46
+
47
+ def sp_logout_url
48
+ request.protocol + request.host +
49
+ Shibbolite.handler_url + Shibbolite.logout_initiator +
50
+ '?' + URI.encode_www_form(return: logout_message_url)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,32 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Shibbolite</title>
5
+ <style>
6
+ body {
7
+ background-color: white;
8
+ color: #474747;
9
+ text-align: left;
10
+ font-family: arial, sans-serif;
11
+ }
12
+
13
+ div.message {
14
+ width: 20em;
15
+ margin: 2em 0 0 2em;
16
+ border: 20px solid #D8000F;
17
+ border-radius: 70px;
18
+ padding: 7px 3em 1em 3em;
19
+ }
20
+
21
+ h1 {
22
+ font-size: 25px;
23
+ color: #D8000F;
24
+ line-height: 2em;
25
+ }
26
+
27
+ </style>
28
+ </head>
29
+
30
+ <body>
31
+ <%= yield %>
32
+ </body>
@@ -0,0 +1,7 @@
1
+ <div class="message">
2
+ <h1>(Shibbolite) Access Denied</h1>
3
+ <p>
4
+ You don't the credentials required to access <%= @requested_url %>.
5
+ </p>
6
+ <%= link_to 'Back', :back %>
7
+ </div>
@@ -0,0 +1,7 @@
1
+ <div class="message">
2
+ <h1>(Shibbolite) Logged out</h1>
3
+ <p>
4
+ You MUST close your browser to
5
+ complete the logout process.
6
+ </p>
7
+ </div>
data/config/routes.rb ADDED
@@ -0,0 +1,6 @@
1
+ Shibbolite::Engine.routes.draw do
2
+ get 'login', to: 'shibboleth#login'
3
+ get 'logout', to: 'shibboleth#logout'
4
+ get 'logout_message', to: 'shibboleth#logout_message'
5
+ get 'access_denied', to: 'shibboleth#access_denied'
6
+ end
@@ -0,0 +1,17 @@
1
+ module Shibbolite
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("../../templates", __FILE__)
5
+
6
+ desc 'Creates the shibbolite_config.rb initializer and mounts the Shibbolite engine'
7
+
8
+ def create_initializer
9
+ copy_file 'shibbolite_config.rb', 'config/initializers/shibbolite_config.rb'
10
+ end
11
+
12
+ def mount_engine
13
+ route "mount Shibbolite::Engine => '/shibbolite'"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ module Shibbolite
2
+ module Generators
3
+ class MigrationGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("../../templates", __FILE__)
5
+
6
+ desc "Creates the migration to add shibboleth attributes to the main app's User class"
7
+
8
+ def creat_migration
9
+ generate "migration AddShibbolethAttributesTo#{Shibbolite.user_table_name} group:string" <<
10
+ Shibbolite.attributes.collect { |attr| " #{attr}:string" }.join
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,51 @@
1
+ # These configuration options must be set before Shibbolite
2
+ # can be loaded. Mandatory settings are :attributes, :user_class,
3
+ # and :primary_user_id. Depending on your environment you may
4
+ # also have to change :handler_url, :session_initiator, or :logout_initiator
5
+ # if they are different from the default values. Check with your SP's
6
+ # shibboleth.xml configuration for the correct settings.
7
+ Shibbolite.config do |c|
8
+
9
+ # any shibboleth attributes available in your environment that you want
10
+ # passed to your application.
11
+ c.attributes = [:eppn, :some_other_attribute]
12
+
13
+ # SP attribute used as a unique username
14
+ # typically this should be the same attribute that
15
+ # your SP uses to set the REMOTE_USER environment variable
16
+ # Use the getter alias Shibbolite.pid of you want to be concise
17
+ c.primary_user_id = :eppn
18
+
19
+ # The defaults for these options will work for most installations
20
+ # all options are listed with their default values, only uncomment
21
+ # if you need to change them
22
+
23
+ # friendly display name for views
24
+ # concise alias Shibbolite.pid_display is available too
25
+ #c.primary_user_id_display = 'Username'
26
+
27
+ # name of your application's User model
28
+ #c.user_class = 'User'
29
+
30
+ # used with the generated migration.
31
+ # Only override if your table doesn't follow
32
+ # normal pluralization or name conventions
33
+ #c.user_table_name = c.user_class.pluralize
34
+
35
+ # NativeSP base location
36
+ # used to construct urls to interact with SP
37
+ #c.handler_url = '/Shibboleth.sso'
38
+
39
+ # NativeSP handler location for starting sessions
40
+ #c.session_initiator = '/Login'
41
+
42
+ # NativeSP handler location for logging out
43
+ #c.logout_initiator = '/Logout'
44
+
45
+ # the types of groups to assign users
46
+ #c.groups = [:user, :admin]
47
+
48
+ # setting to true will skip including validations
49
+ # from the Shibbolite::User class
50
+ #c.skip_validations = false
51
+ end
@@ -0,0 +1,13 @@
1
+ module Shibbolite
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Shibbolite
4
+
5
+ config.generators do |g|
6
+ g.test_framework :rspec, :fixture => false
7
+ g.fixture_replacement :factory_girl, :dir => 'spec/factories'
8
+ g.assets false
9
+ g.helper false
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module Shibbolite
2
+ VERSION = "0.0.1"
3
+ end
data/lib/shibbolite.rb ADDED
@@ -0,0 +1,58 @@
1
+ require "shibbolite/engine"
2
+
3
+ module Shibbolite
4
+
5
+ # shibboleth attributes available in the environment
6
+ mattr_accessor :attributes
7
+
8
+ # shibboleth primary key
9
+ mattr_accessor :primary_user_id
10
+
11
+ mattr_accessor :primary_user_id_display
12
+ @@primary_user_id_display = 'Username'
13
+
14
+ # groups available to assign to users
15
+ mattr_accessor :groups
16
+ @@groups = [:user, :admin]
17
+
18
+ # the parent application's User model
19
+ mattr_accessor :user_class
20
+ @@user_class = 'User'
21
+
22
+ def self.user_class
23
+ @@user_class.constantize
24
+ end
25
+
26
+ mattr_accessor :user_table_name
27
+ @@user_table_name = @@user_class.pluralize
28
+
29
+ mattr_accessor :skip_validations
30
+ @@skip_validations = false
31
+
32
+ # NativeSP base location
33
+ mattr_accessor :handler_url
34
+ @@handler_url = '/Shibboleth.sso'
35
+
36
+ # NativeSP handler location for starting sessions
37
+ mattr_accessor :session_initiator
38
+ @@session_initiator = '/Login'
39
+
40
+ # NativeSP handler location for logging out
41
+ mattr_accessor :logout_initiator
42
+ @@logout_initiator = '/Logout'
43
+
44
+ # shortened/alternate accessors
45
+
46
+ def self.pid
47
+ primary_user_id
48
+ end
49
+
50
+ def self.pid_display
51
+ primary_user_id_display
52
+ end
53
+
54
+ # friendly config
55
+ def self.config
56
+ yield self
57
+ end
58
+ end
@@ -0,0 +1,146 @@
1
+ require 'spec_helper'
2
+
3
+ # a dummy controller used to test the Shibbolite::Filters concern
4
+
5
+ describe FiltersTestController do
6
+
7
+ # presume the session has been loaded
8
+ before { allow(subject).to receive(:load_session) }
9
+
10
+ describe '#require_login' do
11
+
12
+ context 'when logged in' do
13
+ it 'allows the action to continue' do
14
+ allow(subject).to receive(:logged_in?).and_return(true)
15
+ get :_require_login
16
+ expect(response).to render_template(:dummy)
17
+ end
18
+
19
+ end
20
+
21
+ context 'when not logged in' do
22
+ it 'redirects' do
23
+ allow(subject).to receive(:logged_in?).and_return(false)
24
+ get :_require_login
25
+ expect(response).to redirect_to('/shibbolite/login')
26
+ end
27
+ end
28
+ end
29
+
30
+ describe '#require_registered' do
31
+
32
+ context 'when user is registered' do
33
+ it 'allows the action to continue' do
34
+ allow(subject).to receive(:registered_user?).and_return(true)
35
+ get :_require_registered
36
+ expect(response).to render_template(:dummy)
37
+ end
38
+ end
39
+
40
+ context 'when the user is not registered' do
41
+ it 'redirects' do
42
+ allow(subject).to receive(:registered_user?).and_return(false)
43
+ get :_require_registered
44
+ expect(response).to redirect_to('/shibbolite/login')
45
+ end
46
+ end
47
+ end
48
+
49
+ describe '#use_attributes_if_available' do
50
+
51
+ context 'when the user authenticated but login isn\'t required' do
52
+ it 'redirects to login to load the session' do
53
+ allow(subject).to receive(:logged_in?).and_return(false)
54
+ request.env[Shibbolite.pid.to_s] = 'not nil'
55
+ get :_use_attributes_if_available
56
+ expect(response).to redirect_to('/shibbolite/login')
57
+ end
58
+ end
59
+
60
+ context 'when the user did not authenticate' do
61
+ it 'allows the action' do
62
+ get :_use_attributes_if_available
63
+ expect(response).to render_template(:dummy)
64
+ end
65
+ end
66
+ end
67
+
68
+ context 'filters that require a user object' do
69
+
70
+ let!(:user_id) { 17 }
71
+ let!(:admin_id) { 1 }
72
+ let(:guest) { double('guest', id: nil, group: nil )}
73
+ let(:user) { double('user' , id: user_id, group: 'user')}
74
+ let(:admin) { double('admin', id: admin_id, group: 'admin' )}
75
+
76
+ describe '#require_group' do
77
+
78
+ context 'when the user has a group listed' do
79
+ it 'allows the action to continue' do
80
+ allow(subject).to receive(:current_user).and_return(user)
81
+ get :_require_group, groups: ['user', 'admin']
82
+ expect(response).to render_template(:dummy)
83
+ end
84
+ end
85
+
86
+ context 'when the user is not a member' do
87
+ it 'redirects' do
88
+ allow(subject).to receive(:current_user).and_return(guest)
89
+ get :_require_group, groups: ['user', 'admin']
90
+ expect(response).to redirect_to('/shibbolite/login')
91
+ end
92
+ end
93
+ end
94
+
95
+ describe '#require_id' do
96
+
97
+ context 'when the user has the id listed' do
98
+ it 'allows the action to continue' do
99
+ allow(subject).to receive(:current_user).and_return(admin)
100
+ get :_require_id, id: admin_id
101
+ expect(response).to render_template(:dummy)
102
+ end
103
+ end
104
+
105
+ context 'when the user does not have the id' do
106
+ it 'redirects' do
107
+ allow(subject).to receive(:current_user).and_return(guest)
108
+ get :_require_id, id: user_id
109
+ expect(response).to redirect_to('/shibbolite/login')
110
+ end
111
+ end
112
+ end
113
+
114
+ describe '#require_group_or_id' do
115
+
116
+ context 'happy paths' do
117
+
118
+ it 'allows action with matching id' do
119
+ allow(subject).to receive(:current_user).and_return(user)
120
+ get :_require_group_or_id, groups: 'admin', id: user_id
121
+ expect(response).to render_template(:dummy)
122
+ end
123
+
124
+ it 'allows action with matching group' do
125
+ allow(subject).to receive(:current_user).and_return(user)
126
+ get :_require_group_or_id, groups: 'user', id: admin_id
127
+ expect(response).to render_template(:dummy)
128
+ end
129
+
130
+ it 'allows action with both id and group matching' do
131
+ allow(subject).to receive(:current_user).and_return(admin)
132
+ get :_require_group_or_id, groups: 'admin', id: admin_id
133
+ expect(response).to render_template(:dummy)
134
+ end
135
+ end
136
+
137
+ context 'with no matching criteria' do
138
+ it 'redirects' do
139
+ allow(subject).to receive(:current_user).and_return(guest)
140
+ get :_require_group_or_id, groups: 'user', id: user_id
141
+ expect(response).to redirect_to('/shibbolite/login')
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end