Compare commits

...
26 Commits
Author SHA1 Message Date
Liwiusz Ociepa 131b15fc7a Fix access to Repository Parent Path (FORBIDEN instead of AUTH_REQUIRED).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/swistak@2058 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-27 14:51:14 +00:00
Liwiusz Ociepa f6b1583a1a If project_id is uninitialized, initialize it.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/swistak@2050 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-20 13:46:06 +00:00
Liwiusz Ociepa 5bea900443 Some debug added.
There is a bug - don't know if in Redmine.pm or in Netbeans svn client.
In logs:
Use of uninitialized value $project_id in concatenation (.) or string at 
/usr/lib/perl5/Apache/Authn/Redmine.pm line 306, ....


git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/branches/swistak@2049 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-11-20 11:21:14 +00:00
Liwiusz Ociepa 81baddad00 - Disable debug,
- Fix namespace set condition (setting namespace, without 
  configured memcached, should segfault apache before).


git-svn-id: http://redmine.rubyforge.org/svn/branches/swistak@1646 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-07-08 15:06:41 +00:00
Liwiusz Ociepa f6b5632fec Remove default Memcached namespace.
git-svn-id: http://redmine.rubyforge.org/svn/branches/swistak@1645 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-07-07 21:34:43 +00:00
Liwiusz Ociepa f7ede727fd Initial support for caching credentials in Memcached (debug is enabled).
Support for caching in Apache memory pools has been dropped.


