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