cloud_encrypted_sync 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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