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: b500df8636393ecffe587a5b7e605cbf0d8ad0d7 (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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# 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

    def oauth_url
      "#{Gitlab::Observability.observability_url}/v1/auth/start"
    end

    def tracing_url(project)
      "#{Gitlab::Observability.observability_url}/query/#{project.group.id}/#{project.id}/v1/traces"
    end

    def provisioning_url(_project)
      # TODO Change to correct endpoint when API is ready
      Gitlab::Observability.observability_url.to_s
    end

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

      Feature.enabled?(:observability_group_tab)
    end

    # Returns true if Tracing UI is enabled
    def tracing_enabled?(project)
      Feature.enabled?(:observability_tracing, project)
    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