cloud_encrypted_sync 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cloud_encrypted_sync (0.0.1)
4
+ cloud_encrypted_sync (0.1.1)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
data/README.md CHANGED
@@ -23,10 +23,6 @@ preferred cloud.
23
23
  CES runs as a command line tool and takes options as CLI arguments and/or from a config file.
24
24
  Arguments passed at the command line take precedence over those in the config file.
25
25
 
26
- ### Creating a valid encryption key and initialization vector.
27
-
28
- TODO
29
-
30
26
  ### Example
31
27
 
32
28
  ces --adapter=s3 --bucket=my-backup-bucket \
@@ -0,0 +1,57 @@
1
+ module CloudEncryptedSync
2
+ class AdapterLiaison
3
+ include Singleton
4
+
5
+ def initialize
6
+ find_and_require_adapters
7
+ end
8
+
9
+ def push(data,key)
10
+ adapter.write(Cryptographer.encrypt_data(data),key)
11
+ end
12
+
13
+ def pull(key)
14
+ Cryptographer.decrypt_data(adapter.read(key))
15
+ end
16
+
17
+ def delete(key)
18
+ adapter.delete(key)
19
+ end
20
+
21
+ def key_exists?(key)
22
+ adapter.key_exists?(key)
23
+ end
24
+
25
+ def adapters
26
+ Adapters::Template.children
27
+ end
28
+
29
+ #######
30
+ private
31
+ #######
32
+
33
+ def find_and_require_adapters
34
+ latest_versions_of_installed_adapters.each_pair do |adapter_name,adapter_version|
35
+ require File.expand_path("../../../../cloud_encrypted_sync_#{adapter_name}_adapter-#{adapter_version}", __FILE__)
36
+ end
37
+ end
38
+
39
+ def latest_versions_of_installed_adapters
40
+ glob_path = '../../../../cloud_encrypted_sync_*_adapter-*/lib/*.rb'
41
+ Dir.glob(File.expand_path(glob_path,__FILE__)).inject({}) do |hash,adapter_path|
42
+ if adapter_path.match(/cloud_encrypted_sync_(.+)_adapter-(.+)/)
43
+ adapter_name = $1
44
+ adapter_version = $2
45
+ if hash[adapter_name].to_s < adapter_version
46
+ hash[adapter_name] = adapter_version
47
+ end
48
+ end
49
+ hash
50
+ end
51
+ end
52
+
53
+ def adapter
54
+ adapters[Configuration.settings[:adapter_name].to_sym]
55
+ end
56
+ end
57
+ end
@@ -5,29 +5,49 @@ module CloudEncryptedSync
5
5
  class << self
6
6
 
7
7
  def inherited(subclass)
8
- Master.register(subclass)
8
+ register_subclass_with_parent(subclass)
9
+ end
10
+
11
+ def children
12
+ @children ||= {}
9
13
  end
10
14
 
11
15
  def parse_command_line_options(opts,command_line_options)
12
- raise 'called template method: parse_command_line_options'
16
+ raise Errors::TemplateMethodCalled.new('parse_command_line_options')
13
17
  end
14
18
 
15
19
  def write(data, key)
16
- raise 'called template method: write'
20
+ raise Errors::TemplateMethodCalled.new('write')
17
21
  end
18
22
 
19
23
  def read(key)
20
- raise 'called template method: read'
24
+ raise Errors::TemplateMethodCalled.new('read')
21
25
  end
22
26
 
23
27
  def delete(key)
24
- raise 'called template method: delete'
28
+ raise Errors::TemplateMethodCalled.new('delete')
25
29
  end
26
30
 
27
31
  def key_exists?(key)
28
- raise 'called template method: key_exists?'
32
+ raise Errors::TemplateMethodCalled.new('key_exists?')
29
33
  end
30
34
 
35
+ #######
36
+ private
37
+ #######
38
+
39
+ def register_subclass_with_parent(subclass)
40
+ name = formated_name_of(subclass)
41
+ if children[name]
42
+ raise Errors::RegistrationError.new("#{name} already registered")
43
+ else
44
+ children[name] = subclass
45
+ end
46
+ end
47
+
48
+ def formated_name_of(subclass)
49
+ subclass.name.match(/([^:]+)$/)[0].underscore.to_sym
50
+ end
31
51
  end
32
52
  end
33
53
  end
@@ -1,5 +1,3 @@
1
- require 'yaml'
2
-
3
1
  module CloudEncryptedSync
4
2
  class Configuration
5
3
 
@@ -32,10 +30,10 @@ module CloudEncryptedSync
32
30
 
33
31
  if loaded_settings[:sync_path].nil?
34
32
  message = "You must supply a path to a folder to sync.\n\n#{option_parser.help}"
