docker-remote 0.6.0 → 0.7.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: 67104ac59d9b809cfaafaac9c1711b4d68b4d514c4c0360a28cb42cea105a72b
4
- data.tar.gz: 59d0560b904d4fbf9bd4e7484caf179cf0355f3b716eaaf104c87a3700be00f6
3
+ metadata.gz: 7aafe47e42bed5d3cd16a135a0a3a6f771aaceb9c35b796bf0706ec09137814d
4
+ data.tar.gz: 1c5aa9f0e89a229cbe203f5d535154fcb85fe270df340862200f3a14df5e0789
5
5
  SHA512:
6
- metadata.gz: ee73da94c49c3c70779ad258d9b0e0147c2768998762bcaa5829756ba4203163245fcb00031bdd7d9abeaee5d680bc5077788fa5826deb03e7e99f9a7eb01591
7
- data.tar.gz: 1cd3bac68947f1bf7af75ca903de81b932ae2258842ae22a71eee444c621ea63975b243cbc63a1bd273a848d756a3427409548375454580372f6f7c6d00b0626
6
+ metadata.gz: d2a2c898b5150f0a69e6c42a8c097f5956044201749167a3648a89725f0ae62e2e5e03e9432133f68637325406842c3d529fa3aaed76ac07e19c51d67b569920
7
+ data.tar.gz: 1306ccd01efe9184963586f0b34bbe0d9b10ea262b9570538b8d234d4024c0e862dfc001cffa1c144b67699e9a231155940ea18fc36ebccf59ae444ad8777ab1
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 0.7.0
2
+ * Support Azure Container Registry (ACR)
3
+ - ACR does two things differently than other registries like DockerHub, GitHub, etc:
4
+ 1. OAuth tokens are returned under the `access_token` key instead of `token`.
5
+ 1. The repo:<name>:pull scope is not enough to read metadata like the list of tags. You need the repo:<name>:metadata_read scope instead. Fortunately the www-authenticate header contains the scope you need to perform the operation.
6
+
1
7
  ## 0.6.0
2
8
  * Raise `UnknownRepoError` if the registry returns the `NAME_UNKNOWN` error code, which indicates the repo has never been pushed to before.
3
9
 
@@ -0,0 +1,40 @@
1
+ module Docker
2
+ module Remote
3
+ class AuthInfo
4
+ class << self
5
+ def from_header(header, creds)
6
+ idx = header.index(' ')
7
+ auth_type = header[0..idx].strip.downcase
8
+
9
+ params = header[idx..-1].split(',').each_with_object({}) do |param, ret|
10
+ key, value = param.split('=')
11
+ ret[key.strip] = value.strip[1..-2] # remove quotes
12
+ end
13
+
14
+ new(auth_type, params, creds)
15
+ end
16
+ end
17
+
18
+
19
+ attr_reader :auth_type, :params, :creds
20
+
21
+ def initialize(auth_type, params, creds)
22
+ @auth_type = auth_type
23
+ @params = params
24
+ @creds = creds
25
+ end
26
+
27
+ def strategy
28
+ @strategy ||= case auth_type
29
+ when 'bearer'
30
+ BearerAuth.new(self, creds)
31
+ when 'basic'
32
+ BasicAuth.new(creds)
33
+ else
34
+ raise UnsupportedAuthTypeError,
35
+ "unsupported Docker auth type '#{auth_type}'"
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -3,16 +3,15 @@ require 'net/http'
3
3
  module Docker
4
4
  module Remote
5
5
  class BasicAuth
6
- attr_reader :username, :password
6
+ attr_reader :creds
7
7
 
8
- def initialize(username, password)
9
- @username = username
10
- @password = password
8
+ def initialize(creds)
9
+ @creds = creds
11
10
  end
12
11
 
13
12
  def make_get(path)
14
13
  Net::HTTP::Get.new(path).tap do |request|
15
- request.basic_auth(username, password)
14
+ request.basic_auth(creds.username, creds.password)
16
15
  end
17
16
  end
18
17
  end
@@ -7,13 +7,11 @@ module Docker
7
7
  class BearerAuth
8
8
  include Utils
9
9
 
10
- attr_reader :params, :repo, :username, :password
10
+ attr_reader :auth_info, :creds
11
11
 
