Do not blindly expose public project statistics
Add the missing check on GraphQL API for project statistics
This commit is contained in:
parent
ad722a4e1f
commit
d7f10c2949
5 changed files with 93 additions and 3 deletions
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
5
app/policies/project_statistics_policy.rb
Normal file
5
app/policies/project_statistics_policy.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class ProjectStatisticsPolicy < BasePolicy
|
||||||
|
delegate { @subject.project }
|
||||||
|
end
|
83
spec/policies/project_statistics_policy_spec.rb
Normal file
83
spec/policies/project_statistics_policy_spec.rb
Normal 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
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue