docker_registry_cli 0.0.3 → 0.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
  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