gitauth 0.0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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