isaca-rails 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +28 -0
  3. data/Rakefile +102 -0
  4. data/app/assets/images/isaca/rails/geometric-1920-blue.png +0 -0
  5. data/app/assets/images/isaca/rails/isaca-logo.png +0 -0
  6. data/app/assets/javascript/isaca/rails/application.js +13 -0
  7. data/app/assets/stylesheets/isaca/rails/all.css +46 -0
  8. data/app/assets/stylesheets/isaca/rails/application.css +15 -0
  9. data/app/assets/stylesheets/isaca/rails/components/button.css +34 -0
  10. data/app/assets/stylesheets/isaca/rails/components/container.css +4 -0
  11. data/app/assets/stylesheets/isaca/rails/components/flash.css +18 -0
  12. data/app/assets/stylesheets/isaca/rails/components/form-control.css +7 -0
  13. data/app/assets/stylesheets/isaca/rails/sessions.css +96 -0
  14. data/app/assets/stylesheets/isaca/rails/user_consent.css +87 -0
  15. data/app/controllers/isaca/rails/application_controller.rb +5 -0
  16. data/app/controllers/isaca/rails/platform/administrators_controller.rb +68 -0
  17. data/app/controllers/isaca/rails/platform/application_controller.rb +10 -0
  18. data/app/controllers/isaca/rails/platform/claims_controller.rb +34 -0
  19. data/app/controllers/isaca/rails/sessions_controller.rb +56 -0
  20. data/app/controllers/isaca/rails/users_consent_controller.rb +24 -0
  21. data/app/controllers/isaca/rails/welcome_controller.rb +3 -0
  22. data/app/helpers/isaca/rails/application_helper.rb +48 -0
  23. data/app/helpers/isaca/rails/claims_helper.rb +13 -0
  24. data/app/models/session/sign_in/form_object.rb +28 -0
  25. data/app/models/user_consent/agreement/form_object.rb +33 -0
  26. data/app/views/isaca/rails/platform/administrators/_administrator.html.erb +6 -0
  27. data/app/views/isaca/rails/platform/administrators/_claims_form.html.erb +9 -0
  28. data/app/views/isaca/rails/platform/administrators/edit.html.erb +9 -0
  29. data/app/views/isaca/rails/platform/administrators/index.html.erb +15 -0
  30. data/app/views/isaca/rails/platform/administrators/new.html.erb +17 -0
  31. data/app/views/isaca/rails/platform/administrators/show.html.erb +29 -0
  32. data/app/views/isaca/rails/sessions/_form.html.erb +15 -0
  33. data/app/views/isaca/rails/sessions/new.html.erb +28 -0
  34. data/app/views/isaca/rails/sessions/shared/_links.html.erb +2 -0
  35. data/app/views/isaca/rails/users_consent/_form.html.erb +50 -0
  36. data/app/views/isaca/rails/users_consent/show.html.erb +21 -0
  37. data/app/views/isaca/rails/welcome/index.html.erb +81 -0
  38. data/app/views/layouts/isaca-rails.html.erb +23 -0
  39. data/config/application.rb +0 -0
  40. data/config/locales/isaca-rails.en.yml +25 -0
  41. data/config/routes.rb +2 -0
  42. data/lib/generators/isaca/rails/install/USAGE +24 -0
  43. data/lib/generators/isaca/rails/install/install_generator.rb +148 -0
  44. data/lib/generators/isaca/rails/install/templates/README +14 -0
  45. data/lib/generators/isaca/rails/install/templates/add_isaca_claims.rb.erb +10 -0
  46. data/lib/generators/isaca/rails/install/templates/add_isaca_to_existing_users.rb.erb +17 -0
  47. data/lib/generators/isaca/rails/install/templates/add_isaca_users.rb.erb +21 -0
  48. data/lib/generators/isaca/rails/install/templates/claim.rb.erb +13 -0
  49. data/lib/generators/isaca/rails/install/templates/isaca-rails.rb +4 -0
  50. data/lib/generators/isaca/rails/install/templates/isaca.rb +5 -0
  51. data/lib/generators/isaca/rails/install/templates/user.rb.erb +3 -0
  52. data/lib/isaca/rails/authentication.rb +166 -0
  53. data/lib/isaca/rails/authorization.rb +51 -0
  54. data/lib/isaca/rails/controller.rb +14 -0
  55. data/lib/isaca/rails/engine.rb +7 -0
  56. data/lib/isaca/rails/user.rb +16 -0
  57. data/lib/isaca/rails/version.rb +5 -0
  58. data/lib/isaca/rails.rb +83 -0
  59. data/lib/tasks/isaca/rails_tasks.rake +4 -0
  60. metadata +297 -0
