brownbeagle-gitauth 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -48,9 +48,19 @@ Would initialize an admin user with the given public key.
48
48
  Note that from now on, all gitauth keys should be run either logged in as
49
49
  git (via the admin user and ssh) or by being prefixed with asgit or "sudo -H -u git"
50
50
 
51
+ === Web Interface
52
+
53
+ To start the web interface, just run:
54
+
55
+ gitauth webapp
56
+
57
+ The first time you boot the web app, you will be prompted
58
+ to enter a username and a password. Please do so
59
+ and then surf to http://your-server-ip:8998/
60
+
51
61
  === Adding Users
52
62
 
53
- Whenever you want to add a user, it's ass imple as:
63
+ Whenever you want to add a user, it's as simple as:
54
64
 
55
65
  gitauth adduser user-name path-to-public-key
56
66
 
data/bin/gitauth CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  #--
4
- # Copyright (C) 2009 BrownBeagle
4
+ # Copyright (C) 2009 Brown Beagle Software
5
5
  # Copyright (C) 2008 Darcy Laycock <sutto@sutto.net>
6
6
  #
7
7
  # This program is free software: you can redistribute it and/or modify
@@ -26,6 +26,7 @@ require File.join(File.dirname(__FILE__), "..", "lib", "gitauth")
26
26
 
27
27
  class GitAuthRunner < Thor
28
28
 
29
+ # Adding users, groups and repos
29
30
 
30
31
  desc "addrepo REPO-NAME [PATH-PART]", "Adds a new repository"
31
32
  def addrepo(name, path = name)
@@ -43,7 +44,7 @@ class GitAuthRunner < Thor
43
44
  def adduser(name, key_path)
44
45
  GitAuth.setup!
45
46
  admin = !!(options && options[:admin])
46
- if GitAuth::Users.create(name, admin, File.read(key_path).strip)
47
+ if GitAuth::User.create(name, admin, File.read(key_path).strip)
47
48
  $stdout.puts "User added"
48
49
  else
49
50
  $stderr.puts "There was an error adding the given user"
@@ -51,41 +52,34 @@ class GitAuthRunner < Thor
51
52
  end
52
53
  end
53
54
 
54
- desc "repos", "Lists all the current repos"
55
- def repos
55
+ desc "addgroup NAME", "Adds a group with the specified name"
56
+ def addgroup(name)
56
57
  GitAuth.setup!
57
- $stdout.puts "repositories:"
58
- GitAuth::Repo.all.each do |repo|
59
- $stdout.puts " - #{repo.name}"
60
- end
61
- end
62
-
63
- desc "users", "Lists all users in the system"
64
- def users
65
- GitAuth.setup!
66
- $stdout.puts "users:"
67
- GitAuth::Users.all.each do |user|
68
- $stdout.puts "- #{user.name}"
58
+ if GitAuth::Group.create(name)
59
+ $stdout.puts "The group was added"
60
+ else
61
+ $stderr.puts "There was an error creating the aforementioned group"
62
+ exit! 1
69
63
  end
70
64
  end
71
65
 
66
+ # Misc. operations
72
67
 
73
- desc "permissions REPO USER [PERMISION=all,read,write]", "Adds Permissions for a user to a repository"
74
- def permissions(repo, user, permissions = "all")
68
+ desc "permissions REPO USERORGROUP [PERMISION=all,read,write]", "Adds Permissions for a user or group to a repository"
69
+ def permissions(repo, user_or_group, permissions = "all")
75
70
  GitAuth.setup!
76
71
  unless %w(read write all).include?(permissions)
77
72
  $stderr.puts "Invalid permissions: #{permissions}"
78
73
  exit! 1
79
74
  end
80
75
  repo = GitAuth::Repo.get(repo)
81
- user = GitAuth::Users.get(user)
82
- if repo.nil? || user.nil?
76
+ uog = GitAuth.get_user_or_group(user_or_group)
77
+ if repo.nil? || uog.nil?
83
78
  $stderr.puts "Invalid repository or user, please check the name"
84
79
  exit! 1
85
80
  end
86
- repo.writeable_by(user) if %w(all write).include?(permissions)
87
- repo.readable_by(user) if %w(all read).include?(permissions)
88
- GitAuth::Users.save!
81
+ repo.writeable_by(uog) if %w(all write).include?(permissions)
82
+ repo.readable_by(uog) if %w(all read).include?(permissions)
89
83
  GitAuth::Repo.save!
90
84
  $stdout.puts "Permissions Added"
91
85
  end
@@ -99,6 +93,10 @@ class GitAuthRunner < Thor
99
93
  $stderr.puts "Please log in as the correct user and re-run"
