docker_registry_cli 0.0.3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 31d84fd6d9531937a30478e0bfbe82fb519bf5ed
4
- data.tar.gz: af9ec7a25fa3e470677848f99de04b216ec0ae5c
3
+ metadata.gz: 0318db7ea65bbf5d67e1f4d2da3d0295f243b1f6
4
+ data.tar.gz: fad5ebcd07f37a4d5361f35fa89dd56c33fda087
5
5
  SHA512:
6
- metadata.gz: be1aee21531622ea0c6c2157c9b947f6a017afcadd29a4801efc8e76c3aa0a78102c36a28ad17e7c389fe4f3489f006d66e6da6b33a17c66bcbed72d0b64523d
7
- data.tar.gz: c0dde82c3469fc7a686082aa0162863aaa26939f1497ed4179a191ddbea2a7b76dc645bb777f3f8298160d3bb243ae4975adf97d48b2a1e4b628a7a916b8cc6b
6
+ metadata.gz: 22f009f75a5a27ee8793a10d84d617e7cc515cd22890227dc9ab4bb73cc8510a9751a79efb47417bd064db129645dcc7fa78b6b939993cff39ca61c96349fcda
7
+ data.tar.gz: 27a62e49792644cee56d22a4ab5f06386ff8bc45a8084f789d8016e85aa05dd855468852aa774572d19c295c04570144b9273c822caa3873fe49ef1f18708b41
@@ -1,26 +1,44 @@
1
1
  class BasicAuthService
2
-
3
- @@requestToBeAuthed = nil
2
+ @requestToBeAuthed = nil
4
3
  def initialize(requestToBeAuthed)
5
- @@requestToBeAuthed = requestToBeAuthed
4
+ @requestToBeAuthed = requestToBeAuthed
6
5
  end
7
6
  def byCredentials(user, pass)
8
- @@requestToBeAuthed.class.basic_auth user, pass
7
+ @requestToBeAuthed.class.basic_auth user, pass
9
8
  end
10
9
 
11
- def byToken(domain)
10
+ def byToken
12
11
  # load the docker config and see, if the domain is included there - reuse the auth token
13
- config = JSON.parse(File.read(File.join(ENV['HOME'], '.docker/config.json')))
14
- token = config['auths'][domain]['auth']
15
- if(!token)
16
- pp config if @@debug
17
- throw('No auth token found for this domain')
12
+ config = nil
13
+ config_path = File.join(ENV['HOME'], '.docker/config.json')
14
+ if File.exist?(config_path)
15
+ config = JSON.parse(File.read(config_path))
18
16
  end
19
17
 
20
- pp "Using existing token from config.json "
18
+ if !config.nil? && config['auths'].key?(@requestToBeAuthed.registry_domain) && config['auths'][@requestToBeAuthed.registry_domain]
19
+ pp 'Using existing token from config.json not the keychain. Do not do that!!'.red
20
+ # decorate our request object
21
+ # set the Authorization header directly, since it is already base64 encoded
22
+ token = config['auths'][@requestToBeAuthed.registry_domain]['auth']
23
+ if !token
24
+ pp config if @@debug
25
+ throw 'No auth token found for this domain'
26
+ end
27
+ @requestToBeAuthed.class.headers['Authorization'] = "Basic #{token}"
28
+ else
29
+ use_keychain_auth
30
+ end
31
+ end
21
32
 
22
- # decorate our request object
23
- # set the Authorization header directly, since it is already base64 encoded
24
- @@requestToBeAuthed.class.headers['Authorization'] = "Basic #{token}"
33
+ def use_keychain_auth
34
+ osx_cred_entry = Keychain.internet_passwords.where(:path => @requestToBeAuthed.registry_domain).first
35
+ if osx_cred_entry.nil?
36
+ puts "Not found any keychain entry for registry #{@requestToBeAuthed.registry_domain}, please auth using: docker login #{@requestToBeAuthed.registry_domain}".red if @debug
37
+ throw 'Keychain entry not found'
38
+ elsif @debug
39
+ puts "Found user '#{osx_cred_entry.account}' in keychain".white
40
+ end
41
+ # we do nto use basic_auth here, since we cannot override it later if we need to use Bearer auth
42
+ @requestToBeAuthed.class.headers['Authorization'] = @requestToBeAuthed.class.basic_encode(osx_cred_entry.account, osx_cred_entry.password)
25
43
  end
26
44
  end
@@ -1,12 +1,17 @@
1
+ require 'keychain'
2
+ require 'colorize'
3
+
1
4
  class TokenAuthService
2
- @@requestToBeAuthed = nil
5
+ @debug
6
+ @requestToBeAuthed = nil
3
7
  def initialize(requestToBeAuthed)
4
- @@requestToBeAuthed = requestToBeAuthed
8
+ @requestToBeAuthed = requestToBeAuthed
9
+ @debug = @requestToBeAuthed.is_debug
5
10
  end
6
11
 
7
12
  def tokenAuth(responseToBeAuthed)
8
13
  # retrieve how we will contact the token service
