ccli 0.1.1 → 1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a20c067bb37955bc5db9f0977498c65038f57707e9f9a84cd39a229748daa283
4
- data.tar.gz: 888934204b52a9017f4aa8c9d1deea23513c07ca5b08c48831610c2320ed6128
3
+ metadata.gz: d2f2f0b3d9151f52f911b794e164119054e2d281d3291f06a8e04b3ada1382d0
4
+ data.tar.gz: 5fef82bab17bf44d9b922c8ef543c89405e8aee145cf98c138c932d42c37bb4a
5
5
  SHA512:
6
- metadata.gz: 6152b10e88db00f0e7c0c7e9d2baedf9a7446b2695b5aa806b8f195a275c1410d4628df5bac7b8bebcd86390f01e4c9581cf971f7b90e7335326d4433bf21843
7
- data.tar.gz: 0f2761c2c6a7059f2ded3ebb3a853d0a2d99bc31e793655519a75696ac15c0c9c3b3156654de094794bf0840387a2f4f9912bf6200d214c8f69f2fb3df15068b
6
+ metadata.gz: 10b197547309059384bb1fdd65135f5950cbf33cbbb9aed7fb5e871cb436cb493ef8524902e31318dbabd4fd1a98d26a4f769937845e97eb72e5a6c65ad43093
7
+ data.tar.gz: 6ef9539d2512c4ec66ae63d5eb400bc06fd1d8c33147304dffb2f2527ed3f74ba9e7e64b7b85f2da88806bef4f9be18c41ffd73ebe6cfb3ab04a92be4902fbfa
data/.rubocop.yml CHANGED
@@ -1,7 +1,9 @@
1
1
  AllCops:
2
2
  DisplayCopNames: true
3
+ TargetRubyVersion: 2.5
3
4
  Exclude:
4
5
  - spec/**/*
6
+ - ccli.gemspec
5
7
 
6
8
  Metrics/AbcSize:
7
9
  Max: 20
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 2.6.0
data/CHANGELOG.md ADDED
@@ -0,0 +1,27 @@
1
+ # Changelog
2
+
3
+ ## 1.1.0
4
+
5
+ - Rename Account to Encryptable to support Cryptopus > 4.2
6
+
7
+ ## 1.0.1
8
+
9
+ - Reset api user token after login
10
+
11
+ ## 1.0.0
12
+
13
+ - De- and encode data from secrets
14
+
15
+ ## 0.1.2
16
+
17
+ - Updating docs
18
+ - Bugfixing
19
+
20
+ ## 0.1.1
21
+
22
+ - Adding MIT license
23
+
24
+ ## 0.1.0
25
+
26
+ - Publish first version
27
+ - Commands: `login`, `logout`, `account`, `folder`, `{ose,k8s}-secret-pull`, `{ose,k8s}-secret-push`, `teams`, `use`
data/Gemfile CHANGED
@@ -5,9 +5,9 @@ source 'https://rubygems.org'
5
5
  gem 'commander', '~> 4.5', '>= 4.5.2'
6
6
  gem 'rspec', '~> 3.9'
7
7
  gem 'rubocop', '~> 0.89.0'
8
- gem 'tty-command'
9
- gem 'tty-exit'
10
- gem 'tty-logger'
8
+ gem 'tty-command', '~> 0.10'
9
+ gem 'tty-exit', '~> 0.1'
10
+ gem 'tty-logger', '~> 0.6'
11
11
 
12
12
  gem 'pry'
13
13
  gem 'pry-byebug'
data/Gemfile.lock CHANGED
@@ -7,14 +7,12 @@ GEM
7
7
  commander (4.5.2)
8
8
  highline (~> 2.0.0)
9
9
  diff-lcs (1.4.4)
10
- equatable (0.6.1)
11
10
  highline (2.0.3)
12
11
  method_source (1.0.0)
13
12
  parallel (1.19.2)
14
13
  parser (2.7.1.4)
15
14
  ast (~> 2.4.1)
16
- pastel (0.7.4)
17
- equatable (~> 0.6)
15
+ pastel (0.8.0)
18
16
  tty-color (~> 0.5)
19
17
  pry (0.13.1)
20
18
  coderay (~> 1.1)
@@ -24,7 +22,7 @@ GEM
24
22
  pry (~> 0.13.0)
25
23
  rainbow (3.0.0)
26
24
  regexp_parser (1.7.1)
27
- rexml (3.2.4)
25
+ rexml (3.2.5)
28
26
  rspec (3.9.0)
29
27
  rspec-core (~> 3.9.0)
30
28
  rspec-expectations (~> 3.9.0)
@@ -50,12 +48,12 @@ GEM
50
48
  rubocop-ast (0.3.0)