git-svn-id: http://redmine.rubyforge.org/svn/branches/swistak@1644 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-07-07 17:26:39 +00:00
Liwiusz Ociepa b870417860 Merge changes from trunk.
git-svn-id: http://redmine.rubyforge.org/svn/branches/swistak@1618 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-07-04 13:25:32 +00:00
Liwiusz Ociepa 25a7838e0a Memory leak (postgres -> zlib + ssl) has been fixed by apache developers.
git-svn-id: http://redmine.rubyforge.org/svn/branches/swistak@1438 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-05-19 12:41:40 +00:00
Liwiusz Ociepa 7c3ddf2bc0 Simple change to allow multiple RedmineDbWhereClause and added errmsg for RedmineCacheCredsMax
git-svn-id: http://redmine.rubyforge.org/svn/branches/swistak@1424 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-05-13 09:06:11 +00:00
Liwiusz Ociepa 0f8df81b87 Do not crash on reload.
git-svn-id: http://redmine.rubyforge.org/svn/branches/swistak@1423 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-05-08 22:06:44 +00:00
Liwiusz Ociepa c5121d804d Fix when you don't define RedmineDbWhereClause
git-svn-id: http://redmine.rubyforge.org/svn/branches/swistak@1422 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-05-08 17:10:25 +00:00
Liwiusz Ociepa 8bcbbebf65 Small fix for not read-only methods.
git-svn-id: http://redmine.rubyforge.org/svn/branches/swistak@1421 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-05-07 09:22:50 +00:00
Liwiusz Ociepa 2d2afab549 Fix comment about configuration.
git-svn-id: http://redmine.rubyforge.org/svn/branches/swistak@1420 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-05-06 19:51:34 +00:00
Liwiusz Ociepa 35f601e769 Cache Credentials added (login:proj -> "sha1_hex(pass))
git-svn-id: http://redmine.rubyforge.org/svn/branches/swistak@1419 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-05-06 15:14:58 +00:00
Liwiusz Ociepa b84cffd62e Strip extra whitespaces from query in apache config object.
git-svn-id: http://redmine.rubyforge.org/svn/branches/swistak@1418 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-05-05 15:00:50 +00:00
Liwiusz Ociepa 2eba0b66d4 Comment to memory leak with Postgres and ssl.
git-svn-id: http://redmine.rubyforge.org/svn/branches/swistak@1417 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-05-05 13:35:56 +00:00
Liwiusz Ociepa eb2d814344 http://perl.apache.org/docs/2.0/user/config/custom.html#Description
Rewrite module not to use PerlSetVar


git-svn-id: http://redmine.rubyforge.org/svn/branches/swistak@1416 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-05-05 13:18:41 +00:00
Liwiusz Ociepa d9385ce9a2 Add support for db_where_clause - that variable is appended to password query.
git-svn-id: http://redmine.rubyforge.org/svn/branches/swistak@1378 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-04-28 17:49:49 +00:00
Liwiusz Ociepa d3522c10e4 Sync Redmine.pm with trunk.
git-svn-id: http://redmine.rubyforge.org/svn/branches/swistak@1377 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-04-28 17:44:00 +00:00
Jean-Philippe Lang b9957264eb swistak branch added.
git-svn-id: http://redmine.rubyforge.org/svn/branches/swistak@1313 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-03-31 17:01:50 +00:00
Jean-Philippe Lang da641f4122 Global queries can be saved from the global issue list (follows r1311 and closes #897).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1312 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-03-30 14:20:07 +00:00
Jean-Philippe Lang 287d86e363 Queries can be marked as 'For all projects'. Such queries will be available on all projects and on the global issue list (#897, closes #671).
Only admin users can create/edit queries that are public and for all projects.
Note: this change does not allow to save a query from the global issue list. You have to be inside a project but then you can mark the query as 'For all projects'.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@1311 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-03-30 12:29:07 +00:00
Jean-Philippe Lang faf1f1e812 Fixed: Feed content limit setting has no effect (closes #954).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1310 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-03-30 08:33:04 +00:00
Jean-Philippe Lang cd64338a7f Fixed: Priorities not ordered when displayed as a filter in issue list (#956).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1309 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-03-30 08:19:56 +00:00
Jean-Philippe Lang 1b8c5d4058 Fixed: can not display attached images inline in message replies.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1308 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-03-29 22:35:04 +00:00
Jean-Philippe Lang 5ccbeba5c2 Use #blank? instead of #empty? in news/show view.
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1307 e93f8b46-1217-0410-a6f0-8f06a7374b81
2008-03-29 20:03:20 +00:00
16 changed files with 567 additions and 77 deletions
+1
View File
@@ -150,6 +150,7 @@ class ApplicationController < ActionController::Base
def render_feed(items, options={}) def render_feed(items, options={})
@items = items || [] @items = items || []
@items.sort! {|x,y| y.event_datetime <=> x.event_datetime } @items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
@items = @items.slice(0, Setting.feeds_limit.to_i)
@title = options[:title] || Setting.app_title @title = options[:title] || Setting.app_title
render :template => "common/feed.atom.rxml", :layout => false, :content_type => 'application/atom+xml' render :template => "common/feed.atom.rxml", :layout => false, :content_type => 'application/atom+xml'
end end
+8 -1
View File
@@ -73,6 +73,8 @@ class IssuesController < ApplicationController
# Send html if the query is not valid # Send html if the query is not valid
render(:template => 'issues/index.rhtml', :layout => !request.xhr?) render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
end end
rescue ActiveRecord::RecordNotFound
render_404
end end
def changes def changes
@@ -87,6 +89,8 @@ class IssuesController < ApplicationController
end end
@title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name) @title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
render :layout => false, :content_type => 'application/atom+xml' render :layout => false, :content_type => 'application/atom+xml'
rescue ActiveRecord::RecordNotFound
render_404
end end
def show def show
@@ -384,7 +388,10 @@ private
# Retrieve query from session or build a new query # Retrieve query from session or build a new query
def retrieve_query def retrieve_query
if !params[:query_id].blank? if !params[:query_id].blank?
@query = Query.find(params[:query_id], :conditions => {:project_id => (@project ? @project.id : nil)}) cond = "project_id IS NULL"
cond << " OR project_id = #{@project.id}" if @project
@query = Query.find(params[:query_id], :conditions => cond)
@query.project = @project
session[:query] = {:id => @query.id, :project_id => @query.project_id} session[:query] = {:id => @query.id, :project_id => @query.project_id}
else else
if params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil) if params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
+18 -19
View File
@@ -18,19 +18,14 @@
class QueriesController < ApplicationController class QueriesController < ApplicationController
layout 'base' layout 'base'
menu_item :issues menu_item :issues
before_filter :find_project, :authorize before_filter :find_query, :except => :new
before_filter :find_optional_project, :only => :new
def index
@queries = @project.queries.find(:all,
:order => "name ASC",
:conditions => ["is_public = ? or user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
end
def new def new
@query = Query.new(params[:query]) @query = Query.new(params[:query])
@query.project = @project @query.project = params[:query_is_for_all] ? nil : @project
@query.user = User.current @query.user = User.current
@query.is_public = false unless current_role.allowed_to?(:manage_public_queries) @query.is_public = false unless (@query.project && current_role.allowed_to?(:manage_public_queries)) || User.current.admin?
@query.column_names = nil if params[:default_columns] @query.column_names = nil if params[:default_columns]
params[:fields].each do |field| params[:fields].each do |field|
@@ -52,7 +47,8 @@ class QueriesController < ApplicationController
@query.add_filter(field, params[:operators][field], params[:values][field]) @query.add_filter(field, params[:operators][field], params[:values][field])
end if params[:fields] end if params[:fields]
@query.attributes = params[:query] @query.attributes = params[:query]
@query.is_public = false unless current_role.allowed_to?(:manage_public_queries) @query.project = nil if params[:query_is_for_all]
@query.is_public = false unless (@query.project && current_role.allowed_to?(:manage_public_queries)) || User.current.admin?
@query.column_names = nil if params[:default_columns] @query.column_names = nil if params[:default_columns]
if @query.save if @query.save
@@ -64,18 +60,21 @@ class QueriesController < ApplicationController
def destroy def destroy
@query.destroy if request.post? @query.destroy if request.post?
redirect_to :controller => 'queries', :project_id => @project redirect_to :controller => 'issues', :action => 'index', :project_id => @project, :set_filter => 1
end end
private private
def find_project def find_query
if params[:id] @query = Query.find(params[:id])
@query = Query.find(params[:id]) @project = @query.project
@project = @query.project render_403 unless @query.editable_by?(User.current)
render_403 unless @query.editable_by?(User.current) rescue ActiveRecord::RecordNotFound
else render_404
@project = Project.find(params[:project_id]) end
end
def find_optional_project
@project = Project.find(params[:project_id]) if params[:project_id]
User.current.allowed_to?(:save_queries, @project, :global => true)
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
render_404 render_404
end end
+13
View File
@@ -33,6 +33,19 @@ module IssuesHelper
"<strong>#{@cached_label_priority}</strong>: #{issue.priority.name}" "<strong>#{@cached_label_priority}</strong>: #{issue.priority.name}"
end end
def sidebar_queries
unless @sidebar_queries
# User can see public queries and his own queries
visible = ARCondition.new(["is_public = ? OR user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
# Project specific queries and global queries
visible << (@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id])
@sidebar_queries = Query.find(:all,
:order => "name ASC",
:conditions => visible.conditions)
end
@sidebar_queries
end
def show_detail(detail, no_html=false) def show_detail(detail, no_html=false)
case detail.property case detail.property
when 'attr' when 'attr'
+10 -3
View File
@@ -116,6 +116,11 @@ class Query < ActiveRecord::Base
set_language_if_valid(User.current.language) set_language_if_valid(User.current.language)
end end
def after_initialize
# Store the fact that project is nil (used in #editable_by?)
@is_for_all = project.nil?
end
def validate def validate
filters.each_key do |field| filters.each_key do |field|
errors.add label_for(field), :activerecord_error_blank unless errors.add label_for(field), :activerecord_error_blank unless
@@ -128,8 +133,10 @@ class Query < ActiveRecord::Base
def editable_by?(user) def editable_by?(user)
return false unless user return false unless user
return true if !is_public && self.user_id == user.id # Admin can edit them all and regular users can edit their private queries
is_public && user.allowed_to?(:manage_public_queries, project) return true if user.admin? || (!is_public && self.user_id == user.id)
# Members can not edit public queries that are for all project (only admin is allowed to)
is_public && !@is_for_all && user.allowed_to?(:manage_public_queries, project)
end end
def available_filters def available_filters
@@ -139,7 +146,7 @@ class Query < ActiveRecord::Base
@available_filters = { "status_id" => { :type => :list_status, :order => 1, :values => IssueStatus.find(:all, :order => 'position').collect{|s| [s.name, s.id.to_s] } }, @available_filters = { "status_id" => { :type => :list_status, :order => 1, :values => IssueStatus.find(:all, :order => 'position').collect{|s| [s.name, s.id.to_s] } },
"tracker_id" => { :type => :list, :order => 2, :values => trackers.collect{|s| [s.name, s.id.to_s] } }, "tracker_id" => { :type => :list, :order => 2, :values => trackers.collect{|s| [s.name, s.id.to_s] } },
"priority_id" => { :type => :list, :order => 3, :values => Enumeration.find(:all, :conditions => ['opt=?','IPRI']).collect{|s| [s.name, s.id.to_s] } }, "priority_id" => { :type => :list, :order => 3, :values => Enumeration.find(:all, :conditions => ['opt=?','IPRI'], :order => 'position').collect{|s| [s.name, s.id.to_s] } },
"subject" => { :type => :text, :order => 8 }, "subject" => { :type => :text, :order => 8 },
"created_on" => { :type => :date_past, :order => 9 }, "created_on" => { :type => :date_past, :order => 9 },
"updated_on" => { :type => :date_past, :order => 10 }, "updated_on" => { :type => :date_past, :order => 10 },
+19 -10
View File
@@ -222,17 +222,26 @@ class User < ActiveRecord::Base
# action can be: # action can be:
# * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit') # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
# * a permission Symbol (eg. :edit_project) # * a permission Symbol (eg. :edit_project)
def allowed_to?(action, project) def allowed_to?(action, project, options={})
# No action allowed on archived projects if project
return false unless project.active? # No action allowed on archived projects
# No action allowed on disabled modules return false unless project.active?
return false unless project.allows_to?(action) # No action allowed on disabled modules
# Admin users are authorized for anything else return false unless project.allows_to?(action)
return true if admin? # Admin users are authorized for anything else
return true if admin?
role = role_for_project(project) role = role_for_project(project)
return false unless role return false unless role
role.allowed_to?(action) && (project.is_public? || role.member?) role.allowed_to?(action) && (project.is_public? || role.member?)
elsif options[:global]
# authorize if user has at least one role that has this permission
roles = memberships.collect {|m| m.role}.uniq
roles.detect {|r| r.allowed_to?(action)}
else
false
end
end end
def self.current=(user) def self.current=(user)
+6 -5
View File
@@ -1,13 +1,14 @@
<h3><%= l(:label_issue_plural) %></h3> <h3><%= l(:label_issue_plural) %></h3>
<%= link_to l(:label_issue_view_all), { :controller => 'issues', :action => 'index', :project_id => @project, :set_filter => 1 } %><br /> <%= link_to l(:label_issue_view_all), { :controller => 'issues', :action => 'index', :project_id => @project, :set_filter => 1 } %><br />
<% if @project %>
<%= link_to l(:field_summary), :controller => 'reports', :action => 'issue_report', :id => @project %><br /> <%= link_to l(:field_summary), :controller => 'reports', :action => 'issue_report', :id => @project %><br />
<%= link_to l(:label_change_log), :controller => 'projects', :action => 'changelog', :id => @project %> <%= link_to l(:label_change_log), :controller => 'projects', :action => 'changelog', :id => @project %>
<% end %>
<% unless sidebar_queries.empty? -%>
<h3><%= l(:label_query_plural) %></h3> <h3><%= l(:label_query_plural) %></h3>
<% queries = @project.queries.find(:all, <% sidebar_queries.each do |query| -%>
:order => "name ASC",
:conditions => ["is_public = ? or user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
queries.each do |query| %>
<%= link_to query.name, :controller => 'issues', :action => 'index', :project_id => @project, :query_id => query %><br /> <%= link_to query.name, :controller => 'issues', :action => 'index', :project_id => @project, :query_id => query %><br />
<% end %> <% end -%>
<% end -%>
+2 -2
View File
@@ -18,7 +18,7 @@
:update => "content", :update => "content",
}, :class => 'icon icon-reload' %> }, :class => 'icon icon-reload' %>
<% if current_role && current_role.allowed_to?(:save_queries) %> <% if User.current.allowed_to?(:save_queries, @project, :global => true) %>
<%= link_to l(:button_save), {}, :onclick => "$('query_form').submit(); return false;", :class => 'icon icon-save' %> <%= link_to l(:button_save), {}, :onclick => "$('query_form').submit(); return false;", :class => 'icon icon-save' %>
<% end %> <% end %>
</p> </p>
@@ -54,7 +54,7 @@
<% content_for :sidebar do %> <% content_for :sidebar do %>
<%= render :partial => 'issues/sidebar' %> <%= render :partial => 'issues/sidebar' %>
<% end if @project%> <% end %>
<% content_for :header_tags do %> <% content_for :header_tags do %>
<%= auto_discovery_link_tag(:atom, {:query_id => @query, :format => 'atom', :page => nil, :key => User.current.rss_key}, :title => l(:label_issue_plural)) %> <%= auto_discovery_link_tag(:atom, {:query_id => @query, :format => 'atom', :page => nil, :key => User.current.rss_key}, :title => l(:label_issue_plural)) %>
+1 -1
View File
@@ -26,7 +26,7 @@
</div> </div>
<div class="message reply"> <div class="message reply">
<h4><%=h message.subject %> - <%= authoring message.created_on, message.author %></h4> <h4><%=h message.subject %> - <%= authoring message.created_on, message.author %></h4>
<div class="wiki"><%= textilizable message.content %></div> <div class="wiki"><%= textilizable message, :content, :attachments => message.attachments %></div>
<%= link_to_attachments message.attachments, :no_author => true %> <%= link_to_attachments message.attachments, :no_author => true %>
</div> </div>
<% end %> <% end %>
+1 -1
View File
@@ -25,7 +25,7 @@
<div id="preview" class="wiki"></div> <div id="preview" class="wiki"></div>
</div> </div>
<p><em><% unless @news.summary.empty? %><%=h @news.summary %><br /><% end %> <p><em><% unless @news.summary.blank? %><%=h @news.summary %><br /><% end %>
<span class="author"><%= authoring @news.created_on, @news.author %></span></em></p> <span class="author"><%= authoring @news.created_on, @news.author %></span></em></p>
<div class="wiki"> <div class="wiki">
<%= textilizable(@news.description) %> <%= textilizable(@news.description) %>
+8 -3
View File
@@ -6,11 +6,16 @@
<p><label for="query_name"><%=l(:field_name)%></label> <p><label for="query_name"><%=l(:field_name)%></label>
<%= text_field 'query', 'name', :size => 80 %></p> <%= text_field 'query', 'name', :size => 80 %></p>
<% if current_role.allowed_to?(:manage_public_queries) %> <% if User.current.admin? || (@project && current_role.allowed_to?(:manage_public_queries)) %>
<p><label for="query_is_public"><%=l(:field_is_public)%></label> <p><label for="query_is_public"><%=l(:field_is_public)%></label>
<%= check_box 'query', 'is_public' %></p> <%= check_box 'query', 'is_public',
:onchange => (User.current.admin? ? nil : 'if (this.checked) {$("query_is_for_all").checked = false; $("query_is_for_all").disabled = true;} else {$("query_is_for_all").disabled = false;}') %></p>
<% end %> <% end %>
<p><label for="query_is_for_all"><%=l(:field_is_for_all)%></label>
<%= check_box_tag 'query_is_for_all', 1, @query.project.nil?,
:disabled => (!@query.new_record? && (@query.project.nil? || (@query.is_public? && !User.current.admin?))) %></p>
<p><label for="query_default_columns"><%=l(:label_default_columns)%></label> <p><label for="query_default_columns"><%=l(:label_default_columns)%></label>
<%= check_box_tag 'default_columns', 1, @query.has_default_columns?, :id => 'query_default_columns', <%= check_box_tag 'default_columns', 1, @query.has_default_columns?, :id => 'query_default_columns',
:onclick => 'if (this.checked) {Element.hide("columns")} else {Element.show("columns")}' %></p> :onclick => 'if (this.checked) {Element.hide("columns")} else {Element.show("columns")}' %></p>
+185 -23
View File
@@ -8,8 +8,8 @@ against redmine database
=head1 SYNOPSIS =head1 SYNOPSIS
This module allow anonymous users to browse public project and This module allow anonymous users to browse public project and
registred users to browse and commit their project. authentication is registred users to browse and commit their project. Authentication is
done on the redmine database. done against the redmine database or the LDAP configured in redmine.
This method is far simpler than the one with pam_* and works with all This method is far simpler than the one with pam_* and works with all
database without an hassle but you need to have apache/mod_perl on the database without an hassle but you need to have apache/mod_perl on the
@@ -29,12 +29,16 @@ On debian/ubuntu you must do :
aptitude install libapache-dbi-perl libapache2-mod-perl2 libdbd-mysql-perl aptitude install libapache-dbi-perl libapache2-mod-perl2 libdbd-mysql-perl
If your Redmine users use LDAP authentication, you will also need
Authen::Simple::LDAP (and IO::Socket::SSL if LDAPS is used):
aptitude install libauthen-simple-ldap-perl libio-socket-ssl-perl
=head1 CONFIGURATION =head1 CONFIGURATION
## if the module isn't in your perl path ## This module has to be in your perl path
PerlRequire /usr/local/apache/Redmine.pm ## eg: /usr/lib/perl5/Apache/Authn/Redmine.pm
## else PerlLoadModule Apache::Authn::Redmine
# PerlModule Apache::Authn::Redmine
<Location /svn> <Location /svn>
DAV svn DAV svn
SVNParentPath "/var/svn" SVNParentPath "/var/svn"
@@ -47,12 +51,19 @@ On debian/ubuntu you must do :
PerlAuthenHandler Apache::Authn::Redmine::authen_handler PerlAuthenHandler Apache::Authn::Redmine::authen_handler
## for mysql ## for mysql
PerlSetVar dsn DBI:mysql:database=databasename;host=my.db.server RedmineDSN "DBI:mysql:database=databasename;host=my.db.server"
## for postgres ## for postgres
# PerlSetVar dsn DBI:Pg:dbname=databasename;host=my.db.server # RedmineDSN "DBI:Pg:dbname=databasename;host=my.db.server"
PerlSetVar db_user redmine RedmineDbUser "redmine"
PerlSetVar db_pass password RedmineDbPass "password"
## Optional where clause (fulltext search would be slow and
## database dependant).
# RedmineDbWhereClause "and members.role_id IN (1,2)"
## Configuration for memcached
# RedmineMemcacheServers "127.0.0.1:112211"
# RedmineMemcacheExpirySec "60"
# RedmineMemcacheNamespace "RedmineCreds:"
</Location> </Location>
To be able to browse repository inside redmine, you must add something To be able to browse repository inside redmine, you must add something
@@ -87,18 +98,119 @@ And you need to upgrade at least reposman.rb (after r860).
=cut =cut
use strict; use strict;
use warnings FATAL => 'all', NONFATAL => 'redefine';
use DBI; use DBI;
use Digest::SHA1; use Digest::SHA1;
# optional module for LDAP authentication
my $CanUseLDAPAuth = eval("use Authen::Simple::LDAP; 1");
my $CanUseMemcached = eval("use Cache::Memcached; 1");
use Apache2::Module; use Apache2::Module;
use Apache2::Access; use Apache2::Access;
use Apache2::ServerRec qw(); use Apache2::ServerRec qw();
use Apache2::RequestRec qw(); use Apache2::RequestRec qw();
use Apache2::RequestUtil qw(); use Apache2::RequestUtil qw();
use Apache2::Const qw(:common); use Apache2::Const qw(:common :override :cmd_how);
# use Apache2::Directive qw(); # use Apache2::Directive qw();
my @directives = (
{
name => 'RedmineDSN',
req_override => OR_AUTHCFG,
args_how => TAKE1,
errmsg => 'Dsn in format used by Perl DBI. eg: "DBI:Pg:dbname=databasename;host=my.db.server"',
},
{
name => 'RedmineDbUser',
req_override => OR_AUTHCFG,
args_how => TAKE1,
},
{
name => 'RedmineDbPass',
req_override => OR_AUTHCFG,
args_how => TAKE1,
},
{
name => 'RedmineDbWhereClause',
req_override => OR_AUTHCFG,
args_how => TAKE1,
},
{
name => 'RedmineMemcacheServers',
req_override => OR_AUTHCFG,
args_how => TAKE1,
},
{
name => 'RedmineMemcacheExpirySec',
req_override => OR_AUTHCFG,
args_how => TAKE1,
},
{
name => 'RedmineMemcacheNamespace',
req_override => OR_AUTHCFG,
args_how => TAKE1,
},
);
sub RedmineDSN {
my ($self, $parms, $arg) = @_;
$self->{RedmineDSN} = $arg;
my $query = "SELECT
hashed_password, auth_source_id
FROM members, projects, users
WHERE
projects.id=members.project_id
AND users.id=members.user_id
AND users.status=1
AND login=?
AND identifier=? ";
$self->{RedmineQuery} = trim($query);
}
sub RedmineDbUser { set_val('RedmineDbUser', @_); }
sub RedmineDbPass { set_val('RedmineDbPass', @_); }
sub RedmineDbWhereClause {
my ($self, $parms, $arg) = @_;
$self->{RedmineQuery} = trim($self->{RedmineQuery}.($arg ? $arg : "")." ");
}
sub RedmineMemcacheServers {
my ($self, $parms, $arg) = @_;
if ($arg && $CanUseMemcached) {
$self->{RedmineMemcached} = new Cache::Memcached {
'servers' => [ $arg, ],
'debug' => 0,
};
$self->{RedmineMemcache} = 1;
}
}
sub RedmineMemcacheExpirySec { set_val('RedmineMemcacheExpirySec', @_); }
sub RedmineMemcacheNamespace {
my ($self, $parms, $arg) = @_;
if ($self->{RedmineMemcache}) {
# Undocumented feature of Cache::Memcached, please don't kill me
$self->{RedmineMemcached}->{namespace} = $arg;
$self->{RedmineMemcached}->{namespace_len} = length $self->{RedmineMemcached}->{namespace};
}
}
sub trim {
my $string = shift;
$string =~ s/\s{2,}/ /g;
return $string;
}
sub set_val {
my ($key, $self, $parms, $arg) = @_;
$self->{$key} = $arg;
}
Apache2::Module::add(__PACKAGE__, \@directives);
my %read_only_methods = map { $_ => 1 } qw/GET PROPFIND REPORT OPTIONS/; my %read_only_methods = map { $_ => 1 } qw/GET PROPFIND REPORT OPTIONS/;
sub access_handler { sub access_handler {
@@ -110,7 +222,7 @@ sub access_handler {
} }
my $method = $r->method; my $method = $r->method;
return OK unless 1 == $read_only_methods{$method}; return OK if defined $read_only_methods{$method};
my $project_id = get_project_identifier($r); my $project_id = get_project_identifier($r);
@@ -126,7 +238,11 @@ sub authen_handler {
my ($res, $redmine_pass) = $r->get_basic_auth_pw(); my ($res, $redmine_pass) = $r->get_basic_auth_pw();
return $res unless $res == OK; return $res unless $res == OK;
if (is_member($r->user, $redmine_pass, $r)) { my $project_id = get_project_identifier($r);
if (!$project_id) {
return FORBIDDEN;
}
if (is_member($r->user, $redmine_pass, $r, $project_id)) {
return OK; return OK;
} else { } else {
$r->note_auth_failure(); $r->note_auth_failure();
@@ -138,14 +254,27 @@ sub is_public_project {
my $project_id = shift; my $project_id = shift;
my $r = shift; my $r = shift;
my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
if ($cfg->{RedmineMemcache}) {
return 1 if ($cfg->{RedmineMemcached}->get($project_id));
}
my $dbh = connect_database($r); my $dbh = connect_database($r);
my $sth = $dbh->prepare( my $sth = $dbh->prepare(
"SELECT * FROM projects WHERE projects.identifier=? and projects.is_public=true;" "SELECT * FROM projects WHERE projects.identifier=? and projects.is_public=true;"
); );
$sth->execute($project_id); $sth->execute($project_id);
my $ret = $sth->fetchrow_array ? 1 : 0; my $ret = $sth->fetchrow_array ? 1 : 0;
$sth->finish();
$dbh->disconnect(); $dbh->disconnect();
if ($cfg->{RedmineMemcache}) {
if ($cfg->{RedmineMemcacheExpirySec}) {
$cfg->{RedmineMemcached}->set($project_id, $ret, $cfg->{RedmineMemcacheExpirySec});
} else {
$cfg->{RedmineMemcached}->set($project_id, $ret);
}
}
$ret; $ret;
} }
@@ -169,26 +298,59 @@ sub is_member {
my $redmine_user = shift; my $redmine_user = shift;
my $redmine_pass = shift; my $redmine_pass = shift;
my $r = shift; my $r = shift;
my $project_id = shift;
my $dbh = connect_database($r); my $dbh = connect_database($r);
my $project_id = get_project_identifier($r);
my $pass_digest = Digest::SHA1::sha1_hex($redmine_pass); my $pass_digest = Digest::SHA1::sha1_hex($redmine_pass);
my $sth = $dbh->prepare( my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
"SELECT hashed_password FROM members, projects, users WHERE projects.id=members.project_id AND users.id=members.user_id AND users.status=1 AND login=? AND identifier=?;" my $usrprojpass;
); if ($cfg->{RedmineMemcache}) {
$usrprojpass = $cfg->{RedmineMemcached}->get($redmine_user.":".$project_id);
return 1 if (defined $usrprojpass and ($usrprojpass eq $pass_digest));
}
my $query = $cfg->{RedmineQuery};
my $sth = $dbh->prepare($query);
$sth->execute($redmine_user, $project_id); $sth->execute($redmine_user, $project_id);
my $ret; my $ret;
while (my @row = $sth->fetchrow_array) { while (my @row = $sth->fetchrow_array) {
if ($row[0] eq $pass_digest) { unless ($row[1]) {
$ret = 1; if ($row[0] eq $pass_digest) {
last; $ret = 1;
last;
}
} elsif ($CanUseLDAPAuth) {
my $sthldap = $dbh->prepare(
"SELECT host,port,tls,account,account_password,base_dn,attr_login from auth_sources WHERE id = ?;"
);
$sthldap->execute($row[1]);
while (my @rowldap = $sthldap->fetchrow_array) {
my $ldap = Authen::Simple::LDAP->new(
host => ($rowldap[2] == 1 || $rowldap[2] eq "t") ? "ldaps://$rowldap[0]" : $rowldap[0],
port => $rowldap[1],
basedn => $rowldap[5],
binddn => $rowldap[3] ? $rowldap[3] : "",
bindpw => $rowldap[4] ? $rowldap[4] : "",
filter => "(".$rowldap[6]."=%s)"
);
$ret = 1 if ($ldap->authenticate($redmine_user, $redmine_pass));
}
$sthldap->finish();
} }
} }
$sth->finish();
$dbh->disconnect(); $dbh->disconnect();
if ($cfg->{RedmineMemcache} and $ret) {
if ($cfg->{RedmineMemcacheExpirySec}) {
$cfg->{RedmineMemcached}->set($redmine_user.":".$project_id, $pass_digest, $cfg->{RedmineMemcacheExpirySec});
} else {
$cfg->{RedmineMemcached}->set($redmine_user.":".$project_id, $pass_digest);
}
}
$ret; $ret;
} }
@@ -203,8 +365,8 @@ sub get_project_identifier {
sub connect_database { sub connect_database {
my $r = shift; my $r = shift;
my ($dsn, $db_user, $db_pass) = map { $r->dir_config($_) } qw/dsn db_user db_pass/; my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
return DBI->connect($dsn, $db_user, $db_pass); return DBI->connect($cfg->{RedmineDSN}, $cfg->{RedmineDbUser}, $cfg->{RedmineDbPass});
} }
1; 1;
+50 -3
View File
@@ -1,7 +1,9 @@
--- ---
queries_001: queries_001:
name: Multiple custom fields query id: 1
project_id: 1 project_id: 1
is_public: true
name: Multiple custom fields query
filters: | filters: |
--- ---
cf_1: cf_1:
@@ -17,6 +19,51 @@ queries_001:
- "125" - "125"
:operator: "=" :operator: "="
id: 1
is_public: true
user_id: 1 user_id: 1
column_names:
queries_002:
id: 2
project_id: 1
is_public: false
name: Private query for cookbook
filters: |
---
tracker_id:
:values:
- "3"
:operator: "="
status_id:
:values:
- "1"
:operator: o
user_id: 3
column_names:
queries_003:
id: 3
project_id:
is_public: false
name: Private query for all projects
filters: |
---
tracker_id:
:values:
- "3"
:operator: "="
user_id: 3
column_names:
queries_004:
id: 4
project_id:
is_public: true
name: Public query for all projects
filters: |
---
tracker_id:
:values:
- "3"
:operator: "="
user_id: 2
column_names:
-2
View File
@@ -57,7 +57,6 @@ roles_002:
- :add_issue_notes - :add_issue_notes
- :move_issues - :move_issues
- :delete_issues - :delete_issues
- :manage_public_queries
- :save_queries - :save_queries
- :view_gantt - :view_gantt
- :view_calendar - :view_calendar
@@ -94,7 +93,6 @@ roles_003:
- :manage_issue_relations - :manage_issue_relations
- :add_issue_notes - :add_issue_notes
- :move_issues - :move_issues
- :manage_public_queries
- :save_queries - :save_queries
- :view_gantt - :view_gantt
- :view_calendar - :view_calendar
+211
View File
@@ -0,0 +1,211 @@
# redMine - project management software
# Copyright (C) 2006-2008 Jean-Philippe Lang
#
# 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; either version 2
# of the License, or (at your option) any later version.
#
# 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.
require File.dirname(__FILE__) + '/../test_helper'
require 'queries_controller'
# Re-raise errors caught by the controller.
class QueriesController; def rescue_action(e) raise e end; end
class QueriesControllerTest < Test::Unit::TestCase
fixtures :projects, :users, :members, :roles, :trackers, :issue_statuses, :issue_categories, :enumerations, :issues, :custom_fields, :custom_values, :queries
def setup
@controller = QueriesController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
User.current = nil
end
def test_get_new_project_query
@request.session[:user_id] = 2
get :new, :project_id => 1
assert_response :success
assert_template 'new'
assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
:name => 'query[is_public]',
:checked => nil }
assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
:name => 'query_is_for_all',
:checked => nil,
:disabled => nil }
end
def test_get_new_global_query
@request.session[:user_id] = 2
get :new
assert_response :success
assert_template 'new'
assert_no_tag :tag => 'input', :attributes => { :type => 'checkbox',
:name => 'query[is_public]' }
assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
:name => 'query_is_for_all',
:checked => 'checked',
:disabled => nil }
end
def test_new_project_public_query
@request.session[:user_id] = 2
post :new,
:project_id => 'ecookbook',
:confirm => '1',
:default_columns => '1',
:fields => ["status_id", "assigned_to_id"],
:operators => {"assigned_to_id" => "=", "status_id" => "o"},
:values => { "assigned_to_id" => ["1"], "status_id" => ["1"]},
:query => {"name" => "test_new_project_public_query", "is_public" => "1"}
q = Query.find_by_name('test_new_project_public_query')
assert_redirected_to :controller => 'issues', :action => 'index', :query_id => q
assert q.is_public?
assert q.has_default_columns?
assert q.valid?
end
def test_new_project_private_query
@request.session[:user_id] = 3
post :new,
:project_id => 'ecookbook',
:confirm => '1',
:default_columns => '1',
:fields => ["status_id", "assigned_to_id"],
:operators => {"assigned_to_id" => "=", "status_id" => "o"},
:values => { "assigned_to_id" => ["1"], "status_id" => ["1"]},
:query => {"name" => "test_new_project_private_query", "is_public" => "1"}
q = Query.find_by_name('test_new_project_private_query')
assert_redirected_to :controller => 'issues', :action => 'index', :query_id => q
assert !q.is_public?
assert q.has_default_columns?
assert q.valid?
end
def test_new_global_private_query_with_custom_columns
@request.session[:user_id] = 3
post :new,
:confirm => '1',
:fields => ["status_id", "assigned_to_id"],
:operators => {"assigned_to_id" => "=", "status_id" => "o"},
:values => { "assigned_to_id" => ["me"], "status_id" => ["1"]},
:query => {"name" => "test_new_global_private_query", "is_public" => "1", "column_names" => ["", "tracker", "subject", "priority", "category"]}
q = Query.find_by_name('test_new_global_private_query')
assert_redirected_to :controller => 'issues', :action => 'index', :query_id => q
assert !q.is_public?
assert !q.has_default_columns?
assert_equal [:tracker, :subject, :priority, :category], q.columns.collect {|c| c.name}
assert q.valid?
end
def test_get_edit_global_public_query
@request.session[:user_id] = 1
get :edit, :id => 4
assert_response :success
assert_template 'edit'
assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
:name => 'query[is_public]',
:checked => 'checked' }
assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
:name => 'query_is_for_all',
:checked => 'checked',
:disabled => 'disabled' }
end
def test_edit_global_public_query
@request.session[:user_id] = 1
post :edit,
:id => 4,
:confirm => '1',
:default_columns => '1',
:fields => ["status_id", "assigned_to_id"],
:operators => {"assigned_to_id" => "=", "status_id" => "o"},
:values => { "assigned_to_id" => ["1"], "status_id" => ["1"]},
:query => {"name" => "test_edit_global_public_query", "is_public" => "1"}
assert_redirected_to :controller => 'issues', :action => 'index', :query_id => 4
q = Query.find_by_name('test_edit_global_public_query')
assert q.is_public?
assert q.has_default_columns?
assert q.valid?
end
def test_get_edit_global_private_query
@request.session[:user_id] = 3
get :edit, :id => 3
assert_response :success
assert_template 'edit'
assert_no_tag :tag => 'input', :attributes => { :type => 'checkbox',
:name => 'query[is_public]' }
assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
:name => 'query_is_for_all',
:checked => 'checked',
:disabled => 'disabled' }
end
def test_edit_global_private_query
@request.session[:user_id] = 3
post :edit,
:id => 3,
:confirm => '1',
:default_columns => '1',
:fields => ["status_id", "assigned_to_id"],
:operators => {"assigned_to_id" => "=", "status_id" => "o"},
:values => { "assigned_to_id" => ["me"], "status_id" => ["1"]},
:query => {"name" => "test_edit_global_private_query", "is_public" => "1"}
assert_redirected_to :controller => 'issues', :action => 'index', :query_id => 3
q = Query.find_by_name('test_edit_global_private_query')
assert !q.is_public?
assert q.has_default_columns?
assert q.valid?
end
def test_get_edit_project_private_query
@request.session[:user_id] = 3
get :edit, :id => 2
assert_response :success
assert_template 'edit'
assert_no_tag :tag => 'input', :attributes => { :type => 'checkbox',
:name => 'query[is_public]' }
assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
:name => 'query_is_for_all',
:checked => nil,
:disabled => nil }
end
def test_get_edit_project_public_query
@request.session[:user_id] = 2
get :edit, :id => 1
assert_response :success
assert_template 'edit'
assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
:name => 'query[is_public]',
:checked => 'checked'
}
assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
:name => 'query_is_for_all',
:checked => nil,
:disabled => 'disabled' }
end
def test_destroy
@request.session[:user_id] = 2
post :destroy, :id => 1
assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook', :set_filter => 1, :query_id => nil
assert_nil Query.find_by_id(1)
end
end
+32 -2
View File
@@ -1,5 +1,5 @@
# redMine - project management software # redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang # Copyright (C) 2006-2008 Jean-Philippe Lang
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
@@ -18,7 +18,7 @@
require File.dirname(__FILE__) + '/../test_helper' require File.dirname(__FILE__) + '/../test_helper'
class QueryTest < Test::Unit::TestCase class QueryTest < Test::Unit::TestCase
fixtures :projects, :users, :trackers, :issue_statuses, :issue_categories, :enumerations, :issues, :custom_fields, :custom_values, :queries fixtures :projects, :users, :members, :roles, :trackers, :issue_statuses, :issue_categories, :enumerations, :issues, :custom_fields, :custom_values, :queries
def test_query_with_multiple_custom_fields def test_query_with_multiple_custom_fields
query = Query.find(1) query = Query.find(1)
@@ -41,4 +41,34 @@ class QueryTest < Test::Unit::TestCase
c = q.columns.first c = q.columns.first
assert q.has_column?(c) assert q.has_column?(c)
end end
def test_editable_by
admin = User.find(1)
manager = User.find(2)
developer = User.find(3)
# Public query on project 1
q = Query.find(1)
assert q.editable_by?(admin)
assert q.editable_by?(manager)
assert !q.editable_by?(developer)
# Private query on project 1
q = Query.find(2)
assert q.editable_by?(admin)
assert !q.editable_by?(manager)
assert q.editable_by?(developer)
# Private query for all projects
q = Query.find(3)
assert q.editable_by?(admin)
assert !q.editable_by?(manager)
assert q.editable_by?(developer)
# Public query for all projects
q = Query.find(4)
assert q.editable_by?(admin)
assert !q.editable_by?(manager)
assert !q.editable_by?(developer)
end
end end