35
- raise IncompleteConfigurationError.new(message)
33
+ raise Errors::IncompleteConfigurationError.new(message)
36
34
  elsif loaded_settings[:encryption_key].nil? or loaded_settings[:encryption_key].empty?
37
35
  message = "You must supply an encryption key.\n\n#{option_parser.help}"
38
- raise IncompleteConfigurationError.new(message)
36
+ raise Errors::IncompleteConfigurationError.new(message)
39
37
  end
40
38
 
41
39
  return loaded_settings
@@ -64,7 +62,7 @@ module CloudEncryptedSync
64
62
  end
65
63
  opts.on('--adapter ADAPTERNAME', 'Name of cloud adapter to use.') do |adapter_name|
66
64
  clo[:adapter_name] = adapter_name
67
- clo = Master.adapters[adapter_name.to_sym].parse_command_line_options(opts,clo)
65
+ clo = AdapterLiaison.instance.adapters[adapter_name.to_sym].parse_command_line_options(opts,clo)
68
66
  end
69
67
  opts.on('--encryption-key KEY') do |key|
70
68
  clo[:encryption_key] = key
@@ -77,6 +75,4 @@ module CloudEncryptedSync
77
75
 
78
76
  end
79
77
  end
80
-
81
- class IncompleteConfigurationError < RuntimeError; end
82
78
  end
@@ -1,6 +1,3 @@
1
- require 'openssl'
2
- require 'digest'
3
-
4
1
  module CloudEncryptedSync
5
2
  class Cryptographer
6
3
  ALGORITHM = 'AES-256-CBC'
@@ -16,7 +16,7 @@ module CloudEncryptedSync
16
16
  end
17
17
 
18
18
  def read(key)
19
- raise "key doesn't exist" unless key_exists?(key)
19
+ raise Errors::NoSuchKey.new("key doesn't exist: #{key}") unless key_exists?(key)
20
20
  stored_data[bucket_name][key]
21
21
  end
22
22
 
@@ -37,7 +37,6 @@ module CloudEncryptedSync
37
37
  end
38
38
 
39
39
  def bucket_name
40
- raise RuntimeError, Configuration.settings.inspect
41
40
  Configuration.settings[:bucket_name].to_sym
42
41
  end
43
42
 
@@ -0,0 +1,8 @@
1
+ module CloudEncryptedSync
2
+ module Errors
3
+ class RegistrationError < RuntimeError; end
4
+ class IncompleteConfigurationError < RuntimeError; end
5
+ class NoSuchKey < RuntimeError; end
6
+ class TemplateMethodCalled < RuntimeError; end
7
+ end
8
+ end
@@ -0,0 +1,86 @@
1
+ module CloudEncryptedSync
2
+ class Index
3
+
4
+ class << self
5
+
6
+ def local
7
+ @local ||= compile_local_hash
8
+ end
9
+
10
+ def remote
11
+ @remote ||= begin
12
+ YAML.parse(liaison.pull(index_key)).to_ruby
13
+ rescue Errors::NoSuchKey
14
+ {}
15
+ end
16
+ end
17
+
18
+ def write
19
+ local_hash = compile_local_hash #recompile
20
+ liaison.push(local_hash.to_yaml,index_key) #push to remote
21
+ File.open(snapshot_path, 'w') { |file| YAML.dump(local_hash, file) } #save to local
22
+ end
23
+
24
+ def full_file_path(relative_path)
25
+ normalized_sync_path+'/'+relative_path
26
+ end
27
+
28
+ def snapshot_path
29
+ "#{Configuration.data_folder_path}/#{snapshot_filename}"
30
+ end
31
+
32
+ def file_key(full_path)
33
+ Cryptographer.hash_data(relative_file_path(full_path) + File.open(full_path).read).to_s
34
+ end
35
+
36
+ #######
37
+ private
38
+ #######
39
+
40
+ def liaison
41
+ AdapterLiaison.instance
42
+ end
43
+
44
+ def compile_local_hash
45
+ hash = {}
46
+ progress_meter = ProgressMeter.new(Dir["#{normalized_sync_path}/**/*"].length,:label => 'Compiling Local Index: ')
47
+ completed_files = 0
48
+ Find.find(normalized_sync_path) do |path|
49
+ print progress_meter.update(completed_files)
50
+ unless FileTest.directory?(path)
51
+ hash[file_key(path)] = relative_file_path(path)
52
+ end
53
+ completed_files += 1
54
+ end
55
+ puts #newline for progress meter
56
+ return hash
57
+ end
58
+
59
+ def index_key
60
+ @index_key ||= Cryptographer.hash_data(Configuration.settings[:encryption_key])
61
+ end
62
+
63
+ def snapshot_filename
64
+ "#{normalized_sync_path.gsub(/[^A-Za-z0-9]/,'_')}.index.yml"
65
+ end
66
+
67
+ def relative_file_path(full_path)
68
+ full_path.gsub(normalized_sync_path,'')
69
+ end
70
+
71
+ def normalized_sync_path
72
+ @normalized_sync_path ||= normalize_sync_path
73
+ end
74
+
75
+ def normalize_sync_path
76
+ path = Configuration.settings[:sync_path]
77
+ if path.match(/\/$/)
78
+ return path
79
+ else
80
+ return path + '/'
81
+ end
82
+ end
83
+
84
+ end
85
+ end
86
+ end
@@ -1,23 +1,10 @@
1
- require 'find'
2
- require 'active_support/core_ext/string'
3
-
4
1
  module CloudEncryptedSync
