gritano 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +20 -0
  4. data/Gemfile.lock +72 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.rdoc +51 -0
  7. data/Rakefile +62 -0
  8. data/TODO +0 -0
  9. data/VERSION +1 -0
  10. data/bin/gritano +74 -0
  11. data/bin/gritano-check +17 -0
  12. data/db/database.yml +2 -0
  13. data/db/migrate/001_create_users.rb +8 -0
  14. data/db/migrate/002_create_repositories.rb +9 -0
  15. data/db/migrate/003_create_permissions.rb +10 -0
  16. data/db/migrate/004_create_keys.rb +11 -0
  17. data/features/command.feature +12 -0
  18. data/features/console.feature +59 -0
  19. data/features/data/keys/full_authorized_keys +2 -0
  20. data/features/data/keys/igorbonadio.pub +1 -0
  21. data/features/data/keys/igorbonadio_authorized_keys +1 -0
  22. data/features/data/keys/jessicaeto.pub +1 -0
  23. data/features/data/keys/jessicaeto_authorized_keys +1 -0
  24. data/features/keys.feature +31 -0
  25. data/features/polices.feature +55 -0
  26. data/features/step_definitions/command_step.rb +8 -0
  27. data/features/step_definitions/console_step.rb +14 -0
  28. data/features/step_definitions/keys_steps.rb +23 -0
  29. data/features/step_definitions/polices_steps.rb +54 -0
  30. data/features/support/database_cleaner.rb +15 -0
  31. data/features/support/env.rb +25 -0
  32. data/lib/gritano.rb +5 -0
  33. data/lib/gritano/command.rb +16 -0
  34. data/lib/gritano/console.rb +119 -0
  35. data/lib/gritano/models.rb +7 -0
  36. data/lib/gritano/models/key.rb +21 -0
  37. data/lib/gritano/models/permission.rb +39 -0
  38. data/lib/gritano/models/repository.rb +24 -0
  39. data/lib/gritano/models/user.rb +41 -0
  40. data/spec/command_spec.rb +17 -0
  41. data/spec/console_spec.rb +51 -0
  42. data/spec/model_key_spec.rb +18 -0
  43. data/spec/model_repository_spec.rb +23 -0
  44. data/spec/model_user_spec.rb +84 -0
  45. data/spec/spec_helper.rb +32 -0
  46. data/tmp/.gitignore +4 -0
  47. metadata +260 -0
