git_wit 0.0.3 → 0.0.4.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (195) hide show
  1. data/README.md +105 -157
  2. data/app/controllers/git_wit/git_controller.rb +37 -23
  3. data/bin/git_wit +4 -0
  4. data/config/routes.rb +2 -2
  5. data/lib/generators/git_wit/install/install_generator.rb +42 -11
  6. data/lib/generators/git_wit/{templates → install/templates}/README +0 -0
  7. data/lib/generators/git_wit/{templates → install/templates}/git_wit.rb +21 -11
  8. data/lib/generators/git_wit/ssh_user/USAGE +11 -0
  9. data/lib/generators/git_wit/ssh_user/ssh_user_generator.rb +53 -0
  10. data/lib/generators/git_wit/ssh_user/templates/bashrc.tt +7 -0
  11. data/lib/generators/git_wit/ssh_user/templates/sudoers.tt +12 -0
  12. data/lib/git_wit.rb +19 -4
  13. data/lib/git_wit/actions.rb +17 -0
  14. data/lib/git_wit/actions/dscl.rb +15 -0
  15. data/lib/git_wit/actions/dscl/base.rb +75 -0
  16. data/lib/git_wit/actions/dscl/group.rb +20 -0
  17. data/lib/git_wit/actions/dscl/group_membership.rb +30 -0
  18. data/lib/git_wit/actions/dscl/user.rb +39 -0
  19. data/lib/git_wit/actions/ssh.rb +11 -0
  20. data/lib/git_wit/actions/ssh/home.rb +55 -0
  21. data/lib/git_wit/actions/ssh/sudoers.rb +94 -0
  22. data/lib/git_wit/auth.rb +2 -2
  23. data/lib/git_wit/authorized_keys.rb +45 -88
  24. data/lib/git_wit/authorized_keys/file.rb +61 -0
  25. data/lib/git_wit/authorized_keys/key.rb +15 -0
  26. data/lib/git_wit/cli.rb +19 -0
  27. data/lib/git_wit/commands/debug.rb +21 -0
  28. data/lib/git_wit/commands/git_shell.rb +48 -0
  29. data/lib/git_wit/commands/util.rb +37 -0
  30. data/lib/git_wit/errors.rb +1 -0
  31. data/lib/git_wit/version.rb +2 -1
  32. data/lib/tasks/git_wit.rake +46 -0
  33. data/test/dummy/bin/coderay +16 -0
  34. data/test/dummy/bin/erubis +16 -0
  35. data/test/dummy/bin/git_wit +16 -0
  36. data/test/dummy/bin/htmldiff +16 -0
  37. data/test/dummy/bin/ldiff +16 -0
  38. data/test/dummy/bin/posix-spawn-benchmark +16 -0
  39. data/test/dummy/bin/pry +16 -0
  40. data/test/dummy/bin/rackup +16 -0
  41. data/test/dummy/bin/rails +16 -0
  42. data/test/dummy/bin/rake +16 -0
  43. data/test/dummy/bin/rake2thor +16 -0
  44. data/test/dummy/bin/rdoc +16 -0
  45. data/test/dummy/bin/ri +16 -0
  46. data/test/dummy/bin/sprockets +16 -0
  47. data/test/dummy/bin/thor +16 -0
  48. data/test/dummy/bin/tilt +16 -0
  49. data/test/dummy/bin/tt +16 -0
  50. data/test/dummy/bin/tunnels +16 -0
  51. data/test/dummy/config/initializers/git_wit.rb +33 -24
  52. data/test/dummy/db/development.sqlite3 +0 -0
  53. data/test/dummy/db/test.sqlite3 +0 -0
  54. data/test/dummy/log/development.log +1639 -5391
  55. data/test/dummy/log/test.log +108 -2
  56. data/test/dummy/tmp/pids/server.pid +1 -0
  57. data/test/unit/auth_test.rb +10 -6
  58. data/test/unit/authorized_keys_test.rb +5 -5
  59. data/test/unit/config_test.rb +15 -11
  60. data/test/unit/shell_test.rb +5 -5
  61. metadata +84 -278
  62. data/bin/gw-shell +0 -4
  63. data/lib/tasks/git_wit_shell.rake +0 -8
  64. data/test/dummy/tmp/cache/assets/C7E/BC0/sprockets%2Fb7118f368364962573a44054bcfb80d0 +0 -0
  65. data/test/dummy/tmp/cache/assets/C80/840/sprockets%2F562c2d168da585f80579347d10790a0a +0 -0
  66. data/test/dummy/tmp/cache/assets/C8C/B80/sprockets%2F371bf96e99717688ed7313a0c53f4212 +0 -0
  67. data/test/dummy/tmp/cache/assets/C9C/700/sprockets%2Fc7b1373dbf219a8722efc21160641340 +0 -0
  68. data/test/dummy/tmp/cache/assets/C9E/5F0/sprockets%2F2bca2b107bb6c26b720d135270688918 +0 -0
  69. data/test/dummy/tmp/cache/assets/CA9/9C0/sprockets%2F0c1b7ebd087418498ea6037225d33d25 +0 -0
  70. data/test/dummy/tmp/cache/assets/CC8/B00/sprockets%2F9815364bfd49ed907870e270d75a995a +0 -0
  71. data/test/dummy/tmp/cache/assets/CD1/800/sprockets%2Fc044b140dcef533c52712c7b51e21996 +0 -0
  72. data/test/dummy/tmp/cache/assets/CD5/2C0/sprockets%2F166c056119ebdfb8b7104c97b424b423 +0 -0
  73. data/test/dummy/tmp/cache/assets/CD7/380/sprockets%2F4079ce1dbbcf4a599527303670006b6b +0 -0
  74. data/test/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
  75. data/test/dummy/tmp/cache/assets/CE0/CC0/sprockets%2F2b38c3fb549036de5c4666637a0c80c6 +0 -0
  76. data/test/dummy/tmp/cache/assets/CEC/B70/sprockets%2F7f98753ca8c35e4249363a04389b3caf +0 -0
  77. data/test/dummy/tmp/cache/assets/CF0/1D0/sprockets%2F6fc757c2c8329244ca95d6909865bbc2 +0 -0
  78. data/test/dummy/tmp/cache/assets/CF9/980/sprockets%2Fbd55042e1acd32eb611041444d794d4d +0 -0
  79. data/test/dummy/tmp/cache/assets/CFD/560/sprockets%2Fe4e7fe4ee089382325686f806b939d3e +0 -0
  80. data/test/dummy/tmp/cache/assets/D00/B90/sprockets%2F07c00c80f1ea62d95a01f11f9c728b72 +0 -0
  81. data/test/dummy/tmp/cache/assets/D0D/9A0/sprockets%2F1fce44192cdb30f44b7545a37c9891b2 +0 -0
  82. data/test/dummy/tmp/cache/assets/D0F/390/sprockets%2F9df081609c3449ab40c93b1cf07de357 +0 -0
  83. data/test/dummy/tmp/cache/assets/D25/A60/sprockets%2F0e036061ad22b2e6dce1639b234cf15c +0 -0
  84. data/test/dummy/tmp/cache/assets/D27/DB0/sprockets%2Fa3a0a778855bce9fa47913d389ea9884 +0 -0
  85. data/test/dummy/tmp/cache/assets/D29/5A0/sprockets%2Fdad9e81b43ca12671246ee52a1900d0c +0 -0
  86. data/test/dummy/tmp/cache/assets/D2E/FF0/sprockets%2Fc06112642c994b6b7c2ba6632fad1fd0 +0 -0
  87. data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
  88. data/test/dummy/tmp/cache/assets/D36/120/sprockets%2Feac54bd3c540af6b964d025e345227d6 +0 -0
  89. data/test/dummy/tmp/cache/assets/D3E/240/sprockets%2F84b96d6b2d2653cb4127b06d8acb990d +0 -0
  90. data/test/dummy/tmp/cache/assets/D3F/830/sprockets%2F18578d4ef3abd6e12d836841dd6b8a00 +0 -0
  91. data/test/dummy/tmp/cache/assets/D4B/E70/sprockets%2F0909bc70e589d40a6cd90dfe6f18257e +0 -0
  92. data/test/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
  93. data/test/dummy/tmp/cache/assets/D57/3D0/sprockets%2F7bbccc5129a5013b70831ec9ad62ab24 +0 -0
  94. data/test/dummy/tmp/cache/assets/D5A/000/sprockets%2F7d4f67f146b6d7904dfc4edd9135f588 +0 -0
  95. data/test/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
  96. data/test/dummy/tmp/cache/assets/D5B/BB0/sprockets%2Fba769276c4de14151bc4202cba7f3ad3 +0 -0
  97. data/test/dummy/tmp/cache/assets/D5E/BC0/sprockets%2F2d96fa667066778db858d7b7cb0fce69 +0 -0
  98. data/test/dummy/tmp/cache/assets/D6E/BA0/sprockets%2F5178d3788fe35a52acb5f3bd22ea078a +0 -0
  99. data/test/dummy/tmp/cache/assets/D6F/C20/sprockets%2F22e783a8f5f9224f01e8e62fab6afb40 +0 -0
  100. data/test/dummy/tmp/cache/assets/D76/5C0/sprockets%2Fd8a5669df31f129f355283e6dab4c5ad +0 -0
  101. data/test/dummy/tmp/cache/assets/D79/DE0/sprockets%2F7cfd335e68d881b03f6b7f1bd91f270f +0 -0
  102. data/test/dummy/tmp/cache/assets/D8A/CA0/sprockets%2F656af8b87ad378e8e4f2ec94b6b5c719 +0 -0
  103. data/test/dummy/tmp/cache/assets/D8C/620/sprockets%2Ff37f8e5b8cccd9880276a9f5157d4c7e +0 -0
  104. data/test/dummy/tmp/cache/assets/D92/200/sprockets%2Fb816d858281027bdd3fe2bfac43b4ca1 +0 -0
  105. data/test/dummy/tmp/cache/assets/D92/CE0/sprockets%2Ffca6a13676a2be09234905f9acae22cd +0 -0
  106. data/test/dummy/tmp/cache/assets/D96/9E0/sprockets%2F6fabecd33f7a5a087f4fb6a2d6312443 +0 -0
  107. data/test/dummy/tmp/cache/assets/DA2/D20/sprockets%2Ff5faf079fb660bde5bc9502bde442088 +0 -0
  108. data/test/dummy/tmp/cache/assets/DA5/570/sprockets%2F3517de599b6fd005bc5d5d69ba5ff1e2 +0 -0
  109. data/test/dummy/tmp/cache/assets/DA7/070/sprockets%2F69eadf8c3a94b04fe0b4992ee4a8c821 +0 -0
  110. data/test/dummy/tmp/cache/assets/DBA/BF0/sprockets%2Fe63ea1d7bfb0ee50380debe42360a3b5 +0 -0
  111. data/test/dummy/tmp/cache/assets/DBB/3B0/sprockets%2F6a0aaa6c5b0d10b936e237a7ecb4e4c9 +0 -0
  112. data/test/dummy/tmp/cache/assets/DBC/8E0/sprockets%2F908976cfbcdf6ad4c59737bf3c78e1e8 +0 -0
  113. data/test/dummy/tmp/cache/assets/DD3/FD0/sprockets%2Febf97c76a9ba2a889dd01be2caa75806 +0 -0
  114. data/test/dummy/tmp/cache/assets/DD8/410/sprockets%2Fc02eeb7ea977fd713cc19ca93d838af4 +0 -0
  115. data/test/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
  116. data/test/dummy/tmp/cache/assets/DEA/E40/sprockets%2F4166d7d00d1e72fed2004debed2bea3e +0 -0
  117. data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
  118. data/test/dummy/tmp/cache/assets/E05/C70/sprockets%2Fccd814ec859d582ada46e271edfe7ae1 +0 -0
  119. data/test/dummy/tmp/cache/assets/E0C/2A0/sprockets%2F37c59fadd9a21cab7d05d78a7dfe7e85 +0 -0
  120. data/test/dummy/tmp/cache/assets/E28/130/sprockets%2F6f332ca43b7ed658d90b8ba5daaa5ded +0 -0
  121. data/test/dummy/tmp/cache/assets/E3B/080/sprockets%2F09e2a090befacdae0db10cafb1893a0a +0 -0
  122. data/test/dummy/tmp/cache/assets/E65/CD0/sprockets%2F93cdf3fec0e3aa6deefa955c6828fbd0 +0 -0
  123. data/test/dummy/tmp/cache/assets/E9F/450/sprockets%2Fbbfdc5edaaf25dfdb5ee8f9db7895435 +0 -0
  124. data/test/dummy/tmp/git/fuck.git/HEAD +0 -1
  125. data/test/dummy/tmp/git/fuck.git/config +0 -6
  126. data/test/dummy/tmp/git/fuck.git/description +0 -1
  127. data/test/dummy/tmp/git/fuck.git/hooks/applypatch-msg.sample +0 -15
  128. data/test/dummy/tmp/git/fuck.git/hooks/commit-msg.sample +0 -24
  129. data/test/dummy/tmp/git/fuck.git/hooks/post-update.sample +0 -8
  130. data/test/dummy/tmp/git/fuck.git/hooks/pre-applypatch.sample +0 -14
  131. data/test/dummy/tmp/git/fuck.git/hooks/pre-commit.sample +0 -50
  132. data/test/dummy/tmp/git/fuck.git/hooks/pre-rebase.sample +0 -169
  133. data/test/dummy/tmp/git/fuck.git/hooks/prepare-commit-msg.sample +0 -36
  134. data/test/dummy/tmp/git/fuck.git/hooks/update.sample +0 -128
  135. data/test/dummy/tmp/git/fuck.git/info/exclude +0 -6
  136. data/test/dummy/tmp/git/lkasdjf.git/HEAD +0 -1
  137. data/test/dummy/tmp/git/lkasdjf.git/config +0 -6
  138. data/test/dummy/tmp/git/lkasdjf.git/description +0 -1
  139. data/test/dummy/tmp/git/lkasdjf.git/hooks/applypatch-msg.sample +0 -15
  140. data/test/dummy/tmp/git/lkasdjf.git/hooks/commit-msg.sample +0 -24
  141. data/test/dummy/tmp/git/lkasdjf.git/hooks/post-update.sample +0 -8
  142. data/test/dummy/tmp/git/lkasdjf.git/hooks/pre-applypatch.sample +0 -14
  143. data/test/dummy/tmp/git/lkasdjf.git/hooks/pre-commit.sample +0 -50
  144. data/test/dummy/tmp/git/lkasdjf.git/hooks/pre-rebase.sample +0 -169
  145. data/test/dummy/tmp/git/lkasdjf.git/hooks/prepare-commit-msg.sample +0 -36
  146. data/test/dummy/tmp/git/lkasdjf.git/hooks/update.sample +0 -128
  147. data/test/dummy/tmp/git/lkasdjf.git/info/exclude +0 -6
  148. data/test/dummy/tmp/git/new/test.git/HEAD +0 -1
  149. data/test/dummy/tmp/git/new/test.git/config +0 -6
  150. data/test/dummy/tmp/git/new/test.git/description +0 -1
  151. data/test/dummy/tmp/git/new/test.git/hooks/applypatch-msg.sample +0 -15
  152. data/test/dummy/tmp/git/new/test.git/hooks/commit-msg.sample +0 -24
  153. data/test/dummy/tmp/git/new/test.git/hooks/post-update.sample +0 -8
  154. data/test/dummy/tmp/git/new/test.git/hooks/pre-applypatch.sample +0 -14
  155. data/test/dummy/tmp/git/new/test.git/hooks/pre-commit.sample +0 -50
  156. data/test/dummy/tmp/git/new/test.git/hooks/pre-rebase.sample +0 -169
  157. data/test/dummy/tmp/git/new/test.git/hooks/prepare-commit-msg.sample +0 -36
  158. data/test/dummy/tmp/git/new/test.git/hooks/update.sample +0 -128
  159. data/test/dummy/tmp/git/new/test.git/info/exclude +0 -6
  160. data/test/dummy/tmp/git/testing.git/HEAD +0 -1
  161. data/test/dummy/tmp/git/testing.git/config +0 -6
  162. data/test/dummy/tmp/git/testing.git/description +0 -1
  163. data/test/dummy/tmp/git/testing.git/hooks/applypatch-msg.sample +0 -15
  164. data/test/dummy/tmp/git/testing.git/hooks/commit-msg.sample +0 -24
  165. data/test/dummy/tmp/git/testing.git/hooks/post-update.sample +0 -8
  166. data/test/dummy/tmp/git/testing.git/hooks/pre-applypatch.sample +0 -14
  167. data/test/dummy/tmp/git/testing.git/hooks/pre-commit.sample +0 -50
  168. data/test/dummy/tmp/git/testing.git/hooks/pre-rebase.sample +0 -169
  169. data/test/dummy/tmp/git/testing.git/hooks/prepare-commit-msg.sample +0 -36
  170. data/test/dummy/tmp/git/testing.git/hooks/update.sample +0 -128
  171. data/test/dummy/tmp/git/testing.git/info/exclude +0 -6
  172. data/test/dummy/tmp/git/under_scored/sub.git/HEAD +0 -1
  173. data/test/dummy/tmp/git/under_scored/sub.git/config +0 -6
  174. data/test/dummy/tmp/git/under_scored/sub.git/description +0 -1
  175. data/test/dummy/tmp/git/under_scored/sub.git/hooks/applypatch-msg.sample +0 -15
  176. data/test/dummy/tmp/git/under_scored/sub.git/hooks/commit-msg.sample +0 -24
  177. data/test/dummy/tmp/git/under_scored/sub.git/hooks/post-update.sample +0 -8
  178. data/test/dummy/tmp/git/under_scored/sub.git/hooks/pre-applypatch.sample +0 -14
  179. data/test/dummy/tmp/git/under_scored/sub.git/hooks/pre-commit.sample +0 -50
  180. data/test/dummy/tmp/git/under_scored/sub.git/hooks/pre-rebase.sample +0 -169
  181. data/test/dummy/tmp/git/under_scored/sub.git/hooks/prepare-commit-msg.sample +0 -36
  182. data/test/dummy/tmp/git/under_scored/sub.git/hooks/update.sample +0 -128
  183. data/test/dummy/tmp/git/under_scored/sub.git/info/exclude +0 -6
  184. data/test/dummy/tmp/git/work/pls/thnx.git/HEAD +0 -1
  185. data/test/dummy/tmp/git/work/pls/thnx.git/config +0 -6
  186. data/test/dummy/tmp/git/work/pls/thnx.git/description +0 -1
  187. data/test/dummy/tmp/git/work/pls/thnx.git/hooks/applypatch-msg.sample +0 -15
  188. data/test/dummy/tmp/git/work/pls/thnx.git/hooks/commit-msg.sample +0 -24
  189. data/test/dummy/tmp/git/work/pls/thnx.git/hooks/post-update.sample +0 -8
  190. data/test/dummy/tmp/git/work/pls/thnx.git/hooks/pre-applypatch.sample +0 -14
  191. data/test/dummy/tmp/git/work/pls/thnx.git/hooks/pre-commit.sample +0 -50
  192. data/test/dummy/tmp/git/work/pls/thnx.git/hooks/pre-rebase.sample +0 -169
  193. data/test/dummy/tmp/git/work/pls/thnx.git/hooks/prepare-commit-msg.sample +0 -36
  194. data/test/dummy/tmp/git/work/pls/thnx.git/hooks/update.sample +0 -128
  195. data/test/dummy/tmp/git/work/pls/thnx.git/info/exclude +0 -6
