#!/usr/bin/env ruby require 'fileutils' require 'optparse' require 'yaml' $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))) require 'hubbard' FileUtils.mkdir_p(Hubbard::PROJECTS_PATH) FileUtils.mkdir_p(Hubbard::ACCOUNTS_PATH) formats = [:text, :yaml] defaults = { :format => formats.first } options = {} opts = OptionParser.new do |opts| opts.banner = < Projects: list-projects create-project delete-project Repositories: list-repositories create-repository delete-repository fork-repository list-forks Permissions: list-permissions add-permission read|write|admin remove-permission Options: BANNER opts.on("--private", "Create project with visibility set to private") do |o| options[:private] = o end opts.on("-f", "--format [FORMAT]", formats, "Output format (#{formats.join(', ')})") do |o| options[:format] = o end end def next_arg(msg) if ARGV.length < 1 $stderr.puts msg exit 1 end ARGV.shift end def check_status(msg) if $!.exitstatus != 0 $sderr.puts msg exit 1 end end def validate_project_name(name) if name !~ /#{Hubbard::PROJECT_REGEX}/ $stderr.put "Project names can only contain letter, numbers, and hyphens" exit 1 end end def validate_repository_name(name) if name !~ /#{Hubbard::REPOSITORY_REGEX}/ $stderr.put "Repository names can only contain letter, numbers, and hyphens" exit 1 end end def validate_user_name(name) if name !~ /#{Hubbard::USERNAME_REGEX}/ $stderr.put "User names can only contain letter, numbers, and hyphens" exit 1 end end def validate_action_name(action) unless Hubbard::ACTIONS.member?(action) $stderr.put "Not a valid action (must be one of: read, write, admin)" exit 1 end end username = next_arg "Please specify the username to run as" if ENV['SSH_ORIGINAL_COMMAND'] ARGV.clear ENV['SSH_ORIGINAL_COMMAND'].split.each do |arg| ARGV << arg end end opts.parse! OPTIONS = defaults.merge(options) OPTIONS.freeze if ARGV.empty? || ARGV[0] == 'help' puts opts exit 0 end command = next_arg "Please specify a command to run" if command == "run-as" if username != "admin" $stderr.puts "You don't have permission to do that" exit 1 end username = next_arg "Please specify the username to run as" command = next_arg "Please specify a command to run" end @username = username def implies(a1, a2) case a1 when 'admin' true when 'write' a2 != 'admin' when 'read' a2 == 'read' else raise "Unknown action type: *#{a1}*" end end def authorize(project_name, action) unless is_authorized(project_name, action) $stderr.puts "You don't have permission to do that" exit 3 end end def is_authorized(project_name, action) project_dir = find_project_dir(project_name) return false unless File.exist?(project_dir) return true if @username == 'admin' Dir.chdir(project_dir) do if action == 'read' && File.read('.visibility').strip == 'public' return true end return false unless File.exist?(".permissions") File.read(".permissions").split("\n").each do |line| permission = line.strip.split('=') line_username = permission[0] line_action = permission[1] if line_username == @username && implies(line_action, action) return true end end false end end def find_account_dir(user_name) File.join(Hubbard::ACCOUNTS_PATH, user_name) end def find_project_dir(project_name) File.join(Hubbard::PROJECTS_PATH, project_name) end def find_repository_dir(project_name, repository_dir) File.join(find_project_dir(project_name), repository_dir + '.git') end def read_project_name project_name = next_arg("Please specify a project name") validate_project_name(project_name) project_name end def read_repository_name repository_name = next_arg("Please specify a repository name") validate_repository_name(repository_name) repository_name end def read_user_name user_name = next_arg("Please specify a username") validate_user_name(user_name) user_name end def sync_keys File.open(File.expand_path("~/.ssh/authorized_keys"), "w") do |file| Dir.entries(Hubbard::ACCOUNTS_PATH).each do |account| next if account == '.' || account == '..' key_dir = File.join(Hubbard::ACCOUNTS_PATH, account, "keys") Dir.entries(key_dir).each do |name| next if name == '.' || name == '..' key = File.read(File.join(key_dir, name)) file << %Q~command="hubbard #{account}",no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-pty #{key} #{name}\n~ end end end end command_file = File.expand_path(File.join(File.dirname(__FILE__), "..", "commands", "#{command}.rb")) if File.exist?(command_file) begin load command_file rescue SystemCallError => e $stderr.puts "SystemCallError [#{e.errno}]: #{e.message}" exit e.errno end else $stderr.puts "Unknown command: #{command}" exit 1 end