Skip to content

Commit

Permalink
Reorganize code
Browse files Browse the repository at this point in the history
  • Loading branch information
Dany Marcoux committed Apr 26, 2021
1 parent 4e325a1 commit 668df82
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 96 deletions.
99 changes: 22 additions & 77 deletions src/api/app/controllers/trigger_controller.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
class TriggerController < ApplicationController
ALLOWED_GITLAB_EVENTS = ['Push Hook', 'Tag Push Hook', 'Merge Request Hook']

validate_action rebuild: { method: :post, response: :status }
validate_action release: { method: :post, response: :status }
validate_action runservice: { method: :post, response: :status }

before_action :validate_token, :set_package, :set_user, only: [:create]
# before_action :validate_token, :set_package, :set_user, only: [:create]
before_action :disallow_project_param, only: [:release]
before_action :extract_auth_from_request, :validate_auth_token, :require_valid_token, except: [:create]
before_action :validate_gitlab_event
before_action :set_token
# before_action :extract_auth_from_request, :validate_auth_token, :require_valid_token, except: [:create]
#
# Authentication happens with tokens, so no login is required
#
Expand All @@ -20,74 +24,30 @@ class TriggerController < ApplicationController
include Trigger::Errors

def create
if !@user.is_active? || !@user.can_modify?(@package)
render_error message: 'Token not found or not valid.', status: 404
return
end

Backend::Api::Sources::Package.trigger_services(@package.project.name, @package.name, @user.login)
render_ok
end

def rebuild
rebuild_trigger = PackageControllerService::RebuildTrigger.new(package: @pkg, project: @prj,
repository: params[:repository],
arch: params[:arch])
authorize rebuild_trigger.policy_object, :update?
rebuild_trigger.rebuild?
render_ok
end

def release
raise NoPermissionForPackage.setup('no_permission', 403, "no permission for package #{@pkg} in project #{@pkg.project}") unless policy(@pkg).update?

manual_release_targets = @pkg.project.release_targets.where(trigger: 'manual')
raise NoPermissionForPackage.setup('not_found', 404, "#{@pkg.project} has no release targets that are triggered manually") unless manual_release_targets.any?

manual_release_targets.each do |release_target|
release_package(@pkg,
release_target.target_repository,
@pkg.release_target_name,
{ filter_source_repository: release_target.repository,
manual: true,
comment: 'Releasing via trigger event' })
end

render_ok
end

def runservice
raise NoPermissionForPackage.setup('no_permission', 403, "no permission for package #{@pkg} in project #{@pkg.project}") unless policy(@pkg).update?

# execute the service in backend
pass_to_backend(prepare_path_for_runservice)

@pkg.sources_changed
authencation
get token
pundit
token.call
end

private

def prepare_path_for_runservice
path = @pkg.source_path
params = { cmd: 'runservice', comment: 'runservice via trigger', user: User.session!.login }
URI(path + build_query_from_hash(params, [:cmd, :comment, :user])).to_s
end

def disallow_project_param
render_error(message: 'You specified a project, but the token defines the project/package to release', status: 403, errorcode: 'no_permission') if params[:project].present?
end

def extract_auth_from_request
@token_extractor = ::TriggerControllerService::TokenExtractor.new(request).call
# AUTHENTICATION
def set_token
@token = ::TriggerControllerService::TokenExtractor.new(request).call
end

def validate_auth_token
raise InvalidToken unless @token_extractor.valid?
def validate_gitlab_event
return unless request.env['HTTP_X_GITLAB_EVENT']

raise InvalidToken unless request.env['HTTP_X_GITLAB_EVENT'].in?(ALLOWED_GITLAB_EVENTS)
end

def require_valid_token
@token = @token_extractor.token

raise TokenNotFound unless @token

User.session = @token.user
Expand All @@ -114,10 +74,7 @@ def require_valid_token
end
end

def set_package
@package = @token.package || Package.get_by_project_and_name(params[:project], params[:package], use_source: true)
end

# AUTHORIZATION webhook
def validate_token
@token = Token::Service.find_by(id: params[:id])
return if @token && @token.valid_signature?(signature, request.body.read)
Expand All @@ -126,23 +83,11 @@ def validate_token
false
end

def set_user
@user = @token.user
def set_package
@package = @token.package || Package.get_by_project_and_name(params[:project], params[:package], use_source: true)
end

# To trigger the webhook, the sender needs to
# generate a signature with a secret token.
# The signature needs to be generated over the
# payload of the HTTP request and stored
# in a HTTP header.
# GitHub: HTTP_X_HUB_SIGNATURE
# https://developer.github.com/webhooks/securing/
# Pagure: HTTP_X-Pagure-Signature-256
# https://docs.pagure.org/pagure/usage/using_webhooks.html
# Custom signature: HTTP_X_OBS_SIGNATURE
def signature
request.env['HTTP_X_OBS_SIGNATURE'] ||
request.env['HTTP_X_HUB_SIGNATURE'] ||
request.env['HTTP_X-Pagure-Signature-256']
def set_user
@user = @token.user
end
end
7 changes: 7 additions & 0 deletions src/api/app/models/token/rebuild.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ class Token::Rebuild < Token
def self.token_name
'rebuild'
end