@@ -0,0 +1,2 @@
1
+ command="gritano-check igorbonadio" ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFW6Du1iXTyo44g+7R4DNgm4P1fQIiW/iGRFwHJTV1jaPX74VwNq7tC1kBSdPS4+Q9f24wJC1MhWzLxB40BFdqn519JhhV+/1IWZdY/UJ0D5KiUw38U7QPzMM2uA0l0JeB+FwZAl/Oiu/ty3Fq0JsuqsolehIbRRLeiJiwrn1XC5LdhA81b2WBzM8SSFgAaXPimuLBXYJyYrcTR5SXczZvgkWojQEvk7wCavvDzFpy/DtXUFv0ZwUJILhN23cW3mg1IsGMXg7hOQfp67J6cX212YYXhDe+5sI3UpFWKCHcyxv3EdL8rQ/3DLSELkTwWHPRqDhn1wnPmfJlj8ZfbpyX git2@debian-ror
2
+ command="gritano-check jessicaeto" ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDZ4WqIu8XwnHwBz220/1Kbgi1IR7aanq8hW1dB0LD2dmCSyojBvduoht4p7+3k2R6A5y2DZvTetzEios9OFUnCC+4U8g2GTc+zGM0W+msCb6yWnpfYaIwHVuFtsid7lyWOCEYLi2WbNZxfAx0PbwIcHMoYWc9sil3R/YwLGorvQDGH0rFcf6BOMzVMDRD0yPvuN3xgAtBOxrSRl0U4dH+3fAQ9oKLePmouzLrrKvRmyVwl/rHNod8ae5VmmAalC+wXIsiQAI92Hwew757HzhY45wWtjOsdBBf45Psv7BkB1OqGxMfwysO5iwhY3HPTJs70K22K2DARpejFq8Bd8PyV git2@debian-ror
@@ -0,0 +1 @@
1
+ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFW6Du1iXTyo44g+7R4DNgm4P1fQIiW/iGRFwHJTV1jaPX74VwNq7tC1kBSdPS4+Q9f24wJC1MhWzLxB40BFdqn519JhhV+/1IWZdY/UJ0D5KiUw38U7QPzMM2uA0l0JeB+FwZAl/Oiu/ty3Fq0JsuqsolehIbRRLeiJiwrn1XC5LdhA81b2WBzM8SSFgAaXPimuLBXYJyYrcTR5SXczZvgkWojQEvk7wCavvDzFpy/DtXUFv0ZwUJILhN23cW3mg1IsGMXg7hOQfp67J6cX212YYXhDe+5sI3UpFWKCHcyxv3EdL8rQ/3DLSELkTwWHPRqDhn1wnPmfJlj8ZfbpyX git2@debian-ror
@@ -0,0 +1 @@
1
+ command="gritano-check igorbonadio" ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFW6Du1iXTyo44g+7R4DNgm4P1fQIiW/iGRFwHJTV1jaPX74VwNq7tC1kBSdPS4+Q9f24wJC1MhWzLxB40BFdqn519JhhV+/1IWZdY/UJ0D5KiUw38U7QPzMM2uA0l0JeB+FwZAl/Oiu/ty3Fq0JsuqsolehIbRRLeiJiwrn1XC5LdhA81b2WBzM8SSFgAaXPimuLBXYJyYrcTR5SXczZvgkWojQEvk7wCavvDzFpy/DtXUFv0ZwUJILhN23cW3mg1IsGMXg7hOQfp67J6cX212YYXhDe+5sI3UpFWKCHcyxv3EdL8rQ/3DLSELkTwWHPRqDhn1wnPmfJlj8ZfbpyX git2@debian-ror
@@ -0,0 +1 @@
1
+ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDZ4WqIu8XwnHwBz220/1Kbgi1IR7aanq8hW1dB0LD2dmCSyojBvduoht4p7+3k2R6A5y2DZvTetzEios9OFUnCC+4U8g2GTc+zGM0W+msCb6yWnpfYaIwHVuFtsid7lyWOCEYLi2WbNZxfAx0PbwIcHMoYWc9sil3R/YwLGorvQDGH0rFcf6BOMzVMDRD0yPvuN3xgAtBOxrSRl0U4dH+3fAQ9oKLePmouzLrrKvRmyVwl/rHNod8ae5VmmAalC+wXIsiQAI92Hwew757HzhY45wWtjOsdBBf45Psv7BkB1OqGxMfwysO5iwhY3HPTJs70K22K2DARpejFq8Bd8PyV git2@debian-ror
@@ -0,0 +1 @@
1
+ command="gritano-check jessicaeto" ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDZ4WqIu8XwnHwBz220/1Kbgi1IR7aanq8hW1dB0LD2dmCSyojBvduoht4p7+3k2R6A5y2DZvTetzEios9OFUnCC+4U8g2GTc+zGM0W+msCb6yWnpfYaIwHVuFtsid7lyWOCEYLi2WbNZxfAx0PbwIcHMoYWc9sil3R/YwLGorvQDGH0rFcf6BOMzVMDRD0yPvuN3xgAtBOxrSRl0U4dH+3fAQ9oKLePmouzLrrKvRmyVwl/rHNod8ae5VmmAalC+wXIsiQAI92Hwew757HzhY45wWtjOsdBBf45Psv7BkB1OqGxMfwysO5iwhY3HPTJs70K22K2DARpejFq8Bd8PyV git2@debian-ror
@@ -0,0 +1,31 @@
1
+ Feature: Keys
2
+ In Order to restrict access to repositories
3
+ As Gritano
4
+ I want to manage user's keys
5
+
6
+ Background:
7
+ Given the following users exist:
8
+ | login |
9
+ | igorbonadio |
10
+ | jessicaeto |
11
+
12
+ Scenario Outline: Add user key
13
+ Given I add "<key>" key to "<user>"
14
+ When I generate the authorized_keys
15
+ Then I should see "<authorized_keys>" authorized_keys
16
+ Examples:
17
+ | user | key | authorized_keys |
18
+ | igorbonadio | igorbonadio.pub | igorbonadio_authorized_keys |
19
+ | jessicaeto | jessicaeto.pub | jessicaeto_authorized_keys |
20
+
21
+ Scenario: Generate autorized_keys
22
+ Given I add "igorbonadio.pub" key to "igorbonadio"
23
+ And I add "jessicaeto.pub" key to "jessicaeto"
24
+ When I generate the authorized_keys
25
+ Then I should see "full_authorized_keys" authorized_keys
26
+
27
+ Scenario: Duplicated keys
28
+ Given I add "igorbonadio.pub" key to "igorbonadio"
29
+ When I add "igorbonadio.pub" key to "igorbonadio"
30
+ Then I should see that "igorbonadio" has only one key
31
+
@@ -0,0 +1,55 @@
1
+ Feature: Policies
2
+ In order to restrict access to repositories
3
+ As Gritano
4
+ I want to create policies
5
+
6
+ Background:
7
+ Given the following users exist:
8
+ | login |
9
+ | igorbonadio |
10
+ | jessicaeto |
11
+
12
+ And the following repositories exist:
13
+ | name |
14
+ | tmp/gritano.git |
15
+ | tmp/jeka.git |
16
+
17
+ And the following permissions exist:
18
+ | user | repo | access |
19
+ | igorbonadio | tmp/gritano.git | read |
20
+ | igorbonadio | tmp/gritano.git | write |
21
+ | igorbonadio | tmp/jeka.git | read |
22
+ | jessicaeto | tmp/jeka.git | read |
23
+ | jessicaeto | tmp/jeka.git | write |
24
+
25
+ Scenario Outline: Create a new user
26
+ Given I create a new user called "<user>"
27
+ When I check if "<user>" has <access> access to "<repo>"
28
+ Then I should see that the access is <result>
29
+ Examples:
30
+ | user | access | repo | result |
31
+ | arybonadio | read | tmp/gritano.git | denied |
32
+ | arybonadio | write | tmp/gritano.git | denied |
33
+
34
+ Scenario Outline: Create a new repository
35
+ Given I create a new repository called "<repo>" to "<user>"
36
+ Then I should see that only "<user>" has access to "<repo>"
37
+ Examples:
38
+ | user | repo |
39
+ | igorbonadio | tmp/p-lang.git |
40
+ | jessicaeto | tmp/pabel.git |
41
+
42
+ Scenario Outline: Edit access permission
43
+ Given I <op> "<user>" <permission> access to "<repo>"
44
+ When I check if "<user>" has <access> access to "<repo>"
45
+ Then I should see that the access is <result>
46
+ Examples:
47
+ | op | user | permission | repo | access | result |
48
+ | add | igorbonadio | read | tmp/jeka.git | read | allowed |
49
+ | add | igorbonadio | read | tmp/jeka.git | write | denied |
50
+ | add | igorbonadio | write | tmp/jeka.git | read | allowed |
51
+ | add | igorbonadio | write | tmp/jeka.git | write | allowed |
52
+ | remove | jessicaeto | read | tmp/jeka.git | read | denied |
53
+ | remove | jessicaeto | read | tmp/jeka.git | write | allowed |
54
+ | remove | jessicaeto | write | tmp/jeka.git | read | allowed |
55
+ | remove | jessicaeto | write | tmp/jeka.git | write | denied |
@@ -0,0 +1,8 @@
1
+ When /^I receive a "(.*?)" command$/ do |cmd|
2
+ @access, @git_command, @repo = Gritano::Command.eval(cmd)
3
+ end
4
+
5
+ Then /^I should see that it is a "(.*?)" access to "(.*?)"$/ do |access, repo|
6
+ @access.to_s.should be == access
7
+ @repo.to_s.should be == repo
8
+ end
@@ -0,0 +1,14 @@
1
+ Given /^I start the gritano console$/ do
2
+ @console = Gritano::Console.new
3
+ @console.ssh_path = 'tmp'
4
+ end
5
+
6
+ When /^I execute "(.*?)"$/ do |command|
7
+ @output = @console.execute(command.split(' '))
8
+ end
9
+
10
+ Then /^I should see a (success|error) message$/ do |ret|
11
+ expected_output = true if ret == 'success'
12
+ expected_output = false if ret == 'error'
13
+ @output.should be == expected_output
14
+ end
@@ -0,0 +1,23 @@
1
+ Given /^the following keys exist:$/ do |table|
2
+ table.hashes.each do |key|
3
+ Gritano::User.find_by_login(key['login']).keys.create(name: key["key"], key: "key")
4
+ end
5
+ end
6
+
7
+ Given /^I add "(.*?)" key to "(.*?)"$/ do |key, login|
8
+ ssh_key = File.open(File.join("features/data/keys/", key)).readlines.join
9
+ Gritano::User.find_by_login(login).keys.create({name: key, key: ssh_key})
10
+ end
11
+
12
+ When /^I generate the authorized_keys$/ do
13
+ @authorized_keys = Gritano::Key.authorized_keys
14
+ end
15
+
16
+ Then /^I should see "(.*?)" authorized_keys$/ do |authorized_keys|
17
+ expected_authorized_keys = File.open(File.join("features/data/keys/", authorized_keys)).readlines.join
18
+ @authorized_keys.should be == expected_authorized_keys
19
+ end
20
+
21
+ Then /^I should see that "(.*?)" has only one key$/ do |login|
22
+ Gritano::User.find_by_login(login).keys.count.should be == 1
23
+ end
@@ -0,0 +1,54 @@
1
+ Given /^the following users exist:$/ do |table|
2
+ table.hashes.each do |user|
3
+ Gritano::User.create(user)
4
+ end
5
+ end
6
+
7
+ Given /^the following repositories exist:$/ do |table|
8
+ table.hashes.each do |repo|
9
+ Gritano::Repository.create(repo)
10
+ end
11
+ end
12
+
13
+ Given /^the following permissions exist:$/ do |table|
14
+ table.hashes.each do |permission|
15
+ Gritano::User.find_by_login(permission['user'])
16
+ .add_access(Gritano::Repository.find_by_name(permission['repo']), permission['access'].to_sym)
17
+ end
18
+ end
19
+
20
+ Given /^I create a new user called "(.*?)"$/ do |login|
21
+ @user = Gritano::User.create(login: login)
22
+ end
23
+
24
+ When /^I check if "(.*?)" has (read|write) access to "(.*?)"$/ do |login, access, repo|
25
+ @access_result = @user.check_access(Gritano::Repository.find_by_name(repo), access.to_sym)
26
+ end
27
+
28
+ Then /^I should see that the access is (denied|allowed)$/ do |result|
29
+ @expected_result = false if result == 'denied'
30
+ @expected_result = true if result == 'allowed'
31
+ @access_result.should be == @expected_result
32
+ end
33
+
34
+ Given /^I create a new repository called "(.*?)" to "(.*?)"$/ do |repo, login|
35
+ Gritano::User.find_by_login(login).create_repository(name: repo)
36
+ end
37
+
38
+ Then /^I should see that only "(.*?)" has access to "(.*?)"$/ do |login, repo|
39
+ repository = Gritano::Repository.find_by_name(repo)
40
+ user = Gritano::User.find_by_login(login)
41
+ user.check_access(repository, :read).should be_true
42
+ user.check_access(repository, :write).should be_true
43
+ Gritano::User.all.each do |u|
44
+ unless u.login == user.login
45
+ u.check_access(repository, :read).should be_false
46
+ u.check_access(repository, :write).should be_false
47
+ end
48
+ end
49
+ end
50
+
51
+ Given /^I (add|remove) "(.*?)" (read|write) access to "(.*?)"$/ do |op, user, permission, repo|
52
+ @user = Gritano::User.find_by_login(user)
53
+ @user.send("#{op}_access", Gritano::Repository.find_by_name(repo), permission.to_sym)
54
+ end
@@ -0,0 +1,15 @@
1
+ begin
2
+ require 'database_cleaner'
3
+ require 'database_cleaner/cucumber'
4
+ DatabaseCleaner.strategy = :truncation
5
+ rescue NameError
6
+ raise "You need to add database_cleaner to your Gemfile (in the :test group) if you wish to use it."
7
+ end
8
+
9
+ Before do
10
+ DatabaseCleaner.start
11
+ end
12
+
13
+ After do |scenario|
14
+ DatabaseCleaner.clean
15
+ end
@@ -0,0 +1,25 @@
1
+ require 'simplecov'
2
+ SimpleCov.start do
3
+ add_filter "/features/"
4
+ add_filter "/spec/"
5
+ end
6
+
7
+ require 'bundler'
8
+ begin
9
+ Bundler.setup(:default, :development)
10
+ rescue Bundler::BundlerError => e
11
+ $stderr.puts e.message
12
+ $stderr.puts "Run `bundle install` to install missing gems"
13
+ exit e.status_code
14
+ end
15
+
16
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
17
+ require 'gritano'
18
+
19
+ require 'rspec/expectations'
20
+
21
+ require 'active_record'
22
+
23
+ Before do
24
+ ActiveRecord::Base.establish_connection(YAML::load(File.open('db/database.yml')))
25
+ end
data/lib/gritano.rb ADDED
@@ -0,0 +1,5 @@
1
+ ROOT_PATH = File.expand_path(File.dirname(__FILE__))
2
+
3
+ require File.join(ROOT_PATH, '/gritano/models')
4
+ require File.join(ROOT_PATH, '/gritano/console')
5
+ require File.join(ROOT_PATH, '/gritano/command')
@@ -0,0 +1,16 @@
1
+ module Gritano
2
+ class Command
3
+ def self.eval(cmd)
4
+ case cmd
5
+ when /^git-receive-pack/ then
6
+ return :write, "git-receive-pack", self.repo(cmd)
7
+ when /^git-upload-pack/ then
8
+ return :read, "git-upload-pack", self.repo(cmd)
9
+ end
10
+ end
11
+
12
+ def self.repo(cmd)
13
+ cmd.gsub(/^git-receive-pack/, '').gsub(/^git-upload-pack/, '').gsub("'", '').strip
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,119 @@
1
+ module Gritano
2
+ class Console
3
+
4
+ attr_accessor :repo_path
5
+ attr_accessor :ssh_path
6
+
7
+ def initialize
8
+ @repo_path = nil
9
+ @ssh_path = nil
10
+ end
11
+
12
+ def execute(argv)
13
+ send(argv[0..1].join('_').gsub('+', 'add_').gsub('-', 'remove_'), argv[2..-1])
14
+ end
15
+
16
+ def user_add(argv)
17
+ login, = argv
18
+ user = User.new(login: login)
19
+ return true if user.save
20
+ return false
21
+ end
22
+
23
+ def user_rm(argv)
24
+ login, = argv
25
+ user = User.find_by_login(login)
26
+ if user
27
+ if user.destroy
28
+ return true
29
+ end
30
+ end
31
+ return false
32
+ end
33
+
34
+ def repo_add(argv)
35
+ name, = argv
36
+ repo = Repository.new(name: name, path: @repo_path)
37
+ return true if repo.save
38
+ return false
39
+ end
40
+
41
+ def repo_rm(argv)
42
+ name, = argv
43
+ repo = Repository.find_by_name(name)
44
+ if repo
45
+ if repo.destroy
46
+ return true
47
+ end
48
+ end
49
+ return false
50
+ end
51
+
52
+ def repo_add_read(argv)
53
+ repo_name, login = argv
54
+ user = User.find_by_login(login)
55
+ repo = Repository.find_by_name(repo_name)
56
+ if repo and user
57
+ return user.add_access(repo, :read)
58
+ end
59
+ return false
60
+ end
61
+
62
+ def repo_add_write(argv)
63
+ repo_name, login = argv
64
+ user = User.find_by_login(login)
65
+ repo = Repository.find_by_name(repo_name)
66
+ if repo and user
67
+ return user.add_access(repo, :write)
68
+ end
69
+ return false
70
+ end
71
+
72
+ def repo_remove_read(argv)
73
+ repo_name, login = argv
74
+ user = User.find_by_login(login)
75
+ repo = Repository.find_by_name(repo_name)
76
+ if repo and user
77
+ return user.remove_access(repo, :read)
78
+ end
79
+ return false
80
+ end
81
+
82
+ def repo_remove_write(argv)
83
+ repo_name, login = argv
84
+ user = User.find_by_login(login)
85
+ repo = Repository.find_by_name(repo_name)
86
+ if repo and user
87
+ return user.remove_access(repo, :write)
88
+ end
89
+ return false
90
+ end
91
+
92
+ def user_add_key(argv)
93
+ login, key_name, key_file = argv
94
+ user = User.find_by_login(login)
95
+ if File.exist?(key_file)
96
+ if user
97
+ key = user.keys.create(name: key_name, key: File.open(key_file).readlines.join)
98
+ if key.valid?
99
+ File.open(File.join(@ssh_path, 'authorized_keys'), 'w').write(Key.authorized_keys)
100
+ return true
101
+ end
102
+ end
103
+ end
104
+ return false
105
+ end
106
+
107
+ def user_remove_key(argv)
108
+ login, key_name = argv
109
+ key = Key.where(name: key_name).includes(:user).where("users.login" => login).limit(1)[0]
110
+ if key
111
+ if key.destroy
112
+ File.open(File.join(@ssh_path, 'authorized_keys'), 'w').write(Key.authorized_keys)
113
+ return true
114
+ end
115
+ end
116
+ return false
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,7 @@
1
+ require 'active_record'
2
+ require 'grit'
3
+
4
+ require File.join(ROOT_PATH, 'gritano/models/user')
5
+ require File.join(ROOT_PATH, 'gritano/models/repository')
6
+ require File.join(ROOT_PATH, 'gritano/models/permission')
7
+ require File.join(ROOT_PATH, 'gritano/models/key')
@@ -0,0 +1,21 @@
1
+ module Gritano
2
+ class Key < ActiveRecord::Base
3
+ validates :name, :key, presence: true
4
+ validates :name, :uniqueness => { :scope => :user_id, :message => "should happen once per user" }
5
+
6
+ belongs_to :user
7
+
8
+ def self.authorized_keys
9
+ authorized_keys = ""
10
+ keys = Key.find(:all)
11
+ keys.each do |k|
12
+ user_key = k.key
13
+ unless k.key[-1] == "\n"
14
+ user_key = user_key + "\n"
15
+ end
16
+ authorized_keys += "command=\"gritano-check #{k.user.login}\" #{user_key}"
17
+ end
18
+ return authorized_keys
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,39 @@
1
+ module Gritano
2
+ class Permission < ActiveRecord::Base
3
+ belongs_to :user
4
+ belongs_to :repository
5
+
6
+ READ = 1
7
+ WRITE = 2
8
+
9
+ def add_access(access)
10
+ if access == :read
11
+ self.access = READ | (self.access || 0)
12
+ elsif access == :write
13
+ self.access = WRITE | (self.access || 0)
14
+ else
15
+ return false
16
+ end
17
+ return true
18
+ end
19
+
20
+ def remove_access(access)
21
+ if access == :read
22
+ self.access = (self.access || 0) & (~ READ)
23
+ elsif access == :write
24
+ self.access = (self.access || 0) & (~ WRITE)
25
+ else
26
+ return false
27
+ end
28
+ return true
29
+ end
30
+
31
+ def is(access)
32
+ if access == :read
33
+ return (self.access & READ) == READ
34
+ elsif access == :write
35
+ return (self.access & WRITE) == WRITE
36
+ end
37
+ end
38
+ end
39
+ end