Files
ubicloud/lib/util.rb
Jeremy Evans 2b58602ead Make EmailRenderer a Roda app using the mailer plugin
This is about the same amount of code than the previous approach, but
has the following advantages:

* Uses Roda's render plugin, so templates are cached and optimized
  into compiled methods.  The previous approach created 5 separate
  Tilt::ErubiTemplate objects for every email rendered.  The new
  approach does not create any Tilt::ErubiTemplate objects after
  the first email.

* Uses part instead of render for simpler rendering with locals.

* Moves EmailRenderer to separate file, so that reloading works
  correctly.

* Skips the rendering of the email stylesheet, since it does not
  contain any ERB code.  Instead, the file is included without
  rendering.

* Uses fixed locals for the email templates, so providing an
  invalid local will result in an error.

* Removes unnecessary empty `<style>` tag in email layout.

The only spec change is adding an email to one of the invoice
specs.  I'm not sure why this didn't fail before, but the
mail library complains if it tries to deliver a email with
no recipients.
2025-02-05 10:16:51 -08:00

90 lines
2.7 KiB
Ruby

# frozen_string_literal: true
require "net/ssh"
require "openssl"
require "erubi"
require "tilt"
module Util
# A minimal, non-cached SSH implementation.
#
# It must log into an account that can escalate to root via "sudo,"
# which typically includes the "root" account reflexively. The
# ssh-agent is employed by default here, since personnel are thought
# to be involved with preparing new VmHosts.
def self.rootish_ssh(host, user, keys, cmd)
Net::SSH.start(host, user,
Sshable::COMMON_SSH_ARGS.merge(key_data: keys,
use_agent: Config.development?)) do |ssh|
ret = ssh.exec!(cmd)
fail "Ssh command failed: #{ret}" unless ret.exitstatus.zero?
ret
end
end
def self.parse_key(key_data)
OpenSSL::PKey::EC.new(key_data)
rescue OpenSSL::PKey::ECError, OpenSSL::PKey::DSAError
OpenSSL::PKey::RSA.new(key_data)
end
def self.create_root_certificate(common_name:, duration:)
create_certificate(
subject: "/C=US/O=Ubicloud/CN=#{common_name}",
extensions: ["basicConstraints=CA:TRUE", "keyUsage=cRLSign,keyCertSign", "subjectKeyIdentifier=hash"],
duration: duration
).map(&:to_pem)
end
def self.create_certificate(subject:, duration:, extensions: [], issuer_cert: nil, issuer_key: nil)
cert = OpenSSL::X509::Certificate.new
key = OpenSSL::PKey::EC.generate("prime256v1")
# If the issuer is nil, we will create a self-signed certificate.
if issuer_cert.nil?
issuer_cert = cert
issuer_key = key
end
# Set certificate details
cert.version = 2 # X.509v3
cert.serial = OpenSSL::BN.rand(128, 0)
cert.subject = OpenSSL::X509::Name.parse(subject)
cert.issuer = issuer_cert.subject
cert.not_before = Time.now
cert.not_after = Time.now + duration
cert.public_key = key
# Add extensions
ef = OpenSSL::X509::ExtensionFactory.new
ef.subject_certificate = cert
ef.issuer_certificate = issuer_cert
extensions.each do |extension|
cert.add_extension(ef.create_extension(extension))
end
# Sign
cert.sign(issuer_key, OpenSSL::Digest.new("SHA256"))
[cert, key]
end
def self.exception_to_hash(ex)
{exception: {message: ex.message, class: ex.class.to_s, backtrace: ex.backtrace, cause: ex.cause.inspect}}
end
def self.safe_write_to_file(filename, content)
FileUtils.mkdir_p(File.dirname(filename))
temp_filename = filename + ".tmp"
File.open("#{temp_filename}.lock", File::RDWR | File::CREAT) do |lock_file|
lock_file.flock(File::LOCK_EX)
File.write(temp_filename, content)
File.rename(temp_filename, filename)
end
end
def self.send_email(...)
EmailRenderer.sendmail("/", ...)
end
end