lib/global_session/directory.rb in global_session-3.0.5 vs lib/global_session/directory.rb in global_session-3.1.0
- old
+ new
@@ -20,83 +20,84 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
require 'set'
module GlobalSession
- # The global session directory, which provides some lookup and decision services
+ # The global session directory, which provides lookup and decision services
# to instances of Session.
#
# The default implementation is simplistic, but should be suitable for most applications.
# Directory is designed to be specialized via subclassing. To override the behavior to
# suit your needs, simply create a subclass of Directory and add a configuration file
# setting to specify the class name of your implementation:
#
# common:
- # directory: MyCoolDirectory
+ # directory:
+ # class: MyCoolDirectory
#
+ # == Key Management
#
- # === The Authority Keystore
- # Directory uses a filesystem directory as a backing store for RSA
- # public keys of global session authorities. The directory should
- # contain one or more +*.pub+ files containing OpenSSH-format public
- # RSA keys. The name of the pub file determines the name of the
- # authority it represents.
+ # All key-related functionality has been delegated to the Keystore class as of
+ # v3.1. Directory retains its key management hooks for downrev compatibility,
+ # but mostly they are stubs for Keystore functionality.
#
- # === The Local Authority
- # Directory will infer the name of the local authority (if any) by
- # looking for a private-key file in the keystore. If a +*.key+ file
- # is found, then its name is taken to be the name of the local
- # authority and all GlobalSessions created will be signed by that
- # authority's private key.
+ # For more information about key mangement, please refer to the Keystore class.
#
- # If more than one key file is found, Directory will raise an error
- # at initialization time.
- #
class Directory
- attr_reader :configuration, :authorities, :private_key
+ # @return [Configuration] shared configuration object
+ attr_reader :configuration
+ # @return [Keystore] asymmetric crypto keys for signing authorities
+ attr_reader :keystore
+
# @return a representation of the object suitable for printing to the console
def inspect
"<#{self.class.name} @configuration=#{@configuration.inspect}>"
end
# Create a new Directory.
#
- # === Parameters
- # keystore_directory(String):: Absolute path to authority keystore
- #
- # === Raise
- # ConfigurationError:: if too many or too few keys are found, or if *.key/*.pub files are malformatted
- def initialize(configuration, keystore_directory)
+ # @param [Configuration] shared configuration
+ # @param optional [String] keystore_directory (DEPRECATED) if present, directory where keys can be found
+ # @raise [ConfigurationError] if too many or too few keys are found, or if *.key/*.pub files are malformatted
+ def initialize(configuration, keystore_directory=nil)
@configuration = configuration
- certs = Dir[File.join(keystore_directory, '*.pub')]
- keys = Dir[File.join(keystore_directory, '*.key')]
-
@authorities = {}
- certs.each do |cert_file|
- basename = File.basename(cert_file)
- authority = basename[0...(basename.rindex('.'))] #chop trailing .ext
- @authorities[authority] = OpenSSL::PKey::RSA.new(File.read(cert_file))
- raise ConfigurationError, "Expected #{basename} to contain an RSA public key" unless @authorities[authority].public?
- end
- if local_authority_name
- key_file = keys.detect { |kf| kf =~ /#{local_authority_name}.key$/ }
- raise ConfigurationError, "Key file #{local_authority_name}.key not found" unless key_file
- @private_key = OpenSSL::PKey::RSA.new(File.read(key_file))
- raise ConfigurationError, "Expected #{key_file} to contain an RSA private key" unless @private_key.private?
+ # Propagate a deprecated parameter
+ # @deprecated remove for v4.0
+ if keystore_directory.is_a?(String)
+ all_files = Dir.glob(File.join(keystore_directory, '*'))
+ public_keys = all_files.select { |kf| kf =~ /\.pub$/ }
+ raise ConfigurationError, "No public keys (*.pub) found in #{keystore_directory}" if public_keys.empty?
+
+ @configuration['common'] ||= {}
+ @configuration['common']['keystore'] ||= {}
+ @configuration['common']['keystore']['public'] = [keystore_directory]
+
+ # Propagate a deprecated configuration option
+ # @deprecated remove for v4.0
+ if (private_key = @configuration['authority'])
+ key_file = all_files.detect { |kf| kf =~ /#{private_key}\.key$/ }
+ raise ConfigurationError, "Key file #{private_key}.key not found in #{keystore_directory}" unless key_file
+ @configuration['common'] ||= {}
+ @configuration['common']['keystore'] ||= {}
+ @configuration['common']['keystore']['private'] = key_file
+ end
end
+ @keystore = Keystore.new(configuration)
@invalid_sessions = Set.new
end
# Create a new Session, initialized against this directory and ready to
# be used by the app.
#
# DEPRECATED: If a cookie is provided, load an existing session from its
# serialized form. You should use #load_session for this instead.
#
+ # @deprecated will be removed in GlobalSession v4; please use #load_session instead
# @see load_session
#
# === Parameters
# cookie(String):: DEPRECATED - Optional, serialized global session cookie. If none is supplied, a new session is created.
#
@@ -144,24 +145,51 @@
# SecurityError:: if signature is invalid or cookie is not signed by a trusted authority
def load_session(cookie)
Session.new(self, cookie)
end
+ # @return [Hash] map of String authority-names to OpenSSL::PKey public-keys
+ # @deprecated will be removed in GlobalSession v4; please use Keystore instead
+ # @see GlobalSession::Keystore
+ def authorities
+ @keystore.public_keys
+ end
+
+ # Determine the private key associated with this directory, to be used for signing.
+ #
+ # @return [nil,OpenSSL::PKey] local authority key if we are an authority, else nil
+ # @deprecated will be removed in GlobalSession v4; please use Keystore instead
+ # @see GlobalSession::Keystore
+ def private_key
+ @keystore.private_key || @private_key
+ end
+
+ # Determine the authority name associated with this directory's private session-signing key.
+ #
+ # @deprecated will be removed in GlobalSession v4; please use Keystore instead
+ # @see GlobalSession::Keystore
def local_authority_name
- @configuration['authority']
+ @keystore.private_key_name || @private_key_name
end
- # Determine whether this system trusts a particular authority based on
- # the trust settings specified in Configuration.
+ # Determine whether this system trusts a particular named authority based on
+ # the settings specified in Configuration and/or the presence of public key
+ # files on disk.
#
# === Parameters
# authority(String):: The name of the authority
#
# === Return
# trusted(true|false):: whether the local system trusts sessions signed by the specified authority
def trusted_authority?(authority)
- @configuration['trust'].include?(authority)
+ if @configuration.has_key?('trust')
+ # Explicit trust in just the authorities specified in the configuration
+ @configuration['trust'].include?(authority)
+ else
+ # Implicit trust in any public key we found on disk
+ @keystore.public_keys.keys.include?(authority)
+ end
end
# Determine whether the given session UUID is valid. The default implementation only considers
# a session to be invalid if its expired_at timestamp is in the past. Custom implementations
# might want to consider other factors, such as whether the user has signed out of this node
@@ -188,7 +216,7 @@
# === Return
# true:: Always returns true
def report_invalid_session(uuid, expired_at)
@invalid_sessions << uuid
end
- end
+ end
end