12
- def initialize(params, repo, username, password)
13
- @params = params
14
- @repo = repo
15
- @username = username
16
- @password = password
12
+ def initialize(auth_info, creds)
13
+ @auth_info = auth_info
14
+ @creds = creds
17
15
  end
18
16
 
19
17
  def make_get(path)
@@ -25,11 +23,11 @@ module Docker
25
23
  private
26
24
 
27
25
  def realm
28
- @realm ||= URI.parse(params['realm'])
26
+ @realm ||= URI.parse(auth_info.params['realm'])
29
27
  end
30
28
 
31
29
  def service
32
- @serivce ||= params['service']
30
+ @serivce ||= auth_info.params['service']
33
31
  end
34
32
 
35
33
  def token
@@ -37,17 +35,24 @@ module Docker
37
35
  http = Net::HTTP.new(realm.host, realm.port)
38
36
  http.use_ssl = true if realm.scheme == 'https'
39
37
 
38
+ url_params = { service: service }
39
+
40
+ if scope = auth_info.params['scope']
41
+ url_params[:scope] = scope
42
+ end
43
+
40
44
  request = Net::HTTP::Get.new(
41
- "#{realm.request_uri}?service=#{service}&scope=repository:#{repo}:pull"
45
+ "#{realm.request_uri}?#{URI.encode_www_form(url_params)}"
42
46
  )
43
47
 
44
- if username && password
45
- request.basic_auth(username, password)
48
+ if creds.username && creds.password
49
+ request.basic_auth(creds.username, creds.password)
46
50
  end
47
51
 
48
52
  response = http.request(request)
49
53
  potentially_raise_error!(response)
50
- JSON.parse(response.body)['token']
54
+ body_json = JSON.parse(response.body)
55
+ body_json['token'] || body_json['access_token']
51
56
  end
52
57
  end
53
58
  end
@@ -12,7 +12,7 @@ module Docker
12
12
  class Client
13
13
  include Utils
14
14
 
15
- attr_reader :registry_url, :repo, :username, :password
15
+ attr_reader :registry_url, :repo, :creds
16
16
 
17
17
  PORTMAP = { 'ghcr.io' => 443 }.freeze
18
18
  DEFAULT_PORT = 443
@@ -21,8 +21,7 @@ module Docker
21
21
  def initialize(registry_url, repo, username = nil, password = nil)
22
22
  @registry_url = registry_url
23
23
  @repo = repo
24
- @username = username
25
- @password = password
24
+ @creds = Credentials.new(username, password)
26
25
  end
27
26
 
28
27
  def tags
@@ -47,14 +46,14 @@ module Docker
47
46
 
48
47
  def auth
49
48
  @auth ||= begin
50
- response = get('/v2/', use_auth: nil)
49
+ response = get('/v2/', use_auth: NoAuth.instance)
51
50
 
52
- case response.code
53
- when '200'
51
+ case response
52
+ when Net::HTTPSuccess
54
53
  NoAuth.instance
55
- when '401'
56
- www_auth(response)
57
- when '404'
54
+ when Net::HTTPUnauthorized
55
+ www_auth(response).strategy
56
+ when Net::HTTPNotFound
58
57
  raise UnsupportedVersionError,
59
58
  "the registry at #{registry_url} doesn't support v2 "\
60
59
  'of the Docker registry API'
@@ -67,25 +66,7 @@ module Docker
67
66
  end
68
67
 
69
68
  def www_auth(response)
70
- auth = response['www-authenticate']
71
-
72
- idx = auth.index(' ')
73
- auth_type = auth[0..idx].strip
74
-
75
- params = auth[idx..-1].split(',').each_with_object({}) do |param, ret|
76
- key, value = param.split('=')
77
- ret[key.strip] = value.strip[1..-2] # remove quotes
78
- end
79
-
80
- case auth_type.downcase
81
- when 'bearer'
82
- BearerAuth.new(params, repo, username, password)
83
- when 'basic'
84
- BasicAuth.new(username, password)
85
- else
86
- raise UnsupportedAuthTypeError,
87
- "unsupported Docker auth type '#{auth_type}'"
88
- end
69
+ AuthInfo.from_header(response['www-authenticate'], creds)
89
70
  end
