lib/gitolite/ssh_key.rb in gitolite-rugged-1.2.pre.devel vs lib/gitolite/ssh_key.rb in gitolite-rugged-1.2.1.pre.devel
- old
+ new
@@ -1,36 +1,35 @@
+require 'fileutils'
module Gitolite
# Models an SSH key within gitolite
# provides support for multikeys
#
# Types of multi keys:
- # bob.pub => username: bob
- # bob@desktop.pub => username: bob, location: desktop
- # bob@email.com.pub => username: bob@email.com
- # bob@email.com@desktop.pub => username: bob@email.com, location: desktop
+ # username: bob => <keydir>/bob/bob.pub
+ # username: bob, location: desktop => <keydir>/bob/desktop/bob.pub
class SSHKey
attr_accessor :owner, :location, :type, :blob, :email
class << self
def from_file(key)
raise "#{key} does not exist!" unless File.exists?(key)
- # TODO this is old-style locations, use folders instead.
- # Get our owner and location
- File.basename(key) =~ /^([\+\w\.-]+(?:@(?:[\w-]+\.)+\D{2,4})?)(?:@([\w-]+))?.pub$/i
- owner = $1
- location = $2 || ""
+ # Owner is the basename of the key
+ # i.e., <owner>/<location>/<owner>.pub
+ owner = File.basename(key, ".pub")
+ # Location is the middle section of the path, if any
+ location = self.location_from_path(File.dirname(key), owner)
+
# Use string key constructor
self.from_string(File.read(key), owner, location)
end
-
# Construct a SSHKey from a string
def from_string(key_string, owner, location = "")
if owner.nil?
raise ArgumentError, "owner was nil, you must specify an owner"
end
@@ -49,14 +48,62 @@
end
self.new(type, blob, email, owner, location)
end
+ # Parse the key path above the key to be read.
+ # As we can omit the location, there are two possible options:
+ #
+ # 1. Location is empty. Path is <keydir>/<owner>/
+ # 2. Location is non-empty. Path is <keydir>/<owner>/<location>
+ #
+ # We test this by checking the parent of the given path.
+ # If it equals owner, a location was set.
+ # This allows the daft case of e.g., using <keydir>/bob/bob/bob.pub.
+ def location_from_path(path, owner)
+ keyroot = File.dirname(path)
+ if File.basename(keyroot) == owner
+ File.basename(path)
+ else
+ ''
+ end
+ end
+
+ def delete_dir_if_empty(dir)
+ if File.directory?(dir) && Dir["#{dir}/*"].empty?
+ Dir.rmdir(dir)
+ end
+ rescue => e
+ STDERR.puts("Warning: Couldn't delete empty directory: #{e.message}")
+ end
+
+ # Remove a key given a relative path
+ #
+ # Unlinks the key file and removes any empty parent directory
+ # below key_dir
+ def remove(key_file, key_dir_path)
+
+ abs_key_path = File.join(key_dir_path, key_file)
+ key = self.from_file(abs_key_path)
+
+ # Remove the file itself
+ File.unlink(abs_key_path)
+
+ key_owner_dir = File.join(key_dir_path, key.owner)
+
+ # Remove the location, if it exists and is empty
+ if key.location
+ self.delete_dir_if_empty(File.join(key_owner_dir, key.location))
+ end
+
+ # Remove the owner dir, if empty
+ self.delete_dir_if_empty(key_owner_dir)
+ end
end
- def initialize(type, blob, email, owner = nil, location = "")
+ def initialize(type, blob, email, owner=nil, location = "")
@type = type
@blob = blob
@email = email
@owner = owner || email
@@ -68,23 +115,31 @@
[@type, @blob, @email].join(' ')
end
def to_file(path)
- key_file = File.join(path, self.filename)
+ # Ensure multi-key directory structure
+ # <keydir>/<owner>/<location?>/<owner>.pub
+ key_dir = File.join(path, @owner, @location)
+ key_file = File.join(key_dir, self.filename)
+
+ # Ensure subdirs exist
+ FileUtils.mkdir_p(key_dir) unless File.directory?(key_dir)
+
File.open(key_file, "w") do |f|
f.sync = true
f.write(self.to_s)
end
key_file
end
+ def relative_path
+ File.join(@owner, @location, self.filename)
+ end
def filename
- file = @owner
- file += "@#{@location}" unless @location.empty?
- file += ".pub"
+ [@owner, '.pub'].join
end
def ==(key)
@type == key.type &&
@@ -96,8 +151,7 @@
def hash
[@owner, @location, @type, @blob, @email].hash
end
-
end
end