diff --git a/ACU/VCS/Git.pm b/ACU/VCS/Git.pm new file mode 100644 index 0000000..4426083 --- /dev/null +++ b/ACU/VCS/Git.pm @@ -0,0 +1,307 @@ +#! /usr/bin/env perl + +package Git; + +use v5.10.1; +use strict; +use warnings; +use File::Path; +use File::Temp; + +use ACU::LDAP; +use ACU::Log; +use ACU::API::Projects; + +our $git_user = "git"; +our $git_server; +our $git_adminrepo = "gitolite-admin.git"; + +our $configuration_directory = "/conf/"; +our $configuration_file = "subjects.conf"; +our $projects_directory = "subjects/"; +my $gitolite_directory; + +# General part + +sub init_conf(;$) +{ + $git_server = $_ if (shift); + + $gitolite_directory = mktemp("/tmp/git_manage_XXXX") unless(-d $gitolite_directory); + + log INFO, "Cloning $git_user\@$git_server:$git_adminrepo to $gitolite_directory"; + + system ("git clone $git_user\@$git_server:$git_adminrepo $gitolite_directory"); + + chdir($gitolite_directory); + + return $gitolite_directory; +} + +sub save_conf(;$) +{ + chdir($gitolite_directory); + + my $commit = shift; + system ("git commit -am '$commit'") if ($commit); + + log INFO, "Saving repositories configuration"; + + system ("git push"); + unlink ($gitolite_directory); + $gitolite_directory = undef; +} + + +# Auth part: give to user right on repository + +sub auth_add +{ + my $rgroup = shift; + my $rname = shift; + my $accesss = shift; + + init_conf() if (!$gitolite_directory); + + say " repo $rname"; + for my $access (@{ $accesss }) + { + say $access->gen_string("gitolite"); + #say " RW+ = \@admins \@$year-$project_name-$login"; + #say " RW+ = \@chefs \@resp-$year-$project_name"; + } + + +} + +sub auth_update +{ + init_conf() if (!$gitolite_directory); + +} + +sub auth_delete +{ + init_conf() if (!$gitolite_directory); + +} + +sub auth_save +{ + init_conf() if (!$gitolite_directory); + +} + + +# Repository part: manage repositories + +# Gitolite manage repositories only if there are associated with rights + +sub repository_add +{ +} + +sub repository_update +{ +} + +sub repository_delete +{ +} + +sub repository_group_add +{ + my $g_name = shift; #group_name + my $g_comp = shift; # complement, here respo rights + my $skip_save = shift // 0; + + if ($g_name !~ /^[a-zA-Z-_.]+$/) { + log ERROR, "Group name ($g_name) does not respect expected format ; skip add."; + return 0; + } + + init_conf() if (!$gitolite_directory); + + if (-f $gitolite_directory.$configuration_directory.$projects_directory."/".$g_name.".conf") { + log ERROR, "Cannot add new repository group: $g_name already exists!"; + return 0; + } + else { + open my $g_conf, ">", $gitolite_directory.$configuration_directory.$projects_directory."/".$g_name.".conf"; + say $g_conf $g_conf; + close $g_conf; + + open $g_conf, ">>", $gitolite_directory.$configuration_directory.$configuration_file; + say $g_conf "include \"$projects_directory/$g_name.conf\""; + close $g_conf; + } + + save_conf("Add repositories group $g_name") unless($skip_save); + + return 1; +} + +sub repository_group_delete +{ + my $g_name = shift; #group_name + my $skip_save = shift // 0; + + if ($g_name !~ /^[a-zA-Z-_.]+$/) { + log ERROR, "Group name ($g_name) does not respect expected format ; skip add."; + return 0; + } + + init_conf() if (!$gitolite_directory); + + my $configuration_path = $gitolite_directory.$configuration_directory.$configuration_file; + + if (-f $gitolite_directory.$configuration_directory.$projects_directory."/".$g_name.".conf") { + open my $g_conf, "<", $configuration_path; + my @contents = <$g_conf>; + close $g_conf; + + @contents = grep !/^include "\Q$projects_directory\/$g_name.conf\E"$/, @contents; #"; + + open $g_conf, '>', $configuration_path or die $!; + print $g_conf @contents; + close $g_conf; + + unlink($gitolite_directory.$configuration_directory.$projects_directory."/".$g_name.".conf"); + } + else { + log WARN, "Repository group $g_name not found."; + return 0; + } + + save_conf("Delete repositories group $g_name") unless($skip_save); + + return 1; +} + +sub repository_group_update +{ + my $g_name = shift; + + repository_group_delete($g_name, 1); + if (!repository_group_add($g_name, shift, 1)) { + log ERROR, "Unable to readd $g_name group repository. Configuration not saved."; + return 0; + } + +# ...; + auth_add(); + + save_conf("Delete repositories group $g_name") unless(shift); +} + +# User part: manage user authentication (password, keys, ...) + +sub user_add +{ + my $login = shift; + my $skip_save = shift // 0; + my $multiple = shift // 0; + + if (!$login or $login !~ /^(\*|[a-zA-Z0-9._-]+)$/) { + log WARN, "Login required in user_add"; + return 0; + } + + init_conf() if (!$gitolite_directory); + + # First, remove all user keys + user_delete($login, 1, $multiple); + + # Then, extract user keys + my @entries = LDAP::search_dns(undef, "ou=users", "&(uid=$login)(sshPublicKey=*)", [ "uid", "sshPublicKey" ]); + + if ($#entries > 1 && !$multiple) { log WARN, "Found multiple user $login, aborting keys update."; return 0; } + + for my $entry (@entries) + { + my $login = $entry->get_value("uid"); + if ($login) + { + my $i = 0; + my @keys = $entry->get_value("sshPublicKey"); + log INFO, "Updating ".($#keys+1)." keys for $login."; + for my $key (@keys) + { + chomp $key; + + mkdir $gitolite_directory."/keydir/$i" unless (-d $gitolite_directory."/keydir/$i"); + + open my $kf, ">", $gitolite_directory."/keydir/$i/$login.pub"; + print $kf $key; + close $kf; + + system("git add $gitolite_directory/keydir/$i/$login.pub"); + $i += 1; + } + } + } + + if ($multiple) { + save_conf("Update users keys from LDAP") unless ($skip_save); + } + else { + save_conf("Update $login keys from LDAP") unless ($skip_save); + } + + return 1; +} + +sub user_delete +{ + my $login = shift; + my $skip_save = shift // 0; + my $multiple = shift // 0; + + if (!$login) { + log WARN, "Login required in user_add"; + return 0; + } + + init_conf() if (!$gitolite_directory); + + opendir(my $dh, "$gitolite_directory/keydir/") || die "can't opendir keydir: $!"; + for my $f (readdir $dh) + { + if($multiple) + { + if ($f =~ /^[0-9]/ && -d "$gitolite_directory/keydir/$f") { + log INFO, "Removing $f directory"; + rmtree("$gitolite_directory/keydir/$f"); + } + } + else + { + if (-f "$gitolite_directory/keydir/$f/$login.pub") { + log INFO, "Removing $f/$login.pub"; + unlink("$gitolite_directory/keydir/$f/$login.pub"); + } + } + } + closedir $dh; + + save_conf("Remove $login keys") unless ($skip_save); + + return 1; +} + +sub user_update +{ + return user_add(@_); +} + +sub users_update +{ + return user_add("*", (shift // 0), 1); +} + +sub users_del +{ + return user_del("*", (shift // 0), 1); +} + +1;