51
49
  parser (>= 2.7.1.4)
52
50
  ruby-progressbar (1.10.1)
53
- tty-color (0.5.2)
54
- tty-command (0.9.0)
55
- pastel (~> 0.7.0)
51
+ tty-color (0.6.0)
52
+ tty-command (0.10.1)
53
+ pastel (~> 0.8)
56
54
  tty-exit (0.1.0)
57
- tty-logger (0.3.0)
58
- pastel (~> 0.7.0)
55
+ tty-logger (0.6.0)
56
+ pastel (~> 0.8)
59
57
  unicode-display_width (1.7.0)
60
58
 
61
59
  PLATFORMS
@@ -67,9 +65,9 @@ DEPENDENCIES
67
65
  pry-byebug
68
66
  rspec (~> 3.9)
69
67
  rubocop (~> 0.89.0)
70
- tty-command
71
- tty-exit
72
- tty-logger
68
+ tty-command (~> 0.10)
69
+ tty-exit (~> 0.1)
70
+ tty-logger (~> 0.6)
73
71
 
74
72
  BUNDLED WITH
75
73
  2.1.4
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ccli
2
2
 
3
- Cryptopus Command Line Client
3
+ Command Line Client for [Cryptopus](https://github.com/puzzle/cryptopus)
4
4
 
5
5
  ## Installation
6
6
 
@@ -10,42 +10,81 @@ This will install the `cry` command including its dependencies
10
10
 
11
11
  ## Features
12
12
 
13
- - Fetch account data from Cryptopus
13
+ - Fetch encryptable data from Cryptopus
14
14
  - List accessable teams in Cryptopus
15
15
  - Sync Openshift/Kubernetes Secrets to Cryptopus
16
16
  - Sync Secrets from Cryptopus to Openshift/Kubernetes
17
17
 
18
18
  ## Usage
19
19
 
20
- ### Labeling secret to be synced
20
+ [Receiving the login token from Cryptopus](docs/get_login_token.md)
21
21
 
22
- So that a secret even gets considered by the `ccli`, you have to add the `cryptopus-sync=true` label to your secret:
22
+ ### Commands
23
23
 
24
- **oc:** `oc label secret <secret-name> cryptopus-sync=true`
24
+ ```
25
+ Command: Summary:
25
26
 
27
+ encryptable Fetches an encryptable by the given id
28
+ folder Selects the Cryptopus folder by id
29
+ help Display global or [command] help documentation
30
+ k8s-secret-pull Pulls secret from Kubectl to Cryptopus
31
+ k8s-secret-push Pushes secret from Cryptopus to Kubectl
32
+ login Logs in to the ccli
33
+ logout Logs out of the ccli
34
+ ose-secret-pull Pulls secret from Openshift to Cryptopus
35
+ ose-secret-push Pushes secret from Cryptopus to Openshift
36
+ teams Lists all available teams
37
+ use Select the current folder
38
+ ```
26
39
 
27
- **kubectl:** `kubectl label secret <secret-name> cryptopus-sync=true`
40
+ Show more specific documentation by calling `cry help <command>`
28
41
 
29
- ### Commands
42
+ ### Account
43
+
44
+ #### Logging in
45
+
46
+ Use the ccli login copy button from the UI or do it manually:
47
+
48
+ user=<my-user>
49
+ token=<my-token>
50
+ url=https://cryptopus.example.com
51
+
52
+ cry login $(echo -n "$user:$token" | base64)@$url
53
+
54
+ #### Retrieving
55
+
56
+ To retreive encryptable data as yaml:
30
57
 
31
58
  ```
32
- Command: Summary:
59
+ cry encryptable 42 > encryptable.yaml
60
+ ```
61
+ Retreiving encryptable's password and assign it to a variable:
33
62
 
34
- account Fetches an account by the given id
35
- folder Selects the Cryptopus folder by id
36
- help Display global or [command] help documentation
37
- k8s-secret-pull Pulls secret from Kubectl to Cryptopus
38
- k8s-secret-push Pushes secret from Cryptopus to Kubectl
39
- login Logs in to the ccli
40
- logout Logs out of the ccli
41
- ose-secret-pull Pulls secret from Openshift to Cryptopus
42
- ose-secret-push Pushes secret from Cryptopus to Openshift
43
- teams Lists all available teams
44
- use Select the current folder
63
+ ```
64
+ PASSWORD=$(cry encryptable 42 --password)
45
65
  ```
46
66
 
47
- Show more specific documentation by calling `cry help <command>`
67
+ #### Updating
68
+
69
+ not supported yet by ccli
70
+
71
+ ### Kubernetes/Openshift
72
+
73
+ #### Required tools
74
+
75
+ First you'll have to install either [oc](https://docs.openshift.com/container-platform/4.3/cli_reference/openshift_cli/getting-started-cli.html#installing-the-cli) or [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) depending on your usage
76
+
77
+ #### Pulling Kubernetes / Openshift Secrets
78
+
79
+ when using the command `{ose|k8s}-secret-pull` after beeing logged in to a k8s/ose project, all secrets labeled with `cryptopus-sync=true` are backed up to cryptopus.
48
80
 
81
+ to label a specific secret do:
82
+
83
+ **oc:** `oc label secret <secret-name> cryptopus-sync=true`
84
+
85
+ **kubectl:** `kubectl label secret <secret-name> cryptopus-sync=true`
86
+
87
+ Restored secrets by `{ose|k8s}-secret-push` are labeled automatically.
49
88
 
50
89
  ## Development
51
90
 
@@ -61,3 +100,7 @@ You will need the following things properly installed on your computer:
61
100
  - `rvm install 2.6.0`
62
101
  - `gem install bundler`
63
102
  - `bundle install`
103
+
104
+ ### Running tests
105
+
106
+ `bundle exec rspec`
data/bin/cry CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require_relative '../lib/cli'
4
+ require 'cli'
5
5
 
6
6
  CLI.new.run
data/ccli.gemspec CHANGED
@@ -5,8 +5,15 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = 'ccli'
8
- s.version = '0.1.1'
8
+ s.description = <<-EOF
9
+ CCLI is the Cryptopus Command Line Interface. It allows to fetch encryptable data and list teams from Cryptopus.
10
+ One of the main functionality is backing up secrets from cluster services (currently: openshift, kubernetes)
11
+ to Cryptopus and restoring them as well.
12
+ EOF
13
+ s.version = '1.1.0'
9
14
  s.summary = 'Command line client for the opensource password manager Cryptopus'
15
+ s.license = 'MIT'
16
+ s.homepage = 'https://github.com/puzzle/ccli'
10
17
  s.authors = ['Nils Rauch']
11
18
  s.email = 'rauch@puzzle.ch'
12
19
  s.require_paths = ['lib']
@@ -17,12 +24,14 @@ Gem::Specification.new do |s|
17
24
  s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
25
  s.required_ruby_version = Gem::Requirement.new('>= 2.0')
19
26
  s.metadata = {
20
- "source_code_uri" => "https://www.github.com/puzzle/ccli"
27
+ "bug_tracker_uri" => "https://github.com/puzzle/ccli/issues",
28
+ "changelog_uri" => "https://github.com/puzzle/ccli/blob/master/CHANGELOG.md",
29
+ "source_code_uri" => "https://github.com/puzzle/ccli"
21
30
  }
22
- s.license = 'MIT'
23
31
 
24
32
  s.add_runtime_dependency 'commander', '~> 4.5', '>= 4.5.2'
25
- s.add_runtime_dependency 'tty-command'
26
- s.add_runtime_dependency 'tty-exit'
27
- s.add_runtime_dependency 'tty-logger'
33
+ s.add_runtime_dependency 'tty-command', '~> 0.10'
34
+ s.add_runtime_dependency 'tty-exit', '~> 0.1'
35
+ s.add_runtime_dependency 'tty-logger', '~> 0.6'
36
+
28
37
  end
@@ -0,0 +1,18 @@
1
+ # Receiving the Login token from Cryptopus
2
+
3
+ To use the CCLI, you'll first have to receive the login token from Cryptopus.
4
+
5
+ 1. Log in to your instance of Cryptopus
6
+ 2. Navigate to your user settings
7
+ 3. Choose or create the api user you want to use via the ccli (keep the valid time in mind)
8
+ 4. Grant the API user permissions to access the groups you need to use with the ccli
9
+ 5. Use the ccli login copy button
10
+ 6. Copy the command from your clipboard to the terminal
11
+
12
+ ## Accessing user settings
13
+
14
+ ![user_settings](images/access_user_settings.png)
15
+
16
+ ## Copy CCLI Login
17
+
18
+ ![copy_ccli_login](images/copy_ccli_login.png)
Binary file
@@ -35,7 +35,7 @@ class ClusterSecretAdapter
35
35
  raise client_not_logged_in_error unless client_logged_in?
36
36
 
37
37
  File.open("/tmp/#{secret.name}.yml", 'w') do |file|
38
- file.write secret.ose_secret
38
+ file.write secret.to_yaml
39
39
  end
40
40
 
41
41
  cmd.run("#{client} delete -f /tmp/#{secret.name}.yml --ignore-not-found=true")
@@ -34,24 +34,23 @@ class CryptopusAdapter
34
34
  end
35
35
 
36
36
  def save_secret(secret)
37
- secret_account = secret.to_account
38
- secret_account.folder = session_adapter.selected_folder.id
39
-
40
- persisted_secret = Account.find_by_name_and_folder_id(secret.name,
41
- session_adapter.selected_folder.id)
37
+ secret_encryptable = secret.to_encryptable
38
+ secret_encryptable.folder = session_adapter.selected_folder.id
39
+ persisted_secret = Encryptable.find_by_name_and_folder_id(secret.name,
40
+ session_adapter.selected_folder.id)
42
41
  if persisted_secret
43
- patch("accounts/#{persisted_secret.id}", secret_account.to_json)
42
+ patch("encryptables/#{persisted_secret.id}", secret_encryptable.to_json)
44
43
  else
45
- post('accounts', secret_account.to_json)
44
+ post('encryptables', secret_encryptable.to_json)
46
45
  end
47
46
  end
48
47
 
49
- def find_account_by_name(name)
50
- secret_account = Account.find_by_name_and_folder_id(name, session_adapter.selected_folder.id)
48
+ def find_encryptable_by_name(name)
49
+ secret_encryptable = Encryptable.find_by_name_and_folder_id(name, session_adapter.selected_folder.id)
51
50
 
52
- raise CryptopusAccountNotFoundError unless secret_account
51
+ raise CryptopusEncryptableNotFoundError unless secret_encryptable
53
52
 
54
- secret_account
53
+ secret_encryptable
55
54
  end
56
55
 
57
56
  def renewed_auth_token
data/lib/cli.rb CHANGED
@@ -14,7 +14,7 @@ class CLI
14
14
  # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metric/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/BlockLength
15
15
  def run
16
16
  program :name, 'cry - cryptopus cli'
17
- program :version, '1.0.0'
17
+ program :version, '1.1.0'
18
18
  program :description, 'CLI tool to manage Openshift Secrets via Cryptopus'
19
19
  program :help, 'Source Code', 'https://www.github.com/puzzle/ccli'
20
20
  program :help, 'Usage', 'cry [flags]'
@@ -49,20 +49,20 @@ class CLI
49
49
  end
50
50
  end
51
51
 
52
- command :account do |c|
53
- c.syntax = 'cry account <id> [options]'
54
- c.description = 'Fetches an account by the given id'
52
+ command :encryptable do |c|
53
+ c.syntax = 'cry encryptable <id> [options]'
54
+ c.description = 'Fetches an encryptable by the given id'
55
55
  c.option '--username', String, 'Only show the username of the user'
56
56
  c.option '--password', String, 'Only show the password of the user'
57
57
 
58
58
  c.action do |args, options|
59
59
  exit_with_error(:usage_error, 'id missing') if args.empty?
60
60
  execute_action do
61
- logger.info 'Fetching account...'
62
- account = Account.find(args.first)
63
- out = account.username if options.username
64
- out = account.password if options.password
65
- puts out || account.to_yaml
61
+ logger.info 'Fetching encryptable...'
62
+ encryptable = Encryptable.find(args.first)
63
+ out = encryptable.username if options.username
64
+ out = encryptable.password if options.password
65
+ puts out || encryptable.to_yaml
66
66
  end
67
67
  end
68
68
  end
@@ -120,25 +120,25 @@ class CLI
120
120
  c.summary = 'Pushes secret from Cryptopus to Openshift'
121
121
  c.description = 'Pushes the Secret to Openshift by retrieving it from Cryptopus first. ' \
122
122
  'If a Secret in the selected Openshift project using the name ' \
123
- 'of the given accountname is already present, it will be updated accordingly.'
123
+ 'of the given name is already present, it will be updated accordingly.'
124
124
 
125
125
  c.action do |args|
126
126
  secret_name = args.first
127
127
  exit_with_error(:usage_error, 'Only one secret can be pushed') if args.length > 1
128
128
  execute_action({ secret_name: secret_name }) do
129
- secret_accounts = if secret_name.nil?
130
- logger.info 'Fetching all accounts in folder...'
131
- session_adapter.selected_folder.accounts
129
+ secret_encryptables = if secret_name.nil?
130
+ logger.info 'Fetching all encryptables in folder...'
131
+ session_adapter.selected_folder.encryptables
132
132
  else
133
- logger.info "Fetching account #{secret_name}..."
134
- [cryptopus_adapter.find_account_by_name(secret_name)]
133
+ logger.info "Fetching encryptable #{secret_name}..."
134
+ [cryptopus_adapter.find_encryptable_by_name(secret_name)]
135
135
  end
136
- secret_accounts.each do |account|
137
- logger.info "Fetching secret #{account.accountname}..."
138
- secret_account = Account.find(account.id)
139
- logger.info "Inserting secret #{account.accountname}..."
140
- ose_adapter.insert_secret(secret_account.to_osesecret)
141
- log_success "Secret #{secret_account.accountname} was successfully applied"
136
+ secret_encryptables.each do |encryptable|
137
+ logger.info "Fetching secret #{encryptable.name}..."
138
+ secret_encryptable = Encryptable.find(encryptable.id)
139
+ logger.info "Inserting secret #{encryptable.name}..."
140
+ ose_adapter.insert_secret(secret_encryptable.to_osesecret)
141
+ log_success "Secret #{secret_encryptable.name} was successfully applied"
142
142
  end
143
143
  end
144
144
  end
@@ -148,7 +148,7 @@ class CLI
148
148
  c.syntax = 'cry k8s-secret-pull <secret-name>'
149
149
  c.summary = 'Pulls secret from Kubectl to Cryptopus'
150
150
  c.description = "Pulls the Secret from Kubectl and pushes them to Cryptopus.\n" \
151
- 'If a Cryptopus Account in the selected folder using the name ' \
151
+ 'If a Cryptopus Encryptable in the selected folder using the name ' \
152
152
  "of the given secret is already present, it will be updated accordingly.\n" \
153
153
  'If no name is given, it will pull all secrets inside the selected project.'
154
154
 
@@ -180,25 +180,24 @@ class CLI
180
180
  c.summary = 'Pushes secret from Cryptopus to Kubectl'
181
181
  c.description = 'Pushes the Secret to Kubectl by retrieving it from Cryptopus first. ' \
182
182
  'If a Secret in the selected Kubectl project using the name ' \
183
- 'of the given accountname is already present, it will be updated accordingly.'
183
+ 'of the given name is already present, it will be updated accordingly.'
184
184
 
185
185
  c.action do |args|
186
186
  secret_name = args.first
187
187
  exit_with_error(:usage_error, 'Only one secret can be pushed') if args.length > 1
188
188
  execute_action({ secret_name: secret_name }) do
189
- secret_accounts = if secret_name.nil?
190
- logger.info 'Fetching all accounts in folder...'
191
- session_adapter.selected_folder.accounts
189
+ secret_encryptables = if secret_name.nil?
190
+ logger.info 'Fetching all encryptables in folder...'
191
+ session_adapter.selected_folder.encryptables
192
192
  else
193
- logger.info "Fetching account #{secret_name}..."
194
- [cryptopus_adapter.find_account_by_name(secret_name)]
193
+ logger.info "Fetching encryptable #{secret_name}..."
194
+ [cryptopus_adapter.find_encryptable_by_name(secret_name)]
195
195
  end
196
- secret_accounts.each do |account|
197
- logger.info "Fetching secret #{account.accountname}..."
198
- secret_account = Account.find(account.id)
199
- logger.info "Inserting secret #{account.accountname}..."
200
- k8s_adapter.insert_secret(secret_account.to_osesecret)
201
- log_success "Secret #{secret_account.accountname} was successfully applied"
196
+ secret_encryptables.each do |encryptable|
197
+ secret_encryptable = Encryptable.find(encryptable.id)
198
+ logger.info "Inserting secret #{encryptable.name}..."
199
+ k8s_adapter.insert_secret(secret_encryptable.to_osesecret)
200
+ log_success "Secret #{secret_encryptable.name} was successfully applied"
202
201
  end
203
202
  end
204
203
  end
@@ -264,7 +263,7 @@ class CLI
264
263
  exit_with_error(:usage_error, 'kubectl is not installed')
265
264
  rescue KubernetesClientNotLoggedInError
266
265
  exit_with_error(:usage_error, 'kubectl is not logged in')
267
- rescue CryptopusAccountNotFoundError
266
+ rescue CryptopusEncryptableNotFoundError
268
267
  exit_with_error(:usage_error, 'Secret with the given name ' \
269
268
  "#{options[:secret_name]} was not found")
270
269
  rescue OpenshiftSecretNotFoundError
data/lib/errors.rb CHANGED
@@ -30,7 +30,7 @@ end
30
30
  class NoFolderSelectedError < Error
31
31
  end
32
32
 
33
- class CryptopusAccountNotFoundError < Error
33
+ class CryptopusEncryptableNotFoundError < Error
34
34
  end
35
35
 
36
36
  class TeamNotFoundError < Error
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Encryptable
4
+ attr_reader :id, :name, :username, :password, :type, :ose_secret
5
+ attr_accessor :folder
6
+
7
+ def initialize(name: nil, username: nil, password: nil,
8
+ ose_secret: nil, type: nil, id: nil)
9
+ @id = id
10
+ @name = name
11
+ @username = username
12
+ @password = password
13
+ @ose_secret = ose_secret
14
+ @type = type || 'credentials'
15
+ end
16
+
17
+ def to_json(*_args)
18
+ EncryptableSerializer.to_json(self)
19
+ end
20
+
21
+ def to_yaml
22
+ EncryptableSerializer.to_yaml(self)
23
+ end
24
+
25
+ def to_osesecret
26
+ EncryptableSerializer.to_osesecret(self)
27
+ end
28
+
29
+ class << self
30
+ def find(id)
31
+ EncryptableSerializer.from_json(CryptopusAdapter.new.get("encryptables/#{id}"))
32
+ end
33
+
34
+ def find_by_name_and_folder_id(name, id)
35
+ Folder.find(id).encryptables.find do |encryptable|
36
+ encryptable.name.downcase == name.downcase
37
+ end
38
+ end
39
+
40
+ def from_json(json)
41
+ EncryptableSerializer.from_json(json)
42
+ end
43
+ end
44
+ end
data/lib/models/folder.rb CHANGED
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Folder
4
- attr_reader :name, :id, :accounts
4
+ attr_reader :name, :id, :encryptables
5
5
 
6
- def initialize(name: nil, id: nil, accounts: [])
6
+ def initialize(name: nil, id: nil, encryptables: [])
7
7
  @name = name
8
8
  @id = id
9
- @accounts = accounts
9
+ @encryptables = encryptables
10
10
  end
11
11
 
12
12
  class << self
@@ -15,11 +15,11 @@ class Folder
15
15
  symbolize_names: true)
