From 4fec2213c889e6bc0673925f0bd2be62c4df14dc Mon Sep 17 00:00:00 2001 From: Vokhmin Alexey V Date: Fri, 13 Mar 2015 01:43:13 +0300 Subject: [PATCH] #465: added pundit gem, wip... --- Gemfile | 3 +- Gemfile.lock | 11 +- app/controllers/api/v1/base_controller.rb | 2 +- app/controllers/api/v1/projects_controller.rb | 2 +- .../api/v1/repositories_controller.rb | 2 +- app/controllers/application_controller.rb | 16 ++- app/controllers/concerns/strong_params.rb | 9 ++ app/controllers/groups/members_controller.rb | 2 +- .../platforms/platforms_controller.rb | 5 +- .../projects/build_lists_controller.rb | 2 +- .../projects/git/base_controller.rb | 2 +- .../projects/pull_requests_controller.rb | 2 +- app/models/build_script.rb | 2 +- app/models/concerns/flash_notify/finders.rb | 33 +++++ app/models/concerns/platform/finders.rb | 50 ++++++++ app/models/concerns/project/finders.rb | 82 ++++++++++++ app/models/flash_notify.rb | 4 +- app/models/issue.rb | 2 +- app/models/platform.rb | 11 +- app/models/project.rb | 45 +------ app/policies/advisory_policy.rb | 11 ++ app/policies/application_policy.rb | 117 ++++++++++++++++++ app/policies/build_list_policy.rb | 3 + app/policies/group_policy.rb | 3 + app/policies/platform_policy.rb | 62 ++++++++++ app/policies/product_policy.rb | 3 + app/policies/project_policy.rb | 3 + app/policies/statistic_policy.rb | 3 + app/policies/user_policy.rb | 6 + app/views/home/activity.json.jbuilder | 2 +- app/views/layouts/_notifies.html.haml | 2 +- app/views/layouts/application.html.slim | 2 +- app/views/layouts/menu/_bottom.html.haml | 2 +- app/views/layouts/menu/_new_bottom.html.haml | 2 +- app/views/layouts/menu/_new_top.html.slim | 2 +- app/views/platforms/base/_submenu.html.slim | 12 +- app/views/platforms/platforms/index.html.slim | 4 +- app/views/platforms/platforms/show.html.slim | 2 +- 38 files changed, 435 insertions(+), 93 deletions(-) create mode 100644 app/controllers/concerns/strong_params.rb create mode 100644 app/models/concerns/flash_notify/finders.rb create mode 100644 app/models/concerns/platform/finders.rb create mode 100644 app/models/concerns/project/finders.rb create mode 100644 app/policies/advisory_policy.rb create mode 100644 app/policies/application_policy.rb create mode 100644 app/policies/build_list_policy.rb create mode 100644 app/policies/group_policy.rb create mode 100644 app/policies/platform_policy.rb create mode 100644 app/policies/product_policy.rb create mode 100644 app/policies/project_policy.rb create mode 100644 app/policies/statistic_policy.rb create mode 100644 app/policies/user_policy.rb diff --git a/Gemfile b/Gemfile index 6a2e3a54f..24640164f 100644 --- a/Gemfile +++ b/Gemfile @@ -14,8 +14,7 @@ gem 'omniauth-facebook' gem 'omniauth-google-oauth2' gem 'omniauth-github' # gem 'omniauth-openid', '~> 1.0.1' -# gem 'cancan', '1.6.10' -gem 'cancan', git: 'git://github.com/rosa-abf/cancan.git', tag: '1.6.10-abf' +gem 'pundit' gem 'ancestry' gem 'paperclip' diff --git a/Gemfile.lock b/Gemfile.lock index f62782e8b..4dbc5f2cf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -26,13 +26,6 @@ GIT ransack (~> 1.3) sass-rails -GIT - remote: git://github.com/rosa-abf/cancan.git - revision: fe1089b70c08d3ed11bac4f8e69ecb3d1d9adc29 - tag: 1.6.10-abf - specs: - cancan (1.6.10) - GIT remote: git://github.com/rosa-abf/grack.git revision: 020be3fef3fb308b9d214252522aa5945bf6584a @@ -349,6 +342,8 @@ GEM activemodel (>= 4.0.1, < 5.0) puma (2.11.1) rack (>= 1.1, < 2.0) + pundit (0.3.0) + activesupport (>= 3.0.0) pygments.rb (0.6.2) posix-spawn (~> 0.3.6) yajl-ruby (~> 1.2.0) @@ -590,7 +585,6 @@ DEPENDENCIES better_errors binding_of_caller bootstrap-sass - cancan! cape capistrano capistrano_colors @@ -637,6 +631,7 @@ DEPENDENCIES pg protected_attributes puma + pundit rack-throttle (~> 0.3.0) rack-utf8_sanitizer rails (= 4.1.9) diff --git a/app/controllers/api/v1/base_controller.rb b/app/controllers/api/v1/base_controller.rb index 739803f4f..f614e2ead 100644 --- a/app/controllers/api/v1/base_controller.rb +++ b/app/controllers/api/v1/base_controller.rb @@ -4,7 +4,7 @@ class Api::V1::BaseController < ApplicationController helper_method :member_path - rescue_from CanCan::AccessDenied do |exception| + rescue_from Pundit::NotAuthorizedError do |exception| respond_to do |format| format.json { render json: {message: t('flash.exception_message')}.to_json, status: 403 } format.csv { render text: t('flash.exception_message'), status: 403 } diff --git a/app/controllers/api/v1/projects_controller.rb b/app/controllers/api/v1/projects_controller.rb index 541121b9d..4f268cc66 100644 --- a/app/controllers/api/v1/projects_controller.rb +++ b/app/controllers/api/v1/projects_controller.rb @@ -12,7 +12,7 @@ class Api::V1::ProjectsController < Api::V1::BaseController end def get_id - if @project = Project.find_by_owner_and_name(params[:owner], params[:name]) + if @project = Project.find_by_owner_and_name_cached(params[:owner], params[:name]) authorize! :show, @project else raise ActiveRecord::RecordNotFound diff --git a/app/controllers/api/v1/repositories_controller.rb b/app/controllers/api/v1/repositories_controller.rb index 389b958d9..7f9fa2ed7 100644 --- a/app/controllers/api/v1/repositories_controller.rb +++ b/app/controllers/api/v1/repositories_controller.rb @@ -40,7 +40,7 @@ class Api::V1::RepositoriesController < Api::V1::BaseController key, now = [@repository.platform.id, :repository_packages], Time.zone.now last_request = Rails.cache.read(key) if last_request.present? && last_request + 15.minutes > now - raise CanCan::AccessDenied + raise Pundit::NotAuthorizedError else Rails.cache.write(key, now, expires_at: 15.minutes) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index c30360a5f..83988d218 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,4 +1,7 @@ class ApplicationController < ActionController::Base + include StrongParams + include Pundit + AIRBRAKE_IGNORE = [ ActionController::InvalidAuthenticityToken, AbstractController::ActionNotFound @@ -14,6 +17,7 @@ class ApplicationController < ActionController::Base before_action :set_locale before_action -> { EventLog.current_controller = self }, only: [:create, :destroy, :open_id, :cancel, :publish, :change_visibility] # :update + before_action :banned? after_action -> { EventLog.current_controller = nil } helper_method :get_owner @@ -27,7 +31,7 @@ class ApplicationController < ActionController::Base AbstractController::ActionNotFound, with: :render_404 end - rescue_from CanCan::AccessDenied do |exception| + rescue_from Pundit::NotAuthorizedError do |exception| redirect_to forbidden_url, alert: t("flash.exception_message") end @@ -40,6 +44,16 @@ class ApplicationController < ActionController::Base protected + # Disables access to site for banned users + def banned? + authorize :user, :banned? + # if user_signed_in? && current_user.is_banned? + # sign_out current_user + # flash[:error] = I18n.t('messages.account_suspended') + # redirect_to root_path + # end + end + # For this example, we are simply using token authentication # via parameters. However, anyone could use Rails's token # authentication features to get the token from a header. diff --git a/app/controllers/concerns/strong_params.rb b/app/controllers/concerns/strong_params.rb new file mode 100644 index 000000000..0e798ded1 --- /dev/null +++ b/app/controllers/concerns/strong_params.rb @@ -0,0 +1,9 @@ +module StrongParams + extend ActiveSupport::Concern + + protected + + def permit_params(param_name, *accessible) + (params[param_name] || ActionController::Parameters.new).permit(*accessible.flatten) + end +end diff --git a/app/controllers/groups/members_controller.rb b/app/controllers/groups/members_controller.rb index e70119a9c..4dc0c5d0f 100644 --- a/app/controllers/groups/members_controller.rb +++ b/app/controllers/groups/members_controller.rb @@ -6,7 +6,7 @@ class Groups::MembersController < Groups::BaseController end def update - raise CanCan::AccessDenied if @group.owner_id.to_s == params[:member_id] + raise Pundit::NotAuthorizedError if @group.owner_id.to_s == params[:member_id] relation = @group.actors.where(actor_id: params[:member_id], actor_type: 'User').first relation ||= @group.actors.build(actor_id: params[:member_id], actor_type: 'User') diff --git a/app/controllers/platforms/platforms_controller.rb b/app/controllers/platforms/platforms_controller.rb index 8762c11c2..fc940d055 100644 --- a/app/controllers/platforms/platforms_controller.rb +++ b/app/controllers/platforms/platforms_controller.rb @@ -3,14 +3,14 @@ class Platforms::PlatformsController < Platforms::BaseController before_action :authenticate_user! skip_before_action :authenticate_user!, only: [:advisories, :members, :show] if APP_CONFIG['anonymous_access'] - load_and_authorize_resource + # load_and_authorize_resource def index respond_to do |format| format.html {} format.json { - @platforms = @platforms.accessible_by(current_ability, :related) + @platforms = PlatformPolicy::Scope.new(current_user, Platform).related @platforms_count = @platforms.count @platforms = @platforms.paginate(page: current_page, per_page: Platform.per_page) } @@ -18,6 +18,7 @@ class Platforms::PlatformsController < Platforms::BaseController end def show + authorize @platform = Platform.find_cached(params[:id]) end def new diff --git a/app/controllers/projects/build_lists_controller.rb b/app/controllers/projects/build_lists_controller.rb index 983ef4d14..7637c3109 100644 --- a/app/controllers/projects/build_lists_controller.rb +++ b/app/controllers/projects/build_lists_controller.rb @@ -125,7 +125,7 @@ class Projects::BuildListsController < Projects::BaseController end def dependent_projects - raise CanCan::AccessDenied if @build_list.save_to_platform.personal? + raise Pundit::NotAuthorizedError if @build_list.save_to_platform.personal? if request.post? prs = params[:build_list] diff --git a/app/controllers/projects/git/base_controller.rb b/app/controllers/projects/git/base_controller.rb index a52145470..f338a8430 100644 --- a/app/controllers/projects/git/base_controller.rb +++ b/app/controllers/projects/git/base_controller.rb @@ -5,7 +5,7 @@ class Projects::Git::BaseController < Projects::BaseController before_action :authenticate_user, only: %i(show index blame raw archive diff tags branches) end - load_and_authorize_resource :project + # load_and_authorize_resource :project before_action :set_treeish_and_path before_action :set_branch_and_tree diff --git a/app/controllers/projects/pull_requests_controller.rb b/app/controllers/projects/pull_requests_controller.rb index dd71e5ddc..7a0e9239f 100644 --- a/app/controllers/projects/pull_requests_controller.rb +++ b/app/controllers/projects/pull_requests_controller.rb @@ -136,7 +136,7 @@ class Projects::PullRequestsController < Projects::BaseController end def find_destination_project bang=true - project = Project.find_by_owner_and_name params[:to_project] + project = Project.find_by_owner_and_name_cached params[:to_project] raise ActiveRecord::RecordNotFound if bang && !project project || @project.pull_requests.last.try(:to_project) || @project.root end diff --git a/app/models/build_script.rb b/app/models/build_script.rb index 1c5a395d3..51c712847 100644 --- a/app/models/build_script.rb +++ b/app/models/build_script.rb @@ -57,7 +57,7 @@ class BuildScript < ActiveRecord::Base def attach_project if @project_name.present? - self.project = Project.find_by_owner_and_name(@project_name) + self.project = Project.find_by_owner_and_name_cached(@project_name) end end diff --git a/app/models/concerns/flash_notify/finders.rb b/app/models/concerns/flash_notify/finders.rb new file mode 100644 index 000000000..14f6e5bd7 --- /dev/null +++ b/app/models/concerns/flash_notify/finders.rb @@ -0,0 +1,33 @@ +# Private: Finders of all sorts: methods to find FlashNotify records, methods to find +# other records which belong to given FlashNotify. +# +# This module gets included into FlashNotify. +module FlashNotify::Finders + extend ActiveSupport::Concern + + included do + scope :published, -> { where(published: true) } + + after_commit :clear_caches + after_touch :clear_caches + end + + module ClassMethods + + # Public: Get cached first published FlashNotify record. + # + # Returns FlashNotify record or nil. + def published_first_cached + Rails.cache.fetch('FlashNotify.published.first') do + published.first + end + end + end + + protected + + # Private: after_commit and after_touch hook which clears find_cached cache. + def clear_caches + Rails.cache.delete('FlashNotify.published.first') + end +end diff --git a/app/models/concerns/platform/finders.rb b/app/models/concerns/platform/finders.rb new file mode 100644 index 000000000..be1a7c116 --- /dev/null +++ b/app/models/concerns/platform/finders.rb @@ -0,0 +1,50 @@ +# Private: Finders of all sorts: methods to find Platform records, methods to find +# other records which belong to given Platform. +# +# This module gets included into Platform. +module Platform::Finders + extend ActiveSupport::Concern + + included do + + scope :search_order, -> { order(:name) } + scope :search, -> (q) { where("#{table_name}.name ILIKE ?", "%#{q.to_s.strip}%") } + scope :by_visibilities, -> (v) { where(visibility: v) } + scope :opened, -> { where(visibility: Platform::VISIBILITY_OPEN) } + scope :hidden, -> { where(visibility: Platform::VISIBILITY_HIDDEN) } + scope :by_type, -> (type) { where(platform_type: type) if type.present? } + scope :main, -> { by_type(Platform::TYPE_MAIN) } + scope :personal, -> { by_type(Platform::TYPE_PERSONAL) } + scope :waiting_for_regeneration, -> { where(status: Platform::WAITING_FOR_REGENERATION) } + + after_commit :clear_caches + after_touch :clear_caches + end + + module ClassMethods + + # Public: Get cached Platform record by ID or slug. + # + # platform_id - ID or Slug (Numeric/String) + # + # Returns Platform record. + # Raises ActiveRecord::RecordNotFound if nothing was found. + def find_cached(platform_id) + Rails.cache.fetch(['Platform.find', platform_id]) do + find(platform_id) + end + end + end + + protected + + # Private: after_commit and after_touch hook which clears find_cached cache. + def clear_caches + Rails.cache.delete(['Platform.find', id]) + Rails.cache.delete(['Platform.find', slug]) + + if chg = previous_changes["slug"] + Rails.cache.delete(['Platform.find', chg.first]) + end + end +end diff --git a/app/models/concerns/project/finders.rb b/app/models/concerns/project/finders.rb new file mode 100644 index 000000000..2ac417522 --- /dev/null +++ b/app/models/concerns/project/finders.rb @@ -0,0 +1,82 @@ +# Private: Finders of all sorts: methods to find Project records, methods to find +# other records which belong to given Project. +# +# This module gets included into Project. +module Project::Finders + extend ActiveSupport::Concern + + included do + + scope :recent, -> { order(:name) } + scope :search_order, -> { order('CHAR_LENGTH(projects.name) ASC') } + scope :search, ->(q) { + q = q.to_s.strip + by_name("%#{q}%").search_order if q.present? + } + scope :by_name, ->(name) { where('projects.name ILIKE ?', name) if name.present? } + scope :by_owner, ->(name) { where('projects.owner_uname ILIKE ?', "%#{name}%") if name.present? } + scope :by_owner_and_name, ->(*params) { + term = params.map(&:strip).join('/').downcase + where("lower(concat(owner_uname, '/', name)) ILIKE ?", "%#{term}%") if term.present? + } + scope :by_visibilities, ->(v) { where(visibility: v) } + scope :opened, -> { where(visibility: 'open') } + scope :package, -> { where(is_package: true) } + scope :addable_to_repository, ->(repository_id) { + where('projects.id NOT IN ( + SELECT ptr.project_id + FROM project_to_repositories AS ptr + WHERE ptr.repository_id = ?)', repository_id) + } + scope :by_owners, ->(group_owner_ids, user_owner_ids) { + where("(projects.owner_id in (?) AND projects.owner_type = 'Group') OR + (projects.owner_id in (?) AND projects.owner_type = 'User')", group_owner_ids, user_owner_ids) + } + + scope :project_aliases, ->(project) { + where.not(id: project.id). + where('alias_from_id IN (:ids) OR id IN (:ids)', { ids: [project.alias_from_id, project.id].compact }) + } + + after_commit :clear_caches + after_touch :clear_caches + end + + module ClassMethods + + # Public: Get cached Project record by ID or slug. + # + # Returns Project record. + # Raises ActiveRecord::RecordNotFound if nothing was found. + def find_by_owner_and_name_cached(first, last = nil) + Rails.cache.fetch(['Project.find_by_owner_and_name', first, last]) do + find_by_owner_and_name(first, last) + end + end + + def find_by_owner_and_name(first, last = nil) + arr = first.try(:split, '/') || [] + arr = (arr << last).compact + return nil if arr.length != 2 + where(owner_uname: arr.first, name: arr.last).first || by_owner_and_name(*arr).first + end + + def find_by_owner_and_name!(first, last = nil) + find_by_owner_and_name_cached(first, last) or raise ActiveRecord::RecordNotFound + end + end + + protected + + # Private: after_commit and after_touch hook which clears find_cached cache. + def clear_caches + Rails.cache.delete(['Project.find_by_owner_and_name', owner_uname, name]) + Rails.cache.delete(['Project.find_by_owner_and_name', name_with_owner]) + Rails.cache.delete(['Project.find', id]) + Rails.cache.delete(['Project.find', slug]) + + if chg = previous_changes["slug"] + Rails.cache.delete(['Project.find', chg.first]) + end + end +end diff --git a/app/models/flash_notify.rb b/app/models/flash_notify.rb index cfb569297..521ec9556 100644 --- a/app/models/flash_notify.rb +++ b/app/models/flash_notify.rb @@ -1,15 +1,13 @@ require 'digest/md5' class FlashNotify < ActiveRecord::Base - # attr_accessible :title, :body + include FlashNotify::Finders STATUSES = %w[error success info] validates :status, inclusion: {in: STATUSES} validates :body_ru, :body_en, :status, presence: true - scope :published, -> { where(published: true) } - attr_accessible :body_ru, :body_en, :status, :published def hash_id diff --git a/app/models/issue.rb b/app/models/issue.rb index cbae81eda..70c483aa8 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -109,7 +109,7 @@ class Issue < ActiveRecord::Base owner_uname = Regexp.last_match[1].presence || Regexp.last_match[2].presence || project.owner.uname project_name = Regexp.last_match[1] ? Regexp.last_match[2] : project.name serial_id = Regexp.last_match[3] - project = Project.find_by_owner_and_name(owner_uname.chomp('/'), project_name) + project = Project.find_by_owner_and_name_cached(owner_uname.chomp('/'), project_name) return nil unless project return nil unless current_ability.can? :show, project project.issues.where(serial_id: serial_id).first diff --git a/app/models/platform.rb b/app/models/platform.rb index dcddaa29f..3d143446c 100644 --- a/app/models/platform.rb +++ b/app/models/platform.rb @@ -8,6 +8,7 @@ class Platform < ActiveRecord::Base include EventLoggable include EmptyMetadata include DefaultBranchable + include Platform::Finders self.per_page = 20 @@ -96,16 +97,6 @@ class Platform < ActiveRecord::Base after_create -> { symlink_directory unless hidden? } after_destroy -> { remove_symlink_directory unless hidden? } - scope :search_order, -> { order(:name) } - scope :search, -> (q) { where("#{table_name}.name ILIKE ?", "%#{q.to_s.strip}%") } - scope :by_visibilities, -> (v) { where(visibility: v) } - scope :opened, -> { where(visibility: VISIBILITY_OPEN) } - scope :hidden, -> { where(visibility: VISIBILITY_HIDDEN) } - scope :by_type, -> (type) { where(platform_type: type) if type.present? } - scope :main, -> { by_type(TYPE_MAIN) } - scope :personal, -> { by_type(TYPE_PERSONAL) } - scope :waiting_for_regeneration, -> { where(status: WAITING_FOR_REGENERATION) } - accepts_nested_attributes_for :platform_arch_settings, allow_destroy: true attr_accessible :name, :distrib_type, diff --git a/app/models/project.rb b/app/models/project.rb index 506a455ce..40d283564 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -8,6 +8,7 @@ class Project < ActiveRecord::Base include UrlHelper include EventLoggable include Project::DefaultBranch + include Project::Finders VISIBILITIES = ['open', 'hidden'] MAX_OWN_PROJECTS = 32000 @@ -67,37 +68,6 @@ class Project < ActiveRecord::Base :autostart_status attr_readonly :owner_id, :owner_type - scope :recent, -> { order(:name) } - scope :search_order, -> { order('CHAR_LENGTH(projects.name) ASC') } - scope :search, ->(q) { - q = q.to_s.strip - by_name("%#{q}%").search_order if q.present? - } - scope :by_name, ->(name) { where('projects.name ILIKE ?', name) if name.present? } - scope :by_owner, ->(name) { where('projects.owner_uname ILIKE ?', "%#{name}%") if name.present? } - scope :by_owner_and_name, ->(*params) { - term = params.map(&:strip).join('/').downcase - where("lower(concat(owner_uname, '/', name)) ILIKE ?", "%#{term}%") if term.present? - } - scope :by_visibilities, ->(v) { where(visibility: v) } - scope :opened, -> { where(visibility: 'open') } - scope :package, -> { where(is_package: true) } - scope :addable_to_repository, ->(repository_id) { - where('projects.id NOT IN ( - SELECT ptr.project_id - FROM project_to_repositories AS ptr - WHERE ptr.repository_id = ?)', repository_id) - } - scope :by_owners, ->(group_owner_ids, user_owner_ids) { - where("(projects.owner_id in (?) AND projects.owner_type = 'Group') OR - (projects.owner_id in (?) AND projects.owner_type = 'User')", group_owner_ids, user_owner_ids) - } - - scope :project_aliases, ->(project) { - where.not(id: project.id). - where('alias_from_id IN (:ids) OR id IN (:ids)', { ids: [project.alias_from_id, project.id].compact }) - } - before_validation :truncate_name, on: :create before_save -> { self.owner_uname = owner.uname if owner_uname.blank? || owner_id_changed? || owner_type_changed? } before_create :set_maintainer @@ -106,19 +76,6 @@ class Project < ActiveRecord::Base attr_accessor :url, :srpms_list, :mass_import, :add_to_repository_id - class << self - def find_by_owner_and_name(first, last = nil) - arr = first.try(:split, '/') || [] - arr = (arr << last).compact - return nil if arr.length != 2 - where(owner_uname: arr.first, name: arr.last).first || by_owner_and_name(*arr).first - end - - def find_by_owner_and_name!(first, last = nil) - find_by_owner_and_name(first, last) or raise ActiveRecord::RecordNotFound - end - end - def init_mass_import Project.perform_later :low, :run_mass_import, url, srpms_list, visibility, owner, add_to_repository_id end diff --git a/app/policies/advisory_policy.rb b/app/policies/advisory_policy.rb new file mode 100644 index 000000000..bf9b5d700 --- /dev/null +++ b/app/policies/advisory_policy.rb @@ -0,0 +1,11 @@ +class AdvisoryPolicy < ApplicationPolicy + + def index? + true + end + + def show? + true + end + +end diff --git a/app/policies/application_policy.rb b/app/policies/application_policy.rb new file mode 100644 index 000000000..bbc1200e2 --- /dev/null +++ b/app/policies/application_policy.rb @@ -0,0 +1,117 @@ +class ApplicationPolicy + attr_reader :user, :record + + def initialize(user, record) + # raise Pundit::NotAuthorizedError, 'must be logged in' unless user + @user = user || User.new + @record = record + end + + BASIC_ACTIONS = %i(index? show? create? update? destroy? destroy_all?) + + def index? + false + end + + def show? + false + end + + def new? + create? + end + + def edit? + update? + end + + def update? + false + end + + def create? + false + end + + def destroy? + false + end + + def permitted_attributes + [] + end + + class Scope + attr_reader :user, :scope + + def initialize(user, scope) + @user = user + @scope = scope + end + + def resolve + scope + end + + # Public: Get user's group ids. + # + # Returns the Array of group ids. + def user_group_ids + Rails.cache.fetch(['ApplicationPolicy#user_group_ids', user.id]) do + user.group_ids + end + end + end + + protected + + # Public: Check if provided user is the current user. + # + # Returns true if it is, false otherwise. + def current_user?(u) + u == user + end + + # Public: Check if provided user is guest. + # + # Returns true if he is, false otherwise. + def is_guest? + user.new_record? + end + + # Public: Check if provided user is user. + # + # Returns true if he is, false otherwise. + def is_user? + user.persisted? + end + + # Public: Check if provided user is tester. + # + # Returns true if he is, false otherwise. + def is_tester? + user.role == 'tester' + end + + # Public: Check if provided user is system. + # + # Returns true if he is, false otherwise. + def is_system? + user.role == 'system' + end + + # Public: Check if provided user is admin. + # + # Returns true if he is, false otherwise. + def is_admin? + user.role == 'admin' + end + + # Public: Check if provided user is banned. + # + # Returns true if he is, false otherwise. + def is_banned? + user.role == 'banned' + end + +end \ No newline at end of file diff --git a/app/policies/build_list_policy.rb b/app/policies/build_list_policy.rb new file mode 100644 index 000000000..d7230974c --- /dev/null +++ b/app/policies/build_list_policy.rb @@ -0,0 +1,3 @@ +class BuildListPolicy < ApplicationPolicy + +end diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb new file mode 100644 index 000000000..6c35b1e29 --- /dev/null +++ b/app/policies/group_policy.rb @@ -0,0 +1,3 @@ +class GroupPolicy < ApplicationPolicy + +end diff --git a/app/policies/platform_policy.rb b/app/policies/platform_policy.rb new file mode 100644 index 000000000..82665580a --- /dev/null +++ b/app/policies/platform_policy.rb @@ -0,0 +1,62 @@ +class PlatformPolicy < ApplicationPolicy + + def index? + true + end + + def show? + return true unless record.hidden? + return true if record.owner == user + return true if owner? + end + + def create? + is_admin? + end + + def members? + owner? || local_admin? + end + + def clone? + return false if record.personal? + owner? || local_admin? + end + + class Scope < Scope + + def related + scope.where <<-SQL, { user_id: user.id, user_group_ids: user_group_ids, platform_ids: related_platform_ids } + ( + platforms.id IN (:platform_ids) + ) OR ( + platforms.owner_type = 'User' AND platforms.owner_id = :user_id + ) OR ( + platforms.owner_type = 'Group' AND platforms.owner_id IN (:user_group_ids) + ) + SQL + end + + protected + + def related_platform_ids + Rails.cache.fetch(['PlatformPolicy::Scope#related_platform_ids', user.id]) do + user.repositories.pluck(:platform_id) + end + end + end + + protected + + def owner? + record.owner == user || + record.owner.is_a?(Group) && user_group_ids.include?(record.owner_id) + end + + def local_admin? + Rails.cache.fetch(['PlatformPolicy#local_admin?', record, user]) do + user.best_role(record) == 'admin' + end + end + +end diff --git a/app/policies/product_policy.rb b/app/policies/product_policy.rb new file mode 100644 index 000000000..2037e3280 --- /dev/null +++ b/app/policies/product_policy.rb @@ -0,0 +1,3 @@ +class ProductPolicy < ApplicationPolicy + +end diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb new file mode 100644 index 000000000..b0a825656 --- /dev/null +++ b/app/policies/project_policy.rb @@ -0,0 +1,3 @@ +class ProjectPolicy < ApplicationPolicy + +end diff --git a/app/policies/statistic_policy.rb b/app/policies/statistic_policy.rb new file mode 100644 index 000000000..35803074d --- /dev/null +++ b/app/policies/statistic_policy.rb @@ -0,0 +1,3 @@ +class StatisticPolicy < ApplicationPolicy + +end diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb new file mode 100644 index 000000000..5eea7174f --- /dev/null +++ b/app/policies/user_policy.rb @@ -0,0 +1,6 @@ +class UserPolicy < ApplicationPolicy + + def banned? + !is_banned? + end +end diff --git a/app/views/home/activity.json.jbuilder b/app/views/home/activity.json.jbuilder index c3feedd0a..4fc600715 100644 --- a/app/views/home/activity.json.jbuilder +++ b/app/views/home/activity.json.jbuilder @@ -15,7 +15,7 @@ json.feed do end if user project_name_with_owner = "#{item.data[:project_owner]}/#{item.data[:project_name]}" - @project = Project.find_by_owner_and_name(item.data[:project_owner], item.data[:project_name]) + @project = Project.find_by_owner_and_name_cached(item.data[:project_owner], item.data[:project_name]) json.project_name_with_owner project_name_with_owner json.partial! item.partial, item: item, project_name_with_owner: project_name_with_owner diff --git a/app/views/layouts/_notifies.html.haml b/app/views/layouts/_notifies.html.haml index b4dce69a8..8a039b690 100644 --- a/app/views/layouts/_notifies.html.haml +++ b/app/views/layouts/_notifies.html.haml @@ -1,6 +1,6 @@ - if current_user || APP_CONFIG['anonymous_access'] .flash_notify - - if (flash_notify = FlashNotify.published.first) && flash_notify.should_show?(cookies[:flash_notify_hash]) + - if (flash_notify = FlashNotify.published_first_cached) && flash_notify.should_show?(cookies[:flash_notify_hash]) .alert{class: "alert-#{flash_notify.status}"} = flash_notify.body(I18n.locale).html_safe %a.close#close-alert{:'data-dismiss'=>"alert", href: "#"} × diff --git a/app/views/layouts/application.html.slim b/app/views/layouts/application.html.slim index 2ae30e727..9e7d383f9 100644 --- a/app/views/layouts/application.html.slim +++ b/app/views/layouts/application.html.slim @@ -16,7 +16,7 @@ html == yield :submenu if content_for?(:submenu) - if current_user || APP_CONFIG['anonymous_access'] - - if (flash_notify = FlashNotify.published.first) && flash_notify.should_show?(cookies[:flash_notify_hash]) + - if (flash_notify = FlashNotify.published_first_cached) && flash_notify.should_show?(cookies[:flash_notify_hash]) javascript: var FLASH_HASH_ID = "#{flash_notify.hash_id}"; .notify.alert.alert-dismissable.text-center class=alert_class(flash_notify.status) diff --git a/app/views/layouts/menu/_bottom.html.haml b/app/views/layouts/menu/_bottom.html.haml index 829321a87..1342bbe2e 100644 --- a/app/views/layouts/menu/_bottom.html.haml +++ b/app/views/layouts/menu/_bottom.html.haml @@ -20,7 +20,7 @@ %li = image_tag 'square.png' = link_to t('bottom_menu.developer_api'), t('bottom_menu.developer_api_url') - -if pr = Project.find_by_owner_and_name('abf/abf-ideas') + -if pr = Project.find_by_owner_and_name_cached('abf/abf-ideas') %li = image_tag 'square.png' = link_to t('bottom_menu.abf_ideas'), project_issues_url(pr) diff --git a/app/views/layouts/menu/_new_bottom.html.haml b/app/views/layouts/menu/_new_bottom.html.haml index cdf6ba926..1108ddcd3 100644 --- a/app/views/layouts/menu/_new_bottom.html.haml +++ b/app/views/layouts/menu/_new_bottom.html.haml @@ -13,7 +13,7 @@ %li= link_to t('bottom_menu.support'), contact_url %li · %li= link_to t('bottom_menu.developer_api'), t('bottom_menu.developer_api_url') - -if pr = Project.find_by_owner_and_name('abf/abf-ideas') + - if pr = Project.find_by_owner_and_name_cached('abf/abf-ideas') %li · %li= link_to t('bottom_menu.abf_ideas'), project_issues_url(pr) %li · diff --git a/app/views/layouts/menu/_new_top.html.slim b/app/views/layouts/menu/_new_top.html.slim index 9ed905655..2675c2683 100644 --- a/app/views/layouts/menu/_new_top.html.slim +++ b/app/views/layouts/menu/_new_top.html.slim @@ -12,7 +12,7 @@ nav.navbar.navbar-inverse.top_menu role = "navigation" #top-menu-navbar-collapse.collapse.navbar-collapse ul.nav.navbar-nav - (collection = t 'top_menu').each do |base, title| - - if can? :index, base.to_s.classify.constantize + - if policy(base).index? li class=top_menu_class(base) a href=send("#{base}_path") i.fa.hidden-sm class=top_menu_icon(base) diff --git a/app/views/platforms/base/_submenu.html.slim b/app/views/platforms/base/_submenu.html.slim index e7f61fba9..d5b0a0127 100644 --- a/app/views/platforms/base/_submenu.html.slim +++ b/app/views/platforms/base/_submenu.html.slim @@ -22,24 +22,24 @@ = link_to t("layout.repositories.list_header"), platform_repositories_path(@platform) li class=('active' if contr == :contents) = link_to t('layout.platforms.contents'), platform_contents_path(@platform) - - if can? :show, @platform + - if policy(@platform).show? li class=('active' if act == :index && contr == :maintainers) = link_to t("layout.platforms.maintainers"), platform_maintainers_path(@platform) li class=('active' if contr == :mass_builds) = link_to t("layout.platforms.mass_build"), platform_mass_builds_path(@platform) - - if can? :read, @platform.products.build + - if policy(@platform.products.build).index? li class=('active' if contr == :products) = link_to t("layout.products.list_header"), platform_products_path(@platform) - - if can? :advisories, @platform + - if policy(@platform.advisories.build).index? li class=('active' if contr == :platforms && act == :advisories) = link_to t("layout.advisories.list_header"), advisories_platform_path(@platform) - - if can? :update, @platform + - if policy(@platform).update? li class=('active' if act == :edit && contr == :platforms) = link_to t("platform_menu.settings"), edit_platform_path(@platform) - - if can? :members, @platform + - if policy(@platform).members? li class=('active' if act == :members && contr == :platforms) = link_to t("layout.platforms.members"), members_platform_path(@platform) - - if can? :edit, @platform + - if policy(@platform).update? li class=('active' if contr == :key_pairs) = link_to t("layout.key_pairs.header"), platform_key_pairs_path(@platform) li class=('active' if contr == :tokens) diff --git a/app/views/platforms/platforms/index.html.slim b/app/views/platforms/platforms/index.html.slim index 5bef8ba1c..a35dbd503 100644 --- a/app/views/platforms/platforms/index.html.slim +++ b/app/views/platforms/platforms/index.html.slim @@ -1,7 +1,9 @@ - set_meta_tags title: t('layout.platforms.list_header') .row ng-controller='PlatformsCtrl' .col-md-6.col-md-offset-3 ng-cloak=true - = link_to t('layout.platforms.new'), new_platform_path, class: 'btn btn-primary' if can? :create, Platform + - if policy(:platform).create? + a.btn.btn-primary href=new_platform_path + = t('layout.platforms.new') table.table.table-hover.offset10 thead tr diff --git a/app/views/platforms/platforms/show.html.slim b/app/views/platforms/platforms/show.html.slim index 7aac56657..1bcd2904e 100644 --- a/app/views/platforms/platforms/show.html.slim +++ b/app/views/platforms/platforms/show.html.slim @@ -47,7 +47,7 @@ b= t('layout.platforms.distrib_type') .col-md-8= @platform.distrib_type - - if can? :clone, @platform + - if policy(@platform).clone? .row .col-md-4 .col-md-8