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

View File

@@ -150,6 +150,7 @@ class ApplicationController < ActionController::Base
def render_feed(items, options={})
@items = items || []
@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
render :template => "common/feed.atom.rxml", :layout => false, :content_type => 'application/atom+xml'
end

View File

@@ -73,6 +73,8 @@ class IssuesController < ApplicationController
# Send html if the query is not valid
render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
end
rescue ActiveRecord::RecordNotFound
render_404
end
def changes
@@ -87,6 +89,8 @@ class IssuesController < ApplicationController
end
@title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
render :layout => false, :content_type => 'application/atom+xml'
rescue ActiveRecord::RecordNotFound
render_404
end
def show
@@ -384,7 +388,10 @@ private
# Retrieve query from session or build a new query
def retrieve_query
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}
else
if params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)

View File

@@ -18,19 +18,14 @@
class QueriesController < ApplicationController
layout 'base'
menu_item :issues
before_filter :find_project, :authorize
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
before_filter :find_query, :except => :new
before_filter :find_optional_project, :only => :new
def new
@query = Query.new(params[:query])
@query.project = @project
@query.project = params[:query_is_for_all] ? nil : @project
@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]
params[:fields].each do |field|
@@ -52,7 +47,8 @@ class QueriesController < ApplicationController
@query.add_filter(field, params[:operators][field], params[:values][field])
end if params[:fields]
@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]
if @query.save
@@ -64,18 +60,21 @@ class QueriesController < ApplicationController
def destroy
@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
private
def find_project
if params[:id]
@query = Query.find(params[:id])
@project = @query.project
render_403 unless @query.editable_by?(User.current)
else
@project = Project.find(params[:project_id])
end
def find_query
@query = Query.find(params[:id])
@project = @query.project
render_403 unless @query.editable_by?(User.current)
rescue ActiveRecord::RecordNotFound
render_404
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
render_404
end

View File

@@ -32,6 +32,19 @@ module IssuesHelper
"<strong>#{@cached_label_assigned_to}</strong>: #{issue.assigned_to}<br />" +
"<strong>#{@cached_label_priority}</strong>: #{issue.priority.name}"
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)
case detail.property

View File

@@ -116,6 +116,11 @@ class Query < ActiveRecord::Base
set_language_if_valid(User.current.language)
end
def after_initialize
# Store the fact that project is nil (used in #editable_by?)
@is_for_all = project.nil?
end
def validate
filters.each_key do |field|
errors.add label_for(field), :activerecord_error_blank unless
@@ -128,8 +133,10 @@ class Query < ActiveRecord::Base
def editable_by?(user)
return false unless user
return true if !is_public && self.user_id == user.id
is_public && user.allowed_to?(:manage_public_queries, project)
# Admin can edit them all and regular users can edit their private queries
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
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] } },
"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 },
"created_on" => { :type => :date_past, :order => 9 },
"updated_on" => { :type => :date_past, :order => 10 },

View File

@@ -222,17 +222,26 @@ class User < ActiveRecord::Base
# action can be:
# * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
# * a permission Symbol (eg. :edit_project)
def allowed_to?(action, project)
# No action allowed on archived projects
return false unless project.active?
# No action allowed on disabled modules
return false unless project.allows_to?(action)
# Admin users are authorized for anything else
return true if admin?
role = role_for_project(project)
return false unless role
role.allowed_to?(action) && (project.is_public? || role.member?)
def allowed_to?(action, project, options={})
if project
# No action allowed on archived projects
return false unless project.active?
# No action allowed on disabled modules
return false unless project.allows_to?(action)
# Admin users are authorized for anything else
return true if admin?
role = role_for_project(project)
return false unless role
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
def self.current=(user)

View File

@@ -1,13 +1,14 @@
<h3><%= l(:label_issue_plural) %></h3>
<%= 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(:label_change_log), :controller => 'projects', :action => 'changelog', :id => @project %>
<% end %>
<% unless sidebar_queries.empty? -%>
<h3><%= l(:label_query_plural) %></h3>
<% queries = @project.queries.find(:all,
:order => "name ASC",
:conditions => ["is_public = ? or user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
queries.each do |query| %>
<% sidebar_queries.each do |query| -%>
<%= link_to query.name, :controller => 'issues', :action => 'index', :project_id => @project, :query_id => query %><br />
<% end %>
<% end -%>
<% end -%>

View File

@@ -18,7 +18,7 @@
:update => "content",
}, :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' %>
<% end %>
</p>
@@ -54,7 +54,7 @@
<% content_for :sidebar do %>
<%= render :partial => 'issues/sidebar' %>
<% end if @project%>
<% end %>
<% 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)) %>

View File

@@ -26,7 +26,7 @@
</div>
<div class="message reply">
<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 %>
</div>
<% end %>

View File

@@ -25,7 +25,7 @@
<div id="preview" class="wiki"></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>
<div class="wiki">
<%= textilizable(@news.description) %>

View File