16
16
  included = json[:included] || []
17
17
  name = json[:data][:attributes][:name]
18
- accounts = included.map do |record|
19
- Account.from_json(record.to_json) if %w[account_ose_secrets
20
- account_credentials].include? record[:type]
18
+ encryptables = included.map do |record|
19
+ Encryptable.from_json(record.to_json) if %w[encryptable_ose_secrets
20
+ encryptable_credentials].include? record[:type]
21
21
  end.compact
22
- Folder.new(id: id, name: name, accounts: accounts)
22
+ Folder.new(id: id, name: name, encryptables: encryptables)
23
23
  end
24
24
  end
25
25
  end
@@ -8,15 +8,29 @@ class OSESecret
8
8
  @ose_secret = ose_secret
9
9
  end
10
10
 
11
- def to_account
12
- OSESecretSerializer.to_account(self)
11
+ def to_encryptable
12
+ OSESecretSerializer.to_encryptable(self)
13
13
  end
14
14
 
15
15
  def to_yaml
16
16
  OSESecretSerializer.to_yaml(self)
17
17
  end
18
18
 
19
+ private
20
+
21
+ def encoded_data(data)
22
+ data.transform_values do |value|
23
+ Base64.strict_encode64(value)
24
+ rescue ArgumentError
25
+ value
26
+ end
27
+ end
28
+
19
29
  class << self
