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.
- data/README.md +105 -157
- data/app/controllers/git_wit/git_controller.rb +37 -23
- data/bin/git_wit +4 -0
- data/config/routes.rb +2 -2
- data/lib/generators/git_wit/install/install_generator.rb +42 -11
- data/lib/generators/git_wit/{templates → install/templates}/README +0 -0
- data/lib/generators/git_wit/{templates → install/templates}/git_wit.rb +21 -11
- data/lib/generators/git_wit/ssh_user/USAGE +11 -0
- data/lib/generators/git_wit/ssh_user/ssh_user_generator.rb +53 -0
- data/lib/generators/git_wit/ssh_user/templates/bashrc.tt +7 -0
- data/lib/generators/git_wit/ssh_user/templates/sudoers.tt +12 -0
- data/lib/git_wit.rb +19 -4
- data/lib/git_wit/actions.rb +17 -0
- data/lib/git_wit/actions/dscl.rb +15 -0
- data/lib/git_wit/actions/dscl/base.rb +75 -0
- data/lib/git_wit/actions/dscl/group.rb +20 -0
- data/lib/git_wit/actions/dscl/group_membership.rb +30 -0
- data/lib/git_wit/actions/dscl/user.rb +39 -0
- data/lib/git_wit/actions/ssh.rb +11 -0
- data/lib/git_wit/actions/ssh/home.rb +55 -0
- data/lib/git_wit/actions/ssh/sudoers.rb +94 -0
- data/lib/git_wit/auth.rb +2 -2
- data/lib/git_wit/authorized_keys.rb +45 -88
- data/lib/git_wit/authorized_keys/file.rb +61 -0
- data/lib/git_wit/authorized_keys/key.rb +15 -0
- data/lib/git_wit/cli.rb +19 -0
- data/lib/git_wit/commands/debug.rb +21 -0
- data/lib/git_wit/commands/git_shell.rb +48 -0
- data/lib/git_wit/commands/util.rb +37 -0
- data/lib/git_wit/errors.rb +1 -0
- data/lib/git_wit/version.rb +2 -1
- data/lib/tasks/git_wit.rake +46 -0
- data/test/dummy/bin/coderay +16 -0
- data/test/dummy/bin/erubis +16 -0
- data/test/dummy/bin/git_wit +16 -0
- data/test/dummy/bin/htmldiff +16 -0
- data/test/dummy/bin/ldiff +16 -0
- data/test/dummy/bin/posix-spawn-benchmark +16 -0
- data/test/dummy/bin/pry +16 -0
- data/test/dummy/bin/rackup +16 -0
- data/test/dummy/bin/rails +16 -0
- data/test/dummy/bin/rake +16 -0
- data/test/dummy/bin/rake2thor +16 -0
- data/test/dummy/bin/rdoc +16 -0
- data/test/dummy/bin/ri +16 -0
- data/test/dummy/bin/sprockets +16 -0
- data/test/dummy/bin/thor +16 -0
- data/test/dummy/bin/tilt +16 -0
- data/test/dummy/bin/tt +16 -0
- data/test/dummy/bin/tunnels +16 -0
- data/test/dummy/config/initializers/git_wit.rb +33 -24
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/development.log +1639 -5391
- data/test/dummy/log/test.log +108 -2
- data/test/dummy/tmp/pids/server.pid +1 -0
- data/test/unit/auth_test.rb +10 -6
- data/test/unit/authorized_keys_test.rb +5 -5
- data/test/unit/config_test.rb +15 -11
- data/test/unit/shell_test.rb +5 -5
- metadata +84 -278
- data/bin/gw-shell +0 -4
- data/lib/tasks/git_wit_shell.rake +0 -8
- data/test/dummy/tmp/cache/assets/C7E/BC0/sprockets%2Fb7118f368364962573a44054bcfb80d0 +0 -0
- data/test/dummy/tmp/cache/assets/C80/840/sprockets%2F562c2d168da585f80579347d10790a0a +0 -0
- data/test/dummy/tmp/cache/assets/C8C/B80/sprockets%2F371bf96e99717688ed7313a0c53f4212 +0 -0
- data/test/dummy/tmp/cache/assets/C9C/700/sprockets%2Fc7b1373dbf219a8722efc21160641340 +0 -0
- data/test/dummy/tmp/cache/assets/C9E/5F0/sprockets%2F2bca2b107bb6c26b720d135270688918 +0 -0
- data/test/dummy/tmp/cache/assets/CA9/9C0/sprockets%2F0c1b7ebd087418498ea6037225d33d25 +0 -0
- data/test/dummy/tmp/cache/assets/CC8/B00/sprockets%2F9815364bfd49ed907870e270d75a995a +0 -0
- data/test/dummy/tmp/cache/assets/CD1/800/sprockets%2Fc044b140dcef533c52712c7b51e21996 +0 -0
- data/test/dummy/tmp/cache/assets/CD5/2C0/sprockets%2F166c056119ebdfb8b7104c97b424b423 +0 -0
- data/test/dummy/tmp/cache/assets/CD7/380/sprockets%2F4079ce1dbbcf4a599527303670006b6b +0 -0
- data/test/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
- data/test/dummy/tmp/cache/assets/CE0/CC0/sprockets%2F2b38c3fb549036de5c4666637a0c80c6 +0 -0
- data/test/dummy/tmp/cache/assets/CEC/B70/sprockets%2F7f98753ca8c35e4249363a04389b3caf +0 -0
- data/test/dummy/tmp/cache/assets/CF0/1D0/sprockets%2F6fc757c2c8329244ca95d6909865bbc2 +0 -0
- data/test/dummy/tmp/cache/assets/CF9/980/sprockets%2Fbd55042e1acd32eb611041444d794d4d +0 -0
- data/test/dummy/tmp/cache/assets/CFD/560/sprockets%2Fe4e7fe4ee089382325686f806b939d3e +0 -0
- data/test/dummy/tmp/cache/assets/D00/B90/sprockets%2F07c00c80f1ea62d95a01f11f9c728b72 +0 -0
- data/test/dummy/tmp/cache/assets/D0D/9A0/sprockets%2F1fce44192cdb30f44b7545a37c9891b2 +0 -0
- data/test/dummy/tmp/cache/assets/D0F/390/sprockets%2F9df081609c3449ab40c93b1cf07de357 +0 -0
- data/test/dummy/tmp/cache/assets/D25/A60/sprockets%2F0e036061ad22b2e6dce1639b234cf15c +0 -0
- data/test/dummy/tmp/cache/assets/D27/DB0/sprockets%2Fa3a0a778855bce9fa47913d389ea9884 +0 -0
- data/test/dummy/tmp/cache/assets/D29/5A0/sprockets%2Fdad9e81b43ca12671246ee52a1900d0c +0 -0
- data/test/dummy/tmp/cache/assets/D2E/FF0/sprockets%2Fc06112642c994b6b7c2ba6632fad1fd0 +0 -0
- data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/test/dummy/tmp/cache/assets/D36/120/sprockets%2Feac54bd3c540af6b964d025e345227d6 +0 -0
- data/test/dummy/tmp/cache/assets/D3E/240/sprockets%2F84b96d6b2d2653cb4127b06d8acb990d +0 -0
- data/test/dummy/tmp/cache/assets/D3F/830/sprockets%2F18578d4ef3abd6e12d836841dd6b8a00 +0 -0
- data/test/dummy/tmp/cache/assets/D4B/E70/sprockets%2F0909bc70e589d40a6cd90dfe6f18257e +0 -0
- data/test/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/test/dummy/tmp/cache/assets/D57/3D0/sprockets%2F7bbccc5129a5013b70831ec9ad62ab24 +0 -0
- data/test/dummy/tmp/cache/assets/D5A/000/sprockets%2F7d4f67f146b6d7904dfc4edd9135f588 +0 -0
- data/test/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/test/dummy/tmp/cache/assets/D5B/BB0/sprockets%2Fba769276c4de14151bc4202cba7f3ad3 +0 -0
- data/test/dummy/tmp/cache/assets/D5E/BC0/sprockets%2F2d96fa667066778db858d7b7cb0fce69 +0 -0
- data/test/dummy/tmp/cache/assets/D6E/BA0/sprockets%2F5178d3788fe35a52acb5f3bd22ea078a +0 -0
- data/test/dummy/tmp/cache/assets/D6F/C20/sprockets%2F22e783a8f5f9224f01e8e62fab6afb40 +0 -0
- data/test/dummy/tmp/cache/assets/D76/5C0/sprockets%2Fd8a5669df31f129f355283e6dab4c5ad +0 -0
- data/test/dummy/tmp/cache/assets/D79/DE0/sprockets%2F7cfd335e68d881b03f6b7f1bd91f270f +0 -0
- data/test/dummy/tmp/cache/assets/D8A/CA0/sprockets%2F656af8b87ad378e8e4f2ec94b6b5c719 +0 -0
- data/test/dummy/tmp/cache/assets/D8C/620/sprockets%2Ff37f8e5b8cccd9880276a9f5157d4c7e +0 -0
- data/test/dummy/tmp/cache/assets/D92/200/sprockets%2Fb816d858281027bdd3fe2bfac43b4ca1 +0 -0
- data/test/dummy/tmp/cache/assets/D92/CE0/sprockets%2Ffca6a13676a2be09234905f9acae22cd +0 -0
- data/test/dummy/tmp/cache/assets/D96/9E0/sprockets%2F6fabecd33f7a5a087f4fb6a2d6312443 +0 -0
- data/test/dummy/tmp/cache/assets/DA2/D20/sprockets%2Ff5faf079fb660bde5bc9502bde442088 +0 -0
- data/test/dummy/tmp/cache/assets/DA5/570/sprockets%2F3517de599b6fd005bc5d5d69ba5ff1e2 +0 -0
- data/test/dummy/tmp/cache/assets/DA7/070/sprockets%2F69eadf8c3a94b04fe0b4992ee4a8c821 +0 -0
- data/test/dummy/tmp/cache/assets/DBA/BF0/sprockets%2Fe63ea1d7bfb0ee50380debe42360a3b5 +0 -0
- data/test/dummy/tmp/cache/assets/DBB/3B0/sprockets%2F6a0aaa6c5b0d10b936e237a7ecb4e4c9 +0 -0
- data/test/dummy/tmp/cache/assets/DBC/8E0/sprockets%2F908976cfbcdf6ad4c59737bf3c78e1e8 +0 -0
- data/test/dummy/tmp/cache/assets/DD3/FD0/sprockets%2Febf97c76a9ba2a889dd01be2caa75806 +0 -0
- data/test/dummy/tmp/cache/assets/DD8/410/sprockets%2Fc02eeb7ea977fd713cc19ca93d838af4 +0 -0
- data/test/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/test/dummy/tmp/cache/assets/DEA/E40/sprockets%2F4166d7d00d1e72fed2004debed2bea3e +0 -0
- data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/test/dummy/tmp/cache/assets/E05/C70/sprockets%2Fccd814ec859d582ada46e271edfe7ae1 +0 -0
- data/test/dummy/tmp/cache/assets/E0C/2A0/sprockets%2F37c59fadd9a21cab7d05d78a7dfe7e85 +0 -0
- data/test/dummy/tmp/cache/assets/E28/130/sprockets%2F6f332ca43b7ed658d90b8ba5daaa5ded +0 -0
- data/test/dummy/tmp/cache/assets/E3B/080/sprockets%2F09e2a090befacdae0db10cafb1893a0a +0 -0
- data/test/dummy/tmp/cache/assets/E65/CD0/sprockets%2F93cdf3fec0e3aa6deefa955c6828fbd0 +0 -0
- data/test/dummy/tmp/cache/assets/E9F/450/sprockets%2Fbbfdc5edaaf25dfdb5ee8f9db7895435 +0 -0
- data/test/dummy/tmp/git/fuck.git/HEAD +0 -1
- data/test/dummy/tmp/git/fuck.git/config +0 -6
- data/test/dummy/tmp/git/fuck.git/description +0 -1
- data/test/dummy/tmp/git/fuck.git/hooks/applypatch-msg.sample +0 -15
- data/test/dummy/tmp/git/fuck.git/hooks/commit-msg.sample +0 -24
- data/test/dummy/tmp/git/fuck.git/hooks/post-update.sample +0 -8
- data/test/dummy/tmp/git/fuck.git/hooks/pre-applypatch.sample +0 -14
- data/test/dummy/tmp/git/fuck.git/hooks/pre-commit.sample +0 -50
- data/test/dummy/tmp/git/fuck.git/hooks/pre-rebase.sample +0 -169
- data/test/dummy/tmp/git/fuck.git/hooks/prepare-commit-msg.sample +0 -36
- data/test/dummy/tmp/git/fuck.git/hooks/update.sample +0 -128
- data/test/dummy/tmp/git/fuck.git/info/exclude +0 -6
- data/test/dummy/tmp/git/lkasdjf.git/HEAD +0 -1
- data/test/dummy/tmp/git/lkasdjf.git/config +0 -6
- data/test/dummy/tmp/git/lkasdjf.git/description +0 -1
- data/test/dummy/tmp/git/lkasdjf.git/hooks/applypatch-msg.sample +0 -15
- data/test/dummy/tmp/git/lkasdjf.git/hooks/commit-msg.sample +0 -24
- data/test/dummy/tmp/git/lkasdjf.git/hooks/post-update.sample +0 -8
- data/test/dummy/tmp/git/lkasdjf.git/hooks/pre-applypatch.sample +0 -14
- data/test/dummy/tmp/git/lkasdjf.git/hooks/pre-commit.sample +0 -50
- data/test/dummy/tmp/git/lkasdjf.git/hooks/pre-rebase.sample +0 -169
- data/test/dummy/tmp/git/lkasdjf.git/hooks/prepare-commit-msg.sample +0 -36
- data/test/dummy/tmp/git/lkasdjf.git/hooks/update.sample +0 -128
- data/test/dummy/tmp/git/lkasdjf.git/info/exclude +0 -6
- data/test/dummy/tmp/git/new/test.git/HEAD +0 -1
- data/test/dummy/tmp/git/new/test.git/config +0 -6
- data/test/dummy/tmp/git/new/test.git/description +0 -1
- data/test/dummy/tmp/git/new/test.git/hooks/applypatch-msg.sample +0 -15
- data/test/dummy/tmp/git/new/test.git/hooks/commit-msg.sample +0 -24
- data/test/dummy/tmp/git/new/test.git/hooks/post-update.sample +0 -8
- data/test/dummy/tmp/git/new/test.git/hooks/pre-applypatch.sample +0 -14
- data/test/dummy/tmp/git/new/test.git/hooks/pre-commit.sample +0 -50
- data/test/dummy/tmp/git/new/test.git/hooks/pre-rebase.sample +0 -169
- data/test/dummy/tmp/git/new/test.git/hooks/prepare-commit-msg.sample +0 -36
- data/test/dummy/tmp/git/new/test.git/hooks/update.sample +0 -128
- data/test/dummy/tmp/git/new/test.git/info/exclude +0 -6
- data/test/dummy/tmp/git/testing.git/HEAD +0 -1
- data/test/dummy/tmp/git/testing.git/config +0 -6
- data/test/dummy/tmp/git/testing.git/description +0 -1
- data/test/dummy/tmp/git/testing.git/hooks/applypatch-msg.sample +0 -15
- data/test/dummy/tmp/git/testing.git/hooks/commit-msg.sample +0 -24
- data/test/dummy/tmp/git/testing.git/hooks/post-update.sample +0 -8
- data/test/dummy/tmp/git/testing.git/hooks/pre-applypatch.sample +0 -14
- data/test/dummy/tmp/git/testing.git/hooks/pre-commit.sample +0 -50
- data/test/dummy/tmp/git/testing.git/hooks/pre-rebase.sample +0 -169
- data/test/dummy/tmp/git/testing.git/hooks/prepare-commit-msg.sample +0 -36
- data/test/dummy/tmp/git/testing.git/hooks/update.sample +0 -128
- data/test/dummy/tmp/git/testing.git/info/exclude +0 -6
- data/test/dummy/tmp/git/under_scored/sub.git/HEAD +0 -1
- data/test/dummy/tmp/git/under_scored/sub.git/config +0 -6
- data/test/dummy/tmp/git/under_scored/sub.git/description +0 -1
- data/test/dummy/tmp/git/under_scored/sub.git/hooks/applypatch-msg.sample +0 -15
- data/test/dummy/tmp/git/under_scored/sub.git/hooks/commit-msg.sample +0 -24
- data/test/dummy/tmp/git/under_scored/sub.git/hooks/post-update.sample +0 -8
- data/test/dummy/tmp/git/under_scored/sub.git/hooks/pre-applypatch.sample +0 -14
- data/test/dummy/tmp/git/under_scored/sub.git/hooks/pre-commit.sample +0 -50
- data/test/dummy/tmp/git/under_scored/sub.git/hooks/pre-rebase.sample +0 -169
- data/test/dummy/tmp/git/under_scored/sub.git/hooks/prepare-commit-msg.sample +0 -36
- data/test/dummy/tmp/git/under_scored/sub.git/hooks/update.sample +0 -128
- data/test/dummy/tmp/git/under_scored/sub.git/info/exclude +0 -6
- data/test/dummy/tmp/git/work/pls/thnx.git/HEAD +0 -1
- data/test/dummy/tmp/git/work/pls/thnx.git/config +0 -6
- data/test/dummy/tmp/git/work/pls/thnx.git/description +0 -1
- data/test/dummy/tmp/git/work/pls/thnx.git/hooks/applypatch-msg.sample +0 -15
- data/test/dummy/tmp/git/work/pls/thnx.git/hooks/commit-msg.sample +0 -24
- data/test/dummy/tmp/git/work/pls/thnx.git/hooks/post-update.sample +0 -8
- data/test/dummy/tmp/git/work/pls/thnx.git/hooks/pre-applypatch.sample +0 -14
- data/test/dummy/tmp/git/work/pls/thnx.git/hooks/pre-commit.sample +0 -50
- data/test/dummy/tmp/git/work/pls/thnx.git/hooks/pre-rebase.sample +0 -169
- data/test/dummy/tmp/git/work/pls/thnx.git/hooks/prepare-commit-msg.sample +0 -36
- data/test/dummy/tmp/git/work/pls/thnx.git/hooks/update.sample +0 -128
- 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
|
-
##
|
8
|
+
## Crash Course
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
Start hosting git repositories in seconds. Create a new Rails app and
|
11
|
+
install GitWit:
|
12
12
|
|
13
|
-
```
|
14
|
-
|
15
|
-
gem "git_wit"
|
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
|
-
|
18
|
-
|
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
|
-
|
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
|
-
```
|
28
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
60
|
-
|
61
|
-
|
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
|
-
```
|
67
|
-
|
68
|
-
|
69
|
-
|
102
|
+
```ruby
|
103
|
+
config.user_for_authentication = ->(username) do
|
104
|
+
User.active.find_by_login username: username
|
105
|
+
end
|
70
106
|
```
|
71
107
|
|
72
|
-
|
108
|
+
Now the `config.authenticate` authenticator will recieve the `User` instance:
|
73
109
|
|
74
|
-
```
|
75
|
-
|
76
|
-
|
77
|
-
|
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
|
-
|
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
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
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
|
-
|
100
|
-
|
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 `
|
169
|
+
and the `ssh_user` only needs to know how to call the `git_wit git-shell`
|
170
|
+
command.
|
137
171
|
|
138
|
-
|
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 = "
|
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
|
-
|
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
|
-
$
|
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
|
-
|
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
|
-
|
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
|
-
|
15
|
-
|
14
|
+
self.status, self.headers, self.response_body = run_shell
|
15
|
+
end
|
16
16
|
|
17
|
-
|
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
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
27
|
-
|
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
|
-
|
30
|
-
|
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(:
|
45
|
-
GIT_COMMITTER_EMAIL: user_attr(:
|
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
|
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
|
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
|
-
|
94
|
-
|
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.
|
120
|
+
try_user GitWit.send("#{sym}_attribute")
|
109
121
|
end
|
110
122
|
|
111
123
|
def try_user(sym_or_proc)
|
112
|
-
return @user
|
113
|
-
sym_or_proc.
|
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
data/config/routes.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
GitWit::Engine.routes.draw do
|
2
|
-
get ":repository/*refs"
|
3
|
-
post ":repository/:service"
|
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
|
-
|
2
|
-
|
1
|
+
module GitWit
|
2
|
+
class InstallGenerator < Rails::Generators::Base
|
3
|
+
source_root File.expand_path('../templates', __FILE__)
|
3
4
|
|
4
|
-
|
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
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
13
|
-
|
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
|