Permalink
Browse files

Testing shell implementation [#15]

This adds some initial tests for dokuen-shell, which will soon be
replacing gitolite.
  • Loading branch information...
1 parent ecaa4a6 commit 1c59a0fb6410bbe0be3dc653983c35690de1f2b7 @peterkeen committed Jun 18, 2012
Showing with 246 additions and 17 deletions.
  1. +19 −2 Gemfile.lock
  2. +6 −1 Rakefile
  3. +4 −0 dokuen.gemspec
  4. +6 −1 lib/dokuen/application.rb
  5. +4 −4 lib/dokuen/cli.rb
  6. +52 −9 lib/dokuen/shell.rb
  7. +129 −0 specs/shell_spec.rb
  8. +26 −0 specs/spec_helper.rb
View
@@ -1,18 +1,35 @@
PATH
remote: .
specs:
- dokuen (0.0.1)
+ dokuen (0.0.12)
+ foreman
+ mason (>= 0.1.0)
thor
GEM
remote: http://rubygems.org/
specs:
+ diff-lcs (1.1.3)
+ foreman (0.41.0)
+ thor (>= 0.13.6)
+ mason (0.1.0)
+ thor
rake (0.9.2.2)
- thor (0.14.6)
+ rspec (2.10.0)
+ rspec-core (~> 2.10.0)
+ rspec-expectations (~> 2.10.0)
+ rspec-mocks (~> 2.10.0)
+ rspec-core (2.10.1)
+ rspec-expectations (2.10.0)
+ diff-lcs (~> 1.1.3)
+ rspec-mocks (2.10.1)
+ thor (0.15.2)
PLATFORMS
ruby
DEPENDENCIES
dokuen!
rake
+ rspec
+ rspec-mocks
View
@@ -6,7 +6,11 @@ def sys(cmd)
system(cmd) or raise "Error running #{cmd}"
end
-task :build do
+task :spec do
+ sys "rspec specs/*spec.rb"
+end
+
+task :build => :spec do
sys "gem build dokuen.gemspec"
end
@@ -16,3 +20,4 @@ task :release => :build do
sys "git push github master --tags"
sys "gem push dokuen-#{Dokuen::VERSION}.gem"
end
+
View
@@ -24,9 +24,13 @@ Gem::Specification.new do |s|
s.test_files = s.files.select {|path| path =~ /^test\/.*.rb/ }
s.add_development_dependency('rake')
+ s.add_development_dependency('rspec')
+ s.add_development_dependency('rspec-mocks')
+
s.add_dependency('thor')
s.add_dependency('mason', ">= 0.1.0")
s.add_dependency('foreman')
+
s.homepage = 'https://github.com/peterkeen/dokuen'
end
@@ -53,7 +53,7 @@ def env
vars
end
- def create
+ def create(user)
Dir.chdir(File.join(config.dokuen_dir, 'apps')) do
if File.exists?(name)
raise "Application #{name} exists!"
@@ -70,6 +70,11 @@ def create
FileUtils.mkdir_p(dirs)
end
end
+
+ File.open(File.join(configs.dokuen_dir, 'perms', name), 'w+') do |f|
+ f.write(YAML.dump({'owner' => user, 'shared_with' => []}))
+ end
+
end
def deploy(revision)
View
@@ -138,22 +138,22 @@ def run_command(*args)
desc "grant USER", "Grant USER permissions for application"
def grant(user)
- # pass
+ # write a new permissions file with USER in the "shared_with" section
end
desc "revoke USER", "Remove USER permissions for application"
def revoke(user)
- # pass
+ # write a new permissions file without USER in the "shared_with" section
end
desc "addkey USER", "Add a user's public key from stdin"
def addkey(user)
- # pass
+ # read stdin, write key to keys/USER.key, write ~/.ssh/authorized_keys
end
desc "removekey USER", "Remove a user's public key"
def removekey(user)
- # pass
+ # remove keys/USER.key, write ~/.ssh/authorized_keys
end
private
View
@@ -24,33 +24,73 @@ def initialize(basedir, command, user)
end
def determine_appname
+ noapp_commands = %w{addkey removekey}
if match = command.match(/--application=(\w+)/)
return match[1]
elsif match = command.match(/(\w+)\.git/)
return match[1]
+ elsif noapp_commands.include?(commandv[0])
+ return nil
else
raise Dokuen::ExitCode.new(1, "Could not determine appname from #{command}")
end
end
- def _check_permissions_file(filename, user)
+ def is_superuser
+ filename = "#{basedir}/superusers"
if File.exists?(filename)
lines = File.read(filename).split("\n")
return lines.include?(user)
end
+ return false
+ end
+
+ def is_owner
+ with_perms_file do |perms|
+ return user == perms['owner']
+ end
+ end
+
+ def is_shared_with
+ with_perms_file do |perms|
+ return perms['shared_with'].include?(user)
+ end
+ end
+
+ def with_perms_file
+ filename = "#{basedir}/perms/#{application}"
+ if File.exists?(filename)
+ perms = YAML.load(File.read(filename))
+ return yield perms
+ end
+ return false
+ end
+
+ def is_authorized_user
+ return is_superuser || is_owner || is_shared_with
end
def check_permissions
- if _check_permissions_file("#{basedir}/superusers", user)
- return
+ if is_authorized_user
+ return true
end
- if ! _check_permissions_file("#{basedir}/perms/#{application}", user)
- raise Dokuen::ExitCode.new(2, "User #{user} not permitted for app #{application}")
- else
- if commandV[0] != "create"
- raise Dokuen::ExitCode.new(3, "App #{application} does not exist")
- end
+ raise Dokuen::ExitCode.new(2, "User #{user} not permitted for app #{application}")
+ end
+
+ def check_superuser_command
+ superuser_commands = %w{addkey removekey}
+ if superuser_commands.include?(commandv[0]) && ! is_superuser
+ raise Dokuen::ExitCode.new(3, "#{commandv[0]} is restricted to superusers")
+ end
+ return true
+ end
+
+ def check_owner_command
+ owner_commands = %{grant revoke}
+ if owner_commands.include?(commandv[0]) && ! (is_superuser || is_owner)
+ raise Dokuen::ExitCode.new(3, "#{commandv[0]} is restricted to owners")
end
+ return true
end
def run_git_command
@@ -60,6 +100,9 @@ def run_git_command
end
def run_dokuen_subcommand
+ check_superuser_command
+ check_owner_command
+
system("#{basedir}/bin/dokuen #{command}", :in => $stdin, :out => $stdout, :err => $stderr)
raise Dokuen::ExitCode.new($?.exitstatus, '')
end
View
@@ -0,0 +1,129 @@
+require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
+
+describe Dokuen::Shell do
+
+ before do
+ @tmpdir = construct_dokuen_dir
+ end
+
+ describe "basic permissions" do
+
+ before do
+ @shell = Dokuen::Shell.new(@tmpdir, 'command foo.git', 'user')
+ end
+
+ it "should initialize" do
+ @shell.command.should eq "command foo.git"
+ @shell.basedir.should eq @tmpdir
+ @shell.user.should eq "user"
+ @shell.commandv.should eq ['command', 'foo.git']
+ @shell.application.should eq 'foo'
+ end
+
+ it "should check superuser" do
+ @shell.is_superuser.should eq false
+
+ File.open("#{@tmpdir}/superusers", "w+") do |f|
+ f.write('user')
+ end
+
+ @shell.is_superuser.should eq true
+ end
+
+ it "should check owner" do
+ @shell.is_owner.should eq false
+
+ File.open("#{@tmpdir}/perms/foo", "w+") do |f|
+ f.write(YAML.dump({'owner' => 'user'}))
+ end
+
+ @shell.is_owner.should eq true
+ end
+
+ it "should check shared_with" do
+ @shell.is_shared_with.should eq false
+
+ File.open("#{@tmpdir}/perms/foo", "w+") do |f|
+ f.write(YAML.dump({'shared_with' => ['user']}))
+ end
+
+ @shell.is_shared_with.should eq true
+ end
+
+ it "should check superuser for is_authorized_user" do
+ @shell.is_authorized_user.should eq false
+
+ File.open("#{@tmpdir}/superusers", "w+") do |f|
+ f.write('user')
+ end
+ @shell.is_authorized_user.should eq true
+ end
+
+ it "should check owner for is_authorized_user" do
+ @shell.is_authorized_user.should eq false
+
+ File.open("#{@tmpdir}/perms/foo", "w+") do |f|
+ f.write(YAML.dump({'owner' => 'user'}))
+ end
+
+ @shell.is_authorized_user.should eq true
+ end
+
+ it "should check shared_with for is_authorized_user" do
+ @shell.is_authorized_user.should eq false
+
+ File.open("#{@tmpdir}/perms/foo", "w+") do |f|
+ f.write(YAML.dump({'shared_with' => ['user']}))
+ end
+
+ @shell.is_authorized_user.should eq true
+ end
+ end
+
+ describe "check commands" do
+
+ before do
+ File.open("#{@tmpdir}/superusers", "w+") do |f|
+ f.write("superuser")
+ end
+
+ File.open("#{@tmpdir}/perms/foo", "w+") do |f|
+ f.write(YAML.dump({'owner' => 'owner', 'shared_with' => ['shared_with']}))
+ end
+ end
+
+ it "should check permissions" do
+ Dokuen::Shell.new(@tmpdir, 'command foo.git', 'superuser').check_permissions.should eq true
+ Dokuen::Shell.new(@tmpdir, 'command foo.git', 'owner').check_permissions.should eq true
+ Dokuen::Shell.new(@tmpdir, 'command foo.git', 'shared_with').check_permissions.should eq true
+
+ lambda { Dokuen::Shell.new(@tmpdir, 'command foo.git', 'notuser').check_permissions }.should raise_error(Dokuen::ExitCode)
+ end
+
+ it "should check superuser command for addkey" do
+ Dokuen::Shell.new(@tmpdir, 'addkey owner', 'superuser').check_superuser_command.should eq true
+ lambda { Dokuen::Shell.new(@tmpdir, 'addkey owner', 'owner').check_superuser_command }.should raise_error(Dokuen::ExitCode)
+ end
+
+ it "should check superuser command for removekey" do
+ Dokuen::Shell.new(@tmpdir, 'removekey owner', 'superuser').check_superuser_command.should eq true
+ lambda { Dokuen::Shell.new(@tmpdir, 'removekey owner', 'owner').check_superuser_command }.should raise_error(Dokuen::ExitCode)
+ end
+
+ it "should check owner command for grant" do
+ Dokuen::Shell.new(@tmpdir, 'grant shared_with --application=foo', 'superuser').check_owner_command.should eq true
+ Dokuen::Shell.new(@tmpdir, 'grant shared_with --application=foo', 'owner').check_owner_command.should eq true
+
+ lambda { Dokuen::Shell.new(@tmpdir, 'grant shared_with --application=foo', 'shared_with').check_owner_command }.should raise_error(Dokuen::ExitCode)
+ end
+
+ it "should check owner command for revoke" do
+ Dokuen::Shell.new(@tmpdir, 'revoke shared_with --application=foo', 'superuser').check_owner_command.should eq true
+ Dokuen::Shell.new(@tmpdir, 'revoke shared_with --application=foo', 'owner').check_owner_command.should eq true
+
+ lambda { Dokuen::Shell.new(@tmpdir, 'revoke shared_with --application=foo', 'shared_with').check_owner_command }.should raise_error(Dokuen::ExitCode)
+ end
+
+ end
+
+end
View
@@ -0,0 +1,26 @@
+$:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
+
+require 'rspec'
+require 'dokuen'
+require 'tmpdir'
+require 'fileutils'
+
+def construct_dokuen_dir
+ tmpdir = Dir.mktmpdir
+ dirs = [
+ 'apps',
+ 'env',
+ 'perms',
+ 'keys',
+ 'ports',
+ 'nginx',
+ 'bin'
+ ]
+
+ dirs.each do |dir|
+ FileUtils.mkdir_p(File.join(tmpdir, dir))
+ end
+
+ tmpdir
+end
+

0 comments on commit 1c59a0f

Please sign in to comment.