lib/localhost/authority.rb in localhost-1.2.0 vs lib/localhost/authority.rb in localhost-1.3.0

- old
+ new

@@ -1,22 +1,26 @@ # frozen_string_literal: true # Released under the MIT License. -# Copyright, 2018-2023, by Samuel Williams. +# Copyright, 2018-2024, by Samuel Williams. # Copyright, 2019, by Richard S. Leung. # Copyright, 2021, by Akshay Birajdar. # Copyright, 2021, by Ye Lin Aung. # Copyright, 2023, by Antonio Terceiro. # Copyright, 2023, by Yuuji Yaginuma. +# Copyright, 2024, by Colin Shea. +require 'fileutils' require 'openssl' module Localhost # Represents a single public/private key pair for a given hostname. class Authority + # Where to store the key pair on the filesystem. This is a subdirectory + # of $XDG_STATE_HOME, or ~/.local/state/ when that's not defined. def self.path - File.expand_path("~/.localhost") + File.expand_path("localhost.rb", ENV.fetch("XDG_STATE_HOME", "~/.local/state")) end # List all certificate authorities in the given directory: def self.list(root = self.path) return to_enum(:list) unless block_given? @@ -174,31 +178,31 @@ ) end end def load(path = @root) - if File.directory?(path) - certificate_path = File.join(path, "#{@hostname}.crt") - key_path = File.join(path, "#{@hostname}.key") - - return false unless File.exist?(certificate_path) and File.exist?(key_path) - - certificate = OpenSSL::X509::Certificate.new(File.read(certificate_path)) - key = OpenSSL::PKey::RSA.new(File.read(key_path)) - - # Certificates with old version need to be regenerated. - return false if certificate.version < 2 - - @certificate = certificate - @key = key - - return true - end + ensure_authority_path_exists(path) + + certificate_path = File.join(path, "#{@hostname}.crt") + key_path = File.join(path, "#{@hostname}.key") + + return false unless File.exist?(certificate_path) and File.exist?(key_path) + + certificate = OpenSSL::X509::Certificate.new(File.read(certificate_path)) + key = OpenSSL::PKey::RSA.new(File.read(key_path)) + + # Certificates with old version need to be regenerated. + return false if certificate.version < 2 + + @certificate = certificate + @key = key + + return true end def save(path = @root) - Dir.mkdir(path, 0700) unless File.directory?(path) + ensure_authority_path_exists(path) lockfile_path = File.join(path, "#{@hostname}.lock") File.open(lockfile_path, File::RDWR|File::CREAT, 0644) do |lockfile| lockfile.flock(File::LOCK_EX) @@ -210,9 +214,23 @@ File.write( File.join(path, "#{@hostname}.key"), self.key.to_pem ) + end + end + + # Ensures that the directory to store the certificate exists. If the legacy + # directory (~/.localhost/) exists, it is moved into the new XDG Basedir + # compliant directory. + def ensure_authority_path_exists(path = @root) + old_root = File.expand_path("~/.localhost") + + if File.directory?(old_root) and not File.directory?(path) + # Migrates the legacy dir ~/.localhost/ to the XDG compliant directory + File.rename(old_root, path) + elsif not File.directory?(path) + FileUtils.makedirs(path, mode: 0700) end end end end