usman 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +38 -0
- data/Rakefile +37 -0
- data/app/assets/config/usman_manifest.js +2 -0
- data/app/assets/javascripts/usman/application.js +13 -0
- data/app/assets/stylesheets/usman/application.css +15 -0
- data/app/controllers/usman/admin/base_controller.rb +24 -0
- data/app/controllers/usman/admin/dashboard_controller.rb +18 -0
- data/app/controllers/usman/admin/features_controller.rb +86 -0
- data/app/controllers/usman/admin/permissions_controller.rb +76 -0
- data/app/controllers/usman/admin/resource_controller.rb +11 -0
- data/app/controllers/usman/admin/users_controller.rb +117 -0
- data/app/controllers/usman/application_controller.rb +13 -0
- data/app/controllers/usman/sessions_controller.rb +84 -0
- data/app/helpers/usman/application_helper.rb +4 -0
- data/app/helpers/usman/authentication_helper.rb +120 -0
- data/app/jobs/usman/application_job.rb +4 -0
- data/app/mailers/usman/application_mailer.rb +6 -0
- data/app/models/feature.rb +112 -0
- data/app/models/image/base.rb +30 -0
- data/app/models/image/feature_image.rb +3 -0
- data/app/models/image/profile_picture.rb +3 -0
- data/app/models/permission.rb +28 -0
- data/app/models/user.rb +247 -0
- data/app/models/usman/application_record.rb +5 -0
- data/app/services/usman/authentication_service.rb +45 -0
- data/app/uploaders/feature_image_uploader.rb +14 -0
- data/app/uploaders/image_uploader.rb +90 -0
- data/app/uploaders/profile_picture_uploader.rb +14 -0
- data/app/views/layouts/kuppayam/_footer.html.erb +25 -0
- data/app/views/layouts/kuppayam/_header.html.erb +43 -0
- data/app/views/layouts/kuppayam/_navbar.html.erb +55 -0
- data/app/views/layouts/kuppayam/_sidebar.html.erb +78 -0
- data/app/views/usman/admin/dashboard/index.html.erb +52 -0
- data/app/views/usman/admin/features/_action_buttons.html.erb +11 -0
- data/app/views/usman/admin/features/_form.html.erb +19 -0
- data/app/views/usman/admin/features/_index.html.erb +79 -0
- data/app/views/usman/admin/features/_row.html.erb +55 -0
- data/app/views/usman/admin/features/_show.html.erb +48 -0
- data/app/views/usman/admin/features/create.js.erb +16 -0
- data/app/views/usman/admin/features/destroy.js.erb +16 -0
- data/app/views/usman/admin/features/edit.js.erb +7 -0
- data/app/views/usman/admin/features/index.html.erb +25 -0
- data/app/views/usman/admin/features/index.js.erb +8 -0
- data/app/views/usman/admin/features/new.js.erb +7 -0
- data/app/views/usman/admin/features/row.js.erb +10 -0
- data/app/views/usman/admin/features/show.js.erb +8 -0
- data/app/views/usman/admin/features/update.js.erb +16 -0
- data/app/views/usman/admin/permissions/_action_buttons.html.erb +11 -0
- data/app/views/usman/admin/permissions/_form.html.erb +70 -0
- data/app/views/usman/admin/permissions/_index.html.erb +56 -0
- data/app/views/usman/admin/permissions/_row.html.erb +27 -0
- data/app/views/usman/admin/permissions/_show.html.erb +48 -0
- data/app/views/usman/admin/permissions/create.js.erb +17 -0
- data/app/views/usman/admin/permissions/destroy.js.erb +16 -0
- data/app/views/usman/admin/permissions/edit.js.erb +7 -0
- data/app/views/usman/admin/permissions/index.html.erb +25 -0
- data/app/views/usman/admin/permissions/index.js.erb +8 -0
- data/app/views/usman/admin/permissions/new.js.erb +7 -0
- data/app/views/usman/admin/permissions/row.js.erb +10 -0
- data/app/views/usman/admin/permissions/show.js.erb +8 -0
- data/app/views/usman/admin/permissions/update.js.erb +16 -0
- data/app/views/usman/admin/users/_action_buttons.html.erb +11 -0
- data/app/views/usman/admin/users/_form.html.erb +36 -0
- data/app/views/usman/admin/users/_index.html.erb +120 -0
- data/app/views/usman/admin/users/_row.html.erb +92 -0
- data/app/views/usman/admin/users/_show.html.erb +132 -0
- data/app/views/usman/admin/users/create.js.erb +16 -0
- data/app/views/usman/admin/users/destroy.js.erb +16 -0
- data/app/views/usman/admin/users/edit.js.erb +7 -0
- data/app/views/usman/admin/users/index.html.erb +40 -0
- data/app/views/usman/admin/users/index.js.erb +8 -0
- data/app/views/usman/admin/users/new.js.erb +7 -0
- data/app/views/usman/admin/users/row.js.erb +10 -0
- data/app/views/usman/admin/users/show.js.erb +8 -0
- data/app/views/usman/admin/users/update.js.erb +16 -0
- data/app/views/usman/sessions/_form.html.erb +48 -0
- data/app/views/usman/sessions/_sign_in.js.erb +3 -0
- data/app/views/usman/sessions/sign_in.html.erb +63 -0
- data/config/locales/usman.en.yml +61 -0
- data/config/routes.rb +45 -0
- data/db/migrate/20131108102728_create_images.rb +12 -0
- data/db/migrate/20140402113213_create_users.rb +57 -0
- data/db/migrate/20140402113214_create_features.rb +24 -0
- data/lib/tasks/usman_tasks.rake +4 -0
- data/lib/usman/engine.rb +14 -0
- data/lib/usman/version.rb +3 -0
- data/lib/usman.rb +5 -0
- metadata +418 -0
@@ -0,0 +1,120 @@
|
|
1
|
+
module Usman
|
2
|
+
module AuthenticationHelper
|
3
|
+
|
4
|
+
private
|
5
|
+
|
6
|
+
def current_user
|
7
|
+
# Return if @current_user is already initialized else check if the user exists with the auth token present in request header
|
8
|
+
@current_user ||= authenticate_with_http_token { |token, options| User.find_by(auth_token: token)}
|
9
|
+
end
|
10
|
+
|
11
|
+
# Returns the default URL to which the system should redirect the user after successful authentication
|
12
|
+
def default_redirect_url_after_sign_in
|
13
|
+
admin_dashboard_url
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns the default URL to which the system should redirect the user after an unsuccessful attempt to authorise a resource/page
|
17
|
+
def default_sign_in_url
|
18
|
+
sign_in_url
|
19
|
+
end
|
20
|
+
|
21
|
+
# Method to handle the redirection after unsuccesful authentication
|
22
|
+
# This method should also handle the redirection if it has come through a client appliction for authentication
|
23
|
+
# In that case, it should persist the params passed by the client application
|
24
|
+
def redirect_after_unsuccessful_authentication
|
25
|
+
params_hsh = {}
|
26
|
+
params_hsh[:client_app] = params[:client_app] if params[:client_app]
|
27
|
+
params_hsh[:redirect_back_url] = params[:redirect_back_url] if params[:redirect_back_url]
|
28
|
+
params_hsh[:requested_url] = request.original_url if request.get?
|
29
|
+
redirect_to add_query_params(default_sign_in_url, params_hsh)
|
30
|
+
return
|
31
|
+
end
|
32
|
+
|
33
|
+
# Method to redirect after successful authentication
|
34
|
+
# This method should also handle the requests forwarded by the client for authentication
|
35
|
+
def redirect_to_appropriate_page_after_sign_in
|
36
|
+
if params[:redirect_back_url]
|
37
|
+
redirect_to params[:redirect_back_url]+"?auth_token=#{@current_user.auth_token}"
|
38
|
+
elsif params[:requested_url]
|
39
|
+
redirect_to params[:requested_url]
|
40
|
+
else
|
41
|
+
redirect_to default_redirect_url_after_sign_in
|
42
|
+
end
|
43
|
+
return
|
44
|
+
end
|
45
|
+
|
46
|
+
def redirect_or_popup_to_default_sign_in_page
|
47
|
+
respond_to do |format|
|
48
|
+
format.html {
|
49
|
+
redirect_after_unsuccessful_authentication
|
50
|
+
}
|
51
|
+
format.js {
|
52
|
+
render(:partial => 'usman/sessions/sign_in.js.erb', :handlers => [:erb], :formats => [:js])
|
53
|
+
}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# This method is widely used to create the @current_user object from the session
|
58
|
+
# This method will return @current_user if it already exists which will save queries when called multiple times
|
59
|
+
def current_user
|
60
|
+
# Check if the user exists with the auth token present in session
|
61
|
+
@current_user = User.find_by_id(session[:id]) unless @current_user
|
62
|
+
return @current_user
|
63
|
+
end
|
64
|
+
|
65
|
+
# This method is usually used as a before filter to secure some of the actions which requires the user to be signed in.
|
66
|
+
def require_user
|
67
|
+
current_user
|
68
|
+
if @current_user
|
69
|
+
if @current_user.token_expired?
|
70
|
+
#binding.pry
|
71
|
+
@current_user = nil
|
72
|
+
session.delete(:id)
|
73
|
+
set_notification_messages("authentication.session_expired", :error)
|
74
|
+
redirect_or_popup_to_default_sign_in_page
|
75
|
+
return
|
76
|
+
end
|
77
|
+
else
|
78
|
+
set_notification_messages("authentication.permission_denied", :error)
|
79
|
+
redirect_or_popup_to_default_sign_in_page
|
80
|
+
return
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# This method is usually used as a before filter from admin controllers to ensure that the logged in user is a super admin
|
85
|
+
def require_super_admin
|
86
|
+
unless @current_user.is_super_admin?
|
87
|
+
set_notification_messages("authentication.permission_denied", :error)
|
88
|
+
redirect_or_popup_to_default_sign_in_page
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# This method is only used for masquerading. When admin masquerade as user A and then as B, when he logs out as B he should be logged in back as A
|
93
|
+
# This is accomplished by storing the last user id in session and activating it when user is logged off
|
94
|
+
def restore_last_user
|
95
|
+
return @last_user if @last_user
|
96
|
+
if session[:last_user_id].present?
|
97
|
+
@last_user = User.find_by_id(session[:last_user_id])
|
98
|
+
message = translate("users.sign_in_back", user: @last_user.name)
|
99
|
+
set_flash_message(message, :success, false)
|
100
|
+
session.destroy()
|
101
|
+
session[:id] = @last_user.id if @last_user.present?
|
102
|
+
return @last_user
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def masquerade_as_user(user)
|
107
|
+
#if ["development", "it", "test"].include?(Rails.env)
|
108
|
+
message = translate("users.masquerade", user: user.name)
|
109
|
+
set_flash_message(message, :success, false)
|
110
|
+
session[:last_user_id] = current_user.id if current_user
|
111
|
+
user.start_session
|
112
|
+
session[:id] = user.id
|
113
|
+
default_redirect_url_after_sign_in
|
114
|
+
url = admin_dashboard_url
|
115
|
+
redirect_to url
|
116
|
+
#end
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
class Feature < ApplicationRecord
|
2
|
+
|
3
|
+
extend KuppayamValidators
|
4
|
+
|
5
|
+
# Constants
|
6
|
+
UNPUBLISHED = "unpublished"
|
7
|
+
PUBLISHED = "published"
|
8
|
+
DISABLED = "disabled"
|
9
|
+
|
10
|
+
STATUS = {
|
11
|
+
UNPUBLISHED => "Un-Published",
|
12
|
+
PUBLISHED => "Published",
|
13
|
+
DISABLED => "Disabled"
|
14
|
+
}
|
15
|
+
|
16
|
+
STATUS_REVERSE = {
|
17
|
+
"Un-Published" => UNPUBLISHED,
|
18
|
+
"Published" => PUBLISHED,
|
19
|
+
"Disabled" => DISABLED
|
20
|
+
}
|
21
|
+
|
22
|
+
# Associations
|
23
|
+
has_many :permissions
|
24
|
+
has_many :users, through: :permissions
|
25
|
+
has_one :feature_image, :as => :imageable, :dependent => :destroy, :class_name => "Image::FeatureImage"
|
26
|
+
|
27
|
+
# Validations
|
28
|
+
validate_string :name, mandatory: true
|
29
|
+
validates :status, :presence => true, :inclusion => {:in => STATUS.keys, :presence_of => :status, :message => "%{value} is not a valid status" }
|
30
|
+
|
31
|
+
# ------------------
|
32
|
+
# Class Methods
|
33
|
+
# ------------------
|
34
|
+
|
35
|
+
# return an active record relation object with the search query in its where clause
|
36
|
+
# Return the ActiveRecord::Relation object
|
37
|
+
# == Examples
|
38
|
+
# >>> feature.search(query)
|
39
|
+
# => ActiveRecord::Relation object
|
40
|
+
scope :search, lambda {|query| where("LOWER(name) LIKE LOWER('%#{query}%')")
|
41
|
+
}
|
42
|
+
|
43
|
+
scope :status, lambda { |status| where("LOWER(status)='#{status}'") }
|
44
|
+
|
45
|
+
scope :unpublished, -> { where(status: UNPUBLISHED) }
|
46
|
+
scope :published, -> { where(status: PUBLISHED) }
|
47
|
+
scope :disabled, -> { where(status: DISABLED) }
|
48
|
+
|
49
|
+
# * Return full name
|
50
|
+
# == Examples
|
51
|
+
# >>> feature.display_name
|
52
|
+
# => "Products"
|
53
|
+
def display_name
|
54
|
+
"#{name}"
|
55
|
+
end
|
56
|
+
|
57
|
+
# * Return true if the user is not published, else false.
|
58
|
+
# == Examples
|
59
|
+
# >>> feature.published?
|
60
|
+
# => true
|
61
|
+
def published?
|
62
|
+
(status == PUBLISHED)
|
63
|
+
end
|
64
|
+
|
65
|
+
# * Return true if the user is unpublished, else false.
|
66
|
+
# == Examples
|
67
|
+
# >>> feature.unpublished?
|
68
|
+
# => true
|
69
|
+
def unpublished?
|
70
|
+
(status == UNPUBLISHED)
|
71
|
+
end
|
72
|
+
|
73
|
+
# * Return true if the user is disabled, else false.
|
74
|
+
# == Examples
|
75
|
+
# >>> feature.disabled?
|
76
|
+
# => true
|
77
|
+
def disabled?
|
78
|
+
(status == DISABLED)
|
79
|
+
end
|
80
|
+
|
81
|
+
# change the status to :unpublished
|
82
|
+
# Return the status
|
83
|
+
# == Examples
|
84
|
+
# >>> feature.unpublish!
|
85
|
+
# => "unpublished"
|
86
|
+
def unpublish!
|
87
|
+
self.update_attribute(:status, UNPUBLISHED)
|
88
|
+
end
|
89
|
+
|
90
|
+
# change the status to :published
|
91
|
+
# Return the status
|
92
|
+
# == Examples
|
93
|
+
# >>> feature.publish!
|
94
|
+
# => "published"
|
95
|
+
def publish!
|
96
|
+
self.update_attribute(:status, PUBLISHED)
|
97
|
+
end
|
98
|
+
|
99
|
+
# change the status to :suspended
|
100
|
+
# Return the status
|
101
|
+
# == Examples
|
102
|
+
# >>> feature.suspend!
|
103
|
+
# => "suspended"
|
104
|
+
def suspend!
|
105
|
+
self.update_attribute(:status, DISABLED)
|
106
|
+
end
|
107
|
+
|
108
|
+
def can_be_destroyed?
|
109
|
+
return true
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Image::Base < ActiveRecord::Base
|
2
|
+
|
3
|
+
self.table_name = "images"
|
4
|
+
|
5
|
+
attr_accessor :crop_x, :crop_y, :crop_w, :crop_h
|
6
|
+
|
7
|
+
# Associations
|
8
|
+
belongs_to :imageable, :polymorphic => true, optional: true
|
9
|
+
|
10
|
+
# Callbacks
|
11
|
+
after_save :crop_image
|
12
|
+
|
13
|
+
def crop_image
|
14
|
+
image.recreate_versions! if crop_x.present?
|
15
|
+
end
|
16
|
+
|
17
|
+
#one convenient method to pass jq_upload the necessary information
|
18
|
+
def to_jq_upload
|
19
|
+
{
|
20
|
+
"name" => read_attribute(:image),
|
21
|
+
"size" => image.size,
|
22
|
+
"original_url" => image.url,
|
23
|
+
"large_url" => image.large.url,
|
24
|
+
"medium_url" => image.medium.url,
|
25
|
+
"small_url" => image.small.url,
|
26
|
+
"tiny_url" => image.tiny.url
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class Permission < ApplicationRecord
|
2
|
+
|
3
|
+
# Associations
|
4
|
+
belongs_to :user
|
5
|
+
belongs_to :feature
|
6
|
+
|
7
|
+
# Validations
|
8
|
+
validates :can_create, inclusion: { in: [true, false] }
|
9
|
+
validates :can_read, inclusion: { in: [true, false] }
|
10
|
+
validates :can_update, inclusion: { in: [true, false] }
|
11
|
+
validates :can_delete, inclusion: { in: [true, false] }
|
12
|
+
|
13
|
+
# ------------------
|
14
|
+
# Class Methods
|
15
|
+
# ------------------
|
16
|
+
|
17
|
+
# return an active record relation object with the search query in its where clause
|
18
|
+
# Return the ActiveRecord::Relation object
|
19
|
+
# == Examples
|
20
|
+
# >>> permission.search(query)
|
21
|
+
# => ActiveRecord::Relation object
|
22
|
+
scope :search, lambda {|query| joins("INNER JOIN users u on permissions.user_id = u.id").
|
23
|
+
joins("INNER JOIN features f on permissions.feature_id = f.id").
|
24
|
+
where("LOWER(u.name) LIKE LOWER('%#{query}%') OR\
|
25
|
+
LOWER(u.username) LIKE LOWER('%#{query}%') OR\
|
26
|
+
LOWER(u.email) LIKE LOWER('%#{query}%') OR\
|
27
|
+
LOWER(f.name) LIKE LOWER('%#{query}%')")}
|
28
|
+
end
|
data/app/models/user.rb
ADDED
@@ -0,0 +1,247 @@
|
|
1
|
+
class User < ActiveRecord::Base
|
2
|
+
|
3
|
+
extend KuppayamValidators
|
4
|
+
|
5
|
+
# including Password Methods
|
6
|
+
has_secure_password
|
7
|
+
|
8
|
+
# Constants
|
9
|
+
PENDING = "pending"
|
10
|
+
APPROVED = "approved"
|
11
|
+
SUSPENDED = "suspended"
|
12
|
+
|
13
|
+
STATUS = {
|
14
|
+
PENDING => "Pending",
|
15
|
+
APPROVED => "Approved",
|
16
|
+
SUSPENDED => "Suspended"
|
17
|
+
}
|
18
|
+
|
19
|
+
STATUS_REVERSE = {
|
20
|
+
"Pending" => PENDING,
|
21
|
+
"Approved" => APPROVED,
|
22
|
+
"Suspended" => SUSPENDED
|
23
|
+
}
|
24
|
+
|
25
|
+
EXCLUDED_JSON_ATTRIBUTES = [:confirmation_token, :password_digest, :reset_password_token, :unlock_token, :status, :reset_password_sent_at, :remember_created_at, :sign_in_count, :current_sign_in_at, :last_sign_in_at, :current_sign_in_ip, :last_sign_in_ip, :confirmed_at, :confirmation_sent_at, :unconfirmed_email, :failed_attempts, :locked_at, :created_at, :updated_at]
|
26
|
+
DEFAULT_PASSWORD = "Password@1"
|
27
|
+
SESSION_TIME_OUT = 30.minutes
|
28
|
+
|
29
|
+
# Validations
|
30
|
+
validate_string :name, mandatory: true
|
31
|
+
validate_username :username
|
32
|
+
validate_email :email
|
33
|
+
validate_password :password, condition_method: :should_validate_password?
|
34
|
+
|
35
|
+
validates :status, :presence => true, :inclusion => {:in => STATUS.keys, :presence_of => :status, :message => "%{value} is not a valid status" }
|
36
|
+
|
37
|
+
# Callbacks
|
38
|
+
before_validation :generate_auth_token
|
39
|
+
|
40
|
+
# Associations
|
41
|
+
has_one :profile_picture, :as => :imageable, :dependent => :destroy, :class_name => "Image::ProfilePicture"
|
42
|
+
has_many :permissions
|
43
|
+
has_many :features, through: :permissions
|
44
|
+
|
45
|
+
|
46
|
+
# ------------------
|
47
|
+
# Class Methods
|
48
|
+
# ------------------
|
49
|
+
|
50
|
+
def self.find_by_email_or_username(query)
|
51
|
+
self.where("LOWER(email) = LOWER('#{query}') OR LOWER(username) = LOWER('#{query}')").first
|
52
|
+
end
|
53
|
+
|
54
|
+
# return an active record relation object with the search query in its where clause
|
55
|
+
# Return the ActiveRecord::Relation object
|
56
|
+
# == Examples
|
57
|
+
# >>> user.search(query)
|
58
|
+
# => ActiveRecord::Relation object
|
59
|
+
scope :search, lambda {|query| where("LOWER(name) LIKE LOWER('%#{query}%') OR\
|
60
|
+
LOWER(username) LIKE LOWER('%#{query}%') OR\
|
61
|
+
LOWER(email) LIKE LOWER('%#{query}%') OR\
|
62
|
+
LOWER(designation) LIKE LOWER('%#{query}%')")
|
63
|
+
}
|
64
|
+
|
65
|
+
scope :status, lambda { |status| where("LOWER(status)='#{status}'") }
|
66
|
+
|
67
|
+
scope :pending, -> { where(status: PENDING) }
|
68
|
+
scope :approved, -> { where(status: APPROVED) }
|
69
|
+
scope :suspended, -> { where(status: SUSPENDED) }
|
70
|
+
|
71
|
+
# ------------------
|
72
|
+
# Instance variables
|
73
|
+
# ------------------
|
74
|
+
|
75
|
+
# * Return full name
|
76
|
+
# == Examples
|
77
|
+
# >>> user.display_name
|
78
|
+
# => "Joe Black"
|
79
|
+
def display_name
|
80
|
+
"#{name}"
|
81
|
+
end
|
82
|
+
|
83
|
+
# * Return true if the user is not approved, else false.
|
84
|
+
# == Examples
|
85
|
+
# >>> user.approved?
|
86
|
+
# => true
|
87
|
+
def approved?
|
88
|
+
(status == APPROVED)
|
89
|
+
end
|
90
|
+
|
91
|
+
# * Return true if the user is pending, else false.
|
92
|
+
# == Examples
|
93
|
+
# >>> user.pending?
|
94
|
+
# => true
|
95
|
+
def pending?
|
96
|
+
(status == PENDING)
|
97
|
+
end
|
98
|
+
|
99
|
+
# * Return true if the user is suspended, else false.
|
100
|
+
# == Examples
|
101
|
+
# >>> user.suspended?
|
102
|
+
# => true
|
103
|
+
def suspended?
|
104
|
+
(status == SUSPENDED)
|
105
|
+
end
|
106
|
+
|
107
|
+
# change the status to :pending
|
108
|
+
# Return the status
|
109
|
+
# == Examples
|
110
|
+
# >>> user.pending!
|
111
|
+
# => "pending"
|
112
|
+
def pending!
|
113
|
+
self.update_attribute(:status, PENDING)
|
114
|
+
end
|
115
|
+
|
116
|
+
# change the status to :approved
|
117
|
+
# Return the status
|
118
|
+
# == Examples
|
119
|
+
# >>> user.approve!
|
120
|
+
# => "approved"
|
121
|
+
def approve!
|
122
|
+
self.update_attribute(:status, APPROVED)
|
123
|
+
end
|
124
|
+
|
125
|
+
# change the status to :suspended
|
126
|
+
# Return the status
|
127
|
+
# == Examples
|
128
|
+
# >>> user.suspend!
|
129
|
+
# => "suspended"
|
130
|
+
def suspend!
|
131
|
+
self.update_attribute(:status, SUSPENDED)
|
132
|
+
end
|
133
|
+
|
134
|
+
def is_super_admin?
|
135
|
+
super_admin
|
136
|
+
end
|
137
|
+
|
138
|
+
def start_session
|
139
|
+
# FIX ME - specs are not written to ensure that all these data are saved
|
140
|
+
self.token_created_at = Time.now
|
141
|
+
self.sign_in_count = self.sign_in_count ? self.sign_in_count + 1 : 1
|
142
|
+
self.last_sign_in_at = self.current_sign_in_at
|
143
|
+
self.last_sign_in_ip = self.current_sign_in_ip
|
144
|
+
self.current_sign_in_at = self.token_created_at
|
145
|
+
|
146
|
+
# FIX ME - pass remote_ip to this method.
|
147
|
+
# Make necessary changes to authentication service to make it work
|
148
|
+
# self.current_sign_in_ip = remote_ip if remote_ip
|
149
|
+
self.save
|
150
|
+
end
|
151
|
+
|
152
|
+
def end_session
|
153
|
+
# Reseting the auth token for user when he logs out.
|
154
|
+
# (Time.now - 1.second)
|
155
|
+
self.update_attributes auth_token: SecureRandom.hex, token_created_at: nil
|
156
|
+
end
|
157
|
+
|
158
|
+
def assign_default_password
|
159
|
+
self.password = DEFAULT_PASSWORD
|
160
|
+
self.password_confirmation = DEFAULT_PASSWORD
|
161
|
+
end
|
162
|
+
|
163
|
+
def token_expired?
|
164
|
+
return self.token_created_at.nil? || (Time.now > self.token_created_at + SESSION_TIME_OUT)
|
165
|
+
end
|
166
|
+
|
167
|
+
def generate_reset_password_token
|
168
|
+
self.reset_password_token = SecureRandom.hex unless self.reset_password_token
|
169
|
+
self.reset_password_sent_at = Time.now unless self.reset_password_sent_at
|
170
|
+
end
|
171
|
+
|
172
|
+
def default_image_url(size="small")
|
173
|
+
"/assets/kuppayam/defaults/user-#{size}.png"
|
174
|
+
end
|
175
|
+
|
176
|
+
def set_permission(feature_name, **options)
|
177
|
+
options.reverse_merge!(
|
178
|
+
can_create: false,
|
179
|
+
can_read: true,
|
180
|
+
can_update: false,
|
181
|
+
can_delete: false
|
182
|
+
)
|
183
|
+
|
184
|
+
feature = get_feature(feature_name)
|
185
|
+
|
186
|
+
permission = Permission.where("user_id = ? AND feature_id = ?", self.id, feature.id).first || Permission.new(user: self, feature: feature)
|
187
|
+
permission.assign_attributes(options)
|
188
|
+
permission.save
|
189
|
+
end
|
190
|
+
|
191
|
+
def can_create?(feature_name)
|
192
|
+
feature = get_feature(feature_name)
|
193
|
+
|
194
|
+
permission = Permission.where("feature_id = ? AND user_id = ?", feature.id, self.id).first
|
195
|
+
permission && permission.can_create?
|
196
|
+
end
|
197
|
+
|
198
|
+
def can_read?(feature_name)
|
199
|
+
feature = get_feature(feature_name)
|
200
|
+
|
201
|
+
permission = Permission.where("feature_id = ? AND user_id = ?", feature.id, self.id).first
|
202
|
+
permission && permission.can_read?
|
203
|
+
end
|
204
|
+
|
205
|
+
def can_update?(feature_name)
|
206
|
+
feature = get_feature(feature_name)
|
207
|
+
|
208
|
+
permission = Permission.where("feature_id = ? AND user_id = ?", feature.id, self.id).first
|
209
|
+
permission && permission.can_update?
|
210
|
+
end
|
211
|
+
|
212
|
+
def can_delete?(feature_name)
|
213
|
+
feature = get_feature(feature_name)
|
214
|
+
|
215
|
+
permission = Permission.where("feature_id = ? AND user_id = ?", feature.id, self.id).first
|
216
|
+
permission && permission.can_delete?
|
217
|
+
end
|
218
|
+
|
219
|
+
def can_be_destroyed?
|
220
|
+
return true
|
221
|
+
end
|
222
|
+
|
223
|
+
private
|
224
|
+
|
225
|
+
def should_validate_password?
|
226
|
+
self.new_record? || (self.new_record? == false and self.password.present?)
|
227
|
+
end
|
228
|
+
|
229
|
+
def generate_auth_token
|
230
|
+
self.auth_token = SecureRandom.hex unless self.auth_token
|
231
|
+
end
|
232
|
+
|
233
|
+
def get_feature(feature_name)
|
234
|
+
case feature_name
|
235
|
+
when Feature
|
236
|
+
feature = feature_name
|
237
|
+
when String
|
238
|
+
feature = Feature.find_by_name(feature_name)
|
239
|
+
when Integer
|
240
|
+
feature = Feature.find_by_id(feature_name)
|
241
|
+
else
|
242
|
+
raise "Feature with name '#{feature.name}' doesn't exist" unless feature
|
243
|
+
end
|
244
|
+
return feature
|
245
|
+
end
|
246
|
+
|
247
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Usman
|
2
|
+
class AuthenticationService
|
3
|
+
|
4
|
+
attr_reader :login_handle, :password, :error, :user
|
5
|
+
|
6
|
+
def initialize(params)
|
7
|
+
@login_handle = params[:login_handle]
|
8
|
+
@password = params[:password]
|
9
|
+
@error = nil
|
10
|
+
|
11
|
+
check_if_user_exists
|
12
|
+
if @user
|
13
|
+
authenticate
|
14
|
+
check_if_user_is_approved
|
15
|
+
end
|
16
|
+
|
17
|
+
@user.start_session unless @error
|
18
|
+
end
|
19
|
+
|
20
|
+
def invalid_login_error
|
21
|
+
"authentication.invalid_login"
|
22
|
+
end
|
23
|
+
|
24
|
+
def user_status_error
|
25
|
+
"authentication.user_is_#{@user.status.downcase}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def check_if_user_exists
|
29
|
+
@user = User.where("LOWER(email) = LOWER('#{@login_handle}') OR LOWER(username) = LOWER('#{@login_handle}')").first
|
30
|
+
set_error(invalid_login_error) unless @user
|
31
|
+
end
|
32
|
+
|
33
|
+
def check_if_user_is_approved
|
34
|
+
set_error(user_status_error) unless @user.approved?
|
35
|
+
end
|
36
|
+
|
37
|
+
def authenticate
|
38
|
+
set_error(invalid_login_error) unless @user.authenticate(@password)
|
39
|
+
end
|
40
|
+
|
41
|
+
def set_error(id)
|
42
|
+
@error ||= id
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|