debian-mirror-gitlab/lib/gitlab/observability.rb
2023-05-27 22:25:52 +05:30

145 lines
4.6 KiB
Ruby

# 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