capsium 0.1.2 → 0.3.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 +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +46 -7
- data/CHANGELOG.md +183 -0
- data/Gemfile +6 -0
- data/LICENSE +21 -0
- data/README.adoc +496 -127
- data/Rakefile +8 -1
- data/bin/console +1 -1
- data/capsium.gemspec +12 -19
- data/exe/capsium +1 -1
- data/lib/capsium/cli/convert.rb +18 -0
- data/lib/capsium/cli/formatting.rb +27 -0
- data/lib/capsium/cli/package.rb +148 -0
- data/lib/capsium/cli/reactor.rb +33 -0
- data/lib/capsium/cli.rb +4 -96
- data/lib/capsium/converters/jekyll.rb +5 -7
- data/lib/capsium/converters.rb +7 -0
- data/lib/capsium/package/authentication.rb +38 -0
- data/lib/capsium/package/authentication_config.rb +78 -0
- data/lib/capsium/package/cipher.rb +197 -0
- data/lib/capsium/package/composition.rb +77 -0
- data/lib/capsium/package/dataset.rb +67 -34
- data/lib/capsium/package/dependency_resolver.rb +61 -0
- data/lib/capsium/package/encryption_config.rb +38 -0
- data/lib/capsium/package/manifest.rb +33 -22
- data/lib/capsium/package/manifest_config.rb +52 -7
- data/lib/capsium/package/merged_view.rb +168 -0
- data/lib/capsium/package/metadata.rb +4 -8
- data/lib/capsium/package/metadata_config.rb +97 -9
- data/lib/capsium/package/preparation.rb +81 -0
- data/lib/capsium/package/routes.rb +39 -41
- data/lib/capsium/package/routes_config.rb +174 -44
- data/lib/capsium/package/security.rb +140 -0
- data/lib/capsium/package/security_config.rb +60 -0
- data/lib/capsium/package/signer.rb +201 -0
- data/lib/capsium/package/storage.rb +20 -45
- data/lib/capsium/package/storage_config.rb +128 -3
- data/lib/capsium/package/store.rb +109 -0
- data/lib/capsium/package/testing/config_test.rb +70 -0
- data/lib/capsium/package/testing/context.rb +11 -0
- data/lib/capsium/package/testing/data_validation_test.rb +63 -0
- data/lib/capsium/package/testing/file_test.rb +45 -0
- data/lib/capsium/package/testing/report.rb +33 -0
- data/lib/capsium/package/testing/route_test.rb +64 -0
- data/lib/capsium/package/testing/test_case.rb +60 -0
- data/lib/capsium/package/testing/test_suite.rb +86 -0
- data/lib/capsium/package/testing.rb +20 -0
- data/lib/capsium/package/validator.rb +160 -0
- data/lib/capsium/package/verification.rb +32 -0
- data/lib/capsium/package/version.rb +204 -0
- data/lib/capsium/package.rb +92 -106
- data/lib/capsium/packager.rb +75 -6
- data/lib/capsium/reactor/authenticator.rb +180 -0
- data/lib/capsium/reactor/deploy.rb +71 -0
- data/lib/capsium/reactor/htpasswd.rb +154 -0
- data/lib/capsium/reactor/introspection.rb +91 -0
- data/lib/capsium/reactor/oauth2.rb +106 -0
- data/lib/capsium/reactor/serving.rb +75 -0
- data/lib/capsium/reactor/session.rb +82 -0
- data/lib/capsium/reactor.rb +93 -43
- data/lib/capsium/thor_ext.rb +1 -1
- data/lib/capsium/version.rb +1 -1
- data/lib/capsium.rb +7 -9
- data/sig/capsium/package/authentication.rbs +23 -0
- data/sig/capsium/package/authentication_config.rbs +71 -0
- data/sig/capsium/package/cipher.rbs +51 -0
- data/sig/capsium/package/composition.rbs +20 -0
- data/sig/capsium/package/dataset.rbs +28 -0
- data/sig/capsium/package/dependency_resolver.rbs +49 -0
- data/sig/capsium/package/encryption_config.rbs +35 -0
- data/sig/capsium/package/manifest.rbs +34 -0
- data/sig/capsium/package/manifest_config.rbs +38 -0
- data/sig/capsium/package/merged_view.rbs +46 -0
- data/sig/capsium/package/metadata.rbs +26 -0
- data/sig/capsium/package/metadata_config.rbs +49 -0
- data/sig/capsium/package/preparation.rbs +23 -0
- data/sig/capsium/package/routes.rbs +37 -0
- data/sig/capsium/package/routes_config.rbs +107 -0
- data/sig/capsium/package/security.rbs +72 -0
- data/sig/capsium/package/security_config.rbs +57 -0
- data/sig/capsium/package/signer.rbs +59 -0
- data/sig/capsium/package/storage.rbs +28 -0
- data/sig/capsium/package/storage_config.rbs +85 -0
- data/sig/capsium/package/store.rbs +37 -0
- data/sig/capsium/package/testing/config_test.rbs +21 -0
- data/sig/capsium/package/testing/context.rbs +14 -0
- data/sig/capsium/package/testing/data_validation_test.rbs +22 -0
- data/sig/capsium/package/testing/file_test.rbs +17 -0
- data/sig/capsium/package/testing/report.rbs +20 -0
- data/sig/capsium/package/testing/route_test.rbs +22 -0
- data/sig/capsium/package/testing/test_case.rbs +41 -0
- data/sig/capsium/package/testing/test_suite.rbs +20 -0
- data/sig/capsium/package/testing.rbs +7 -0
- data/sig/capsium/package/validator.rbs +29 -0
- data/sig/capsium/package/verification.rbs +24 -0
- data/sig/capsium/package/version.rbs +39 -0
- data/sig/capsium/package.rbs +61 -0
- data/sig/capsium/packager.rbs +32 -0
- data/sig/capsium/reactor/authenticator.rbs +40 -0
- data/sig/capsium/reactor/deploy.rbs +34 -0
- data/sig/capsium/reactor/htpasswd.rbs +36 -0
- data/sig/capsium/reactor/introspection.rbs +31 -0
- data/sig/capsium/reactor/oauth2.rbs +31 -0
- data/sig/capsium/reactor/serving.rbs +22 -0
- data/sig/capsium/reactor/session.rbs +36 -0
- data/sig/capsium/reactor.rbs +35 -0
- data/sig/capsium.rbs +3 -1
- metadata +108 -170
- data/lib/capsium/package/dataset_config.rb +0 -28
- data/lib/capsium/protector.rb +0 -103
data/Rakefile
CHANGED
|
@@ -9,4 +9,11 @@ require "rubocop/rake_task"
|
|
|
9
9
|
|
|
10
10
|
RuboCop::RakeTask.new
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
namespace :rbs do
|
|
13
|
+
desc "Validate the RBS signatures (sig/)"
|
|
14
|
+
task :validate do
|
|
15
|
+
sh "bundle exec rbs -I sig validate"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
task default: %i[spec rubocop rbs:validate]
|
data/bin/console
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require "bundler/setup"
|
|
5
|
-
require "capsium"
|
|
5
|
+
require File.expand_path("../lib/capsium", __dir__)
|
|
6
6
|
|
|
7
7
|
# You can add fixtures and/or initialization code here to make experimenting
|
|
8
8
|
# with your gem easier. You can also use a different console, if you like.
|
data/capsium.gemspec
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require File.expand_path("lib/capsium/version", __dir__)
|
|
4
4
|
|
|
5
5
|
Gem::Specification.new do |spec|
|
|
6
6
|
spec.name = "capsium"
|
|
@@ -8,14 +8,19 @@ Gem::Specification.new do |spec|
|
|
|
8
8
|
spec.authors = ["Ribose Inc."]
|
|
9
9
|
spec.email = ["open.source@ribose.com"]
|
|
10
10
|
|
|
11
|
-
spec.summary = "Capsium"
|
|
12
|
-
spec.description = "Capsium"
|
|
11
|
+
spec.summary = "Packager and reactor for Capsium packages (.cap)"
|
|
12
|
+
spec.description = "Capsium facilitates the creation, management and " \
|
|
13
|
+
"deployment of content packages with ease. This gem " \
|
|
14
|
+
"provides a structured way to handle content, data, " \
|
|
15
|
+
"and metadata for various applications: it packages " \
|
|
16
|
+
"them into .cap files and serves them over HTTP."
|
|
13
17
|
spec.homepage = "https://github.com/capsiums/capsium"
|
|
14
18
|
spec.license = "MIT"
|
|
15
19
|
|
|
16
20
|
spec.metadata["homepage_uri"] = spec.homepage
|
|
17
21
|
spec.metadata["source_code_uri"] = spec.homepage
|
|
18
22
|
spec.metadata["changelog_uri"] = "https://github.com/capsiums/capsium/releases"
|
|
23
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
19
24
|
|
|
20
25
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
21
26
|
`git ls-files -z`
|
|
@@ -26,30 +31,18 @@ Gem::Specification.new do |spec|
|
|
|
26
31
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
27
32
|
spec.require_paths = ["lib"]
|
|
28
33
|
|
|
29
|
-
spec.required_ruby_version = ">= 2.
|
|
34
|
+
spec.required_ruby_version = ">= 3.2.0"
|
|
30
35
|
|
|
31
|
-
spec.add_dependency "
|
|
36
|
+
spec.add_dependency "bcrypt"
|
|
32
37
|
spec.add_dependency "csv"
|
|
33
|
-
spec.add_dependency "htmlcompressor"
|
|
34
|
-
spec.add_dependency "html-proofer"
|
|
35
|
-
spec.add_dependency "jekyll"
|
|
36
38
|
spec.add_dependency "json"
|
|
37
39
|
spec.add_dependency "json-schema"
|
|
38
40
|
spec.add_dependency "listen"
|
|
41
|
+
spec.add_dependency "lutaml-model", ">= 0.8", "< 1.0"
|
|
39
42
|
spec.add_dependency "marcel"
|
|
40
|
-
spec.add_dependency "rubyzip", "
|
|
41
|
-
spec.add_dependency "scss_lint"
|
|
42
|
-
spec.add_dependency "shale"
|
|
43
|
+
spec.add_dependency "rubyzip", ">= 2.3.2", "< 4.0"
|
|
43
44
|
spec.add_dependency "sqlite3"
|
|
44
45
|
spec.add_dependency "thor"
|
|
45
|
-
spec.add_dependency "uglifier"
|
|
46
46
|
spec.add_dependency "webrick"
|
|
47
47
|
spec.add_dependency "yaml"
|
|
48
|
-
|
|
49
|
-
spec.add_development_dependency "pry"
|
|
50
|
-
spec.add_development_dependency "rack-test"
|
|
51
|
-
spec.add_development_dependency "rake", "~> 13.0"
|
|
52
|
-
spec.add_development_dependency "rspec", "~> 3.11"
|
|
53
|
-
spec.add_development_dependency "rubocop", "~> 1.58"
|
|
54
|
-
spec.add_development_dependency "rubocop-performance"
|
|
55
48
|
end
|
data/exe/capsium
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Capsium
|
|
4
|
+
class Cli
|
|
5
|
+
class Convert < Thor
|
|
6
|
+
extend ThorExt::Start
|
|
7
|
+
|
|
8
|
+
desc "jekyll PACKAGE_FILE OUTPUT_DIRECTORY",
|
|
9
|
+
"Convert a Capsium package to a Jekyll site"
|
|
10
|
+
|
|
11
|
+
def jekyll(package_file, output_directory)
|
|
12
|
+
converter = Capsium::Converters::Jekyll.new(package_file,
|
|
13
|
+
output_directory)
|
|
14
|
+
converter.convert
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Capsium
|
|
4
|
+
class Cli < Thor
|
|
5
|
+
# Output formatting helpers for CLI subcommands.
|
|
6
|
+
module Formatting
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def format_result(result)
|
|
10
|
+
line = "#{result.ok? ? 'PASS' : 'FAIL'} #{result.name}"
|
|
11
|
+
return line if result.messages.empty?
|
|
12
|
+
|
|
13
|
+
"#{line}: #{result.messages.join('; ')}"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# The resolved dependency tree (ARCHITECTURE.md section 4a): one
|
|
17
|
+
# line per dependency showing the declared GUID and range, the
|
|
18
|
+
# resolved version and the store .cap it resolved to, nested.
|
|
19
|
+
def print_dependency_tree(package, indent = "")
|
|
20
|
+
package.resolved_dependencies.each do |dep|
|
|
21
|
+
puts "#{indent}- #{dep.guid} (#{dep.range}) => #{dep.version} [#{dep.path}]"
|
|
22
|
+
print_dependency_tree(dep.package, "#{indent} ")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Capsium
|
|
6
|
+
class Cli
|
|
7
|
+
class Package < Thor
|
|
8
|
+
extend ThorExt::Start
|
|
9
|
+
include Formatting
|
|
10
|
+
|
|
11
|
+
desc "info PACKAGE_PATH", "Display information about the package"
|
|
12
|
+
option :store, type: :string, desc: "Package store directory (default: CAPSIUM_STORE)"
|
|
13
|
+
|
|
14
|
+
def info(path_to_package)
|
|
15
|
+
package = Capsium::Package.new(path_to_package, store: options[:store])
|
|
16
|
+
puts "Package Path: #{package.path}"
|
|
17
|
+
puts "Routes: #{package.routes.to_hash}"
|
|
18
|
+
puts "Manifest: #{package.manifest.to_hash}"
|
|
19
|
+
print_dependency_tree(package)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
desc "manifest PATH_TO_PACKAGE",
|
|
23
|
+
"Show the manifest content of the package"
|
|
24
|
+
|
|
25
|
+
def manifest(path_to_package)
|
|
26
|
+
package = Capsium::Package.new(path_to_package)
|
|
27
|
+
puts JSON.pretty_generate(package.manifest.to_hash)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
desc "storage PATH_TO_PACKAGE", "Show the storage datasets of the package"
|
|
31
|
+
|
|
32
|
+
def storage(path_to_package)
|
|
33
|
+
package = Capsium::Package.new(path_to_package)
|
|
34
|
+
puts JSON.pretty_generate(package.storage.to_hash)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
desc "routes PATH_TO_PACKAGE", "Show the routes of the package"
|
|
38
|
+
|
|
39
|
+
def routes(path_to_package)
|
|
40
|
+
package = Capsium::Package.new(path_to_package)
|
|
41
|
+
puts JSON.pretty_generate(package.routes.to_hash)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
desc "metadata PATH_TO_PACKAGE", "Show the metadata of the package"
|
|
45
|
+
|
|
46
|
+
def metadata(path_to_package)
|
|
47
|
+
package = Capsium::Package.new(path_to_package)
|
|
48
|
+
puts JSON.pretty_generate(package.metadata.to_hash)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
desc "pack PATH_TO_PACKAGE_FOLDER", "Package the files into the package"
|
|
52
|
+
option :force, type: :boolean, default: false, aliases: "-f"
|
|
53
|
+
|
|
54
|
+
def pack(path_to_package)
|
|
55
|
+
package = Capsium::Package.new(path_to_package)
|
|
56
|
+
packager = Capsium::Packager.new
|
|
57
|
+
packager.pack(package, options)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
desc "unpack PACKAGE_FILE", "Unpack a .cap package into a directory"
|
|
61
|
+
option :output, type: :string, aliases: "-o", desc: "Output directory"
|
|
62
|
+
|
|
63
|
+
def unpack(path_to_package)
|
|
64
|
+
destination = options[:output] || File.basename(path_to_package, ".cap")
|
|
65
|
+
Capsium::Packager.new.unpack(path_to_package, destination)
|
|
66
|
+
puts "Package unpacked to: #{destination}"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
desc "validate PACKAGE_PATH", "Validate a package directory or .cap file"
|
|
70
|
+
|
|
71
|
+
def validate(path_to_package)
|
|
72
|
+
results = Capsium::Package::Validator.new(path_to_package).run
|
|
73
|
+
results.each { |result| puts format_result(result) }
|
|
74
|
+
return if results.all?(&:ok?)
|
|
75
|
+
|
|
76
|
+
raise Thor::Error, "Package validation failed"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
desc "sign PACKAGE_PATH --key KEY.pem [--cert CERT.pem]",
|
|
80
|
+
"Sign the package (RSA-SHA256) and record the signature in security.json"
|
|
81
|
+
option :key, type: :string, required: true,
|
|
82
|
+
desc: "Path to the RSA private key PEM (min 2048 bits)"
|
|
83
|
+
option :cert, type: :string,
|
|
84
|
+
desc: "Path to the X.509 certificate PEM (must match the key)"
|
|
85
|
+
|
|
86
|
+
def sign(path_to_package)
|
|
87
|
+
Capsium::Package::Signer.sign_package(path_to_package, options[:key], options[:cert])
|
|
88
|
+
puts "Package signed: #{path_to_package}"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
desc "verify-signature PACKAGE_PATH [--cert CERT.pem]",
|
|
92
|
+
"Verify the package digital signature declared in security.json"
|
|
93
|
+
option :cert, type: :string,
|
|
94
|
+
desc: "Path to the X.509 certificate or public key PEM " \
|
|
95
|
+
"(defaults to the key embedded in the package)"
|
|
96
|
+
|
|
97
|
+
def verify_signature(path_to_package)
|
|
98
|
+
unless Capsium::Package::Signer.verify_package(path_to_package, options[:cert])
|
|
99
|
+
raise Thor::Error, "Signature verification failed: #{path_to_package}"
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
puts "Signature valid: #{path_to_package}"
|
|
103
|
+
rescue Capsium::Package::Signer::SignatureError => e
|
|
104
|
+
raise Thor::Error, e.message
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
desc "encrypt PACKAGE_PATH --public-key PUB.pem -o OUT.cap",
|
|
108
|
+
"Encrypt a package (AES-256-GCM, RSA-OAEP-SHA256 wrapped DEK)"
|
|
109
|
+
option :public_key, type: :string, required: true,
|
|
110
|
+
desc: "Path to the recipient RSA public key or X.509 certificate PEM"
|
|
111
|
+
option :output, type: :string, required: true, aliases: "-o",
|
|
112
|
+
desc: "Output path for the encrypted .cap"
|
|
113
|
+
|
|
114
|
+
def encrypt(path_to_package)
|
|
115
|
+
Capsium::Package::Cipher.new.encrypt(
|
|
116
|
+
path_to_package, options[:public_key], options[:output]
|
|
117
|
+
)
|
|
118
|
+
puts "Package encrypted: #{options[:output]}"
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
desc "decrypt PACKAGE_PATH --private-key PRIV.pem [-o OUT.cap]",
|
|
122
|
+
"Decrypt an encrypted package"
|
|
123
|
+
option :private_key, type: :string, required: true,
|
|
124
|
+
desc: "Path to the recipient RSA private key PEM"
|
|
125
|
+
option :output, type: :string, aliases: "-o",
|
|
126
|
+
desc: "Output path (default: <name>-decrypted.cap)"
|
|
127
|
+
|
|
128
|
+
def decrypt(path_to_package)
|
|
129
|
+
output = options[:output] || "#{File.basename(path_to_package, '.cap')}-decrypted.cap"
|
|
130
|
+
Capsium::Package::Cipher.new.decrypt(path_to_package, options[:private_key], output)
|
|
131
|
+
puts "Package decrypted: #{output}"
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
desc "test PACKAGE_PATH", "Run the package's tests/*.yaml suite (05x-testing DSL)"
|
|
135
|
+
|
|
136
|
+
def test(path_to_package)
|
|
137
|
+
package = Capsium::Package.new(path_to_package)
|
|
138
|
+
report = Capsium::Package::Testing::TestSuite.new(package).run
|
|
139
|
+
report.results.each { |result| puts format_result(result) }
|
|
140
|
+
if report.results.empty?
|
|
141
|
+
puts "No tests found (#{Capsium::Package::Testing::TestSuite::TESTS_DIR}/*.yaml)"
|
|
142
|
+
end
|
|
143
|
+
puts report.summary
|
|
144
|
+
raise Thor::Error, "Package tests failed" unless report.ok?
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Capsium
|
|
4
|
+
class Cli
|
|
5
|
+
class Reactor < Thor
|
|
6
|
+
extend ThorExt::Start
|
|
7
|
+
|
|
8
|
+
desc "serve PACKAGE_PATH",
|
|
9
|
+
"Start the Capsium reactor to serve the package"
|
|
10
|
+
option :port, type: :numeric, default: Capsium::Reactor::DEFAULT_PORT
|
|
11
|
+
option :do_not_listen, type: :boolean, default: false
|
|
12
|
+
option :store, type: :string,
|
|
13
|
+
desc: "Package store directory for dependency " \
|
|
14
|
+
"resolution (default: CAPSIUM_STORE)"
|
|
15
|
+
option :deploy, type: :string,
|
|
16
|
+
desc: "deploy.json with reactor-side secrets " \
|
|
17
|
+
"(default: CAPSIUM_DEPLOY)"
|
|
18
|
+
|
|
19
|
+
def serve(path_to_package)
|
|
20
|
+
reactor = Capsium::Reactor.new(
|
|
21
|
+
package: path_to_package,
|
|
22
|
+
port: options[:port],
|
|
23
|
+
do_not_listen: options[:do_not_listen],
|
|
24
|
+
store: options[:store],
|
|
25
|
+
deploy: options[:deploy]
|
|
26
|
+
)
|
|
27
|
+
reactor.serve
|
|
28
|
+
ensure
|
|
29
|
+
reactor&.package&.cleanup
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
data/lib/capsium/cli.rb
CHANGED
|
@@ -1,107 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# lib/capsium/cli.rb
|
|
4
3
|
require "thor"
|
|
5
|
-
require_relative "package"
|
|
6
|
-
require_relative "reactor"
|
|
7
|
-
require_relative "thor_ext"
|
|
8
|
-
require_relative "converters/jekyll"
|
|
9
4
|
|
|
10
5
|
module Capsium
|
|
11
6
|
class Cli < Thor
|
|
12
7
|
extend ThorExt::Start
|
|
13
8
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def info(path_to_package)
|
|
20
|
-
package = Capsium::Package.new(path_to_package)
|
|
21
|
-
puts "Package Path: #{package.path}"
|
|
22
|
-
puts "Routes: #{package.routes.as_json}"
|
|
23
|
-
puts "Manifest: #{package.manifest.as_json}"
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
desc "manifest PATH_TO_PACKAGE",
|
|
27
|
-
"Show the manifest content of the package"
|
|
28
|
-
|
|
29
|
-
def manifest(path_to_package)
|
|
30
|
-
package = Capsium::Package.new(path_to_package)
|
|
31
|
-
puts JSON.pretty_generate(package.manifest.as_json)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
desc "storage PATH_TO_PACKAGE", "Show the storage datasets of the package"
|
|
35
|
-
|
|
36
|
-
def storage(path_to_package)
|
|
37
|
-
package = Capsium::Package.new(path_to_package)
|
|
38
|
-
puts JSON.pretty_generate(package.storage.as_json)
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
desc "routes PATH_TO_PACKAGE", "Show the routes of the package"
|
|
42
|
-
|
|
43
|
-
def routes(path_to_package)
|
|
44
|
-
package = Capsium::Package.new(path_to_package)
|
|
45
|
-
puts JSON.pretty_generate(package.routes.as_json)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
desc "metadata PATH_TO_PACKAGE", "Show the metadata of the package"
|
|
49
|
-
|
|
50
|
-
def metadata(path_to_package)
|
|
51
|
-
package = Capsium::Package.new(path_to_package)
|
|
52
|
-
puts JSON.pretty_generate(package.metadata.as_json)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
desc "pack PATH_TO_PACKAGE_FOLDER", "Package the files into the package"
|
|
56
|
-
option :force, type: :boolean, default: false, aliases: "-f"
|
|
57
|
-
|
|
58
|
-
def pack(path_to_package)
|
|
59
|
-
package = Capsium::Package.new(path_to_package)
|
|
60
|
-
packager = Capsium::Packager.new
|
|
61
|
-
packager.pack(package, options)
|
|
62
|
-
rescue StandardError => e
|
|
63
|
-
puts e
|
|
64
|
-
puts e.inspect
|
|
65
|
-
puts e.backtrace
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
class Reactor < Thor
|
|
70
|
-
extend ThorExt::Start
|
|
71
|
-
|
|
72
|
-
desc "serve PACKAGE_PATH",
|
|
73
|
-
"Start the Capsium reactor to serve the package"
|
|
74
|
-
option :port, type: :numeric, default: Capsium::Reactor::DEFAULT_PORT
|
|
75
|
-
option :do_not_listen, type: :boolean, default: false
|
|
76
|
-
|
|
77
|
-
def serve(path_to_package)
|
|
78
|
-
reactor = Capsium::Reactor.new(
|
|
79
|
-
package: path_to_package,
|
|
80
|
-
port: options[:port],
|
|
81
|
-
do_not_listen: options[:do_not_listen],
|
|
82
|
-
)
|
|
83
|
-
reactor.serve
|
|
84
|
-
rescue StandardError => e
|
|
85
|
-
puts e
|
|
86
|
-
puts e.inspect
|
|
87
|
-
puts e.backtrace
|
|
88
|
-
ensure
|
|
89
|
-
reactor.package.cleanup
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
class Convert < Thor
|
|
94
|
-
extend ThorExt::Start
|
|
95
|
-
|
|
96
|
-
desc "jekyll PACKAGE_FILE OUTPUT_DIRECTORY",
|
|
97
|
-
"Convert a Capsium package to a Jekyll site"
|
|
98
|
-
|
|
99
|
-
def jekyll(package_file, output_directory)
|
|
100
|
-
converter = Capsium::Converters::Jekyll.new(package_file,
|
|
101
|
-
output_directory)
|
|
102
|
-
converter.convert
|
|
103
|
-
end
|
|
104
|
-
end
|
|
9
|
+
autoload :Convert, "capsium/cli/convert"
|
|
10
|
+
autoload :Formatting, "capsium/cli/formatting"
|
|
11
|
+
autoload :Package, "capsium/cli/package"
|
|
12
|
+
autoload :Reactor, "capsium/cli/reactor"
|
|
105
13
|
|
|
106
14
|
desc "package SUBCOMMAND ...ARGS", "Manage packages"
|
|
107
15
|
subcommand "package", Capsium::Cli::Package
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# lib/capsium/converters/jekyll.rb
|
|
4
3
|
require "fileutils"
|
|
5
|
-
require "
|
|
6
|
-
require "
|
|
4
|
+
require "pathname"
|
|
5
|
+
require "yaml"
|
|
7
6
|
|
|
8
7
|
module Capsium
|
|
9
8
|
module Converters
|
|
@@ -38,7 +37,7 @@ module Capsium
|
|
|
38
37
|
"baseurl" => "",
|
|
39
38
|
"url" => "",
|
|
40
39
|
"markdown" => "kramdown",
|
|
41
|
-
"theme" => "minima"
|
|
40
|
+
"theme" => "minima"
|
|
42
41
|
}
|
|
43
42
|
write_file("_config.yml", config.to_yaml)
|
|
44
43
|
end
|
|
@@ -51,7 +50,7 @@ module Capsium
|
|
|
51
50
|
|
|
52
51
|
def copy_content_files(package)
|
|
53
52
|
package.content_files.each do |file_path|
|
|
54
|
-
relative_path = Pathname.new(file_path).relative_path_from(Pathname.new(package.
|
|
53
|
+
relative_path = Pathname.new(file_path).relative_path_from(Pathname.new(package.path))
|
|
55
54
|
output_path = File.join(@output_directory, relative_path)
|
|
56
55
|
FileUtils.mkdir_p(File.dirname(output_path))
|
|
57
56
|
FileUtils.cp(file_path, output_path)
|
|
@@ -63,8 +62,7 @@ module Capsium
|
|
|
63
62
|
route.path == "/"
|
|
64
63
|
end
|
|
65
64
|
if root_route
|
|
66
|
-
index_path = File.join(package.
|
|
67
|
-
root_route.target.file)
|
|
65
|
+
index_path = File.join(package.path, root_route.resource)
|
|
68
66
|
index_content = File.read(index_path)
|
|
69
67
|
write_file("index.html", index_content)
|
|
70
68
|
else
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "forwardable"
|
|
4
|
+
|
|
5
|
+
module Capsium
|
|
6
|
+
class Package
|
|
7
|
+
# Loads a package's authentication.json (ARCHITECTURE.md section 4b).
|
|
8
|
+
class Authentication
|
|
9
|
+
extend Forwardable
|
|
10
|
+
|
|
11
|
+
attr_reader :path, :config
|
|
12
|
+
|
|
13
|
+
def_delegators :@config, :to_json, :to_hash
|
|
14
|
+
|
|
15
|
+
def initialize(path)
|
|
16
|
+
@path = path
|
|
17
|
+
@config = File.exist?(path) ? AuthenticationConfig.from_json(File.read(path)) : nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def present?
|
|
21
|
+
!@config.nil?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def basic_auth
|
|
25
|
+
@config&.authentication&.basic_auth
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def oauth2
|
|
29
|
+
@config&.authentication&.oauth2
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Whether any authentication method is enabled.
|
|
33
|
+
def enabled?
|
|
34
|
+
!!(basic_auth&.enabled? || oauth2&.enabled?)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "lutaml/model"
|
|
5
|
+
|
|
6
|
+
module Capsium
|
|
7
|
+
class Package
|
|
8
|
+
# The "basicAuth" object of authentication.json (ARCHITECTURE.md
|
|
9
|
+
# section 4b): Apache-style Basic authentication verified against an
|
|
10
|
+
# htpasswd file. "passwdFile" is a package-relative path (password
|
|
11
|
+
# hashes, not plaintext secrets, so it may ship in the package);
|
|
12
|
+
# deploy.json may override it with a reactor-side file.
|
|
13
|
+
class BasicAuthConfig < Lutaml::Model::Serializable
|
|
14
|
+
attribute :enabled, :boolean, default: false
|
|
15
|
+
attribute :passwd_file, :string
|
|
16
|
+
attribute :realm, :string, default: "capsium"
|
|
17
|
+
|
|
18
|
+
json do
|
|
19
|
+
map :enabled, to: :enabled
|
|
20
|
+
map "passwdFile", to: :passwd_file
|
|
21
|
+
map :realm, to: :realm
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def enabled?
|
|
25
|
+
!!enabled
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# The "oauth2" object of authentication.json: an OAuth2
|
|
30
|
+
# authorization-code flow. The client secret is NEVER read from the
|
|
31
|
+
# package — it comes from deploy.json or reactor configuration.
|
|
32
|
+
class OAuth2Config < Lutaml::Model::Serializable
|
|
33
|
+
attribute :enabled, :boolean, default: false
|
|
34
|
+
attribute :provider, :string
|
|
35
|
+
attribute :client_id, :string
|
|
36
|
+
attribute :authorization_url, :string
|
|
37
|
+
attribute :token_url, :string
|
|
38
|
+
attribute :userinfo_url, :string
|
|
39
|
+
attribute :redirect_path, :string, default: "/auth/callback"
|
|
40
|
+
attribute :scopes, :string, collection: true, default: []
|
|
41
|
+
|
|
42
|
+
json do
|
|
43
|
+
map :enabled, to: :enabled
|
|
44
|
+
map :provider, to: :provider
|
|
45
|
+
map "clientId", to: :client_id
|
|
46
|
+
map "authorizationUrl", to: :authorization_url
|
|
47
|
+
map "tokenUrl", to: :token_url
|
|
48
|
+
map "userinfoUrl", to: :userinfo_url
|
|
49
|
+
map "redirectPath", to: :redirect_path
|
|
50
|
+
map :scopes, to: :scopes
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def enabled?
|
|
54
|
+
!!enabled
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# The "authentication" object of authentication.json.
|
|
59
|
+
class AuthenticationData < Lutaml::Model::Serializable
|
|
60
|
+
attribute :basic_auth, BasicAuthConfig
|
|
61
|
+
attribute :oauth2, OAuth2Config
|
|
62
|
+
|
|
63
|
+
json do
|
|
64
|
+
map "basicAuth", to: :basic_auth
|
|
65
|
+
map :oauth2, to: :oauth2
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Canonical authentication.json model (ARCHITECTURE.md section 4b).
|
|
70
|
+
class AuthenticationConfig < Lutaml::Model::Serializable
|
|
71
|
+
attribute :authentication, AuthenticationData
|
|
72
|
+
|
|
73
|
+
json do
|
|
74
|
+
map :authentication, to: :authentication
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|