Do not blindly expose public project statistics

Add the missing check on GraphQL API for project statistics
This commit is contained in:
Mayra Cabrera 2019-06-14 20:40:21 +00:00 committed by Stan Hu
parent ad722a4e1f
commit d7f10c2949
5 changed files with 93 additions and 3 deletions

View file

@ -4,6 +4,8 @@ module Types
class ProjectStatisticsType < BaseObject class ProjectStatisticsType < BaseObject
graphql_name 'ProjectStatistics' graphql_name 'ProjectStatistics'
authorize :read_statistics
field :commit_count, GraphQL::INT_TYPE, null: false field :commit_count, GraphQL::INT_TYPE, null: false
field :storage_size, GraphQL::INT_TYPE, null: false field :storage_size, GraphQL::INT_TYPE, null: false

View file

@ -70,7 +70,7 @@ module Types
field :group, Types::GroupType, null: true field :group, Types::GroupType, null: true
field :statistics, Types::ProjectStatisticsType, field :statistics, Types::ProjectStatisticsType,
null: false, null: true,
resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchProjectStatisticsLoader.new(obj.id).find } resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchProjectStatisticsLoader.new(obj.id).find }
field :repository, Types::RepositoryType, null: false field :repository, Types::RepositoryType, null: false

View file

@ -0,0 +1,5 @@
# frozen_string_literal: true
class ProjectStatisticsPolicy < BasePolicy
delegate { @subject.project }
end

View file

@ -0,0 +1,83 @@
# frozen_string_literal: true
require 'spec_helper'
describe ProjectStatisticsPolicy do
using RSpec::Parameterized::TableSyntax
describe '#rules' do
let(:external) { create(:user, :external) }
let(:guest) { create(:user) }
let(:reporter) { create(:user) }
let(:developer) { create(:user) }
let(:maintainer) { create(:user) }
let(:users) do
{
unauthenticated: nil,
non_member: create(:user),
guest: guest,
reporter: reporter,
developer: developer,
maintainer: maintainer
}
end
where(:project_type, :user_type, :outcome) do
[
# Public projects
[:public, :unauthenticated, false],
[:public, :non_member, false],
[:public, :guest, false],
[:public, :reporter, true],
[:public, :developer, true],
[:public, :maintainer, true],
# Private project
[:private, :unauthenticated, false],
[:private, :non_member, false],
[:private, :guest, false],
[:private, :reporter, true],
[:private, :developer, true],
[:private, :maintainer, true],
# Internal projects
[:internal, :unauthenticated, false],
[:internal, :non_member, false],
[:internal, :guest, false],
[:internal, :reporter, true],
[:internal, :developer, true],
[:internal, :maintainer, true]
]
end
with_them do
let(:user) { users[user_type] }
let(:project) { create(:project, visibility_level: Gitlab::VisibilityLevel.level_value(project_type.to_s)) }
let(:project_statistics) { create(:project_statistics, project: project) }
subject { Ability.allowed?(user, :read_statistics, project_statistics) }
before do
project.add_guest(guest)
project.add_reporter(reporter)
project.add_developer(developer)
project.add_maintainer(maintainer)
end
it { is_expected.to eq(outcome) }
context 'when the user is external' do
let(:user) { external }
before do
unless [:unauthenticated, :non_member].include?(user_type)
project.add_user(external, user_type)
end
end
it { is_expected.to eq(outcome) }
end
end
end
end

View file

@ -34,10 +34,10 @@ describe 'rendering namespace statistics' do
context 'when the project is public' do context 'when the project is public' do
let(:project) { create(:project, :public) } let(:project) { create(:project, :public) }
it 'includes the statistics regardless of the user' do it 'hides statistics for unauthenticated requests' do
post_graphql(query, current_user: nil) post_graphql(query, current_user: nil)
expect(graphql_data['project']['statistics']).to be_present expect(graphql_data['project']['statistics']).to be_blank
end end
end end
end end