ccli 0.1.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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