Refactor for authorization sanity

This commit is contained in:
2015-02-19 16:29:17 +01:00
parent a1474e934a
commit 5b3c1a9007
22 changed files with 190 additions and 80 deletions
@@ -15,6 +15,7 @@ App.ApplicationController = Ember.Controller.extend
#@set 'supplier', null #@set 'supplier', null
#@store.find('supplier', 'current').then success, error #@store.find('supplier', 'current').then success, error
# @set 'supplier', @store.find('supplier', supplier_id) # @set 'supplier', @store.find('supplier', supplier_id)
current_employee: (-> @get('employee') ).property('employee')
actions: actions:
signOut: -> signOut: ->
window.location = Routes.destroy_employee_session_path() window.location = Routes.destroy_employee_session_path()
@@ -34,16 +34,19 @@ App.IndexController = Ember.ObjectController.extend
toggleDashboardOrders: ->@set 'show_orders', !@get('show_orders') toggleDashboardOrders: ->@set 'show_orders', !@get('show_orders')
markOrderDelivered: (id)-> markOrderDelivered: (id)->
$.post('/supplier/order_is_delivered', order_id: id) #$.post('/supplier/order_is_delivered', order_id: id)
Ember.$.post Routes.mark_delivered_suppliers_order_path(id)
if order = App.Order.findCached(id) if order = App.Order.findCached(id)
order.set('state', 'delivered') order.set('state', 'delivered')
markOrderActive: (id)-> markOrderActive: (id)->
$.post('/supplier/mark_order_in_process', order_id: id) #$.post('/supplier/mark_order_in_process', order_id: id)
Ember.$.post Routes.mark_in_process_suppliers_order_path(id)
if order = App.Order.findCached(id) if order = App.Order.findCached(id)
order.set('state', 'active') order.set('state', 'active')
cancelOrder: (order)-> cancelOrder: (order)->
$.post "/supplier/orders/#{order.id}/cancel" #$.post "/supplier/orders/#{order.id}/cancel"
Ember.$.post Routes.cancel_suppliers_order_path(order.id)
order.set 'state', 'cancelled' order.set 'state', 'cancelled'
showList: (id)-> showList: (id)->
@transitionToRoute 'list', id @transitionToRoute 'list', id
@@ -1,5 +1,5 @@
Ember.Handlebars.registerHelper 'can', (operation, resource, options)-> Ember.Handlebars.registerHelper 'can', (operation, resource, options)->
if operation is 'manage' and @get('controllers.application.employee_settings.manager') if operation is 'manage' and @get('current_employee.manager')
options.fn @ options.fn @
else else
options.inverse @ options.inverse @
@@ -2,6 +2,8 @@ attr = DS.attr
App.Employee= DS.Model.extend Ember.Validations.Mixin, App.Employee= DS.Model.extend Ember.Validations.Mixin,
name: attr 'string' name: attr 'string'
email: attr 'string' email: attr 'string'
manager: attr 'boolean', defaultValue: false
active: attr 'boolean', defaultValue: true
validations: validations:
name: {presence: true} name: {presence: true}
@@ -45,11 +45,11 @@ App.List = DS.Model.extend
).property('state', 'orders.@each.state') ).property('state', 'orders.@each.state')
close: -> close: ->
@markClosed() @markClosed()
$.post Routes.supplier_close_list_path(), list_id: @id $.post Routes.close_suppliers_list_pat(@id)
is_helped: -> is_helped: ->
@markHelped() @markHelped()
$.post Routes.supplier_mark_list_as_helped_path(), list_id: @id $.post Routes.mark_helped_suppliers_list_path(@id)
remove_needs_payment: -> remove_needs_payment: ->
@set 'needs_payment', false @set 'needs_payment', false
$.post Routes.supplier_remove_list_needs_payment_path(), list_id: @id $.post Routes.supplier_remove_list_needs_payment_path(), list_id: @id
@@ -11,6 +11,7 @@ ControllerExtensions = Ember.Mixin.create
@send "openModal", name, options @send "openModal", name, options
all_sections: (-> @store.all('section')).property() all_sections: (-> @store.all('section')).property()
current_employee: (-> @get('controllers.application.employee') ).property('controllers.application.employee')
Ember.Controller.reopen ControllerExtensions Ember.Controller.reopen ControllerExtensions
Ember.ArrayController.reopen ControllerExtensions Ember.ArrayController.reopen ControllerExtensions
@@ -14,7 +14,6 @@ App.ApplicationRoute = Ember.Route.extend
setupController: (controller)-> setupController: (controller)->
controller.set 'supplier', @supplier controller.set 'supplier', @supplier
controller.set 'employee', @employee controller.set 'employee', @employee
controller.set 'employee_settings', Ember.Object.create(employee_settings)
# @set 'supplier', @store.find('supplier', supplier_id) # @set 'supplier', @store.find('supplier', supplier_id)
#controller.set 'sections', @sections #controller.set 'sections', @sections
#controller.set 'product_categories', @product_categories #controller.set 'product_categories', @product_categories
@@ -2,6 +2,7 @@ aside.side-menu
ul ul
li.title: h3 Menu li.title: h3 Menu
li.supplier-name= supplier.name li.supplier-name= supplier.name
can manage supplier
if supplier.open if supplier.open
li: a.supplier-close-shop{action "markSupplierClosed"}= t 'supplier.close_for_orders' li: a.supplier-close-shop{action "markSupplierClosed"}= t 'supplier.close_for_orders'
else else
+1 -1
View File
@@ -27,7 +27,7 @@ class DashboardController < ApplicationController
if Rails.env.test? if Rails.env.test?
@tables = Table.all @tables = Table.all
else else
@tables = (current_supplier || Supplier.first).tables.sample(5) | List.active.map(&:table) | Supplier.find_by_email('supplier2@mozo.bar').tables.sample(3) @tables = (Supplier.last || Supplier.first).tables.sample(5) | List.active.map(&:table) | Supplier.find_by_email('supplier2@mozo.bar').tables.sample(3)
end end
respond_to do |format| respond_to do |format|
format.html { render layout: 'phone' } format.html { render layout: 'phone' }
+2 -33
View File
@@ -1,7 +1,4 @@
class SupplierController < Suppliers::ApplicationController class SupplierController < Suppliers::ApplicationController
before_filter :setup_employee_and_supplier!
layout 'supplier/app'
def home def home
end end
@@ -38,11 +35,11 @@ class SupplierController < Suppliers::ApplicationController
def mark_as_open def mark_as_open
current_supplier.mark_as_open! current_supplier.mark_as_open!
redirect_to :back head :ok
end end
def mark_as_closed def mark_as_closed
current_supplier.mark_as_closed! current_supplier.mark_as_closed!
redirect_to :back head :ok
end end
# GET /suppliers/1/active_orders # GET /suppliers/1/active_orders
# GET /suppliers/1/active_orders.json # GET /suppliers/1/active_orders.json
@@ -97,20 +94,6 @@ class SupplierController < Suppliers::ApplicationController
end end
end end
# POST /supplier/close_list
def close_list
@list = List.find_by_supplier_id_and_id(current_supplier.id, params[:list_id])
@list.close!
render nothing: true
end
# POST /supplier/mark_list_as_helped
def mark_list_as_helped
@list = List.find_by_supplier_id_and_id(current_supplier.id, params[:list_id])
@list.is_helped!
render nothing: true
end
#POST /supplier/remove_list_needs_payment #POST /supplier/remove_list_needs_payment
def remove_list_needs_payment def remove_list_needs_payment
@list = List.find_by_supplier_id_and_id(current_supplier.id, params[:list_id]) @list = List.find_by_supplier_id_and_id(current_supplier.id, params[:list_id])
@@ -118,20 +101,6 @@ class SupplierController < Suppliers::ApplicationController
render nothing: true render nothing: true
end end
# POST /orders/1/is_being_processed
def mark_order_in_process
@order = Order.find_by_supplier_id_and_id!(current_supplier.id, params[:order_id])
@order.is_being_processed!
render nothing: true
end
# POST /orders/1/is_delivered
def order_is_delivered
@order = Order.find_by_supplier_id_and_id(current_supplier.id, params[:order_id])
@order.is_delivered!
render nothing: true
end
private private
def supplier_params def supplier_params
@@ -2,23 +2,26 @@ module Suppliers
class ApplicationController < ::ApplicationController class ApplicationController < ::ApplicationController
before_action :setup_employee_and_supplier! before_action :setup_employee_and_supplier!
load_and_authorize_resource load_and_authorize_resource
attr_reader :current_supplier, :employee_settings attr_reader :current_supplier
helper_method :current_supplier, :employee_settings helper_method :current_supplier
layout 'supplier/app' layout 'supplier/app'
rescue_from 'RestClient::Conflict' do |e| rescue_from 'RestClient::Conflict' do |e|
#binding.pry #binding.pry
end end
rescue_from CanCan::AccessDenied do |exception| rescue_from CanCan::AccessDenied do |exception|
render json: {}, status: :forbidden respond_to do |format|
format.html { redirect_to root_path, alert: 'Action forbidden'}
format.json { render json: {}, status: :forbidden }
end
end end
def setup_employee_and_supplier! def setup_employee_and_supplier!
authenticate_employee! authenticate_employee!
find_current_supplier! find_current_supplier!
return unless current_supplier.present? return unless current_supplier.present?
@employee_settings = current_supplier.employee_settings.for_employee( current_employee ) current_employee.enrich_with_settings current_supplier.settings_for(current_employee)
@current_ability = ::Ability.new(@employee_settings) @current_ability = ::Ability.new( current_employee )
end end
@@ -142,6 +142,19 @@ module Suppliers
format.json { head :no_content } format.json { head :no_content }
end end
end end
# POST /supplier/lists/1/close
def close
@list = List.find_by_supplier_id_and_id(current_supplier.id, params[:id])
@list.close!
head :ok
end
# POST /supplier/lists/1/mark_helped
def mark_helped
@list = List.find_by_supplier_id_and_id(current_supplier.id, params[:id])
@list.mark_helped!
head :ok
end
private private
@@ -27,5 +27,19 @@ module Suppliers
format.json { render json: @order } format.json { render json: @order }
end end
end end
# POST /orders/1/mark_in_process
def mark_in_process
@order = Order.find_by_supplier_id_and_id!(current_supplier.id, params[:id])
@order.is_being_processed!
head :ok
end
# POST /orders/1/is_delivered
def mark_delivered
@order = Order.find_by_supplier_id_and_id(current_supplier.id, params[:id])
@order.is_delivered!
render nothing: true
end
end end
end end
+20
View File
@@ -1,6 +1,12 @@
class Employee class Employee
include SimplyStored::Couch include SimplyStored::Couch
include ActiveModel::SerializerSupport include ActiveModel::SerializerSupport
DEFAULT_SETTINGS = {
'manager' => false,
'active' => true
}
attr_accessor *DEFAULT_SETTINGS.keys
attr_reader :settings
view :by_confirmation_token, key: :confirmation_token # devise confirmable view :by_confirmation_token, key: :confirmation_token # devise confirmable
devise :database_authenticatable, :recoverable, :rememberable, :trackable, :registerable, :confirmable devise :database_authenticatable, :recoverable, :rememberable, :trackable, :registerable, :confirmable
@@ -15,6 +21,9 @@ class Employee
def count_by_email(email) def count_by_email(email)
database.view by_email(key: email, reduce: true) database.view by_email(key: email, reduce: true)
end end
def default_settings
DEFAULT_SETTINGS
end
end end
#validates :email, email: true #validates :email, email: true
@@ -22,4 +31,15 @@ class Employee
has_many :orders has_many :orders
has_and_belongs_to_many :lists, storing_keys: false has_and_belongs_to_many :lists, storing_keys: false
def enrich_with_settings(settings)
@settings = settings
settings.settings.each do |attribute, value|
public_send "#{attribute}=", value
end
settings
end
def settings
@settings || SupplierEmployeesSettings.new(Supplier.new).for_employee(self)
end
end end
+2 -2
View File
@@ -150,7 +150,7 @@ class List
set_price # should not be needed, but extra secure set_price # should not be needed, but extra secure
orders.map(&:close!) # close the connected orders orders.map(&:close!) # close the connected orders
self.state = 'closed' self.state = 'closed'
self.is_helped! if needs_help? self.mark_helped! if needs_help?
self.needs_payment = false self.needs_payment = false
self.user_requests_closing = false # if a user requested closing, not needed anymore self.user_requests_closing = false # if a user requested closing, not needed anymore
self.closed_at = Time.now self.closed_at = Time.now
@@ -173,7 +173,7 @@ class List
end end
end end
def is_helped! def mark_helped!
self.needs_help = false self.needs_help = false
if save if save
broadcast_users 'list_helped', id: id broadcast_users 'list_helped', id: id
+10 -1
View File
@@ -42,6 +42,11 @@ class Supplier
has_many :sections, dependent: :destroy has_many :sections, dependent: :destroy
has_and_belongs_to_many :employees, storing_keys: true has_and_belongs_to_many :employees, storing_keys: true
alias_method :non_enriced_employees, :employees
def employees
non_enriced_employees.tap { |es| es.each{ |e| e.enrich_with_settings(settings_for(e) ) }}
end
after_create :add_section_on_create after_create :add_section_on_create
validates :name, presence: true validates :name, presence: true
@@ -66,7 +71,11 @@ class Supplier
end end
def employee_settings def employee_settings
SupplierEmployeesSettings.new(self) @employee_settings ||= SupplierEmployeesSettings.new(self)
end
def settings_for(employee)
employee_settings.for_employee(employee)
end end
def active_orders(options = {}) def active_orders(options = {})
+69 -10
View File
@@ -2,7 +2,7 @@ class SupplierEmployeesSettings
attr_reader :dictionary, :employee_ids, :supplier attr_reader :dictionary, :employee_ids, :supplier
def initialize(supplier) def initialize(supplier)
@supplier = supplier @supplier = supplier
@dictionary = supplier.employee_settings_storage || {} @dictionary = (supplier.employee_settings_storage || {}).to_h
@employee_ids = supplier.employee_ids || [] @employee_ids = supplier.employee_ids || []
end end
@@ -39,33 +39,92 @@ class SupplierEmployeesSettings
self.replace options if options.is_a? Hash self.replace options if options.is_a? Hash
end end
alias_method :orig_setter, :[]=
def []=(*args) def []=(*args)
super orig_setter(*args)
all_employees_settings.persist all_employees_settings.persist
end end
end end
class SupplierEmployeeSettings class SupplierEmployeeSettings
delegate :as_json, :to_json, to: :settings delegate :as_json, :to_json, to: :settings
DEFAULTS = {
'manager' => false
}
attr_reader :id, :settings, :all_employees_settings attr_reader :id, :settings, :all_employees_settings
class NullObject < ::NullObject class NullObject < ::NullObject
::Employee::DEFAULT_SETTINGS.each do |attribute, value|
if value == true or value == false
define_method("#{attribute}?"){ value }
end
define_method(attribute){ value }
end
end end
def initialize(all_employees_settings, employee_id, settings = {}) def initialize(all_employees_settings, employee_id, settings = {})
@all_employees_settings = all_employees_settings @all_employees_settings = all_employees_settings
@id = employee_id @id = employee_id
@settings = DEFAULTS.merge(settings || {}) @settings = Employee.default_settings.merge(settings || {})
end end
def manager? def [](val)
settings['manager'] settings[val.to_s]
end end
def is_manager! def set(attribute, value, persist: false)
all_employees_settings[id]['manager'] = true settings[attribute] = value
all_employees_settings[id][attribute] = value
if employee = all_employees_settings.supplier.employees.find{|e| e.id == id}
employee.public_send("#{attribute}=", value)
end
all_employees_settings.supplier.save if persist
self
end
def []=(attribute, value)
set attribute, value
end
def set!(attribute, value)
set attribute, value, persist: true
end
# Parse a method name to its underlying operations
# settings.is_manager?
# is a getter for the attribute manager
# settings.is_manager!
# is a boolean setter to true for the attribute manager and persists the inderlying model
# without exclamation mark it is the same, but without persisting the model.
# settings.is_not_manager!
# is a boolean setter to false for the attribute manger.
# settings.manager
# is a normal accessor for the attribute manager
def parse_method_for_boolean_operation(method)
method = method.to_s
persist = false
attribute = method
set_value = nil
if method.end_with? '!'
persist = true
attribute.chop!
end
if method.end_with? '?'
# Always a getter, so no set_value
attribute.chop!
attribute = attribute.from(3) if attribute.start_with? 'is_'
elsif method.start_with? 'is_not_'
set_value = false
attribute = attribute.from 7
elsif method.start_with? 'is_'
set_value = true
attribute = attribute.from 3
end
raise "Settings do not support attribute '#{attribute}'" unless Employee.default_settings.has_key?(attribute) # Do not return a nonexistent attribute
[attribute, set_value, persist]
end
def method_missing(m, *args)
attribute, set_value, persist = parse_method_for_boolean_operation(m)
return unless attribute # method cannot be used in the settings
return settings[attribute] if set_value.nil? # Getter operation, setting nil is not supported
set attribute, set_value, persist: persist
end end
end end
end end
@@ -1,5 +1,5 @@
class Suppliers::EmployeeSerializer < Qwaiter::Serializer class Suppliers::EmployeeSerializer < Qwaiter::Serializer
self.root = :employee self.root = :employee
embed :ids, include: true embed :ids, include: true
attributes :name, :email attributes :name, :email, :manager, :active
end end
+19 -3
View File
@@ -1,11 +1,27 @@
class Ability class Ability
attr_reader :resource
include CanCan::Ability include CanCan::Ability
def initialize(settings) def initialize(resource)
settings ||= SupplierEmployeesSettings.new(Supplier.new).for_employee(nil) @resource = resource
if settings.manager? case resource
when Employee then supplier_authorization
end
end
def supplier_authorization
if resource.settings.manager?
can :manage, :all can :manage, :all
#cam :mark_as_open, Supplier
else else
can :read, :all can :read, :all
can :qr_codes, Table
can :home, Supplier
can :current, :all
can :close, List
can :mark_helped, List
can :mark_in_process, Order
can :mark_delivered, Order
can :cancel, Order
end end
end end
end end
@@ -2,8 +2,7 @@
//var $locale = '<%= I18n.locale %>'; //var $locale = '<%= I18n.locale %>';
//var supplier_id = '<%= current_supplier.id %>'; //var supplier_id = '<%= current_supplier.id %>';
var supplier_object=<%= {id: current_supplier.id}.to_json.html_safe %>; var supplier_object=<%= {id: current_supplier.id}.to_json.html_safe %>;
var employee_object=<%=raw current_employee.attributes.slice(:name, :email).merge(id: current_employee.id).to_json %>; var employee_object=<%=raw Suppliers::EmployeeSerializer.new(current_employee, root: false).to_json %>;
var employee_settings=<%= raw employee_settings.to_json %>;
var data_host = ''; var data_host = '';
var event_host = '<%= Qwaiter.event_host %>'; var event_host = '<%= Qwaiter.event_host %>';
//var datepicker_options = {dateFormat: 'yy-mm-dd', firstDay: <%= current_supplier.week_starts_on_monday? ? 1 : 0 %>}; //var datepicker_options = {dateFormat: 'yy-mm-dd', firstDay: <%= current_supplier.week_starts_on_monday? ? 1 : 0 %>};
+8 -4
View File
@@ -104,12 +104,12 @@ Qwaiter::Application.routes.draw do
get '/supplier/menu' => 'supplier#menu', as: :suppliers_menu get '/supplier/menu' => 'supplier#menu', as: :suppliers_menu
#get '/supplier/lists/:list_id' => 'supplier#show_list', as: :supplier_show_list #get '/supplier/lists/:list_id' => 'supplier#show_list', as: :supplier_show_list
post '/supplier/close_list' => 'supplier#close_list', as: :supplier_close_list #post '/supplier/close_list' => 'supplier#close_list', as: :supplier_close_list
post '/supplier/mark_list_as_helped' => 'supplier#mark_list_as_helped', as: :supplier_mark_list_as_helped #post '/supplier/mark_list_as_helped' => 'supplier#mark_list_as_helped', as: :supplier_mark_list_as_helped
post '/supplier/remove_list_needs_payment' => 'supplier#remove_list_needs_payment', as: :supplier_remove_list_needs_payment post '/supplier/remove_list_needs_payment' => 'supplier#remove_list_needs_payment', as: :supplier_remove_list_needs_payment
post '/supplier/mark_order_in_process' => 'supplier#mark_order_in_process', as: :supplier_mark_order_in_process #post '/supplier/mark_order_in_process' => 'supplier#mark_order_in_process', as: :supplier_mark_order_in_process
post '/supplier/order_is_delivered' => 'supplier#order_is_delivered', as: :supplier_order_is_delivered #post '/supplier/order_is_delivered' => 'supplier#order_is_delivered', as: :supplier_order_is_delivered
post '/supplier/mark_as_open' => 'supplier#mark_as_open', as: :supplier_mark_as_open post '/supplier/mark_as_open' => 'supplier#mark_as_open', as: :supplier_mark_as_open
post '/supplier/mark_as_closed' => 'supplier#mark_as_closed', as: :supplier_mark_as_closed post '/supplier/mark_as_closed' => 'supplier#mark_as_closed', as: :supplier_mark_as_closed
@@ -147,6 +147,8 @@ Qwaiter::Application.routes.draw do
member do member do
get :extra_info get :extra_info
post :mark_helped
post :close
end end
end end
resources :product_categories do resources :product_categories do
@@ -158,6 +160,8 @@ Qwaiter::Application.routes.draw do
resources :orders, only: [:index, :show] do resources :orders, only: [:index, :show] do
member do member do
post :cancel post :cancel
post :mark_in_process
post :mark_delivered
end end
end end
root to: 'sections#index' root to: 'sections#index'
-3
View File
@@ -18,9 +18,6 @@ describe SupplierController, type: :routing do
it "routes to #mark_list_as_helped" do it "routes to #mark_list_as_helped" do
post("/supplier/mark_list_as_helped").should route_to("supplier#mark_list_as_helped") post("/supplier/mark_list_as_helped").should route_to("supplier#mark_list_as_helped")
end end
it "routes to #mark_order_in_process" do
post("/supplier/mark_order_in_process").should route_to("supplier#mark_order_in_process")
end
it "routes to #order_is_delivered" do it "routes to #order_is_delivered" do
post("/supplier/order_is_delivered").should route_to("supplier#order_is_delivered") post("/supplier/order_is_delivered").should route_to("supplier#order_is_delivered")
end end