isaca-rails 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|