Serve Woodpecker pipeline configs from the engine (/build/config)
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
require "erb"
|
||||
|
||||
module WarpEngine
|
||||
# A /build/config platform-template-jeinek renderelése: a pipeline-logika
|
||||
# a lib/warp_engine/ci_templates/<platform>.yaml.erb fájlokban él, a
|
||||
# platformonkénti builder image-eket a WarpEngine.config.ci_platforms adja.
|
||||
class CiConfigService
|
||||
PLATFORM_FORMAT = /\A[a-z0-9_-]+\z/
|
||||
|
||||
# A renderelt pipeline YAML, vagy nil, ha a platform nem kiszolgált.
|
||||
def render(platform:, name:, update_server:)
|
||||
platform = platform.to_s
|
||||
return nil unless platform.match?(PLATFORM_FORMAT)
|
||||
|
||||
spec = platform_spec(platform)
|
||||
return nil if spec.nil?
|
||||
|
||||
path = templates_dir.join("#{platform}.yaml.erb")
|
||||
return nil unless path.exist?
|
||||
|
||||
ERB.new(path.read, trim_mode: "-").result_with_hash(
|
||||
name: name.to_s,
|
||||
update_server: update_server.to_s,
|
||||
builder: spec[:builder],
|
||||
exporter: spec[:exporter]
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def platform_spec(platform)
|
||||
spec = WarpEngine.config.ci_platforms.stringify_keys[platform]
|
||||
spec&.symbolize_keys
|
||||
end
|
||||
|
||||
def templates_dir
|
||||
WarpEngine::Engine.root.join("lib", "warp_engine", "ci_templates")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,90 @@
|
||||
require "openssl"
|
||||
require "base64"
|
||||
require "net/http"
|
||||
|
||||
module WarpEngine
|
||||
# A Woodpecker configuration-extension kéréseinek httpsig-ellenőrzése
|
||||
# (draft-cavage http-signatures, ed25519). A szerver az aláírt headerek
|
||||
# listáját a Signature headerben küldi — tipikusan "(request-target) date".
|
||||
class CiSignatureVerifier
|
||||
SIGNATURE_PARAM = /(\w+)="([^"]*)"/
|
||||
|
||||
@key_cache = {}
|
||||
@key_mutex = Mutex.new
|
||||
|
||||
class << self
|
||||
# A letöltött kulcs process-szinten cache-elt (URL-enként).
|
||||
def fetch_public_key(url)
|
||||
@key_mutex.synchronize do
|
||||
@key_cache[url] ||= Net::HTTP.get(URI.parse(url))
|
||||
end
|
||||
end
|
||||
|
||||
def reset_key_cache!
|
||||
@key_mutex.synchronize { @key_cache = {} }
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(request)
|
||||
@request = request
|
||||
end
|
||||
|
||||
def valid?
|
||||
pem = public_key_pem
|
||||
if pem.blank?
|
||||
Rails.logger.error("[CiSignatureVerifier] nincs ci_extension_public_key(_url) konfigurálva — kérés elutasítva")
|
||||
return false
|
||||
end
|
||||
|
||||
params = signature_params
|
||||
return false if params.nil? || params["signature"].blank?
|
||||
|
||||
signing_string = build_signing_string(params.fetch("headers", "date"))
|
||||
return false if signing_string.nil?
|
||||
|
||||
key = OpenSSL::PKey.read(pem)
|
||||
key.verify(nil, Base64.decode64(params["signature"]), signing_string)
|
||||
rescue OpenSSL::PKey::PKeyError, ArgumentError => e
|
||||
Rails.logger.error("[CiSignatureVerifier] #{e.class}: #{e.message}")
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def public_key_pem
|
||||
config = WarpEngine.config
|
||||
return config.ci_extension_public_key if config.ci_extension_public_key.present?
|
||||
return nil if config.ci_extension_public_key_url.blank?
|
||||
|
||||
self.class.fetch_public_key(config.ci_extension_public_key_url)
|
||||
rescue StandardError => e
|
||||
Rails.logger.error("[CiSignatureVerifier] kulcs-letöltés sikertelen: #{e.class}: #{e.message}")
|
||||
nil
|
||||
end
|
||||
|
||||
# A Signature header (vagy az "Authorization: Signature ..." forma) paraméterei.
|
||||
def signature_params
|
||||
header = @request.headers["Signature"].presence
|
||||
if header.nil?
|
||||
auth = @request.headers["Authorization"].to_s
|
||||
header = auth.delete_prefix("Signature ") if auth.start_with?("Signature ")
|
||||
end
|
||||
return nil if header.blank?
|
||||
|
||||
header.scan(SIGNATURE_PARAM).to_h
|
||||
end
|
||||
|
||||
def build_signing_string(headers_list)
|
||||
lines = headers_list.split(" ").map do |name|
|
||||
if name == "(request-target)"
|
||||
"(request-target): #{@request.request_method.downcase} #{@request.fullpath}"
|
||||
else
|
||||
value = @request.headers[name]
|
||||
return nil if value.nil?
|
||||
"#{name.downcase}: #{value}"
|
||||
end
|
||||
end
|
||||
lines.join("\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user