usman 0.1.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 (90) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +38 -0
  4. data/Rakefile +37 -0
  5. data/app/assets/config/usman_manifest.js +2 -0
  6. data/app/assets/javascripts/usman/application.js +13 -0
  7. data/app/assets/stylesheets/usman/application.css +15 -0
  8. data/app/controllers/usman/admin/base_controller.rb +24 -0
  9. data/app/controllers/usman/admin/dashboard_controller.rb +18 -0
  10. data/app/controllers/usman/admin/features_controller.rb +86 -0
  11. data/app/controllers/usman/admin/permissions_controller.rb +76 -0
  12. data/app/controllers/usman/admin/resource_controller.rb +11 -0
  13. data/app/controllers/usman/admin/users_controller.rb +117 -0
  14. data/app/controllers/usman/application_controller.rb +13 -0
  15. data/app/controllers/usman/sessions_controller.rb +84 -0
  16. data/app/helpers/usman/application_helper.rb +4 -0
  17. data/app/helpers/usman/authentication_helper.rb +120 -0
  18. data/app/jobs/usman/application_job.rb +4 -0
  19. data/app/mailers/usman/application_mailer.rb +6 -0
  20. data/app/models/feature.rb +112 -0
  21. data/app/models/image/base.rb +30 -0
  22. data/app/models/image/feature_image.rb +3 -0
  23. data/app/models/image/profile_picture.rb +3 -0
  24. data/app/models/permission.rb +28 -0
  25. data/app/models/user.rb +247 -0
  26. data/app/models/usman/application_record.rb +5 -0
  27. data/app/services/usman/authentication_service.rb +45 -0
  28. data/app/uploaders/feature_image_uploader.rb +14 -0
  29. data/app/uploaders/image_uploader.rb +90 -0
  30. data/app/uploaders/profile_picture_uploader.rb +14 -0
  31. data/app/views/layouts/kuppayam/_footer.html.erb +25 -0
  32. data/app/views/layouts/kuppayam/_header.html.erb +43 -0
  33. data/app/views/layouts/kuppayam/_navbar.html.erb +55 -0
  34. data/app/views/layouts/kuppayam/_sidebar.html.erb +78 -0
  35. data/app/views/usman/admin/dashboard/index.html.erb +52 -0
  36. data/app/views/usman/admin/features/_action_buttons.html.erb +11 -0
  37. data/app/views/usman/admin/features/_form.html.erb +19 -0
  38. data/app/views/usman/admin/features/_index.html.erb +79 -0
  39. data/app/views/usman/admin/features/_row.html.erb +55 -0
  40. data/app/views/usman/admin/features/_show.html.erb +48 -0
  41. data/app/views/usman/admin/features/create.js.erb +16 -0
  42. data/app/views/usman/admin/features/destroy.js.erb +16 -0
  43. data/app/views/usman/admin/features/edit.js.erb +7 -0
  44. data/app/views/usman/admin/features/index.html.erb +25 -0
  45. data/app/views/usman/admin/features/index.js.erb +8 -0
  46. data/app/views/usman/admin/features/new.js.erb +7 -0
  47. data/app/views/usman/admin/features/row.js.erb +10 -0
  48. data/app/views/usman/admin/features/show.js.erb +8 -0
  49. data/app/views/usman/admin/features/update.js.erb +16 -0
  50. data/app/views/usman/admin/permissions/_action_buttons.html.erb +11 -0
  51. data/app/views/usman/admin/permissions/_form.html.erb +70 -0
  52. data/app/views/usman/admin/permissions/_index.html.erb +56 -0
  53. data/app/views/usman/admin/permissions/_row.html.erb +27 -0
  54. data/app/views/usman/admin/permissions/_show.html.erb +48 -0
  55. data/app/views/usman/admin/permissions/create.js.erb +17 -0
  56. data/app/views/usman/admin/permissions/destroy.js.erb +16 -0
  57. data/app/views/usman/admin/permissions/edit.js.erb +7 -0
  58. data/app/views/usman/admin/permissions/index.html.erb +25 -0
  59. data/app/views/usman/admin/permissions/index.js.erb +8 -0
  60. data/app/views/usman/admin/permissions/new.js.erb +7 -0
  61. data/app/views/usman/admin/permissions/row.js.erb +10 -0
  62. data/app/views/usman/admin/permissions/show.js.erb +8 -0
  63. data/app/views/usman/admin/permissions/update.js.erb +16 -0
  64. data/app/views/usman/admin/users/_action_buttons.html.erb +11 -0
  65. data/app/views/usman/admin/users/_form.html.erb +36 -0
  66. data/app/views/usman/admin/users/_index.html.erb +120 -0
  67. data/app/views/usman/admin/users/_row.html.erb +92 -0
  68. data/app/views/usman/admin/users/_show.html.erb +132 -0
  69. data/app/views/usman/admin/users/create.js.erb +16 -0
  70. data/app/views/usman/admin/users/destroy.js.erb +16 -0
  71. data/app/views/usman/admin/users/edit.js.erb +7 -0
  72. data/app/views/usman/admin/users/index.html.erb +40 -0
  73. data/app/views/usman/admin/users/index.js.erb +8 -0
  74. data/app/views/usman/admin/users/new.js.erb +7 -0
  75. data/app/views/usman/admin/users/row.js.erb +10 -0
  76. data/app/views/usman/admin/users/show.js.erb +8 -0
  77. data/app/views/usman/admin/users/update.js.erb +16 -0
  78. data/app/views/usman/sessions/_form.html.erb +48 -0
  79. data/app/views/usman/sessions/_sign_in.js.erb +3 -0
  80. data/app/views/usman/sessions/sign_in.html.erb +63 -0
  81. data/config/locales/usman.en.yml +61 -0
  82. data/config/routes.rb +45 -0
  83. data/db/migrate/20131108102728_create_images.rb +12 -0
  84. data/db/migrate/20140402113213_create_users.rb +57 -0
  85. data/db/migrate/20140402113214_create_features.rb +24 -0
  86. data/lib/tasks/usman_tasks.rake +4 -0
  87. data/lib/usman/engine.rb +14 -0
  88. data/lib/usman/version.rb +3 -0
  89. data/lib/usman.rb +5 -0
  90. 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,4 @@
1
+ module Usman
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module Usman
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ 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,3 @@
1
+ class Image::FeatureImage < Image::Base
2
+ mount_uploader :image, FeatureImageUploader
3
+ end
@@ -0,0 +1,3 @@
1
+ class Image::ProfilePicture < Image::Base
2
+ mount_uploader :image, ProfilePictureUploader
3
+ 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
@@ -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,5 @@
1
+ module Usman
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ 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
@@ -0,0 +1,14 @@
1
+ class FeatureImageUploader < ImageUploader
2
+ def store_dir
3
+ "uploads/feature_images/#{model.id}"
4
+ end
5
+
6
+ version :large do
7
+ process :resize_to_fill => [400, 400]
8
+ end
9
+
10
+ version :small do
11
+ process :resize_to_fill => [100, 100]
12
+ end
13
+
14
+ end