classifieds 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f7a28ed7dd95a4865ff0f44347b3bd6c569e7286
4
+ data.tar.gz: 228092b3aaca67a48753c77d59540cb093a7b455
5
+ SHA512:
6
+ metadata.gz: fd48a3b29771b3e2153e58a740e156b09a3943303f0b70677e5a99b0e1f8f531a86e50ecaef7c2f63e30dab6357a88e1452ba7ef15444b7e7da4a6991916017d
7
+ data.tar.gz: 487ec5bbc7a0a70350189cceb1fcc7d0d8fa5482fd47a8a67ddae34a6d3bcba186fdbe0ee981ada576c1215d0707d70649ac7f5010dacd038f9c3f4ad5ed3ec1
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in classifieds.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 kaihar4
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,162 @@
1
+ # Classifieds
2
+
3
+ File Encryption Manager.
4
+
5
+ ## Description
6
+
7
+ Classifieds manages the encryption of files in the repository.
8
+
9
+ It is possible to securely encrypted using OpenSSL and also possible to manage them easily.
10
+
11
+ ## Installation
12
+
13
+ ```
14
+ $ gem install classifieds
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ Initialize classifieds.
20
+
21
+ ```
22
+ $ classifieds init
23
+ ```
24
+
25
+ Encrypt files which were described in .classifieds.
26
+
27
+ ```
28
+ $ classifieds encrypt
29
+ ```
30
+
31
+ Decrypt files which were described in .classifieds.
32
+
33
+ ```
34
+ $ classifieds decrypt
35
+ ```
36
+
37
+ Show a status of the encryption of this repository.
38
+
39
+ ```
40
+ $ classifieds status
41
+ ```
42
+
43
+ ## Example
44
+
45
+ In your repository:
46
+
47
+ ```
48
+ ├── bar
49
+ │   ├── bar1
50
+ │   └── bar2
51
+ ├── foo
52
+ ├── fuga
53
+ │   ├── fuga1
54
+ │   └── fuga2
55
+ └── hoge
56
+ ├── hoge1.rb
57
+ └── hoge2
58
+ ```
59
+
60
+ First, initialize classifieds.
61
+
62
+ ```
63
+ $ classifieds init
64
+ .classifieds was created
65
+
66
+ $ ls -a
67
+ .classifieds bar/ foo fuga/ hoge/
68
+ ```
69
+
70
+ Write files as relative path from `.classifieds` which you want to encrypt in `.classifieds`.
71
+
72
+ ```
73
+ $ vim .classifieds
74
+ bar/*
75
+ !bar/bar1
76
+ foo
77
+ fuga
78
+ */*.rb
79
+ ```
80
+
81
+ Show the status.
82
+
83
+ ```
84
+ $ classifieds status
85
+ Unencrypted:
86
+ /path/to/foo
87
+ /path/to/bar/bar2
88
+ /path/to/hoge/hoge1.rb
89
+ /path/to/fuga/fuga1
90
+ /path/to/fuga/fuga2
91
+ ```
92
+
93
+ Encrypt files.
94
+
95
+ ```
96
+ $ classifieds encrypt
97
+ Password:
98
+ Retype password:
99
+ Encrypted:
100
+ /path/to/foo
101
+ /path/to/bar/bar2
102
+ /path/to/hoge/hoge1.rb
103
+ /path/to/fuga/fuga1
104
+ /path/to/fuga/fuga2
105
+ ```
106
+
107
+ Check the status.
108
+
109
+ ```
110
+ $ classifieds status
111
+ Encrypted:
112
+ /path/to/foo
113
+ /path/to/bar/bar2
114
+ /path/to/hoge/hoge1.rb
115
+ /path/to/fuga/fuga1
116
+ /path/to/fuga/fuga2
117
+
118
+ $ cat foo
119
+ 65c0ec273963aacc69af593b03d1710ff90f75da¢É™¸
120
+ ```
121
+
122
+ Decrypt files.
123
+
124
+ ```
125
+ $ classifieds decrypt
126
+ Password:
127
+ Decrypted:
128
+ /path/to/foo
129
+ /path/to/bar/bar2
130
+ /path/to/hoge/hoge1.rb
131
+ /path/to/fuga/fuga1
132
+ /path/to/fuga/fuga2
133
+ ```
134
+
135
+ Check the status.
136
+
137
+ ```
138
+ $ classifieds status
139
+ Unencrypted:
140
+ /path/to/foo
141
+ /path/to/bar/bar2
142
+ /path/to/hoge/hoge1.rb
143
+ /path/to/fuga/fuga1
144
+ /path/to/fuga/fuga2
145
+
146
+ $ cat foo
147
+ foo
148
+ ```
149
+
150
+ ## Development
151
+
152
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
153
+
154
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
155
+
156
+ ## Contributing
157
+
158
+ 1. Fork it ( https://github.com/kaihar4/classifieds/fork )
159
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
160
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
161
+ 4. Push to the branch (`git push origin my-new-feature`)
162
+ 5. Create a new Pull Request
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'classifieds'
5
+
6
+ require 'irb'
7
+ IRB.start
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install --path vendor/bundle
@@ -0,0 +1,26 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'classifieds/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'classifieds'
7
+ spec.version = Classifieds::VERSION
8
+ spec.authors = ["kaihar4"]
9
+ spec.email = ["0@kaihar4.com"]
10
+
11
+ spec.summary = 'File Encryption Manager'
12
+ spec.description = spec.summary
13
+ spec.homepage = 'https://github.com/kaihar4/classifieds'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject {|f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = 'exe'
18
+ spec.executables = spec.files.grep(%r{^exe/}) {|f| File.basename(f) }
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_runtime_dependency 'thor'
22
+ spec.add_runtime_dependency 'safe_colorize'
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.9'
25
+ spec.add_development_dependency 'rake', '~> 10.0'
26
+ end
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'classifieds'
4
+
5
+ Classifieds::Main.start(ARGV)
@@ -0,0 +1,7 @@
1
+ require 'classifieds/exception'
2
+ require 'classifieds/parser'
3
+ require 'classifieds/main'
4
+
5
+ module Classifieds
6
+ SOURCE_FILE = '.classifieds'
7
+ end
@@ -0,0 +1,3 @@
1
+ module Classifieds
2
+ class ParseError < StandardError;end
3
+ end
@@ -0,0 +1,162 @@
1
+ require 'digest/sha1'
2
+ require 'openssl'
3
+ require 'fileutils'
4
+
5
+ require 'safe_colorize'
6
+
7
+ require 'thor'
8
+
9
+ module Classifieds
10
+ class Main < Thor
11
+ using SafeColorize
12
+
13
+ def initialize(*args)
14
+ @prefix = Digest::SHA1.hexdigest('classifieds')
15
+ super
16
+ end
17
+
18
+ desc 'init', 'Initialize classifieds'
19
+ def init
20
+ if File.exists?(SOURCE_FILE)
21
+ puts 'Classifieds already initialized'.color(:red)
22
+ else
23
+ FileUtils.touch(SOURCE_FILE)
24
+ puts "#{SOURCE_FILE} was created".color(:green)
25
+ end
26
+ end
27
+
28
+ desc 'encrypt', 'Encrypt files which were described in .classifieds'
29
+ def encrypt
30
+ @password ||= ask_password
31
+ retype_password
32
+
33
+ encrypted_files = classifieds.each_with_object([]) do |file_path, array|
34
+ next if encrypted?(file_path)
35
+
36
+ file = File.open(file_path, 'r+')
37
+ file.flock(File::LOCK_EX)
38
+
39
+ data = file.read.chomp
40
+
41
+ begin
42
+ encrypted = encrypt_data(data)
43
+ rescue ArgumentError
44
+ next
45
+ end
46
+
47
+ file.rewind
48
+ file.write @prefix + encrypted
49
+ file.truncate(file.tell)
50
+
51
+ array << file_path
52
+ end
53
+
54
+ puts 'Encrypted:'.color(:green) unless encrypted_files.empty?
55
+ encrypted_files.each {|encrypted_file| puts "\t" + encrypted_file }
56
+ end
57
+
58
+ desc 'decrypt', 'Decrypt files which were described in .classifieds'
59
+ def decrypt
60
+ @password ||= ask_password
61
+
62
+ decrypted_files = classifieds.each_with_object([]) do |file_path, array|
63
+ next if decrypted?(file_path)
64
+
65
+ file = File.open(file_path, 'r+')
66
+ file.flock(File::LOCK_EX)
67
+
68
+ file.read(@prefix.size)
69
+ data = file.read.chomp
70
+
71
+ begin
72
+ decrypted = decrypt_data(data)
73
+ rescue OpenSSL::Cipher::CipherError
74
+ STDERR.puts 'The entered password is wrong: '.color(:red) + file_path
75
+ next
76
+ end
77
+
78
+ file.rewind
79
+ file.write decrypted
80
+ file.truncate(file.tell)
81
+
82
+ array << file_path
83
+ end
84
+
85
+ puts 'Decrypted:'.color(:green) unless decrypted_files.empty?
86
+ decrypted_files.each {|decrypted_file| puts "\t" + decrypted_file }
87
+ end
88
+
89
+ desc 'status', 'Show a status of the encryption of this repository'
90
+ def status
91
+ encrypted_files = []
92
+ unencrypted_files = []
93
+
94
+ classifieds.each do |file|
95
+ if encrypted?(file)
96
+ encrypted_files << file
97
+ else
98
+ unencrypted_files << file
99
+ end
100
+ end
101
+ puts 'Encrypted:'.color(:green) unless encrypted_files.empty?
102
+ encrypted_files.each {|file| puts "\t" + file }
103
+ puts 'Unencrypted:'.color(:red) unless unencrypted_files.empty?
104
+ unencrypted_files.each {|file| puts "\t" + file }
105
+ end
106
+
107
+ private
108
+
109
+ def ask_password
110
+ print 'Password: '
111
+ password = STDIN.noecho(&:gets)
112
+ print "\n"
113
+
114
+ password
115
+ end
116
+
117
+ def retype_password
118
+ print 'Retype password: '
119
+ password = STDIN.noecho(&:gets)
120
+ print "\n"
121
+
122
+ unless password == @password
123
+ STDERR.puts 'Sorry, passwords do not match'.color(:red)
124
+ @password = ask_password
125
+ retype_password
126
+ end
127
+ end
128
+
129
+ def classifieds
130
+ File.open(SOURCE_FILE) do |f|
131
+ Parser.parse(f.read)
132
+ end
133
+ rescue Errno::ENOENT
134
+ STDERR.puts "#{SOURCE_FILE} is not found".color(:red)
135
+ exit 1
136
+ end
137
+
138
+ def encrypt_data(data)
139
+ cipher = OpenSSL::Cipher.new('AES-256-CBC')
140
+ cipher.encrypt
141
+ cipher.pkcs5_keyivgen(@password)
142
+ cipher.update(data) + cipher.final
143
+ end
144
+
145
+ def decrypt_data(data)
146
+ cipher = OpenSSL::Cipher.new('AES-256-CBC')
147
+ cipher.decrypt
148
+ cipher.pkcs5_keyivgen(@password)
149
+ cipher.update(data) + cipher.final
150
+ end
151
+
152
+ def encrypted?(file)
153
+ File.open(file, 'r') do |f|
154
+ !!f.read(@prefix.size).to_s.match(/\A#{@prefix}\z/)
155
+ end
156
+ end
157
+
158
+ def decrypted?(file)
159
+ !encrypted?(file)
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,64 @@
1
+ module Classifieds
2
+ class Parser
3
+ def self.parse(text)
4
+ lines = text.each_line.map(&:chomp)
5
+ result = lines.each_with_object([]) do |line, array|
6
+ target = detect_target(line)
7
+
8
+ case target
9
+ when Array
10
+ if classifieds_ignore?(line)
11
+ target.each do |file|
12
+ array.delete(file)
13
+ end
14
+ else
15
+ array.concat(target)
16
+ end
17
+ when String
18
+ if classifieds_ignore?(line)
19
+ array.delete(target)
20
+ else
21
+ array << target
22
+ end
23
+ end
24
+ end
25
+
26
+ result
27
+ end
28
+
29
+ def self.detect_target(string)
30
+ path = string.match(/^!?(.+)/)[1]
31
+ absolute_path = File.join(Dir.pwd, path)
32
+
33
+ # Dir.glob notation
34
+ if absolute_path.include?('*')
35
+ recursive_glob(absolute_path)
36
+ else
37
+ case File.ftype(path)
38
+ when 'file'
39
+ absolute_path
40
+ when 'directory'
41
+ recursive_glob(File.join(absolute_path, '*'))
42
+ else
43
+ raise ParseError, "invalid file type: #{string}"
44
+ end
45
+ end
46
+ rescue Errno::ENOENT
47
+ end
48
+
49
+ def self.classifieds_ignore?(string)
50
+ string.start_with?('!')
51
+ end
52
+
53
+ def self.recursive_glob(pattern)
54
+ Dir.glob(pattern).each_with_object([]) do |path, array|
55
+ case File.ftype(path)
56
+ when 'file'
57
+ array << path
58
+ when 'directory'
59
+ array.concat(recursive_glob(File.join(path, '*')))
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,3 @@
1
+ module Classifieds
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: classifieds
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - kaihar4
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-06-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: safe_colorize
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.9'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.9'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ description: File Encryption Manager
70
+ email:
71
+ - 0@kaihar4.com
72
+ executables:
73
+ - classifieds
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - bin/console
83
+ - bin/setup
84
+ - classifieds.gemspec
85
+ - exe/classifieds
86
+ - lib/classifieds.rb
87
+ - lib/classifieds/exception.rb
88
+ - lib/classifieds/main.rb
89
+ - lib/classifieds/parser.rb
90
+ - lib/classifieds/version.rb
91
+ homepage: https://github.com/kaihar4/classifieds
92
+ licenses:
93
+ - MIT
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 2.4.5
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: File Encryption Manager
115
+ test_files: []