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

Commit

Permalink
api/v2: provide tests for the regressions after docker 1.8
Browse files Browse the repository at this point in the history
Moreover, I've also added more documentation in the `authorize_scopes` method.
Finally, the `authorize_scopes` method will raise a `Pundit::NoAuthorizedError`
exception if no scopes can be authorized for the current user.

Signed-off-by: Miquel Sabaté Solà <msabate@suse.com>
  • Loading branch information
mssola committed Aug 27, 2015
1 parent 59d2ee6 commit 9f31f29
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 1 deletion.
15 changes: 15 additions & 0 deletions app/controllers/api/v2/tokens_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,21 @@ def show
def authorize_scopes(registry)
return unless params[:scope]

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

scopes.each do |scope|
# It will try to check if the current user is authorized to access the
# scope given in this iteration. If everything is fine, then nothing will
# happen, otherwise there are two possible exceptions that can be raised:
#
# - NoMethodError: the targeted resource does not handle the scope that
# is being checked. It will raise a ScopeNotHandled.
# - Pundit::NotAuthorizedError: the targeted resource unauthorized the
# given user for the scope that is being checked. In this case this
# scope gets removed from `auth_scope.actions`.
begin
authorize auth_scope.resource, "#{scope}?".to_sym
rescue NoMethodError
Expand All @@ -45,6 +56,10 @@ def authorize_scopes(registry)
end
end

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

Expand Down
40 changes: 39 additions & 1 deletion spec/api/v2/token_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,26 @@
end
end

context "as another user" do
let(:another) { create(:user, password: password) }

it "does not allow to pull a private namespace from another team" do
# It works for the regular user
get v2_token_url,
{ service: registry.hostname, account: user.username, scope: "repository:#{user.username}/busybox:push,pull" },
"HTTP_AUTHORIZATION" => auth_mech.encode_credentials(user.username, password)

expect(response.status).to eq 200

# But not for another
get v2_token_url,
{ service: registry.hostname, account: another.username, scope: "repository:#{user.username}/busybox:push,pull" },
"HTTP_AUTHORIZATION" => auth_mech.encode_credentials(another.username, password)

expect(response.status).to eq 401
end
end

context "as valid user" do
let(:valid_request) do
{
Expand Down Expand Up @@ -110,7 +130,7 @@
end
end

context "reposity scope" do
context "repository scope" do
it "delegates authentication to the Namespace policy" do
personal_namespace = Namespace.find_by(name: user.username)
expect_any_instance_of(Api::V2::TokensController).to receive(:authorize)
Expand All @@ -122,6 +142,24 @@
{ service: registry.hostname, account: user.username, scope: "repository:#{user.username}/busybox:push,pull" },
valid_auth_header
end

it "allows to pull an image in which this user is just a viewer" do
# Quick way to force a "viewer" policy.
allow_any_instance_of(NamespacePolicy).to receive(:push?).and_return(false)
allow_any_instance_of(NamespacePolicy).to receive(:pull?).and_return(true)

get v2_token_url,
{ service: registry.hostname, account: user.username, scope: "repository:#{user.username}/busybox:push,pull" },
valid_auth_header

expect(response.status).to eq 200

# And check that the only authorized scope is "pull"
token = JSON.parse(response.body)["token"]
payload = JWT.decode(token, nil, false, leeway: 2)[0]
expect(payload["access"][0]["name"]).to eq "#{user.username}/busybox"
expect(payload["access"][0]["actions"]).to match_array ["pull"]
end
end

context "registry scope" do
Expand Down

0 comments on commit 9f31f29

Please sign in to comment.