ccs 1.0.0.rc1 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +66 -4
- data/exe/ccs +69 -49
- data/lib/ccs.rb +4 -2
- data/lib/ccs/commands/copy.rb +59 -0
- data/lib/ccs/decrypter.rb +15 -0
- data/lib/ccs/document.rb +54 -0
- data/lib/ccs/downloader.rb +27 -5
- data/lib/ccs/encrypter.rb +19 -0
- data/lib/ccs/uploader.rb +31 -7
- data/lib/ccs/version.rb +2 -1
- metadata +18 -31
- data/lib/ccs/application.rb +0 -34
- data/lib/ccs/configuration_file.rb +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ce4380f6f1c426727a7147117ba50c7dc21866ec766ecf01083846e5340d8d79
|
4
|
+
data.tar.gz: 162d1b0bebdcbec23a1bb72b2c82d7ad4b6994c4ab6fc4258d1ebeebe29c61b9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70e1cd7ac88384e75d62093429b4fa37a4b2aba6c6b3439e47c856653b2eac54c56b2e55d11281b2258ee0497a6e5008cc365b3f402ae3277e6612764af9e502
|
7
|
+
data.tar.gz: 8b580583449616952eedf65b13e7c893464cfb7fd3595ff2f238eaf933741211dad276df92d31008b54b7d0976ed727bc9a9471d109dc6d8697e3caa047e2f3c
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
# CCS
|
2
2
|
|
3
|
+
Configuration control system
|
3
4
|
|
4
5
|
## Installation
|
5
6
|
|
6
7
|
Add this line to your application's Gemfile:
|
7
8
|
|
8
|
-
|
9
|
-
gem 'ccs'
|
10
|
-
```
|
9
|
+
gem 'ccs'
|
11
10
|
|
12
11
|
And then execute:
|
13
12
|
|
@@ -19,17 +18,80 @@ Or install it yourself as:
|
|
19
18
|
|
20
19
|
## Usage
|
21
20
|
|
21
|
+
ccs cp [OPTIONS] <(LocalPath|STDIN)|(CCSUri|Uri)> <(CCSUri|Uri)|(LocalPath|STDOUT)>
|
22
|
+
|
23
|
+
Options:
|
24
|
+
-a CCS_ACCESS_TOKEN, CCS Access Token
|
25
|
+
--access-token
|
26
|
+
-p CCS_PASSPHRASE, CCS Passphrase
|
27
|
+
--passphrase
|
28
|
+
-f, --[no-]force Overwrite remote documents when uploading
|
29
|
+
|
30
|
+
|
22
31
|
## Example
|
23
32
|
|
33
|
+
Download to STDOUT
|
34
|
+
|
35
|
+
ccs cp ccs://workspace-name/0.1.0/path/to/file.yml -
|
36
|
+
ccs cp http://host.tld:9292/workspace-name/0.1.0/path/to/file.yml -
|
37
|
+
ccs cp https://host.tld/workspace-name/0.1.0/path/to/file.yml -
|
38
|
+
|
39
|
+
Download to local file
|
40
|
+
|
41
|
+
ccs cp ccs://workspace-name/0.1.0/path/to/file.yml /local/path/to/file.yml
|
42
|
+
|
43
|
+
Upload local file
|
44
|
+
|
45
|
+
ccs cp /local/path/to/file.yml ccs://workspace-name/0.1.0/path/to/file.yml
|
46
|
+
|
47
|
+
Upload local file, overwriting remote document if any'
|
48
|
+
|
49
|
+
ccs cp --force /local/path/to/file.yml ccs://workspace-name/0.1.0/path/to/file.yml
|
50
|
+
|
51
|
+
Upload content from STDIN
|
52
|
+
|
53
|
+
echo "{ a: 1 }" | ccs cp - ccs://workspace-name/0.1.0/path/to/file.yml
|
54
|
+
cat /local/path/to/file.yml | ccs cp - ccs://workspace-name/0.1.0/path/to/file.yml
|
55
|
+
|
56
|
+
## API
|
57
|
+
|
58
|
+
Upload
|
59
|
+
|
60
|
+
require 'ccs'
|
61
|
+
|
62
|
+
destination = 'ccs://workspace-name/0.1.0/path/to/file.yml'
|
63
|
+
access_token = ENV.fetch('CCS_ACCESS_TOKEN')
|
64
|
+
passphrase = 'MyPassphrase'
|
65
|
+
content = 'RAILS_ENV=production'
|
66
|
+
|
67
|
+
Ccs::Document.new(destination, access_token, passphrase).upload(content)
|
68
|
+
|
69
|
+
Download
|
70
|
+
|
71
|
+
require 'ccs'
|
72
|
+
|
73
|
+
source = 'ccs://workspace-name/0.1.0/path/to/file.yml'
|
74
|
+
access_token = ENV.fetch('CCS_ACCESS_TOKEN')
|
75
|
+
passphrase = 'MyPassphrase'
|
76
|
+
|
77
|
+
Ccs::Document.new(source, access_token, passphrase).download
|
78
|
+
|
24
79
|
## Development
|
25
80
|
|
26
81
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
27
82
|
|
28
83
|
To install this gem onto your local machine, run `bin/rake install`. To release a new version, update the version number in `version.rb`, and then run `bin/rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
29
84
|
|
85
|
+
## Documentation
|
86
|
+
|
87
|
+
Documentation for the code should be written in YARD format. It can be generated locally into the `doc` directory by running
|
88
|
+
|
89
|
+
bundle exec yard
|
90
|
+
|
91
|
+
|
30
92
|
## Contributing
|
31
93
|
|
32
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
94
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/occson/ccs. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
33
95
|
|
34
96
|
|
35
97
|
## License
|
data/exe/ccs
CHANGED
@@ -9,62 +9,82 @@ require 'io/console'
|
|
9
9
|
|
10
10
|
options = {
|
11
11
|
'access_token' => ENV['CCS_ACCESS_TOKEN'],
|
12
|
-
'
|
12
|
+
'passphrase' => ENV['CCS_PASSPHRASE']
|
13
13
|
}
|
14
14
|
|
15
|
-
option_parser = OptionParser.new
|
16
|
-
option_parser.banner = 'Usage: ccs [OPTIONS]
|
17
|
-
option_parser.separator ''
|
18
|
-
option_parser.separator 'Configuration Control System (CCS)'
|
19
|
-
option_parser.separator ''
|
20
|
-
option_parser.separator 'Options:'
|
15
|
+
option_parser = OptionParser.new do |option_parser|
|
16
|
+
option_parser.banner = 'Usage: ccs [COMMAND [OPTIONS]]'
|
21
17
|
|
22
|
-
option_parser.
|
23
|
-
|
24
|
-
|
18
|
+
option_parser.separator ''
|
19
|
+
option_parser.separator 'Configuration Control System (CCS)'
|
20
|
+
option_parser.separator ''
|
21
|
+
option_parser.separator 'Commands:'
|
22
|
+
option_parser.separator ' cp Copy'
|
25
23
|
|
26
|
-
option_parser.
|
27
|
-
|
24
|
+
option_parser.separator ''
|
25
|
+
option_parser.separator format('Version: %<version>s', version: Ccs::VERSION)
|
28
26
|
end
|
29
27
|
|
30
|
-
|
31
|
-
|
32
|
-
|
28
|
+
case ARGV.shift
|
29
|
+
when 'cp'
|
30
|
+
option_parser = OptionParser.new do |option_parser|
|
31
|
+
option_parser.banner = 'Usage: ccs cp [OPTIONS] <(LocalPath|STDIN)|(CCSUri|Uri)> <(CCSUri|Uri)|(LocalPath|STDOUT)>'
|
32
|
+
option_parser.separator ''
|
33
|
+
option_parser.separator 'Configuration Control System (CCS) - Copy'
|
34
|
+
option_parser.separator ''
|
35
|
+
option_parser.separator 'Options:'
|
33
36
|
|
34
|
-
option_parser.
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
option_parser.
|
43
|
-
|
44
|
-
|
45
|
-
option_parser.separator ' Download to local file'
|
46
|
-
option_parser.separator ' ccs ccs://workspace-name/0.1.0/path/to/file.yml /local/path/to/file.yml'
|
47
|
-
option_parser.separator ' Upload local file'
|
48
|
-
option_parser.separator ' ccs /local/path/to/file.yml ccs://workspace-name/0.1.0/path/to/file.yml'
|
49
|
-
option_parser.separator ' Upload content from STDIN'
|
50
|
-
option_parser.separator ' echo "{ a: 1 }" | ccs - ccs://workspace-name/0.1.0/path/to/file.yml'
|
51
|
-
option_parser.separator ' cat /local/path/to/file.yml | ccs - ccs://workspace-name/0.1.0/path/to/file.yml'
|
52
|
-
option_parser.separator ''
|
53
|
-
|
54
|
-
option_parser.separator format('Version: %<version>s', version: Ccs::VERSION)
|
55
|
-
|
56
|
-
arguments = option_parser.parse!
|
57
|
-
|
58
|
-
unless options.values.any?
|
59
|
-
puts option_parser
|
60
|
-
exit(1)
|
61
|
-
end
|
37
|
+
option_parser.on('-a CCS_ACCESS_TOKEN', '--access-token CCS_ACCESS_TOKEN', String, 'CCS Access Token') do |v|
|
38
|
+
options['access_token'] = v
|
39
|
+
end
|
40
|
+
|
41
|
+
option_parser.on('-p CCS_PASSPHRASE', '--passphrase CCS_PASSPHRASE', String, 'CCS Passphrase') do |v|
|
42
|
+
options['passphrase'] = v
|
43
|
+
end
|
44
|
+
|
45
|
+
option_parser.on('-f', '--[no-]force', 'Overwrite remote documents when uploading') do |v|
|
46
|
+
options['force'] = v
|
47
|
+
end
|
62
48
|
|
63
|
-
|
49
|
+
option_parser.separator ''
|
50
|
+
option_parser.separator 'Configure via environment variables:'
|
51
|
+
option_parser.separator ' CCS_ACCESS_TOKEN'
|
52
|
+
option_parser.separator ' CCS_PASSPHRASE'
|
64
53
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
54
|
+
option_parser.separator ''
|
55
|
+
option_parser.separator 'Examples:'
|
56
|
+
option_parser.separator ' Download to STDOUT'
|
57
|
+
option_parser.separator ' ccs cp ccs://workspace-name/0.1.0/path/to/file.yml -'
|
58
|
+
option_parser.separator ' ccs cp http://host.tld:9292/workspace-name/0.1.0/path/to/file.yml -'
|
59
|
+
option_parser.separator ' ccs cp https://host.tld/workspace-name/0.1.0/path/to/file.yml -'
|
60
|
+
option_parser.separator ' Download to local file'
|
61
|
+
option_parser.separator ' ccs cp ccs://workspace-name/0.1.0/path/to/file.yml /local/path/to/file.yml'
|
62
|
+
option_parser.separator ' Upload local file'
|
63
|
+
option_parser.separator ' ccs cp /local/path/to/file.yml ccs://workspace-name/0.1.0/path/to/file.yml'
|
64
|
+
option_parser.separator ' Upload local file, overwriting remote document if any'
|
65
|
+
option_parser.separator ' ccs cp --force /local/path/to/file.yml ccs://workspace-name/0.1.0/path/to/file.yml'
|
66
|
+
option_parser.separator ' Upload content from STDIN'
|
67
|
+
option_parser.separator ' echo "{ a: 1 }" | ccs cp - ccs://workspace-name/0.1.0/path/to/file.yml'
|
68
|
+
option_parser.separator ' cat /local/path/to/file.yml | ccs cp - ccs://workspace-name/0.1.0/path/to/file.yml'
|
69
|
+
option_parser.separator ''
|
69
70
|
|
70
|
-
|
71
|
+
option_parser.separator format('Version: %<version>s', version: Ccs::VERSION)
|
72
|
+
end
|
73
|
+
|
74
|
+
arguments = option_parser.parse!
|
75
|
+
|
76
|
+
unless options.values.any?
|
77
|
+
puts option_parser
|
78
|
+
exit(1)
|
79
|
+
end
|
80
|
+
|
81
|
+
raise OptionParser::MissingArgument, 'access_token' unless options['access_token']
|
82
|
+
raise OptionParser::MissingArgument, 'passphrase' unless options['passphrase']
|
83
|
+
raise OptionParser::MissingArgument, 'source' unless arguments.fetch(0, nil)
|
84
|
+
raise OptionParser::MissingArgument, 'destination' unless arguments.fetch(1, nil)
|
85
|
+
|
86
|
+
exit(1) unless Ccs::Commands::Copy.new(arguments[0], arguments[1], options['access_token'], options['passphrase'], force: options.fetch('force', false)).call
|
87
|
+
else
|
88
|
+
puts option_parser
|
89
|
+
exit(1)
|
90
|
+
end
|
data/lib/ccs.rb
CHANGED
@@ -11,7 +11,9 @@ require 'ccs/encrypter'
|
|
11
11
|
require 'ccs/decrypter'
|
12
12
|
require 'ccs/uploader'
|
13
13
|
require 'ccs/downloader'
|
14
|
-
require 'ccs/
|
15
|
-
require 'ccs/application'
|
14
|
+
require 'ccs/document'
|
16
15
|
|
16
|
+
require 'ccs/commands/copy'
|
17
|
+
|
18
|
+
# Top level `Ccs` namespace.
|
17
19
|
module Ccs; end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ccs
|
4
|
+
module Commands
|
5
|
+
# The copy command, responsible for copying a target to a destination, performing encryption
|
6
|
+
# and decryption as necessary.
|
7
|
+
#
|
8
|
+
# The target and destinations can be:
|
9
|
+
#
|
10
|
+
# - STDIN/STDOUT: a `-` sign is interpreted as these standard streams
|
11
|
+
#
|
12
|
+
# - The Occson server: strings beginning with `ccs://` or `http(s)://` are interpreted as such
|
13
|
+
#
|
14
|
+
# - local files: everything not matching the previous descriptions is assumed to
|
15
|
+
# be a path on the local system
|
16
|
+
class Copy
|
17
|
+
# Builds an instance of the Copy command.
|
18
|
+
#
|
19
|
+
# @param source [String] `-` for STDIN, an URI or a local file path
|
20
|
+
# @param destination [String] `-` for STDOUT, an URI or a local file path
|
21
|
+
# @param access_token [String] Occson access token
|
22
|
+
# @param passphrase [String] Passphrase used for encryption of the document
|
23
|
+
# @param force [Boolean] Whether to overwrite target document in Occson, if any. Default `false`.
|
24
|
+
def initialize(source, destination, access_token, passphrase, force: false)
|
25
|
+
@source = source
|
26
|
+
@destination = destination
|
27
|
+
@access_token = access_token
|
28
|
+
@passphrase = passphrase
|
29
|
+
@force = force
|
30
|
+
end
|
31
|
+
|
32
|
+
# Performs a transfer between locations - an upload if `@source` is local or STDIN,
|
33
|
+
# a download if `@source` is an URI.
|
34
|
+
#
|
35
|
+
# No guarantees are made about the return values of this method.
|
36
|
+
def call
|
37
|
+
download? ? download : upload
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def download?
|
43
|
+
@source.match?(%r{\A(ccs|https?):\/\/})
|
44
|
+
end
|
45
|
+
|
46
|
+
def download
|
47
|
+
content = Document.new(@source, @access_token, @passphrase).download
|
48
|
+
return unless content
|
49
|
+
|
50
|
+
(@destination.eql?('-') ? STDOUT : File.new(@destination, 'w')).print content
|
51
|
+
end
|
52
|
+
|
53
|
+
def upload
|
54
|
+
content = @source.eql?('-') ? STDIN.read : File.read(@source)
|
55
|
+
Document.new(@destination, @access_token, @passphrase).upload(content, force: @force)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/ccs/decrypter.rb
CHANGED
@@ -1,12 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Ccs
|
4
|
+
# Handles client-side decryption for documents.
|
5
|
+
#
|
6
|
+
# The decrypter uses AES-256 in CBC mode internally. A salt is
|
7
|
+
# expected in bytes 8..15, with ciphertext occupying the
|
8
|
+
# further bytes.
|
4
9
|
class Decrypter
|
10
|
+
# Constructs a Decrypter instance with given passphrase and content.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# Ccs::Decrypter.new('the content passphrase', content)
|
14
|
+
#
|
15
|
+
# @param passphrase [String] Passphrase for content decryption
|
16
|
+
# @param content [String] Encrypted document content
|
5
17
|
def initialize(passphrase, content)
|
6
18
|
@passphrase = passphrase
|
7
19
|
@content = content
|
8
20
|
end
|
9
21
|
|
22
|
+
# Performs decryption, returning plaintext if passphrase matched.
|
23
|
+
#
|
24
|
+
# @return [String] Plaintext document content
|
10
25
|
def call
|
11
26
|
decryptor.pkcs5_keyivgen(@passphrase, ciphertext_salt, 1)
|
12
27
|
result = decryptor.update(encrypted)
|
data/lib/ccs/document.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ccs
|
4
|
+
# An abstraction for the Document concept. Simplifies building URLs,
|
5
|
+
# uploading and downloading contents. Abstracts away workspaces due to
|
6
|
+
# the use of access tokens in constructions.
|
7
|
+
class Document
|
8
|
+
# Constructs a Document instance from a given URI, access token and passphrase.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# uri = 'ccs://path/to/file.yml'
|
12
|
+
# access_token = 'f30b5450421362c9ca0b'
|
13
|
+
# passphrase = 'my document passphrase'
|
14
|
+
#
|
15
|
+
# Ccs::Document.new(uri, access_token, passphrase)
|
16
|
+
#
|
17
|
+
# @param uri [String] Document URI. Accepts `ccs://` as shorthand for Occson location.
|
18
|
+
# @param access_token [String] Occson access token.
|
19
|
+
# @param passphrase [String] Document passphrase, used in encryption and decryption.
|
20
|
+
def initialize(uri, access_token, passphrase)
|
21
|
+
@uri = build_uri(uri)
|
22
|
+
@access_token = access_token
|
23
|
+
@passphrase = passphrase
|
24
|
+
end
|
25
|
+
|
26
|
+
# Uploads the given plaintext `content` to target URI.
|
27
|
+
#
|
28
|
+
# @example
|
29
|
+
# document.upload('My example plaintext.')
|
30
|
+
#
|
31
|
+
# @param content [String] Plaintext to be encrypted and uploaded.
|
32
|
+
# @param force [Boolean] Whether to overwrite target document in Occson, if any. Default `false`.
|
33
|
+
def upload(content, force: false)
|
34
|
+
Uploader.new(@uri, content, @access_token, @passphrase, force: force).call
|
35
|
+
end
|
36
|
+
|
37
|
+
# Downloads the encrypted document at `@uri` and returns the plaintext
|
38
|
+
# contents (given that `@passphrase` matches).
|
39
|
+
#
|
40
|
+
# @example
|
41
|
+
# plaintext = document.download
|
42
|
+
#
|
43
|
+
# @return [String] Decrypted document contents
|
44
|
+
def download
|
45
|
+
Downloader.new(@uri, @access_token, @passphrase).call
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def build_uri(uri)
|
51
|
+
URI uri.sub('ccs://', 'https://api.occson.com/')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/ccs/downloader.rb
CHANGED
@@ -1,30 +1,52 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Ccs
|
4
|
+
# Downloads and decrypts the document at given URI with given access token.
|
5
|
+
# Decryption occurs using given passphrase.
|
4
6
|
class Downloader
|
5
|
-
|
7
|
+
# Constructs a Downloader instance from a given URI, access token and passphrase.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# uri = 'ccs://path/to/file.yml'
|
11
|
+
# access_token = 'f30b5450421362c9ca0b'
|
12
|
+
# passphrase = 'my document passphrase'
|
13
|
+
#
|
14
|
+
# Ccs::Downloader.new(uri, access_token, passphrase)
|
15
|
+
#
|
16
|
+
# @param uri [String] Document URI. Accepts `ccs://` as shorthand for Occson location.
|
17
|
+
# @param access_token [String] Occson access token.
|
18
|
+
# @param passphrase [String] Document passphrase, used in encryption and decryption.
|
19
|
+
def initialize(uri, access_token, passphrase)
|
6
20
|
@uri = uri
|
7
21
|
@access_token = access_token
|
8
|
-
@
|
22
|
+
@passphrase = passphrase
|
9
23
|
end
|
10
24
|
|
25
|
+
# Performs the download and decryption of document.
|
26
|
+
#
|
27
|
+
# @return [String|nil] Decrypted body of the document or `nil` in case the
|
28
|
+
# server did not respond with a `200` HTTP code.
|
11
29
|
def call
|
12
30
|
response = http.request(request)
|
13
31
|
body = response.body
|
14
32
|
return unless response.code.eql? '200'
|
15
33
|
json = JSON.parse body
|
16
34
|
|
17
|
-
Decrypter.new(@
|
35
|
+
Decrypter.new(@passphrase, json['encrypted_content']).call
|
18
36
|
end
|
19
37
|
|
20
38
|
private
|
21
39
|
|
22
40
|
def http
|
23
|
-
Net::HTTP.new(@uri.host, @uri.port)
|
41
|
+
@http ||= Net::HTTP.new(@uri.host, @uri.port).tap do |http|
|
42
|
+
http.use_ssl = @uri.scheme.eql?('https')
|
43
|
+
end
|
24
44
|
end
|
25
45
|
|
26
46
|
def request
|
27
|
-
Net::HTTP::Get.new(@uri.path, headers)
|
47
|
+
Net::HTTP::Get.new(@uri.path, headers).tap do |request|
|
48
|
+
request["User-Agent"] = format('ccs/%s', Ccs::VERSION)
|
49
|
+
end
|
28
50
|
end
|
29
51
|
|
30
52
|
def headers
|
data/lib/ccs/encrypter.rb
CHANGED
@@ -1,13 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Ccs
|
4
|
+
# Encrypts the given content for transmission. Uses AES-256 in CBC
|
5
|
+
# mode internally, with salting.
|
4
6
|
class Encrypter
|
7
|
+
# Constructs an Encrypter instance with given passphrase, content and salt.
|
8
|
+
# Salt _must_ be exactly 8 characters long.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# passphrase = 'my long document passphrase'
|
12
|
+
# content = 'very secret content'
|
13
|
+
# salt = '12345678'
|
14
|
+
#
|
15
|
+
# Ccs::Encrypter.new(passphrase, content, salt)
|
16
|
+
#
|
17
|
+
# @param passphrase [String] Document passphrase.
|
18
|
+
# @param content [String] Plaintext content to be encrypted.
|
19
|
+
# @param salt [String] Salt to reinforce the encryption, included in
|
20
|
+
# plaintext in the encrypted document.
|
5
21
|
def initialize(passphrase, content, salt)
|
6
22
|
@passphrase = passphrase
|
7
23
|
@content = content
|
8
24
|
@salt = salt
|
9
25
|
end
|
10
26
|
|
27
|
+
# Performs the actual encryption, returning base64-encoded ciphertext.
|
28
|
+
#
|
29
|
+
# @return [String] base64-encoded ciphertext
|
11
30
|
def call
|
12
31
|
encryptor.pkcs5_keyivgen(@passphrase, @salt, 1)
|
13
32
|
encrypted = encryptor.update(@content)
|
data/lib/ccs/uploader.rb
CHANGED
@@ -1,27 +1,51 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Ccs
|
4
|
+
# Encrypts and uploads the document to Occson.
|
4
5
|
class Uploader
|
5
|
-
|
6
|
+
# Constructs an Uploader instance from a given URI, content, access token and passphrase.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# uri = 'ccs://path/to/file.yml'
|
10
|
+
# content = 'my very secret message'
|
11
|
+
# access_token = 'f30b5450421362c9ca0b'
|
12
|
+
# passphrase = 'my document passphrase'
|
13
|
+
#
|
14
|
+
# Ccs::Uploader.new(uri, access_token, passphrase)
|
15
|
+
#
|
16
|
+
# @param uri [String] Document URI. Accepts `ccs://` as shorthand for Occson location.
|
17
|
+
# @param content [String] Plaintext for encryption and upload.
|
18
|
+
# @param access_token [String] Occson access token.
|
19
|
+
# @param passphrase [String] Document passphrase, used in encryption and decryption.
|
20
|
+
# @param force [Boolean] Whether to overwrite target document in Occson, if any. Default `false`.
|
21
|
+
def initialize(uri, content, access_token, passphrase, force: false)
|
6
22
|
@uri = uri
|
7
23
|
@content = content
|
8
24
|
@access_token = access_token
|
9
|
-
@
|
25
|
+
@passphrase = passphrase
|
26
|
+
@force = force.to_s
|
10
27
|
end
|
11
28
|
|
29
|
+
# Performs the actual upload to server.
|
30
|
+
#
|
31
|
+
# @return [Boolean] `true` for a successful upload, `false` otherwise
|
12
32
|
def call
|
13
|
-
request.body = { encrypted_content: encrypted_content }.to_json
|
14
|
-
http.request(request).code
|
33
|
+
request.body = { encrypted_content: encrypted_content, force: @force }.to_json
|
34
|
+
%w[200 201].include?(http.request(request).code)
|
15
35
|
end
|
16
36
|
|
17
37
|
private
|
18
38
|
|
19
39
|
def http
|
20
|
-
Net::HTTP.new(@uri.host, @uri.port)
|
40
|
+
@http ||= Net::HTTP.new(@uri.host, @uri.port).tap do |http|
|
41
|
+
http.use_ssl = @uri.scheme.eql?('https')
|
42
|
+
end
|
21
43
|
end
|
22
44
|
|
23
45
|
def request
|
24
|
-
@request ||= Net::HTTP::Post.new(@uri.path, headers)
|
46
|
+
@request ||= Net::HTTP::Post.new(@uri.path, headers).tap do |request|
|
47
|
+
request["User-Agent"] = format('ccs/%s', Ccs::VERSION)
|
48
|
+
end
|
25
49
|
end
|
26
50
|
|
27
51
|
def headers
|
@@ -32,7 +56,7 @@ module Ccs
|
|
32
56
|
end
|
33
57
|
|
34
58
|
def encrypted_content
|
35
|
-
@encrypted_content ||= Encrypter.new(@
|
59
|
+
@encrypted_content ||= Encrypter.new(@passphrase, @content, salt).call
|
36
60
|
end
|
37
61
|
|
38
62
|
def salt
|
data/lib/ccs/version.rb
CHANGED
metadata
CHANGED
@@ -1,43 +1,30 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ccs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 3.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- tkowalewski
|
8
|
+
- paweljw
|
8
9
|
autorequire:
|
9
10
|
bindir: exe
|
10
11
|
cert_chain: []
|
11
|
-
date:
|
12
|
+
date: 2020-11-23 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: bundler
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 1.16.1
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: 1.16.1
|
27
14
|
- !ruby/object:Gem::Dependency
|
28
15
|
name: inch
|
29
16
|
requirement: !ruby/object:Gem::Requirement
|
30
17
|
requirements:
|
31
18
|
- - "~>"
|
32
19
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.
|
20
|
+
version: 0.8.0
|
34
21
|
type: :development
|
35
22
|
prerelease: false
|
36
23
|
version_requirements: !ruby/object:Gem::Requirement
|
37
24
|
requirements:
|
38
25
|
- - "~>"
|
39
26
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.
|
27
|
+
version: 0.8.0
|
41
28
|
- !ruby/object:Gem::Dependency
|
42
29
|
name: pry
|
43
30
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,14 +45,14 @@ dependencies:
|
|
58
45
|
requirements:
|
59
46
|
- - "~>"
|
60
47
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
48
|
+
version: 13.0.1
|
62
49
|
type: :development
|
63
50
|
prerelease: false
|
64
51
|
version_requirements: !ruby/object:Gem::Requirement
|
65
52
|
requirements:
|
66
53
|
- - "~>"
|
67
54
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
55
|
+
version: 13.0.1
|
69
56
|
- !ruby/object:Gem::Dependency
|
70
57
|
name: reek
|
71
58
|
requirement: !ruby/object:Gem::Requirement
|
@@ -140,19 +127,20 @@ dependencies:
|
|
140
127
|
name: yard
|
141
128
|
requirement: !ruby/object:Gem::Requirement
|
142
129
|
requirements:
|
143
|
-
- - "
|
130
|
+
- - ">="
|
144
131
|
- !ruby/object:Gem::Version
|
145
|
-
version: 0.
|
132
|
+
version: 0.9.11
|
146
133
|
type: :development
|
147
134
|
prerelease: false
|
148
135
|
version_requirements: !ruby/object:Gem::Requirement
|
149
136
|
requirements:
|
150
|
-
- - "
|
137
|
+
- - ">="
|
151
138
|
- !ruby/object:Gem::Version
|
152
|
-
version: 0.
|
139
|
+
version: 0.9.11
|
153
140
|
description: ''
|
154
141
|
email:
|
155
142
|
- me@tkowalewski.pl
|
143
|
+
- p@steamshard.net
|
156
144
|
executables:
|
157
145
|
- ccs
|
158
146
|
extensions: []
|
@@ -164,14 +152,14 @@ files:
|
|
164
152
|
- README.md
|
165
153
|
- exe/ccs
|
166
154
|
- lib/ccs.rb
|
167
|
-
- lib/ccs/
|
168
|
-
- lib/ccs/configuration_file.rb
|
155
|
+
- lib/ccs/commands/copy.rb
|
169
156
|
- lib/ccs/decrypter.rb
|
157
|
+
- lib/ccs/document.rb
|
170
158
|
- lib/ccs/downloader.rb
|
171
159
|
- lib/ccs/encrypter.rb
|
172
160
|
- lib/ccs/uploader.rb
|
173
161
|
- lib/ccs/version.rb
|
174
|
-
homepage: https://github.com/
|
162
|
+
homepage: https://github.com/occson/ccs
|
175
163
|
licenses:
|
176
164
|
- MIT
|
177
165
|
metadata: {}
|
@@ -186,12 +174,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
186
174
|
version: '0'
|
187
175
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
188
176
|
requirements:
|
189
|
-
- - "
|
177
|
+
- - ">="
|
190
178
|
- !ruby/object:Gem::Version
|
191
|
-
version:
|
179
|
+
version: '0'
|
192
180
|
requirements: []
|
193
|
-
|
194
|
-
rubygems_version: 2.7.3
|
181
|
+
rubygems_version: 3.0.3
|
195
182
|
signing_key:
|
196
183
|
specification_version: 4
|
197
184
|
summary: Configuration Control System (CCS)
|
data/lib/ccs/application.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Ccs
|
4
|
-
class Application
|
5
|
-
def initialize(source, destination, access_token, secret_token)
|
6
|
-
@source = source
|
7
|
-
@destination = destination
|
8
|
-
@access_token = access_token
|
9
|
-
@secret_token = secret_token
|
10
|
-
end
|
11
|
-
|
12
|
-
def run
|
13
|
-
download? ? download : upload
|
14
|
-
end
|
15
|
-
|
16
|
-
private
|
17
|
-
|
18
|
-
def download?
|
19
|
-
@source.match?(%r{\A(ccs|https?):\/\/})
|
20
|
-
end
|
21
|
-
|
22
|
-
def download
|
23
|
-
content = ConfigurationFile.new(@source, @access_token, @secret_token).download
|
24
|
-
return unless content
|
25
|
-
|
26
|
-
(@destination.eql?('-') ? STDOUT : File.new(@destination, 'w')).print content
|
27
|
-
end
|
28
|
-
|
29
|
-
def upload
|
30
|
-
content = @source.eql?('-') ? STDIN.read : File.read(@source)
|
31
|
-
ConfigurationFile.new(@destination, @access_token, @secret_token).upload(content)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Ccs
|
4
|
-
class ConfigurationFile
|
5
|
-
def initialize(uri, access_token, secret_token)
|
6
|
-
@uri = build_uri(uri)
|
7
|
-
@access_token = access_token
|
8
|
-
@secret_token = secret_token
|
9
|
-
end
|
10
|
-
|
11
|
-
def upload(content)
|
12
|
-
Uploader.new(@uri, content, @access_token, @secret_token).call
|
13
|
-
end
|
14
|
-
|
15
|
-
def download
|
16
|
-
Downloader.new(@uri, @access_token, @secret_token).call
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def build_uri(uri)
|
22
|
-
URI uri.sub('ccs://', 'https://api.occson.com/')
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|