Skip to content

Commit

Permalink
Created 'acl' brick to support Access Control Lists (wrote tests, tra…
Browse files Browse the repository at this point in the history
…nslations).
  • Loading branch information
gaspard committed Jun 9, 2011
1 parent 062961e commit 2270846
Show file tree
Hide file tree
Showing 73 changed files with 1,740 additions and 1,072 deletions.
1 change: 1 addition & 0 deletions History.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* [link] and other url related zafu tags ([form]) *do not typecast to String* by default:
you need to use <r:link foo='%{baz}'/> for literal values.
* Created "uv" brick to support ultraviolet syntax highlighting.
* Created 'acl' brick to support Access Control Lists.

* Minor changes
* Fixed login redirect to not leave ssh.
Expand Down
19 changes: 13 additions & 6 deletions app/controllers/acls_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ def index

def show
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => @acl }
format.html { render :file => admin_layout, :layout => false }
format.js
end
end

Expand All @@ -30,12 +30,11 @@ def edit
end

def create
@acl = secure(Acl) {Acl.create(params[:acl])}
puts @acl.inspect
@acl = secure(Acl) {Acl.create(acl_attributes)}
end

def update
@acl.update_attributes(params[:acl])
@acl.update_attributes(acl_attributes)

respond_to do |format|
format.html do
Expand All @@ -45,7 +44,7 @@ def update
render :action => 'edit'
end
end
format.js { render :action => 'show' }
format.js
end
end

Expand Down Expand Up @@ -73,4 +72,12 @@ def find_acl
@acl = secure!(Acl) { Acl.find(params[:id]) }
end
end

def acl_attributes
return {} unless acl_params = params[:acl]
if skin_zip = acl_params[:exec_skin_id]
acl_params[:exec_skin_id] = skin_zip.blank? ? '' : Node.translate_pseudo_id(skin_zip)
end
acl_params
end
end
36 changes: 22 additions & 14 deletions app/controllers/nodes_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ def index
format.html { render_and_cache :mode => '+index' }
format.xml { render :xml => @node.to_xml }
end
elsif visitor.use_acls?
node = visitor.node_without_secure
if visitor.acl_authorized?(:read, {:id => node.zip})
elsif base_node = visitor.node_without_secure
if node = visitor.find_node(nil, base_node.zip, nil, {}, :get)
# If the visitor is acl authorized to view his own node,
# redirect there.
redirect_to zen_path(node)
else
raise ActiveRecord::RecordNotFound
Expand Down Expand Up @@ -218,7 +219,15 @@ def create
attrs['klass'] = 'Document'
end

@node = secure!(Node) { Node.create_node(attrs) }
begin
# Make sure we can load parent (also enables ACL to work for us here).
parent = visitor.find_node(nil, attrs.delete(:parent_id), nil, {}, :post)
@node = parent.new_child(attrs)
@node.save
rescue ActiveRecord::RecordNotFound
# Let normal processing insert errors
@node = secure!(Node) { Node.create_node(attrs) }
end
@node.errors.add('file', file_error) if file_error

respond_to do |format|
Expand All @@ -232,7 +241,11 @@ def create
else
format.html do
flash[:error] = error_messages_for('node', :object => @node)
redirect_to request.referer
if request.referer
redirect_to request.referer
else
raise ActiveRecord::RecordNotFound
end
end
format.js
format.xml { render :xml => @node.errors, :status => :unprocessable_entity }
Expand Down Expand Up @@ -541,21 +554,16 @@ def find_node
set_format(asset_and_format)
end

