##// END OF EJS Templates
Adds support for SCM/LDAP passwords encryption in the database (#7411)....
Jean-Philippe Lang -
r4830:a78d5659593d
parent child
Show More
@@ -0,0 +1,9
1 class ChangeRepositoriesPasswordLimit < ActiveRecord::Migration
2 def self.up
3 change_column :repositories, :password, :string, :limit => nil, :default => ''
4 end
5
6 def self.down
7 change_column :repositories, :password, :string, :limit => 60, :default => ''
8 end
9 end
@@ -0,0 +1,9
1 class ChangeAuthSourcesAccountPasswordLimit < ActiveRecord::Migration
2 def self.up
3 change_column :auth_sources, :account_password, :string, :limit => nil, :default => ''
4 end
5
6 def self.down
7 change_column :auth_sources, :account_password, :string, :limit => 60, :default => ''
8 end
9 end
@@ -0,0 +1,95
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 module Redmine
19 module Ciphering
20 def self.included(base)
21 base.extend ClassMethods
22 end
23
24 class << self
25 def encrypt_text(text)
26 if cipher_key.blank?
27 text
28 else
29 c = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
30 iv = c.random_iv
31 c.encrypt
32 c.key = cipher_key
33 c.iv = iv
34 e = c.update(text.to_s)
35 e << c.final
36 "aes-256-cbc:" + [e, iv].map {|v| Base64.encode64(v).strip}.join('--')
37 end
38 end
39
40 def decrypt_text(text)
41 if text && match = text.match(/\Aaes-256-cbc:(.+)\Z/)
42 text = match[1]
43 c = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
44 e, iv = text.split("--").map {|s| Base64.decode64(s)}
45 c.decrypt
46 c.key = cipher_key
47 c.iv = iv
48 d = c.update(e)
49 d << c.final
50 else
51 text
52 end
53 end
54
55 def cipher_key
56 key = Redmine::Configuration['database_cipher_key'].to_s
57 key.blank? ? nil : Digest::SHA256.hexdigest(key)
58 end
59 end
60
61 module ClassMethods
62 def encrypt_all(attribute)
63 transaction do
64 all.each do |object|
65 clear = object.send(attribute)
66 object.send "#{attribute}=", clear
67 raise(ActiveRecord::Rollback) unless object.save(false)
68 end
69 end ? true : false
70 end
71
72 def decrypt_all(attribute)
73 transaction do
74 all.each do |object|
75 clear = object.send(attribute)
76 object.write_attribute attribute, clear
77 raise(ActiveRecord::Rollback) unless object.save(false)
78 end
79 end
80 end ? true : false
81 end
82
83 private
84
85 # Returns the value of the given ciphered attribute
86 def read_ciphered_attribute(attribute)
87 Redmine::Ciphering.decrypt_text(read_attribute(attribute))
88 end
89
90 # Sets the value of the given ciphered attribute
91 def write_ciphered_attribute(attribute, value)
92 write_attribute(attribute, Redmine::Ciphering.encrypt_text(value))
93 end
94 end
95 end
@@ -0,0 +1,35
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18
19 namespace :db do
20 desc 'Encrypts SCM and LDAP passwords in the database.'
21 task :encrypt => :environment do
22 unless (Repository.encrypt_all(:password) &&
23 AuthSource.encrypt_all(:account_password))
24 raise "Some objects could not be saved after encryption, update was rollback'ed."
25 end
26 end
27
28 desc 'Decrypts SCM and LDAP passwords in the database.'
29 task :decrypt => :environment do
30 unless (Repository.decrypt_all(:password) &&
31 AuthSource.decrypt_all(:account_password))
32 raise "Some objects could not be saved after decryption, update was rollback'ed."
33 end
34 end
35 end
@@ -0,0 +1,84
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 require File.expand_path('../../../../test_helper', __FILE__)
19
20 class Redmine::CipheringTest < ActiveSupport::TestCase
21
22 def test_password_should_be_encrypted
23 Redmine::Configuration.with 'database_cipher_key' => 'secret' do
24 r = Repository::Subversion.generate!(:password => 'foo')
25 assert_equal 'foo', r.password
26 assert r.read_attribute(:password).match(/\Aaes-256-cbc:.+\Z/)
27 end
28 end
29
30 def test_password_should_be_clear_with_blank_key
31 Redmine::Configuration.with 'database_cipher_key' => '' do
32 r = Repository::Subversion.generate!(:password => 'foo')
33 assert_equal 'foo', r.password
34 assert_equal 'foo', r.read_attribute(:password)
35 end
36 end
37
38 def test_password_should_be_clear_with_nil_key
39 Redmine::Configuration.with 'database_cipher_key' => nil do
40 r = Repository::Subversion.generate!(:password => 'foo')
41 assert_equal 'foo', r.password
42 assert_equal 'foo', r.read_attribute(:password)
43 end
44 end
45
46 def test_unciphered_password_should_be_readable
47 Redmine::Configuration.with 'database_cipher_key' => nil do
48 r = Repository::Subversion.generate!(:password => 'clear')
49 end
50
51 Redmine::Configuration.with 'database_cipher_key' => 'secret' do
52 r = Repository.first(:order => 'id DESC')
53 assert_equal 'clear', r.password
54 end
55 end
56
57 def test_encrypt_all
58 Repository.delete_all
59 Redmine::Configuration.with 'database_cipher_key' => nil do
60 Repository::Subversion.generate!(:password => 'foo')
61 Repository::Subversion.generate!(:password => 'bar')
62 end
63
64 Redmine::Configuration.with 'database_cipher_key' => 'secret' do
65 assert Repository.encrypt_all(:password)
66 r = Repository.first(:order => 'id DESC')
67 assert_equal 'bar', r.password
68 assert r.read_attribute(:password).match(/\Aaes-256-cbc:.+\Z/)
69 end
70 end
71
72 def test_decrypt_all
73 Repository.delete_all
74 Redmine::Configuration.with 'database_cipher_key' => 'secret' do
75 Repository::Subversion.generate!(:password => 'foo')
76 Repository::Subversion.generate!(:password => 'bar')
77
78 assert Repository.decrypt_all(:password)
79 r = Repository.first(:order => 'id DESC')
80 assert_equal 'bar', r.password
81 assert_equal 'bar', r.read_attribute(:password)
82 end
83 end
84 end
@@ -1,58 +1,68
1 1 # redMine - project management software
2 2 # Copyright (C) 2006 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class AuthSource < ActiveRecord::Base
19 include Redmine::Ciphering
20
19 21 has_many :users
20 22
21 23 validates_presence_of :name
22 24 validates_uniqueness_of :name
23 25 validates_length_of :name, :maximum => 60
24 26
25 27 def authenticate(login, password)
26 28 end
27 29
28 30 def test_connection
29 31 end
30 32
31 33 def auth_method_name
32 34 "Abstract"
33 35 end
36
37 def account_password
38 read_ciphered_attribute(:account_password)
39 end
40
41 def account_password=(arg)
42 write_ciphered_attribute(:account_password, arg)
43 end
34 44
35 45 def allow_password_changes?
36 46 self.class.allow_password_changes?
37 47 end
38 48
39 49 # Does this auth source backend allow password changes?
40 50 def self.allow_password_changes?
41 51 false
42 52 end
43 53
44 54 # Try to authenticate a user not yet registered against available sources
45 55 def self.authenticate(login, password)
46 56 AuthSource.find(:all, :conditions => ["onthefly_register=?", true]).each do |source|
47 57 begin
48 58 logger.debug "Authenticating '#{login}' against '#{source.name}'" if logger && logger.debug?
49 59 attrs = source.authenticate(login, password)
50 60 rescue => e
51 61 logger.error "Error during authentication: #{e.message}"
52 62 attrs = nil
53 63 end
54 64 return attrs if attrs
55 65 end
56 66 return nil
57 67 end
58 68 end
@@ -1,130 +1,130
1 1 # redMine - project management software
2 2 # Copyright (C) 2006 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'net/ldap'
19 19 require 'iconv'
20 20
21 21 class AuthSourceLdap < AuthSource
22 22 validates_presence_of :host, :port, :attr_login
23 validates_length_of :name, :host, :account_password, :maximum => 60, :allow_nil => true
24 validates_length_of :account, :base_dn, :maximum => 255, :allow_nil => true
23 validates_length_of :name, :host, :maximum => 60, :allow_nil => true
24 validates_length_of :account, :account_password, :base_dn, :maximum => 255, :allow_nil => true
25 25 validates_length_of :attr_login, :attr_firstname, :attr_lastname, :attr_mail, :maximum => 30, :allow_nil => true
26 26 validates_numericality_of :port, :only_integer => true
27 27
28 28 before_validation :strip_ldap_attributes
29 29
30 30 def after_initialize
31 31 self.port = 389 if self.port == 0
32 32 end
33 33
34 34 def authenticate(login, password)
35 35 return nil if login.blank? || password.blank?
36 36 attrs = get_user_dn(login)
37 37
38 38 if attrs && attrs[:dn] && authenticate_dn(attrs[:dn], password)
39 39 logger.debug "Authentication successful for '#{login}'" if logger && logger.debug?
40 40 return attrs.except(:dn)
41 41 end
42 42 rescue Net::LDAP::LdapError => text
43 43 raise "LdapError: " + text
44 44 end
45 45
46 46 # test the connection to the LDAP
47 47 def test_connection
48 48 ldap_con = initialize_ldap_con(self.account, self.account_password)
49 49 ldap_con.open { }
50 50 rescue Net::LDAP::LdapError => text
51 51 raise "LdapError: " + text
52 52 end
53 53
54 54 def auth_method_name
55 55 "LDAP"
56 56 end
57 57
58 58 private
59 59
60 60 def strip_ldap_attributes
61 61 [:attr_login, :attr_firstname, :attr_lastname, :attr_mail].each do |attr|
62 62 write_attribute(attr, read_attribute(attr).strip) unless read_attribute(attr).nil?
63 63 end
64 64 end
65 65
66 66 def initialize_ldap_con(ldap_user, ldap_password)
67 67 options = { :host => self.host,
68 68 :port => self.port,
69 69 :encryption => (self.tls ? :simple_tls : nil)
70 70 }
71 71 options.merge!(:auth => { :method => :simple, :username => ldap_user, :password => ldap_password }) unless ldap_user.blank? && ldap_password.blank?
72 72 Net::LDAP.new options
73 73 end
74 74
75 75 def get_user_attributes_from_ldap_entry(entry)
76 76 {
77 77 :dn => entry.dn,
78 78 :firstname => AuthSourceLdap.get_attr(entry, self.attr_firstname),
79 79 :lastname => AuthSourceLdap.get_attr(entry, self.attr_lastname),
80 80 :mail => AuthSourceLdap.get_attr(entry, self.attr_mail),
81 81 :auth_source_id => self.id
82 82 }
83 83 end
84 84
85 85 # Return the attributes needed for the LDAP search. It will only
86 86 # include the user attributes if on-the-fly registration is enabled
87 87 def search_attributes
88 88 if onthefly_register?
89 89 ['dn', self.attr_firstname, self.attr_lastname, self.attr_mail]
90 90 else
91 91 ['dn']
92 92 end
93 93 end
94 94
95 95 # Check if a DN (user record) authenticates with the password
96 96 def authenticate_dn(dn, password)
97 97 if dn.present? && password.present?
98 98 initialize_ldap_con(dn, password).bind
99 99 end
100 100 end
101 101
102 102 # Get the user's dn and any attributes for them, given their login
103 103 def get_user_dn(login)
104 104 ldap_con = initialize_ldap_con(self.account, self.account_password)
105 105 login_filter = Net::LDAP::Filter.eq( self.attr_login, login )
106 106 object_filter = Net::LDAP::Filter.eq( "objectClass", "*" )
107 107 attrs = {}
108 108
109 109 ldap_con.search( :base => self.base_dn,
110 110 :filter => object_filter & login_filter,
111 111 :attributes=> search_attributes) do |entry|
112 112
113 113 if onthefly_register?
114 114 attrs = get_user_attributes_from_ldap_entry(entry)
115 115 else
116 116 attrs = {:dn => entry.dn}
117 117 end
118 118
119 119 logger.debug "DN found for #{login}: #{attrs[:dn]}" if logger && logger.debug?
120 120 end
121 121
122 122 attrs
123 123 end
124 124
125 125 def self.get_attr(entry, attr_name)
126 126 if !attr_name.blank?
127 127 entry[attr_name].is_a?(Array) ? entry[attr_name].first : entry[attr_name]
128 128 end
129 129 end
130 130 end
@@ -1,266 +1,277
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class Repository < ActiveRecord::Base
19 include Redmine::Ciphering
20
19 21 belongs_to :project
20 22 has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC"
21 23 has_many :changes, :through => :changesets
22 24
23 25 # Raw SQL to delete changesets and changes in the database
24 26 # has_many :changesets, :dependent => :destroy is too slow for big repositories
25 27 before_destroy :clear_changesets
26 28
29 validates_length_of :password, :maximum => 255, :allow_nil => true
27 30 # Checks if the SCM is enabled when creating a repository
28 31 validate_on_create { |r| r.errors.add(:type, :invalid) unless Setting.enabled_scm.include?(r.class.name.demodulize) }
29 32
30 33 # Removes leading and trailing whitespace
31 34 def url=(arg)
32 35 write_attribute(:url, arg ? arg.to_s.strip : nil)
33 36 end
34 37
35 38 # Removes leading and trailing whitespace
36 39 def root_url=(arg)
37 40 write_attribute(:root_url, arg ? arg.to_s.strip : nil)
38 41 end
42
43 def password
44 read_ciphered_attribute(:password)
45 end
46
47 def password=(arg)
48 write_ciphered_attribute(:password, arg)
49 end
39 50
40 51 def scm_adapter
41 52 self.class.scm_adapter_class
42 53 end
43 54
44 55 def scm
45 56 @scm ||= self.scm_adapter.new(url, root_url,
46 57 login, password, path_encoding)
47 58 update_attribute(:root_url, @scm.root_url) if root_url.blank?
48 59 @scm
49 60 end
50 61
51 62 def scm_name
52 63 self.class.scm_name
53 64 end
54 65
55 66 def supports_cat?
56 67 scm.supports_cat?
57 68 end
58 69
59 70 def supports_annotate?
60 71 scm.supports_annotate?
61 72 end
62 73
63 74 def entry(path=nil, identifier=nil)
64 75 scm.entry(path, identifier)
65 76 end
66 77
67 78 def entries(path=nil, identifier=nil)
68 79 scm.entries(path, identifier)
69 80 end
70 81
71 82 def branches
72 83 scm.branches
73 84 end
74 85
75 86 def tags
76 87 scm.tags
77 88 end
78 89
79 90 def default_branch
80 91 scm.default_branch
81 92 end
82 93
83 94 def properties(path, identifier=nil)
84 95 scm.properties(path, identifier)
85 96 end
86 97
87 98 def cat(path, identifier=nil)
88 99 scm.cat(path, identifier)
89 100 end
90 101
91 102 def diff(path, rev, rev_to)
92 103 scm.diff(path, rev, rev_to)
93 104 end
94 105
95 106 def diff_format_revisions(cs, cs_to, sep=':')
96 107 text = ""
97 108 text << cs_to.format_identifier + sep if cs_to
98 109 text << cs.format_identifier if cs
99 110 text
100 111 end
101 112
102 113 # Returns a path relative to the url of the repository
103 114 def relative_path(path)
104 115 path
105 116 end
106 117
107 118 # Finds and returns a revision with a number or the beginning of a hash
108 119 def find_changeset_by_name(name)
109 120 return nil if name.blank?
110 121 changesets.find(:first, :conditions => (name.match(/^\d*$/) ? ["revision = ?", name.to_s] : ["revision LIKE ?", name + '%']))
111 122 end
112 123
113 124 def latest_changeset
114 125 @latest_changeset ||= changesets.find(:first)
115 126 end
116 127
117 128 # Returns the latest changesets for +path+
118 129 # Default behaviour is to search in cached changesets
119 130 def latest_changesets(path, rev, limit=10)
120 131 if path.blank?
121 132 changesets.find(:all, :include => :user,
122 133 :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
123 134 :limit => limit)
124 135 else
125 136 changes.find(:all, :include => {:changeset => :user},
126 137 :conditions => ["path = ?", path.with_leading_slash],
127 138 :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
128 139 :limit => limit).collect(&:changeset)
129 140 end
130 141 end
131 142
132 143 def scan_changesets_for_issue_ids
133 144 self.changesets.each(&:scan_comment_for_issue_ids)
134 145 end
135 146
136 147 # Returns an array of committers usernames and associated user_id
137 148 def committers
138 149 @committers ||= Changeset.connection.select_rows("SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}")
139 150 end
140 151
141 152 # Maps committers username to a user ids
142 153 def committer_ids=(h)
143 154 if h.is_a?(Hash)
144 155 committers.each do |committer, user_id|
145 156 new_user_id = h[committer]
146 157 if new_user_id && (new_user_id.to_i != user_id.to_i)
147 158 new_user_id = (new_user_id.to_i > 0 ? new_user_id.to_i : nil)
148 159 Changeset.update_all("user_id = #{ new_user_id.nil? ? 'NULL' : new_user_id }", ["repository_id = ? AND committer = ?", id, committer])
149 160 end
150 161 end
151 162 @committers = nil
152 163 @found_committer_users = nil
153 164 true
154 165 else
155 166 false
156 167 end
157 168 end
158 169
159 170 # Returns the Redmine User corresponding to the given +committer+
160 171 # It will return nil if the committer is not yet mapped and if no User
161 172 # with the same username or email was found
162 173 def find_committer_user(committer)
163 174 unless committer.blank?
164 175 @found_committer_users ||= {}
165 176 return @found_committer_users[committer] if @found_committer_users.has_key?(committer)
166 177
167 178 user = nil
168 179 c = changesets.find(:first, :conditions => {:committer => committer}, :include => :user)
169 180 if c && c.user
170 181 user = c.user
171 182 elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/
172 183 username, email = $1.strip, $3
173 184 u = User.find_by_login(username)
174 185 u ||= User.find_by_mail(email) unless email.blank?
175 186 user = u
176 187 end
177 188 @found_committer_users[committer] = user
178 189 user
179 190 end
180 191 end
181 192
182 193 # Fetches new changesets for all repositories of active projects
183 194 # Can be called periodically by an external script
184 195 # eg. ruby script/runner "Repository.fetch_changesets"
185 196 def self.fetch_changesets
186 197 Project.active.has_module(:repository).find(:all, :include => :repository).each do |project|
187 198 if project.repository
188 199 begin
189 200 project.repository.fetch_changesets
190 201 rescue Redmine::Scm::Adapters::CommandFailed => e
191 202 logger.error "scm: error during fetching changesets: #{e.message}"
192 203 end
193 204 end
194 205 end
195 206 end
196 207
197 208 # scan changeset comments to find related and fixed issues for all repositories
198 209 def self.scan_changesets_for_issue_ids
199 210 find(:all).each(&:scan_changesets_for_issue_ids)
200 211 end
201 212
202 213 def self.scm_name
203 214 'Abstract'
204 215 end
205 216
206 217 def self.available_scm
207 218 subclasses.collect {|klass| [klass.scm_name, klass.name]}
208 219 end
209 220
210 221 def self.factory(klass_name, *args)
211 222 klass = "Repository::#{klass_name}".constantize
212 223 klass.new(*args)
213 224 rescue
214 225 nil
215 226 end
216 227
217 228 def self.scm_adapter_class
218 229 nil
219 230 end
220 231
221 232 def self.scm_command
222 233 ret = ""
223 234 begin
224 235 ret = self.scm_adapter_class.client_command if self.scm_adapter_class
225 236 rescue Redmine::Scm::Adapters::CommandFailed => e
226 237 logger.error "scm: error during get command: #{e.message}"
227 238 end
228 239 ret
229 240 end
230 241
231 242 def self.scm_version_string
232 243 ret = ""
233 244 begin
234 245 ret = self.scm_adapter_class.client_version_string if self.scm_adapter_class
235 246 rescue Redmine::Scm::Adapters::CommandFailed => e
236 247 logger.error "scm: error during get version string: #{e.message}"
237 248 end
238 249 ret
239 250 end
240 251
241 252 def self.scm_available
242 253 ret = false
243 254 begin
244 255 ret = self.scm_adapter_class.client_available if self.scm_adapter_class
245 256 rescue Redmine::Scm::Adapters::CommandFailed => e
246 257 logger.error "scm: error during get scm available: #{e.message}"
247 258 end
248 259 ret
249 260 end
250 261
251 262 private
252 263
253 264 def before_save
254 265 # Strips url and root_url
255 266 url.strip!
256 267 root_url.strip!
257 268 true
258 269 end
259 270
260 271 def clear_changesets
261 272 cs, ch, ci = Changeset.table_name, Change.table_name, "#{table_name_prefix}changesets_issues#{table_name_suffix}"
262 273 connection.delete("DELETE FROM #{ch} WHERE #{ch}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
263 274 connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
264 275 connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}")
265 276 end
266 277 end
@@ -1,133 +1,147
1 1 # = Redmine configuration file
2 2 #
3 3 # Each environment has it's own configuration options. If you are only
4 4 # running in production, only the production block needs to be configured.
5 5 # Environment specific configuration options override the default ones.
6 6 #
7 7 # Note that this file needs to be a valid YAML file.
8 8 #
9 9 # == Outgoing email settings (email_delivery setting)
10 10 #
11 11 # === Common configurations
12 12 #
13 13 # ==== Sendmail command
14 14 #
15 15 # production:
16 16 # email_delivery:
17 17 # delivery_method: :sendmail
18 18 #
19 19 # ==== Simple SMTP server at localhost
20 20 #
21 21 # production:
22 22 # email_delivery:
23 23 # delivery_method: :smtp
24 24 # smtp_settings:
25 25 # address: "localhost"
26 26 # port: 25
27 27 #
28 28 # ==== SMTP server at example.com using LOGIN authentication and checking HELO for foo.com
29 29 #
30 30 # production:
31 31 # email_delivery:
32 32 # delivery_method: :smtp
33 33 # smtp_settings:
34 34 # address: "example.com"
35 35 # port: 25
36 36 # authentication: :login
37 37 # domain: 'foo.com'
38 38 # user_name: 'myaccount'
39 39 # password: 'password'
40 40 #
41 41 # ==== SMTP server at example.com using PLAIN authentication
42 42 #
43 43 # production:
44 44 # email_delivery:
45 45 # delivery_method: :smtp
46 46 # smtp_settings:
47 47 # address: "example.com"
48 48 # port: 25
49 49 # authentication: :plain
50 50 # domain: 'example.com'
51 51 # user_name: 'myaccount'
52 52 # password: 'password'
53 53 #
54 54 # ==== SMTP server at using TLS (GMail)
55 55 #
56 56 # This requires some additional configuration. See the article at:
57 57 # http://redmineblog.com/articles/setup-redmine-to-send-email-using-gmail/
58 58 #
59 59 # production:
60 60 # email_delivery:
61 61 # delivery_method: :smtp
62 62 # smtp_settings:
63 63 # tls: true
64 64 # address: "smtp.gmail.com"
65 65 # port: 587
66 66 # domain: "smtp.gmail.com" # 'your.domain.com' for GoogleApps
67 67 # authentication: :plain
68 68 # user_name: "your_email@gmail.com"
69 69 # password: "your_password"
70 70 #
71 71 #
72 72 # === More configuration options
73 73 #
74 74 # See the "Configuration options" at the following website for a list of the
75 75 # full options allowed:
76 76 #
77 77 # http://wiki.rubyonrails.org/rails/pages/HowToSendEmailsWithActionMailer
78 78
79 79
80 80 # default configuration options for all environments
81 81 default:
82 82 # Outgoing emails configuration (see examples above)
83 83 email_delivery:
84 84 delivery_method: :smtp
85 85 smtp_settings:
86 86 address: smtp.example.net
87 87 port: 25
88 88 domain: example.net
89 89 authentication: :login
90 90 user_name: "redmine@example.net"
91 91 password: "redmine"
92 92
93 93 # Absolute path to the directory where attachments are stored.
94 94 # The default is the 'files' directory in your Redmine instance.
95 95 # Your Redmine instance needs to have write permission on this
96 96 # directory.
97 97 # Examples:
98 98 # attachments_storage_path: /var/redmine/files
99 99 # attachments_storage_path: D:/redmine/files
100 100 attachments_storage_path:
101 101
102 102 # Configuration of the autologin cookie.
103 103 # autologin_cookie_name: the name of the cookie (default: autologin)
104 104 # autologin_cookie_path: the cookie path (default: /)
105 105 # autologin_cookie_secure: true sets the cookie secure flag (default: false)
106 106 autologin_cookie_name:
107 107 autologin_cookie_path:
108 108 autologin_cookie_secure:
109 109
110 110 # Configuration of SCM executable command.
111 111 # Absolute path (e.g. /usr/local/bin/hg) or command name (e.g. hg.exe, bzr.exe)
112 112 # On Windows, *.cmd, *.bat (e.g. hg.cmd, bzr.bat) does not work.
113 113 # Examples:
114 114 # scm_subversion_command: svn # (default: svn)
115 115 # scm_mercurial_command: C:\Program Files\TortoiseHg\hg.exe # (default: hg)
116 116 # scm_git_command: /usr/local/bin/git # (default: git)
117 117 # scm_cvs_command: cvs # (default: cvs)
118 118 # scm_bazaar_command: bzr.exe # (default: bzr)
119 119 # scm_darcs_command: darcs-1.0.9-i386-linux # (default: darcs)
120 120 scm_subversion_command:
121 121 scm_mercurial_command:
122 122 scm_git_command:
123 123 scm_cvs_command:
124 124 scm_bazaar_command:
125 125 scm_darcs_command:
126 126
127 # Key used to encrypt sensitive data in the database (SCM and LDAP passwords).
128 # If you don't want to enable data encryption, just leave it blank.
129 # WARNING: losing/changing this key will make encrypted data unreadable.
130 #
131 # If you want to encrypt existing passwords in your database:
132 # * set the cipher key here in your configuration file
133 # * encrypt data using 'rake db:encrypt RAILS_ENV=production'
134 #
135 # If you have encrypted data and want to change this key, you have to:
136 # * decrypt data using 'rake db:decrypt RAILS_ENV=production' first
137 # * change the cipher key here in your configuration file
138 # * encrypt data using 'rake db:encrypt RAILS_ENV=production'
139 database_cipher_key:
140
127 141 # specific configuration options for production environment
128 142 # that overrides the default ones
129 143 production:
130 144
131 145 # specific configuration options for development environment
132 146 # that overrides the default ones
133 147 development:
General Comments 0
You need to be logged in to leave comments. Login now