30
+ def from_yaml(yaml)
31
+ OSESecretSerializer.from_yaml(yaml)
32
+ end
33
+
20
34
  def find_by_name(name)
21
35
  OSESecretSerializer.from_yaml(OSEAdapter.new.fetch_secret(name))
22
36
  end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ class EncryptableSerializer
6
+ class << self
7
+ # rubocop:disable Metrics/MethodLength
8
+ def to_json(encryptable)
9
+ {
10
+ data: {
11
+ type: 'encryptables',
12
+ id: encryptable.id,
13
+ attributes: {
14
+ name: encryptable.name,
15
+ type: encryptable.type,
16
+ cleartext_username: encryptable.username,
17
+ cleartext_password: encryptable.password,
18
+ cleartext_ose_secret: encryptable.ose_secret
19
+ },
20
+ relationships: {
21
+ folder: {
22
+ data: {
23
+ id: encryptable.folder,
24
+ type: 'folders'
25
+ }
26
+ }
27
+ }
28
+ }
29
+ }.compact.to_json
30
+ end
31
+ # rubocop:enable Metrics/MethodLength
32
+
33
+ def to_yaml(encryptable)
34
+ { 'id' => encryptable.id,
35
+ 'name' => encryptable.name,
36
+ 'username' => encryptable.username,
37
+ 'password' => encryptable.password,
38
+ 'type' => encryptable.type }.to_yaml
39
+ end
40
+
41
+ def from_json(json)
42
+ json = JSON.parse(json, symbolize_names: true)
43
+ data = json[:data] || json
44
+ attributes = data[:attributes]
45
+ Encryptable.new(name: attributes[:name],
46
+ username: attributes[:cleartext_username],
47
+ password: attributes[:cleartext_password],
48
+ ose_secret: attributes[:ose_secret],
49
+ type: attributes[:type],
50
+ id: data[:id])
51
+ end
52
+
53
+ def to_osesecret(account)
54
+ OSESecret.from_yaml(account.ose_secret)
55
+ end
56
+ end
57
+ end
@@ -1,16 +1,54 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'psych'
4
+ require 'base64'
4
5
 
