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.
- 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
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
module GitWit::Actions::Ssh
|
|
2
|
+
class Sudoers < Thor::Actions::EmptyDirectory
|
|
3
|
+
|
|
4
|
+
attr_reader :base, :name
|
|
5
|
+
|
|
6
|
+
def initialize(base, name, config = {})
|
|
7
|
+
@base, @name = base, name
|
|
8
|
+
@config = {verbose: true}.merge config
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def invoke!
|
|
12
|
+
invoke_with_conflict_check do
|
|
13
|
+
create
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def revoke!
|
|
18
|
+
say_status :remove, :red
|
|
19
|
+
destroy if !pretend? && exists?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def exists?
|
|
23
|
+
`sudo grep '#{sentinel}' /etc/sudoers &>/dev/null`
|
|
24
|
+
$?.success?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
protected
|
|
28
|
+
def sentinel
|
|
29
|
+
"# git_wit sudoers #{name}"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def real_path
|
|
33
|
+
"/etc/sudoers"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def lock_path
|
|
37
|
+
"#{real_path}.tmp"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def append(str)
|
|
41
|
+
`echo '#{str}' | sudo tee -a '#{lock_path}' >/dev/null`
|
|
42
|
+
raise Thor::Error, "Could not append to #{lock_path}" unless $?.success?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def with_lock(&block)
|
|
46
|
+
`sudo cp -p '#{real_path}' '#{lock_path}'`
|
|
47
|
+
unless $?.success?
|
|
48
|
+
raise Thor::Error, "Could not copy sudoers from #{real_path} to #{lock_path}"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
old_destination = base.destination_root
|
|
52
|
+
Dir.mktmpdir do |dir|
|
|
53
|
+
base.destination_root = dir
|
|
54
|
+
yield
|
|
55
|
+
end
|
|
56
|
+
base.destination_root = old_destination
|
|
57
|
+
|
|
58
|
+
`sudo visudo -cqf '#{lock_path}'`
|
|
59
|
+
unless $?.success?
|
|
60
|
+
raise Thor::Error, "Invalid sudoers lock at #{lock_path}"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
out = `sudo cat '#{lock_path}'`
|
|
64
|
+
raise Thor::Error, "Refusing to install empty sudoers" unless out.present?
|
|
65
|
+
|
|
66
|
+
`sudo mv '#{lock_path}' '#{real_path}'`
|
|
67
|
+
raise Thor::Error, "Could install sudoers from #{lock_path} to #{real_path}" unless $?.success?
|
|
68
|
+
ensure
|
|
69
|
+
`sudo rm -rf '#{lock_path}'`
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def create
|
|
73
|
+
with_lock do
|
|
74
|
+
base.template "sudoers.tt"
|
|
75
|
+
append sentinel
|
|
76
|
+
`cat '#{base.destination_root}/sudoers' | sudo tee -a '#{lock_path}' >/dev/null`
|
|
77
|
+
raise Thor::Error, "Could not modify #{lock_path}" unless $?.success?
|
|
78
|
+
append sentinel
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def destroy
|
|
83
|
+
with_lock do
|
|
84
|
+
`sudo cat '#{lock_path}' | sed -e '/^#{sentinel}/,/^#{sentinel}/d' > '#{base.destination_root}/sudoers'`
|
|
85
|
+
`cat '#{base.destination_root}/sudoers' | sudo tee '#{lock_path}' >/dev/null`
|
|
86
|
+
raise Thor::Error, "Could not modify #{lock_path}" unless $?.success?
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def relative_destination
|
|
91
|
+
"sudoers #{name}"
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
data/lib/git_wit/auth.rb
CHANGED
|
@@ -10,7 +10,7 @@ module GitWit
|
|
|
10
10
|
if config.authenticate.respond_to?(:call)
|
|
11
11
|
return config.authenticate.call(user, password)
|
|
12
12
|
end
|
|
13
|
-
|
|
13
|
+
config.authenticate
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def self.authorize_write(user, repository)
|
|
@@ -23,6 +23,6 @@ module GitWit
|
|
|
23
23
|
|
|
24
24
|
def self.authorize(operation, user, repository)
|
|
25
25
|
cfg = config.send "authorize_#{operation}".to_sym
|
|
26
|
-
cfg.respond_to?(:call) ? cfg.call(user, repository) :
|
|
26
|
+
cfg.respond_to?(:call) ? cfg.call(user, repository) : cfg
|
|
27
27
|
end
|
|
28
28
|
end
|
|
@@ -1,103 +1,60 @@
|
|
|
1
|
-
require "tempfile"
|
|
2
|
-
require "authorized_keys"
|
|
3
|
-
|
|
4
1
|
module GitWit
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
# Public: Determine the path to the authorized_keys file based on the
|
|
3
|
+
# configuration. If not explicitly configured, the ssh_user's home directory
|
|
4
|
+
# is used to construct the path by adding ".ssh/authorized_keys".
|
|
5
|
+
#
|
|
6
|
+
# Returns the path as a String or nothing if it cannot be determined.
|
|
7
|
+
def self.authorized_keys_path
|
|
8
|
+
return config.authorized_keys_path if config.authorized_keys_path.present?
|
|
9
|
+
if ssh_user.present?
|
|
10
|
+
File.expand_path(File.join("~#{ssh_user}", ".ssh", "authorized_keys"))
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Public: Get an authorized_keys file instance.
|
|
15
|
+
#
|
|
16
|
+
# Returns an AuthorizedKeys::File instance.
|
|
17
|
+
# Raises ConfigurationError if the path cannot be determined.
|
|
18
|
+
def self.authorized_keys_file
|
|
19
|
+
path = authorized_keys_path
|
|
20
|
+
return AuthorizedKeys::File.new path if path.present?
|
|
21
|
+
raise ConfigurationError, "Could not determine path to authorized_keys file"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Public: Clear out all existing public keys in the authorized_keys file and
|
|
25
|
+
# add each public key for each user in the key map to the new file.
|
|
26
|
+
#
|
|
27
|
+
# keys_map - The Hash of String public key contents, keyed by String username.
|
|
28
|
+
#
|
|
29
|
+
# Returns nothing.
|
|
30
|
+
def self.regenerate_authorized_keys(keys_map)
|
|
31
|
+
keys_file = authorized_keys_file
|
|
32
|
+
keys_file.clear do |file|
|
|
33
|
+
keys_map.each do |username, keys|
|
|
9
34
|
keys.each do |key|
|
|
10
|
-
|
|
35
|
+
keys_file.add AuthorizedKeys::Key.shell_key_for_username(username, key)
|
|
11
36
|
end
|
|
12
37
|
end
|
|
13
38
|
end
|
|
39
|
+
nil
|
|
14
40
|
end
|
|
15
41
|
|
|
42
|
+
# Public: Add a public key for a given username to the authorized_keys file.
|
|
43
|
+
#
|
|
44
|
+
# username - The String username for the public key owner.
|
|
45
|
+
# key - The String public key contents.
|
|
46
|
+
#
|
|
47
|
+
# Returns nothing.
|
|
16
48
|
def self.add_authorized_key(username, key)
|
|
17
49
|
authorized_keys_file.add AuthorizedKeys::Key.shell_key_for_username(username, key)
|
|
18
50
|
end
|
|
19
51
|
|
|
52
|
+
# Public: Remove a public key from the authorized_keys file.
|
|
53
|
+
#
|
|
54
|
+
# key - The String public key contents.
|
|
55
|
+
#
|
|
56
|
+
# Returns nothing.
|
|
20
57
|
def self.remove_authorized_key(key)
|
|
21
58
|
authorized_keys_file.remove key
|
|
22
59
|
end
|
|
23
|
-
|
|
24
|
-
def self.authorized_keys_file
|
|
25
|
-
AuthorizedKeys::File.new authorized_keys_path
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def self.authorized_keys_path
|
|
29
|
-
config.authorized_keys_path || File.expand_path("~#{ssh_user}/.ssh/authorized_keys")
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
module AuthorizedKeys
|
|
33
|
-
class File < ::AuthorizedKeys::File
|
|
34
|
-
attr_accessor :original_location
|
|
35
|
-
|
|
36
|
-
def keys
|
|
37
|
-
list = []
|
|
38
|
-
modify "r" do |file|
|
|
39
|
-
file.each do |line|
|
|
40
|
-
list << Key.new(line.chomp)
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
list
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def remove(key)
|
|
47
|
-
key = Key.new(key) if key.is_a?(String)
|
|
48
|
-
cached_keys = keys
|
|
49
|
-
modify 'w' do |file|
|
|
50
|
-
cached_keys.each do |k|
|
|
51
|
-
file.puts k unless key == k
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def owned?
|
|
57
|
-
owner == Process.uid
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def owner(file = nil)
|
|
61
|
-
file ||= location
|
|
62
|
-
::File.stat(file).uid
|
|
63
|
-
rescue Errno::EACCES, Errno::ENOENT
|
|
64
|
-
parent = ::File.dirname file
|
|
65
|
-
owner parent unless file == parent
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def modify(mode, &block)
|
|
69
|
-
return super if owned? || self.original_location
|
|
70
|
-
contents = %x(sudo -u "##{owner}" cat "#{location}") unless mode.include? "w"
|
|
71
|
-
original_owner = owner
|
|
72
|
-
self.original_location = location
|
|
73
|
-
tmp = Tempfile.new "git_wit_authorized_keys"
|
|
74
|
-
self.location = tmp.path
|
|
75
|
-
tmp.write contents unless mode.include? "w"
|
|
76
|
-
tmp.close
|
|
77
|
-
super
|
|
78
|
-
self.location = original_location
|
|
79
|
-
if mode != "r"
|
|
80
|
-
%x(cat "#{tmp.path}" | sudo -u "##{owner}" tee "#{location}" >/dev/null)
|
|
81
|
-
end
|
|
82
|
-
tmp.unlink
|
|
83
|
-
self.original_location = nil
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def clear(&block)
|
|
87
|
-
modify "w", &block
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
class Key < ::AuthorizedKeys::Key
|
|
92
|
-
SHELL_OPTIONS = %w(no-port-forwarding no-X11-forwarding
|
|
93
|
-
no-agent-forwarding no-pty)
|
|
94
|
-
|
|
95
|
-
def self.shell_key_for_username(username, key, debug = false)
|
|
96
|
-
key = self.new key if key.is_a? String
|
|
97
|
-
debug = debug ? "--debug " : ""
|
|
98
|
-
key.options = [%(command="gw-shell #{debug}#{username}"), *SHELL_OPTIONS]
|
|
99
|
-
key
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
60
|
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module GitWit
|
|
2
|
+
module AuthorizedKeys
|
|
3
|
+
class File < ::AuthorizedKeys::File
|
|
4
|
+
attr_accessor :original_location
|
|
5
|
+
|
|
6
|
+
def keys
|
|
7
|
+
list = []
|
|
8
|
+
modify "r" do |file|
|
|
9
|
+
file.each do |line|
|
|
10
|
+
list << Key.new(line.chomp)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
list
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def remove(key)
|
|
17
|
+
key = Key.new(key) if key.is_a?(String)
|
|
18
|
+
cached_keys = keys
|
|
19
|
+
modify 'w' do |file|
|
|
20
|
+
cached_keys.each do |k|
|
|
21
|
+
file.puts k unless key == k
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def owned?
|
|
27
|
+
owner == Process.uid
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def owner(file = nil)
|
|
31
|
+
file ||= location
|
|
32
|
+
::File.stat(file).uid
|
|
33
|
+
rescue Errno::EACCES, Errno::ENOENT
|
|
34
|
+
parent = ::File.dirname file
|
|
35
|
+
owner parent unless file == parent
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def modify(mode, &block)
|
|
39
|
+
return super if owned? || self.original_location
|
|
40
|
+
contents = %x(sudo -u "##{owner}" cat "#{location}") unless mode.include? "w"
|
|
41
|
+
original_owner = owner
|
|
42
|
+
self.original_location = location
|
|
43
|
+
tmp = Tempfile.new "git_wit_authorized_keys"
|
|
44
|
+
self.location = tmp.path
|
|
45
|
+
tmp.write contents unless mode.include? "w"
|
|
46
|
+
tmp.close
|
|
47
|
+
super
|
|
48
|
+
self.location = original_location
|
|
49
|
+
if mode != "r"
|
|
50
|
+
%x(cat "#{tmp.path}" | sudo -u "##{owner}" tee "#{location}" >/dev/null)
|
|
51
|
+
end
|
|
52
|
+
tmp.unlink
|
|
53
|
+
self.original_location = nil
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def clear(&block)
|
|
57
|
+
modify "w", &block
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module GitWit
|
|
2
|
+
module AuthorizedKeys
|
|
3
|
+
class Key < ::AuthorizedKeys::Key
|
|
4
|
+
SHELL_OPTIONS = %w(no-port-forwarding no-X11-forwarding
|
|
5
|
+
no-agent-forwarding no-pty)
|
|
6
|
+
|
|
7
|
+
def self.shell_key_for_username(username, key, debug = false)
|
|
8
|
+
key = self.new key if key.is_a? String
|
|
9
|
+
cmd = debug ? "debug" : "git-shell #{username}"
|
|
10
|
+
key.options = [%(command="git_wit #{cmd}"), *SHELL_OPTIONS]
|
|
11
|
+
key
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/git_wit/cli.rb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require "thor"
|
|
2
|
+
require "git_wit/commands/util"
|
|
3
|
+
require "git_wit/commands/git_shell"
|
|
4
|
+
require "git_wit/commands/debug"
|
|
5
|
+
|
|
6
|
+
module GitWit
|
|
7
|
+
class Cli < Thor
|
|
8
|
+
include Thor::Actions
|
|
9
|
+
include Commands::Util
|
|
10
|
+
include Commands::GitShell
|
|
11
|
+
include Commands::Debug
|
|
12
|
+
|
|
13
|
+
desc "debug", "debug the SSH configuration"
|
|
14
|
+
def debug(*args); super; end
|
|
15
|
+
|
|
16
|
+
desc "git-shell USER CMD REPO", "run git-shell CMD as USER in REPO"
|
|
17
|
+
def git_shell(*args); super; end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module GitWit
|
|
2
|
+
module Commands
|
|
3
|
+
module Debug
|
|
4
|
+
def debug
|
|
5
|
+
boot_app
|
|
6
|
+
debug_banner "Start"
|
|
7
|
+
pp "ENVIRONMENT:", ENV
|
|
8
|
+
GitWit.configure { |c| pp "GitWit Config:", c }
|
|
9
|
+
debug_banner "End"
|
|
10
|
+
$stdout.flush
|
|
11
|
+
exec_with_sudo!
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
protected
|
|
15
|
+
def debug_banner(msg = nil)
|
|
16
|
+
msg = "#{msg} " if msg.present?
|
|
17
|
+
puts "\n" * 2 + "*" * 5 + " GitWit DEBUG #{msg}" + "*" * 5 + "\n" * 2
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module GitWit
|
|
2
|
+
module Commands
|
|
3
|
+
module GitShell
|
|
4
|
+
GIT_SHELL_COMMAND_RE = /^(git-[^\s]+)\s+'([^']+\.git)'/
|
|
5
|
+
GIT_SHELL_COMMANDS = %w(git-upload-pack git-receive-pack git-upload-archive)
|
|
6
|
+
|
|
7
|
+
def git_shell(user, cmd = nil, repo = nil)
|
|
8
|
+
@command, @repository, @user = cmd, repo, nil
|
|
9
|
+
exec_with_sudo!
|
|
10
|
+
boot_app
|
|
11
|
+
parse_ssh_original_command if ENV["SSH_ORIGINAL_COMMAND"].present?
|
|
12
|
+
validate_git_shell_command
|
|
13
|
+
authenticate user
|
|
14
|
+
authorize
|
|
15
|
+
run_git_shell
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
protected
|
|
19
|
+
def parse_ssh_original_command
|
|
20
|
+
m = GIT_SHELL_COMMAND_RE.match ENV["SSH_ORIGINAL_COMMAND"]
|
|
21
|
+
nothing, @command, @repository = m.to_a if m.present?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def validate_git_shell_command
|
|
25
|
+
unless GIT_SHELL_COMMANDS.include? @command
|
|
26
|
+
abort "Unknown git shell command: #{@command}"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def authenticate(user)
|
|
31
|
+
@user = GitWit.user_for_authentication user
|
|
32
|
+
abort "Anonymous access denied via SSH" unless @user.present?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def authorize
|
|
36
|
+
op = @command == "git-receive-pack" ? :write : :read
|
|
37
|
+
abort "Unauthorized" unless GitWit.authorize op, @user, @repository
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def run_git_shell
|
|
41
|
+
repo_path = File.expand_path File.join(GitWit.repositories_path, @repository)
|
|
42
|
+
cmd = [GitWit.git_path, "shell", "-c", "#{@command} '#{repo_path}'"]
|
|
43
|
+
Rails.logger.info "GitWit SSH cmd: #{cmd.join " "}"
|
|
44
|
+
exec *cmd
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module GitWit
|
|
2
|
+
module Commands
|
|
3
|
+
module Util
|
|
4
|
+
def exec_with_sudo!(user = app_user)
|
|
5
|
+
return if running_as?(user)
|
|
6
|
+
Dir.chdir rails_root
|
|
7
|
+
ENV["TERM"] = "dumb"
|
|
8
|
+
cmd = ["sudo", "-u", "##{user}", $PROGRAM_NAME, *ARGV]
|
|
9
|
+
exec *cmd
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def running_as?(user)
|
|
13
|
+
Process.uid == user
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def app_user
|
|
17
|
+
File.stat(rails_root).uid
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def rails_root
|
|
21
|
+
return File.expand_path(ENV["RAILS_ROOT"]) if ENV["RAILS_ROOT"]
|
|
22
|
+
return Dir.pwd if File.exist? File.join(Dir.pwd, "config/environment.rb")
|
|
23
|
+
return File.expand_path("..", ENV["BUNDLE_GEMFILE"]) if ENV["BUNDLE_GEMFILE"]
|
|
24
|
+
Dir.pwd
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def boot_app
|
|
28
|
+
require File.expand_path File.join(rails_root, "config/environment") unless booted?
|
|
29
|
+
require "git_wit"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def booted?
|
|
33
|
+
defined?(Rails)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|