Read about our upcoming Code of Conduct on this issue

snippets_finder.rb 3.48 KB
Newer Older
Douwe Maan's avatar
Douwe Maan committed
1
2
3
4
5
6
7
8
9
10
11
12
13
# Snippets Finder
#
# Used to filter Snippets collections by a set of params
#
# Arguments.
#
# current_user - The current user, nil also can be used.
# params:
#   visibility (integer) - Individual snippet visibility: Public(20), internal(10) or private(0).
#   project (Project) - Project related.
#   author (User) - Author related.
#
# params are optional
14
class SnippetsFinder < UnionFinder
Douwe Maan's avatar
Douwe Maan committed
15
  include Gitlab::Allowable
16
17
18
  include FinderMethods

  attr_accessor :current_user, :project, :params
19
20
21
22

  def initialize(current_user, params = {})
    @current_user = current_user
    @params = params
Douwe Maan's avatar
Douwe Maan committed
23
    @project = params[:project]
24
25
26
27
28
29
30
31
  end

  def execute
    items = init_collection
    items = by_author(items)
    items = by_visibility(items)

    items.fresh
32
33
34
35
  end

  private

36
  def init_collection
Douwe Maan's avatar
Douwe Maan committed
37
38
39
40
41
42
    if project.present?
      authorized_snippets_from_project
    else
      authorized_snippets
    end
  end
43

Douwe Maan's avatar
Douwe Maan committed
44
45
46
47
48
49
50
51
52
53
  def authorized_snippets_from_project
    if can?(current_user, :read_project_snippet, project)
      if project.team.member?(current_user)
        project.snippets
      else
        project.snippets.public_to_user(current_user)
      end
    else
      Snippet.none
    end
54
55
  end

Douwe Maan's avatar
Douwe Maan committed
56
  def authorized_snippets
57
58
    Snippet.where(feature_available_projects.or(not_project_related))
      .public_or_visible_to_user(current_user)
Douwe Maan's avatar
Douwe Maan committed
59
  end
60

61
62
63
  # Returns a collection of projects that is either public or visible to the
  # logged in user.
  #
64
  # A caller must pass in a block to modify individual parts of
65
66
67
  # the query, e.g. to apply .with_feature_available_for_user on top of it.
  # This is useful for performance as we can stick those additional filters
  # at the bottom of e.g. the UNION.
68
69
  def projects_for_user
    return yield(Project.public_to_user) unless current_user
70
71
72

    # If the current_user is allowed to see all projects,
    # we can shortcut and just return.
73
    return yield(Project.all) if current_user.full_private_access?
74

75
    authorized_projects = yield(Project.where('EXISTS (?)', current_user.authorizations_for_projects))
76
77

    levels = Gitlab::VisibilityLevel.levels_for_user(current_user)
78
    visible_projects = yield(Project.where(visibility_level: levels))
79
80
81
82
83
84
85
86

    # We use a UNION here instead of OR clauses since this results in better
    # performance.
    union = Gitlab::SQL::Union.new([authorized_projects.select('projects.id'), visible_projects.select('projects.id')])

    Project.from("(#{union.to_sql}) AS #{Project.table_name}")
  end

Douwe Maan's avatar
Douwe Maan committed
87
  def feature_available_projects
88
89
90
    # Don't return any project related snippets if the user cannot read cross project
    return table[:id].eq(nil) unless Ability.allowed?(current_user, :read_cross_project)

91
92
93
    projects = projects_for_user do |part|
      part.with_feature_available_for_user(:snippets, current_user)
    end.select(:id)
94

Douwe Maan's avatar
Douwe Maan committed
95
96
    arel_query = Arel::Nodes::SqlLiteral.new(projects.to_sql)
    table[:project_id].in(arel_query)
97
98
  end

Douwe Maan's avatar
Douwe Maan committed
99
100
101
102
103
104
  def not_project_related
    table[:project_id].eq(nil)
  end

  def table
    Snippet.arel_table
105
  end
106

107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
  def by_visibility(items)
    visibility = params[:visibility] || visibility_from_scope

    return items unless visibility

    items.where(visibility_level: visibility)
  end

  def by_author(items)
    return items unless params[:author]

    items.where(author_id: params[:author].id)
  end

  def visibility_from_scope
    case params[:scope].to_s
123
    when 'are_private'
124
      Snippet::PRIVATE
125
    when 'are_internal'
126
      Snippet::INTERNAL
127
    when 'are_public'
128
      Snippet::PUBLIC
129
    else
130
      nil
131
132
    end
  end
133
end