gitauth 0.0.5.0
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/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
|