@@ -6,11 +6,16 @@
<p><label for="query_name"><%=l(:field_name)%></label>
<%= text_field 'query', 'name', :size => 80 %></p>
<% if current_role.allowed_to?(:manage_public_queries) %>
<p><label for="query_is_public"><%=l(:field_is_public)%></label>
<%= check_box 'query', 'is_public' %></p>
<% if User.current.admin? || (@project && current_role.allowed_to?(:manage_public_queries)) %>
<p><label for="query_is_public"><%=l(:field_is_public)%></label>
<%= 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 %>
<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>
<%= 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>

View File

@@ -8,8 +8,8 @@ against redmine database
=head1 SYNOPSIS
This module allow anonymous users to browse public project and
registred users to browse and commit their project. authentication is
done on the redmine database.
registred users to browse and commit their project. Authentication is
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
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
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
## if the module isn't in your perl path
PerlRequire /usr/local/apache/Redmine.pm
## else
# PerlModule Apache::Authn::Redmine
## This module has to be in your perl path
## eg: /usr/lib/perl5/Apache/Authn/Redmine.pm
PerlLoadModule Apache::Authn::Redmine
<Location /svn>
DAV svn
SVNParentPath "/var/svn"
@@ -47,12 +51,19 @@ On debian/ubuntu you must do :
PerlAuthenHandler Apache::Authn::Redmine::authen_handler
## for mysql
PerlSetVar dsn DBI:mysql:database=databasename;host=my.db.server
RedmineDSN "DBI:mysql:database=databasename;host=my.db.server"
## 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
PerlSetVar db_pass password
RedmineDbUser "redmine"
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>
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
use strict;
use warnings FATAL => 'all', NONFATAL => 'redefine';
use DBI;
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::Access;
use Apache2::ServerRec qw();
use Apache2::RequestRec qw();
use Apache2::RequestUtil qw();
use Apache2::Const qw(:common);
use Apache2::Const qw(:common :override :cmd_how);
# 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/;
sub access_handler {
@@ -110,7 +222,7 @@ sub access_handler {
}
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);
@@ -126,7 +238,11 @@ sub authen_handler {
my ($res, $redmine_pass) = $r->get_basic_auth_pw();
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;
} else {
$r->note_auth_failure();
@@ -138,14 +254,27 @@ sub is_public_project {
my $project_id = 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 $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);
my $ret = $sth->fetchrow_array ? 1 : 0;
$sth->finish();
$dbh->disconnect();
if ($cfg->{RedmineMemcache}) {
if ($cfg->{RedmineMemcacheExpirySec}) {
$cfg->{RedmineMemcached}->set($project_id, $ret, $cfg->{RedmineMemcacheExpirySec});
} else {
$cfg->{RedmineMemcached}->set($project_id, $ret);
}
}
$ret;
}
@@ -169,26 +298,59 @@ sub is_member {
my $redmine_user = shift;
my $redmine_pass = shift;
my $r = shift;
my $project_id = shift;
my $dbh = connect_database($r);
my $project_id = get_project_identifier($r);
my $pass_digest = Digest::SHA1::sha1_hex($redmine_pass);
my $sth = $dbh->prepare(
"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 $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
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);
my $ret;
while (my @row = $sth->fetchrow_array) {
if ($row[0] eq $pass_digest) {
$ret = 1;
last;
unless ($row[1]) {
if ($row[0] eq $pass_digest) {
$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();
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;
}
@@ -202,9 +364,9 @@ sub get_project_identifier {
sub connect_database {
my $r = shift;
my ($dsn, $db_user, $db_pass) = map { $r->dir_config($_) } qw/dsn db_user db_pass/;
return DBI->connect($dsn, $db_user, $db_pass);
my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
return DBI->connect($cfg->{RedmineDSN}, $cfg->{RedmineDbUser}, $cfg->{RedmineDbPass});
}
1;

View File

@@ -1,7 +1,9 @@
---
queries_001:
name: Multiple custom fields query
id: 1
project_id: 1
is_public: true
name: Multiple custom fields query
filters: |
---
cf_1:
@@ -17,6 +19,51 @@ queries_001:
- "125"
:operator: "="
id: 1
is_public: true
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:

View File

@@ -57,7 +57,6 @@ roles_002:
- :add_issue_notes
- :move_issues
- :delete_issues
- :manage_public_queries
- :save_queries
- :view_gantt
- :view_calendar
@@ -94,7 +93,6 @@ roles_003:
- :manage_issue_relations
- :add_issue_notes
- :move_issues
- :manage_public_queries
- :save_queries
- :view_gantt
- :view_calendar

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

View File

@@ -1,5 +1,5 @@
# 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
# modify it under the terms of the GNU General Public License
@@ -18,7 +18,7 @@
require File.dirname(__FILE__) + '/../test_helper'
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
query = Query.find(1)
@@ -41,4 +41,34 @@ class QueryTest < Test::Unit::TestCase
c = q.columns.first
assert q.has_column?(c)
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