100
94
  exit! 1
101
95
  end
96
+ if !GitAuth::Repo.has_git?
97
+ $stderr.puts "'git' was not found in your path - please install it before continuing."
98
+ exit! 1
99
+ end
102
100
  require 'fileutils'
103
101
  folder = File.expand_path("~/.ssh")
104
102
  if !File.exist?(folder) || !File.directory?(folder)
@@ -139,7 +137,7 @@ class GitAuthRunner < Thor
139
137
  end
140
138
  if !public_key_path.nil? && File.exist?(public_key_path)
141
139
  GitAuth.setup!
142
- created = GitAuth::Users.create("admin", true, File.read(public_key_path).strip)
140
+ created = GitAuth::User.create("admin", true, File.read(public_key_path).strip)
143
141
  if created
144
142
  $stdout.puts "Admin User Created."
145
143
  else
@@ -153,6 +151,84 @@ class GitAuthRunner < Thor
153
151
  exit! 1
154
152
  end
155
153
 
154
+ # Viewing Users etc
155
+
156
+ desc "repos", "Lists all the current repos handled by gitauth"
157
+ def repos
158
+ GitAuth.setup!
159
+ $stdout.puts "Repositories:"
160
+ GitAuth::Repo.all.each do |repo|
161
+ line = " - #{repo.name}"
162
+ line << " (#{repo.path})" if repo.path != repo.name
163
+ $stdout.puts line
164
+ end
165
+ end
166
+
167
+ desc "users", "Lists all users handled by gitauth"
168
+ def users
169
+ GitAuth.setup!
170
+ $stdout.puts "Users:"
171
+ GitAuth::User.all.each do |user|
172
+ line = "- #{user}"
173
+ line << " (admin)" if user.admin?
174
+ $stdout.puts line
175
+ end
176
+ end
177
+
178
+ desc "groups", "Lists all groups handled by gitauth"
179
+ def groups
180
+ GitAuth.setup!
181
+ $stdout.puts "Groups:"
182
+ GitAuth::Group.all.each do |group|
183
+ $stdout.puts "- #{group} - #{group.members.empty? ? "no members" : group.members.join(", ")}"
184
+ end
185
+ end
186
+
187
+ desc "webapp", "starts serving the GitAuth web-app on Port 8998"
188
+ def webapp
189
+ s = GitAuth.settings
190
+ if s.web_username.to_s.empty? || s.web_password_hash.to_s.empty?
191
+ $stdout.puts "To use the web interface you must first setup some credentials:"
192
+ $stdout.print "What username would you like to use? (default is 'gitauth'): "
193
+ username = Readline.readline.strip
194
+ username = "gitauth" if username.empty?
195
+ $stdout.print "What password would you like to use?: "
196
+ password = read_password
197
+ while password.empty?
198
+ $stdout.print "Please try again, What password would you like to use?: "
199
+ password = read_password
200
+ end
201
+ print "Please enter your password again: "
202
+ confirmation = read_password
203
+ while confirmation != password
204
+ print "Wrong password, please confirm again: "
205
+ confirmation = read_password
206
+ end
207
+ require 'digest/sha2'
208
+ settings = YAML.load_file(File.join(GitAuth::GITAUTH_DIR, "settings.yml"))
209
+ settings.merge!({
210
+ "web_username" => username,
211
+ "web_password_hash" => Digest::SHA256.hexdigest(password)
212
+ })
213
+ File.open(File.join(GitAuth::GITAUTH_DIR, "settings.yml"), "w+") { |f| f.write settings.to_yaml }
214
+ puts "Username and Password saved."
215
+ GitAuth.reload_settings!
216
+ end
217
+ GitAuth.serve_web!
218
+ rescue Interrupt
219
+ exit! 1
220
+ end
221
+
222
+ protected
223
+
224
+ def read_password
225
+ system "stty -echo"
226
+ line = Readline.readline.strip
227
+ system "stty echo"
228
+ print "\n"
229
+ return line
230
+ end
231
+
156
232
  end
157
233
 
158
234
  if ARGV.empty?
data/bin/gitauth-shell CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  #--
4
- # Copyright (C) 2009 BrownBeagle
4
+ # Copyright (C) 2009 Brown Beagle Software
5
5
  # Copyright (C) 2008 Darcy Laycock <sutto@sutto.net>
6
6
  #
7
7
  # This program is free software: you can redistribute it and/or modify
@@ -32,35 +32,5 @@ File.umask(0022)
32
32
  user_name = ARGV[0]
33
33
  command = ENV["SSH_ORIGINAL_COMMAND"]
34
34
 
