emacs-collab/collab.rb
2017-06-03 01:32:23 +02:00

325 lines
6.6 KiB
Ruby

require 'fileutils'
POSIX_NAME_PREFIX = "emacs"
USER_LOCATION = "./users/"
$projects = []
$users = []
$project_id_map = {}
$user_id_map = {}
#####################
# User manipulation #
#####################
class User
attr_accessor :id, :keypath
attr_reader :keys
def to_s
"#{@id}"
end
def initialize(id)
@id = id
@keypath = "#{USER_LOCATION}#{id}"
refresh_keys
end
def add_key_from_string(keystring)
unless valid_pubkey? keystring
raise InvalidSSHPubKey, "Public key not valid"
end
ks = keystring.split(" ");
key = SSHKey.new(ks[0][4..6], ks[1], ks[2])
unless @keys.include? key
@keys << key
else
raise DuplicateSSHPubKey, "Public key is already added to user"
end
flush
refresh_keys
flush_projects_with_user(self)
end
def remove_key(key)
@keys.delete(key)
flush
refresh_keys
flush_projects_with_user(self)
end
def refresh_keys
File.open(@keypath,"r") do |f|
@keys = f.read.split("\n").select{ |line| line[0..2] == "ssh" }.map{ |ks| k = ks.split(" "); SSHKey.new(k[0][4..6], k[1], k[2]) }
end
end
def flush
File.open(@keypath,"w+") do |key_file|
@keys.each do |key|
key_file << "#{key.to_s}\n"
end
end
end
end
def add_user(id)
user = User.new(id)
$users << user
$user_id_map[id] = user
end
def get_user_by_id(id)
$user_id_map[id]
end
def validate_password
end
########################
# Project manipulation #
########################
class Project
attr_accessor :id, :path, :posixname, :users
attr_reader :keys
def initialize
@users = []
end
def to_s
"#{@id}, #{@path}"
end
def add_key(key)
@keys << key
end
def add_user(user)
@users << user
user.keys.each do |key|
p key
add_key(key)
end
flush
refresh
end
def remove_user(user)
@user.delete(user)
flush
refresh
end
def refresh
@users = File.open("#{@path}/.ssh/users","r") do |f|
f.read.split("\n").map{|id| p id; puts $user_id_map; get_user_by_id(id)}
end
p @users
@keys = extract_ssh_pubkeys("#{@path}/.ssh/authorized_keys")
end
def flush
File.open("#{@path}/.ssh/users","w+") do |user_file|
@users.each do |u|
user_file << "#{u.id}\n"
end
end
get_ssh_keyfile(self,"w+") do |key_file|
@users.each do |user|
user.keys.each do |key|
key_file << "#{key.to_s}\n"
end
end
end
end
end
def get_project(id)
$project_id_map[id]
end
def add_project(project)
$projects << project
$project_id_map[project.id] = project
end
def create_project(id)
if not system("useradd -m #{POSIX_NAME_PREFIX}#{id}") then
raise "Project creation failed"
end
project = Project.new
project.id = id
project.path = "/home/#{POSIX_NAME_PREFIX}#{id}"
project.posixname = "#{POSIX_NAME_PREFIX}#{id}"
$projects << project
$project_id_map[id] = project
FileUtils.mkdir "#{project.path}/.ssh"
FileUtils.touch "#{project.path}/.ssh/authorized_keys"
FileUtils.touch "#{project.path}/.ssh/users"
FileUtils.chown_R project.id, project.id, "#{project.path}/.ssh"
project
end
def remove_project(project)
system("userdel -r #{project.posixname}")
$projects.remove(project)
$project_id_map[id] = nil
end
def project_by_posix_name(posix_name)
project = Project.new
project.id = posix_name[POSIX_NAME_PREFIX.length..-1]
project.path = "/home/#{posix_name}"
project.posixname = posix_name
project
end
####################
# Key manipulation #
####################
class SSHKey
attr_accessor :cipher, :pubkey, :comment
def initialize(cipher,pubkey,comment="")
@cipher = cipher
@pubkey = pubkey
@comment = comment
end
def ==(o)
o.class == self.class && o.attrs == attrs
end
def pretty
"#{@cipher} #{@comment}"
end
def to_s
"ssh-#{@cipher} #{@pubkey} #{@comment}"
end
def attrs
[@cipher, @pubkey, @comment]
end
end
class InvalidSSHPubKey < StandardError
end
class DuplicateSSHPubKey < StandardError
end
def get_ssh_keyfile(project,mode,&block)
file = File.open("#{project.path}/.ssh/authorized_keys",mode)
return file unless block_given?
yield(file)
ensure
file.close
end
def get_ssh_pubkeys(project)
extract_ssh_pubkeys("#{project.path}/.ssh/authorized_keys")
end
# Deprecate
def add_ssh_pubkey(project,key)
unless valid_pubkey? key
raise InvalidSSHPubKey, "Public key not valid"
end
get_ssh_keyfile(project,"a") do |key_file|
key_file << "#{key.to_s}\n"
end
end
def reset_pubkeys(project)
FileUtils.rm "#{project.path}/.ssh/authorized_keys"
FileUtils.touch "#{project.path}/.ssh/authorized_keys"
FileUtils.chown_R project.id, project.id, "#{project.path}/.ssh"
end
def remove_ssh_pubkey(project,key)
keys = get_ssh_pubkeys(project)
reset_pubkeys(project)
keys.each{ |k| unless k == key then add_ssh_pubkey(project,k) end}
end
def remove_ssh_pubkey_by_key
end
def remove_ssh_pubkey_by_comment
end
def valid_pubkey?(key)
IO.popen("ssh-keygen -qlf -","r+") do |io|
io.write key.to_s
io.close_write
io.read
end
if $?.to_i == 0
true
else
false
end
end
def extract_ssh_pubkeys(file)
File.open(file,"r") do |f|
f.read.split("\n").select{ |line| line[0..2] == "ssh" }.map{ |ks| k = ks.split(" "); SSHKey.new(k[0][4..6], k[1], k[2]) }
end
end
################
###########
# Project DB #
###########
def reload_projects_from_passwd
$projects = []
`cut -d: -f1 /etc/passwd`.split("\n").
select{|u| u.start_with? POSIX_NAME_PREFIX}.
map{|u| project_by_posix_name(u)}.
each{|u| add_project(u)}
$projects.each{|p| p.refresh}
end
def reload_users_from_dir
`ls -1 #{USER_LOCATION}`.split("\n").
map{|u| User.new(u)}.
each{|u| add_user(u.id)}
end
def flush_all_to_disk
$users.each {|u| u.flush}
$projects.each {|p| p.flush}
end
def flush_projects_with_user(user)
$projects.select{|p| p.users.include? user}.each do |p|
p.flush
end
end
################################################
reload_users_from_dir
reload_projects_from_passwd
#remove_project 5
#create_project 3
#add_ssh_pubkey(get_project(3), "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7ZKN2fKcUQUaQqDjNCQQBSdqBVGX7lprNCJmceDBfybhbnuZJ8KvzJJIJIIbzqheW5BVCfkWJY6OgkpAumLWSRCS5n2+AnDHwQgpKDS93OeV+9/kattVtsVUBZaghymyJ2UfA0r918dkxcT9SZbNSl9raiDUUmj3JY8UM219BQP7BRqoZ6e/YZz9lO7ORy6yQT6fIMaVOaZcoDPr6oyNJfadm9POvS/Wl63onoRI9dzpHQG9RuHCcUhHJhkGtzY7GeRWc85WqA9Q4vYo0SK5Je9BG1cvAAVTfV+eYEJEiSDMwWj60roH0C3/ipmzxD/kWqg6YBJWL+XAyQkDnmbuD christoffermadsen@strawberry.thedevcave.net")