#!/bin/sh # Echo help information usage() { cat < Tool for VCS repository administration. VCS support: $possible_vcs Options: -h, --help Print help message. -c , --config= Specify config file (default is /etc/repoforge/rfa.conf) -s , --vcs= Choose VCS type to create new repository (default is git) --git Same as "-c git" --svn Same as "-c svn" Commands: info Get information for specified repository. add [repository2] ... Create new repository. del [repository2] ... Delete existing repository. fixmod [repository2] ... Fix access rights for repository. rename Rename repository. useradd [user2] ... Set access rights for user(s) to repository. userdel [user2] ... Remove access rights for user(s) to repository. userdel-all Remove access rights for all users for repository. usermod [] Deny/permit interactive user shell (git-shell/normal shell) EOF } group_add() { groupadd -K GID_MIN=${group_gid_min} "${group_r_prefix}$1" || return 1 groupadd -K GID_MIN=${group_gid_min} "${group_w_prefix}$1" || return 1 } group_del() { groupdel "${group_r_prefix}$1" || return 1 groupdel "${group_w_prefix}$1" || return 1 } group_rename() { local old_name="$1" local new_name="$2" groupmod -n "${group_r_prefix}$new_name" "${group_r_prefix}$old_name" || return 1 groupmod -n "${group_w_prefix}$new_name" "${group_w_prefix}$old_name" || return 1 return 0 } get_group_members() { local group="$1" grep -e "^$group:" /etc/group | sed -e "s|^.*:||" -e "s|,| |g" } create_repo_svn() { svnadmin --fs-type=fsfs create "$1" || return 1 } create_repo_git() { mkdir -p "$1" || return 1 # git -C "$1" init --bare --shared >/dev/null || return 1 cd "$1" || return 1 git init --bare --shared >/dev/null || return 1 cd - >/dev/null } data_add() { local r_name="$1" local r_root="$2" local r_link="$3" local repo_r="${r_root}/${r_name}" local repo_w="${r_root}/${r_name}/${r_name}" mkdir -p "${repo_r}" local saved_umask=`umask` umask 002 local create_vcs_func="create_repo_$opts_vcs" $create_vcs_func "$repo_w" || return 1 umask ${saved_umask} } data_fixmod() { local r_name="$1" local r_root="$2" local r_link="$3" local repo_r="${r_root}/${r_name}" local repo_w="${r_root}/${r_name}/${r_name}" chmod 750 "${repo_r}" || return 1 chgrp "${group_r_prefix}${r_name}" "${repo_r}" || return 1 chgrp -R "${group_w_prefix}${r_name}" "${repo_w}" || return 1 local dirs="" dirs=`find "$repo_w" -type d` local dir="" for dir in $dirs; do chmod g+s "$dir" || return 1 done chmod -R g+w "${repo_w}" || return 1 } data_del() { local r_name="$1" local r_root="$2" rm -Rf "$r_root/$r_name" } link_add() { local r_name="$1" local r_root="$2" local r_link="$3" ln -sf "$r_root/$r_name/$r_name" "$r_link/$r_name" } link_del() { local r_name="$1" local r_link="$2" rm -f "$r_link/$r_name" } repository_add() { local rep_name="" local repo_root local repo_link for rep_name in "$@"; do repo_root=`find_repo_root "$rep_name"` repo_link=`find_repo_link "$rep_name"` test "x$repo_root" = "x" || { echo "Error: The repository \"$rep_name\" already exists." 1>&2; return 1; } eval repo_root="\$repository_${opts_vcs}_root" eval repo_link="\$repository_${opts_vcs}_link" test "x$repo_root" = "x" && { echo "Error: Illegal repository root \"\"" 1>&2; return 1; } test "x$repo_link" = "x" && { echo "Error: Illegal repository link \"\"" 1>&2; return 1; } test -d "$repo_root" || { echo "Error: Illegal repository root \"$repo_root\"" 1>&2; return 1; } test -d "$repo_link" || { echo "Error: Illegal repository link \"$repo_link\"" 1>&2; return 1; } group_add "$rep_name" || { echo "Error: Can't add group for repository \"$rep_name\"" 1>&2; return 1; } data_add "$rep_name" "$repo_root" "$repo_link" || { echo "Error: Can't add repository \"$rep_name\"" 1>&2; return 1; } data_fixmod "$rep_name" "$repo_root" "$repo_link" || { echo "Error: Can't fix mode for repository \"$rep_name\"" 1>&2; return 1; } link_add "$rep_name" "$repo_root" "$repo_link" || { echo "Error: Can't add link for repository \"$rep_name\"" 1>&2; return 1; } echo "Info: The repository \"$rep_name\" was succesfully created." done } # Delete the repository repository_del() { local rep_name="" local sure="" local repo_root local repo_link for rep_name in "$@"; do repo_root=`find_repo_root "$rep_name"` repo_link=`find_repo_link "$rep_name"` test "x$repo_root" = "x" -o "x$repo_link" = "x" && { echo "Error: Can't find repository \"$rep_name\"" 1>&2; return 1; } if test "x$opts_force" = "x"; then read -r -p "Deleting repository \"$rep_name\". Are you sure (y/n)? " sure test "x$sure" = "xy" -o "x$sure" = "xY" || { echo "Info: The repository \"$rep_name\" will be not deleted."; continue; } fi link_del "$rep_name" "$repo_link" || { echo "Error: Can't remove link for repository \"$rep_name\"" 1>&2; return 1; } data_del "$rep_name" "$repo_root" || { echo "Error: Can't remove repository \"$rep_name\"" 1>&2; return 1; } group_del "$rep_name" || { echo "Error: Can't remove group for repository \"$rep_name\"" 1>&2; return 1; } echo "Info: The repository \"$rep_name\" was succesfully deleted." done } # Fix broken repository: symlink, access rights, owner. repository_fixmod() { local rep_name="" local repo_root local repo_link for rep_name in "$@"; do repo_root=`find_repo_root "$rep_name"` repo_link=`find_repo_link "$rep_name"` test "x$repo_root" = "x" -o "x$repo_link" = "x" && { echo "Error: Can't find repository \"$rep_name\"" 1>&2; return 1; } data_fixmod "$rep_name" "$repo_root" "$repo_link" || { echo "Error: Can't fix repository's \"$rep_name\" mode" 1>&2; return 1; } link_add "$rep_name" "$repo_root" "$repo_link" || { echo "Error: Can't create link for repository \"$rep_name\"" 1>&2; return 1; } echo "Info: The repository \"$rep_name\" was succesfully fixed." done } # Rename existing repository repository_rename() { local old_name="$1" local new_name="$2" local repo_root local repo_link # Check if target name is already exists repo_root=`find_repo_root "$new_name"` test "x$repo_root" = "x" || { echo "Error: The repository \"$new_name\" is already exist" 1>&2; return 1; } repo_root=`find_repo_root "$old_name"` repo_link=`find_repo_link "$old_name"` test "x$repo_root" != "x" -a -d "$repo_root/$old_name" || { echo "Error: Can't find repository \"$old_name\"" 1>&2; return 1; } test "x$repo_root" != "x" -a -L "$repo_link/$old_name" || { echo "Error: Can't find link of repository \"$old_name\"" 1>&2; return 1; } # Real move repository and link mv -f "$repo_root/$old_name/$old_name" "$repo_root/$old_name/$new_name" || { echo "Error: Can't move repository \"$old_name\"" 1>&2; return 1; } mv -f "$repo_root/$old_name" "$repo_root/$new_name" || { echo "Error: Can't move repository \"$old_name\"" 1>&2; return 1; } link_del "$old_name" "$repo_link" || { echo "Error: Can't remove link for repository \"$old_name\"" 1>&2; } link_add "$new_name" "$repo_root" "$repo_link" || { echo "Error: Can't create link for repository \"$new_name\"" 1>&2; } # Rename a groups group_rename "$old_name" "$new_name" || { echo "Error: Can't rename access groups for repository \"$new_name\"" 1>&2; } echo "Info: The repository \"$old_name\" was succesfully renamed to \"$new_name\"" return 0 } # Show repository info repository_info() { local repo="$1" local repo_root local repo_link repo_root=`find_repo_root "$repo"` repo_link=`find_repo_link "$repo"` test "x$repo_root" != "x" -a -d "$repo_root/$repo" || { echo "Error: Can't find repository \"$repo\"" 1>&2; return 1; } echo "Name: $repo" echo "VCS : "`find_repo_vcs "$repo"` echo "Path: $repo_root" echo "Link: $repo_link" echo "Write access: "`get_group_members "${group_w_prefix}$repo"` echo "Read access: "`get_group_members "${group_r_prefix}$repo"` return 0 } # user restricted mode user_getshell() { local sh=`cat /etc/passwd | grep "^$1:" | cut -d ':' -f 7` echo $sh } user_interactive() { local user="$1" local git_shell="/home/$user/git-shell-commands" if [ ! -f "$git_shell/no-interactive-login" ]; then return 0 fi local sh=`cat "$git_shell/no-interactive-login" | sed -e 's/^#[ \t]*//g'` if [ "x$sh" = "x" ]; then echo "Can not get user shell, using /bin/bash..." 1>&2 sh="/bin/bash" fi chsh -s "$sh" "$1" || return 1 rm -f "$git_shell/no-interactive-login" || return 1 rmdir "$git_shell" >/dev/null 2>&1 return 0 } user_restricted() { local user="$1" local git_shell="/home/$user/git-shell-commands" if [ -f "$git_shell/no-interactive-login" ]; then return 0 fi local sh=`user_getshell "$user"` if [ "x$sh" = "x" ]; then echo "Can not get user shell, using /bin/bash..." 1>&2 sh="/bin/bash" fi chsh -s $(command -v git-shell) "$user" || return 1 test -d "$git_shell" || mkdir "$git_shell" echo "# $sh" > "$git_shell/no-interactive-login" && chmod +x "$git_shell/no-interactive-login" && return 0 return 1 } user_showmod() { local user="$1" local git_shell="/home/$user/git-shell-commands" if [ -f "$git_shell/no-interactive-login" ]; then echo "restricted" else echo "interactive" fi } user_mod() { local user="$1" local mode="$2" if ! grep -q "^$user:" /etc/passwd; then echo "No such user." 1>&2 return 1 fi if test "x$mode" = "x"; then user_showmod "$user" return 0 fi case "$mode" in restricted) if ! user_restricted "$user"; then echo "Error: can not set mode, rollback..." 1>&2 user_interactive "$user" return 1 fi ;; interactive) if ! user_interactive "$user"; then echo "Error: can not set mode, rollback..." 1>&2 user_restricted "$user" return 1 fi ;; *) echo "Error: Illegal mode \"$mode\" (restricted|interactive)" 1>&2 return 1 ;; esac return 0 } # Add users to access groups user_add() { local w=0 local r=0 local repository_name="$1" local user_name="" shift case "$1" in r) r=1 ;; w) w=1 ;; rw|wr) r=1 w=1 ;; *) echo "Error: Illegal parameter \"$1\"" 1>&2 return 1 ;; esac shift for user_name in "$@"; do test $r -ne 0 && adduser "${user_name}" "${group_r_prefix}${repository_name}" test $w -ne 0 && adduser "${user_name}" "${group_w_prefix}${repository_name}" done } # Remove users from access groups user_del() { local w=0 local r=0 local repository_name=$1 local user_name="" shift case "$1" in r) r=1 ;; w) w=1 ;; rw|wr) r=1 w=1 ;; *) echo "Error: Illegal parameter \"$1\"" 1>&2 return 1 ;; esac shift for user_name in "$@"; do test $r -ne 0 && deluser "${user_name}" "${group_r_prefix}${repository_name}" test $w -ne 0 && deluser "${user_name}" "${group_w_prefix}${repository_name}" done } # Remove all users from access groups user_del_all() { local w=0 local r=0 local repository_name=$1 local user_name="" shift case "$1" in r) r=1 ;; w) w=1 ;; rw|wr) r=1 w=1 ;; *) echo "Error: Illegal parameter \"$1\"" 1>&2 return 1 ;; esac if test $r -ne 0; then user_name=`get_group_members "${group_r_prefix}${repository_name}"` test "x$user_name" != "x" && user_del "$repository_name" "r" $user_name fi if test $w -ne 0; then user_name=`get_group_members "${group_w_prefix}${repository_name}"` test "x$user_name" != "x" && user_del "$repository_name" "w" $user_name fi } # Find repository VCS find_repo_vcs() { local r="" local r_root="" for r in $possible_vcs; do eval r_root="\$repository_${r}_root" test "x$r_root" = "x" && continue test -d "$r_root/$1" || continue echo "$r" break done return 0 } # Find repository root path by repository name find_repo_root() { local r="" local r_root="" r=`find_repo_vcs "$1"` eval r_root="\$repository_${r}_root" echo "$r_root" } # Find repository link path by repository name find_repo_link() { local r="" local r_link="" r=`find_repo_vcs "$1"` eval r_link="\$repository_${r}_link" echo "$r_link" } #------------------ MAIN ----------------------------------------- possible_vcs="svn git" # Defaults for rfa.conf group_gid_min="3000" group_w_prefix="vcs-w-" group_r_prefix="vcs-r-" repository_root= repository_link= default_vcs="git" # Parse command line options action="help" opts_force="" opts_conf="/etc/repoforge/rfa.conf" opts_vcs="$default_vcs" while test "x$1" != "x"; do option="$1" case "$option" in -h|--help) usage exit 0 ;; -f|--force) opts_force=1 ;; # Config file -c) shift opts_conf="$1" ;; --config=*) opts_conf=`echo "$option" | sed 's/--config=//'` ;; # Choose VCS -s) shift opts_vcs="$1" ;; --vcs=*) opts_vcs=`echo "$option" | sed 's/--vcs=//'` ;; --git) opts_vcs="git" ;; --svn) opts_vcs="svn" ;; # Default *) action="$option" shift break ;; esac shift done # Early help message test "x$action" = "xhelp" && { usage; exit 0; } # Check options bad_vcs=1 for v in $possible_vcs; do test "x$opts_vcs" = "x$v" && { bad_vcs=""; break; } done test "x$bad_vcs" = "x" || { echo "Error: Illegal VCS \"$opts_vcs\"" 1>&2; exit 1; } # Include config file test -r "$opts_conf" && . $opts_conf # Compatibility (suppose SVN) test "x$repository_svn_root" = "x" && repository_svn_root="$repository_root" test "x$repository_svn_link" = "x" && repository_svn_link="$repository_link" # Action case "$action" in "info") test $# -lt 1 && { echo "Error: Repository name is expected" 1>&2; exit 1; } repository_info "$@" || exit 1 ;; "add") test $# -lt 1 && { echo "Error: Repository name is expected" 1>&2; exit 1; } repository_add "$@" || exit 1 ;; "del") test $# -lt 1 && { echo "Error: Repository name is expected" 1>&2; exit 1; } repository_del "$@" || exit 1 ;; "fixmod") test $# -lt 1 && { echo "Error: Repository name is expected" 1>&2; exit 1; } repository_fixmod "$@" || exit 1 ;; "rename") test $# -lt 2 && { echo "Error: The old and new repository names are expected" 1>&2; exit 1; } repository_rename "$@" || exit 1 ;; "adduser"|"useradd") test $# -lt 3 && { echo "Error: Not enough parameters" 1>&2; exit 1; } user_add "$@" || exit 1 ;; "deluser"|"userdel") test $# -lt 3 && { echo "Error: Not enough parameters" 1>&2; exit 1; } user_del "$@" || exit 1 ;; "deluser-all"|"userdel-all") test $# -lt 2 && { echo "Error: Not enough parameters" 1>&2; exit 1; } user_del_all "$@" || exit 1 ;; "usermod") test $# -lt 1 && { echo "Error: Not enough parameters" 1>&2; exit 1; } user_mod "$@" || exit 1 ;; *) echo "Error: Unknown command" 1>&2 exit 1 ;; esac exit $?