occson 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b08e08740cea055b6c5a32c1380dd6eb7dfa8c4f75d1fadcb717c6f2f6a7022d
4
+ data.tar.gz: 11808e7cb917b8abf9a0f290289cc0353e3522695725cc0055c3ebd4eed0254e
5
+ SHA512:
6
+ metadata.gz: 805566d333b73c0ff8e4796476195e361fcda648360e7a6dba99858f7603fe605fefa263d93b6fa3ac053208e0e4525eb686bd963d5530f36001992b83aee7af
7
+ data.tar.gz: 4976e02e9f0810e5dda2e80cb4370b611587d1a17597a1810e1f5f6288b6d81b0701a84e6a294c14f1e1221d76c15cb391c560bffe4894ce61c5af3025cc0ddf
data/CHANGELOG.md ADDED
@@ -0,0 +1,29 @@
1
+ ## 4.0.0 2021-10-06
2
+
3
+ ### Changed
4
+
5
+ - rename css to occson
6
+
7
+ [Compare 3.1.1...4.0.0](https://github.com/occson/occson.rb/compare/3.1.1...4.0.0)
8
+
9
+
10
+ ## 3.1.1 2020-12-07
11
+
12
+
13
+ ### Fixed
14
+
15
+ - fix loading ccs library in executable (tkowalewski)
16
+
17
+ [Compare 3.1.0...3.1.1](https://github.com/occson/occson.rb/compare/3.1.0...3.1.1)
18
+
19
+
20
+ ## 3.1.0 2020-11-23
21
+
22
+
23
+ ### Added
24
+
25
+ - `force` argument to overwrite remote documents when uploading (paweljw)
26
+ - YARD documentation (paweljw)
27
+
28
+
29
+ [Compare 3.0.0...3.1.0](https://github.com/occson/occson.rb/compare/3.0.0...3.1.0)
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at me@tkowalewski.pl. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 me@tkowalewski.pl
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.
data/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # OCCSON
2
+
3
+ Store, manage and deploy configuration securely with Occson.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'occson'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install occson
18
+
19
+ ## Usage
20
+
21
+ occson cp [OPTIONS] <(LocalPath|STDIN)|(OccsonUri|Uri)> <(OccsonUri|Uri)|(LocalPath|STDOUT)>
22
+
23
+ Options:
24
+ -a OCCSON_ACCESS_TOKEN, Occson access token
25
+ --access-token
26
+ -p OCCSON_PASSPHRASE, Occson passphrase
27
+ --passphrase
28
+ -f, --[no-]force Overwrite remote documents when uploading
29
+
30
+
31
+ ## Example
32
+
33
+ Download to STDOUT
34
+
35
+ occson cp occson://0.1.0/path/to/file.yml -
36
+ occson cp https://api.occson.com/0.1.0/path/to/file.yml -
37
+ occson cp http://host.tld:9292/0.1.0/path/to/file.yml -
38
+ occson cp https://host.tld/0.1.0/path/to/file.yml -
39
+
40
+ Download to local file
41
+
42
+ occson cp occson://0.1.0/path/to/file.yml /local/path/to/file.yml
43
+
44
+ Upload local file
45
+
46
+ occson cp /local/path/to/file.yml occson://0.1.0/path/to/file.yml
47
+
48
+ Upload local file, overwriting remote document if any'
49
+
50
+ occson cp --force /local/path/to/file.yml occson://0.1.0/path/to/file.yml
51
+
52
+ Upload content from STDIN
53
+
54
+ echo "{ a: 1 }" | occson cp - occson://0.1.0/path/to/file.yml
55
+ cat /local/path/to/file.yml | occson cp - occson://0.1.0/path/to/file.yml
56
+
57
+ ## API
58
+
59
+ Upload
60
+
61
+ require 'occson'
62
+
63
+ destination = 'occson://0.1.0/path/to/file.yml'
64
+ access_token = ENV.fetch('OCCSON_ACCESS_TOKEN')
65
+ passphrase = 'MyPassphrase'
66
+ content = 'RAILS_ENV=production'
67
+
68
+ Occson::Document.new(destination, access_token, passphrase).upload(content)
69
+
70
+ Download
71
+
72
+ require 'occson'
73
+
74
+ source = 'occson://0.1.0/path/to/file.yml'
75
+ access_token = ENV.fetch('OCCSON_ACCESS_TOKEN')
76
+ passphrase = 'MyPassphrase'
77
+
78
+ Occson::Document.new(source, access_token, passphrase).download
79
+
80
+ ## Development
81
+
82
+ 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.
83
+
84
+ 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).
85
+
86
+ ## Documentation
87
+
88
+ Documentation for the code should be written in YARD format. It can be generated locally into the `doc` directory by running
89
+
90
+ bundle exec yard
91
+
92
+
93
+ ## Contributing
94
+
95
+ Bug reports and pull requests are welcome on GitHub at https://github.com/occson/occson.rb. 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.
96
+
97
+
98
+ ## License
99
+
100
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
101
+
data/exe/occson ADDED
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift("#{__dir__}/../lib")
5
+
6
+ require 'occson'
7
+ require 'optparse'
8
+ require 'io/console'
9
+
10
+ options = {
11
+ 'access_token' => ENV['OCCSON_ACCESS_TOKEN'],
12
+ 'passphrase' => ENV['OCCSON_PASSPHRASE']
13
+ }
14
+
15
+ option_parser = OptionParser.new do |option_parser|
16
+ option_parser.banner = 'Usage: occson [COMMAND [OPTIONS]]'
17
+
18
+ option_parser.separator ''
19
+ option_parser.separator 'Store, manage and deploy configuration securely with Occson.'
20
+ option_parser.separator ''
21
+ option_parser.separator 'Commands:'
22
+ option_parser.separator ' cp Copy'
23
+
24
+ option_parser.separator ''
25
+ option_parser.separator format('Version: %<version>s', version: Occson::VERSION)
26
+ end
27
+
28
+ case ARGV.shift
29
+ when 'cp'
30
+ option_parser = OptionParser.new do |option_parser|
31
+ option_parser.banner = 'Usage: occson cp [OPTIONS] <(LocalPath|STDIN)|(OccsonUri|Uri)> <(OccsonUri|Uri)|(LocalPath|STDOUT)>'
32
+ option_parser.separator ''
33
+ option_parser.separator 'Store, manage and deploy configuration securely with Occson.'
34
+ option_parser.separator ''
35
+ option_parser.separator 'Options:'
36
+
37
+ option_parser.on('-a OCCSON_ACCESS_TOKEN', '--access-token OCCSON_ACCESS_TOKEN', String, 'Occson access token') do |v|
38
+ options['access_token'] = v
39
+ end
40
+
41
+ option_parser.on('-p OCCSON_PASSPHRASE', '--passphrase OCCSON_PASSPHRASE', String, 'Occson 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
48
+
49
+ option_parser.separator ''
50
+ option_parser.separator 'Configure via environment variables:'
51
+ option_parser.separator ' OCCSON_ACCESS_TOKEN'
52
+ option_parser.separator ' OCCSON_PASSPHRASE'
53
+
54
+ option_parser.separator ''
55
+ option_parser.separator 'Examples:'
56
+ option_parser.separator ' Download to STDOUT'
57
+ option_parser.separator ' occson cp occson://0.1.0/path/to/file.yml -'
58
+ option_parser.separator ' occson cp https://api.occson.com/0.1.0/path/to/file.yml -'
59
+ option_parser.separator ' occson cp http://host.tld:9292/0.1.0/path/to/file.yml -'
60
+ option_parser.separator ' occson cp https://host.tld/0.1.0/path/to/file.yml -'
61
+ option_parser.separator ' Download to local file'
62
+ option_parser.separator ' occson cp occson://0.1.0/path/to/file.yml /local/path/to/file.yml'
63
+ option_parser.separator ' Upload local file'
64
+ option_parser.separator ' occson cp /local/path/to/file.yml occson://0.1.0/path/to/file.yml'
65
+ option_parser.separator ' Upload local file, overwriting remote document if any'
66
+ option_parser.separator ' occson cp --force /local/path/to/file.yml occson://0.1.0/path/to/file.yml'
67
+ option_parser.separator ' Upload content from STDIN'
68
+ option_parser.separator ' echo "{ a: 1 }" | occson cp - occson://0.1.0/path/to/file.yml'
69
+ option_parser.separator ' cat /local/path/to/file.yml | occson cp - occson://0.1.0/path/to/file.yml'
70
+ option_parser.separator ''
71
+
72
+ option_parser.separator format('Version: %<version>s', version: Occson::VERSION)
73
+ end
74
+
75
+ arguments = option_parser.parse!
76
+
77
+ unless options.values.any?
78
+ puts option_parser
79
+ exit(1)
80
+ end
81
+
82
+ raise OptionParser::MissingArgument, 'access_token' unless options['access_token']
83
+ raise OptionParser::MissingArgument, 'passphrase' unless options['passphrase']
84
+ raise OptionParser::MissingArgument, 'source' unless arguments.fetch(0, nil)
85
+ raise OptionParser::MissingArgument, 'destination' unless arguments.fetch(1, nil)
86
+
87
+ exit(1) unless Occson::Commands::Copy.new(arguments[0], arguments[1], options['access_token'], options['passphrase'], force: options.fetch('force', false)).call
88
+ else
89
+ puts option_parser
90
+ exit(1)
91
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Occson
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 `occson://` 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(occson|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
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Occson
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.
9
+ class Decrypter
10
+ # Constructs a Decrypter instance with given passphrase and content.
11
+ #
12
+ # @example
13
+ # Occson::Decrypter.new('the content passphrase', content)
14
+ #
15
+ # @param passphrase [String] Passphrase for content decryption
16
+ # @param content [String] Encrypted document content
17
+ def initialize(passphrase, content)
18
+ @passphrase = passphrase
19
+ @content = content
20
+ end
21
+
22
+ # Performs decryption, returning plaintext if passphrase matched.
23
+ #
24
+ # @return [String] Plaintext document content
25
+ def call
26
+ decryptor.pkcs5_keyivgen(@passphrase, ciphertext_salt, 1)
27
+ result = decryptor.update(encrypted)
28
+ result << decryptor.final
29
+ end
30
+
31
+ private
32
+
33
+ def decryptor
34
+ @decryptor ||= OpenSSL::Cipher::AES.new(256, :CBC).decrypt
35
+ end
36
+
37
+ def openssl_salted_ciphertext
38
+ @openssl_salted_ciphertext ||= Base64.strict_decode64 @content
39
+ end
40
+
41
+ def ciphertext_salt
42
+ openssl_salted_ciphertext[8..15]
43
+ end
44
+
45
+ def encrypted
46
+ openssl_salted_ciphertext[16..-1]
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Occson
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 = 'occson://path/to/file.yml'
12
+ # access_token = 'f30b5450421362c9ca0b'
13
+ # passphrase = 'my document passphrase'
14
+ #
15
+ # Occson::Document.new(uri, access_token, passphrase)
16
+ #
17
+ # @param uri [String] Document URI. Accepts `occson://` 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('occson://', 'https://api.occson.com/')
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Occson
4
+ # Downloads and decrypts the document at given URI with given access token.
5
+ # Decryption occurs using given passphrase.
6
+ class Downloader
7
+ # Constructs a Downloader instance from a given URI, access token and passphrase.
8
+ #
9
+ # @example
10
+ # uri = 'occson://path/to/file.yml'
11
+ # access_token = 'f30b5450421362c9ca0b'
12
+ # passphrase = 'my document passphrase'
13
+ #
14
+ # Occson::Downloader.new(uri, access_token, passphrase)
15
+ #
16
+ # @param uri [String] Document URI. Accepts `occson://` 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)
20
+ @uri = uri
21
+ @access_token = access_token
22
+ @passphrase = passphrase
23
+ end
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.
29
+ def call
30
+ response = http.request(request)
31
+ body = response.body
32
+ return unless response.code.eql? '200'
33
+ json = JSON.parse body
34
+
35
+ Decrypter.new(@passphrase, json['encrypted_content']).call
36
+ end
37
+
38
+ private
39
+
40
+ def http
41
+ @http ||= Net::HTTP.new(@uri.host, @uri.port).tap do |http|
42
+ http.use_ssl = @uri.scheme.eql?('https')
43
+ end
44
+ end
45
+
46
+ def request
47
+ Net::HTTP::Get.new(@uri.path, headers).tap do |request|
48
+ request["User-Agent"] = format('occson/%s', Occson::VERSION)
49
+ end
50
+ end
51
+
52
+ def headers
53
+ {
54
+ 'Authorization' => format('Token token=%<access_token>s', access_token: @access_token),
55
+ 'Content-Type' => 'application/json'
56
+ }
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Occson
4
+ # Encrypts the given content for transmission. Uses AES-256 in CBC
5
+ # mode internally, with salting.
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
+ # Occson::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.
21
+ def initialize(passphrase, content, salt)
22
+ @passphrase = passphrase
23
+ @content = content
24
+ @salt = salt
25
+ end
26
+
27
+ # Performs the actual encryption, returning base64-encoded ciphertext.
28
+ #
29
+ # @return [String] base64-encoded ciphertext
30
+ def call
31
+ encryptor.pkcs5_keyivgen(@passphrase, @salt, 1)
32
+ encrypted = encryptor.update(@content)
33
+ encrypted << encryptor.final
34
+
35
+ openssl_salted_ciphertext = 'Salted__' + @salt + encrypted
36
+ Base64.strict_encode64(openssl_salted_ciphertext)
37
+ end
38
+
39
+ private
40
+
41
+ def encryptor
42
+ @encryptor ||= OpenSSL::Cipher::AES.new(256, :CBC).encrypt
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Occson
4
+ # Encrypts and uploads the document to Occson.
5
+ class Uploader
6
+ # Constructs an Uploader instance from a given URI, content, access token and passphrase.
7
+ #
8
+ # @example
9
+ # uri = 'occson://path/to/file.yml'
10
+ # content = 'my very secret message'
11
+ # access_token = 'f30b5450421362c9ca0b'
12
+ # passphrase = 'my document passphrase'
13
+ #
14
+ # Occson::Uploader.new(uri, access_token, passphrase)
15
+ #
16
+ # @param uri [String] Document URI. Accepts `occson://` 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)
22
+ @uri = uri
23
+ @content = content
24
+ @access_token = access_token
25
+ @passphrase = passphrase
26
+ @force = force.to_s
27
+ end
28
+
29
+ # Performs the actual upload to server.
30
+ #
31
+ # @return [Boolean] `true` for a successful upload, `false` otherwise
32
+ def call
33
+ request.body = { encrypted_content: encrypted_content, force: @force }.to_json
34
+ %w[200 201].include?(http.request(request).code)
35
+ end
36
+
37
+ private
38
+
39
+ def http
40
+ @http ||= Net::HTTP.new(@uri.host, @uri.port).tap do |http|
41
+ http.use_ssl = @uri.scheme.eql?('https')
42
+ end
43
+ end
44
+
45
+ def request
46
+ @request ||= Net::HTTP::Post.new(@uri.path, headers).tap do |request|
47
+ request["User-Agent"] = format('occson/%s', Occson::VERSION)
48
+ end
49
+ end
50
+
51
+ def headers
52
+ {
53
+ 'Authorization' => format('Token token=%<access_token>s', access_token: @access_token),
54
+ 'Content-Type' => 'application/json'
55
+ }
56
+ end
57
+
58
+ def encrypted_content
59
+ @encrypted_content ||= Encrypter.new(@passphrase, @content, salt).call
60
+ end
61
+
62
+ def salt
63
+ @access_token[0...8]
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Occson
4
+ # Occson gem version definition
5
+ VERSION = '4.0.0'
6
+ end
data/lib/occson.rb ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'json'
5
+ require 'openssl'
6
+ require 'base64'
7
+ require 'uri'
8
+
9
+ require 'occson/version'
10
+ require 'occson/encrypter'
11
+ require 'occson/decrypter'
12
+ require 'occson/uploader'
13
+ require 'occson/downloader'
14
+ require 'occson/document'
15
+
16
+ require 'occson/commands/copy'
17
+
18
+ # Top level `Occson` namespace.
19
+ module Occson; end
metadata ADDED
@@ -0,0 +1,185 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: occson
3
+ version: !ruby/object:Gem::Version
4
+ version: 4.0.0
5
+ platform: ruby
6
+ authors:
7
+ - tkowalewski
8
+ - paweljw
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2021-10-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: inch
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 0.8.0
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 0.8.0
28
+ - !ruby/object:Gem::Dependency
29
+ name: pry
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: 0.11.3
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: 0.11.3
42
+ - !ruby/object:Gem::Dependency
43
+ name: rake
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: 13.0.1
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: 13.0.1
56
+ - !ruby/object:Gem::Dependency
57
+ name: reek
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: 4.7.3
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: 4.7.3
70
+ - !ruby/object:Gem::Dependency
71
+ name: rspec
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: 3.7.0
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: 3.7.0
84
+ - !ruby/object:Gem::Dependency
85
+ name: rubocop
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: 0.52.1
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: 0.52.1
98
+ - !ruby/object:Gem::Dependency
99
+ name: simplecov
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: 0.15.1
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: 0.15.1
112
+ - !ruby/object:Gem::Dependency
113
+ name: webmock
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: 3.3.0
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: 3.3.0
126
+ - !ruby/object:Gem::Dependency
127
+ name: yard
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: 0.9.11
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: 0.9.11
140
+ description: ''
141
+ email:
142
+ - me@tkowalewski.pl
143
+ - p@steamshard.net
144
+ executables:
145
+ - occson
146
+ extensions: []
147
+ extra_rdoc_files: []
148
+ files:
149
+ - CHANGELOG.md
150
+ - CODE_OF_CONDUCT.md
151
+ - LICENSE.txt
152
+ - README.md
153
+ - exe/occson
154
+ - lib/occson.rb
155
+ - lib/occson/commands/copy.rb
156
+ - lib/occson/decrypter.rb
157
+ - lib/occson/document.rb
158
+ - lib/occson/downloader.rb
159
+ - lib/occson/encrypter.rb
160
+ - lib/occson/uploader.rb
161
+ - lib/occson/version.rb
162
+ homepage: https://github.com/occson/occson.rb
163
+ licenses:
164
+ - MIT
165
+ metadata: {}
166
+ post_install_message:
167
+ rdoc_options: []
168
+ require_paths:
169
+ - lib
170
+ required_ruby_version: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ version: '0'
175
+ required_rubygems_version: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ requirements: []
181
+ rubygems_version: 3.0.3
182
+ signing_key:
183
+ specification_version: 4
184
+ summary: Store, manage and deploy configuration securely with Occson.
185
+ test_files: []