]> source.dussan.org Git - redmine.git/commitdiff
Adds configuration settings to limit valid repository path (#1415).
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Sat, 8 Nov 2014 10:52:59 +0000 (10:52 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Sat, 8 Nov 2014 10:52:59 +0000 (10:52 +0000)
git-svn-id: http://svn.redmine.org/redmine/trunk@13573 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/helpers/repositories_helper.rb
app/models/repository.rb
app/models/repository/cvs.rb
config/configuration.yml.example
config/locales/en.yml
config/locales/fr.yml
lib/redmine/configuration.rb
test/unit/repository_cvs_test.rb
test/unit/repository_subversion_test.rb

index 32b72ec54625053fb5e13f0f479265300e6809ae..ebe73da3fe866312820f36b4412b071b482cef00 100644 (file)
@@ -152,7 +152,7 @@ module RepositoriesHelper
   def subversion_field_tags(form, repository)
       content_tag('p', form.text_field(:url, :size => 60, :required => true,
                        :disabled => !repository.safe_attribute?('url')) +
-                       content_tag('em', '(file:///, http://, https://, svn://, svn+[tunnelscheme]://)', :class => 'info')) +
+                       scm_path_info_tag(repository)) +
       content_tag('p', form.text_field(:login, :size => 30)) +
       content_tag('p', form.password_field(
                             :password, :size => 30, :name => 'ignore',
@@ -165,7 +165,8 @@ module RepositoriesHelper
     content_tag('p', form.text_field(
                      :url, :label => l(:field_path_to_repository),
                      :size => 60, :required => true,
-                     :disabled => !repository.safe_attribute?('url'))) +
+                     :disabled => !repository.safe_attribute?('url')) +
+                     scm_path_info_tag(repository)) +
     scm_log_encoding_tag(form, repository)
   end
 
@@ -175,7 +176,7 @@ module RepositoriesHelper
                        :size => 60, :required => true,
                        :disabled => !repository.safe_attribute?('url')
                          ) +
-                     content_tag('em', l(:text_mercurial_repository_note), :class => 'info')) +
+                     scm_path_info_tag(repository)) +
     scm_path_encoding_tag(form, repository)
   end
 
@@ -185,7 +186,7 @@ module RepositoriesHelper
                        :size => 60, :required => true,
                        :disabled => !repository.safe_attribute?('url')
                          ) +
-                      content_tag('em', l(:text_git_repository_note), :class => 'info')) +
+                      scm_path_info_tag(repository)) +
     scm_path_encoding_tag(form, repository) +
     content_tag('p', form.check_box(
                         :extra_report_last_commit,
@@ -198,7 +199,8 @@ module RepositoriesHelper
                      :root_url,
                      :label => l(:field_cvsroot),
                      :size => 60, :required => true,
-                     :disabled => !repository.safe_attribute?('root_url'))) +
+                     :disabled => !repository.safe_attribute?('root_url')) +
+                     scm_path_info_tag(repository)) +
     content_tag('p', form.text_field(
                      :url,
                      :label => l(:field_cvs_module),
@@ -212,7 +214,8 @@ module RepositoriesHelper
     content_tag('p', form.text_field(
                      :url, :label => l(:field_path_to_repository),
                      :size => 60, :required => true,
-                     :disabled => !repository.safe_attribute?('url'))) +
+                     :disabled => !repository.safe_attribute?('url')) +
+                     scm_path_info_tag(repository)) +
     scm_log_encoding_tag(form, repository)
   end
 
@@ -220,10 +223,29 @@ module RepositoriesHelper
     content_tag('p', form.text_field(
                      :url, :label => l(:field_root_directory),
                      :size => 60, :required => true,
-                     :disabled => !repository.safe_attribute?('url'))) +
+                     :disabled => !repository.safe_attribute?('url')) +
+                     scm_path_info_tag(repository)) +
     scm_path_encoding_tag(form, repository)
   end
 