if name =~ /^\d+$/
@node = secure!(Node) { Node.find_by_zip(name) }
elsif name
basepath = (path[0..-2] + [name]).map {|p| String.from_url_name(p) }.join('/')
@node = secure!(Node) { Node.find_by_path(basepath) || Node.find_by_path(basepath, current_site.root_id, true) }
else
@node = secure!(Node) { Node.find_by_zip(zip) }
end
# We use the visitor to find the node in order to ease implementation
# of custom access rules (Acl).
@node = visitor.find_node(path, zip, name, params, request.method)
else
# bad url
Node.logger.warn "Path #{path.last.inspect} does not match #{Zena::Use::Urls::ALLOWED_REGEXP}"
raise ActiveRecord::RecordNotFound
end
elsif params[:id]
@node = secure!(Node) { Node.find_by_zip(params[:id]) }
@node = visitor.find_node(nil, params[:id], nil, params, request.method)
end

if params[:link_id]
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class UsersController < ApplicationController

def show
respond_to do |format|
format.html { render :file => admin_layout, :layout => false } # render, content_for_layout = nil
format.html { render :file => admin_layout, :layout => false }
format.js
end
end
Expand Down
3 changes: 3 additions & 0 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
module ApplicationHelper
def help(key)
%Q{<div class='help'><p>#{_(key)}</p></div>}
end
end

Bricks.apply_patches
37 changes: 27 additions & 10 deletions app/models/acl.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
class Acl < ActiveRecord::Base
ACTIONS = %w{create read update delete}
# Used during script compilation
attr_reader :node, :params
# List of access actions, ordered for their use in the form.
ACTIONS = %w{read update create delete}
ACTION_FROM_METHOD = Hash[:get,'read',:put,'update',:post,'create',:delete,'delete']

before_save :set_visitor_id, :set_site_id
belongs_to :exec_group, :class_name => 'Group', :foreign_key => 'exec_group_id'
belongs_to :group
validate :validate_acl
Expand All @@ -13,44 +18,56 @@ class Acl < ActiveRecord::Base
def safe_method_type(signature, receiver = nil)
if type = super
type
elsif type = base_node.safe_method_type(signature)
elsif type = node.safe_method_type(signature)
type.merge(:method => "@node.#{type[:method]}")
else
nil
end
end

def self.new(attrs = {})
super({}.merge(attrs))
end

def authorize?(base_node, params)
Node.do_find(:first, eval(make_query.to_s))
Node.do_find(:first, eval(make_query(base_node, params).to_s))
end

def exec_skin_zip
exec_skin ? exec_skin.zip : nil
end

def exec_skin
secure(Skin) { Skin.find(exec_skin_id) }
@exec_skin ||= secure(Skin) { Skin.find(exec_skin_id) }
end

protected
def set_visitor_id
self.user_id = visitor.id
end

def set_site_id
self.site_id = current_site.id
end

def validate_acl
make_query(visitor.prototype, {})
end

def make_query(node, params)
@node = node
@params = params
Node.build_query(:first, query,
# We add a stupid order clause to avoid the 'order by title' thing.
query = Node.build_query(:first, self.query + ' order by id asc',
:node_name => '@node',
:main_class => @node.virtual_class,
:rubyless_helper => self
)
# Find only the current node amongst all the allowed nodes.
query.add_filter("#{query.table}.zip = [[params[:id]]]")
query
rescue ::QueryBuilder::Error => err
errors.add(:query, err.message)
if err.message =~ /\AException raised while processing '.*?' \((.+)\)\Z/m
errors.add(:query, $1)
else
errors.add(:query, err.message)
end
nil
rescue ::RubyLess::Error => err
errors.add(:query, err.message)
Expand Down
14 changes: 14 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,20 @@ def get_skin(node)
end
end

def find_node(path, zip, name, params, method)
secure!(Node) do
if name =~ /^\d+$/
Node.find_by_zip(name)
elsif name
basepath = (path[0..-2] + [name]).map {|p| String.from_url_name(p) }.join('/')
Node.find_by_path(basepath) ||
Node.find_by_path(basepath, current_site.root_id, true)
else
Node.find_by_zip(zip)
end
end
end

private

def user_site
Expand Down
2 changes: 1 addition & 1 deletion app/views/acls/_add.rhtml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<tr id='add_acl' class='btn_add'>
<td class='add'><%= link_to_function _('btn_acl_add'), "['add_acl', 'add_acl_form'].each(Element.toggle); " %></td><td colspan='7' class='add_user'></td>
<td class='add'><%= link_to_function _('btn_acl_add'), "['add_acl', 'add_acl_form'].each(Element.toggle); " %></td><td colspan='6' class='add_user'></td>
</tr>
<tr id='add_acl_form' style='display:none;'>
<%= render :partial=>'acls/form' %>
Expand Down
28 changes: 14 additions & 14 deletions app/views/acls/_form.rhtml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
link_to_function _('btn_x'), "['add_acl', 'add_acl_form'].each(Element.toggle);"
end %>
</td>
<td class='add' colspan='7'>
<td class='add' colspan='6'>
<div class='errors'><%= error_messages_for(:acl, :object => @acl) %></div>
<% if @acl[:id] %>
<%= form_remote_tag(:url=> acl_path(@acl)) %>
Expand All @@ -15,8 +15,16 @@
<%= form_remote_tag(:url=> acls_path) %>
<% end %>
<table cellspacing='0'>
<tr>
<td class='label'><%= _('group')%> <%= help('acl_group_help') %></td>
<td><%= select('acl', 'group_id', visitor.all_groups.map{|g| [g.name, g.id]}) %></td>
</tr>
<tr>
<td class='label'><%= _('action')%> <%= help('acl_action_help') %></td>
<td><%= select('acl', 'action', Acl::ACTIONS ) %></td>
</tr>
<tr class='priority'>
<td class='label'><%= _('priority')%></td>
<td class='label'><%= _('priority')%> <%= help('acl_priority_help') %></td>
<td><%= text_field('acl', 'priority', :size => 4 ) %></td>
</tr>
<tr>
Expand All @@ -28,23 +36,15 @@
<td><%= text_area('acl', 'description', :rows => 2, :cols => 40) %></td>
</tr>
<tr>
<td class='label'><%= _('query')%></td>
<td class='label'><%= _('query')%> <%= help('acl_query_help') %></td>
<td><%= text_area('acl', 'query', :rows => 4, :cols => 40) %></td>
</tr>
<tr>
<td class='label'><%= _('action')%></td>
<td><%= select('acl', 'action', Acl::ACTIONS ) %></td>
</tr>
<tr>
<td class='label'><%= _('group')%></td>
<td><%= select('acl', 'group_id', visitor.all_groups.map{|g| [g.name, g.id]}) %></td>
</tr>
<tr class='exec'>
<td class='label'><%= _('exec group')%></td>
<td class='label'><%= _('exec group')%> <%= help('acl_exec_group_help') %></td>
<td><%= select('acl', 'exec_group_id', visitor.all_groups.map{|g| [g.name, g.id]}, :include_blank => true) %></td>
</tr>
<tr class='exec'>
<td class='label'><%= _('exec skin')%></td>
<td class='label'><%= _('exec skin')%> <%= help('acl_exec_skin_help') %></td>
<td><%= select('acl', 'exec_skin_id', form_skins, {:include_blank => true, :selected => @acl.exec_skin_zip}) %></td>
</tr>
<tr>
Expand All @@ -57,7 +57,7 @@
<% if !@acl.new_record? -%>
<%= form_remote_tag(:url => acl_path(@acl), :confirm => _('Are you sure you want to destroy this access ?') ) %>
<input type='hidden' name='_method' value ='delete'/>
<p class='destroy'><%= _('destroy acl') %> <input type='submit' value='<%= _('destroy') %>'></p>
<p class='destroy'><%= _('destroy acl') %> <input type='submit' value='<%= _('destroy') %>'></p></div>
</form>
<% end -%>
</td>
10 changes: 4 additions & 6 deletions app/views/acls/_li.rhtml
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
<tr id='acl<%= li[:id] %>'>
<td class="adm_icon"><%= link_to_remote( _("img_acl"), :update=>"acl#{li[:id]}", :url=>edit_acl_path(li), :method=>:get) %></td>
<td class='group'><%= li.group.name %></td>
<td class='act'><%= li.action %></td>
<td class='name'><%= li.name %></td>
<td class='query'><%= li.query %></td>
<td class='description'><%= zazen(li.description) %></td>
<td class='action'><%= li.action %></td>
<td class='group'><%= li.group.name %></td>
<td class='exec group'><%= li.exec_group.name %></td>
<td class='exec group'><%= li.exec_group_id ? li.exec_group.name : '' %></td>
<td class='exec skin'><%= li.exec_skin ? li.exec_skin.title : '' %></td>
<td class='priority'><%= li.priority %></td>
</tr>
</tr>
2 changes: 1 addition & 1 deletion app/views/acls/create.rjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ if @acl.errors.empty?
page.insert_html :before, 'add_acl', :partial=>'acls/li', :collection=>[@acl]
@acl = Acl.new
page.replace_html 'add_acl_form', :partial=>'acls/form'
page.javascript "$('acl_name').focus();"
page << "['add_acl_form', 'add_acl'].each(Element.toggle);"
else
page.replace_html 'add_acl_form', :partial=>'acls/form'
end
6 changes: 6 additions & 0 deletions app/views/acls/destroy.rjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
if @acl.errors.empty?
page.visual_effect :highlight, "acl#{@acl[:id]}", :duration => 0.3
page.visual_effect :fade, "acl#{@acl[:id]}", :duration => 0.5
else
page.replace_html "acl#{@acl[:id]}", :partial=>"acls/form"
end
9 changes: 4 additions & 5 deletions app/views/acls/index.rhtml
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
<h2 class='title'><%= _('access controls') %></h2>
<table class="admin" cellspacing="0">
<tr><th class='nav' colspan='8'><%= will_paginate @acls %></th></tr>
<tr><th class='nav' colspan='7'><%= will_paginate @acls %></th></tr>
<tr>
<th>&nbsp;</th>
<th><%= _('group') %></th>
<th><%= _('action') %></th>
<th><%= _('name') %></th>
<th><%= _('description') %></th>
<th><%= _('query') %></th>
<th><%= _('action') %></th>
<th><%= _('group') %></th>
<th><%= _('exec group') %></th>
<th><%= _('exec skin') %></th>
<th><%= _('priority') %></th>
</tr>
<%= render :partial=>'acls/li', :collection => @acls %>
<%= render :partial=>'acls/add' %>
Expand Down
1 change: 1 addition & 0 deletions app/views/acls/show.rjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
page.replace "acl#{@acl[:id]}", :partial=>'acls/li', :collection=>[@acl]
7 changes: 7 additions & 0 deletions app/views/acls/update.rjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
if @acl.errors.empty?
# ok
page.replace "acl#{@acl[:id]}", :partial=>"acls/li", :collection=>[@acl]
else
# render form
page.replace_html "acl#{@acl[:id]}", :partial=>"acls/form"
end
2 changes: 1 addition & 1 deletion app/views/columns/destroy.rjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ if @column.errors.empty?
page.visual_effect :highlight, "column#{@column[:id]}", :duration => 0.3
page.visual_effect :fade, "column#{@column[:id]}", :duration => 0.5
else
page.replace "column#{@column[:id]}", :partial=>"columns/form"
page.replace_html "column#{@column[:id]}", :partial=>"columns/form"
end
4 changes: 2 additions & 2 deletions app/views/columns/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
<tr><th class='nav' colspan='3'><%= will_paginate @columns %></th></tr>
<% role_id = nil; @columns.each do |col| -%>
<% if col.role_id != role_id; role_id = col.role_id -%>
<tr>
<th class='nav sub <%= col.role.class %>' colspan='3'>
<tr class='<%= col.role.class.to_s.underscore %>'>
<th class='nav sub constant' colspan='3'>
<span class='kpath'><%= col.role.kpath %></span> <%= col.role.name %>
</th>
</tr>
Expand Down

0 comments on commit 2270846

Please sign in to comment.