gitlab-org--gitlab-foss/app/models/project_feature.rb
Stan Hu fea80aa12d Fix project deletion when feature visibility is set to private
Projects that are destroyed are put in the pending_delete state.
The ProjectDestroyWorker checks whether the current user has
access, but since the ProjectFeature class uses the default scope
of the Project, it will not be able to find the right project.

This was a regression in 8.12 that caused the following stack trace:

```
NoMethodError: undefined method `team' for nil:NilClass
  from app/models/project_feature.rb:62:in `get_permission'
  from app/models/project_feature.rb:34:in `feature_available?'
  from app/models/project.rb:21:in `feature_available?'
  from app/policies/project_policy.rb:170:in `disabled_features!'
  from app/policies/project_policy.rb:29:in `rules'
  from app/policies/base_policy.rb:82:in `block in abilities'
  from app/policies/base_policy.rb:113:in `collect_rules'
  from app/policies/base_policy.rb:82:in `abilities'
  from app/policies/base_policy.rb:50:in `abilities'
  from app/models/ability.rb:64:in `uncached_allowed'
  from app/models/ability.rb:58:in `allowed'
  from app/models/ability.rb:49:in `allowed?'
  from app/services/base_service.rb:11:in `can?'
  from lib/gitlab/metrics/instrumentation.rb:155:in `block in can?'
  from lib/gitlab/metrics/method_call.rb:23:in `measure'
  from lib/gitlab/metrics/instrumentation.rb:155:in `can?'
  from app/services/projects/destroy_service.rb:18:in `execute'
```

Closes #22948
2016-10-04 20:53:15 -07:00

72 lines
2 KiB
Ruby

class ProjectFeature < ActiveRecord::Base
# == Project features permissions
#
# Grants access level to project tools
#
# Tools can be enabled only for users, everyone or disabled
# Access control is made only for non private projects
#
# levels:
#
# Disabled: not enabled for anyone
# Private: enabled only for team members
# Enabled: enabled for everyone able to access the project
#
# Permision levels
DISABLED = 0
PRIVATE = 10
ENABLED = 20
FEATURES = %i(issues merge_requests wiki snippets builds)
# Default scopes force us to unscope here since a service may need to check
# permissions for a project in pending_delete
# http://stackoverflow.com/questions/1540645/how-to-disable-default-scope-for-a-belongs-to
belongs_to :project, -> { unscope(where: :pending_delete) }
default_value_for :builds_access_level, value: ENABLED, allows_nil: false
default_value_for :issues_access_level, value: ENABLED, allows_nil: false
default_value_for :merge_requests_access_level, value: ENABLED, allows_nil: false
default_value_for :snippets_access_level, value: ENABLED, allows_nil: false
default_value_for :wiki_access_level, value: ENABLED, allows_nil: false
def feature_available?(feature, user)
raise ArgumentError, 'invalid project feature' unless FEATURES.include?(feature)
get_permission(user, public_send("#{feature}_access_level"))
end
def builds_enabled?
return true unless builds_access_level
builds_access_level > DISABLED
end
def wiki_enabled?
return true unless wiki_access_level
wiki_access_level > DISABLED
end
def merge_requests_enabled?
return true unless merge_requests_access_level
merge_requests_access_level > DISABLED
end
private
def get_permission(user, level)
case level
when DISABLED
false
when PRIVATE
user && (project.team.member?(user) || user.admin?)
when ENABLED
true
else
true
end
end
end