def rebuild
rebuild_trigger = PackageControllerService::RebuildTrigger.new(package: @pkg, project: @prj, params: params)
authorize rebuild_trigger.policy_object, :update?
rebuild_trigger.rebuild?
render_ok
end
end

# == Schema Information
Expand Down
18 changes: 18 additions & 0 deletions src/api/app/models/token/release.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,24 @@ class Token::Release < Token
def self.token_name
'release'
end

def release
raise NoPermissionForPackage.setup('no_permission', 403, "no permission for package #{@pkg} in project #{@pkg.project}") unless policy(@pkg).update?

manual_release_targets = @pkg.project.release_targets.where(trigger: 'manual')
raise NoPermissionForPackage.setup('not_found', 404, "#{@pkg.project} has no release targets that are triggered manually") unless manual_release_targets.any?

manual_release_targets.each do |release_target|
release_package(@pkg,
release_target.target_repository,
@pkg.release_target_name,
{ filter_source_repository: release_target.repository,
manual: true,
comment: 'Releasing via trigger event' })
end

render_ok
end
end

# == Schema Information
Expand Down
25 changes: 20 additions & 5 deletions src/api/app/models/token/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,31 @@ def self.token_name
'runservice'
end

def valid_signature?(signature, body)
return false unless signature
def code_from_webhook_controller
if !@user.is_active? || !@user.can_modify?(@package)
render_error message: 'Token not found or not valid.', status: 404
return
end

ActiveSupport::SecurityUtils.secure_compare(signature_of(body), signature)
Backend::Api::Sources::Package.trigger_services(@package.project.name, @package.name, @user.login)
render_ok
end

def runservice
raise NoPermissionForPackage.setup('no_permission', 403, "no permission for package #{@pkg} in project #{@pkg.project}") unless policy(@pkg).update?

# execute the service in backend
pass_to_backend(prepare_path_for_runservice)

@pkg.sources_changed
end

private

def signature_of(body)
'sha1=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), string, body)
def prepare_path_for_runservice
path = @pkg.source_path
params = { cmd: 'runservice', comment: 'runservice via trigger', user: User.session!.login }
URI(path + build_query_from_hash(params, [:cmd, :comment, :user])).to_s
end
end

Expand Down
59 changes: 45 additions & 14 deletions src/api/app/services/trigger_controller_service/token_extractor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,60 @@ module TriggerControllerService
class TokenExtractor
def initialize(http_request)
@http_request = http_request
@token_id = http_request.params[:id]
@body = @http_request.body.read
end

def call
extract_auth_token
self
if @token_id
extract_token_from_request_signature
else
extract_auth_token_from_headers
end
end

def extract_auth_token
events = ['Push Hook', 'Tag Push Hook', 'Merge Request Hook']
@auth_token = if events.any?(@http_request.env['HTTP_X_GITLAB_EVENT'])
'Token ' + @http_request.env['HTTP_X_GITLAB_TOKEN']
else
@http_request.env['HTTP_AUTHORIZATION']
end
private

def extract_auth_token_from_headers
auth_token = @http_request.env['HTTP_X_GITLAB_TOKEN'] ||
@http_request.env['HTTP_AUTHORIZATION'].to_s.slice(6..-1)
return unless auth_token

Token.find_by_string!(auth_token) if auth_token.match?(%r{^[A-Za-z0-9+/]+$})
end

def extract_token_from_request_signature
token = Token::Service.find_by(id: @token_id)
return token if token && token.valid_signature?(signature, @body)
end

# from Token::Service
def valid_signature?(signature)
return false unless signature

ActiveSupport::SecurityUtils.secure_compare(signature_of(@body), signature)
end

def valid?
@auth_token.present? && @auth_token[0..4] == 'Token' && @auth_token[6..-1].match?(%r{^[A-Za-z0-9+/]+$})
# from Token::Service
def signature_of
# TODO: use sha256 (from X-Hub-Signature-256)
'sha1=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), string, @body)
end

# it will return a Token subclass or raise ActiveRecord::RecordNotFound
def token
Token.token_type(@http_request['action']).find_by_string!(@auth_token[6..-1])
# To trigger the webhook, the sender needs to
# generate a signature with a secret token.
# The signature needs to be generated over the
# payload of the HTTP request and stored
# in a HTTP header.
# GitHub: HTTP_X_HUB_SIGNATURE
# https://developer.github.com/webhooks/securing/
# Pagure: HTTP_X-Pagure-Signature-256
# https://docs.pagure.org/pagure/usage/using_webhooks.html
# Custom signature: HTTP_X_OBS_SIGNATURE
def signature
@http_request.env['HTTP_X_OBS_SIGNATURE'] ||
@http_request.env['HTTP_X_HUB_SIGNATURE'] ||
@http_request.env['HTTP_X-Pagure-Signature-256']
end
end
end

0 comments on commit 668df82

Please sign in to comment.