diff options
Diffstat (limited to 'app/controllers')
20 files changed, 213 insertions, 46 deletions
diff --git a/app/controllers/account_controller.rb b/app/controllers/account_controller.rb index 5e615d17f..ea75d5de1 100644 --- a/app/controllers/account_controller.rb +++ b/app/controllers/account_controller.rb @@ -36,6 +36,7 @@ class AccountController < ApplicationController redirect_back_or_default home_url, :referer => true end end + no_store rescue AuthSourceException => e logger.error "An error occurred when authenticating #{params[:username]}: #{e.message}" render_error :message => e.message @@ -95,6 +96,7 @@ class AccountController < ApplicationController end end end + no_store render :template => "account/password_recovery" return else @@ -218,6 +220,7 @@ class AccountController < ApplicationController def twofa_confirm @twofa_view = @twofa.otp_confirm_view_variables + no_store end def twofa diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb index 892629af1..9b45f9553 100644 --- a/app/controllers/admin_controller.rb +++ b/app/controllers/admin_controller.rb @@ -36,9 +36,7 @@ class AdminController < ApplicationController end def projects - retrieve_query(ProjectQuery, false, :defaults => @default_columns_names) - @query.admin_projects = 1 - + retrieve_query(ProjectAdminQuery, false, :defaults => @default_columns_names) @entry_count = @query.result_count @entry_pages = Paginator.new @entry_count, per_page_option, params['page'] @projects = @query.results_scope(:limit => @entry_pages.per_page, :offset => @entry_pages.offset).to_a diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 111c85bc5..a01d5c75f 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -131,6 +131,14 @@ class ApplicationController < ActionController::Base if (key = api_key_from_request) # Use API key user = User.find_by_api_key(key) + elsif access_token = Doorkeeper.authenticate(request) + # Oauth + if access_token.accessible? + user = User.active.find_by_id(access_token.resource_owner_id) + user.oauth_scope = access_token.scopes.all.map(&:to_sym) + else + doorkeeper_render_error + end elsif /\ABasic /i.match?(request.authorization.to_s) # HTTP Basic, either username/password or API key/random authenticate_with_http_basic do |username, password| @@ -482,15 +490,17 @@ class ApplicationController < ActionController::Base end helper_method :back_url - def redirect_back_or_default(default, options={}) + def redirect_back_or_default(default, options = {}) + referer = options.delete(:referer) + if back_url = validate_back_url(params[:back_url].to_s) redirect_to(back_url) return - elsif options[:referer] + elsif referer redirect_to_referer_or default return end - redirect_to default + redirect_to default, options false end @@ -509,11 +519,9 @@ class ApplicationController < ActionController::Base if uri.send(component).present? && uri.send(component) != request.send(component) return false end - - uri.send(:"#{component}=", nil) end - # Always ignore basic user:password in the URL - uri.userinfo = nil + # Remove unnecessary components to convert the URL into a relative URL + uri.omit!(:scheme, :authority) rescue Addressable::URI::InvalidURIError return false end diff --git a/app/controllers/auto_completes_controller.rb b/app/controllers/auto_completes_controller.rb index 2982447e9..77105c8e8 100644 --- a/app/controllers/auto_completes_controller.rb +++ b/app/controllers/auto_completes_controller.rb @@ -26,7 +26,7 @@ class AutoCompletesController < ApplicationController status = params[:status].to_s issue_id = params[:issue_id].to_s - scope = Issue.cross_project_scope(@project, params[:scope]).visible + scope = Issue.cross_project_scope(@project, params[:scope]).includes(:tracker).visible scope = scope.open(status == 'o') if status.present? scope = scope.where.not(:id => issue_id.to_i) if issue_id.present? if q.present? diff --git a/app/controllers/context_menus_controller.rb b/app/controllers/context_menus_controller.rb index 4c7305873..1e37f623b 100644 --- a/app/controllers/context_menus_controller.rb +++ b/app/controllers/context_menus_controller.rb @@ -33,7 +33,7 @@ class ContextMenusController < ApplicationController @can = { :edit => @issues.all?(&:attributes_editable?), - :log_time => (@project && User.current.allowed_to?(:log_time, @project)), + :log_time => @issue&.time_loggable?, :copy => User.current.allowed_to?(:copy_issues, @projects) && Issue.allowed_target_projects.any?, :add_watchers => User.current.allowed_to?(:add_issue_watchers, @projects), :delete => @issues.all?(&:deletable?), diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index c5af8658f..a9c3f6183 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -112,8 +112,13 @@ class IssuesController < ApplicationController respond_to do |format| format.html do @priorities = IssuePriority.active - @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project) - @time_entries = @issue.time_entries.visible.preload(:activity, :user) + if @project.module_enabled?(:time_tracking) + @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project) + @time_entries = @issue.time_entries.visible.preload(:activity, :user) + else + @time_entry = nil + @time_entries = [] + end @relation = IssueRelation.new @has_changesets = @issue.changesets.visible.preload(:repository, :user).exists? retrieve_previous_and_next_issue_ids @@ -224,9 +229,8 @@ class IssuesController < ApplicationController end respond_to do |format| format.html do - redirect_back_or_default( - issue_path(@issue, previous_and_next_issue_ids_params) - ) + redirect_back_or_default issue_path(@issue), + flash: { previous_and_next_issue_ids: previous_and_next_issue_ids_params } end format.api {render_api_ok} end @@ -512,11 +516,14 @@ class IssuesController < ApplicationController end def retrieve_previous_and_next_issue_ids - if params[:prev_issue_id].present? || params[:next_issue_id].present? - @prev_issue_id = params[:prev_issue_id].presence.try(:to_i) - @next_issue_id = params[:next_issue_id].presence.try(:to_i) - @issue_position = params[:issue_position].presence.try(:to_i) - @issue_count = params[:issue_count].presence.try(:to_i) + if flash.key?(:previous_and_next_issue_ids) + flash[:previous_and_next_issue_ids].then do |info| + @prev_issue_id = info[:prev_issue_id].presence.try(:to_i) + @next_issue_id = info[:next_issue_id].presence.try(:to_i) + @issue_position = info[:issue_position].presence.try(:to_i) + @issue_count = info[:issue_count].presence.try(:to_i) + end + flash.delete(:previous_and_next_issue_ids) else retrieve_query_from_session if @query diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb index 5159bf540..8b26bee73 100644 --- a/app/controllers/messages_controller.rb +++ b/app/controllers/messages_controller.rb @@ -51,6 +51,8 @@ class MessagesController < ApplicationController offset(@reply_pages.offset). to_a + Message.preload_reaction_details(@replies) + @reply = Message.new(:subject => "RE: #{@message.subject}") render :action => "show", :layout => false if request.xhr? end @@ -134,7 +136,7 @@ class MessagesController < ApplicationController def preview message = @board.messages.find_by_id(params[:id]) - @text = params[:text] ? params[:text] : nil + @text = params[:text] || nil @previewed = message render :partial => 'common/preview' end diff --git a/app/controllers/my_controller.rb b/app/controllers/my_controller.rb index 01fe3995c..35483c8ef 100644 --- a/app/controllers/my_controller.rb +++ b/app/controllers/my_controller.rb @@ -115,6 +115,7 @@ class MyController < ApplicationController end end end + no_store end # Create a new feeds key diff --git a/app/controllers/news_controller.rb b/app/controllers/news_controller.rb index 06240e359..dd6bade24 100644 --- a/app/controllers/news_controller.rb +++ b/app/controllers/news_controller.rb @@ -67,8 +67,10 @@ class NewsController < ApplicationController end def show - @comments = @news.comments.to_a + @comments = @news.comments.preload(:commented).to_a @comments.reverse! if User.current.wants_comments_in_reverse_order? + + Comment.preload_reaction_details(@comments) end def new diff --git a/app/controllers/oauth2_applications_controller.rb b/app/controllers/oauth2_applications_controller.rb new file mode 100644 index 000000000..107af2ec0 --- /dev/null +++ b/app/controllers/oauth2_applications_controller.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +# +# Redmine - project management software +# Copyright (C) 2006- 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. +# +class Oauth2ApplicationsController < Doorkeeper::ApplicationsController + private + + def application_params + params[:doorkeeper_application] ||= {} + params[:doorkeeper_application][:scopes] ||= [] + + scopes = Redmine::AccessControl.public_permissions.map{|p| p.name.to_s} + + if params[:doorkeeper_application][:scopes].is_a?(Array) + scopes |= params[:doorkeeper_application][:scopes] + else + scopes |= params[:doorkeeper_application][:scopes].split(/\s+/) + end + params[:doorkeeper_application][:scopes] = scopes.join(' ') + super + end +end diff --git a/app/controllers/previews_controller.rb b/app/controllers/previews_controller.rb index 9dd228a3d..744daa7c8 100644 --- a/app/controllers/previews_controller.rb +++ b/app/controllers/previews_controller.rb @@ -26,7 +26,7 @@ class PreviewsController < ApplicationController if @issue @previewed = @issue end - @text = params[:text] ? params[:text] : nil + @text = params[:text] || nil render :partial => 'common/preview' end @@ -34,12 +34,12 @@ class PreviewsController < ApplicationController if params[:id].present? && news = News.visible.find_by_id(params[:id]) @previewed = news end - @text = params[:text] ? params[:text] : nil + @text = params[:text] || nil render :partial => 'common/preview' end def text - @text = params[:text] ? params[:text] : nil + @text = params[:text] || nil render :partial => 'common/preview' end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index f9a390c58..2a42c99ed 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -176,7 +176,7 @@ class ProjectsController < ApplicationController respond_to do |format| format.html do @principals_by_role = @project.principals_by_role - @subprojects = @project.children.visible.to_a + @subprojects = @project.leaf? ? [] : @project.children.visible.to_a @news = @project.news.limit(5).includes(:author, :project).reorder("#{News.table_name}.created_on DESC").to_a with_subprojects = Setting.display_subprojects_issues? @trackers = @project.rolled_up_trackers(with_subprojects).visible diff --git a/app/controllers/queries_controller.rb b/app/controllers/queries_controller.rb index 53ce029b9..24f37eda2 100644 --- a/app/controllers/queries_controller.rb +++ b/app/controllers/queries_controller.rb @@ -19,6 +19,8 @@ class QueriesController < ApplicationController menu_item :issues + layout :query_layout + before_action :find_query, :only => [:edit, :update, :destroy] before_action :find_optional_project, :only => [:new, :create] @@ -52,7 +54,6 @@ class QueriesController < ApplicationController @query.user = User.current @query.project = @project @query.build_from_params(params) - render :layout => 'admin' if params[:admin_projects] end def create @@ -63,14 +64,13 @@ class QueriesController < ApplicationController if @query.save flash[:notice] = l(:notice_successful_create) - redirect_to_items(:query_id => @query, :admin_projects => params[:admin_projects]) + redirect_to_items(:query_id => @query) else render :action => 'new', :layout => !request.xhr? end end def edit - render :layout => 'admin' if params[:admin_projects] end def update @@ -78,7 +78,7 @@ class QueriesController < ApplicationController if @query.save flash[:notice] = l(:notice_successful_update) - redirect_to_items(:query_id => @query, :admin_projects => params[:admin_projects]) + redirect_to_items(:query_id => @query) else render :action => 'edit' end @@ -109,18 +109,20 @@ class QueriesController < ApplicationController end def current_menu_item - @query ? @query.queried_class.to_s.underscore.pluralize.to_sym : nil + return unless @query + return if query_layout == 'admin' + + @query.queried_class.to_s.underscore.pluralize.to_sym end def current_menu(project) - super if params[:admin_projects].nil? + super unless query_layout == 'admin' end private def find_query @query = Query.find(params[:id]) - @query.admin_projects = params[:admin_projects] if @query.is_a?(ProjectQuery) @project = @query.project render_403 unless @query.editable_by?(User.current) rescue ActiveRecord::RecordNotFound @@ -171,17 +173,25 @@ class QueriesController < ApplicationController end def redirect_to_project_query(options) - if params[:admin_projects] - redirect_to admin_projects_path(options) - else - redirect_to projects_path(options) - end + redirect_to projects_path(options) + end + + def redirect_to_project_admin_query(options) + redirect_to admin_projects_path(options) end def redirect_to_user_query(options) redirect_to users_path(options) end + def query_layout + @query&.layout || 'base' + end + + def menu_items + {self.controller_name.to_sym => {:actions => {}, :default => current_menu_item}} + end + # Returns the Query subclass, IssueQuery by default # for compatibility with previous behaviour def query_class diff --git a/app/controllers/reactions_controller.rb b/app/controllers/reactions_controller.rb new file mode 100644 index 000000000..71b37e5f8 --- /dev/null +++ b/app/controllers/reactions_controller.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +# Redmine - project management software +# Copyright (C) 2006- 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. + +class ReactionsController < ApplicationController + before_action :require_login + + before_action :check_enabled + before_action :set_object, :authorize_reactable + + def create + respond_to do |format| + format.js do + @object.reactions.find_or_create_by!(user: User.current) + end + format.any { head :not_found } + end + end + + def destroy + respond_to do |format| + format.js do + reaction = @object.reactions.by(User.current).find_by(id: params[:id]) + reaction&.destroy + end + format.any { head :not_found } + end + end + + private + + def check_enabled + render_403 unless Setting.reactions_enabled? + end + + def set_object + object_type = params[:object_type] + + unless Redmine::Reaction::REACTABLE_TYPES.include?(object_type) + render_403 + return + end + + @object = object_type.constantize.find(params[:object_id]) + end + + def authorize_reactable + render_403 unless Redmine::Reaction.editable?(@object, User.current) + end +end diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index 9be7878ce..d6a13daf2 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -160,7 +160,15 @@ class RepositoriesController < ApplicationController # Force the download send_opt = {:filename => filename_for_content_disposition(@path.split('/').last)} send_type = Redmine::MimeType.of(@path) - send_opt[:type] = send_type.to_s if send_type + case send_type + when nil + # No MIME type detected. Let Rails use the default type. + when 'application/javascript' + # Avoid ActionController::InvalidCrossOriginRequest exception by setting non-JS content type + send_opt[:type] = 'text/plain' + else + send_opt[:type] = send_type + end send_opt[:disposition] = disposition(@path) send_data @repository.cat(@path, @rev), send_opt else diff --git a/app/controllers/roles_controller.rb b/app/controllers/roles_controller.rb index dfe7c2b8f..89f9ee497 100644 --- a/app/controllers/roles_controller.rb +++ b/app/controllers/roles_controller.rb @@ -99,7 +99,15 @@ class RolesController < ApplicationController begin @role.destroy rescue - flash[:error] = l(:error_can_not_remove_role) + flash[:error] = l(:error_can_not_remove_role) + + if @role.members.present? + projects = Project.joins(members: :member_roles).where(member_roles: { role_id: @role.id }).distinct.sorted + links = projects.map do |p| + view_context.link_to(p, settings_project_path(p, tab: 'members')) + end.join(', ') + flash[:error] += l(:error_can_not_remove_role_reason_members_html, projects: links) + end end redirect_to roles_path end diff --git a/app/controllers/twofa_backup_codes_controller.rb b/app/controllers/twofa_backup_codes_controller.rb index 8e14247b0..923b9671b 100644 --- a/app/controllers/twofa_backup_codes_controller.rb +++ b/app/controllers/twofa_backup_codes_controller.rb @@ -26,7 +26,7 @@ class TwofaBackupCodesController < ApplicationController before_action :twofa_setup - require_sudo_mode :init + require_sudo_mode :init, :confirm, :create, :show def init if @twofa.send_code(controller: 'twofa_backup_codes', action: 'create') @@ -37,6 +37,7 @@ class TwofaBackupCodesController < ApplicationController def confirm @twofa_view = @twofa.otp_confirm_view_variables + no_store end def create @@ -64,6 +65,7 @@ class TwofaBackupCodesController < ApplicationController if tokens.present? && (@created_at = tokens.collect(&:created_on).max) > 5.minutes.ago @backup_codes = tokens.collect(&:value) + no_store else flash[:warning] = l('twofa_backup_codes_already_shown', bc_path: my_twofa_backup_codes_init_path) redirect_to controller: 'my', action: 'account' diff --git a/app/controllers/twofa_controller.rb b/app/controllers/twofa_controller.rb index 446d2f105..3023caa9b 100644 --- a/app/controllers/twofa_controller.rb +++ b/app/controllers/twofa_controller.rb @@ -27,10 +27,14 @@ class TwofaController < ApplicationController before_action :require_active_twofa - require_sudo_mode :activate_init, :deactivate_init + require_sudo_mode :select_scheme, + :activate_init, :activate_confirm, :activate, + :deactivate_init, :deactivate_confirm, :deactivate skip_before_action :check_twofa_activation, only: [:select_scheme, :activate_init, :activate_confirm, :activate] + before_action :ensure_user_has_no_twofa, only: [:select_scheme, :activate_init, :activate_confirm, :activate] + def select_scheme @user = User.current end @@ -43,6 +47,7 @@ class TwofaController < ApplicationController def activate_confirm @twofa_view = @twofa.init_pairing_view_variables + no_store end def activate @@ -114,4 +119,13 @@ class TwofaController < ApplicationController redirect_to my_account_path end end + + def ensure_user_has_no_twofa + # Allow activating a new 2FA scheme / showing twofa secret only if no other + # is already configured + return true if User.current.twofa_scheme.blank? + + flash[:warning] = l('twofa_already_setup') + redirect_to controller: 'my', action: 'account' + end end diff --git a/app/controllers/versions_controller.rb b/app/controllers/versions_controller.rb index d52b43ba3..328d3e56e 100644 --- a/app/controllers/versions_controller.rb +++ b/app/controllers/versions_controller.rb @@ -51,7 +51,7 @@ class VersionsController < ApplicationController if @selected_tracker_ids.any? && @versions.any? issues = Issue.visible. includes(:project, :tracker). - preload(:status, :priority, :fixed_version). + preload(:status, :priority, :fixed_version, {:assigned_to => :email_address}). where(:tracker_id => @selected_tracker_ids, :project_id => project_ids, :fixed_version_id => @versions.map(&:id)). order("#{Project.table_name}.lft, #{Tracker.table_name}.position, #{Issue.table_name}.id") @issues_by_version = issues.group_by(&:fixed_version) @@ -69,7 +69,7 @@ class VersionsController < ApplicationController format.html do @issues = @version.fixed_issues.visible. includes(:status, :tracker, :priority). - preload(:project). + preload(:project, {:assigned_to => :email_address}). reorder("#{Tracker.table_name}.position, #{Issue.table_name}.id"). to_a end diff --git a/app/controllers/wiki_controller.rb b/app/controllers/wiki_controller.rb index 36b90da77..bcb3b0891 100644 --- a/app/controllers/wiki_controller.rb +++ b/app/controllers/wiki_controller.rb @@ -240,6 +240,7 @@ class WikiController < ApplicationController # don't load text @versions = @page.content.versions. select("id, author_id, comments, updated_on, version"). + preload(:author). reorder('version DESC'). limit(@version_pages.per_page + 1). offset(@version_pages.offset). |