mbbx6spp-gitauth 0.0.5.2

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.
@@ -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
@@ -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
@@ -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