rfa 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  1. #!/bin/sh
  2. # Echo help information
  3. usage()
  4. {
  5. cat <<EOF
  6. Usage: $0 [options] <command> <parameters>
  7. Tool for VCS repository administration.
  8. VCS support: $possible_vcs
  9. Options:
  10. -h, --help
  11. Print help message.
  12. -c <PATH>, --config=<PATH>
  13. Specify config file (default is /etc/repoforge/rfa.conf)
  14. -s <git/svn>, --vcs=<git/svn>
  15. Choose VCS type to create new repository (default is git)
  16. --git
  17. Same as "-c git"
  18. --svn
  19. Same as "-c svn"
  20. Commands:
  21. info <repository>
  22. Get information for specified repository.
  23. add <repository1> [repository2] ...
  24. Create new repository.
  25. del <repository1> [repository2] ...
  26. Delete existing repository.
  27. fixmod <repository1> [repository2] ...
  28. Fix access rights for repository.
  29. rename <old_name> <new_name>
  30. Rename repository.
  31. useradd <repository> <r/w/rw> <user1> [user2] ...
  32. Set access rights for user(s) to repository.
  33. userdel <repository> <r/w/rw> <user1> [user2] ...
  34. Remove access rights for user(s) to repository.
  35. userdel-all <repository> <r/w/rw>
  36. Remove access rights for all users for repository.
  37. usermod <user> [<restricted|interactive>]
  38. Deny/permit interactive user shell (git-shell/normal shell)
  39. EOF
  40. }
  41. group_add()
  42. {
  43. groupadd -K GID_MIN=${group_gid_min} "${group_r_prefix}$1" || return 1
  44. groupadd -K GID_MIN=${group_gid_min} "${group_w_prefix}$1" || return 1
  45. }
  46. group_del()
  47. {
  48. groupdel "${group_r_prefix}$1" || return 1
  49. groupdel "${group_w_prefix}$1" || return 1
  50. }
  51. group_rename()
  52. {
  53. local old_name="$1"
  54. local new_name="$2"
  55. groupmod -n "${group_r_prefix}$new_name" "${group_r_prefix}$old_name" || return 1
  56. groupmod -n "${group_w_prefix}$new_name" "${group_w_prefix}$old_name" || return 1
  57. return 0
  58. }
  59. get_group_members()
  60. {
  61. local group="$1"
  62. grep -e "^$group:" /etc/group | sed -e "s|^.*:||" -e "s|,| |g"
  63. }
  64. create_repo_svn()
  65. {
  66. svnadmin --fs-type=fsfs create "$1" || return 1
  67. }
  68. create_repo_git()
  69. {
  70. mkdir -p "$1" || return 1
  71. # git -C "$1" init --bare --shared >/dev/null || return 1
  72. cd "$1" || return 1
  73. git init --bare --shared >/dev/null || return 1
  74. cd - >/dev/null
  75. }
  76. data_add()
  77. {
  78. local r_name="$1"
  79. local r_root="$2"
  80. local r_link="$3"
  81. local repo_r="${r_root}/${r_name}"
  82. local repo_w="${r_root}/${r_name}/${r_name}"
  83. mkdir -p "${repo_r}"
  84. local saved_umask=`umask`
  85. umask 002
  86. local create_vcs_func="create_repo_$opts_vcs"
  87. $create_vcs_func "$repo_w" || return 1
  88. umask ${saved_umask}
  89. }
  90. data_fixmod()
  91. {
  92. local r_name="$1"
  93. local r_root="$2"
  94. local r_link="$3"
  95. local repo_r="${r_root}/${r_name}"
  96. local repo_w="${r_root}/${r_name}/${r_name}"
  97. chmod 750 "${repo_r}" || return 1
  98. chgrp "${group_r_prefix}${r_name}" "${repo_r}" || return 1
  99. chgrp -R "${group_w_prefix}${r_name}" "${repo_w}" || return 1
  100. local dirs=""
  101. dirs=`find "$repo_w" -type d`
  102. local dir=""
  103. for dir in $dirs; do
  104. chmod g+s "$dir" || return 1
  105. done
  106. chmod -R g+w "${repo_w}" || return 1
  107. }
  108. data_del()
  109. {
  110. local r_name="$1"
  111. local r_root="$2"
  112. rm -Rf "$r_root/$r_name"
  113. }
  114. link_add()
  115. {
  116. local r_name="$1"
  117. local r_root="$2"
  118. local r_link="$3"
  119. ln -sf "$r_root/$r_name/$r_name" "$r_link/$r_name"
  120. }
  121. link_del()
  122. {
  123. local r_name="$1"
  124. local r_link="$2"
  125. rm -f "$r_link/$r_name"
  126. }
  127. repository_add()
  128. {
  129. local rep_name=""
  130. local repo_root
  131. local repo_link
  132. for rep_name in "$@"; do
  133. repo_root=`find_repo_root "$rep_name"`
  134. repo_link=`find_repo_link "$rep_name"`
  135. test "x$repo_root" = "x" || { echo "Error: The repository \"$rep_name\" already exists." 1>&2; return 1; }
  136. eval repo_root="\$repository_${opts_vcs}_root"
  137. eval repo_link="\$repository_${opts_vcs}_link"
  138. test "x$repo_root" = "x" && { echo "Error: Illegal repository root \"\"" 1>&2; return 1; }
  139. test "x$repo_link" = "x" && { echo "Error: Illegal repository link \"\"" 1>&2; return 1; }
  140. test -d "$repo_root" || { echo "Error: Illegal repository root \"$repo_root\"" 1>&2; return 1; }
  141. test -d "$repo_link" || { echo "Error: Illegal repository link \"$repo_link\"" 1>&2; return 1; }
  142. group_add "$rep_name" || { echo "Error: Can't add group for repository \"$rep_name\"" 1>&2; return 1; }
  143. data_add "$rep_name" "$repo_root" "$repo_link" || { echo "Error: Can't add repository \"$rep_name\"" 1>&2; return 1; }
  144. data_fixmod "$rep_name" "$repo_root" "$repo_link" || { echo "Error: Can't fix mode for repository \"$rep_name\"" 1>&2; return 1; }
  145. link_add "$rep_name" "$repo_root" "$repo_link" || { echo "Error: Can't add link for repository \"$rep_name\"" 1>&2; return 1; }
  146. echo "Info: The repository \"$rep_name\" was succesfully created."
  147. done
  148. }
  149. # Delete the repository
  150. repository_del()
  151. {
  152. local rep_name=""
  153. local sure=""
  154. local repo_root
  155. local repo_link
  156. for rep_name in "$@"; do
  157. repo_root=`find_repo_root "$rep_name"`
  158. repo_link=`find_repo_link "$rep_name"`
  159. test "x$repo_root" = "x" -o "x$repo_link" = "x" && { echo "Error: Can't find repository \"$rep_name\"" 1>&2; return 1; }
  160. if test "x$opts_force" = "x"; then
  161. read -r -p "Deleting repository \"$rep_name\". Are you sure (y/n)? " sure
  162. test "x$sure" = "xy" -o "x$sure" = "xY" || { echo "Info: The repository \"$rep_name\" will be not deleted."; continue; }
  163. fi
  164. link_del "$rep_name" "$repo_link" || { echo "Error: Can't remove link for repository \"$rep_name\"" 1>&2; return 1; }
  165. data_del "$rep_name" "$repo_root" || { echo "Error: Can't remove repository \"$rep_name\"" 1>&2; return 1; }
  166. group_del "$rep_name" || { echo "Error: Can't remove group for repository \"$rep_name\"" 1>&2; return 1; }
  167. echo "Info: The repository \"$rep_name\" was succesfully deleted."
  168. done
  169. }
  170. # Fix broken repository: symlink, access rights, owner.
  171. repository_fixmod()
  172. {
  173. local rep_name=""
  174. local repo_root
  175. local repo_link
  176. for rep_name in "$@"; do
  177. repo_root=`find_repo_root "$rep_name"`
  178. repo_link=`find_repo_link "$rep_name"`
  179. test "x$repo_root" = "x" -o "x$repo_link" = "x" && { echo "Error: Can't find repository \"$rep_name\"" 1>&2; return 1; }
  180. data_fixmod "$rep_name" "$repo_root" "$repo_link" || { echo "Error: Can't fix repository's \"$rep_name\" mode" 1>&2; return 1; }
  181. link_add "$rep_name" "$repo_root" "$repo_link" || { echo "Error: Can't create link for repository \"$rep_name\"" 1>&2; return 1; }
  182. echo "Info: The repository \"$rep_name\" was succesfully fixed."
  183. done
  184. }
  185. # Rename existing repository
  186. repository_rename()
  187. {
  188. local old_name="$1"
  189. local new_name="$2"
  190. local repo_root
  191. local repo_link
  192. # Check if target name is already exists
  193. repo_root=`find_repo_root "$new_name"`
  194. test "x$repo_root" = "x" || { echo "Error: The repository \"$new_name\" is already exist" 1>&2; return 1; }
  195. repo_root=`find_repo_root "$old_name"`
  196. repo_link=`find_repo_link "$old_name"`
  197. test "x$repo_root" != "x" -a -d "$repo_root/$old_name" || { echo "Error: Can't find repository \"$old_name\"" 1>&2; return 1; }
  198. 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; }
  199. # Real move repository and link
  200. 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; }
  201. mv -f "$repo_root/$old_name" "$repo_root/$new_name" || { echo "Error: Can't move repository \"$old_name\"" 1>&2; return 1; }
  202. link_del "$old_name" "$repo_link" || { echo "Error: Can't remove link for repository \"$old_name\"" 1>&2; }
  203. link_add "$new_name" "$repo_root" "$repo_link" || { echo "Error: Can't create link for repository \"$new_name\"" 1>&2; }
  204. # Rename a groups
  205. group_rename "$old_name" "$new_name" || { echo "Error: Can't rename access groups for repository \"$new_name\"" 1>&2; }
  206. echo "Info: The repository \"$old_name\" was succesfully renamed to \"$new_name\""
  207. return 0
  208. }
  209. # Show repository info
  210. repository_info()
  211. {
  212. local repo="$1"
  213. local repo_root
  214. local repo_link
  215. repo_root=`find_repo_root "$repo"`
  216. repo_link=`find_repo_link "$repo"`
  217. test "x$repo_root" != "x" -a -d "$repo_root/$repo" || { echo "Error: Can't find repository \"$repo\"" 1>&2; return 1; }
  218. echo "Name: $repo"
  219. echo "VCS : "`find_repo_vcs "$repo"`
  220. echo "Path: $repo_root"
  221. echo "Link: $repo_link"
  222. echo "Write access: "`get_group_members "${group_w_prefix}$repo"`
  223. echo "Read access: "`get_group_members "${group_r_prefix}$repo"`
  224. return 0
  225. }
  226. # user restricted mode
  227. user_getshell()
  228. {
  229. local sh=`cat /etc/passwd | grep "^$1:" | cut -d ':' -f 7`
  230. echo $sh
  231. }
  232. user_interactive()
  233. {
  234. local user="$1"
  235. local git_shell="/home/$user/git-shell-commands"
  236. if [ ! -f "$git_shell/no-interactive-login" ]; then
  237. return 0
  238. fi
  239. local sh=`cat "$git_shell/no-interactive-login" | sed -e 's/^#[ \t]*//g'`
  240. if [ "x$sh" = "x" ]; then
  241. echo "Can not get user shell, using /bin/bash..." 1>&2
  242. sh="/bin/bash"
  243. fi
  244. chsh -s "$sh" "$1" || return 1
  245. rm -f "$git_shell/no-interactive-login" || return 1
  246. rmdir "$git_shell" >/dev/null 2>&1
  247. return 0
  248. }
  249. user_restricted()
  250. {
  251. local user="$1"
  252. local git_shell="/home/$user/git-shell-commands"
  253. if [ -f "$git_shell/no-interactive-login" ]; then
  254. return 0
  255. fi
  256. local sh=`user_getshell "$user"`
  257. if [ "x$sh" = "x" ]; then
  258. echo "Can not get user shell, using /bin/bash..." 1>&2
  259. sh="/bin/bash"
  260. fi
  261. chsh -s $(command -v git-shell) "$user" || return 1
  262. test -d "$git_shell" || mkdir "$git_shell"
  263. echo "# $sh" > "$git_shell/no-interactive-login" && chmod +x "$git_shell/no-interactive-login" && return 0
  264. return 1
  265. }
  266. user_showmod()
  267. {
  268. local user="$1"
  269. local git_shell="/home/$user/git-shell-commands"
  270. if [ -f "$git_shell/no-interactive-login" ]; then
  271. echo "restricted"
  272. else
  273. echo "interactive"
  274. fi
  275. }
  276. user_mod()
  277. {
  278. local user="$1"
  279. local mode="$2"
  280. if ! grep -q "^$user:" /etc/passwd; then
  281. echo "No such user." 1>&2
  282. return 1
  283. fi
  284. if test "x$mode" = "x"; then
  285. user_showmod "$user"
  286. return 0
  287. fi
  288. case "$mode" in
  289. restricted)
  290. if ! user_restricted "$user"; then
  291. echo "Error: can not set mode, rollback..." 1>&2
  292. user_interactive "$user"
  293. return 1
  294. fi
  295. ;;
  296. interactive)
  297. if ! user_interactive "$user"; then
  298. echo "Error: can not set mode, rollback..." 1>&2
  299. user_restricted "$user"
  300. return 1
  301. fi
  302. ;;
  303. *)
  304. echo "Error: Illegal mode \"$mode\" (restricted|interactive)" 1>&2
  305. return 1
  306. ;;
  307. esac
  308. return 0
  309. }
  310. # Add users to access groups
  311. user_add()
  312. {
  313. local w=0
  314. local r=0
  315. local repository_name="$1"
  316. local user_name=""
  317. shift
  318. case "$1" in
  319. r)
  320. r=1
  321. ;;
  322. w)
  323. w=1
  324. ;;
  325. rw|wr)
  326. r=1
  327. w=1
  328. ;;
  329. *)
  330. echo "Error: Illegal parameter \"$1\"" 1>&2
  331. return 1
  332. ;;
  333. esac
  334. shift
  335. for user_name in "$@"; do
  336. test $r -ne 0 && adduser "${user_name}" "${group_r_prefix}${repository_name}"
  337. test $w -ne 0 && adduser "${user_name}" "${group_w_prefix}${repository_name}"
  338. done
  339. }
  340. # Remove users from access groups
  341. user_del()
  342. {
  343. local w=0
  344. local r=0
  345. local repository_name=$1
  346. local user_name=""
  347. shift
  348. case "$1" in
  349. r)
  350. r=1
  351. ;;
  352. w)
  353. w=1
  354. ;;
  355. rw|wr)
  356. r=1
  357. w=1
  358. ;;
  359. *)
  360. echo "Error: Illegal parameter \"$1\"" 1>&2
  361. return 1
  362. ;;
  363. esac
  364. shift
  365. for user_name in "$@"; do
  366. test $r -ne 0 && deluser "${user_name}" "${group_r_prefix}${repository_name}"
  367. test $w -ne 0 && deluser "${user_name}" "${group_w_prefix}${repository_name}"
  368. done
  369. }
  370. # Remove all users from access groups
  371. user_del_all()
  372. {
  373. local w=0
  374. local r=0
  375. local repository_name=$1
  376. local user_name=""
  377. shift
  378. case "$1" in
  379. r)
  380. r=1
  381. ;;
  382. w)
  383. w=1
  384. ;;
  385. rw|wr)
  386. r=1
  387. w=1
  388. ;;
  389. *)
  390. echo "Error: Illegal parameter \"$1\"" 1>&2
  391. return 1
  392. ;;
  393. esac
  394. if test $r -ne 0; then
  395. user_name=`get_group_members "${group_r_prefix}${repository_name}"`
  396. test "x$user_name" != "x" && user_del "$repository_name" "r" $user_name
  397. fi
  398. if test $w -ne 0; then
  399. user_name=`get_group_members "${group_w_prefix}${repository_name}"`
  400. test "x$user_name" != "x" && user_del "$repository_name" "w" $user_name
  401. fi
  402. }
  403. # Find repository VCS
  404. find_repo_vcs()
  405. {
  406. local r=""
  407. local r_root=""
  408. for r in $possible_vcs; do
  409. eval r_root="\$repository_${r}_root"
  410. test "x$r_root" = "x" && continue
  411. test -d "$r_root/$1" || continue
  412. echo "$r"
  413. break
  414. done
  415. return 0
  416. }
  417. # Find repository root path by repository name
  418. find_repo_root()
  419. {
  420. local r=""
  421. local r_root=""
  422. r=`find_repo_vcs "$1"`
  423. eval r_root="\$repository_${r}_root"
  424. echo "$r_root"
  425. }
  426. # Find repository link path by repository name
  427. find_repo_link()
  428. {
  429. local r=""
  430. local r_link=""
  431. r=`find_repo_vcs "$1"`
  432. eval r_link="\$repository_${r}_link"
  433. echo "$r_link"
  434. }
  435. #------------------ MAIN -----------------------------------------
  436. possible_vcs="svn git"
  437. # Defaults for rfa.conf
  438. group_gid_min="3000"
  439. group_w_prefix="vcs-w-"
  440. group_r_prefix="vcs-r-"
  441. repository_root=
  442. repository_link=
  443. default_vcs="git"
  444. # Parse command line options
  445. action="help"
  446. opts_force=""
  447. opts_conf="/etc/repoforge/rfa.conf"
  448. opts_vcs="$default_vcs"
  449. while test "x$1" != "x"; do
  450. option="$1"
  451. case "$option" in
  452. -h|--help)
  453. usage
  454. exit 0
  455. ;;
  456. -f|--force)
  457. opts_force=1
  458. ;;
  459. # Config file
  460. -c)
  461. shift
  462. opts_conf="$1"
  463. ;;
  464. --config=*)
  465. opts_conf=`echo "$option" | sed 's/--config=//'`
  466. ;;
  467. # Choose VCS
  468. -s)
  469. shift
  470. opts_vcs="$1"
  471. ;;
  472. --vcs=*)
  473. opts_vcs=`echo "$option" | sed 's/--vcs=//'`
  474. ;;
  475. --git)
  476. opts_vcs="git"
  477. ;;
  478. --svn)
  479. opts_vcs="svn"
  480. ;;
  481. # Default
  482. *)
  483. action="$option"
  484. shift
  485. break
  486. ;;
  487. esac
  488. shift
  489. done
  490. # Early help message
  491. test "x$action" = "xhelp" && { usage; exit 0; }
  492. # Check options
  493. bad_vcs=1
  494. for v in $possible_vcs; do
  495. test "x$opts_vcs" = "x$v" && { bad_vcs=""; break; }
  496. done
  497. test "x$bad_vcs" = "x" || { echo "Error: Illegal VCS \"$opts_vcs\"" 1>&2; exit 1; }
  498. # Include config file
  499. test -r "$opts_conf" && . $opts_conf
  500. # Compatibility (suppose SVN)
  501. test "x$repository_svn_root" = "x" && repository_svn_root="$repository_root"
  502. test "x$repository_svn_link" = "x" && repository_svn_link="$repository_link"
  503. # Action
  504. case "$action" in
  505. "info")
  506. test $# -lt 1 && { echo "Error: Repository name is expected" 1>&2; exit 1; }
  507. repository_info "$@" || exit 1
  508. ;;
  509. "add")
  510. test $# -lt 1 && { echo "Error: Repository name is expected" 1>&2; exit 1; }
  511. repository_add "$@" || exit 1
  512. ;;
  513. "del")
  514. test $# -lt 1 && { echo "Error: Repository name is expected" 1>&2; exit 1; }
  515. repository_del "$@" || exit 1
  516. ;;
  517. "fixmod")
  518. test $# -lt 1 && { echo "Error: Repository name is expected" 1>&2; exit 1; }
  519. repository_fixmod "$@" || exit 1
  520. ;;
  521. "rename")
  522. test $# -lt 2 && { echo "Error: The old and new repository names are expected" 1>&2; exit 1; }
  523. repository_rename "$@" || exit 1
  524. ;;
  525. "adduser"|"useradd")
  526. test $# -lt 3 && { echo "Error: Not enough parameters" 1>&2; exit 1; }
  527. user_add "$@" || exit 1
  528. ;;
  529. "deluser"|"userdel")
  530. test $# -lt 3 && { echo "Error: Not enough parameters" 1>&2; exit 1; }
  531. user_del "$@" || exit 1
  532. ;;
  533. "deluser-all"|"userdel-all")
  534. test $# -lt 2 && { echo "Error: Not enough parameters" 1>&2; exit 1; }
  535. user_del_all "$@" || exit 1
  536. ;;
  537. "usermod")
  538. test $# -lt 1 && { echo "Error: Not enough parameters" 1>&2; exit 1; }
  539. user_mod "$@" || exit 1
  540. ;;
  541. *)
  542. echo "Error: Unknown command" 1>&2
  543. exit 1
  544. ;;
  545. esac
  546. exit $?