5
6
  class OSESecretSerializer
6
7
  class << self
8
+ # rubocop:disable Metrics/MethodLength
7
9
  def from_yaml(yaml)
8
- secret_hash = Psych.load(yaml, symbolize_names: true)
9
- OSESecret.new(secret_hash.dig(:metadata, :name), yaml)
10
+ secret_hash = Psych.load(yaml)
11
+ data = {
12
+ 'apiVersion' => secret_hash['apiVersion'],
13
+ 'data' => decoded_data(secret_hash['data']),
14
+ 'kind' => secret_hash['kind'],
15
+ 'metadata' => {
16
+ 'name' => secret_hash['metadata']['name'],
17
+ 'labels' => secret_hash['metadata']['labels']
18
+ }
19
+ }.to_yaml
20
+ OSESecret.new(secret_hash['metadata']['name'], data.to_s)
10
21
  end
22
+ # rubocop:enable Metrics/MethodLength
11
23
 
12
- def to_account(secret)
13
- Account.new(accountname: secret.name, ose_secret: secret.ose_secret, type: 'ose_secret')
24
+ def to_encryptable(secret)
25
+ Encryptable.new(name: secret.name, ose_secret: secret.ose_secret, type: 'ose_secret')
26
+ end
27
+
28
+ def to_yaml(secret)
29
+ secret_hash = Psych.load(secret.ose_secret)
30
+ secret_hash['data'] = encoded_data(secret_hash['data'])
31
+ secret_hash.to_yaml
32
+ end
33
+
34
+ private
35
+
36
+ def decoded_data(data)
37
+ return {} unless data
38
+
39
+ data.transform_values do |value|
40
+ Base64.strict_decode64(value)
41
+ rescue ArgumentError
42
+ value
43
+ end
44
+ end
45
+
46
+ def encoded_data(data)
47
+ return {} unless data
48
+
49
+ data.transform_values do |value|
50
+ Base64.strict_encode64(value)
51
+ end
14
52
  end
15
53
  end
16
54
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ccli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nils Rauch
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-22 00:00:00.000000000 Z
11
+ date: 2022-05-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: commander
@@ -34,45 +34,48 @@ dependencies:
34
34
  name: tty-command
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
- - - ">="
37
+ - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '0'
39
+ version: '0.10'
40
40
  type: :runtime
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
- - - ">="
44
+ - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '0'
46
+ version: '0.10'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: tty-exit
49
49
  requirement: !ruby/object:Gem::Requirement
50
50
  requirements:
51
- - - ">="
51
+ - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '0'
53
+ version: '0.1'
54
54
  type: :runtime
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
- - - ">="
58
+ - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: '0'
60
+ version: '0.1'
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: tty-logger
63
63
  requirement: !ruby/object:Gem::Requirement
64
64
  requirements:
65
- - - ">="
65
+ - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: '0'
67
+ version: '0.6'
68
68
  type: :runtime
69
69
  prerelease: false
70
70
  version_requirements: !ruby/object:Gem::Requirement
71
71
  requirements:
72
- - - ">="
72
+ - - "~>"
73
73
  - !ruby/object:Gem::Version
74
- version: '0'
75
- description:
74
+ version: '0.6'
75
+ description: |2
76
+ CCLI is the Cryptopus Command Line Interface. It allows to fetch encryptable data and list teams from Cryptopus.
77
+ One of the main functionality is backing up secrets from cluster services (currently: openshift, kubernetes)
78
+ to Cryptopus and restoring them as well.
76
79
  email: rauch@puzzle.ch
