lib/site_hook/webhook.rb in site_hook-0.8.2 vs lib/site_hook/webhook.rb in site_hook-0.9.3

- old
+ new

@@ -1,31 +1,25 @@ ########## -# -> File: /home/ken/RubymineProjects/site_hook/lib/site_hook/webhook.rb +# -> File: /site_hook/lib/site_hook/webhook.rb # -> Project: site_hook # -> Author: Ken Spencer <me@iotaspencer.me> # -> Last Modified: 1/10/2018 21:35:44 # -> Copyright (c) 2018 Ken Spencer # -> License: MIT ########## -require 'site_hook/persist' -require 'site_hook/const' -require 'sinatra' +require 'rack' +require 'site_hook/logger' +require 'json' +require 'grape' +require 'grape-route-helpers' module SiteHook - class Webhook < Sinatra::Base + class Server < Grape::API + version nil + prefix '' + format :json - set server: %w[thin] - set quiet: true - set raise_errors: true - set views: Pathname(SiteHook::Paths.lib_dir).join('site_hook', 'views') - set :public_folder, Pathname(SiteHook::Paths.lib_dir).join('site_hook', 'assets') - use CoffeeHandler - def self.set_options(host, port) - self.set bind: host.to_s - self.set port: port.to_i - end - # # @param [String] body JSON String of body # @param [String] sig Signature or token from git service # @param [String] secret User-defined verification token # @param [Boolean] plaintext Whether the verification is plaintext @@ -34,144 +28,141 @@ if plaintext sig == secret else case service when 'gogs' - if sig == OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, secret, body) - APPLOG.debug "Secret verified: #{sig} === #{OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, secret, body)}" + if sig == OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, secret, body.chomp) + SiteHook::Log.app.debug "Secret verified: #{sig} === #{OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, secret, body)}" true end when 'github' - if sig == OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, secret, body) - APPLOG.debug "Secret verified: #{sig} === #{OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, secret, body)}" + if sig == OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, secret, body.chomp) + SiteHook::Log.app.debug "Secret verified: #{sig} === #{OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, secret, body)}" true end else # This shouldn't happen end - end end - CONTENT_TYPE = 'Content-Type' - get '/' do - halt 403, {CONTENT_TYPE => 'text/html'}, '<h1>See <a href="/webhooks/">here</a> for the active webhooks</h1>' + helpers do + def halt(status, message, headers) + error!(message, status, headers) + end end - APPLICATION_JSON = 'application/json' - get '/webhooks.json', provides: :json do - content_type APPLICATION_JSON - public_projects = JPHRC['projects'].select do |_project, hsh| - !hsh.fetch('private') - end - result = {} - public_projects.each do |project, hsh| - result[project] = {} - hsh.delete('hookpass') - result[project].merge!(hsh) - end - headers CONTENT_TYPE => APPLICATION_JSON, 'Accept' => APPLICATION_JSON - json result, layout: false + before do + remote_addr = request.env['REMOTE_ADDR'] + cf_connecting_ip = request.env.fetch('HTTP_CF_CONNECTING_IP', nil) + ip = cf_connecting_ip || remote_addr + SiteHook::Log.access.log "#{ip} - #{request.request_method} #{request.path}" end - - get '/webhooks/?' do - haml :webhooks, locals: {'projects' => JPHRC['projects']} + after do + SiteHook::Log.access.log "#{status}" end + resource :webhook do + route_param :hook_name do + get do + STDOUT.puts params[:hook_name] + project = SiteHook::Config.projects.find_project(StrExt.mkvar(params[:hook_name])) + if project.nil? + {message: 'project not found or private', status: 1, project: {}} + else + project_obj = {} + %i[src dst repo host].each do |option| + project_obj[option] = project.instance_variable_get(StrExt.mkvar(option)) + end + {project: project_obj} + end + end + post do + service = nil + request.body.rewind + req_body = request.body.read + project = SiteHook::Config.projects.get(StrExt.mkvar(params[:hook_name])) + CONTENT_TYPE = 'Content-Type' + if project == :not_found + halt 404, {message: 'no such project', status: 1}.to_json, {CONTENT_TYPE => APPLICATION_JSON} + elsif project == :no_projects + halt 500, {message: 'no projects defined', status: 2}.to_json, {CONTENT_TYPE => APPLICATION_JSON} + end + plaintext = false + signature = nil + event = nil + gogs = request.env.fetch('HTTP_X_GOGS_EVENT', nil) + unless gogs.nil? + event = 'push' if gogs == 'push' + end + github = request.env.fetch('HTTP_X_GITHUB_EVENT', nil) + unless github.nil? + event = 'push' if github == 'push' + end + gitlab = request.env.fetch('HTTP_X_GITLAB_EVENT', nil) + unless gitlab.nil? + event = 'push' if gitlab == 'push' + end - get '/webhook/*' do - if params[:splat] - pass - else - halt 405, {CONTENT_TYPE => APPLICATION_JSON}, {message: 'GET not allowed'}.to_json - end - end - post '/webhook/:hook_name/?' do - service = nil - request.body.rewind - req_body = request.body.read - js = RecursiveOpenStruct.new(JSON.parse(req_body)) + events = {'github' => github, 'gitlab' => gitlab, 'gogs' => gogs} + if events['github'] && events['gogs'] + events['github'] = nil + end + events_m_e = events.values.one? + case events_m_e + when true + event = 'push' + service = events.select { |_key, value| value }.keys.first + when false + halt 400, {message: 'events are mutually exclusive', status: 'failure'}.to_json, {CONTENT_TYPE => APPLICATION_JSON} - projects = JPHRC['projects'] - project = projects.fetch(params[:hook_name], nil) - if project.nil? - halt 404, {CONTENT_TYPE => APPLICATION_JSON}, {message: 'no such project', status: 1}.to_json - end - plaintext = false - signature = nil - event = nil - gogs = request.env.fetch('HTTP_X_GOGS_EVENT', nil) - unless gogs.nil? - event = 'push' if gogs == 'push' - end - github = request.env.fetch('HTTP_X_GITHUB_EVENT', nil) - unless github.nil? - event = 'push' if github == 'push' - end - gitlab = request.env.fetch('HTTP_X_GITLAB_EVENT', nil) - unless gitlab.nil? - event = 'push' if gitlab == 'push' - end + else + halt 400, {'status': 'failure', 'message': 'something weird happened'}, {CONTENT_TYPE => APPLICATION_JSON} + end + if event != 'push' && event.nil? + halt 400, {message: 'no event header', status: 'failure'}.to_json, {CONTENT_TYPE => APPLICATION_JSON} + end + case service + when 'gitlab' + signature = request.env.fetch('HTTP_X_GITLAB_TOKEN', '') + plaintext = true + when 'github' + signature = request.env.fetch('HTTP_X_HUB_SIGNATURE', '').sub!(/^sha1=/, '') + plaintext = false - events = {'github' => github, 'gitlab' => gitlab, 'gogs' => gogs} - if events['github'] && events['gogs'] - events['github'] = nil - end - events_m_e = events.values.one? - case events_m_e - when true - event = 'push' - service = events.select { |_key, value| value }.keys.first - when false - halt 400, {CONTENT_TYPE: APPLICATION_JSON}, {message: 'events are mutually exclusive', status: 'failure'}.to_json - - else - halt 400, - {CONTENT_TYPE: APPLICATION_JSON}, - 'status': 'failure', 'message': 'something weird happened' - end - if event != 'push' && event.nil? - halt 400, {CONTENT_TYPE: APPLICATION_JSON}, {message: 'no event header'}.to_json - end - case service - when 'gitlab' - signature = request.env.fetch('HTTP_X_GITLAB_TOKEN', '') - plaintext = true - when 'github' - signature = request.env.fetch('HTTP_X_HUB_SIGNATURE', '').sub!(/^sha1=/, '') - plaintext = false - - when 'gogs' - signature = request.env.fetch('HTTP_X_GOGS_SIGNATURE', '') - plaintext = false - else - # This shouldn't happen - end - if Webhook.verified?(req_body.to_s, signature, project['hookpass'], plaintext: plaintext, service: service) - BUILDLOG.info 'Building...' - begin - jekyll_status = SiteHook::Senders::Jekyll.build(project['src'], project['dst'], BUILDLOG, options: {config: project['config']}) - case jekyll_status - - when 0 - status 200 - headers CONTENT_TYPE => APPLICATION_JSON - body { {'status': 'success'}.to_json } - when -1, -2, -3 - halt 400, {CONTENT_TYPE => APPLICATION_JSON}, {'status': 'exception', error: jekyll_status.fetch(:message).to_s} + when 'gogs' + signature = request.env.fetch('HTTP_X_GOGS_SIGNATURE', '') + plaintext = false else # This shouldn't happen end + if Server.verified?(req_body.to_s, signature, project.hookpass, plaintext: plaintext, service: service) + SiteHook::Log.build.info 'Building...' + begin + jekyll_status = SiteHook::Senders::Jekyll.build(project.src, project.dst, SiteHook::Log.build, options: {config: project.config}) + case jekyll_status[:status] - rescue => e - halt 500, {CONTENT_TYPE => APPLICATION_JSON}, {'status': 'exception', error: e.to_s} - + when 0 + status 200 + {status: 'success'} + when -1, -2, -3 + halt 400, {'status': 'exception', error: jekyll_status.fetch(:message).to_s}, {CONTENT_TYPE => APPLICATION_JSON} + else + # This shouldn't happen + end + rescue => e + halt 500, {'status': 'exception', error: e.to_s}, {CONTENT_TYPE => APPLICATION_JSON} + end + else + halt 403, {message: 'incorrect secret', 'status': 'failure'}.to_json, {CONTENT_TYPE => APPLICATION_JSON} + end end - - else - halt 403, {CONTENT_TYPE => APPLICATION_JSON}, {message: 'incorrect secret', 'status': 'failure'}.to_json end end - post '/webhook/?' do - halt 403, {CONTENT_TYPE => APPLICATION_JSON}, {message: 'pick a hook', error: 'root webhook hit', 'status': 'failure'}.to_json + + resource :webhooks do + get do + SiteHook::Config.projects.to_h + end + end end end