usman 0.1.0

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