90
71
 
91
72
  def get(path, http: registry_http, use_auth: auth, limit: 5)
@@ -93,24 +74,31 @@ module Docker
93
74
  raise DockerRemoteError, 'too many redirects'
94
75
  end
95
76
 
96
- request = if use_auth
97
- use_auth.make_get(path)
98
- else
99
- Net::HTTP::Get.new(path)
100
- end
101
-
77
+ request = use_auth.make_get(path)
102
78
  response = http.request(request)
103
79
 
104
80
  case response
81
+ when Net::HTTPUnauthorized
82
+ auth_info = www_auth(response)
83
+
84
+ if auth_info.params['error'] == 'insufficient_scope'
85
+ if auth_info.params.include?('scope')
86
+ return get(
87
+ path,
88
+ http: http,
89
+ use_auth: auth_info.strategy,
90
+ limit: limit - 1
91
+ )
92
+ end
93
+ end
105
94
  when Net::HTTPRedirection
106
95
  redirect_uri = URI.parse(response['location'])
107
96
  redirect_http = make_http(redirect_uri)
108
97
  return get(
109
- redirect_uri.path, {
110
- http: redirect_http,
111
- use_auth: use_auth,
112
- limit: limit - 1
113
- }
98
+ redirect_uri.path,
99
+ http: redirect_http,
100
+ use_auth: use_auth,
101
+ limit: limit - 1
114
102
  )
115
103
  end
116
104
 
@@ -0,0 +1,12 @@
1
+ module Docker
2
+ module Remote
3
+ class Credentials
4
+ attr_reader :username, :password
5
+
6
+ def initialize(username, password)
7
+ @username = username
8
+ @password = password
9
+ end
10
+ end
11
+ end
12
+ end
@@ -1,5 +1,5 @@
1
1
  module Docker
2
2
  module Remote
3
- VERSION = '0.6.0'
3
+ VERSION = '0.7.0'
4
4
  end
5
5
  end
data/lib/docker/remote.rb CHANGED
@@ -8,10 +8,12 @@ module Docker
8
8
 
9
9
  class UnsupportedAuthTypeError < StandardError; end
10
10
 
11
- autoload :BasicAuth, 'docker/remote/basic_auth'
12
- autoload :BearerAuth, 'docker/remote/bearer_auth'
13
- autoload :Client, 'docker/remote/client'
14
- autoload :NoAuth, 'docker/remote/no_auth'
15
- autoload :Utils, 'docker/remote/utils'
11
+ autoload :AuthInfo, 'docker/remote/auth_info'
12
+ autoload :BasicAuth, 'docker/remote/basic_auth'
13
+ autoload :BearerAuth, 'docker/remote/bearer_auth'
14
+ autoload :Client, 'docker/remote/client'
15
+ autoload :Credentials, 'docker/remote/credentials'
16
+ autoload :NoAuth, 'docker/remote/no_auth'
17
+ autoload :Utils, 'docker/remote/utils'
16
18
  end
17
19
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: docker-remote
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cameron Dutro
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-06 00:00:00.000000000 Z
11
+ date: 2022-02-11 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A Ruby client for communicating with the Docker registry API v2.
14
14
  email:
@@ -23,9 +23,11 @@ files:
23
23
  - Rakefile
24
24
  - docker-remote.gemspec
25
25
  - lib/docker/remote.rb
26
+ - lib/docker/remote/auth_info.rb
26
27
  - lib/docker/remote/basic_auth.rb
27
28
  - lib/docker/remote/bearer_auth.rb
28
29
  - lib/docker/remote/client.rb
30
+ - lib/docker/remote/credentials.rb
29
31
  - lib/docker/remote/no_auth.rb
30
32
  - lib/docker/remote/utils.rb
31
33
  - lib/docker/remote/version.rb
@@ -47,7 +49,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
47
49
  - !ruby/object:Gem::Version
48
50
  version: '0'
49
51
  requirements: []
50
- rubygems_version: 3.1.4
52
+ rubygems_version: 3.2.22
51
53
  signing_key:
52
54
  specification_version: 4
53
55
  summary: A Ruby client for communicating with the Docker registry API v2.