35
- GitAuth::Client.start!(user_name, command) do |c|
36
-
37
- c.on(:invalid_user) do |c|
38
- c.exit_with_error "An invalid user / key was used. Please ensure it is setup with GitAuth"
39
- end
40
-
41
- c.on(:invalid_command) do |c|
42
- if c.user.shell_accessible?
43
- exec(ENV["SHELL"])
44
- else
45
- c.exit_with_error "SSH_ORIGINAL_COMMAND is needed, mmmkay?"
46
- end
47
- end
48
-
49
- c.on(:invalid_repository) do |c|
50
- c.exit_with_error "Ze repository you specified does not exist."
51
- end
52
-
53
- c.on(:bad_command) do |c|
54
- c.exit_with_error "A Bad Command Has Failed Ye, Thou Shalt Not Continue."
55
- end
56
-
57
- c.on(:access_denied) do |c|
58
- c.exit_with_error "These are not the droids you are looking for"
59
- end
60
-
61
- c.on(:fatal_error) do |c|
62
- c.exit_with_error "Holy crap, we've imploded cap'n!"
63
- end
64
-
65
- end
35
+ GitAuth::Client.start!(user_name, command)
66
36
 
@@ -25,20 +25,12 @@ module GitAuth
25
25
  attr_accessor :user, :command
26
26
 
27
27
  def initialize(user_name, command)
28
- GitAuth.logger.debug "Initializing client with command: '#{command}' and user name '#{user_name}'"
28
+ GitAuth.logger.debug "Initializing client with command: #{command.inspect} and user name #{user_name.inspect}"
29
29
  @callbacks = Hash.new { |h,k| h[k] = [] }
30
- @user = GitAuth::Users.get(user_name.to_s.strip)
30
+ @user = GitAuth::User.get(user_name.to_s.strip)
31
31
  @command = command
32
32
  end
33
33
 
34
- def on(command, &blk)
35
- @callbacks[command.to_sym] << blk
36
- end
37
-
38
- def execute_callback!(command)
39
- @callbacks[command.to_sym].each { |c| c.call(self) }
40
- end
41
-
42
34
  def exit_with_error(error)
43
35
  GitAuth.logger.warn "Exiting with error: #{error}"
44
36
  $stderr.puts error
@@ -47,24 +39,30 @@ module GitAuth
47
39
 
48
40
  def run!
49
41
  if @user.nil?
50
- execute_callback! :invalid_user
42
+ exit_with_error "An invalid user / key was used. Please ensure it is setup with GitAuth"
51
43
  elsif @command.to_s.strip.empty?
52
- execute_callback! :invalid_command
44
+ if user.shell_accessible?
45
+ exec(ENV["SHELL"])
46
+ else
47
+ exit_with_error "SSH_ORIGINAL_COMMAND is needed, mmmkay?"
48
+ end
53
49
  else
54
50
  command = Command.parse!(@command)
55
- repo = Repo.get(extract_repo_name(command))
51
+ repo = command.bad? ? nil : Repo.get(extract_repo_name(command))
56
52
  if command.bad?
57
- execute_callback! :bad_command
53
+ if user.shell_accessible?
54
+ exec(@command)
55
+ else
56
+ exit_with_error "A Bad Command Has Failed Ye, Thou Shalt Not Continue."
57
+ end
58
58
  elsif repo.nil?
59
- execute_callback! :invalid_repository
59
+ exit_with_error "Ze repository you specified does not exist."
60
60
  elsif user.can_execute?(command, repo)
61
- # We can go ahead.
62
61
  git_shell_argument = "#{command.verb} '#{repo.real_path}'"
63
- # And execute that soab.
64
62
  GitAuth.logger.info "Running command: #{git_shell_argument} for user: #{@user.name}"
65
63
  exec("git-shell", "-c", git_shell_argument)
66
64
  else
67
- execute_callback! :access_denied
65
+ exit_with_error "These are not the droids you are looking for"
68
66
  end
69
67
  end
70
68
  rescue Exception => e
@@ -72,7 +70,7 @@ module GitAuth
72
70
  e.backtrace.each do |l|
73
71
  GitAuth.logger.fatal " => #{l}"
74
72
  end
75
- execute_callback! :fatal_error
73
+ exit_with_error "Holy crap, we've imploded cap'n!"
76
74
  end
77
75
 
78
76
  def self.start!(user, command)
@@ -34,7 +34,7 @@ module GitAuth
34
34
  # Standard Commands
35
35
  READ_COMMANDS = ["git-upload-pack", "git upload-pack"]
36
36
  WRITE_COMMANDS = ["git-receive-pack", "git receive-pack"]
