gitauth 0.0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +661 -0
- data/README.rdoc +141 -0
- data/USAGE +70 -0
- data/bin/gitauth +261 -0
- data/bin/gitauth-shell +30 -0
- data/config.ru +22 -0
- data/lib/gitauth.rb +100 -0
- data/lib/gitauth/apache_authentication.rb +64 -0
- data/lib/gitauth/auth_setup_middleware.rb +44 -0
- data/lib/gitauth/client.rb +95 -0
- data/lib/gitauth/command.rb +101 -0
- data/lib/gitauth/group.rb +87 -0
- data/lib/gitauth/message.rb +69 -0
- data/lib/gitauth/repo.rb +155 -0
- data/lib/gitauth/saveable_class.rb +54 -0
- data/lib/gitauth/user.rb +135 -0
- data/lib/gitauth/web_app.rb +310 -0
- data/public/gitauth.css +316 -0
- data/public/gitauth.js +17 -0
- data/public/jquery.js +19 -0
- data/resources/messages.yml +9 -0
- data/views/auth_setup.erb +27 -0
- data/views/clone_repo.erb +24 -0
- data/views/group.erb +24 -0
- data/views/index.erb +88 -0
- data/views/layout.erb +27 -0
- data/views/repo.erb +57 -0
- data/views/user.erb +51 -0
- metadata +152 -0
data/bin/gitauth-shell
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#--
|
4
|
+
# Copyright (C) 2009 Brown Beagle Software
|
5
|
+
# Copyright (C) 2009 Darcy Laycock <sutto@sutto.net>
|
6
|
+
#
|
7
|
+
# This program is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Affero General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# This program is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Affero General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Affero General Public License
|
18
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
#++
|
20
|
+
|
21
|
+
|
22
|
+
base = __FILE__
|
23
|
+
# Get out of symlink-hell and then require the gitauth file.
|
24
|
+
base = File.readlink(base) while File.symlink?(base)
|
25
|
+
|
26
|
+
require File.expand_path(File.join(File.dirname(base), "..", "lib", "gitauth"))
|
27
|
+
|
28
|
+
# Start the cli client.
|
29
|
+
GitAuth::Client.start!(ARGV[0], ENV["SSH_ORIGINAL_COMMAND"])
|
30
|
+
|
data/config.ru
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
vendor_dir = File.join(File.dirname(__FILE__), "vendor")
|
2
|
+
|
3
|
+
require File.join(vendor_dir, "rack", "lib", "rack")
|
4
|
+
require File.join(vendor_dir, "sinatra", "lib", "sinatra")
|
5
|
+
|
6
|
+
require File.join(File.dirname(__FILE__), "lib", "gitauth")
|
7
|
+
require GitAuth::BASE_DIR.join("lib", "gitauth", "web_app")
|
8
|
+
|
9
|
+
GitAuth::Settings.setup!
|
10
|
+
|
11
|
+
output = File.open(GitAuth::Logger.default_logger_path, "a+")
|
12
|
+
|
13
|
+
STDOUT.reopen(output)
|
14
|
+
STDERR.reopen(output)
|
15
|
+
|
16
|
+
{:root => GitAuth::BASE_DIR, :run => false, :env => :production}.each_pair do |key, value|
|
17
|
+
GitAuth::WebApp.configure do
|
18
|
+
set key, value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
run GitAuth::WebApp.new
|
data/lib/gitauth.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (C) 2009 Brown Beagle Software
|
3
|
+
# Copyright (C) 2009 Darcy Laycock <sutto@sutto.net>
|
4
|
+
#
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU Affero General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU Affero General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Affero General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
#++
|
18
|
+
|
19
|
+
require 'pathname'
|
20
|
+
|
21
|
+
# Prepend lib dir + any vendored lib's to the front of the load
|
22
|
+
# path to ensure they're loaded first.
|
23
|
+
$LOAD_PATH.unshift(*Dir[Pathname(__FILE__).dirname.join("../{lib,vendor/*/lib}").expand_path])
|
24
|
+
|
25
|
+
require 'perennial'
|
26
|
+
|
27
|
+
module GitAuth
|
28
|
+
include Perennial
|
29
|
+
include Loggable
|
30
|
+
|
31
|
+
VERSION = [0, 0, 5, 0]
|
32
|
+
BASE_DIR = Pathname(__FILE__).dirname.join("..").expand_path
|
33
|
+
GITAUTH_DIR = Pathname("~/.gitauth/").expand_path
|
34
|
+
|
35
|
+
manifest do |m, l|
|
36
|
+
Settings.root = File.dirname(__FILE__)
|
37
|
+
Settings.default_settings_path = GITAUTH_DIR.join("settings.yml")
|
38
|
+
Settings.lookup_key_path = []
|
39
|
+
Logger.default_logger_path = GITAUTH_DIR.join("gitauth.log")
|
40
|
+
# Register stuff on the loader.
|
41
|
+
l.register_controller :web_app, 'GitAuth::WebApp'
|
42
|
+
l.before_run { GitAuth.prepare }
|
43
|
+
end
|
44
|
+
|
45
|
+
require 'gitauth/message' # Basic error messages etc (as of yet unushed)
|
46
|
+
require 'gitauth/saveable_class' # Simple YAML store for dumpables classes
|
47
|
+
require 'gitauth/repo' # The basic GitAuth repo object
|
48
|
+
require 'gitauth/user' # The basic GitAuth user object
|
49
|
+
require 'gitauth/group' # The basic GitAuth group object (collection of users)
|
50
|
+
require 'gitauth/command' # Processes / filters commands
|
51
|
+
require 'gitauth/client' # Handles the actual SSH interaction / bringing it together
|
52
|
+
|
53
|
+
autoload :AuthSetupMiddleware, 'gitauth/auth_setup_middleware'
|
54
|
+
autoload :ApacheAuthentication, 'gitauth/apache_authentication'
|
55
|
+
autoload :WebApp, 'gitauth/web_app'
|
56
|
+
|
57
|
+
class << self
|
58
|
+
|
59
|
+
def prepare
|
60
|
+
GitAuth::Settings.setup!
|
61
|
+
reload_models!
|
62
|
+
end
|
63
|
+
|
64
|
+
def version
|
65
|
+
VERSION.join(".")
|
66
|
+
end
|
67
|
+
|
68
|
+
def msg(type, message)
|
69
|
+
Message.new(type, message)
|
70
|
+
end
|
71
|
+
|
72
|
+
def get_user_or_group(name)
|
73
|
+
name = name.to_s.strip
|
74
|
+
return if name.empty?
|
75
|
+
(name =~ /^@/ ? Group : User).get(name)
|
76
|
+
end
|
77
|
+
|
78
|
+
def has_git?
|
79
|
+
!`which git`.blank?
|
80
|
+
end
|
81
|
+
|
82
|
+
def each_model(method = nil, &blk)
|
83
|
+
[Repo, User, Group].each { |m| m.send(method) } if method.present?
|
84
|
+
[Repo, User, Group].each(&blk) unless blk.nil?
|
85
|
+
end
|
86
|
+
|
87
|
+
def reload_models!
|
88
|
+
each_model(:load!)
|
89
|
+
end
|
90
|
+
|
91
|
+
def run(command)
|
92
|
+
GitAuth::Logger.info "Running command: #{command}"
|
93
|
+
result = system "#{command} 2> /dev/null 1> /dev/null"
|
94
|
+
GitAuth::Logger.info "Command was #{"not " if !result}successful"
|
95
|
+
return result
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'digest/sha2'
|
3
|
+
|
4
|
+
module GitAuth
|
5
|
+
class ApacheAuthentication
|
6
|
+
PUBLIC_DIR = GitAuth::BASE_DIR.join("public")
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
def setup?
|
11
|
+
PUBLIC_DIR.join(".htaccess").file? && PUBLIC_DIR.join(".htpasswd").file?
|
12
|
+
end
|
13
|
+
|
14
|
+
def setup
|
15
|
+
GitAuth::WebApp.check_auth
|
16
|
+
puts "To continue, we require you re-enter the password you wish to use."
|
17
|
+
raw_password = ''
|
18
|
+
password_hash = ''
|
19
|
+
existing_hash = GitAuth::Settings.web_password_hash
|
20
|
+
while raw_password.blank? && password_hash != existing_hash
|
21
|
+
raw_password = read_password('GitAuth Password: ')
|
22
|
+
password_hash = sha256_password(raw_password)
|
23
|
+
if raw_password.blank?
|
24
|
+
puts "You need to provide a password, please try again"
|
25
|
+
elsif password_hash != existing_hash
|
26
|
+
puts "Your password doesn't match the stored password. Please try again."
|
27
|
+
end
|
28
|
+
end
|
29
|
+
raw_username = GitAuth::Settings.web_username
|
30
|
+
encoded_password = "{SHA}#{[Digest::SHA1.digest(raw_password)].pack('m').strip}"
|
31
|
+
File.open(PUBLIC_DIR.join(".htpasswd"), "w+") do |file|
|
32
|
+
file.puts "#{raw_username}:#{encoded_password}"
|
33
|
+
end
|
34
|
+
File.open(PUBLIC_DIR.join(".htaccess"), "w+") do |file|
|
35
|
+
file.puts "AuthType Basic"
|
36
|
+
file.puts "AuthName \"GitAuth\""
|
37
|
+
file.puts "AuthUserFile #{PUBLIC_DIR.join(".htpasswd").expand_path}"
|
38
|
+
file.puts "Require valid-user"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def remove
|
43
|
+
PUBLIC_DIR.join(".htaccess").delete
|
44
|
+
PUBLIC_DIR.join(".htpasswd").delete
|
45
|
+
rescue Errno::ENOENT
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
|
50
|
+
def read_password(text)
|
51
|
+
system "stty -echo"
|
52
|
+
password = Readline.readline(text)
|
53
|
+
system "stty echo"
|
54
|
+
print "\n"
|
55
|
+
return password
|
56
|
+
end
|
57
|
+
|
58
|
+
def sha256_password(pass)
|
59
|
+
Digest::SHA256.hexdigest(pass)
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (C) 2009 Brown Beagle Software
|
3
|
+
# Copyright (C) 2009 Darcy Laycock <sutto@sutto.net>
|
4
|
+
#
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU Affero General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU Affero General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Affero General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
#++
|
18
|
+
|
19
|
+
module GitAuth
|
20
|
+
class AuthSetupMiddleware
|
21
|
+
|
22
|
+
def initialize(app)
|
23
|
+
@app = app
|
24
|
+
@files = Rack::File.new(GitAuth::BASE_DIR.join("public").to_s)
|
25
|
+
end
|
26
|
+
|
27
|
+
def call(env)
|
28
|
+
dup._call(env)
|
29
|
+
end
|
30
|
+
|
31
|
+
def _call(env)
|
32
|
+
if GitAuth::WebApp.has_auth?
|
33
|
+
@app.call(env)
|
34
|
+
elsif env["PATH_INFO"].include?("/gitauth.css")
|
35
|
+
@files.call(env)
|
36
|
+
else
|
37
|
+
content = ERB.new(File.read(GitAuth::BASE_DIR.join("views", "auth_setup.erb"))).result
|
38
|
+
headers = {"Content-Type" => "text/html", "Content-Length" => Rack::Utils.bytesize(content).to_s}
|
39
|
+
[403, headers, [content]]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (C) 2009 Brown Beagle Software
|
3
|
+
# Copyright (C) 2009 Darcy Laycock <sutto@sutto.net>
|
4
|
+
# Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
|
5
|
+
# Copyright (C) 2007, 2008 Johan Sørensen <johan@johansorensen.com>
|
6
|
+
# Copyright (C) 2008 Tor Arne Vestbø <tavestbo@trolltech.com>
|
7
|
+
#
|
8
|
+
# This program is free software: you can redistribute it and/or modify
|
9
|
+
# it under the terms of the GNU Affero General Public License as published by
|
10
|
+
# the Free Software Foundation, either version 3 of the License, or
|
11
|
+
# (at your option) any later version.
|
12
|
+
#
|
13
|
+
# This program is distributed in the hope that it will be useful,
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
16
|
+
# GNU Affero General Public License for more details.
|
17
|
+
#
|
18
|
+
# You should have received a copy of the GNU Affero General Public License
|
19
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
20
|
+
#++
|
21
|
+
|
22
|
+
module GitAuth
|
23
|
+
class Client
|
24
|
+
include GitAuth::Loggable
|
25
|
+
|
26
|
+
attr_accessor :user, :command
|
27
|
+
|
28
|
+
def initialize(user_name, command)
|
29
|
+
logger.debug "Initializing client with command: #{command.inspect} and user name #{user_name.inspect}"
|
30
|
+
@callbacks = Hash.new { |h,k| h[k] = [] }
|
31
|
+
@user = GitAuth::User.get(user_name.to_s.strip)
|
32
|
+
@command = command
|
33
|
+
end
|
34
|
+
|
35
|
+
def exit_with_error(error)
|
36
|
+
logger.warn "Exiting with error: #{error}"
|
37
|
+
$stderr.puts error
|
38
|
+
exit! 1
|
39
|
+
end
|
40
|
+
|
41
|
+
def run!
|
42
|
+
if @user.nil?
|
43
|
+
exit_with_error "An invalid user / key was used. Please ensure it is setup with GitAuth"
|
44
|
+
elsif @command.to_s.strip.empty?
|
45
|
+
if user.shell_accessible?
|
46
|
+
exec(ENV["SHELL"])
|
47
|
+
else
|
48
|
+
exit_with_error "SSH_ORIGINAL_COMMAND is needed, mmmkay?"
|
49
|
+
end
|
50
|
+
else
|
51
|
+
command = Command.parse(@command)
|
52
|
+
repo = command.bad? ? nil : Repo.get(extract_repo_name(command))
|
53
|
+
if command.bad?
|
54
|
+
if user.shell_accessible?
|
55
|
+
exec(@command)
|
56
|
+
else
|
57
|
+
exit_with_error "Invalid ssh command - Access Denied"
|
58
|
+
end
|
59
|
+
elsif repo.nil?
|
60
|
+
exit_with_error "Unable to push to a non-existant repository"
|
61
|
+
elsif user.can_execute?(command, repo)
|
62
|
+
git_shell_argument = "#{command.verb} '#{repo.real_path}'"
|
63
|
+
logger.info "Running command: #{git_shell_argument} for user: #{@user.name}"
|
64
|
+
exec("git-shell", "-c", git_shell_argument)
|
65
|
+
else
|
66
|
+
exit_with_error "Unable to execute command on this repository"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
rescue Exception => e
|
70
|
+
logger.fatal "Exception: #{e.class.name}: #{e.message}"
|
71
|
+
e.backtrace.each do |l|
|
72
|
+
logger.fatal " => #{l}"
|
73
|
+
end
|
74
|
+
exit_with_error "Exception raised - Please check your gitauth log / contact an administrator"
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.start!(user, command)
|
78
|
+
# Gitorious does it so I should too!
|
79
|
+
File.umask(0022)
|
80
|
+
# Setup models etc
|
81
|
+
GitAuth.prepare
|
82
|
+
# Finally, create and initialize
|
83
|
+
client = self.new(user, command)
|
84
|
+
yield client if block_given?
|
85
|
+
client.run!
|
86
|
+
end
|
87
|
+
|
88
|
+
protected
|
89
|
+
|
90
|
+
def extract_repo_name(command)
|
91
|
+
command.path.gsub(/\.git$/, "")
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (C) 2009 Brown Beagle Software
|
3
|
+
# Copyright (C) 2009 Darcy Laycock <sutto@sutto.net>
|
4
|
+
# Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
|
5
|
+
# Copyright (C) 2007, 2008 Johan Sørensen <johan@johansorensen.com>
|
6
|
+
# Copyright (C) 2008 Tim Dysinger <tim@dysinger.net>
|
7
|
+
# Copyright (C) 2008 Tor Arne Vestbø <tavestbo@trolltech.com>
|
8
|
+
#
|
9
|
+
# This program is free software: you can redistribute it and/or modify
|
10
|
+
# it under the terms of the GNU Affero General Public License as published by
|
11
|
+
# the Free Software Foundation, either version 3 of the License, or
|
12
|
+
# (at your option) any later version.
|
13
|
+
#
|
14
|
+
# This program is distributed in the hope that it will be useful,
|
15
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
16
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
17
|
+
# GNU Affero General Public License for more details.
|
18
|
+
#
|
19
|
+
# You should have received a copy of the GNU Affero General Public License
|
20
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
21
|
+
#++
|
22
|
+
|
23
|
+
# This along with the associated gitauth-shell command are inspired by
|
24
|
+
# (and essentially, derived from) gitosis (http://eagain.net/gitweb/?p=gitosis.git)
|
25
|
+
# and gitorius (http://gitorius.org)
|
26
|
+
# Gitosis is of this writing licensed under the GPLv2 and is copyright (c) Tommi Virtanen
|
27
|
+
# and can be found at http://eagain.net/gitweb/?p=gitosis.git
|
28
|
+
# GitAuth::Command is licensed under the same license
|
29
|
+
|
30
|
+
module GitAuth
|
31
|
+
class Command
|
32
|
+
|
33
|
+
# Standard Commands
|
34
|
+
READ_COMMANDS = ["git-upload-pack", "git upload-pack"]
|
35
|
+
WRITE_COMMANDS = ["git-receive-pack", "git receive-pack"]
|
36
|
+
PATH_REGEXP = /^'([\w\_\-\~\.\+\/]+\/)?([\w\_\-\.\+]+(\.git)?)'$/i.freeze
|
37
|
+
|
38
|
+
attr_reader :path, :verb, :command
|
39
|
+
|
40
|
+
def initialize(command)
|
41
|
+
@command = command
|
42
|
+
@verb = nil
|
43
|
+
@argument = nil
|
44
|
+
@path = nil
|
45
|
+
@bad_command = true
|
46
|
+
end
|
47
|
+
|
48
|
+
def bad?
|
49
|
+
@bad_command
|
50
|
+
end
|
51
|
+
|
52
|
+
def write?
|
53
|
+
!bad? && @verb_type == :write
|
54
|
+
end
|
55
|
+
|
56
|
+
def read?
|
57
|
+
!bad? && !write?
|
58
|
+
end
|
59
|
+
|
60
|
+
def process
|
61
|
+
return if @command.include?("\n") || @command !~ /^git[ \-]/i
|
62
|
+
@verb, @argument = split_command
|
63
|
+
return if @argument.nil? || @argument.is_a?(Array)
|
64
|
+
# Check if it's read / write
|
65
|
+
if READ_COMMANDS.include?(@verb)
|
66
|
+
@verb_type = :read
|
67
|
+
elsif WRITE_COMMANDS.include?(@verb)
|
68
|
+
@verb_type = :write
|
69
|
+
else
|
70
|
+
return
|
71
|
+
end
|
72
|
+
if PATH_REGEXP =~ @argument
|
73
|
+
@path = $2
|
74
|
+
return unless @path
|
75
|
+
else
|
76
|
+
return
|
77
|
+
end
|
78
|
+
@bad_command = false
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.parse(command)
|
82
|
+
command = self.new(command)
|
83
|
+
command.process
|
84
|
+
command
|
85
|
+
end
|
86
|
+
|
87
|
+
protected
|
88
|
+
|
89
|
+
def split_command
|
90
|
+
parts = @command.split(" ")
|
91
|
+
if parts.size == 3
|
92
|
+
["#{parts[0]} #{parts[1]}", parts[2]]
|
93
|
+
elsif parts.size == 2
|
94
|
+
parts
|
95
|
+
else
|
96
|
+
raise BadCommandError
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|