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.
- checksums.yaml +7 -0
- data/README.md +28 -0
- data/Rakefile +102 -0
- data/app/assets/images/isaca/rails/geometric-1920-blue.png +0 -0
- data/app/assets/images/isaca/rails/isaca-logo.png +0 -0
- data/app/assets/javascript/isaca/rails/application.js +13 -0
- data/app/assets/stylesheets/isaca/rails/all.css +46 -0
- data/app/assets/stylesheets/isaca/rails/application.css +15 -0
- data/app/assets/stylesheets/isaca/rails/components/button.css +34 -0
- data/app/assets/stylesheets/isaca/rails/components/container.css +4 -0
- data/app/assets/stylesheets/isaca/rails/components/flash.css +18 -0
- data/app/assets/stylesheets/isaca/rails/components/form-control.css +7 -0
- data/app/assets/stylesheets/isaca/rails/sessions.css +96 -0
- data/app/assets/stylesheets/isaca/rails/user_consent.css +87 -0
- data/app/controllers/isaca/rails/application_controller.rb +5 -0
- data/app/controllers/isaca/rails/platform/administrators_controller.rb +68 -0
- data/app/controllers/isaca/rails/platform/application_controller.rb +10 -0
- data/app/controllers/isaca/rails/platform/claims_controller.rb +34 -0
- data/app/controllers/isaca/rails/sessions_controller.rb +56 -0
- data/app/controllers/isaca/rails/users_consent_controller.rb +24 -0
- data/app/controllers/isaca/rails/welcome_controller.rb +3 -0
- data/app/helpers/isaca/rails/application_helper.rb +48 -0
- data/app/helpers/isaca/rails/claims_helper.rb +13 -0
- data/app/models/session/sign_in/form_object.rb +28 -0
- data/app/models/user_consent/agreement/form_object.rb +33 -0
- data/app/views/isaca/rails/platform/administrators/_administrator.html.erb +6 -0
- data/app/views/isaca/rails/platform/administrators/_claims_form.html.erb +9 -0
- data/app/views/isaca/rails/platform/administrators/edit.html.erb +9 -0
- data/app/views/isaca/rails/platform/administrators/index.html.erb +15 -0
- data/app/views/isaca/rails/platform/administrators/new.html.erb +17 -0
- data/app/views/isaca/rails/platform/administrators/show.html.erb +29 -0
- data/app/views/isaca/rails/sessions/_form.html.erb +15 -0
- data/app/views/isaca/rails/sessions/new.html.erb +28 -0
- data/app/views/isaca/rails/sessions/shared/_links.html.erb +2 -0
- data/app/views/isaca/rails/users_consent/_form.html.erb +50 -0
- data/app/views/isaca/rails/users_consent/show.html.erb +21 -0
- data/app/views/isaca/rails/welcome/index.html.erb +81 -0
- data/app/views/layouts/isaca-rails.html.erb +23 -0
- data/config/application.rb +0 -0
- data/config/locales/isaca-rails.en.yml +25 -0
- data/config/routes.rb +2 -0
- data/lib/generators/isaca/rails/install/USAGE +24 -0
- data/lib/generators/isaca/rails/install/install_generator.rb +148 -0
- data/lib/generators/isaca/rails/install/templates/README +14 -0
- data/lib/generators/isaca/rails/install/templates/add_isaca_claims.rb.erb +10 -0
- data/lib/generators/isaca/rails/install/templates/add_isaca_to_existing_users.rb.erb +17 -0
- data/lib/generators/isaca/rails/install/templates/add_isaca_users.rb.erb +21 -0
- data/lib/generators/isaca/rails/install/templates/claim.rb.erb +13 -0
- data/lib/generators/isaca/rails/install/templates/isaca-rails.rb +4 -0
- data/lib/generators/isaca/rails/install/templates/isaca.rb +5 -0
- data/lib/generators/isaca/rails/install/templates/user.rb.erb +3 -0
- data/lib/isaca/rails/authentication.rb +166 -0
- data/lib/isaca/rails/authorization.rb +51 -0
- data/lib/isaca/rails/controller.rb +14 -0
- data/lib/isaca/rails/engine.rb +7 -0
- data/lib/isaca/rails/user.rb +16 -0
- data/lib/isaca/rails/version.rb +5 -0
- data/lib/isaca/rails.rb +83 -0
- data/lib/tasks/isaca/rails_tasks.rake +4 -0
- 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,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,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
|
data/lib/isaca/rails.rb
ADDED
@@ -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
|