37
- PATH_REGEXP = /^'([a-z0-9\-\+]+(\.git)?)'$/i.freeze
37
+ PATH_REGEXP = /^'([\w\_\-\.\+]+(\.git)?)'$/i.freeze
38
38
 
39
39
  attr_reader :path, :verb, :command
40
40
 
@@ -61,7 +61,7 @@ module GitAuth
61
61
  # These exceptions are FUGLY.
62
62
  # Clean up, mmkay?
63
63
  def process!
64
- raise BadCommandError if @command.include?("\n")
64
+ raise BadCommandError if @command.include?("\n") || @command !~ /^git/i
65
65
  @verb, @argument = split_command
66
66
  raise BadCommandError if @argument.nil? || @argument.is_a?(Array)
67
67
  # Check if it's read / write
@@ -0,0 +1,86 @@
1
+ #--
2
+ # Copyright (C) 2009 Brown Beagle Software
3
+ # Copyright (C) 2008 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
+
23
+ attr_accessor :name, :members
24
+
25
+ def initialize(name)
26
+ @name = name
27
+ @members = []
28
+ end
29
+
30
+ def destroy!
31
+ GitAuth::Repo.all.each { |r| r.remove_permissions_for(self) }
32
+ self.class.all.each { |r| r.remove_member(self) }
33
+ self.class.all.reject! { |g| g == self }
34
+ GitAuth::Repo.save!
35
+ self.class.save!
36
+ end
37
+
38
+ def add_member(member)
39
+ return if member == self
40
+ @members << member.to_s
41
+ @members.uniq!
42
+ end
43
+
44
+ def remove_member(member)
45
+ @members.reject! { |m| m == member.to_s }
46
+ end
47
+
48
+ def ==(group)
49
+ group.is_a?(Group) && group.name == self.name
50
+ end
51
+
52
+ def member?(user_or_group, recurse = false, level = 0)
53
+ member = @members.include?(user_or_group.to_s)
54
+ Thread.current[:checked_groups] = [] if level == 0
55
+ if !member
56
+ return false if level > 0 && Thread.current[:checked_groups].include?(self)
57
+ Thread.current[:checked_groups] << self
58
+ member = recurse && @members.map { |m| Group.get(m) }.compact.any? { |g| g.member?(user_or_group, true, level + 1) }
59
+ end
60
+ Thread.current[:checked_groups] = nil if level == 0
61
+ return member
62
+ end
63
+
64
+ def to_s
65
+ "@#{name}"
66
+ end
67
+
68
+ def self.create(name)
69
+ name = name.to_s.strip.gsub(/^@/, "")
70
+ return false if name.empty? || name !~ /^([\w\_\-\.]+)$/
71
+ self.add_item self.new(name)
72
+ return true
73
+ end
74
+
75
+ def self.get(name)
76
+ GitAuth.logger.debug "Getting group named #{name.inspect}"
77
+ real_name = name.to_s.gsub(/^@/, "")
78
+ self.all.detect { |g| g.name == real_name }
79
+ end
80
+
81
+ def self.group?(name)
82
+ name.to_s =~ /^@/ && !get(name).nil?
83
+ end
84
+
85
+ end
86
+ end
data/lib/gitauth/repo.rb CHANGED
@@ -18,28 +18,8 @@
18
18
 
19
19
  require 'fileutils'
20
20
  module GitAuth
21
- class Repo
22
- REPOS_PATH = File.join(GitAuth::GITAUTH_DIR, "repositories.yml")
23
-
24
- def self.all
25
- @@all_repositories ||= nil
26
- end
27
-
28
- def self.load!
29
- self.all = YAML.load_file(REPOS_PATH) rescue nil if File.exist?(REPOS_PATH)
30
- self.all = [] unless self.all.is_a?(Array)
31
- end
32
-
33
- def self.save!
34
- load! if self.all.nil?
35
- File.open(REPOS_PATH, "w+") do |f|
36
- f.write self.all.to_yaml
37
- end
38
- end
39
-
40
- def self.all=(value)
41
- @@all_repositories = value
42
- end
21
+ class Repo < SaveableClass(:repositories)
22
+ NAME_RE = /^([\w\_\-\.\+]+(\.git)?)$/i
43
23
 
44
24
  def self.get(name)
45
25
  GitAuth.logger.debug "Getting Repo w/ name: '#{name}'"
@@ -47,43 +27,55 @@ module GitAuth
47
27
  end
48
28
 
49
29
  def self.create(name, path = name)
50
- return false unless self.get(name).nil?
30
+ return false if name.nil? || path.nil?
31
+ return false if self.get(name) || self.all.any? { |r| r.path == path } || name !~ NAME_RE || path !~ NAME_RE
51
32
  repository = self.new(name, path)
