Skip to content
This repository was archived by the owner on Apr 17, 2023. It is now read-only.

Commit ea4751e

Browse files
committed
Merge pull request #282 from SUSE/issue-276
Support auth protocol introduced by docker 1.8
2 parents 2d4e89e + b717fb9 commit ea4751e

File tree

3 files changed

+60
-2
lines changed

3 files changed

+60
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ PR [#212](https://github.com/SUSE/Portus/pull/212).
2121
- Added the appliance tests. See PR [#208](https://github.com/SUSE/Portus/pull/208).
2222
- Star/Unstar repositories. See PR [#230](https://github.com/SUSE/Portus/pull/230).
2323
- Now users can be disabled. See PR [#240](https://github.com/SUSE/Portus/pull/240).
24+
- Fixed the authentication process for Docker 1.8. See PR
25+
[#282](https://github.com/SUSE/Portus/pull/282).
2426
- And some minor fixes here and there.
2527

2628
## 1.0.1

app/controllers/api/v2/tokens_controller.rb

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,36 @@ def show
3030
def authorize_scopes(registry)
3131
return unless params[:scope]
3232

33+
# First try to fetch the requested scopes and the handler. If no scopes
34+
# were successfully given, respond with a 401.
3335
auth_scope, scopes = scope_handler(registry, params[:scope])
3436
raise Pundit::NotAuthorizedError, "No scopes to handle" if scopes.empty?
3537

3638
scopes.each do |scope|
39+
# It will try to check if the current user is authorized to access the
40+
# scope given in this iteration. If everything is fine, then nothing will
41+
# happen, otherwise there are two possible exceptions that can be raised:
42+
#
43+
# - NoMethodError: the targeted resource does not handle the scope that
44+
# is being checked. It will raise a ScopeNotHandled.
45+
# - Pundit::NotAuthorizedError: the targeted resource unauthorized the
46+
# given user for the scope that is being checked. In this case this
47+
# scope gets removed from `auth_scope.actions`.
3748
begin
3849
authorize auth_scope.resource, "#{scope}?".to_sym
3950
rescue NoMethodError
4051
logger.warn "Cannot handle scope #{scope}"
4152
raise ScopeNotHandled, "Cannot handle scope #{scope}"
53+
rescue Pundit::NotAuthorizedError
54+
logger.debug "scope #{scope} not authorized, removing from actions"
55+
auth_scope.actions.delete_if { |a| a == scope }
4256
end
4357
end
4458

59+
# If auth_scope.actions is empty, it means that the previous loop
60+
# unauthorized all the requested scopes for the current user. Therefore
61+
# respond with a 401. Otherwise, return the resulting auth_scope.
62+
raise Pundit::NotAuthorizedError if auth_scope.actions.empty?
4563
auth_scope
4664
end
4765

@@ -61,6 +79,6 @@ def scope_handler(registry, scope_string)
6179
raise ScopeNotHandled
6280
end
6381

64-
[auth_scope, auth_scope.scopes]
82+
[auth_scope, auth_scope.scopes.dup]
6583
end
6684
end

spec/api/v2/token_spec.rb

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,26 @@
5959
end
6060
end
6161

62+
context "as another user" do
63+
let(:another) { create(:user, password: password) }
64+
65+
it "does not allow to pull a private namespace from another team" do
66+
# It works for the regular user
67+
get v2_token_url,
68+
{ service: registry.hostname, account: user.username, scope: "repository:#{user.username}/busybox:push,pull" },
69+
"HTTP_AUTHORIZATION" => auth_mech.encode_credentials(user.username, password)
70+
71+
expect(response.status).to eq 200
72+
73+
# But not for another
74+
get v2_token_url,
75+
{ service: registry.hostname, account: another.username, scope: "repository:#{user.username}/busybox:push,pull" },
76+
"HTTP_AUTHORIZATION" => auth_mech.encode_credentials(another.username, password)
77+
78+
expect(response.status).to eq 401
79+
end
80+
end
81+
6282
context "as valid user" do
6383
let(:valid_request) do
6484
{
@@ -119,7 +139,7 @@
119139
end
120140
end
121141

122-
context "reposity scope" do
142+
context "repository scope" do
123143
it "delegates authentication to the Namespace policy" do
124144
personal_namespace = Namespace.find_by(name: user.username)
125145
expect_any_instance_of(Api::V2::TokensController).to receive(:authorize)
@@ -135,6 +155,24 @@
135155
},
136156
valid_auth_header
137157
end
158+
159+
it "allows to pull an image in which this user is just a viewer" do
160+
# Quick way to force a "viewer" policy.
161+
allow_any_instance_of(NamespacePolicy).to receive(:push?).and_return(false)
162+
allow_any_instance_of(NamespacePolicy).to receive(:pull?).and_return(true)
163+
164+
get v2_token_url,
165+
{ service: registry.hostname, account: user.username, scope: "repository:#{user.username}/busybox:push,pull" },
166+
valid_auth_header
167+
168+
expect(response.status).to eq 200
169+
170+
# And check that the only authorized scope is "pull"
171+
token = JSON.parse(response.body)["token"]
172+
payload = JWT.decode(token, nil, false, leeway: 2)[0]
173+
expect(payload["access"][0]["name"]).to eq "#{user.username}/busybox"
174+
expect(payload["access"][0]["actions"]).to match_array ["pull"]
175+
end
138176
end
139177

140178
context "registry scope" do

0 commit comments

Comments
 (0)