5
2
  class Master
6
3
 
7
4
  class << self
8
5
  attr_accessor :finalize_required
9
- attr_reader :command_line_options, :adapters
10
- attr_writer :sync_path
11
-
12
- def register(adapter)
13
- @adapters ||= {}
14
- name = adapter.name.match(/([^:]+)$/)[0].underscore.to_sym
15
- raise RegistrationError, "#{name} already registered" if @adapters[name]
16
- @adapters[name] = adapter
17
- end
18
6
 
19
7
  def activate!
20
- find_and_require_adapters
21
8
  sync
22
9
  end
23
10
 
@@ -28,7 +15,7 @@ module CloudEncryptedSync
28
15
  CloudEncryptedSync::Master.pull_files!
29
16
  CloudEncryptedSync::Master.push_files!
30
17
  CloudEncryptedSync::Master.finalize!
31
- rescue IncompleteConfigurationError => exception
18
+ rescue Errors::IncompleteConfigurationError => exception
32
19
  puts exception.message
33
20
  end
34
21
  end
@@ -37,12 +24,13 @@ module CloudEncryptedSync
37
24
  progress_meter = ProgressMeter.new(files_to_pull.keys.size,:label => 'Pushing Files: ')
38
25
  pushed_files_counter = 0
39
26
  files_to_push.each_pair do |key,relative_path|
40
- if adapter.key_exists?(key)
27
+ puts #newline for progress meter
28
+ if liaison.key_exists?(key)
41
29
  #already exists. probably left over from an earlier aborted push
42
30
  puts "Not Pushing (already exists): #{relative_path}"
43
31
  else
44
32
  puts "Pushing: #{relative_path}"
45
- encrypt_to_adapter(File.read(full_file_path(relative_path)),key)
33
+ liaison.push(File.read(Index.full_file_path(relative_path)),key)
46
34
  self.finalize_required = true
47
35
  end
48
36
  pushed_files_counter += 1
@@ -54,17 +42,18 @@ module CloudEncryptedSync
54
42
  progress_meter = ProgressMeter.new(files_to_pull.keys.size,:label => 'Pulling Files: ')
55
43
  pulled_files_counter = 0
56
44
  files_to_pull.each_pair do |key,relative_path|
57
- full_path = full_file_path(relative_path)
58
- if File.exist?(full_path) and (file_key(full_path) == key)
45
+ full_path = Index.full_file_path(relative_path)
46
+ puts #newline for progress meter
47
+ if File.exist?(full_path) and (Index.file_key(full_path) == key)
59
48
  #already exists. probably left over from an earlier aborted pull
60
49
  puts "Not Pulling (already exists): #{path}"
61
50
  else
62
51
  Dir.mkdir(File.dirname(full_path)) unless File.exist?(File.dirname(full_path))
63
52
  puts "Pulling: #{relative_path}"
64
53
  begin
65
- File.open(full_path,'w') { |file| file.write(decrypt_from_adapter(key)) }
54
+ File.open(full_path,'w') { |file| file.write(liaison.pull(key)) }
66
55
  self.finalize_required = true
67
- rescue #AWS::S3::Errors::NoSuchKey Should provide error for adapters to raise
56
+ rescue Errors::NoSuchKey
68
57
  puts "Failed to pull #{relative_path}"
69
58
  end
70
59
  end
@@ -76,15 +65,15 @@ module CloudEncryptedSync
76
65
  def delete_remote_files!
77
66
  remote_files_to_delete.each_pair do |key,path|
78
67
  puts "Deleting Remote: #{path}"
79
- adapter.delete(key)
68
+ liaison.delete(key)
80
69
  self.finalize_required = true
81
70
  end
82
71
  end
83
72
 
84
73
  def delete_local_files!
85
74
  local_files_to_delete.each_pair do |key,relative_path|
86
- full_path = full_file_path(relative_path)
87
- if !File.exist?(full_path) or (file_key(full_path) == key)
75
+ full_path = Index.full_file_path(relative_path)
76
+ if !File.exist?(full_path) or (Index.file_key(full_path) == key)
88
77
  puts "Not Deleting Local: #{relative_path}"
