This is a repost of the patch series from last Tuesday. The main change is that auth errors are now handled with Rails rescue_from mechanism. David
David Lutterkort
2009-May-04 23:15 UTC
[Ovirt-devel] [PATCH server 1/4] * src/Rakefile: do not require gettext for all tasks, breaks rdoc
--- src/Rakefile | 7 ++++++- 1 files changed, 6 insertions(+), 1 deletions(-) diff --git a/src/Rakefile b/src/Rakefile index 74b9d18..eebf798 100644 --- a/src/Rakefile +++ b/src/Rakefile @@ -1,3 +1,4 @@ +# -*- ruby -*- # Add your own tasks in files placed in lib/tasks ending in .rake, # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. @@ -8,14 +9,18 @@ require 'rake/testtask' require 'rake/rdoctask' require 'tasks/rails' -require 'gettext/utils' +# Do not require 'gettext/utils' here, it breaks rdoc, +# require it in tasks that actually need it instead + desc "Create mo-files for L10n" task :makemo do + require 'gettext/utils' GetText.create_mofiles(true, "po", "locale") end desc "Update pot/po files to match new version." task :updatepo do + require 'gettext/utils' MY_APP_TEXT_DOMAIN = "ovirt" MY_APP_VERSION = "ovirt 0.0.1" GetText.update_pofiles(MY_APP_TEXT_DOMAIN, -- 1.6.0.6
David Lutterkort
2009-May-04 23:15 UTC
[Ovirt-devel] [PATCH server 2/4] * app/controllers/application.rb: handler for permission errors
Use Rails' rescue_from to catch permission errors and render an appropriate
response.
---
src/app/controllers/application.rb | 35 +++++++++++++++++++++++++++++++++++
1 files changed, 35 insertions(+), 0 deletions(-)
diff --git a/src/app/controllers/application.rb
b/src/app/controllers/application.rb
index e5f4d4b..3902e78 100644
--- a/src/app/controllers/application.rb
+++ b/src/app/controllers/application.rb
@@ -48,6 +48,8 @@ class ApplicationController < ActionController::Base
before_filter :tmp_authorize_admin, :only => [:create, :update, :destroy]
before_filter :is_logged_in, :get_help_section
+ # General error handlers
+ rescue_from PermissionError, :with => :handle_perm_error
def choose_layout
if(params[:component_layout])
@@ -140,6 +142,39 @@ class ApplicationController < ActionController::Base
end
end
+ def handle_perm_error(error)
+ handle_error(:error => error, :status => :forbidden,
+ :title => "Access denied")
+ end
+
+ def handle_error(hash)
+ msg = hash[:message] || hash[:error].message
+ title = hash[:title] || "Internal Server Error"
+ status = hash[:status] || :internal_server_error
+ respond_to do |format|
+ format.html do
+ @title = title || "Something went very wrong"
+ @errmsg = msg
+ @ajax = params[:ajax]
+ @nolayout = params[:nolayout]
+ if @ajax
+ render :template => 'layouts/popup-error', :layout =>
'tabs-and-content'
+ elsif @nolayout
+ render :template => 'layouts/popup-error', :layout =>
'help-and-content'
+ else
+ render :template => 'layouts/popup-error', :layout =>
'popup'
+ end
+ end
+ format.json do
+ @json_hash ||= {}
+ @json_hash[:success] = (status == :ok)
+ @json_hash[:alert] = msg
+ render :json => @json_hash
+ end
+ format.xml { head status }
+ end
+ end
+
# don't define find_opts for array inputs
def json_hash(full_items, attributes, arg_list=[], find_opts={},
id_method=:id)
page = params[:page].to_i
--
1.6.0.6
David Lutterkort
2009-May-04 23:15 UTC
[Ovirt-devel] [PATCH server 3/4] Remove handle_auth_error since we use rescue_with now
---
src/app/controllers/application.rb | 27 +--------------------
src/app/controllers/storage_volume_controller.rb | 5 +++-
src/app/controllers/vm_controller.rb | 12 ++-------
3 files changed, 9 insertions(+), 35 deletions(-)
diff --git a/src/app/controllers/application.rb
b/src/app/controllers/application.rb
index 3902e78..638f9c0 100644
--- a/src/app/controllers/application.rb
+++ b/src/app/controllers/application.rb
@@ -111,36 +111,13 @@ class ApplicationController < ActionController::Base
def authorize_action(privilege, msg=nil)
msg ||= 'You have insufficient privileges to perform action.'
unless authorized?(privilege)
- handle_auth_error(msg)
+ handle_error(:message => msg,
+ :title => "Access Denied", :status =>
:forbidden)
false
else
true
end
end
- def handle_auth_error(msg)
- respond_to do |format|
- format.html do
- @title = "Access denied"
- @errmsg = msg
- @ajax = params[:ajax]
- @nolayout = params[:nolayout]
- if @ajax
- render :template => 'layouts/popup-error', :layout =>
'tabs-and-content'
- elsif @nolayout
- render :template => 'layouts/popup-error', :layout =>
'help-and-content'
- else
- render :template => 'layouts/popup-error', :layout =>
'popup'
- end
- end
- format.json do
- @json_hash ||= {}
- @json_hash[:success] = false
- @json_hash[:alert] = msg
- render :json => @json_hash
- end
- format.xml { head :forbidden }
- end
- end
def handle_perm_error(error)
handle_error(:error => error, :status => :forbidden,
diff --git a/src/app/controllers/storage_volume_controller.rb
b/src/app/controllers/storage_volume_controller.rb
index b6b0593..c6ce58d 100644
--- a/src/app/controllers/storage_volume_controller.rb
+++ b/src/app/controllers/storage_volume_controller.rb
@@ -112,7 +112,10 @@ class StorageVolumeController < ApplicationController
@storage_volume = StorageVolume.find(params[:id])
set_perms(@storage_volume.storage_pool.hardware_pool)
unless authorized?(Privilege::MODIFY) and
@storage_volume.storage_pool.user_subdividable
- handle_auth_error("You do not have permission to delete this storage
volume.")
+ handle_error(:message =>
+ "You do not have permission to delete this storage
volume.",
+ :status => :forbidden,
+ :title => "Access Denied")
else
alert, success = delete_volume_internal(@storage_volume)
respond_to do |format|
diff --git a/src/app/controllers/vm_controller.rb
b/src/app/controllers/vm_controller.rb
index ee6d79e..c276f0b 100644
--- a/src/app/controllers/vm_controller.rb
+++ b/src/app/controllers/vm_controller.rb
@@ -41,13 +41,9 @@ class VmController < ApplicationController
end
def show
- begin
- svc_show(params[:id])
- @actions = @vm.get_action_hash(@user)
- render :layout => 'selection'
- rescue PermissionError => perm_error
- handle_auth_error(perm_error.message)
- end
+ svc_show(params[:id])
+ @actions = @vm.get_action_hash(@user)
+ render :layout => 'selection'
end
def add_to_smart_pool
@@ -65,8 +61,6 @@ class VmController < ApplicationController
begin
alert = svc_create(params[:vm], params[:start_now])
render :json => { :object => "vm", :success => true,
:alert => alert }
- rescue PermissionError => perm_error
- handle_auth_error(perm_error.message)
rescue Exception => error
json_error("vm", @vm, error)
end
--
1.6.0.6
David Lutterkort
2009-May-04 23:15 UTC
[Ovirt-devel] [PATCH server 4/4] * app/controllers/host_controller.rb: factor biz logic out
The business logic is now in HostService.
The main functional difference is that svc_list filters the host list by
permission.
---
src/app/controllers/host_controller.rb | 125 +++++++++++++-------------------
src/app/services/host_service.rb | 119 ++++++++++++++++++++++++++++++
2 files changed, 168 insertions(+), 76 deletions(-)
create mode 100644 src/app/services/host_service.rb
diff --git a/src/app/controllers/host_controller.rb
b/src/app/controllers/host_controller.rb
index 994b5e2..9b683fa 100644
--- a/src/app/controllers/host_controller.rb
+++ b/src/app/controllers/host_controller.rb
@@ -19,8 +19,13 @@
class HostController < ApplicationController
- EQ_ATTRIBUTES = [ :state, :arch, :hostname, :uuid,
- :hardware_pool_id ]
+ include HostService
+
+ # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
+ verify :method => [:post, :put], :only => [ :create, :update ],
+ :redirect_to => { :action => :list }
+ verify :method => [:post, :delete], :only => :destroy,
+ :redirect_to => { :action => :list }
def index
list
@@ -30,42 +35,21 @@ class HostController < ApplicationController
end
end
- before_filter :pre_action, :only => [:host_action, :enable, :disable,
:clear_vms, :edit_network]
- before_filter :pre_addhost, :only => [:addhost]
-
- # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
- verify :method => [:post, :put], :only => [ :create, :update ],
- :redirect_to => { :action => :list }
- verify :method => [:post, :delete], :only => :destroy,
- :redirect_to => { :action => :list }
-
- def list
- conditions = []
- EQ_ATTRIBUTES.each do |attr|
- if params[attr]
- conditions << "#{attr} = :#{attr}"
- end
- end
- @hosts = Host.find(:all,
- :conditions => [conditions.join(" and "),
params],
- :order => "id")
- end
-
+ def list
+ svc_list(params)
+ end
def show
- if authorize_view
- respond_to do |format|
- format.html { render :layout => 'selection' }
- format.xml { render :xml => @host.to_xml(:include => [ :cpus ] )
}
- end
+ svc_show(params[:id])
+ respond_to do |format|
+ format.html { render :layout => 'selection' }
+ format.xml { render :xml => @host.to_xml(:include => [ :cpus ] ) }
end
end
def quick_summary
- pre_show
- if authorize_view
- render :layout => false
- end
+ svc_show(id)
+ render :layout => false
end
# retrieves data used by snapshot graphs
@@ -73,15 +57,17 @@ class HostController < ApplicationController
end
def addhost
- @hardware_pool = Pool.find(params[:hardware_pool_id])
- render :layout => 'popup'
- end
+ # FIXME: This probably should go into PoolService.svc_modify,
+ # so that we have permission checks in only one place
- def pre_addhost
+ # Old pre_addhost
@pool = Pool.find(params[:hardware_pool_id])
@parent = @pool.parent
set_perms(@pool)
authorize_admin
+ # Old addhost
+ @hardware_pool = Pool.find(params[:hardware_pool_id])
+ render :layout => 'popup'
end
def add_to_smart_pool
@@ -89,6 +75,11 @@ class HostController < ApplicationController
render :layout => 'popup'
end
+ # FIXME: We implement the standard controller actions, but catch
+ # them in filters and kick out friendly warnings that you can't
+ # perform them on hosts. Tat's overkill - the only way for a user
+ # to get to these actions is with URL surgery or from a bug in the
+ # application, both of which deserve a noisy error
def new
end
@@ -116,52 +107,40 @@ class HostController < ApplicationController
end
def disable
- set_disabled(1)
+ svc_enable(params[:id], "disabled")
+ render :json => {
+ :object => :host,
+ :alert => "Host was successfully disabled",
+ :success => true
+ }
end
+
def enable
- set_disabled(0)
- end
-
- def set_disabled(value)
- operation = value == 1 ? "disabled" : "enabled"
- begin
- @host.is_disabled = value
- @host.save!
- @json_hash[:alert]="Host was successfully #{operation}"
- @json_hash[:success]=true
- rescue
- @json_hash[:alert]="Error setting host to #{operation}"
- @json_hash[:success]=false
- end
- render :json => @json_hash
+ svc_enable(params[:id], "enabled")
+ render :json => {
+ :object => :host,
+ :alert => "Host was successfully enabled",
+ :success => true
+ }
end
def clear_vms
- begin
- Host.transaction do
- task = HostTask.new({ :user => get_login_user,
- :task_target => @host,
- :action => HostTask::ACTION_CLEAR_VMS,
- :state => Task::STATE_QUEUED})
- task.save!
- @host.is_disabled = true
- @host.save!
- end
- @json_hash[:alert]="Clear VMs action was successfully queued."
- @json_hash[:success]=true
- rescue
- @json_hash[:alert]="Error in queueing Clear VMs action."
- @json_hash[:success]=false
- end
- render :json => @json_hash
+ svc_clear_vms(params[:id])
+ render :json => {
+ :object => :host,
+ :alert => "Clear VMs action was successfully queued.",
+ :success => true
+ }
end
def edit_network
+ svc_modify(params[:id])
render :layout => 'popup'
end
def bondings_json
- bondings = Host.find(params[:id]).bondings
+ svc_show(params[:id])
+ bondings = @host.bondings
render :json => bondings.collect{ |x| {:id => x.id, :name =>
x.name} }
end
@@ -180,12 +159,6 @@ class HostController < ApplicationController
flash[:notice] = 'Hosts may not be edited via the web UI'
redirect_to :action=> 'show', :id => @host
end
- def pre_action
- @host = Host.find(params[:id])
- set_perms(@host.hardware_pool)
- @json_hash = { :object => :host }
- authorize_admin
- end
def pre_show
@host = Host.find(params[:id])
set_perms(@host.hardware_pool)
diff --git a/src/app/services/host_service.rb b/src/app/services/host_service.rb
new file mode 100644
index 0000000..074d830
--- /dev/null
+++ b/src/app/services/host_service.rb
@@ -0,0 +1,119 @@
+#--
+# Copyright (C) 2009 Red Hat, Inc.
+# Written by David Lutterkort <lutter at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA. A copy of the GNU General Public License is
+# also available at http://www.gnu.org/copyleft/gpl.html.
+#++
+# Business logic around hosts aka nodes
+module HostService
+
+ include ApplicationService
+
+ # Host attributes on which we filter with '='
+ EQ_ATTRIBUTES = [ :state, :arch, :hostname, :uuid,
+ :hardware_pool_id ]
+
+ # List hosts matching criteria described by +params+; only the entries
+ # for keys in +EQ_ATTRIBUTES+ are used
+ #
+ # === Instance variables
+ # [<tt>@hosts</tt>] stores list of hosts matching criteria
+ # === Required permissions
+ # [<tt>Privilege::VIEW</tt>] no exception raised,
<tt>@hosts</tt>
+ # is filtered by privilege
+ def svc_list(params)
+ conditions = []
+ EQ_ATTRIBUTES.each do |attr|
+ if params[attr]
+ conditions << "hosts.#{attr} = :#{attr}"
+ end
+ end
+ # Add permission check
+ params = params.dup
+ params[:user] = get_login_user
+ params[:priv] = Privilege::VIEW
+ conditions << "privileges.name=:priv"
+ conditions << "permissions.uid=:user"
+ incl = [{ :hardware_pool => { :permissions => { :role =>
:privileges}}}]
+ @hosts = Host.find(:all,
+ :include => incl,
+ :conditions => [conditions.join(" and "),
params],
+ :order => "hosts.id")
+ end
+
+ # Load the Host with +id+ for viewing
+ #
+ # === Instance variables
+ # [<tt>@host</tt>] stores the Host with +id+
+ # === Required permissions
+ # [<tt>Privilege::VIEW</tt>] on host's HardwarePool
+ def svc_show(id)
+ lookup(id, Privilege::VIEW)
+ end
+
+ # Load the Host with +id+ for editing
+ #
+ # === Instance variables
+ # [<tt>@host</tt>] stores the Host with +id+
+ # === Required permissions
+ # [<tt>Privilege::MODIFY</tt>] on host's HardwarePool
+ def svc_modify(id)
+ lookup(id, Privilege::MODIFY)
+ end
+
+ # Set the disabled state of the Host with +id+ to
<tt>:enabled</tt>
+ # or <tt>:disabled</tt>
+ #
+ # === Instance variables
+ # [<tt>@host</tt>] stores the Host with +id+
+ # === Required permissions
+ # [<tt>Privilege::MODIFY</tt>] on host's HardwarePool
+ def svc_enable(id, state)
+ ind = ["enabled", "disabled"].index(state.to_s)
+ if ind.nil?
+ raise ArgumentError, "STATE must be 'enabled' or
'disabled'"
+ end
+ svc_modify(id)
+ @host.is_disabled = ind
+ @host.save!
+ end
+
+ # Queue task to migrate all VM's off the Host with +id+, and mark the
+ # host as disabled
+ #
+ # === Instance variables
+ # [<tt>@host</tt>] stores the Host with +id+
+ # === Required permissions
+ # [<tt>Privilege::MODIFY</tt>] on host's HardwarePool
+ def svc_clear_vms(id)
+ svc_modify(id)
+ Host.transaction do
+ task = HostTask.new({ :user => get_login_user,
+ :task_target => @host,
+ :action => HostTask::ACTION_CLEAR_VMS,
+ :state => Task::STATE_QUEUED})
+ task.save!
+ @host.is_disabled = true
+ @host.save!
+ end
+ end
+
+ private
+ def lookup(id, priv)
+ @host = Host.find(id)
+ authorized!(priv, @host.hardware_pool)
+ end
+end
--
1.6.0.6