git_wit 0.0.3 → 0.0.4.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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