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
|