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

observability.rb « gitlab « lib - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: f7f65c91339289837f760aba68d181c6b90cfa5f (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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# frozen_string_literal: true

module Gitlab
  module Observability
    extend self

    ACTION_TO_PERMISSION = {
      explore: :read_observability,
      datasources: :admin_observability,
      manage: :admin_observability,
      dashboards: :read_observability
    }.freeze

    EMBEDDABLE_PATHS = %w[explore goto].freeze

    # Returns the GitLab Observability URL
    #
    def observability_url
      return ENV['OVERRIDE_OBSERVABILITY_URL'] if ENV['OVERRIDE_OBSERVABILITY_URL']
      # TODO Make observability URL configurable https://gitlab.com/gitlab-org/opstrace/opstrace-ui/-/issues/80
      return 'https://observe.staging.gitlab.com' if Gitlab.staging?

      'https://observe.gitlab.com'
    end

    # Returns true if the Observability feature flag is enabled
    #
    def enabled?(group = nil)
      return Feature.enabled?(:observability_group_tab, group) if group

      Feature.enabled?(:observability_group_tab)
    end

    # Returns the embeddable Observability URL of a given URL
    #
    #  - Validates the URL
    #  - Checks that the path is embeddable
    #  - Converts the gitlab.com URL to observe.gitlab.com URL
    #
    # e.g.
    #
    #  from: gitlab.com/groups/GROUP_PATH/-/observability/explore?observability_path=/explore
    #  to observe.gitlab.com/-/GROUP_ID/explore
    #
    # Returns nil if the URL is not a valid Observability URL or the path is not embeddable
    #
    def embeddable_url(url)
      uri = validate_url(url, Gitlab.config.gitlab.url)
      return unless uri

      group = group_from_observability_url(url)
      return unless group

      parsed_query = CGI.parse(uri.query.to_s).transform_values(&:first).symbolize_keys
      observability_path = parsed_query[:observability_path]

      return build_full_url(group, observability_path, '/') if observability_path_embeddable?(observability_path)
    end

    # Returns true if the user is allowed to perform an action within a group
    #
    def allowed_for_action?(user, group, action)
      return false if action.nil?

      permission = ACTION_TO_PERMISSION.fetch(action.to_sym, :admin_observability)
      allowed?(user, group, permission)
    end

    # Returns true if the user has the specified permission within the group
    def allowed?(user, group, permission = :admin_observability)
      return false unless group && user

      observability_url.present? && Ability.allowed?(user, permission, group)
    end

    # Builds the full Observability URL given a certan group and path
    #
    # If unsanitized_observability_path is not valid or missing, fallbacks to fallback_path
    #
    def build_full_url(group, unsanitized_observability_path, fallback_path)
      return unless group

      # When running Observability UI in standalone mode (i.e. not backed by Observability Backend)
      # the group-id is not required. !!This is only used for local dev!!
      base_url = ENV['STANDALONE_OBSERVABILITY_UI'] == 'true' ? observability_url : "#{observability_url}/-/#{group.id}"

      sanitized_path = if unsanitized_observability_path && sanitize(unsanitized_observability_path) != ''
                         CGI.unescapeHTML(sanitize(unsanitized_observability_path))
                       else
                         fallback_path || '/'
                       end

      sanitized_path.prepend('/') if sanitized_path[0] != '/'

      "#{base_url}#{sanitized_path}"
    end

    private

    def validate_url(url, reference_url)
      uri = URI.parse(url)
      reference_uri = URI.parse(reference_url)

      return uri if uri.scheme == reference_uri.scheme &&
        uri.port == reference_uri.port &&
        uri.host.casecmp?(reference_uri.host)
    rescue URI::InvalidURIError
      nil
    end

    def link_sanitizer
      @link_sanitizer ||= Rails::Html::Sanitizer.safe_list_sanitizer.new
    end

    def sanitize(input)
      link_sanitizer.sanitize(input, {})&.html_safe
    end

    def group_from_observability_url(url)
      match = Rails.application.routes.recognize_path(url)

      return if match[:unmatched_route].present?
      return if match[:group_id].blank? || match[:action].blank? || match[:controller] != "groups/observability"

      group_path = match[:group_id]
      Group.find_by_full_path(group_path)
    rescue ActionController::RoutingError
      nil
    end

    def observability_path_embeddable?(observability_path)
      return false unless observability_path

      observability_path = observability_path[1..] if observability_path[0] == '/'

      parsed_observability_path = URI.parse(observability_path).path.split('/')

      base_path = parsed_observability_path[0]

      EMBEDDABLE_PATHS.include?(base_path)
    rescue URI::InvalidURIError
      false
    end
  end
end