77
80
  executables:
78
81
  - cry
@@ -80,12 +83,17 @@ extensions: []
80
83
  extra_rdoc_files: []
81
84
  files:
82
85
  - ".rubocop.yml"
86
+ - ".tool-versions"
83
87
  - ".travis.yml"
88
+ - CHANGELOG.md
84
89
  - Gemfile
85
90
  - Gemfile.lock
86
91
  - README.md
87
92
  - bin/cry
88
93
  - ccli.gemspec
94
+ - docs/get_login_token.md
95
+ - docs/images/access_user_settings.png
96
+ - docs/images/copy_ccli_login.png
89
97
  - lib/adapters/cluster_secret_adapter.rb
90
98
  - lib/adapters/cryptopus_adapter.rb
91
99
  - lib/adapters/k8s_adapter.rb
@@ -93,21 +101,23 @@ files:
93
101
  - lib/adapters/session_adapter.rb
94
102
  - lib/cli.rb
95
103
  - lib/errors.rb
96
- - lib/models/account.rb
104
+ - lib/models/encryptable.rb
97
105
  - lib/models/folder.rb
98
106
  - lib/models/k8s_secret.rb
99
107
  - lib/models/ose_secret.rb
100
108
  - lib/models/team.rb
101
109
  - lib/presenters/team_presenter.rb