89
78
  else
90
79
  puts "Deleting Local: #{relative_path}"
@@ -95,119 +84,39 @@ module CloudEncryptedSync
95
84
  end
96
85
 
97
86
  def finalize!
98
- if finalize_required
99
- store_directory_hash_file
100
- File.open(snapshot_file_path, 'w') { |file| YAML.dump(directory_hash, file) }
101
- end
87
+ Index.write if finalize_required
102
88
  end
103
89
 
104
90
  #######
105
91
  private
106
92
  #######
107
93
 
108
- def find_and_require_adapters
109
- latest_versions_of_installed_adapters.each_pair do |adapter_name,adapter_version|
110
- require File.expand_path("../../../../cloud_encrypted_sync_#{adapter_name}_adapter-#{adapter_version}", __FILE__)
111
- end
112
- end
113
-
114
- def latest_versions_of_installed_adapters
115
- glob_path = '../../../../cloud_encrypted_sync_*_adapter-*/lib/*.rb'
116
- Dir.glob(File.expand_path(glob_path,__FILE__)).inject({}) do |hash,adapter_path|
117
- if adapter_path.match(/cloud_encrypted_sync_(.+)_adapter-(.+)/)
118
- adapter_name = $1
119
- adapter_version = $2
120
- if hash[adapter_name].to_s < adapter_version
121
- hash[adapter_name] = adapter_version
122
- end
123
- end
124
- hash
125
- end
126
- end
127
-
128
- def adapter
129
- @adapters[Configuration.settings[:adapter_name].to_sym]
130
- end
131
-
132
- def encrypt_to_adapter(data,key)
133
- adapter.write(Cryptographer.encrypt_data(data),key)
134
- end
135
-
136
- def decrypt_from_adapter(key)
137
- Cryptographer.decrypt_data(adapter.read(key))
138
- end
139
-
140
- def directory_hash
141
- return @directory_hash if @directory_hash
142
- @directory_hash = {}
143
- progress_meter = ProgressMeter.new(Dir["#{normalized_sync_path}/**/*"].length,:label => 'Compiling Directory Analysis: ')
144
- completed_files = 0
145
- Find.find(normalized_sync_path) do |path|
146
- print progress_meter.update(completed_files)
147
- if FileTest.directory?(path)
148
- completed_files += 1
149
- next
150
- else
151
- @directory_hash[file_key(path)] = relative_file_path(path)
152
- completed_files += 1
153
- end
154
- end
155
- puts
156
- return @directory_hash
157
- end
158
-
159
- def directory_key
160
- @directory_key ||= Cryptographer.hash_data(Configuration.settings[:encryption_key])
161
- end
162
-
163
- def normalized_sync_path
164
- @normalized_sync_path ||= normalize_sync_path
165
- end
166
-
167
- def normalize_sync_path
168
- path = Configuration.settings[:sync_path]
169
- if path.match(/\/$/)
170
- return path
171
- else
172
- return path + '/'
173
- end
94
+ def liaison
95
+ AdapterLiaison.instance
174
96
  end
175
97
 
176
98
  def last_sync_date
177
- @last_sync_date ||= File.exist?(snapshot_file_path) ? File.stat(snapshot_file_path).ctime : nil
99
+ @last_sync_date ||= File.exist?(Index.snapshot_path) ? File.stat(Index.snapshot_path).ctime : nil
178
100
  end
179
101
 
180
102
  def last_sync_hash
181
- @last_sync_hash ||= File.exist?(snapshot_file_path) ? YAML.load(File.read(snapshot_file_path)) : {}
103
+ @last_sync_hash ||= File.exist?(Index.snapshot_path) ? YAML.load(File.read(Index.snapshot_path)) : {}
182
104
  end
183
105
 
184
106
  def files_to_push
185
- syncable_files_check(directory_hash,remote_directory_hash)
107
+ syncable_files_check(Index.local,Index.remote)
186
108
  end
187
109
 
188
110
  def files_to_pull
189
- syncable_files_check(remote_directory_hash,directory_hash)
111
+ syncable_files_check(Index.remote,Index.local)
190
112
  end
191
113
 
192
114
  def remote_files_to_delete
193
- deletable_files_check(remote_directory_hash,directory_hash)
115
+ deletable_files_check(Index.remote,Index.local)
194
116
  end
195
117
 
196
118
  def local_files_to_delete
197
- deletable_files_check(directory_hash,remote_directory_hash)
198
- end
199
-
200
- def remote_directory_hash
201
- @remote_directory_hash ||= begin
202
- YAML.parse(decrypt_from_adapter(directory_key)).to_ruby
203
- rescue #AWS::S3::Errors::NoSuchKey should provide error for adapters to raise
204
- {}
205
- end
206
- end
207
-
208
- def store_directory_hash_file
209
- @directory_hash = nil #force re-compile before pushing to remote
210
- encrypt_to_adapter(directory_hash.to_yaml,directory_key)
119
+ deletable_files_check(Index.local,Index.remote)
211
120
  end