9
- auth_description = responseToBeAuthed.headers()['www-authenticate']
14
+ auth_description = responseToBeAuthed.headers['www-authenticate']
10
15
  options = {
11
16
  :query => {
12
17
  }
@@ -17,25 +22,52 @@ class TokenAuthService
17
22
 
18
23
  # get the service we will generate the tokens for
19
24
  m = /service="(?<service>[^"]+)"/.match(auth_description)
20
- if(m)
25
+ if m
21
26
  options[:query][:service] = m['service']
22
27
  end
23
- # the scope, importent if scopes are used in the token service
28
+ # the scope, important if scopes are used in the token service
24
29
  # this is not always set
25
30
  m = /scope="(?<scope>[^"]+)"/.match(auth_description)
26
- if(m)
31
+ if m
27
32
  options[:query][:scope] = m['scope']
28
33
  end
29
34
 
30
- unless(url)
31
- puts "Could not extract auth information for Bearer token".colorize(:red) if @@debug
32
- throw "Could not extract auth information for Bearer token"
35
+ unless url
36
+ puts 'Could not extract auth information for Bearer token'.colorize(:red) if @debug
37
+ throw 'Could not extract auth information for Bearer token'
38
+ end
39
+
40
+ config = nil
41
+ config_path = File.join(ENV['HOME'], '.docker/config.json')
42
+ if File.exist?(config_path)
43
+ config = JSON.parse(File.read(config_path))
33
44
  end
34
- # get the bearer token
35
- response = HTTParty.get(url,options)
45
+
46
+ if !config.nil? && config['auths'].key?(@requestToBeAuthed.registry_domain) && config['auths'][@requestToBeAuthed.registry_domain]
47
+ domain_settings = config['auths'][@requestToBeAuthed.registry_domain]
48
+ puts 'Using existing token from config.json not the keychain. Do not do that, use the keychain!'.red
49
+ # decorate our request object
50
+ # set the Authorization header directly, since it is already base64 encoded
51
+ options[:headers] = {}
52
+ options[:headers]['Authorization'] = "Basic #{domain_settings['auth']}"
53
+ else
54
+ # load credentials from the keychain
55
+ puts 'getting credentials from keychain'.white if @debug
56
+ osx_cred_entry = Keychain.internet_passwords.where(:path => @requestToBeAuthed.registry_domain).first
57
+ if osx_cred_entry.nil?
58
+ puts "Not found any keychain entry for registry #{@requestToBeAuthed.registry_domain}, please auth using: docker login #{@requestToBeAuthed.registry_domain}".red if @debug
59
+ throw 'Keychain entry not found'
60
+ elsif @debug
61
+ puts "Found user '#{osx_cred_entry.account}' in keychain".white
62
+ end
63
+ # get the bearer token
64
+ options[:basic_auth] = {username: osx_cred_entry.account, password: osx_cred_entry.password}
65
+ end
66
+
67
+ response = HTTParty.get(url, options)
36
68
 
37
69
  throw "Failed to authenticate, code #{response.code}" unless response.code == 200 || response.has_key?('token')
38
70
  # decorate our request object
39
- @@requestToBeAuthed.class.headers['Authorization'] = "Bearer #{response['token']}"
71
+ @requestToBeAuthed.class.headers['authorization'] = "Bearer #{response['token']}"
40
72
  end
41
73
  end
@@ -7,7 +7,7 @@ require 'pp'
7
7
  require_relative '../commands/DockerRegistryCommand'
8
8
 
9
9
  # our defaults / defines
10
- options = {:user => nil,:password => nil, :domain => nil, :debug => false}
10
+ options = {:user => nil, :password => nil, :registry_domain => nil, :debug => false}
11
11
  ops = ['list','search', 'tags', 'delete_image', 'delete_tag']
12
12
 
13
13
  # define our options and help
@@ -20,10 +20,10 @@ OptionParser.new do |opts|
20
20
  opts.on("-p", "--password PASSWORD", "optional, password to login") do |v|
21
21
  options[:password] = v
22
22
  end
23
- opts.on("--domain DOMAIN", "Set this to override the domain you defined in ~./docker_registry.yml") do |v|
24
- options[:domain] = v
23
+ opts.on("-d", "--domain DOMAIN", "Set this to override the domain you defined in ~./docker_registry.yml") do |v|
24
+ options[:registry_domain] = v
25
25
  end
26
- opts.on("-d", "--debug", "debug mode") do |v|
26
+ opts.on("-v", "--verbose", "verbose mode") do |v|
27
27
  options[:debug] = v
28
28
  end
29
29
  opts.on("-h", "--help", "Prints this help") do
@@ -52,7 +52,7 @@ begin
52
52
  pp config
53
53
  end
54
54
 
55
- options[:domain] = config['domain'] if config['domain'] && !options[:domain]
55
+ options[:registry_domain] = config['domain'] if config['domain'] && !options[:registry_domain]
56
56
  options[:user] = config['user'] if config['user'] && !options[:user]
57
57
  options[:password] = config['password'] if config['password'] && !options[:password]
