mbbx6spp-gitauth 0.0.5.2
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 +16 -0
- data/lib/gitauth/apache_authentication.rb +64 -0
- data/lib/gitauth/auth_setup_middleware.rb +53 -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 +314 -0
- data/lib/gitauth.rb +101 -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 +196 -0
@@ -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
|
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
|
@@ -0,0 +1,87 @@
|
|
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
|
+
|
20
|
+
module GitAuth
|
21
|
+
class Group < SaveableClass(:groups)
|
22
|
+
include GitAuth::Loggable
|
23
|
+
|
24
|
+
attr_accessor :name, :members
|
25
|
+
|
26
|
+
def initialize(name)
|
27
|
+
@name = name
|
28
|
+
@members = []
|
29
|
+
end
|
30
|
+
|
31
|
+
def destroy!
|
32
|
+
GitAuth::Repo.all.each { |r| r.remove_permissions_for(self) }
|
33
|
+
self.class.all.each { |r| r.remove_member(self) }
|
34
|
+
self.class.all.reject! { |g| g == self }
|
35
|
+
GitAuth::Repo.save!
|
36
|
+
self.class.save!
|
37
|
+
end
|
38
|
+
|
39
|
+
def add_member(member)
|
40
|
+
return if member == self
|
41
|
+
@members << member.to_s
|
42
|
+
@members.uniq!
|
43
|
+
end
|
44
|
+
|
45
|
+
def remove_member(member)
|
46
|
+
@members.reject! { |m| m == member.to_s }
|
47
|
+
end
|
48
|
+
|
49
|
+
def ==(group)
|
50
|
+
group.is_a?(Group) && group.name == self.name
|
51
|
+
end
|
52
|
+
|
53
|
+
def member?(user_or_group, recurse = false, level = 0)
|
54
|
+
member = @members.include?(user_or_group.to_s)
|
55
|
+
Thread.current[:checked_groups] = [] if level == 0
|
56
|
+
if !member
|
57
|
+
return false if level > 0 && Thread.current[:checked_groups].include?(self)
|
58
|
+
Thread.current[:checked_groups] << self
|
59
|
+
member = recurse && @members.map { |m| Group.get(m) }.compact.any? { |g| g.member?(user_or_group, true, level + 1) }
|
60
|
+
end
|
61
|
+
Thread.current[:checked_groups] = nil if level == 0
|
62
|
+
return member
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_s
|
66
|
+
"@#{name}"
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.create(name)
|
70
|
+
name = name.to_s.strip.gsub(/^@/, "")
|
71
|
+
return false if name.empty? || name !~ /^([\w\_\-\.]+)$/
|
72
|
+
self.add_item self.new(name)
|
73
|
+
return true
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.get(name)
|
77
|
+
logger.debug "Getting group named #{name.inspect}"
|
78
|
+
real_name = name.to_s.gsub(/^@/, "")
|
79
|
+
self.all.detect { |g| g.name == real_name }
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.group?(name)
|
83
|
+
name.to_s =~ /^@/ && !get(name).nil?
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,69 @@
|
|
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 'yaml'
|
20
|
+
|
21
|
+
module GitAuth
|
22
|
+
class Message
|
23
|
+
|
24
|
+
TEMPLATES = YAML.load_file(BASE_DIR.join("resources", "messages.yml"))
|
25
|
+
|
26
|
+
attr_accessor :type, :name, :message, :variables
|
27
|
+
|
28
|
+
def initialize(type, name, variables = {})
|
29
|
+
@type = type
|
30
|
+
@name = name
|
31
|
+
@variables = {}
|
32
|
+
variables.each_pair { |k,v| @variables[k.to_s] = v }
|
33
|
+
auto_set_message!
|
34
|
+
end
|
35
|
+
|
36
|
+
def success?
|
37
|
+
@type.to_sym == :notice
|
38
|
+
end
|
39
|
+
|
40
|
+
def error?
|
41
|
+
@type.to_sym == :error
|
42
|
+
end
|
43
|
+
|
44
|
+
class << self
|
45
|
+
# Handy accessor / generate methods
|
46
|
+
# for a given error code.
|
47
|
+
|
48
|
+
def error(name = :unknown)
|
49
|
+
new(:error, name)
|
50
|
+
end
|
51
|
+
|
52
|
+
def notice(name = :unknown)
|
53
|
+
new(:notice, name)
|
54
|
+
end
|
55
|
+
|
56
|
+
def warning(name = :unknown)
|
57
|
+
new(:warning, name)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
protected
|
62
|
+
|
63
|
+
def auto_set_message!
|
64
|
+
raw_message = (TEMPLATES[@type.to_s] || {})[@name.to_s] || ""
|
65
|
+
@message = raw_message.gsub(/\:(\w+)/i) { |v| @variables[$1] || "" }
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
data/lib/gitauth/repo.rb
ADDED
@@ -0,0 +1,155 @@
|
|
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 'fileutils'
|
20
|
+
module GitAuth
|
21
|
+
class Repo < SaveableClass(:repositories)
|
22
|
+
include GitAuth::Loggable
|
23
|
+
|
24
|
+
NAME_RE = /^([\w\_\-\.\+]+(\.git)?)$/i
|
25
|
+
|
26
|
+
def self.get(name)
|
27
|
+
logger.debug "Getting Repo w/ name: '#{name}'"
|
28
|
+
(all || []).detect { |r| r.name == name }
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.create(name, path = name)
|
32
|
+
return false if name.nil? || path.nil?
|
33
|
+
return false if self.get(name) || self.all.any? { |r| r.path == path } || name !~ NAME_RE || path !~ NAME_RE
|
34
|
+
repository = new(name, path)
|
35
|
+
return false unless repository.create_repo!
|
36
|
+
add_item(repository)
|
37
|
+
repository
|
38
|
+
end
|
39
|
+
|
40
|
+
attr_accessor :name, :path, :permissions
|
41
|
+
|
42
|
+
def initialize(name, path, auto_create = false)
|
43
|
+
@name, @path = name, path
|
44
|
+
@permissions = {}
|
45
|
+
end
|
46
|
+
|
47
|
+
def ==(other)
|
48
|
+
other.is_a?(Repo) && other.name == name && other.path == path
|
49
|
+
end
|
50
|
+
|
51
|
+
def writeable_by(whom)
|
52
|
+
add_permissions :write, whom
|
53
|
+
end
|
54
|
+
|
55
|
+
def readable_by(whom)
|
56
|
+
add_permissions :read, whom
|
57
|
+
end
|
58
|
+
|
59
|
+
def update_permissions!(user, permissions = [])
|
60
|
+
remove_permissions_for(user)
|
61
|
+
writeable_by(user) if permissions.include?("write")
|
62
|
+
readable_by(user) if permissions.include?("read")
|
63
|
+
self.class.save!
|
64
|
+
end
|
65
|
+
|
66
|
+
def writeable_by?(user_or_group)
|
67
|
+
has_permissions_for :write, user_or_group
|
68
|
+
end
|
69
|
+
|
70
|
+
def readable_by?(user_or_group)
|
71
|
+
has_permissions_for :read, user_or_group
|
72
|
+
end
|
73
|
+
|
74
|
+
def remove_permissions_for(user_or_group)
|
75
|
+
@permissions.each_value do |val|
|
76
|
+
val.reject! { |m| m == user_or_group.to_s }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def real_path
|
81
|
+
File.join(GitAuth::Settings.base_path, @path)
|
82
|
+
end
|
83
|
+
|
84
|
+
def create_repo!
|
85
|
+
return false if !GitAuth.has_git?
|
86
|
+
unless File.directory?(real_path)
|
87
|
+
FileUtils.mkdir_p(real_path)
|
88
|
+
output = ""
|
89
|
+
Dir.chdir(real_path) { IO.popen("git --bare init") { |f| output << f.read } }
|
90
|
+
!!(output =~ /Initialized empty Git repository/)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def destroy!
|
95
|
+
FileUtils.rm_rf(real_path) if File.exist?(real_path)
|
96
|
+
self.class.all.reject! { |r| r == self }
|
97
|
+
self.class.save!
|
98
|
+
end
|
99
|
+
|
100
|
+
def make_empty!
|
101
|
+
tmp_path = "/tmp/gitauth-#{rand(100000)}-#{Time.now.to_i}"
|
102
|
+
logger.info "Creating temporary dir at #{tmp_path}"
|
103
|
+
FileUtils.mkdir_p("#{tmp_path}/current-repo")
|
104
|
+
logger.info "Changing to new directory"
|
105
|
+
Dir.chdir("#{tmp_path}/current-repo") do
|
106
|
+
logger.info "Marking as git repo"
|
107
|
+
GitAuth.run "git init"
|
108
|
+
logger.info "Touching .gitignore"
|
109
|
+
GitAuth.run "touch .gitignore"
|
110
|
+
# Configure it
|
111
|
+
GitAuth.run "git config push.default current"
|
112
|
+
logger.info "Commiting"
|
113
|
+
GitAuth.run "git add ."
|
114
|
+
GitAuth.run "git commit -am 'Initialize Empty Repository'"
|
115
|
+
# Push the changes to the actual repository
|
116
|
+
logger.info "Adding origin #{self.real_path}"
|
117
|
+
GitAuth.run "git remote add origin '#{self.real_path}'"
|
118
|
+
logger.info "Pushing..."
|
119
|
+
GitAuth.run "git push origin master"
|
120
|
+
end
|
121
|
+
ensure
|
122
|
+
logger.info "Cleaning up old tmp file"
|
123
|
+
FileUtils.rm_rf(tmp_path) if File.directory?(tmp_path)
|
124
|
+
end
|
125
|
+
|
126
|
+
def execute_post_create_hook!
|
127
|
+
script = File.expand_path("~/.gitauth/post-create")
|
128
|
+
if File.executable?(script)
|
129
|
+
system(script, @name, @path)
|
130
|
+
return $?.success?
|
131
|
+
else
|
132
|
+
# If there isn't a file, run it ourselves.
|
133
|
+
return true
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
protected
|
138
|
+
|
139
|
+
def add_permissions(type, whom)
|
140
|
+
@permissions[type] ||= []
|
141
|
+
@permissions[type] << whom.to_s
|
142
|
+
@permissions[type].uniq!
|
143
|
+
end
|
144
|
+
|
145
|
+
def has_permissions_for(type, whom)
|
146
|
+
whom = GitAuth.get_user_or_group(whom) if whom.is_a?(String)
|
147
|
+
logger.info "Checking if #{whom.to_s} can #{type} #{self.name}"
|
148
|
+
!(@permissions[type] || []).detect do |reader|
|
149
|
+
reader = GitAuth.get_user_or_group(reader)
|
150
|
+
reader == whom || (reader.is_a?(Group) && reader.member?(whom, true))
|
151
|
+
end.nil?
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,54 @@
|
|
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
|
+
|
21
|
+
class BasicSaveable
|
22
|
+
|
23
|
+
class_inheritable_accessor :all, :store_path
|
24
|
+
self.all = nil
|
25
|
+
|
26
|
+
class << self
|
27
|
+
|
28
|
+
def load!
|
29
|
+
self.all = YAML.load(File.read(store_path)) rescue nil if File.file?(store_path)
|
30
|
+
self.all = [] unless all.is_a?(Array)
|
31
|
+
end
|
32
|
+
|
33
|
+
def save!
|
34
|
+
load! if all.nil?
|
35
|
+
File.open(store_path, "w+") { |f| f.write all.to_yaml }
|
36
|
+
end
|
37
|
+
|
38
|
+
def add_item(item)
|
39
|
+
load! if all.nil?
|
40
|
+
all << item
|
41
|
+
save!
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.SaveableClass(kind)
|
48
|
+
klass = Class.new(BasicSaveable)
|
49
|
+
klass.store_path = GitAuth::GITAUTH_DIR.join("#{kind}.yml").to_s
|
50
|
+
klass.all = nil
|
51
|
+
return klass
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/lib/gitauth/user.rb
ADDED
@@ -0,0 +1,135 @@
|
|
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
|
+
|
20
|
+
module GitAuth
|
21
|
+
class User < SaveableClass(:users)
|
22
|
+
include GitAuth::Loggable
|
23
|
+
|
24
|
+
def self.get(name)
|
25
|
+
logger.debug "Getting user for the name '#{name}'"
|
26
|
+
(all || []).detect { |r| r.name == name }
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.create(name, admin, key)
|
30
|
+
# Basic sanity checking
|
31
|
+
return false if name.nil? || admin.nil? || key.nil?
|
32
|
+
# Require that the name is valid and admin is a boolean.
|
33
|
+
return false unless name =~ /^([\w\_\-\.]+)$/ && !!admin == admin
|
34
|
+
# Check there isn't an existing user
|
35
|
+
return false unless get(name).blank?
|
36
|
+
if (user = new(name, admin)).write_ssh_key!(key)
|
37
|
+
add_item(user)
|
38
|
+
return true
|
39
|
+
else
|
40
|
+
return false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
attr_reader :name, :admin
|
45
|
+
|
46
|
+
def initialize(name, admin = false)
|
47
|
+
@name = name
|
48
|
+
@admin = admin
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_s
|
52
|
+
@name.to_s
|
53
|
+
end
|
54
|
+
|
55
|
+
def write_ssh_key!(key)
|
56
|
+
cleaned_key = self.class.clean_ssh_key(key)
|
57
|
+
if cleaned_key.nil?
|
58
|
+
return false
|
59
|
+
else
|
60
|
+
output = "#{command_prefix} #{cleaned_key}"
|
61
|
+
File.open(GitAuth::Settings.authorized_keys_file, "a+") do |file|
|
62
|
+
file.puts output
|
63
|
+
end
|
64
|
+
return true
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def command_prefix
|
69
|
+
options = ["command=\"#{GitAuth::Settings.shell_executable} #{@name}\"",
|
70
|
+
"no-port-forwarding", "no-X11-forwarding", "no-agent-forwarding"]
|
71
|
+
options << "no-pty" if !shell_accessible?
|
72
|
+
options.join(",")
|
73
|
+
end
|
74
|
+
|
75
|
+
def destroy!
|
76
|
+
GitAuth::Repo.all.each { |r| r.remove_permissions_for(self) }
|
77
|
+
GitAuth::Group.all.each { |g| g.remove_member(self) }
|
78
|
+
# Remove the public key from the authorized_keys file.
|
79
|
+
auth_keys_path = GitAuth::Settings.authorized_keys_file
|
80
|
+
if File.exist?(auth_keys_path)
|
81
|
+
contents = File.read(auth_keys_path)
|
82
|
+
contents.gsub!(/#{command_prefix} ssh-\w+ [a-zA-Z0-9\/\+]+==\r?\n?/m, "")
|
83
|
+
File.open(auth_keys_path, "w+") { |f| f.write contents }
|
84
|
+
end
|
85
|
+
self.class.all.reject! { |u| u == self }
|
86
|
+
# Finally, save everything
|
87
|
+
self.class.save!
|
88
|
+
GitAuth::Repo.save!
|
89
|
+
GitAuth::Group.save!
|
90
|
+
end
|
91
|
+
|
92
|
+
def groups
|
93
|
+
(Group.all || []).select { |g| g.member?(self) }
|
94
|
+
end
|
95
|
+
|
96
|
+
def admin?
|
97
|
+
!!@admin
|
98
|
+
end
|
99
|
+
|
100
|
+
alias shell_accessible? admin?
|
101
|
+
|
102
|
+
def pushable?(repo)
|
103
|
+
admin? || repo.writeable_by?(self)
|
104
|
+
end
|
105
|
+
|
106
|
+
def pullable?(repo)
|
107
|
+
admin? || repo.readable_by?(self)
|
108
|
+
end
|
109
|
+
|
110
|
+
def can_execute?(command, repo)
|
111
|
+
return if command.bad?
|
112
|
+
if command.write?
|
113
|
+
logger.debug "Checking if #{self.name} can push to #{repo.name}"
|
114
|
+
pushable?(repo)
|
115
|
+
else
|
116
|
+
logger.debug "Checking if #{self.name} can pull from #{repo.name}"
|
117
|
+
pullable?(repo)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.clean_ssh_key(key)
|
122
|
+
if key =~ /^(ssh-\w+ [a-zA-Z0-9\/\+]+==?).*$/
|
123
|
+
return $1
|
124
|
+
else
|
125
|
+
return nil
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.valid_key?(key)
|
130
|
+
clean_ssh_key(key).present?
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
Users = User # For Backwards Compat.
|
135
|
+
end
|