212
121
 
213
122
  def deletable_files_check(source_hash,comparison_hash)
@@ -222,27 +131,6 @@ module CloudEncryptedSync
222
131
  source_hash.select{|k,v| !comparison_hash.has_key?(k) and (last_sync_has_key ? last_sync_hash.has_key?(k) : !last_sync_hash.has_key?(k)) }
223
132
  end
224
133
 
225
- def snapshot_file_path
226
- "#{Configuration.data_folder_path}/#{snapshot_filename}"
227
- end
228
-
229
- def snapshot_filename
230
- "#{normalized_sync_path.gsub(/[^A-Za-z0-9]/,'_')}.snapshot.yml"
231
- end
232
-
233
- def file_key(full_path)
234
- Cryptographer.hash_data(relative_file_path(full_path) + File.open(full_path).read).to_s
235
- end
236
-
237
- def relative_file_path(full_path)
238
- full_path.gsub(normalized_sync_path,'')
239
- end
240
-
241
- def full_file_path(relative_path)
242
- normalized_sync_path+'/'+relative_path
243
- end
244
134
  end
245
135
  end
246
-
247
- class RegistrationError < RuntimeError; end
248
136
  end
@@ -1,3 +1,3 @@
1
1
  module CloudEncryptedSync
2
- VERSION = '0.1.0'
2
+ VERSION = '0.1.1'
3
3
  end
@@ -1,6 +1,17 @@
1
+ require 'singleton'
2
+ require 'yaml'
3
+ require 'openssl'
4
+ require 'digest'
5
+ require 'find'
6
+ require 'active_support/core_ext/string'
7
+
1
8
  require File.expand_path('../cloud_encrypted_sync/master', __FILE__)
9
+
10
+ require File.expand_path('../cloud_encrypted_sync/adapter_liaison', __FILE__)
11
+ require File.expand_path('../cloud_encrypted_sync/adapter_template', __FILE__)
2
12
  require File.expand_path('../cloud_encrypted_sync/configuration', __FILE__)
3
13
  require File.expand_path('../cloud_encrypted_sync/cryptographer', __FILE__)
4
- require File.expand_path('../cloud_encrypted_sync/adapter_template', __FILE__)
5
14
  require File.expand_path('../cloud_encrypted_sync/dummy_adapter', __FILE__)
15
+ require File.expand_path('../cloud_encrypted_sync/errors', __FILE__)
16
+ require File.expand_path('../cloud_encrypted_sync/index', __FILE__)
6
17
  require File.expand_path('../cloud_encrypted_sync/progress_meter', __FILE__)
data/test/test_helper.rb CHANGED
@@ -22,7 +22,7 @@ module CloudEncryptedSync
22
22
  def preset_environment
23
23
  Configuration.instance_variable_set(:@settings,nil)
24
24
  Configuration.instance_variable_set(:@command_line_options,nil)
25
- Master.instance_variable_set(:@directory_hash, nil)
25
+ Index.instance_variable_set(:@local, nil)
26
26
  FileUtils.mkdir_p test_source_folder
27
27
  FileUtils.mkdir_p test_source_folder + '/test_sub_folder'
28
28
  File.open(test_source_folder + '/test_sub_folder/test_file_one.txt', 'w') do |test_file|
@@ -0,0 +1,32 @@
1
+ require 'test_helper'
2
+
3
+ module CloudEncryptedSync
4
+ class AdapterLiaisonTest < ActiveSupport::TestCase
5
+
6
+ def setup
7
+ Configuration.stubs(:settings).returns({
8
+ :encryption_key => 'asdf',
9
+ :adapter_name => 'dummy',
10
+ :bucket => "test-bucket",
11
+ :sync_path => test_source_folder
12
+ })
13
+ Configuration.stubs(:data_folder_path).returns("#{Etc.getpwuid.dir}/.cloud_encrypted_sync")
14
+ end
15
+
16
+ test 'should encrypt when writing' do
17
+ precrypted_data = File.read(test_source_folder + '/test_sub_folder/test_file_one.txt')
18
+ key = Cryptographer.hash_data('test_file_key')
19
+ Adapters::Dummy.expects(:write).with(anything,key).returns(true)
20
+ AdapterLiaison.instance.push(precrypted_data,key)
21
+ end
22
+
23
+ test 'should decrypt_when_reading' do
24
+ precrypted_data = File.read(test_source_folder + '/test_sub_folder/test_file_one.txt')
25
+ encrypted_data = Cryptographer.encrypt_data(precrypted_data)
26
+ key = Cryptographer.hash_data('test_file_key')
27
+ Adapters::Dummy.expects(:read).with(key).returns(encrypted_data)
28
+ assert_equal(precrypted_data,AdapterLiaison.instance.pull(key))
29
+ end
30
+
31
+ end
32
+ end
@@ -21,12 +21,12 @@ module CloudEncryptedSync
21
21
 
