Welcome to mirror list, hosted at ThFree Co, Russian Federation.

inline_metrics_redactor_filter.rb « filter « banzai « lib - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: e84ba83e03e2a91f4a5d1f7ee1c8b2724ed1a9fa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# frozen_string_literal: true

module Banzai
  module Filter
    # HTML filter that removes embeded elements that the current user does
    # not have permission to view.
    class InlineMetricsRedactorFilter < HTML::Pipeline::Filter
      include Gitlab::Utils::StrongMemoize

      METRICS_CSS_CLASS = '.js-render-metrics'
      URL = Gitlab::Metrics::Dashboard::Url

      Embed = Struct.new(:project_path, :permission)

      # Finds all embeds based on the css class the FE
      # uses to identify the embedded content, removing
      # only unnecessary nodes.
      def call
        nodes.each do |node|
          embed = embeds_by_node[node]
          user_has_access = user_access_by_embed[embed]

          node.remove unless user_has_access
        end

        doc
      end

      private

      def user
        context[:current_user]
      end

      # Returns all nodes which the FE will identify as
      # a metrics embed placeholder element
      #
      # @return [Nokogiri::XML::NodeSet]
      def nodes
        @nodes ||= doc.css(METRICS_CSS_CLASS)
      end

      # Maps a node to key properties of an embed.
      # Memoized so we only need to run the regex to get
      # the project full path from the url once per node.
      #
      # @return [Hash<Nokogiri::XML::Node, Embed>]
      def embeds_by_node
        strong_memoize(:embeds_by_node) do
          nodes.each_with_object({}) do |node, embeds|
            embed = Embed.new
            url = node.attribute('data-dashboard-url').to_s

            set_path_and_permission(embed, url, URL.regex, :read_environment)
            set_path_and_permission(embed, url, URL.grafana_regex, :read_project) unless embed.permission

            embeds[node] = embed if embed.permission
          end
        end
      end

      # Attempts to determine the path and permission attributes
      # of a url based on expected dashboard url formats and
      # sets the attributes on an Embed object
      #
      # @param embed [Embed]
      # @param url [String]
      # @param regex [RegExp]
      # @param permission [Symbol]
      def set_path_and_permission(embed, url, regex, permission)
        return unless path = regex.match(url) do |m|
          "#{$~[:namespace]}/#{$~[:project]}"
        end

        embed.project_path = path
        embed.permission = permission
      end

      # Returns a mapping representing whether the current user
      # has permission to view the embed for the project.
      # Determined in a batch
      #
      # @return [Hash<Embed, Boolean>]
      def user_access_by_embed
        strong_memoize(:user_access_by_embed) do
          unique_embeds.each_with_object({}) do |embed, access|
            project = projects_by_path[embed.project_path]

            access[embed] = Ability.allowed?(user, embed.permission, project)
          end
        end
      end

      # Returns a unique list of embeds
      #
      # @return [Array<Embed>]
      def unique_embeds
        embeds_by_node.values.uniq
      end

      # Maps a project's full path to a Project object.
      # Contains all of the Projects referenced in the
      # metrics placeholder elements of the current document
      #
      # @return [Hash<String, Project>]
      def projects_by_path
        strong_memoize(:projects_by_path) do
          Project.eager_load(:route, namespace: [:route])
            .where_full_path_in(unique_project_paths)
            .index_by(&:full_path)
        end
      end

      # Returns a list of the full_paths of every project which
      # has an embed in the doc
      #
      # @return [Array<String>]
      def unique_project_paths
        embeds_by_node.values.map(&:project_path).uniq
      end
    end
  end
end