secret_hub 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6835039458d739b136ad9d56baf090fe8261f4969d0ba144b7f361a436ca7116
4
+ data.tar.gz: b404dff3cb2248e124b12b3a5a7a9547a6a88554ae2e043a99b8608d09e850a0
5
+ SHA512:
6
+ metadata.gz: e78bf842dcaa9455a7bf612b49ced104346c8ccabf6de097add9073a14e9d6bb2095d8b3529ab1a1da7e39b4b82da1d2e3d7f4031f0e64decb8268e1d9a09ffa
7
+ data.tar.gz: 2dc1e103428e7a226a07a893166aac0922757b2f3224a0962c9f7a1bd2a80f4032a919adcbc62c9dabbcef5c5fd31ed80f77c25349b45d77062b4a7d04b57856
data/README.md ADDED
@@ -0,0 +1,24 @@
1
+ SecretHub
2
+ ==================================================
3
+
4
+ [![Gem Version](https://badge.fury.io/rb/secret_hub.svg)](https://badge.fury.io/rb/secret_hub)
5
+ [![Build Status](https://travis-ci.com/DannyBen/secret_hub.svg?branch=master)](https://travis-ci.com/DannyBen/secret_hub)
6
+ [![Maintainability](https://api.codeclimate.com/v1/badges/.../maintainability)](https://codeclimate.com/github/DannyBen/secret_hub/maintainability)
7
+
8
+ ---
9
+
10
+ Manage GitHub secrets over multiple repositories
11
+
12
+ ---
13
+
14
+ Installation
15
+ --------------------------------------------------
16
+
17
+ $ gem install secret_hub
18
+
19
+
20
+
21
+ Usage
22
+ --------------------------------------------------
23
+
24
+ TODO
data/bin/secrethub ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ require 'secret_hub'
3
+ require 'secret_hub/cli'
4
+ require 'colsole'
5
+ include Colsole
6
+
7
+ router = SecretHub::CLI.router
8
+
9
+ begin
10
+ exit router.run ARGV
11
+ rescue => e
12
+ puts e.backtrace.reverse if ENV['DEBUG']
13
+ say! "!txtred!#{e.class}"
14
+ say! e.message
15
+ exit 1
16
+ end
@@ -0,0 +1,23 @@
1
+ require 'mister_bin'
2
+ require 'secret_hub/commands/base'
3
+ require 'secret_hub/commands/list'
4
+ require 'secret_hub/commands/save'
5
+ require 'secret_hub/commands/delete'
6
+ require 'secret_hub/commands/bulk'
7
+
8
+ module SecretHub
9
+ class CLI
10
+ def self.router
11
+ router = MisterBin::Runner.new version: VERSION,
12
+ header: "GitHub Secret Manager"
13
+
14
+ router.route 'list', to: Commands::List
15
+ router.route 'save', to: Commands::Save
16
+ router.route 'delete', to: Commands::Delete
17
+ router.route 'bulk', to: Commands::Bulk
18
+
19
+ router
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,16 @@
1
+ require 'mister_bin'
2
+ require 'colsole'
3
+ require 'lp'
4
+
5
+ module SecretHub
6
+ module Commands
7
+ class Base < MisterBin::Command
8
+ include Colsole
9
+
10
+ def github
11
+ @github ||= GitHubClient.new
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,105 @@
1
+ require 'yaml'
2
+
3
+ module SecretHub
4
+ module Commands
5
+ class Bulk < Base
6
+ summary "Update or delete multiple secrets from multiple repositories"
7
+
8
+ usage "secrethub bulk init [CONFIG]"
9
+ usage "secrethub bulk save [CONFIG --clean]"
10
+ usage "secrethub bulk clean [CONFIG]"
11
+ usage "secrethub bulk list [CONFIG]"
12
+ usage "secrethub bulk (-h|--help)"
13
+
14
+ command "init", "Create a sample configuration file in the current directory"
15
+ command "save", "Save multiple secrets to multiple repositories"
16
+ command "clean", "Delete secrets from multiple repositories unless they are specified in the config file"
17
+ command "list", "Show all secrets in all repositories"
18
+
19
+ option "-c, --clean", "Also delete any other secret not defined in the config file"
20
+
21
+ param "CONFIG", "Path to the configuration file"
22
+
23
+ example "secrethub bulk init"
24
+ example "secrethub bulk clean"
25
+ example "secrethub bulk update mysecrets.yml"
26
+ example "secrethub bulk update --clean"
27
+
28
+ def init_command
29
+ raise SecretHubError, "File #{config_file} already exists" if File.exist? config_file
30
+
31
+ content = {
32
+ "user/repo" => %w[SECRET PASSWORD SECRET_KEY],
33
+ "user/another-repo" => %w[SECRET SECRET_KEY],
34
+ }
35
+
36
+ File.write config_file, content.to_yaml
37
+ say "!txtgrn!Saved #{config_file}"
38
+ end
39
+
40
+ def save_command
41
+ clean = args['--clean']
42
+
43
+ config.each do |repo, keys|
44
+ say "!txtblu!#{repo}"
45
+ update_repo repo, keys
46
+ clean_repo repo, keys if clean
47
+ end
48
+ end
49
+
50
+ def clean_command
51
+ config.each do |repo, keys|
52
+ say "!txtblu!#{repo}"
53
+ clean_repo repo, keys
54
+ end
55
+ end
56
+
57
+ def list_command
58
+ config.each do |repo, keys|
59
+ say "!txtblu!#{repo}"
60
+ github.secrets(repo).each do |secret|
61
+ say "!txtpur!#{secret}"
62
+ end
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ def clean_repo(repo, keys)
69
+ repo_keys = github.secrets repo
70
+ delete_candidates = repo_keys - keys
71
+
72
+ delete_candidates.each do |key|
73
+ say "delete !txtpur!#{key} "
74
+ success = github.delete_secret repo, key
75
+ say "!txtgrn!OK"
76
+ end
77
+ end
78
+
79
+ def update_repo(repo, keys)
80
+ keys.each do |key|
81
+ say "save !txtpur!#{key} "
82
+ github.put_secret repo, key, secret_value(key)
83
+ say "!txtgrn!OK"
84
+ end
85
+ end
86
+
87
+ def secret_value(key)
88
+ ENV[key] || raise(EnvironmentError, "Please set the #{key} environment variable")
89
+ end
90
+
91
+ def config_file
92
+ args['CONFIG'] || 'secrethub.yml'
93
+ end
94
+
95
+ def config
96
+ raise ConfigurationError, "Config file not found #{config_flie}" unless File.exist? config_file
97
+ result = YAML.load_file config_file
98
+ result.each do |key, value|
99
+ raise ConfigurationError, "Invalid repo #{key}" unless key.include? '/'
100
+ raise ConfigurationError, "Invalid keys for #{key} - must be an array" unless value.is_a? Array
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,23 @@
1
+ module SecretHub
2
+ module Commands
3
+ class Delete < Base
4
+ summary "Delete a secret from a repository"
5
+
6
+ usage "secrethub delete REPO KEY"
7
+ usage "secrethub delete (-h|--help)"
8
+
9
+ param "REPO", "Full name of the GitHub repository (user/repo)"
10
+ param "KEY", "The name of the secret"
11
+
12
+ example "secrethub delete bob/vault PASSWORD"
13
+
14
+ def run
15
+ repo = args['REPO']
16
+ key = args['KEY']
17
+
18
+ success = github.delete_secret repo, key
19
+ say "Deleted !txtblu!#{repo} !txtpur!#{key}"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ module SecretHub
2
+ module Commands
3
+ class List < Base
4
+ summary "Show secrets for a repository"
5
+
6
+ usage "secrethub list REPO"
7
+ usage "secrethub list (-h|--help)"
8
+
9
+ param "REPO", "Full name of the GitHub repository (user/repo)"
10
+
11
+ example "secrethub list bob/repo-woth-secrets"
12
+
13
+ def run
14
+ repo = args['REPO']
15
+ say "!txtblu!#{repo}"
16
+ github.secrets(repo).each do |secret|
17
+ say "!txtpur!#{secret}"
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,25 @@
1
+ module SecretHub
2
+ module Commands
3
+ class Save < Base
4
+ summary "Create or update a secret in a repository"
5
+
6
+ usage "secrethub save REPO KEY VALUE"
7
+ usage "secrethub save (-h|--help)"
8
+
9
+ param "REPO", "Full name of the GitHub repository (user/repo)"
10
+ param "KEY", "The name of the secret"
11
+ param "VALUE", "The plain text secret value"
12
+
13
+ example "secrethub save bob/vault PASSWORD p4ssw0rd"
14
+
15
+ def run
16
+ repo = args['REPO']
17
+ key = args['KEY']
18
+ value = args['VALUE']
19
+
20
+ github.put_secret repo, key, value
21
+ say "Saved !txtblu!#{repo} !txtpur!#{key}"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,14 @@
1
+ module SecretHub
2
+ SecretHubError = Class.new StandardError
3
+ ConfigurationError = Class.new SecretHubError
4
+ EnvironmentError = Class.new SecretHubError
5
+
6
+ class APIError < SecretHubError
7
+ attr_reader :response
8
+
9
+ def initialize(response)
10
+ @response = response
11
+ super "[#{response.code}] #{response.body}"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,83 @@
1
+ require 'httparty'
2
+ require 'secret_hub/sodium'
3
+
4
+ module SecretHub
5
+ class GitHubClient
6
+ include Sodium
7
+ include HTTParty
8
+
9
+ def initialize
10
+ self.class.base_uri ENV['SECRET_HUB_API_BASE'] || 'https://api.github.com'
11
+ end
12
+
13
+ # GET /repos/:owner/:repo/actions/secrets/public-key
14
+ def public_key(repo)
15
+ public_keys[repo] ||= get("/repos/#{repo}/actions/secrets/public-key")
16
+ end
17
+
18
+ # GET /repos/:owner/:repo/actions/secrets
19
+ def secrets(repo)
20
+ response = get "/repos/#{repo}/actions/secrets"
21
+ response['secrets'].map { |s| s['name'] }
22
+ end
23
+
24
+ # PUT /repos/:owner/:repo/actions/secrets/:name
25
+ def put_secret(repo, name, value)
26
+ secret = encrypt_for_repo repo, value
27
+ key_id = public_key(repo)['key_id']
28
+ put "/repos/#{repo}/actions/secrets/#{name}",
29
+ encrypted_value: secret,
30
+ key_id: key_id
31
+ end
32
+
33
+ # DELETE /repos/:owner/:repo/actions/secrets/:name
34
+ def delete_secret(repo, name)
35
+ delete "/repos/#{repo}/actions/secrets/#{name}"
36
+ end
37
+
38
+ private
39
+
40
+ def public_keys
41
+ @public_keys ||= {}
42
+ end
43
+
44
+ def encrypt_for_repo(repo, secret)
45
+ public_key = public_key(repo)['key']
46
+ encrypt secret, public_key
47
+ end
48
+
49
+ def get(url)
50
+ response = self.class.get(url, request_options)
51
+ response.success? or raise APIError, response
52
+ response.parsed_response
53
+ end
54
+
55
+ def put(url, args = {})
56
+ options = { body: args.to_json }
57
+ all_options = request_options.merge options
58
+ response = self.class.put url, all_options
59
+ response.success? or raise APIError, response
60
+ end
61
+
62
+ def delete(url)
63
+ response = self.class.delete url, request_options
64
+ response.success? or raise APIError, response
65
+ end
66
+
67
+ def request_options
68
+ { headers: headers }
69
+ end
70
+
71
+ def headers
72
+ {
73
+ "Authorization" => "token #{secret_token}",
74
+ "User-Agent" => "SecretHub Gem"
75
+ }
76
+ end
77
+
78
+ def secret_token
79
+ ENV['GITHUB_ACCESS_TOKEN'] || raise(EnvironmentError, "Please set GITHUB_ACCESS_TOKEN")
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,16 @@
1
+ require "base64"
2
+ require 'rbnacl'
3
+
4
+ module SecretHub
5
+ module Sodium
6
+ def encrypt(secret, public_key)
7
+ key = Base64.decode64 public_key
8
+ public_key = RbNaCl::PublicKey.new key
9
+
10
+ box = RbNaCl::Boxes::Sealed.from_public_key public_key
11
+ encrypted_secret = box.encrypt secret
12
+
13
+ Base64.strict_encode64 encrypted_secret
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module SecretHub
2
+ VERSION = "0.0.1"
3
+ end
data/lib/secret_hub.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'secret_hub/version'
2
+ require 'secret_hub/exceptions'
3
+ require 'secret_hub/github_client'
4
+
5
+ require 'byebug' if ENV['BYEBUG']
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: secret_hub
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Danny Ben Shitrit
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-02-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mister_bin
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: colsole
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.7'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: httparty
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.17'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.17'
55
+ - !ruby/object:Gem::Dependency
56
+ name: lp
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.2'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.2'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rbnacl
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '7.1'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '7.1'
83
+ description: Command line interface for managing GitHub secrets in bulk
84
+ email: db@dannyben.com
85
+ executables:
86
+ - secrethub
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - README.md
91
+ - bin/secrethub
92
+ - lib/secret_hub.rb
93
+ - lib/secret_hub/cli.rb
94
+ - lib/secret_hub/commands/base.rb
95
+ - lib/secret_hub/commands/bulk.rb
96
+ - lib/secret_hub/commands/delete.rb
97
+ - lib/secret_hub/commands/list.rb
98
+ - lib/secret_hub/commands/save.rb
99
+ - lib/secret_hub/exceptions.rb
100
+ - lib/secret_hub/github_client.rb
101
+ - lib/secret_hub/sodium.rb
102
+ - lib/secret_hub/version.rb
103
+ homepage: https://github.com/dannyben/secret_hub
104
+ licenses:
105
+ - MIT
106
+ metadata: {}
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: 2.4.0
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubygems_version: 3.0.3
123
+ signing_key:
124
+ specification_version: 4
125
+ summary: Manage GitHub secrets over multiple repositories
126
+ test_files: []