22
22
  test 'should gracefully fail without path in ARGV' do
23
23
  ::ARGV = '--adapter dummy --bucket foobar'.split(/\s/)
24
- assert_raise(IncompleteConfigurationError) { Configuration.settings }
24
+ assert_raise(Errors::IncompleteConfigurationError) { Configuration.settings }
25
25
  end
26
26
 
27
27
  test 'should gracefully fail when not provided encryption_key and vector provided path in ARGV' do
28
28
  ::ARGV = '--adapter dummy --bucket foobar /some/path/to/sync'.split(/\s/)
29
- assert_raise(IncompleteConfigurationError) { Configuration.settings }
29
+ assert_raise(Errors::IncompleteConfigurationError) { Configuration.settings }
30
30
  end
31
31
 
32
32
  end
@@ -6,7 +6,6 @@ module CloudEncryptedSync
6
6
  def setup
7
7
  Configuration.stubs(:settings).returns({
8
8
  :encryption_key => 'asdf',
9
- :initialization_vector => 'qwerty',
10
9
  :adapter_name => 'dummy',
11
10
  :bucket => "test-bucket",
12
11
  :data_dir => "#{Etc.getpwuid.dir}/.cloud_encrypted_sync",
@@ -1,5 +1,4 @@
1
1
  require 'test_helper'
2
- require 'yaml'
3
2
 
4
3
  module CloudEncryptedSync
5
4
  class MasterTest < ActiveSupport::TestCase
@@ -7,7 +6,6 @@ module CloudEncryptedSync
7
6
  def setup
8
7
  Configuration.stubs(:settings).returns({
9
8
  :encryption_key => 'asdf',
10
- :initialization_vector => 'qwerty',
11
9
  :adapter_name => 'dummy',
12
10
  :bucket => "test-bucket",
13
11
  :sync_path => test_source_folder
@@ -17,10 +15,10 @@ module CloudEncryptedSync
17
15
 
18
16
  test 'should generate directory hash' do
19
17
  assert_equal('',$stdout.string)
20
- hash = Master.send(:directory_hash)
21
- assert_match(/\% Complete/,$stdout.string)
18
+ hash = Index.local
22
19
  assert_equal(1,hash.keys.size)
23
20
  assert_equal('test_sub_folder/test_file_one.txt',hash[hash.keys.first])
21
+ assert_match(/\% Complete/,$stdout.string)
24
22
  end
25
23
 
26
24
  test 'should_return_nil_if_never_synced_before' do
@@ -29,25 +27,10 @@ module CloudEncryptedSync
29
27
  end
30
28
 
31
29
  test 'should want to push everything on first run with local files and empty remote' do
32
- Master.stubs(:remote_directory_hash).returns({})
33
- Master.stubs(:directory_hash).returns({"old_file_key"=>"test_sub_folder/old_file.txt"})
30
+ Index.stubs(:remote).returns({})
31
+ Index.stubs(:local).returns({"old_file_key"=>"test_sub_folder/old_file.txt"})
34
32
  Master.stubs(:last_sync_hash).returns({})
35
- assert_equal(Master.directory_hash,Master.send(:files_to_push))
36
- end
37
-
38
- test 'should encrypt when writing' do
39
- precrypted_data = File.read(test_source_folder + '/test_sub_folder/test_file_one.txt')
40
- key = Cryptographer.hash_data('test_file_key')
41
- Adapters::Dummy.expects(:write).with(anything,key).returns(true)
42
- Master.send(:encrypt_to_adapter,precrypted_data,key)
43
- end
44
-
45
- test 'should decrypt_when_reading' do
46
- precrypted_data = File.read(test_source_folder + '/test_sub_folder/test_file_one.txt')
47
- encrypted_data = Cryptographer.encrypt_data(precrypted_data)
48
- key = Cryptographer.hash_data('test_file_key')
49
- Adapters::Dummy.expects(:read).with(key).returns(encrypted_data)
50
- assert_equal(precrypted_data,Master.send(:decrypt_from_adapter,key))
33
+ assert_equal(Index.local,Master.send(:files_to_push))
51
34
  end
52
35
 
53
36
  test 'should push files' do
@@ -61,15 +44,15 @@ module CloudEncryptedSync
61
44
  end
62
45
 
63
46
  test 'should want to pull everything on first run with remote files and empty local' do
64
- Master.stubs(:remote_directory_hash).returns({'new_file_key' => 'test_sub_folder/new_file.txt'})
65
- Master.stubs(:directory_hash).returns({})
47
+ Index.stubs(:remote).returns({'new_file_key' => 'test_sub_folder/new_file.txt'})
48
+ Index.stubs(:local).returns({})
66
49
  Master.stubs(:last_sync_hash).returns({})
67
50
  assert_equal({'new_file_key' => 'test_sub_folder/new_file.txt'},Master.send(:files_to_pull))
68
51
  end
69
52
 
70
53
  test 'should pull files' do
71
- Master.stubs(:remote_directory_hash).returns({'new_file_key' => 'test_sub_folder/new_file.txt'})
72
- Master.stubs(:directory_hash).returns({})
54
+ Index.stubs(:remote).returns({'new_file_key' => 'test_sub_folder/new_file.txt'})
55
+ Index.stubs(:local).returns({})
73
56
  Master.stubs(:last_sync_hash).returns({})
74
57
  Adapters::Dummy.expects(:read).with('new_file_key').returns(Cryptographer.encrypt_data('foobar'))
75
58
  assert_equal('',$stdout.string)
@@ -80,44 +63,44 @@ module CloudEncryptedSync
80
63
  end
81
64
 
82
65
  test 'should only want to push new files on later run' do
83
- Master.stubs(:remote_directory_hash).returns({'old_file_key' => 'test_sub_folder/old_file.txt'})
84
- Master.stubs(:directory_hash).returns({'new_file_key' => 'test_sub_folder/new_file.txt', 'old_file_key' => 'test_sub_folder/old_file.txt'})
66
+ Index.stubs(:remote).returns({'old_file_key' => 'test_sub_folder/old_file.txt'})
67
+ Index.stubs(:local).returns({'new_file_key' => 'test_sub_folder/new_file.txt', 'old_file_key' => 'test_sub_folder/old_file.txt'})
85
68
  Master.stubs(:last_sync_hash).returns({'old_file_key' => 'test_sub_folder/old_file.txt'})
86
69
  assert_equal({'new_file_key' => 'test_sub_folder/new_file.txt'},Master.send(:files_to_push))
87
70
  end
88
71
 
89
72
  test 'should want to pull new files from cloud' do
90
- Master.stubs(:remote_directory_hash).returns({'new_file_key' => 'test_sub_folder/new_file.txt', 'old_file_key' => 'test_sub_folder/old_file.txt'})
91
- Master.stubs(:directory_hash).returns({'old_file_key' => 'test_sub_folder/old_file.txt'})
73
+ Index.stubs(:remote).returns({'new_file_key' => 'test_sub_folder/new_file.txt', 'old_file_key' => 'test_sub_folder/old_file.txt'})
74
+ Index.stubs(:local).returns({'old_file_key' => 'test_sub_folder/old_file.txt'})
92
75
  Master.stubs(:last_sync_hash).returns({'old_file_key' => 'test_sub_folder/old_file.txt'})
93
76
  assert_equal({'new_file_key' => 'test_sub_folder/new_file.txt'},Master.send(:files_to_pull))
94
77
  end
95
78
 
96
79
  test 'should want to delete locally missing files from cloud' do
97
- Master.stubs(:remote_directory_hash).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt', 'deleted_file_key' => 'test_sub_folder/deleted_file.txt'})
98
- Master.stubs(:directory_hash).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt'})
80
+ Index.stubs(:remote).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt', 'deleted_file_key' => 'test_sub_folder/deleted_file.txt'})
81
+ Index.stubs(:local).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt'})
99
82
  Master.stubs(:last_sync_hash).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt', 'deleted_file_key' => 'test_sub_folder/deleted_file.txt'})
100
83
  assert_equal({'deleted_file_key' => 'test_sub_folder/deleted_file.txt'},Master.send(:remote_files_to_delete))
101
84
  end
102
85
 
103
86
  test 'should delete files from cloud' do
104
- Master.stubs(:remote_directory_hash).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt', 'deleted_file_key' => 'test_sub_folder/deleted_file.txt'})
105
- Master.stubs(:directory_hash).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt'})
87
+ Index.stubs(:remote).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt', 'deleted_file_key' => 'test_sub_folder/deleted_file.txt'})
88
+ Index.stubs(:local).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt'})
106
89
  Master.stubs(:last_sync_hash).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt', 'deleted_file_key' => 'test_sub_folder/deleted_file.txt'})
107
90
  Adapters::Dummy.expects(:delete).with('deleted_file_key').returns(true)
108
91
  Master.delete_remote_files!
109
92
  end
110
93
 
111
94
  test 'should want to delete appropriate files locally' do
112
- Master.stubs(:remote_directory_hash).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt'})
113
- Master.stubs(:directory_hash).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt', 'deleted_file_key' => 'test_sub_folder/deleted_file.txt'})
95
+ Index.stubs(:remote).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt'})
96
+ Index.stubs(:local).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt', 'deleted_file_key' => 'test_sub_folder/deleted_file.txt'})
114
97
  Master.stubs(:last_sync_hash).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt', 'deleted_file_key' => 'test_sub_folder/deleted_file.txt'})
115
98
  assert_equal({'deleted_file_key' => 'test_sub_folder/deleted_file.txt'},Master.send(:local_files_to_delete))
116
99
  end
117
100
 
118
101
  test 'should delete local files' do
119
- Master.stubs(:remote_directory_hash).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt'})
120
- Master.stubs(:last_sync_hash).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt', 'deleted_file_key' => 'test_sub_folder/deleted_file.txt'}.merge(Master.send(:directory_hash)))
102
+ Index.stubs(:remote).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt'})
103
+ Master.stubs(:last_sync_hash).returns({'saved_file_key' => 'test_sub_folder/saved_file.txt', 'deleted_file_key' => 'test_sub_folder/deleted_file.txt'}.merge(Index.local))
121
104
  assert_difference('Dir["#{test_source_folder}/**/*"].length',-1) do
122
105
  Master.delete_local_files!
123
106
  end
@@ -128,7 +111,7 @@ module CloudEncryptedSync
128
111
  sample_directory_hash = {'sample_file_key' => 'test_sub_folder/sample_file.txt'}
129
112
  Master.instance_variable_set(:@finalize_required,true)
130
113
  Master.stubs(:directory_hash).returns(sample_directory_hash)
131
- Adapters::Dummy.expects(:write).with(anything,Master.send(:directory_key)).returns(true)
114
+ Adapters::Dummy.expects(:write).with(anything,Index.send(:index_key)).returns(true)
132
115
  Master.finalize!
133
116
  end
134
117
 
@@ -136,15 +119,15 @@ module CloudEncryptedSync
136
119
  #setup mock data
137
120
  sample_directory_hash = {'sample_file_key' => 'test_sub_folder/sample_file.txt'}
138
121
  encrypted_directory_hash = Cryptographer.encrypt_data(sample_directory_hash.to_yaml)
139
- Adapters::Dummy.stubs(:read).with(Master.send(:directory_key)).returns(encrypted_directory_hash)
122
+ Adapters::Dummy.expects(:read).with(Index.send(:index_key)).returns(encrypted_directory_hash)
140
123
 
141
124
  #do actual test
142
- decrypted_remote_hash = Master.send(:remote_directory_hash)
125
+ decrypted_remote_hash = Index.remote
143
126
  assert_equal(sample_directory_hash,decrypted_remote_hash)
144
127
  end
145
128
 
146
129
  test 'should puts error message to stdout' do
147
- Configuration.stubs(:settings).raises(IncompleteConfigurationError,'test message')
130
+ Configuration.stubs(:settings).raises(Errors::IncompleteConfigurationError,'test message')
148
131
  assert_equal('',$stdout.string)
149
132
  Master.expects(:pull_files).never
150
133
  Master.sync
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloud_encrypted_sync
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-14 00:00:00.000000000 Z
12
+ date: 2012-11-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mocha
@@ -93,14 +93,18 @@ files:
93
93
  - bin/ces
94
94
  - cloud_encrypted_sync.gemspec
95
95
  - lib/cloud_encrypted_sync.rb
96
+ - lib/cloud_encrypted_sync/adapter_liaison.rb
96
97
  - lib/cloud_encrypted_sync/adapter_template.rb
97
98
  - lib/cloud_encrypted_sync/configuration.rb
98
99
  - lib/cloud_encrypted_sync/cryptographer.rb
99
100
  - lib/cloud_encrypted_sync/dummy_adapter.rb
101
+ - lib/cloud_encrypted_sync/errors.rb
102
+ - lib/cloud_encrypted_sync/index.rb
100
103
  - lib/cloud_encrypted_sync/master.rb
101
104
  - lib/cloud_encrypted_sync/progress_meter.rb
102
105
  - lib/cloud_encrypted_sync/version.rb
103
106
  - test/test_helper.rb
107
+ - test/unit/adapter_liaison_test.rb
104
108
  - test/unit/configuration_test.rb
105
109
  - test/unit/cryptographer_test.rb
106
110
  - test/unit/master_test.rb
@@ -131,6 +135,7 @@ specification_version: 3
131
135
  summary: Encrypted sync of folder contents to/from cloud storage.
132
136
  test_files:
133
137
  - test/test_helper.rb
138
+ - test/unit/adapter_liaison_test.rb
134
139
  - test/unit/configuration_test.rb
135
140
  - test/unit/cryptographer_test.rb
136
141
  - test/unit/master_test.rb