+  def scm_path_info_tag(repository)
+    text = scm_path_info(repository)
+    if text.present?
+      content_tag('em', text, :class => 'info')
+    else
+      ''
+    end
+  end
+
+  def scm_path_info(repository)
+    scm_name = repository.scm_name.to_s.downcase
+
+    info_from_config = Redmine::Configuration["scm_#{scm_name}_path_info"].presence
+    return info_from_config.html_safe if info_from_config
+
+    l("text_#{scm_name}_repository_note", :default => '')
+  end
+
   def scm_log_encoding_tag(form, repository)
     select = form.select(
       :log_encoding,
index 1cab86bd15183bc9f136883caa11fbe2eed66713..4d81c47abc622005e2a8e7e16b872899d496a22b 100644 (file)
@@ -45,6 +45,7 @@ class Repository < ActiveRecord::Base
   validates_format_of :identifier, :with => /\A(?!\d+$)[a-z0-9\-_]*\z/, :allow_blank => true
   # Checks if the SCM is enabled when creating a repository
   validate :repo_create_validation, :on => :create
+  validate :validate_repository_path
   attr_protected :id
 
   safe_attributes 'identifier',
@@ -458,6 +459,18 @@ class Repository < ActiveRecord::Base
 
   protected
 
+  # Validates repository url based against an optional regular expression
+  # that can be set in the Redmine configuration file.
+  def validate_repository_path(attribute=:url)
+    regexp = Redmine::Configuration["scm_#{scm_name.to_s.downcase}_path_regexp"]
+    if changes[attribute] && regexp.present?
+      regexp = regexp.to_s.strip.gsub('%project%') {Regexp.escape(project.try(:identifier).to_s)}
+      unless send(attribute).to_s.match(Regexp.new("\\A#{regexp}\\z"))
+        errors.add(attribute, :invalid)
+      end
+    end
+  end
+
   def check_default
     if !is_default? && set_as_default?
       self.is_default = true
index abc4083046a694ed8477a3cb8c1ea1008761dbc6..991c0449c0c7d505584630dba22e0e79642be4af 100644 (file)
@@ -192,6 +192,14 @@ class Repository::Cvs < Repository
     @current_revision_number = nil
   end
 
+  protected
+
+  # Overrides Repository#validate_repository_path to validate
+  # against root_url attribute.
+  def validate_repository_path(attribute=:root_url)
+    super(attribute)
+  end
+
   private
 
   # Returns the next revision number to assign to a CVS changeset
index da209f0bd6e50d3471687c00da68c5cf47355752..e6a8c6e2035da50ff3ec79db212aba15d96dfc3c 100644 (file)
@@ -108,6 +108,33 @@ default:
   scm_bazaar_command:
   scm_darcs_command:
 
+  # SCM paths validation.
+  #
+  # You can configure a regular expression for each SCM that will be used to
+  # validate the path of new repositories (eg. path entered by users with the
+  # "Manage repositories" permission and path returned by reposman.rb).
+  # The regexp will be wrapped with \A \z, so it must match the whole path.
+  # And the regexp is case sensitive.
+  #
+  # You can match the project identifier by using %project% in the regexp.
+  #
+  # You can also set a custom hint message for each SCM that will be displayed
+  # on the repository form instead of the default one.
+  #
+  # Examples:
+  # scm_subversion_path_regexp: file:///svnpath/[a-z0-9_]+
+  # scm_subversion_path_info: SVN URL (eg. file:///svnpath/foo)
+  #
+  # scm_git_path_regexp: /gitpath/%project%(\.[a-z0-9_])?/
+  #
+  scm_subversion_path_regexp:
+  scm_mercurial_path_regexp:
+  scm_git_path_regexp:
+  scm_cvs_path_regexp:
+  scm_bazaar_path_regexp:
+  scm_darcs_path_regexp:
+  scm_filesystem_path_regexp:
+
   # Absolute path to the SCM commands errors (stderr) log file.
   # The default is to log in the 'log' directory of your Redmine instance.
   # Example:
index 66de43259ea3539faee25a58433f12963bbdfc76..f0372bccf4ec2c986277fa8e9a28a4a1c38ae8cb 100644 (file)
@@ -1052,6 +1052,7 @@ en:
   text_zoom_out: Zoom out
   text_warn_on_leaving_unsaved: "The current page contains unsaved text that will be lost if you leave this page."
   text_scm_path_encoding_note: "Default: UTF-8"
+  text_subversion_repository_note: "Examples: file:///, http://, https://, svn://, svn+[tunnelscheme]://"
   text_git_repository_note: Repository is bare and local (e.g. /gitrepo, c:\gitrepo)
   text_mercurial_repository_note: Local repository (e.g. /hgrepo, c:\hgrepo)
   text_scm_command: Command
index e575388e8f5b65aae206c660d0cea47e3027653a..25693d080de2f0fb8aba49c9e0132a76602649e0 100644 (file)
@@ -1072,8 +1072,9 @@ fr:
   text_zoom_out: Zoom arrière
   text_warn_on_leaving_unsaved: "Cette page contient du texte non sauvegardé qui sera perdu si vous quittez la page."
   text_scm_path_encoding_note: "Défaut : UTF-8"
-  text_git_repository_note: "Le dépôt est vide et local (exemples : /gitrepo, c:\\gitrepo)"
-  text_mercurial_repository_note: "Dépôt local (exemples : /hgrepo, c:\\hgrepo)"
+  text_subversion_repository_note: "Exemples (en fonction des protocoles supportés) : file:///, http://, https://, svn://, svn+[tunnelscheme]://"
+  text_git_repository_note: "Chemin vers un dépôt vide et local (exemples : /gitrepo, c:\\gitrepo)"
+  text_mercurial_repository_note: "Chemin vers un dépôt local (exemples : /hgrepo, c:\\hgrepo)"
   text_scm_command: Commande
   text_scm_command_version: Version
   text_scm_config: Vous pouvez configurer les commandes des SCM dans config/configuration.yml. Redémarrer l'application après modification.
index 01abf4e932f5cebf4048bd53d0ad90905413f02a..77bdd8fd01845b56f4367ffa5f51513d6bd62b6e 100644 (file)
@@ -58,6 +58,7 @@ module Redmine
           end
         end
 
+        check_regular_expressions
         @config
       end
 
@@ -112,6 +113,20 @@ module Redmine
           @config.merge!({'email_delivery' => load_from_yaml(deprecated_email_conf, env)})
         end
       end
+
+      # Checks the validness of regular expressions set for repository paths at startup
+      def check_regular_expressions
+        @config.each do |name, value|
+          if value.present? && name =~ /^scm_.+_path_regexp$/
+            begin
+              Regexp.new value.to_s.strip
+            rescue => e
+              $stderr.puts "Invalid regular expression set as #{name} setting in your Redmine configuration file:\n#{e.message}"
+              exit 1
+            end
+          end
+        end
+      end
     end
   end
 end
index 8b5ac6271ba7db7258a35436c05a79ee6bf97f02..fec41329a03eb08a22c5e75e1897a4e74552833c 100644 (file)
@@ -93,6 +93,24 @@ class RepositoryCvsTest < ActiveSupport::TestCase
     assert_include str, repo.errors.full_messages
   end
 
+  def test_root_url_should_be_validated_against_regexp_set_in_configuration
+    Redmine::Configuration.with 'scm_cvs_path_regexp' => '/cvspath/[a-z]+' do
+      repo = Repository::Cvs.new(
+        :project       => @project,
+        :identifier    => 'test',
+        :log_encoding  => 'UTF-8',
+        :path_encoding => '',
+        :url           => MODULE_NAME
+      )
+      repo.root_url = '/wrong_path'
+      assert !repo.valid?
+      assert repo.errors[:root_url].present?
+
+      repo.root_url = '/cvspath/foo'
+      assert repo.valid?
+    end
+  end
+
   if File.directory?(REPOSITORY_PATH)
     def test_fetch_changesets_from_scratch
       assert_equal 0, @repository.changesets.count
index 2fe50da144693954cfbcb706e8beb59763564d06..42e963288e591b239f94fe4cb0a5bc477d8a08a4 100644 (file)
@@ -57,6 +57,37 @@ class RepositorySubversionTest < ActiveSupport::TestCase
     end
   end
 
+  def test_url_should_be_validated_against_regexp_set_in_configuration
+    Redmine::Configuration.with 'scm_subversion_path_regexp' => 'file:///svnpath/[a-z]+' do
+      repo = Repository::Subversion.new(:project => @project, :identifier => 'test')
+      repo.url = 'http://foo'
+      assert !repo.valid?
+      assert repo.errors[:url].present?
+
+      repo.url = 'file:///svnpath/foo/bar'
+      assert !repo.valid?
+      assert repo.errors[:url].present?
+
+      repo.url = 'file:///svnpath/foo'
+      assert repo.valid?
+    end
+  end
+
+  def test_url_should_be_validated_against_regexp_set_in_configuration_with_project_identifier
+    Redmine::Configuration.with 'scm_subversion_path_regexp' => 'file:///svnpath/%project%(\.[a-z]+)?' do
+      repo = Repository::Subversion.new(:project => @project, :identifier => 'test')
+      repo.url = 'file:///svnpath/invalid'
+      assert !repo.valid?
+      assert repo.errors[:url].present?
+
+      repo.url = 'file:///svnpath/subproject1'
+      assert repo.valid?
+
+      repo.url = 'file:///svnpath/subproject1.foo'
+      assert repo.valid?
+    end
+  end
+
   if repository_configured?('subversion')
     def test_fetch_changesets_from_scratch
       assert_equal 0, @repository.changesets.count