102
- - lib/serializers/account_serializer.rb
110
+ - lib/serializers/encryptable_serializer.rb
103
111
  - lib/serializers/folder_serializer.rb
104
112
  - lib/serializers/ose_secret_serializer.rb
105
113
  - lib/serializers/team_serializer.rb
106
- homepage:
114
+ homepage: https://github.com/puzzle/ccli
107
115
  licenses:
108
116
  - MIT
109
117
  metadata:
110
- source_code_uri: https://www.github.com/puzzle/ccli
118
+ bug_tracker_uri: https://github.com/puzzle/ccli/issues
119
+ changelog_uri: https://github.com/puzzle/ccli/blob/master/CHANGELOG.md
120
+ source_code_uri: https://github.com/puzzle/ccli
111
121
  post_install_message:
112
122
  rdoc_options: []
113
123
  require_paths:
@@ -123,8 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
123
133
  - !ruby/object:Gem::Version
124
134
  version: '0'
125
135
  requirements: []
126
- rubyforge_project:
127
- rubygems_version: 2.7.9
136
+ rubygems_version: 3.0.8
128
137
  signing_key:
129
138
  specification_version: 4
130
139
  summary: Command line client for the opensource password manager Cryptopus
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Account
4
- attr_reader :id, :accountname, :username, :password, :type, :ose_secret
5
- attr_accessor :folder
6
-
7
- def initialize(accountname: nil, username: nil, password: nil,
8
- ose_secret: nil, type: nil, id: nil)
9
- @id = id
10
- @accountname = accountname
11
- @username = username
12
- @password = password
13
- @ose_secret = ose_secret
14
- @type = type || 'credentials'
15
- end
16
-
17
- def to_json(*_args)
18
- AccountSerializer.to_json(self)
19
- end
20
-
21
- def to_yaml
22
- AccountSerializer.to_yaml(self)
23
- end
24
-
25
- def to_osesecret
26
- AccountSerializer.to_osesecret(self)
27
- end
28
-
29
- class << self
30
- def find(id)
31
- AccountSerializer.from_json(CryptopusAdapter.new.get("accounts/#{id}"))
32
- end
33
-
34
- def find_by_name_and_folder_id(name, id)
35
- Folder.find(id).accounts.find do |account|
36
- account.accountname.downcase == name.downcase
37
- end
38
- end
39
-
40
- def from_json(json)
41
- AccountSerializer.from_json(json)
42
- end
43
- end
44
- end
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'yaml'
4
-
5
- class AccountSerializer
6
- class << self
7
- # rubocop:disable Metrics/MethodLength
8
- def to_json(account)
9
- {
10
- data: {
11
- type: 'accounts',
12
- id: account.id,
13
- attributes: {
14
- accountname: account.accountname,
15
- type: account.type,
16
- cleartext_username: account.username,
17
- cleartext_password: account.password,
18
- ose_secret: account.ose_secret
19
- },
20
- relationships: {
21
- folder: {
22
- data: {
23
- id: account.folder,
24
- type: 'folders'
25
- }
26
- }
27
- }
28
- }
29
- }.compact.to_json
30
- end
31
- # rubocop:enable Metrics/MethodLength
32
-
33
- def to_yaml(account)
34
- { 'id' => account.id,
35
- 'accountname' => account.accountname,
36
- 'username' => account.username,
37
- 'password' => account.password,
38
- 'type' => account.type }.to_yaml
39
- end
40
-
41
- def from_json(json)
42
- json = JSON.parse(json, symbolize_names: true)
43
- data = json[:data] || json
44
- attributes = data[:attributes]
45
- Account.new(accountname: attributes[:accountname],
46
- username: attributes[:cleartext_username],
47
- password: attributes[:cleartext_password],
48
- ose_secret: attributes[:ose_secret],
49
- type: attributes[:type],
50
- id: data[:id])
51
- end
52
-
53
- def to_osesecret(account)
54
- OSESecret.new(account.accountname, account.ose_secret)
55
- end
56
- end
57
- end