58
58
  rescue
@@ -62,7 +62,7 @@ rescue
62
62
  puts 'The first argument should be your registry domain without schema (HTTPS mandatory), optional with :port'.colorize(:red)
63
63
  exit 1
64
64
  else
65
- options[:domain] = ARGV.shift
65
+ options[:registry_domain] = ARGV.shift
66
66
  end
67
67
  end
68
68
 
@@ -86,7 +86,7 @@ if options[:debug]
86
86
  end
87
87
 
88
88
  # configure our request handler
89
- registry = DockerRegistryCommand.new(options[:domain], options[:user], options[:password], options[:debug])
89
+ registry = DockerRegistryCommand.new(options[:registry_domain], options[:user], options[:password], options[:debug])
90
90
 
91
91
  # run the operations, which can actually have different amounts of mandatory arguments
92
92
  case op
@@ -7,7 +7,7 @@ class DockerRegistryCommand < DockerRegistryRequest
7
7
  def list(search_key = nil)
8
8
  response = send_get_request("/_catalog")
9
9
  if response['repositories'].nil?
10
- puts "no repositories found"
10
+ puts 'no repositories found'
11
11
  exit 1
12
12
  else
13
13
  response['repositories'].each { |repo|
@@ -14,45 +14,54 @@ class DockerRegistryRequest
14
14
  format :json
15
15
  headers 'Content-Type' => 'application/json'
16
16
  headers 'Accept' => 'application/json'
17
- @@debug = false
17
+ @debug = false
18
+ @registry_domain = nil
19
+ def initialize(registry_domain, user = nil, pass = nil, debug = false)
20
+ @debug = debug
21
+ @registry_domain = registry_domain
22
+ self.class.base_uri "https://#{registry_domain}/v2"
23
+ handle_pre_auth(registry_domain, user, pass)
24
+ end
25
+
26
+ def registry_domain
27
+ @registry_domain
28
+ end
18
29
 
19
- def initialize(domain, user = nil, pass = nil, debug = false)
20
- @@debug = debug
21
- self.class.base_uri "https://#{domain}/v2"
22
- handle_preauth(domain, user, pass)
30
+ def is_debug
31
+ return @debug
23
32
  end
24
33
 
25
- def handle_preauth(domain, user = nil, pass = nil)
34
+ def handle_pre_auth(domain, user = nil, pass = nil)
26
35
  # Only BasicAuth is supported
27
- authService = BasicAuthService.new(self)
36
+ auth_service = BasicAuthService.new(self)
28
37
  begin
29
38
  if user && pass
30
39
  # this will base64 encode automatically
31
- authService.byCredentials(user, pass)
40
+ auth_service.byCredentials(user, pass)
32
41
  else
33
- authService.byToken(domain)
42
+ auth_service.byToken
34
43
  end
35
44
  rescue Exception
36
- puts "No BasicAuth pre-auth available, will try to use different auth-services later".green
45
+ puts 'No BasicAuth pre-auth available, will try to use different auth-services later'.green if is_debug
37
46
  end
38
47
 
39
48
  end
40
49
 
41
50
  ### check if the login actually will succeed
42
51
  def authenticate(response)
43
- headers = response.headers()
52
+ headers = response.headers
44
53
  begin
45
54
  if headers.has_key?('www-authenticate')
46
55
  auth_description = headers['www-authenticate']
47
56
  if auth_description.match('Bearer realm=')
48
- authService = TokenAuthService.new(self)
49
- authService.tokenAuth(response)
57
+ auth_service = TokenAuthService.new(self)
58
+ auth_service.tokenAuth(response)
50
59
  else
51
60
  throw "Auth method not supported #{auth_description}"
52
61
  end
53
62
  end
54
- rescue Exception
55
- puts "Authentication failed".colorize(:red)
63
+ rescue Exception => e
64
+ puts "Authentication failed #{e.message}".colorize(:red)
56
65
  end
57
66
  end
58
67
 
@@ -73,7 +82,7 @@ class DockerRegistryRequest
73
82
  unless response.code == 200
74
83
  throw "Could not finish request, status #{response.code}"
75
84
  end
76
- return response
85
+ response
77
86
  end
78
87
 
79
88
  ## sends a delete request, authenticates if needed
@@ -91,7 +100,7 @@ class DockerRegistryRequest
91
100
  if response.code != 200 && response.code != 202
92
101
  throw "Could not finish request, status #{response.code}"
93
102
  end
94
- return response
103
+ response
95
104
  end
96
105
 
97
106
  ### returns the digest for a tag
@@ -114,6 +123,6 @@ class DockerRegistryRequest
114
123
  exit 1
115
124
  end
116
125
 
117
- return response.headers['docker-content-digest']
126
+ response.headers['docker-content-digest']
118
127
  end
119
128
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: docker_registry_cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eugen Mayer
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: ruby-keychain
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  description: This cli-tool lets you query your private docker registry for different
42
56
  things. For now, there is no tool provided by docker to do so
43
57
  email: eugen.mayer@kontextwork.de