52
- if repository.create_repo!
53
- self.load!
54
- self.all << repository
55
- self.save!
56
- return true
57
- else
58
- return false
59
- end
33
+ return false unless repository.create_repo!
34
+ self.add_item(repository)
35
+ return true
60
36
  end
61
37
 
62
- attr_accessor :name, :path
38
+ attr_accessor :name, :path, :permissions
63
39
 
64
40
  def initialize(name, path, auto_create = false)
65
41
  @name, @path = name, path
66
42
  @permissions = {}
67
43
  end
68
44
 
69
- def writeable_by(user)
45
+ def ==(other)
46
+ other.is_a?(Repo) && other.name == name && other.path == path
47
+ end
48
+
49
+ def writeable_by(user_or_group)
70
50
  @permissions[:write] ||= []
71
- @permissions[:write] << user.name
51
+ @permissions[:write] << user_or_group.to_s
72
52
  @permissions[:write].uniq!
73
53
  end
74
54
 
75
- def readable_by(user)
55
+ def readable_by(user_or_group)
76
56
  @permissions[:read] ||= []
77
- @permissions[:read] << user.name
57
+ @permissions[:read] << user_or_group.to_s
78
58
  @permissions[:read].uniq!
79
59
  end
80
60
 
81
- def writeable_by?(user)
82
- (@permissions[:write] || []).include? user.name
61
+ def writeable_by?(user_or_group)
62
+ !(@permissions[:write] || []).detect do |writer|
63
+ writer = GitAuth.get_user_or_group(writer)
64
+ writer == user_or_group || (writer.is_a?(Group) && writer.member?(user_or_group, true))
65
+ end.nil?
83
66
  end
84
67
 
85
- def readable_by?(user)
86
- (@permissions[:read] || []).include? user.name
68
+ def readable_by?(user_or_group)
69
+ !(@permissions[:read] || []).detect do |reader|
70
+ reader = GitAuth.get_user_or_group(reader)
71
+ reader == user_or_group || (reader.is_a?(Group) && reader.member?(user_or_group, true))
72
+ end.nil?
73
+ end
74
+
75
+ def remove_permissions_for(user_or_group)
76
+ @permissions.each_value do |val|
77
+ val.reject! { |m| m == user_or_group.to_s }
78
+ end
87
79
  end
88
80
 
89
81
  def real_path
@@ -91,6 +83,7 @@ module GitAuth
91
83
  end
92
84
 
93
85
  def create_repo!
86
+ return false if !self.class.has_git?
94
87
  path = self.real_path
95
88
  unless File.exist?(path) && File.directory?(path)
96
89
  FileUtils.mkdir_p(path)
@@ -102,5 +95,15 @@ module GitAuth
102
95
  end
103
96
  end
104
97
 
98
+ def destroy!
99
+ FileUtils.rm_rf(self.real_path) if File.exist?(self.real_path)
100
+ self.class.all.reject! { |r| r == self }
101
+ self.class.save!
102
+ end
103
+
104
+ def self.has_git?
105
+ !`which git`.strip.empty?
106
+ end
107
+
105
108
  end
106
109
  end
@@ -0,0 +1,60 @@
1
+ #--
2
+ # Copyright (C) 2009 Brown Beagle Software
3
+ # Copyright (C) 2008 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
+ def self.SaveableClass(kind)
22
+ klass = Class.new
23
+ path_name = "#{kind.to_s.upcase}_PATH"
24
+ yaml_file_name = "#{kind}.yml"
25
+
26
+ saveable_class_def = <<-END
27
+
28
+ #{path_name} = File.join(GitAuth::GITAUTH_DIR, #{yaml_file_name.inspect})
29
+
30
+ def self.all
31
+ @@all_#{kind} ||= nil
32
+ end
33
+
34
+ def self.all=(value)
35
+ @@all_#{kind} = value
36
+ end
37
+
38
+ def self.load!
39
+ self.all = YAML.load_file(#{path_name}) rescue nil if File.exist?(#{path_name})
40
+ self.all = [] unless self.all.is_a?(Array)
41
+ end
42
+
43
+ def self.save!
44
+ load! if self.all.nil?
45
+ File.open(#{path_name}, "w+") do |f|
46
+ f.write self.all.to_yaml
47
+ end
48
+ end
49
+
50
+ def self.add_item(item)
51
+ self.load! if self.all.nil?
52
+ self.all << item
53
+ self.save!
54
+ end
55
+
56
+ END
57
+ klass.class_eval(saveable_class_def)
58
+ return klass
59
+ end
60
+ end