#-- copyright # OpenProject is an open source project management software. # Copyright (C) the OpenProject GmbH # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. # # OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: # Copyright (C) 2006-2013 Jean-Philippe Lang # Copyright (C) 2010-2013 the ChiliProject Team # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # See COPYRIGHT and LICENSE files for more details. #++ module OpenProject::GiteaIntegration class HookHandler # Gitea webhook events we handle. KNOWN_EVENTS = %w[ pull_request issue_comment ].freeze def process(_hook, request, params, user) event_type = request.env["HTTP_X_GITEA_EVENT"].to_s.downcase.tr(" ", "_") Rails.logger.debug { "Received Gitea webhook #{event_type}" } return 404 unless KNOWN_EVENTS.include?(event_type) return 403 unless authorized?(request, user) notify(params, user, event_type) 200 end private def authorized?(request, user) valid_signature?(request) && user.present? && (configured_user_id.blank? || user.id == configured_user_id) end def notify(params, user, event_type) payload = params[:payload] .permit! .to_h .merge("open_project_user_id" => user.id, "gitea_event" => event_type) OpenProject::Notifications.send(:"gitea.#{event_type}", payload) end # Gitea uses HMAC-SHA256, same as GitHub (X-Gitea-Signature header). def valid_signature?(request) secret = plugin_settings[:webhook_secret].presence return true if secret.blank? signature_header = request.env["HTTP_X_GITEA_SIGNATURE"] return false if signature_header.blank? expected = "sha256=#{OpenSSL::HMAC.hexdigest("SHA256", secret, request.raw_post)}" ActiveSupport::SecurityUtils.secure_compare(expected, signature_header) end def configured_user_id plugin_settings[:gitea_user_id].presence&.to_i end def plugin_settings Hash(Setting.plugin_openproject_gitea_integration).with_indifferent_access end end end