##// END OF EJS Templates
Merged r7985 from trunk....
Jean-Philippe Lang -
r7877:5d1388db02a2
parent child
Show More
@@ -1,95 +1,95
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 module Redmine
18 module Redmine
19 module Ciphering
19 module Ciphering
20 def self.included(base)
20 def self.included(base)
21 base.extend ClassMethods
21 base.extend ClassMethods
22 end
22 end
23
23
24 class << self
24 class << self
25 def encrypt_text(text)
25 def encrypt_text(text)
26 if cipher_key.blank?
26 if cipher_key.blank? || text.blank?
27 text
27 text
28 else
28 else
29 c = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
29 c = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
30 iv = c.random_iv
30 iv = c.random_iv
31 c.encrypt
31 c.encrypt
32 c.key = cipher_key
32 c.key = cipher_key
33 c.iv = iv
33 c.iv = iv
34 e = c.update(text.to_s)
34 e = c.update(text.to_s)
35 e << c.final
35 e << c.final
36 "aes-256-cbc:" + [e, iv].map {|v| Base64.encode64(v).strip}.join('--')
36 "aes-256-cbc:" + [e, iv].map {|v| Base64.encode64(v).strip}.join('--')
37 end
37 end
38 end
38 end
39
39
40 def decrypt_text(text)
40 def decrypt_text(text)
41 if text && match = text.match(/\Aaes-256-cbc:(.+)\Z/)
41 if text && match = text.match(/\Aaes-256-cbc:(.+)\Z/)
42 text = match[1]
42 text = match[1]
43 c = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
43 c = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
44 e, iv = text.split("--").map {|s| Base64.decode64(s)}
44 e, iv = text.split("--").map {|s| Base64.decode64(s)}
45 c.decrypt
45 c.decrypt
46 c.key = cipher_key
46 c.key = cipher_key
47 c.iv = iv
47 c.iv = iv
48 d = c.update(e)
48 d = c.update(e)
49 d << c.final
49 d << c.final
50 else
50 else
51 text
51 text
52 end
52 end
53 end
53 end
54
54
55 def cipher_key
55 def cipher_key
56 key = Redmine::Configuration['database_cipher_key'].to_s
56 key = Redmine::Configuration['database_cipher_key'].to_s
57 key.blank? ? nil : Digest::SHA256.hexdigest(key)
57 key.blank? ? nil : Digest::SHA256.hexdigest(key)
58 end
58 end
59 end
59 end
60
60
61 module ClassMethods
61 module ClassMethods
62 def encrypt_all(attribute)
62 def encrypt_all(attribute)
63 transaction do
63 transaction do
64 all.each do |object|
64 all.each do |object|
65 clear = object.send(attribute)
65 clear = object.send(attribute)
66 object.send "#{attribute}=", clear
66 object.send "#{attribute}=", clear
67 raise(ActiveRecord::Rollback) unless object.save(false)
67 raise(ActiveRecord::Rollback) unless object.save(false)
68 end
68 end
69 end ? true : false
69 end ? true : false
70 end
70 end
71
71
72 def decrypt_all(attribute)
72 def decrypt_all(attribute)
73 transaction do
73 transaction do
74 all.each do |object|
74 all.each do |object|
75 clear = object.send(attribute)
75 clear = object.send(attribute)
76 object.write_attribute attribute, clear
76 object.write_attribute attribute, clear
77 raise(ActiveRecord::Rollback) unless object.save(false)
77 raise(ActiveRecord::Rollback) unless object.save(false)
78 end
78 end
79 end
79 end
80 end ? true : false
80 end ? true : false
81 end
81 end
82
82
83 private
83 private
84
84
85 # Returns the value of the given ciphered attribute
85 # Returns the value of the given ciphered attribute
86 def read_ciphered_attribute(attribute)
86 def read_ciphered_attribute(attribute)
87 Redmine::Ciphering.decrypt_text(read_attribute(attribute))
87 Redmine::Ciphering.decrypt_text(read_attribute(attribute))
88 end
88 end
89
89
90 # Sets the value of the given ciphered attribute
90 # Sets the value of the given ciphered attribute
91 def write_ciphered_attribute(attribute, value)
91 def write_ciphered_attribute(attribute, value)
92 write_attribute(attribute, Redmine::Ciphering.encrypt_text(value))
92 write_attribute(attribute, Redmine::Ciphering.encrypt_text(value))
93 end
93 end
94 end
94 end
95 end
95 end
@@ -1,84 +1,92
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../../../test_helper', __FILE__)
18 require File.expand_path('../../../../test_helper', __FILE__)
19
19
20 class Redmine::CipheringTest < ActiveSupport::TestCase
20 class Redmine::CipheringTest < ActiveSupport::TestCase
21
21
22 def test_password_should_be_encrypted
22 def test_password_should_be_encrypted
23 Redmine::Configuration.with 'database_cipher_key' => 'secret' do
23 Redmine::Configuration.with 'database_cipher_key' => 'secret' do
24 r = Repository::Subversion.generate!(:password => 'foo')
24 r = Repository::Subversion.generate!(:password => 'foo')
25 assert_equal 'foo', r.password
25 assert_equal 'foo', r.password
26 assert r.read_attribute(:password).match(/\Aaes-256-cbc:.+\Z/)
26 assert r.read_attribute(:password).match(/\Aaes-256-cbc:.+\Z/)
27 end
27 end
28 end
28 end
29
29
30 def test_password_should_be_clear_with_blank_key
30 def test_password_should_be_clear_with_blank_key
31 Redmine::Configuration.with 'database_cipher_key' => '' do
31 Redmine::Configuration.with 'database_cipher_key' => '' do
32 r = Repository::Subversion.generate!(:password => 'foo')
32 r = Repository::Subversion.generate!(:password => 'foo')
33 assert_equal 'foo', r.password
33 assert_equal 'foo', r.password
34 assert_equal 'foo', r.read_attribute(:password)
34 assert_equal 'foo', r.read_attribute(:password)
35 end
35 end
36 end
36 end
37
37
38 def test_password_should_be_clear_with_nil_key
38 def test_password_should_be_clear_with_nil_key
39 Redmine::Configuration.with 'database_cipher_key' => nil do
39 Redmine::Configuration.with 'database_cipher_key' => nil do
40 r = Repository::Subversion.generate!(:password => 'foo')
40 r = Repository::Subversion.generate!(:password => 'foo')
41 assert_equal 'foo', r.password
41 assert_equal 'foo', r.password
42 assert_equal 'foo', r.read_attribute(:password)
42 assert_equal 'foo', r.read_attribute(:password)
43 end
43 end
44 end
44 end
45
45
46 def test_blank_password_should_be_clear
47 Redmine::Configuration.with 'database_cipher_key' => 'secret' do
48 r = Repository::Subversion.generate!(:password => '')
49 assert_equal '', r.password
50 assert_equal '', r.read_attribute(:password)
51 end
52 end
53
46 def test_unciphered_password_should_be_readable
54 def test_unciphered_password_should_be_readable
47 Redmine::Configuration.with 'database_cipher_key' => nil do
55 Redmine::Configuration.with 'database_cipher_key' => nil do
48 r = Repository::Subversion.generate!(:password => 'clear')
56 r = Repository::Subversion.generate!(:password => 'clear')
49 end
57 end
50
58
51 Redmine::Configuration.with 'database_cipher_key' => 'secret' do
59 Redmine::Configuration.with 'database_cipher_key' => 'secret' do
52 r = Repository.first(:order => 'id DESC')
60 r = Repository.first(:order => 'id DESC')
53 assert_equal 'clear', r.password
61 assert_equal 'clear', r.password
54 end
62 end
55 end
63 end
56
64
57 def test_encrypt_all
65 def test_encrypt_all
58 Repository.delete_all
66 Repository.delete_all
59 Redmine::Configuration.with 'database_cipher_key' => nil do
67 Redmine::Configuration.with 'database_cipher_key' => nil do
60 Repository::Subversion.generate!(:password => 'foo')
68 Repository::Subversion.generate!(:password => 'foo')
61 Repository::Subversion.generate!(:password => 'bar')
69 Repository::Subversion.generate!(:password => 'bar')
62 end
70 end
63
71
64 Redmine::Configuration.with 'database_cipher_key' => 'secret' do
72 Redmine::Configuration.with 'database_cipher_key' => 'secret' do
65 assert Repository.encrypt_all(:password)
73 assert Repository.encrypt_all(:password)
66 r = Repository.first(:order => 'id DESC')
74 r = Repository.first(:order => 'id DESC')
67 assert_equal 'bar', r.password
75 assert_equal 'bar', r.password
68 assert r.read_attribute(:password).match(/\Aaes-256-cbc:.+\Z/)
76 assert r.read_attribute(:password).match(/\Aaes-256-cbc:.+\Z/)
69 end
77 end
70 end
78 end
71
79
72 def test_decrypt_all
80 def test_decrypt_all
73 Repository.delete_all
81 Repository.delete_all
74 Redmine::Configuration.with 'database_cipher_key' => 'secret' do
82 Redmine::Configuration.with 'database_cipher_key' => 'secret' do
75 Repository::Subversion.generate!(:password => 'foo')
83 Repository::Subversion.generate!(:password => 'foo')
76 Repository::Subversion.generate!(:password => 'bar')
84 Repository::Subversion.generate!(:password => 'bar')
77
85
78 assert Repository.decrypt_all(:password)
86 assert Repository.decrypt_all(:password)
79 r = Repository.first(:order => 'id DESC')
87 r = Repository.first(:order => 'id DESC')
80 assert_equal 'bar', r.password
88 assert_equal 'bar', r.password
81 assert_equal 'bar', r.read_attribute(:password)
89 assert_equal 'bar', r.read_attribute(:password)
82 end
90 end
83 end
91 end
84 end
92 end
General Comments 0
You need to be logged in to leave comments. Login now