s3-secure 0.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +89 -0
- data/Guardfile +19 -0
- data/LICENSE.txt +22 -0
- data/README.md +56 -0
- data/Rakefile +14 -0
- data/exe/s3-secure +14 -0
- data/lib/s3-secure.rb +1 -0
- data/lib/s3_secure.rb +14 -0
- data/lib/s3_secure/abstract_base.rb +17 -0
- data/lib/s3_secure/autoloader.rb +27 -0
- data/lib/s3_secure/aws_services.rb +36 -0
- data/lib/s3_secure/batch.rb +25 -0
- data/lib/s3_secure/cli.rb +37 -0
- data/lib/s3_secure/command.rb +82 -0
- data/lib/s3_secure/completer.rb +159 -0
- data/lib/s3_secure/completer/script.rb +6 -0
- data/lib/s3_secure/completer/script.sh +10 -0
- data/lib/s3_secure/encryption.rb +27 -0
- data/lib/s3_secure/encryption/base.rb +4 -0
- data/lib/s3_secure/encryption/disable.rb +18 -0
- data/lib/s3_secure/encryption/enable.rb +42 -0
- data/lib/s3_secure/encryption/list.rb +28 -0
- data/lib/s3_secure/encryption/show.rb +18 -0
- data/lib/s3_secure/help.rb +9 -0
- data/lib/s3_secure/help/completion.md +20 -0
- data/lib/s3_secure/help/completion_script.md +3 -0
- data/lib/s3_secure/help/hello.md +5 -0
- data/lib/s3_secure/policy.rb +27 -0
- data/lib/s3_secure/policy/base.rb +4 -0
- data/lib/s3_secure/policy/checker.rb +15 -0
- data/lib/s3_secure/policy/document.rb +27 -0
- data/lib/s3_secure/policy/document/base.rb +15 -0
- data/lib/s3_secure/policy/document/force_ssl_only_access.rb +33 -0
- data/lib/s3_secure/policy/document/force_ssl_only_access_remove.rb +33 -0
- data/lib/s3_secure/policy/enforce.rb +36 -0
- data/lib/s3_secure/policy/list.rb +29 -0
- data/lib/s3_secure/policy/show.rb +19 -0
- data/lib/s3_secure/policy/unforce.rb +41 -0
- data/lib/s3_secure/version.rb +3 -0
- data/s3-secure.gemspec +33 -0
- data/spec/lib/cli_spec.rb +12 -0
- data/spec/lib/policy/checker_spec.rb +68 -0
- data/spec/lib/policy/document/force_ssl_remove_spec.rb +107 -0
- data/spec/lib/policy/document_spec.rb +68 -0
- data/spec/spec_helper.rb +29 -0
- metadata +252 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 0ac426bde14db38fecbc1d692dc5c79ddd4078da837d9b6d389752fdb96d5805
|
4
|
+
data.tar.gz: 2f8a8aaf36996722a23c4b0afd95ff05936ed12e4c1e384f9594cd4b22cc4054
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c0ff5d52734ae470d203b43c19fff7c87924709b15cb5f86420faa2886a4e43bc4ead66cd531fd9a98dcb480143db2286a9c58af8d3be1e4357966d357c78ac4
|
7
|
+
data.tar.gz: 95ca0ef971df08986f2dfb7b9fd6bcec8d1097d44fb38fc999d456e6863f0a7ba28767824a9c203c4c9f78c81b9074f16811f762efc8fc3276ec25f2e4bdcf93
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
s3-secure (0.1.0)
|
5
|
+
activesupport
|
6
|
+
aws-sdk-s3
|
7
|
+
memoist
|
8
|
+
rainbow
|
9
|
+
thor
|
10
|
+
zeitwerk
|
11
|
+
|
12
|
+
GEM
|
13
|
+
remote: https://rubygems.org/
|
14
|
+
specs:
|
15
|
+
activesupport (6.0.1)
|
16
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
17
|
+
i18n (>= 0.7, < 2)
|
18
|
+
minitest (~> 5.1)
|
19
|
+
tzinfo (~> 1.1)
|
20
|
+
zeitwerk (~> 2.2)
|
21
|
+
aws-eventstream (1.0.3)
|
22
|
+
aws-partitions (1.240.0)
|
23
|
+
aws-sdk-core (3.78.0)
|
24
|
+
aws-eventstream (~> 1.0, >= 1.0.2)
|
25
|
+
aws-partitions (~> 1, >= 1.239.0)
|
26
|
+
aws-sigv4 (~> 1.1)
|
27
|
+
jmespath (~> 1.0)
|
28
|
+
aws-sdk-kms (1.25.0)
|
29
|
+
aws-sdk-core (~> 3, >= 3.71.0)
|
30
|
+
aws-sigv4 (~> 1.1)
|
31
|
+
aws-sdk-s3 (1.56.0)
|
32
|
+
aws-sdk-core (~> 3, >= 3.77.0)
|
33
|
+
aws-sdk-kms (~> 1)
|
34
|
+
aws-sigv4 (~> 1.1)
|
35
|
+
aws-sigv4 (1.1.0)
|
36
|
+
aws-eventstream (~> 1.0, >= 1.0.2)
|
37
|
+
byebug (11.0.1)
|
38
|
+
cli_markdown (0.1.0)
|
39
|
+
codeclimate-test-reporter (1.0.9)
|
40
|
+
simplecov (<= 0.13)
|
41
|
+
concurrent-ruby (1.1.5)
|
42
|
+
diff-lcs (1.3)
|
43
|
+
docile (1.1.5)
|
44
|
+
i18n (1.7.0)
|
45
|
+
concurrent-ruby (~> 1.0)
|
46
|
+
jmespath (1.4.0)
|
47
|
+
json (2.2.0)
|
48
|
+
memoist (0.16.1)
|
49
|
+
minitest (5.13.0)
|
50
|
+
rainbow (3.0.0)
|
51
|
+
rake (13.0.1)
|
52
|
+
rspec (3.9.0)
|
53
|
+
rspec-core (~> 3.9.0)
|
54
|
+
rspec-expectations (~> 3.9.0)
|
55
|
+
rspec-mocks (~> 3.9.0)
|
56
|
+
rspec-core (3.9.0)
|
57
|
+
rspec-support (~> 3.9.0)
|
58
|
+
rspec-expectations (3.9.0)
|
59
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
60
|
+
rspec-support (~> 3.9.0)
|
61
|
+
rspec-mocks (3.9.0)
|
62
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
63
|
+
rspec-support (~> 3.9.0)
|
64
|
+
rspec-support (3.9.0)
|
65
|
+
simplecov (0.13.0)
|
66
|
+
docile (~> 1.1.0)
|
67
|
+
json (>= 1.8, < 3)
|
68
|
+
simplecov-html (~> 0.10.0)
|
69
|
+
simplecov-html (0.10.2)
|
70
|
+
thor (0.20.3)
|
71
|
+
thread_safe (0.3.6)
|
72
|
+
tzinfo (1.2.5)
|
73
|
+
thread_safe (~> 0.1)
|
74
|
+
zeitwerk (2.2.1)
|
75
|
+
|
76
|
+
PLATFORMS
|
77
|
+
ruby
|
78
|
+
|
79
|
+
DEPENDENCIES
|
80
|
+
bundler
|
81
|
+
byebug
|
82
|
+
cli_markdown
|
83
|
+
codeclimate-test-reporter
|
84
|
+
rake
|
85
|
+
rspec
|
86
|
+
s3-secure!
|
87
|
+
|
88
|
+
BUNDLED WITH
|
89
|
+
2.0.2
|
data/Guardfile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
guard "bundler", cmd: "bundle" do
|
2
|
+
watch("Gemfile")
|
3
|
+
watch(/^.+\.gemspec/)
|
4
|
+
end
|
5
|
+
|
6
|
+
guard :rspec, cmd: "bundle exec rspec" do
|
7
|
+
require "guard/rspec/dsl"
|
8
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
9
|
+
|
10
|
+
# RSpec files
|
11
|
+
rspec = dsl.rspec
|
12
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
13
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
14
|
+
watch(rspec.spec_files)
|
15
|
+
|
16
|
+
# Ruby files
|
17
|
+
ruby = dsl.ruby
|
18
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
19
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2019 Tung Nguyen
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# s3-secure
|
2
|
+
|
3
|
+
[](http://badge.fury.io/rb/s3-secure)
|
4
|
+
|
5
|
+
[](https://www.boltops.com)
|
6
|
+
|
7
|
+
The s3-secure tool can be used to harden your s3 bucket security posture. The tool is useful if you have a lot of buckets to update. It supports:
|
8
|
+
|
9
|
+
* enabling encryption
|
10
|
+
* adding an enforce ssl bucket policy
|
11
|
+
|
12
|
+
## Usage
|
13
|
+
|
14
|
+
Summary of encryption commands:
|
15
|
+
|
16
|
+
s3-secure encryption list
|
17
|
+
s3-secure encryption show BUCKET
|
18
|
+
s3-secure encryption enable BUCKET
|
19
|
+
s3-secure encryption disable BUCKET
|
20
|
+
|
21
|
+
Summary of policy commands:
|
22
|
+
|
23
|
+
s3-secure policy list
|
24
|
+
s3-secure policy show BUCKET
|
25
|
+
s3-secure policy enforce_ssl BUCKET
|
26
|
+
s3-secure policy unforce_ssl BUCKET
|
27
|
+
|
28
|
+
## Batch Commands
|
29
|
+
|
30
|
+
There are some supported batch commands:
|
31
|
+
|
32
|
+
s3-secure batch encryption enable FILE.txt
|
33
|
+
s3-secure batch encryption disable FILE.txt
|
34
|
+
s3-secure batch policy enforce_ssl FILE.txt
|
35
|
+
s3-secure batch policy unforce_ssl FILE.txt
|
36
|
+
|
37
|
+
The format of FILE.txt is a list of bucket names separated by newlines. Example:
|
38
|
+
|
39
|
+
buckets.txt:
|
40
|
+
|
41
|
+
my-bucket-1
|
42
|
+
my-bucket-2
|
43
|
+
|
44
|
+
## Installation
|
45
|
+
|
46
|
+
Install with the `gem` command:
|
47
|
+
|
48
|
+
gem install s3-secure
|
49
|
+
|
50
|
+
## Contributing
|
51
|
+
|
52
|
+
1. Fork it
|
53
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
54
|
+
3. Commit your changes (`git commit -am "Add some feature"`)
|
55
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
56
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
|
4
|
+
task default: :spec
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new
|
7
|
+
|
8
|
+
require_relative "lib/s3-secure"
|
9
|
+
require "cli_markdown"
|
10
|
+
desc "Generates cli reference docs as markdown"
|
11
|
+
task :docs do
|
12
|
+
mkdir_p "docs/_includes"
|
13
|
+
CliMarkdown::Creator.create_all(cli_class: S3Secure::CLI, cli_name: "s3-secure")
|
14
|
+
end
|
data/exe/s3-secure
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Trap ^C
|
4
|
+
Signal.trap("INT") {
|
5
|
+
puts "\nCtrl-C detected. Exiting..."
|
6
|
+
sleep 0.1
|
7
|
+
exit
|
8
|
+
}
|
9
|
+
|
10
|
+
$:.unshift(File.expand_path("../../lib", __FILE__))
|
11
|
+
require "s3-secure"
|
12
|
+
require "s3_secure/cli"
|
13
|
+
|
14
|
+
S3Secure::CLI.start(ARGV)
|
data/lib/s3-secure.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative "s3_secure"
|
data/lib/s3_secure.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
$:.unshift(File.expand_path("../", __FILE__))
|
2
|
+
require "json"
|
3
|
+
require "memoist"
|
4
|
+
require "rainbow/ext/string"
|
5
|
+
require "s3_secure/version"
|
6
|
+
require "active_support/core_ext/module" # for delegate
|
7
|
+
require "active_support/core_ext/string"
|
8
|
+
|
9
|
+
require "s3_secure/autoloader"
|
10
|
+
S3Secure::Autoloader.setup
|
11
|
+
|
12
|
+
module S3Secure
|
13
|
+
class Error < StandardError; end
|
14
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module S3Secure
|
2
|
+
class AbstractBase
|
3
|
+
include S3Secure::AwsServices
|
4
|
+
extend Memoist
|
5
|
+
|
6
|
+
def initialize(options={})
|
7
|
+
@options = options
|
8
|
+
@bucket = options[:bucket] # not set on the list command but common enough to set here
|
9
|
+
end
|
10
|
+
|
11
|
+
def buckets
|
12
|
+
resp = s3_client.list_buckets
|
13
|
+
resp.buckets.map(&:name)
|
14
|
+
end
|
15
|
+
memoize :buckets
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "zeitwerk"
|
2
|
+
|
3
|
+
module S3Secure
|
4
|
+
class Autoloader
|
5
|
+
class Inflector < Zeitwerk::Inflector
|
6
|
+
def camelize(basename, _abspath)
|
7
|
+
map = {
|
8
|
+
cli: "CLI",
|
9
|
+
force_ssl_only_access: "ForceSSLOnlyAccess",
|
10
|
+
force_ssl_only_access_remove: "ForceSSLOnlyAccessRemove",
|
11
|
+
version: "VERSION",
|
12
|
+
}
|
13
|
+
map[basename.to_sym] || super
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def setup
|
19
|
+
loader = Zeitwerk::Loader.new
|
20
|
+
loader.inflector = Inflector.new
|
21
|
+
loader.push_dir(File.dirname(__dir__)) # lib
|
22
|
+
loader.ignore("#{File.dirname(__dir__)}/s3-secure.rb")
|
23
|
+
loader.setup
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "aws-sdk-s3"
|
2
|
+
|
3
|
+
module S3Secure
|
4
|
+
module AwsServices
|
5
|
+
extend Memoist
|
6
|
+
|
7
|
+
@@buckets = {} # holds bucket => region map
|
8
|
+
def s3_regional_client(bucket)
|
9
|
+
region = @@buckets[bucket]
|
10
|
+
|
11
|
+
unless region
|
12
|
+
resp = s3_client.get_bucket_location(bucket: bucket)
|
13
|
+
region = resp.location_constraint
|
14
|
+
region = 'us-east-1' if region.empty? # "" means us-east-1
|
15
|
+
end
|
16
|
+
|
17
|
+
new_s3_regional_client(region)
|
18
|
+
end
|
19
|
+
|
20
|
+
def new_s3_regional_client(region=nil)
|
21
|
+
options = {}
|
22
|
+
options[:endpoint] = "https://s3.#{region}.amazonaws.com" if region
|
23
|
+
options[:region] = region if region
|
24
|
+
Aws::S3::Client.new(options)
|
25
|
+
end
|
26
|
+
memoize :new_s3_regional_client
|
27
|
+
|
28
|
+
# Generic s3 client. Will be configured to whatever region user has locally configured in ~/.aws/config
|
29
|
+
# Used to call get_bucket_location to get each specific bucket's location.
|
30
|
+
# Generally use the s3_regional_client instead of this.
|
31
|
+
def s3_client
|
32
|
+
Aws::S3::Client.new
|
33
|
+
end
|
34
|
+
memoize :s3_client
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module S3Secure
|
2
|
+
class Batch
|
3
|
+
extend Memoist
|
4
|
+
|
5
|
+
def initialize(*params)
|
6
|
+
@params = params
|
7
|
+
@command, @subcommand, @file = params
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
buckets.each do |bucket|
|
12
|
+
args = @params
|
13
|
+
args.pop
|
14
|
+
args << bucket
|
15
|
+
puts "Running: s3-secure #{args.join(' ')}".color(:green)
|
16
|
+
S3Secure::CLI.start(args)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def buckets
|
21
|
+
IO.readlines(@file).map(&:strip).reject(&:empty?)
|
22
|
+
end
|
23
|
+
memoize :buckets
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module S3Secure
|
2
|
+
class CLI < Command
|
3
|
+
class_option :verbose, type: :boolean
|
4
|
+
class_option :noop, type: :boolean
|
5
|
+
|
6
|
+
desc "encryption SUBCOMMAND", "encryption subcommands"
|
7
|
+
long_desc Help.text(:encryption)
|
8
|
+
subcommand "encryption", Encryption
|
9
|
+
|
10
|
+
desc "policy SUBCOMMAND", "policy subcommands"
|
11
|
+
long_desc Help.text(:policy)
|
12
|
+
subcommand "policy", Policy
|
13
|
+
|
14
|
+
desc "batch *PARAMS", "Batch wrapper method"
|
15
|
+
long_desc Help.text(:batch)
|
16
|
+
def batch(*params)
|
17
|
+
Batch.new(*params).run
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "completion *PARAMS", "Prints words for auto-completion."
|
21
|
+
long_desc Help.text(:completion)
|
22
|
+
def completion(*params)
|
23
|
+
Completer.new(CLI, *params).run
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "completion_script", "Generates a script that can be eval to setup auto-completion."
|
27
|
+
long_desc Help.text(:completion_script)
|
28
|
+
def completion_script
|
29
|
+
Completer::Script.generate
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "version", "prints version"
|
33
|
+
def version
|
34
|
+
puts VERSION
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require "thor"
|
2
|
+
|
3
|
+
# Override thor's long_desc identation behavior
|
4
|
+
# https://github.com/erikhuda/thor/issues/398
|
5
|
+
class Thor
|
6
|
+
module Shell
|
7
|
+
class Basic
|
8
|
+
def print_wrapped(message, options = {})
|
9
|
+
message = "\n#{message}" unless message[0] == "\n"
|
10
|
+
stdout.puts message
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module S3Secure
|
17
|
+
class Command < Thor
|
18
|
+
class << self
|
19
|
+
def dispatch(m, args, options, config)
|
20
|
+
# Allow calling for help via:
|
21
|
+
# s3-secure command help
|
22
|
+
# s3-secure command -h
|
23
|
+
# s3-secure command --help
|
24
|
+
# s3-secure command -D
|
25
|
+
#
|
26
|
+
# as well thor's normal way:
|
27
|
+
#
|
28
|
+
# s3-secure help command
|
29
|
+
help_flags = Thor::HELP_MAPPINGS + ["help"]
|
30
|
+
if args.length > 1 && !(args & help_flags).empty?
|
31
|
+
args -= help_flags
|
32
|
+
args.insert(-2, "help")
|
33
|
+
end
|
34
|
+
|
35
|
+
# s3-secure version
|
36
|
+
# s3-secure --version
|
37
|
+
# s3-secure -v
|
38
|
+
version_flags = ["--version", "-v"]
|
39
|
+
if args.length == 1 && !(args & version_flags).empty?
|
40
|
+
args = ["version"]
|
41
|
+
end
|
42
|
+
|
43
|
+
super
|
44
|
+
end
|
45
|
+
|
46
|
+
# Override command_help to include the description at the top of the
|
47
|
+
# long_description.
|
48
|
+
def command_help(shell, command_name)
|
49
|
+
meth = normalize_command_name(command_name)
|
50
|
+
command = all_commands[meth]
|
51
|
+
alter_command_description(command)
|
52
|
+
super
|
53
|
+
end
|
54
|
+
|
55
|
+
def alter_command_description(command)
|
56
|
+
return unless command
|
57
|
+
|
58
|
+
# Add description to beginning of long_description
|
59
|
+
long_desc = if command.long_description
|
60
|
+
"#{command.description}\n\n#{command.long_description}"
|
61
|
+
else
|
62
|
+
command.description
|
63
|
+
end
|
64
|
+
|
65
|
+
# add reference url to end of the long_description
|
66
|
+
unless website.empty?
|
67
|
+
full_command = [command.ancestor_name, command.name].compact.join('-')
|
68
|
+
url = "#{website}/reference/s3-secure-#{full_command}"
|
69
|
+
long_desc += "\n\nHelp also available at: #{url}"
|
70
|
+
end
|
71
|
+
|
72
|
+
command.long_description = long_desc
|
73
|
+
end
|
74
|
+
private :alter_command_description
|
75
|
+
|
76
|
+
# meant to be overriden
|
77
|
+
def website
|
78
|
+
""
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|