data/README.md CHANGED
@@ -5,99 +5,132 @@
5
5
 
6
6
  Dead simple Git hosting for Rails apps.
7
7
 
8
- ## Quickstart
8
+ ## Crash Course
9
9
 
10
- Create a Rails 3.2 app if you don't already have one. Add `git_wit` to your
11
- Gemfile:
10
+ Start hosting git repositories in seconds. Create a new Rails app and
11
+ install GitWit:
12
12
 
13
- ```ruby
14
- # Use github for now - early development:
15
- gem "git_wit", git: "https://github.com/xdissent/git_wit.git"
13
+ ```console
14
+ $ rails new example --skip-bundle; cd example # New Rails app
15
+ $ echo 'gem "git_wit"' >> Gemfile; bundle # Install git_wit gem
16
+ $ rails g git_wit:install insecure_auth insecure_write authenticate authorize_read authorize_write
17
+ $ rails s -d # <- Start Rails server # ^- Install/config GitWit
18
+ ```
16
19
 
17
- # Later it might be safe to use a rubygems release:
18
- # gem "git_wit", "~> 0.1.0"
20
+ That's it - your app is hosting git repositories. Create a repositories folder,
21
+ init a bare repo, and push to it:
22
+
23
+ ```console
24
+ $ git init
25
+ $ git add .
26
+ $ git commit -m "That was easy"
27
+ $ mkdir repositories # Hosted repos folder
28
+ $ git init --bare repositories/example.git # Example bare repo
29
+ $ git remote add origin http://localhost:3000/example.git
30
+ $ git push origin master # Push example app to itself to store in itself!
19
31
  ```
20
32
 
21
- Run `bundle install` followed by `rails g git_wit:install` and then checkout
22
- [`config/initializers/git_wit.rb`](https://github.com/xdissent/git_wit/blob/master/lib/generators/git_wit/templates/git_wit.rb).
23
- You'll want to first change `config.repositories_path` to a folder where you'd
24
- like to store your repositories. Let's use "tmp/repositories" in our app root
25
- for fun:
33
+ HTTPS? That works too:
26
34
 
27
- ```ruby
28
- config.repositories_path = Rails.root.join("tmp", "repositories").to_s
35
+ ```console
36
+ $ sudo echo "pre-loading sudo so we can background tunnels in a moment"
37
+ $ rails g git_wit:install authenticate authorize_read authorize_write -f
38
+ $ gem install tunnels
39
+ $ sudo tunnels 443 3000 & # or `rvmsudo tunnels...` if using RVM
40
+ $ git remote add https https://localhost/example.git
41
+ $ GIT_SSL_NO_VERIFY=1 git push https master:https-master # Trust yourself
29
42
  ```