@@ -0,0 +1,17 @@
1
+ class AddIsaca<%= name.pluralize.camelize %> < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ <%= "add_column #{model_symbol_string(name)}, :imis_id, :string" unless model_has_column? name, :imis_id %>
4
+ <%= "add_index #{model_symbol_string(name)}, :imis_id, unique: true" unless model_has_column? name, :imis_id %>
5
+ <%= "add_column #{model_symbol_string(name)}, :imis_expiration_date, :datetime" unless model_has_column? name, :imis_expiration_date %>
6
+ <%= "add_column #{model_symbol_string(name)}, :imis_member_type, :string" unless model_has_column? name, :imis_member_type %>
7
+ <%= "add_column #{model_symbol_string(name)}, :imis_enterprise_id, :string" unless model_has_column? name, :imis_enterprise_id %>
8
+ <%= "add_column #{model_symbol_string(name)}, :imis_active_member, :boolean" unless model_has_column? name, :imis_active_member %>
9
+ <%= "add_column #{model_symbol_string(name)}, :email, :string" unless model_has_column? name, :email %>
10
+ <%= "add_column #{model_symbol_string(name)}, :first_name, :string" unless model_has_column? name, :first_name %>
11
+ <%= "add_column #{model_symbol_string(name)}, :last_name, :string" unless model_has_column? name, :last_name %>
12
+ <%= "add_column #{model_symbol_string(name)}, :username, :string" unless model_has_column? name, :username %>
13
+ <%= "add_column #{model_symbol_string(name)}, :country, :string" unless model_has_column? name, :country %>
14
+ <%= "add_column #{model_symbol_string(name)}, :admin, :boolean, default: false, null: false" unless model_has_column? name, :admin %>
15
+ <%= "add_column #{model_symbol_string(name)}, :last_sign_in_at, :datetime" unless model_has_column? name, :last_sign_in_at %>
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ class AddIsaca<%= name.pluralize.camelize %> < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ create_table <%= model_symbol_string(name) %> do |t|
4
+ t.timestamps null: false
5
+ t.string :imis_id, null: false
6
+ t.datetime :imis_expiration_date
7
+ t.boolean :imis_active_member
8
+ t.string :imis_member_type
9
+ t.string :imis_enterprise_id
10
+ t.string :email
11
+ t.string :first_name
12
+ t.string :last_name
13
+ t.string :username
14
+ t.string :country
15
+ t.boolean :admin, default: false, null: false
16
+ t.datetime :last_sign_in_at
17
+ end
18
+
19
+ add_index <%= model_symbol_string(name) %>, :imis_id, unique: true
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ class Claim < ApplicationRecord
2
+ enum privilege: {
3
+ read_administrators: 100,
4
+ write_administrators: 101,
5
+ read_claims: 102,
6
+ write_claims: 103
7
+ }
8
+
9
+ belongs_to :<%= name.downcase.singularize.to_sym %>
10
+ validates :<%= name.downcase.singularize.to_sym %>, presence: true
11
+ validates :privilege, presence: true
12
+ validates_uniqueness_of :privilege, scope: :<%= name.downcase.singularize.to_sym %>, message: '<%= name.singularize.humanize %> already has privilege'
13
+ end
@@ -0,0 +1,4 @@
1
+ Isaca::Rails.configure do |config|
2
+ # config.user_model = ::User
3
+ # config.redirect_for_consent = true
4
+ end
@@ -0,0 +1,5 @@
1
+ Isaca.configure do |config|
2
+ config.url = 'http://isaca-dev-stage-sp-2420.centralus.cloudapp.azure.com:7777/isacaservices/Service1.svc'
3
+ config.secret_pass = ENV["ISACA_SECRET_PASS"]
4
+ config.user_agent = 'my_application'
5
+ end
@@ -0,0 +1,3 @@
1
+ class <%= name.singularize.classify %> < ApplicationRecord
2
+ include Isaca::Rails::User
3
+ end
@@ -0,0 +1,166 @@
1
+ module Isaca
2
+ module Rails
3
+ module Authentication
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ helper_method :current_isaca_user
8
+ helper_method :user_signed_in?
9
+ helper_method :isaca_requires_consent?
10
+ helper_method :redirect_for_consent?
11
+ end
12
+
13
+ # Checks to see if there is a current_isaca_user, if not it redirects to the new_session_path. This method is intended
14
+ # to be used with before_action.
15
+ #
16
+ # @return nil
17
+ def authenticate_isaca_user
18
+ if user_signed_in?
19
+ if request.path != user_consent_path && redirect_for_consent?
20
+ session[:after_sign_in_path] = request.fullpath if request.get?
21
+ flash.alert = t('isaca.rails.user_consent.consent_required')
22
+ redirect_to user_consent_path
23
+ end
24
+ else
25
+ session[:after_sign_in_path] = request.fullpath if request.get?
26
+ flash.alert = t('isaca.rails.sessions.sign_in_required')
27
+ redirect_to sign_in_path
28
+ end
29
+ end
30
+
31
+ # A helper method for referencing the user who is currently logged in.
32
+ #
33
+ # @return [ActiveModel::Model|nil]
34
+ def current_isaca_user
35
+ return @current_isaca_user if @current_isaca_user
36
+
37
+ begin
38
+ set_current_isaca_user if token_cookie_exists?
39
+ rescue Isaca::ServiceError => e
40
+ Rails.logger.warn("Error occurred while setting the current isaca user: #{e.message}")
41
+ end
42
+ end
43
+
44
+ # Method used to to login a user and set the token
45
+ #
46
+ # @param username [String] The user's username
47
+ # @param password [String] The user's password
48
+ #
49
+ # @return [Boolean] Whether or not the user's record was updated with the last_sign_in_at datetime
50
+ def authenticate(username, password)
51
+ session = Isaca::Request::AuthenticateUser.get(username, password)
52
+ raise Isaca::SessionError.new(session.value) unless session.is_valid?
53
+ isaca_sign_in(session.value)
54
+ current_isaca_user.update_attribute(:last_sign_in_at, DateTime.current)
55
+ end
56
+
57
+ # Destroys the user token and sets the current_isaca_user attribute to nil
58
+ #
59
+ # @param [Hash] params Optional
60
+ # @option params [String] token The session token to be deleted.
61
+ #
62
+ # @return nil
63
+ def isaca_sign_out(**params)
64
+ token = nil
65
+ params && params[:token] ? (token = params[:token]) : (token = cookies['Token'] if token_cookie_exists?)
66
+
67
+ if token && Isaca::Request::LogOut.get(token)
68
+ cookies.delete('Token', domain: :all) if token_cookie_exists?
69
+ @current_isaca_user = nil
70
+ reset_session
71
+ end
72
+ end
73
+
74
+ # Helper method to check and see if the current_isaca_user attribute exists
75
+ #
76
+ # @return [Boolean] The truthiness of the current_isaca_user attribute
77
+ def user_signed_in?
78
+ !current_isaca_user.nil?
79
+ end
80
+
81
+ def isaca_requires_consent?
82
+ user_signed_in? && !current_isaca_user.privacy
83
+ end
84
+
85
+ # Helper method to redirect to a saved path or fallback
86
+ #
87
+ # @param fallback [String] Path to visit if session[:after_sign_in_path] does not exist
88
+ def redirect_after_sign_in_or(fallback)
89
+ redirect_to(session[:after_sign_in_path] || fallback)
90
+ session.delete(:after_sign_in_path)
91
+ end
92
+
93
+ # Helper method used to check the conditions for redirecting for consent
94
+ #
95
+ # @return [Boolean] Whether or not a redirect is required
96
+ def redirect_for_consent?
97
+ isaca_requires_consent? && Isaca::Rails.configuration.redirect_for_consent
98
+ end
99
+
100
+ private
101
+
102
+ # Creates a 'Token' cookie
103
+ #
104
+ # @param token [String] - The user's session token
105
+ #
106
+ # @return [String] - Returns the provided token
107
+ def isaca_sign_in(token)
108
+ opts = ::Rails.env.production? ? {secure: true} : {secure: false}
109
+ cookies['Token'] = {value: token, domain: :all, httponly: true}.merge(opts)
110
+ end
111
+
112
+ # Method used to create or find a user model based on an existing 'Token' cookie
113
+ #
114
+ # @return [ActiveModel::Model]
115
+ #
116
+ # @raise [Isaca::ServiceError] An error can be raised by {Isaca::Request::GetUserDetailsByToken#get} or {Isaca::Request::GetUserByID#get}
117
+ def set_current_isaca_user
118
+ # Using the Token cookie we can fetch our users details from isaca
119
+ isaca_user = Isaca::Request::GetUserDetailsByToken.get(cookies['Token'])
120
+
121
+ # The GetUserDetailsByToken endpoint does not return everything we need, we need to supplement our attributes
122
+ # by fetching the GetUserByID endpoint as well.
123
+ membership = Isaca::Request::GetUserByID.get(isaca_user.imis_id)
124
+
125
+ # Set all the aggregated user data to a hash for user record creation or user record updating
126
+ attributes = {
127
+ imis_enterprise_id: isaca_user.enterprise_id,
128
+ first_name: isaca_user.first_name,
129
+ last_name: isaca_user.last_name,
130
+ email: isaca_user.email,
131
+ username: isaca_user.username,
132
+ country: isaca_user.country,
133
+ imis_member_type: membership.member_type,
134
+ imis_expiration_date: membership.expiration_date
135
+ }
136
+
137
+ # Since isaca-rails allows configurable user models, we need to pull the model type from the configuration.
138
+ klass = Isaca::Rails.configuration.user_model
139
+
140
+ # Fetch the first record with a matching imis id or initialize a new record with the given user data.
141
+ @current_isaca_user = klass.create_with(attributes).find_or_initialize_by(imis_id: isaca_user.imis_id)
142
+
143
+ # Update the old user record or save a new user record
144
+ if @current_isaca_user.new_record?
145
+ @current_isaca_user.save
146
+ else
147
+ @current_isaca_user.update_attributes(attributes)
148
+ end
149
+
150
+ # Temporal data so we do not need dedicated columns for this but we do need to associate it with the user
151
+ @current_isaca_user.privacy = isaca_user.privacy
152
+ @current_isaca_user.marketing = isaca_user.marketing
153
+
154
+ # Return the current isaca user
155
+ @current_isaca_user
156
+ end
157
+
158
+ # Checks to see if a 'Token' cookie exists
159
+ #
160
+ # @return [Boolean] Whether or not the `Token` cookie exists
161
+ def token_cookie_exists?
162
+ cookies && cookies.key?('Token')
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,51 @@
1
+ module Isaca
2
+ module Rails
3
+ module Authorization
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ helper_method :user_has_privilege?
8
+ end
9
+
10
+ def authorize_isaca_user
11
+ if current_isaca_user.admin?
12
+ if %w(index new show create update destroy).include?(action_name)
13
+ if %w(index show).include?(action_name)
14
+ behavior = 'read'
15
+ else
16
+ behavior = 'write'
17
+ end
18
+ else
19
+ if %w(GET).include?(request.method)
20
+ behavior = 'read'
21
+ else
22
+ behavior = 'write'
23
+ end
24
+ end
25
+
26
+ privilege = "#{behavior}_#{controller_name.underscore}".to_sym
27
+ unless user_has_privilege?(current_isaca_user, privilege)
28
+ redirect_to root_path, alert: "#{t('isaca.rails.claims.admin_required')} Missing claim: #{privilege}."
29
+ end
30
+ else
31
+ redirect_to root_path, alert: t('isaca.rails.claims.admin_required')
32
+ end
33
+ end
34
+
35
+ def user_has_privilege?(user, privilege)
36
+ user.claims.select {|c| c.privilege.to_sym == privilege}.any?
37
+ end
38
+
39
+ def claim_symbols(claim_params, state)
40
+ case state
41
+ when :destroyable
42
+ claim_params.select {|k,v| v == '0'}.keys.map(&:to_sym)
43
+ when :creatable
44
+ claim_params.select {|k,v| v == '1'}.keys.map(&:to_sym)
45
+ else
46
+ nil
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,14 @@
1
+ require 'isaca/rails/authentication'
2
+ require 'isaca/rails/authorization'
3
+
4
+ # This class is meant to be included in Rails controllers where you want authentication/authorization
5
+ module Isaca
6
+ module Rails
7
+ module Controller
8
+ extend ActiveSupport::Concern
9
+
10
+ include Isaca::Rails::Authentication
11
+ include Isaca::Rails::Authorization
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ module Isaca
2
+ module Rails
3
+ class Engine < ::Rails::Engine
4
+ config.assets.precompile += %w(isaca/rails/isaca-logo.png)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ module Isaca
2
+ module Rails
3
+ module User
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ attr_accessor :privacy, :marketing, :country
8
+
9
+ has_many :claims
10
+
11
+ validates :imis_id, presence: true, uniqueness: true
12
+ validates :imis_member_type, presence: true, inclusion: %w(student member non_member)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,5 @@
1
+ module Isaca
2
+ module Rails
3
+ VERSION = '0.2.0'
4
+ end
5
+ end
@@ -0,0 +1,83 @@
1
+ require "isaca"
2
+ require "isaca/rails/engine"
3
+ require "isaca/rails/controller"
4
+ require "isaca/rails/user"
5
+
6
+ module Isaca
7
+ module Rails
8
+ class << self
9
+ attr_accessor :configuration
10
+ # @!attribute configuration
11
+ # @return [Isaca::Rails::Configuration] Object used to configure the engine
12
+ end
13
+
14
+
15
+ # Configuration block used to configure the engine.
16
+ #
17
+ # @yield [Isaca::Rails::Configuration]
18
+ #
19
+ # @example An example configuration
20
+ # Isaca::Rails.configure do |config|
21
+ # config.user_model = ::Person
22
+ # end
23
+ def self.configure
24
+ yield configuration
25
+ self.configuration
26
+ end
27
+
28
+ # Method used to fetch the configuration object
29
+ #
30
+ # @return [Isaca::Rails::Configuration]
31
+ def self.configuration
32
+ @configuration ||= Configuration.new
33
+ end
34
+
35
+ # Method used to reset configuration. Primarily required during testing.
36
+ #
37
+ # @return [Isaca::Rails::Configuration]
38
+ def self.reset
39
+ @configuration = Configuration.new
40
+ end
41
+
42
+ # Method used during testing to temporarily set configurations and then immediately reset the configuration
43
+ #
44
+ # @example An example configuration
45
+ # Isaca::Rails.test_configure do |config|
46
+ # config.user_model = ::Person
47
+ #
48
+ # assert_equal ::Person, Isaca::Rails.configuration.user_model
49
+ # end
50
+ #
51
+ # @return [Isaca::Rails::Configuration]
52
+ def self.test_configure
53
+ yield configuration
54
+ self.reset
55
+ end
56
+
57
+ class Configuration
58
+ # ActiveRecord class that represents the users of your application. Should return an ActiveRecord class.
59
+ #
60
+ # You can set the user_model in the configuration for this engine.
61
+ #
62
+ # Isaca::Rails.configure {|config| config.user_model = ::Person}
63
+ #
64
+ # Default `::User`
65
+ attr_accessor :user_model
66
+
67
+ # Whether or not users should be redirected and required to provide consent if they have not already
68
+ #
69
+ # Isaca::Rails.configure {|config| config.redirect_for_consent = ::Person}
70
+ #
71
+ # Default true
72
+ attr_accessor :redirect_for_consent
73
+
74
+ def initialize
75
+ @redirect_for_consent = true
76
+ end
77
+
78
+ def user_model
79
+ @user_model ||= ::User
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :isaca_rails do
3
+ # # Task goes here
4
+ # end