30
43
 
31
- Normally you wouldn't want to allow users to send their authentication
32
- credentials over an insecure protocol like HTTP, because they'll be sent in
33
- plain text over the wire. And since anonymous write access is always disallowed,
34
- that means you can't safely push over HTTP without SSL. To disable these
35
- protections, something you'd **never** do in a production environment, change
36
- the following config values in the initializer:
44
+ Still not impressed? Try SSH (OS X only currently):
37
45
 
46
+ ```console
47
+ $ rails g git_wit:install authenticate authorize_read authorize_write ssh_user:git_wit -f
48
+ $ rails g git_wit:ssh_user # Creates/configs git_wit SSH user
49
+ $ rake git_wit:ssh:add_key # Grant access for ~/.ssh/id_rsa.pub
50
+ $ git remote add ssh git_wit@localhost:example.git
51
+ $ git push ssh master:ssh-master
38
52
  ```
53
+
54
+ You might want to get rid of that system user you just created:
55
+
56
+ ```console
57
+ $ rails d git_wit:ssh_user
58
+ ```
59
+
60
+
61
+ ## Overview
62
+
63
+ GitWit adds git hosting abilities to any Rails app. It provides configurable
64
+ authentication and authorization methods that can be integrated with any
65
+ user/repository access model you'd like. All configuration is handled through a
66
+ single initializer,
67
+ [`config/initializers/git_wit.rb`](https://github.com/xdissent/git_wit/blob/master/lib/generators/git_wit/templates/git_wit.rb).
68
+ Run `rails g git_wit:install` to generate a default configuration for
69
+ modification. All configuration details are contained within comments inside
70
+ the initializer, or read on for the highlights.
71
+
72
+
73
+ ## Authentication
74
+
75
+ Normally GitWit prevents the user from sending authentication credentials in
76
+ plaintext (via HTTP without SSL). To disable these protections, something you'd
77
+ **never** do in a production environment, change the following config values
78
+ in the initializer:
79
+
80
+ ```ruby
39
81
  config.insecure_auth = true
40
82
  config.insecure_write = true
41
83
  ```
42
84
 
43
- Now let's set up some simple (fake) authentication and authorization:
85
+ Authentication is handled by the `config.authenticate` attribute. A valid
86
+ authenticator is any callable that accepts a user model instance and a
87
+ clear-text password. The authenticator should return a boolean response
88
+ indicating whether the user is authenticated for the given password. To allow
89
+ any user as long as the password matches the username:
44
90
 
45
91
  ```ruby
46
92
  config.authenticate = ->(user, password) do
47
- %w(reader writer).include?(user) && user == password
48
- end
49
-
50
- config.authorize_read = ->(user, repository) do
51
- %w(reader writer).include?(user)
52
- end
53
-
54
- config.authorize_write = ->(user, repository) do
55
- user == "writer"
93
+ user == password
56
94
  end
57
95
  ```
58
96
 
59
- What we've done is effectively create two users: `reader` and `writer`. Both can
60
- read all repositories, but only `writer` may write (and can write to any repo.)
61
- Both users are considered authenticated if the password matches the username.
62
-
63
- Now your app is ready to start serving git repos over HTTP. Just create the
64
- repositories folder, initialize a repo and start the server:
97
+ The user model is simply the username as a string by default. Before passing
98
+ the user to the authenticator, GitWit will call `config.user_for_authenication`,
99
+ passing it the username and expecting a new user model instance in return. For
100
+ example:
65
101
 
66
- ```console
67
- $ mkdir -p tmp/repositories
68
- $ git init --bare tmp/repositories/example.git
69
- $ rails s
102
+ ```ruby
103
+ config.user_for_authentication = ->(username) do
104
+ User.active.find_by_login username: username
105
+ end
70
106
  ```
71
107
 
72
- Clone your repo, make some changes, and push:
108
+ Now the `config.authenticate` authenticator will recieve the `User` instance:
73
109
 
74
- ```console
75
- $ git clone http://localhost:3000/example.git
76
- $ cd example
77
- $ touch README
78
- $ git add README
79
- $ git commit -m "First"
80
- $ git push origin master
110
+ ```ruby
111
+ config.authenticate = ->(user, password) do
112
+ user.valid_password? password # user is a User
113
+ end
81
114
  ```
82
115
 
83
- Your server will ask you for a username and password when you push - use
84
- `writer` for both and it should accept your changes.
85
116
 
117
+ ## Authorization
86
118
 
87
- ## SSL
119
+ Two configuration attributes are responsible for authorization:
120
+ `config.authorize_read` and `config.authorize_write`. They're passed the user
121
+ instance (already authenticated) and the repository path as a string. The
122
+ repository path is relative to `config.repositories_path`
123
+ (`<app root>/repositories` by default). The authorizers should return a boolean
124
+ to grant or deny access accordingly. A simple example:
88
125
 
89
- You **really** should turn `insecure_auth` and `insecure_write` back to `false`
90
- as quickly as possible and enable SSL for read/write access. GitWit doesn't
91
- need any special SSL configuration - just flip SSL on in whatever web server
92
- is running Rails. You can also use the
93
- [tunnels](https://github.com/jugyo/tunnels) gem to run your app with SSL in
94
- development. Just add it to the Gemfile and run `bundle install` followed by
95
- `sudo tunnels` (or `rvmsudo tunnels` for RVM). For `rails s`, which runs on
96
- port 3000 by default, run `sudo tunnels 443 3000`. Now you may clone
97
- repositories over HTTPS:
126
+ ```ruby
127
+ config.authorize_read = ->(user, repository) do
128
+ %w(reader writer).include?(user)
129
+ end
98
130
 
99
- ```console
100
- $ git clone https://localhost/example.git
131
+ config.authorize_write = ->(user, repository) do
132
+ user == "writer"
133
+ end
101
134
  ```
102
135
 
103
136
 
@@ -133,110 +166,25 @@ authenticating, the SSH user will `sudo` to the application user to continue
133
166
  with the git operation. This eliminates the need for all the bat-shit crazy git
134
167
  pulls/pushes and SSH wrappers and crap that are typical of gitolite/gitosis
135
168
  setups. Your application user owns everything except the `authorized_keys` file
136
- and the `ssh_user` only needs to know how to call the `gw-shell` command.
169
+ and the `ssh_user` only needs to know how to call the `git_wit git-shell`
170
+ command.
137
171
 
138
- First, create a dedicated SSH user. On Mountain Lion:
139
-
140
- ```console
141
- $ sudo dscl . -create /Groups/gitwit
142
- $ sudo dscl . -create /Groups/gitwit PrimaryGroupID 333
143
- $ sudo dscl . -create /Groups/gitwit RealName "GitWit Server"
144
- $ sudo dscl . -create /Users/gitwit UniqueID 333
145
- $ sudo dscl . -create /Users/gitwit PrimaryGroupID 333
146
- $ sudo dscl . -create /Users/gitwit NFSHomeDirectory /var/gitwit
147
- $ sudo dscl . -create /Users/gitwit UserShell /bin/bash
148
- $ sudo dscl . -create /Users/gitwit RealName "GitWit Server"
149
- $ sudo mkdir -p ~gitwit
150
- $ sudo chown -R gitwit:gitwit ~gitwit
151
- ```
152
-
153
- Enable the `ssh_user` config value in `config/initializers/git_wit.rb`:
172
+ GitWit comes with an initializer to set everything up for you. First, enable the
173
+ `ssh_user` config in `config/initializers/git_wit.rb`:
154
174
 
155
175
  ```ruby
156
- config.ssh_user = "gitwit"
157
- ```
158
-
159
- Now your application user needs to be allowed to `sudo` as `ssh_user` and vice
160
- versa. Edit `/etc/sudoers` using `sudo visudo` and add the following lines:
161
-
162
- ```
163
- rails_user ALL=(gitwit) NOPASSWD:ALL
164
- gitwit ALL=(rails_user) NOPASSWD:ALL
165
- ```
166
-
167
- Replace `rails_user` with the application under which your Rails app runs, which
168
- will be your personal username if using `rails s` or Pow.
169
-
170
- Test your `sudo` rights and initialize the `ssh_user` environment:
171
-
172
- ```console
173
- $ sudo -u gitwit -i
174
- $ mkdir .ssh
175
- $ chmod 700 .ssh
176
- $ touch .ssh/authorized_keys
177
- $ chmod 600 .ssh/authorized_keys
178
- ```
179
-
180
- If you're using RVM or some other wacky environment manipulating tool, you're
181
- going to want to adjust the login environment for `ssh_user` by creating a
182
- `~ssh_user/.bashrc` file. For example, to load a specific RVM gemset:
183
-
184
- ```bash
185
- source "/Users/xdissent/.rvm/environments/ruby-1.9.3-p385@git_wit"
176
+ config.ssh_user = "git_wit"
186
177
  ```
187
178
 
188
- You may also need to adjust the `PATH` to include the location of the `gw-shell`
189
- executable. If you're using `bundle --binstubs` for example:
190
-
191
- ```bash
192
- export PATH="/path/to/app/bin:$PATH"
193
- ```
194
-
195
- The `gw-shell` command handles the authentication and authorization for the SSH
196
- protocol. It is initially called by `ssh_user` upon login (git operation) and it
197
- will attempt to `sudo` to the application user and re-run itself with the same
198
- environment. It determines which user is the "application user" by looking at
199
- who owns the rails app root folder. To determine where the app root is actually
200
- located, it looks for the ENV variables `RAILS_ROOT` and `BUNDLE_GEMFILE` in
201
- order. When in doubt, set `RAILS_ROOT` in `~ssh_user/.bashrc`:
202
-
203
- ```bash
204
- export RAILS_ROOT="/path/to/app"
205
- ```
206
-
207
- **Remember to add `export RAILS_ENV="production"` for production deployments!**
208
-
209
- You can easily sanity check your environment using `sudo` as your app user:
179
+ Now run the initializer:
210
180
 
211
181
  ```console
212
- $ sudo -u gitwit -i
213
- $ source .bashrc
214
- $ which gw-shell
215
- /Users/xdissent/Code/git_wit/stubs/gw-shell
216
- $ echo $RAILS_ROOT
217
- /Users/xdissent/Code/git_wit/test/dummy
182
+ $ rails g git_wit:ssh_user
218
183
  ```
219
184
 
220
- Now all that's left to do is add some `authorized_keys` and you're all set.
221
- This can be done from the rails console (`rails c`):
222
-
223
- ```ruby
224
- GitWit.add_authorized_key "writer", "ssh-rsa long-ass-key-string writer@example.com"
225
- # => nil
226
- GitWit.authorized_keys_file.keys
227
- # => [command="gw-shell writer",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa long-ass-key-string writer@example.com]
228
- ```
229
-
230
- You may now clone/push/pull over SSH - assuming the key you installed for
231
- `writer` is known to your ssh agent (ie `~/.ssh/id_rsa`):
232
-
233
- ```console
234
- $ git clone gitwit@localhost:example.git
235
- ```
185
+ To add a public key: `rake git_wit:ssh:add_key`
236
186
 
237
- See the dummy app in
238
- [`test/dummy`](https://github.com/xdissent/git_wit/tree/master/test/dummy) for
239
- a more advanced example of `authorized_keys` management.
187
+ Something not working? `rake git_wit:ssh:debug`
240
188
 
241
189
 
242
190
  ## Git hooks and configs and umasks and everything
@@ -11,38 +11,50 @@ module GitWit
11
11
 
12
12
  def service
13
13
  # Shell out to git-http-backend.
14
- out, err, status = Open3.capture3 shell_env, GitWit.git_http_backend_path,
15
- stdin_data: request.raw_post, binmode: true
14
+ self.status, self.headers, self.response_body = run_shell
15
+ end
16
16
 
17
- # Bail if the backend failed.
17
+ private
18
+ def run_shell
19
+ out, err, status = Open3.capture3 shell_env, shell_command, shell_opts
18
20
  raise GitError, err unless status.success?
21
+ parse_cgi_response out
22
+ end
19
23
 
20
- # Split headers and body from response.
21
- headers, body = out.split("\r\n\r\n", 2)
22
-
23
- # Convert CGI headers to HTTP headers.
24
- headers = Hash[headers.split("\r\n").map { |l| l.split(/\s*\:\s*/, 2) }]
24
+ def parse_cgi_response(cgi_response)
25
+ cgi_headers, body = cgi_response.split("\r\n\r\n", 2)
26
+ headers = parse_cgi_headers(cgi_headers)
27
+ [parse_cgi_status(headers), headers, body]
28
+ end
25
29
 
26
- # Set status from header if given, otherwise it's a 200.
27
- self.status = headers.delete("Status").to_i if headers.key? "Status"
30
+ def parse_cgi_headers(cgi_headers)
31
+ Hash[cgi_headers.split("\r\n").map { |l| l.split(/\s*\:\s*/, 2) }]
32
+ end
28
33
 
29
- # Set response body if given, otherwise empty string.
30
- self.response_body = body.presence || ""
34
+ def parse_cgi_status(cgi_headers)
35
+ cgi_headers.key?("Status") ? cgi_headers.delete("Status").to_i : 200
31
36
  end
32
37
 
33
- private
34
38
  def shell_env
35
39
  request.headers.dup.extract!(*ENV_KEEPERS).merge(http_env).merge(git_env)
36
40
  end
37
41
 
42
+ def shell_command
43
+ [GitWit.git_path, "http-backend"].join " "
44
+ end
45
+
46
+ def shell_opts
47
+ {stdin_data: request.raw_post, binmode: true}
48
+ end
49
+
38
50
  def git_env
39
51
  {
40
52
  GIT_HTTP_EXPORT_ALL: "uknoit",
41
53
  GIT_PROJECT_ROOT: GitWit.repositories_path,
42
54
  PATH_INFO: "/#{params[:repository]}/#{params[:refs] || params[:service]}",
43
55
  REMOTE_USER: (user_attr(:username) || @username),
44
- GIT_COMMITTER_NAME: user_attr(:committer_name),
45
- GIT_COMMITTER_EMAIL: user_attr(:committer_email)
56
+ GIT_COMMITTER_NAME: user_attr(:name),
57
+ GIT_COMMITTER_EMAIL: user_attr(:email)
46
58
  }.reject { |_, v| v.nil? }.stringify_keys
47
59
  end
48
60
 
@@ -63,7 +75,7 @@ module GitWit
63
75
  end
64
76
 
65
77
  # Request credentials again if provided and no user was authenticated.
66
- if @user.nil? && request.authorization.present?
78
+ if !@user.present? && request.authorization.present?
67
79
  request_http_basic_authentication_if_allowed
68
80
  end
69
81
  end
@@ -79,7 +91,7 @@ module GitWit
79
91
 
80
92
  def authorize_write
81
93
  # Never allow anonymous write operations.
82
- return request_http_basic_authentication_if_allowed if @user.nil?
94
+ return request_http_basic_authentication_if_allowed unless @user.present?
83
95
 
84
96
  # Disallow write operations over insecure protocol per configuration.
85
97
  raise ForbiddenError if !GitWit.insecure_write && !request.ssl?
@@ -89,9 +101,9 @@ module GitWit
89
101
  end
90
102
 
91
103
  def authorize_read
92
- return if GitWit.authorize_read(@user, params[:repository])
93
- return request_http_basic_authentication_if_allowed if @user.nil?
94
- raise UnauthorizedError
104
+ return true if GitWit.authorize_read(@user, params[:repository])
105
+ raise UnauthorizedError if @user.present?
106
+ request_http_basic_authentication_if_allowed
95
107
  end
96
108
 
97
109
  # TODO: Sure about this?
@@ -105,12 +117,14 @@ module GitWit
105
117
  end
106
118
 
107
119
  def user_attr(sym)
108
- try_user GitWit.config.send("#{sym}_attribute")
120
+ try_user GitWit.send("#{sym}_attribute")
109
121
  end
110
122
 
111
123
  def try_user(sym_or_proc)
112
- return @user.try(sym_or_proc) if sym_or_proc.is_a? Symbol
113
- sym_or_proc.call(@user) if sym_or_proc.respond_to? :call
124
+ return sym_or_proc.call(@user) if sym_or_proc.respond_to? :call
125
+ if sym_or_proc.is_a?(Symbol) && @user.respond_to?(sym_or_proc)
126
+ @user.try(sym_or_proc)
127
+ end
114
128
  end
115
129
  end
116
130
  end
data/bin/git_wit ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "git_wit/cli"
4
+ GitWit::Cli.start ARGV
data/config/routes.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  GitWit::Engine.routes.draw do
2
- get ":repository/*refs" => "git#service", repository: /[\-\/\w\.]+\.git/
3
- post ":repository/:service" => "git#service", repository: /[\-\/\w\.]+\.git/,
2
+ get ":repository/*refs", to: "git#service", repository: /[\-\/\w\.]+\.git/
3
+ post ":repository/:service", to: "git#service", repository: /[\-\/\w\.]+\.git/,
4
4
  service: /git-[\w\-]+/
5
5
  end
@@ -1,15 +1,46 @@
1
- class GitWit::InstallGenerator < Rails::Generators::Base
2
- source_root File.expand_path('../../templates', __FILE__)
1
+ module GitWit
2
+ class InstallGenerator < Rails::Generators::Base
3
+ source_root File.expand_path('../templates', __FILE__)
3
4
 
4
- def copy_initializer
5
- template "git_wit.rb", "config/initializers/git_wit.rb"
6
- end
5
+ argument :attributes, type: :array, default: [], banner: "config[:value] config[:value]"
7
6
 
8
- def show_readme
9
- readme "README" if behavior == :invoke
10
- end
7
+ def initialize(args, *options)
8
+ super
9
+ parse_attributes! if respond_to?(:attributes)
10
+ end
11
+
12
+ def copy_initializer
13
+ template "git_wit.rb", "config/initializers/git_wit.rb"
14
+ end
15
+
16
+ def show_readme
17
+ readme "README" if behavior == :invoke
18
+ end
19
+
20
+ def mount_route
21
+ route 'mount GitWit::Engine => "/"'
22
+ end
23
+
24
+ protected
25
+ def parse_attributes!
26
+ attrs = (attributes || [])
27
+ self.attributes = Hash[attrs.map { |attr| parse_attribute(attr) }.compact]
28
+ end
29
+
30
+ def parse_attribute(attr)
31
+ k, v = attr.split(":", 2)
32
+ v ||= true
33
+ v = false if v == "false"
34
+ [k.to_sym, v] if k.present?
35
+ end
11
36
 
12
- def mount_route
13
- route 'mount GitWit::Engine => "/"'
37
+ def maybe_config(name, default)
38
+ given = attributes.key?(name)
39
+ value = given ? attributes[name] : default
40
+ value = %("#{value}") if value.is_a? String
41
+ value = ":#{value}" if value.is_a? Symbol
42
+ pre = given ? "" : "# "
43
+ "#{pre}config.#{name} = #{value}"
44
+ end
14
45
  end
15
- end
46
+ end