cando 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.3"
4
+ cache: bundler
5
+ before_script:
6
+ - mysql -e 'create database cando_test;'
7
+
8
+ env: CANDO_TEST_DB=mysql://travis@127.0.0.1/cando_test
data/Gemfile ADDED
@@ -0,0 +1,21 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ gem "sequel"
7
+ gem "rake"
8
+
9
+ # Add dependencies to develop your gem here.
10
+ # Include everything needed to run rake, tests, features, etc.
11
+ group :development do
12
+ gem "rspec", "~> 2.10.0"
13
+ gem "rdoc", "~> 3.12"
14
+ gem "bundler", "~> 1.0"
15
+ gem "jeweler", "~> 2.0.1"
16
+ gem "simplecov", ">= 0"
17
+ end
18
+
19
+ group :test do
20
+ gem "mysql"
21
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,79 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ addressable (2.3.6)
5
+ builder (3.2.2)
6
+ descendants_tracker (0.0.4)
7
+ thread_safe (~> 0.3, >= 0.3.1)
8
+ diff-lcs (1.1.3)
9
+ docile (1.1.3)
10
+ faraday (0.9.0)
11
+ multipart-post (>= 1.2, < 3)
12
+ git (1.2.6)
13
+ github_api (0.11.3)
14
+ addressable (~> 2.3)
15
+ descendants_tracker (~> 0.0.1)
16
+ faraday (~> 0.8, < 0.10)
17
+ hashie (>= 1.2)
18
+ multi_json (>= 1.7.5, < 2.0)
19
+ nokogiri (~> 1.6.0)
20
+ oauth2
21
+ hashie (2.1.1)
22
+ highline (1.6.21)
23
+ jeweler (2.0.1)
24
+ builder
25
+ bundler (>= 1.0)
26
+ git (>= 1.2.5)
27
+ github_api
28
+ highline (>= 1.6.15)
29
+ nokogiri (>= 1.5.10)
30
+ rake
31
+ rdoc
32
+ json (1.8.1)
33
+ jwt (0.1.13)
34
+ multi_json (>= 1.5)
35
+ mini_portile (0.5.3)
36
+ multi_json (1.10.0)
37
+ multi_xml (0.5.5)
38
+ multipart-post (2.0.0)
39
+ mysql (2.9.1)
40
+ nokogiri (1.6.1)
41
+ mini_portile (~> 0.5.0)
42
+ oauth2 (0.9.3)
43
+ faraday (>= 0.8, < 0.10)
44
+ jwt (~> 0.1.8)
45
+ multi_json (~> 1.3)
46
+ multi_xml (~> 0.5)
47
+ rack (~> 1.2)
48
+ rack (1.5.2)
49
+ rake (10.3.1)
50
+ rdoc (3.12.2)
51
+ json (~> 1.4)
52
+ rspec (2.10.0)
53
+ rspec-core (~> 2.10.0)
54
+ rspec-expectations (~> 2.10.0)
55
+ rspec-mocks (~> 2.10.0)
56
+ rspec-core (2.10.1)
57
+ rspec-expectations (2.10.0)
58
+ diff-lcs (~> 1.1.3)
59
+ rspec-mocks (2.10.1)
60
+ sequel (4.10.0)
61
+ simplecov (0.8.2)
62
+ docile (~> 1.1.0)
63
+ multi_json
64
+ simplecov-html (~> 0.8.0)
65
+ simplecov-html (0.8.0)
66
+ thread_safe (0.3.3)
67
+
68
+ PLATFORMS
69
+ ruby
70
+
71
+ DEPENDENCIES
72
+ bundler (~> 1.0)
73
+ jeweler (~> 2.0.1)
74
+ mysql
75
+ rake
76
+ rdoc (~> 3.12)
77
+ rspec (~> 2.10.0)
78
+ sequel
79
+ simplecov
data/LICENSE.txt ADDED
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2014 Daniel Bornkessel
2
+
3
+ The MIT License (MIT)
4
+
5
+ Copyright © 2013 SoundCloud Ltd., Internal Tools Team
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
24
+
25
+
data/README.md ADDED
@@ -0,0 +1,144 @@
1
+ # CanDo [![Build Status](https://travis-ci.org/soundcloud/cando.svg?branch=master)](https://travis-ci.org/soundcloud/cando)
2
+
3
+ CanDo is a small gem to implement a simple user access system based on users, roles &
4
+ capabilites, where:
5
+
6
+ - each user can have 0, 1 or many roles
7
+ - each role can have 0, 1 or many capabilites
8
+
9
+ Users have capabilities by getting roles assigned (role == collection of
10
+ capabilities). Within the code, the `can` helper method can be used to test
11
+ whether a user has a certain capability or not (see below for a working code example).
12
+
13
+ ## Dependencies
14
+
15
+ CanDo depends on the following software:
16
+
17
+ * [sequel](http://sequel.jeremyevans.net)
18
+ * [rake](https://github.com/jimweirich/rake)
19
+
20
+
21
+ ## Installation and deployment
22
+
23
+ Download and install rake with the following.
24
+
25
+ gem install cando
26
+
27
+ ## Configuration and usage
28
+
29
+ ### Database setup & configuration
30
+ If you want to use an individual database for cando, create the db + credentials (adjust values):
31
+
32
+ CREATE DATABASE IF NOT EXISTS cando DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;
33
+ GRANT ALL ON `cando`.* to 'cando_user'@'localhost' identified by 'cando_passwd';
34
+
35
+ Whenever you want to use a CanDo rake task, you need to set the database config via the env var `$CANDO_DB`:
36
+
37
+ export CANDO_DB=mysql://cando_user@cando_passwd@localhost/cando
38
+
39
+ for other dbs, [see the sequel
40
+ documentation](http://sequel.jeremyevans.net/rdoc/classes/Sequel.html#method-c-connect).
41
+ **Note that you will have to require the gem for your respective dbms, i.e. the
42
+ `mysql`-gem for mysql, the `sqlite3`-gem for sqlite, etc. **
43
+
44
+ ### Init cando
45
+ Cando provides a rake task to get you started. This will setup the necessary
46
+ tables (they are all prefixed with `cando` and thus should not interfere with
47
+ your database.
48
+
49
+ rake cando:init
50
+
51
+ ### Using rake tasks
52
+ Cando provides several useful rake tasks for easy cli-based operations. In order
53
+ to use those edit (or create) the `Rakefile` and include
54
+
55
+ require 'cando'
56
+
57
+ To get an overview, execute:
58
+
59
+ $ rake -T cando
60
+ rake cando:init # Initialize cando (creates schema and runs migration)
61
+
62
+ rake cando:list # List roles
63
+ rake cando:add # Add a new role (pass in role name and capabilities with role=<name> capabilities=<cap1>,<cap2>,...
64
+ rake cando:update # Update role (pass in role name and capabilities with role=<name> capabilities=<cap1>,<cap2>,...
65
+ rake cando:remove # Remove role (pass in role name with role=<name>)
66
+
67
+ rake cando:assign # Assign role to user (args: roles=<r1>,<r2>,<rn> user=<user_urn>)
68
+ rake cando:users # List users and their roles
69
+
70
+ ### Using CanDo in your project's code
71
+ Using the CanDo in your code (working code with an empty database):
72
+
73
+ require 'cando'
74
+ include CanDo
75
+
76
+ CanDo.init do
77
+ # if passed, this will be executed if the user does not have the
78
+ # asked-for capability (only applies if 'can' is passed a block)
79
+ cannot_block do |user_urn, capability|
80
+ raise "#{user_urn} can not #{capability}"
81
+ end
82
+
83
+ connect "mysql://cando_user:cando_passwd@host:port/database"
84
+ end
85
+
86
+ # if the role or a capability does not exist, it'll be created
87
+ define_role("r1", ["capability1", "capability3"])
88
+ define_role("r2", ["capability2"])
89
+
90
+ # if the user does not exist, he'll be created -- the roles must be available
91
+ assign_roles("user1", ["r1", "r2"])
92
+ assign_roles("user2", ["r1"])
93
+
94
+ # use 'can' block syntax
95
+ can("user1", :capability1) do
96
+ puts "user has capability1"
97
+ end
98
+
99
+ # this will raise an exception as declared in the init block
100
+ can("user1", :super_admin) do
101
+ puts "hey hoh" # this will not be printed
102
+ end
103
+
104
+ # when no block is given, 'can' returns true or false/nil
105
+ unless can("user2", :capability2)
106
+ puts "user does not have capability1"
107
+ end
108
+
109
+ ## Versioning
110
+ CanDo adheres to Semantic Versioning 2.0.0. If there is a violation of
111
+ this scheme, report it as a bug.Specifically, if a patch or minor version is
112
+ released and breaks backward compatibility, that version should be immediately
113
+ yanked and/or a new version should be immediately released that restores
114
+ compatibility. Any change that breaks the public API will only be introduced at
115
+ a major-version release. As a result of this policy, you can (and should)
116
+ specify any dependency on <project name> by using the Pessimistic Version
117
+ Constraint with two digits of precision.
118
+
119
+ ## Licensing
120
+
121
+ See the [LICENSE](LICENSE.md) file for details.
122
+
123
+ The MIT License (MIT)
124
+
125
+ Copyright &copy; 2014 Daniel Bornkessel
126
+
127
+ Permission is hereby granted, free of charge, to any person obtaining a copy
128
+ of this software and associated documentation files (the "Software"), to deal
129
+ in the Software without restriction, including without limitation the rights
130
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
131
+ copies of the Software, and to permit persons to whom the Software is
132
+ furnished to do so, subject to the following conditions:
133
+
134
+ The above copyright notice and this permission notice shall be included in
135
+ all copies or substantial portions of the Software.
136
+
137
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
138
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
139
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
140
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
141
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
142
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
143
+ THE SOFTWARE.
144
+
data/Rakefile ADDED
@@ -0,0 +1,52 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rake'
4
+ require 'rubygems'
5
+ require 'bundler'
6
+ begin
7
+ Bundler.setup(:default, :development)
8
+ rescue Bundler::BundlerError => e
9
+ $stderr.puts e.message
10
+ $stderr.puts "Run `bundle install` to install missing gems"
11
+ exit e.status_code
12
+ end
13
+ require 'rake'
14
+
15
+ require 'jeweler'
16
+ Jeweler::Tasks.new do |gem|
17
+ # gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
18
+ gem.name = "cando"
19
+ gem.homepage = "http://github.com/kesselborn/cando"
20
+ gem.license = "MIT"
21
+ gem.summary = %Q{Simple roles helper}
22
+ gem.description = %Q{cando description}
23
+ gem.email = "daniel@soundcloud.com"
24
+ gem.authors = ["Daniel Bornkessel"]
25
+ # dependencies defined in Gemfile
26
+ end
27
+ Jeweler::RubygemsDotOrgTasks.new
28
+
29
+ require 'rspec/core'
30
+ require 'rspec/core/rake_task'
31
+ RSpec::Core::RakeTask.new(:spec) do |spec|
32
+ spec.pattern = FileList['spec/**/*_spec.rb']
33
+ end
34
+
35
+ desc "Code coverage detail"
36
+ task :simplecov do
37
+ ENV['COVERAGE'] = "true"
38
+ Rake::Task['spec'].execute
39
+ end
40
+
41
+ task :default => :spec
42
+
43
+ require 'rdoc/task'
44
+ Rake::RDocTask.new do |rdoc|
45
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
46
+
47
+ rdoc.rdoc_dir = 'rdoc'
48
+ rdoc.title = "cando #{version}"
49
+ rdoc.rdoc_files.include('README*')
50
+ rdoc.rdoc_files.include('lib/**/*.rb')
51
+ rdoc.rdoc_files.include('contrib/**/*.rb')
52
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/cando.gemspec ADDED
@@ -0,0 +1,75 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "cando"
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Daniel Bornkessel"]
12
+ s.date = "2014-05-20"
13
+ s.description = "cando description"
14
+ s.email = "daniel@soundcloud.com"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".rspec",
22
+ ".travis.yml",
23
+ "Gemfile",
24
+ "Gemfile.lock",
25
+ "LICENSE.txt",
26
+ "README.md",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "cando.gemspec",
30
+ "contrib/initial_schema.rb",
31
+ "lib/cando.rb",
32
+ "lib/models/capability.rb",
33
+ "lib/models/role.rb",
34
+ "lib/models/user.rb",
35
+ "lib/tasks/cando.rake",
36
+ "spec/cando_spec.rb",
37
+ "spec/spec_helper.rb"
38
+ ]
39
+ s.homepage = "http://github.com/kesselborn/cando"
40
+ s.licenses = ["MIT"]
41
+ s.require_paths = ["lib"]
42
+ s.rubygems_version = "1.8.23"
43
+ s.summary = "Simple roles helper"
44
+
45
+ if s.respond_to? :specification_version then
46
+ s.specification_version = 3
47
+
48
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
49
+ s.add_runtime_dependency(%q<sequel>, [">= 0"])
50
+ s.add_runtime_dependency(%q<rake>, [">= 0"])
51
+ s.add_development_dependency(%q<rspec>, ["~> 2.10.0"])
52
+ s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
53
+ s.add_development_dependency(%q<bundler>, ["~> 1.0"])
54
+ s.add_development_dependency(%q<jeweler>, ["~> 2.0.1"])
55
+ s.add_development_dependency(%q<simplecov>, [">= 0"])
56
+ else
57
+ s.add_dependency(%q<sequel>, [">= 0"])
58
+ s.add_dependency(%q<rake>, [">= 0"])
59
+ s.add_dependency(%q<rspec>, ["~> 2.10.0"])
60
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
61
+ s.add_dependency(%q<bundler>, ["~> 1.0"])
62
+ s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
63
+ s.add_dependency(%q<simplecov>, [">= 0"])
64
+ end
65
+ else
66
+ s.add_dependency(%q<sequel>, [">= 0"])
67
+ s.add_dependency(%q<rake>, [">= 0"])
68
+ s.add_dependency(%q<rspec>, ["~> 2.10.0"])
69
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
70
+ s.add_dependency(%q<bundler>, ["~> 1.0"])
71
+ s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
72
+ s.add_dependency(%q<simplecov>, [">= 0"])
73
+ end
74
+ end
75
+
@@ -0,0 +1,39 @@
1
+ Sequel.migration do
2
+ up do
3
+ create_table :cando_users do
4
+ String :id, :unique => true, :null => false
5
+ primary_key :id
6
+ end
7
+
8
+ create_table :cando_roles do
9
+ String :id, :unique => true, :null => false
10
+ primary_key :id
11
+ end
12
+
13
+ create_table :cando_capabilities do
14
+ String :id, :unique => true, :null => false
15
+ primary_key :id
16
+ end
17
+
18
+ # associations
19
+ create_table :cando_roles_users do
20
+ String :user_id
21
+ String :role_id
22
+ primary_key [:user_id, :role_id], :name => :ur_pk
23
+ end
24
+
25
+ create_table :cando_capabilities_roles do
26
+ String :role_id
27
+ String :capability_id
28
+ primary_key [:role_id, :capability_id], :name =>:rc_pk
29
+ end
30
+ end
31
+
32
+ down do
33
+ drop_table :cando_users
34
+ drop_table :cando_roles
35
+ drop_table :cando_capabilities
36
+ drop_table :cando_roles_users
37
+ drop_table :cando_capabilities_roles
38
+ end
39
+ end
data/lib/cando.rb ADDED
@@ -0,0 +1,88 @@
1
+ require 'sequel'
2
+
3
+ if File.basename($0) == "rake" # we are in a rake call: export our rake stuff
4
+ require 'rake'
5
+ import File.join(File.dirname(File.dirname(__FILE__)), "lib", "tasks", "cando.rake" )
6
+ end
7
+
8
+ module CanDo
9
+ class ConfigCannotBlockError < RuntimeError; end
10
+ class ConfigMysqlDBError < RuntimeError; end
11
+ class ConfigMysqlConnectionError < RuntimeError; end
12
+ class DBNotConnected < RuntimeError; end
13
+
14
+ def self.db
15
+ @db or raise DBNotConnected.new("CanDo is not connected to a database")
16
+ end
17
+
18
+ def self.init(&block)
19
+ CanDo.instance_eval &block
20
+
21
+ begin
22
+ Sequel::Model.db.test_connection
23
+ rescue ::Sequel::Error => e
24
+ raise DBNotConnected.new("No database connection established. Have you called 'connect' within the 'CanDo.init' block? Sequel error message is:\n#{e.message}")
25
+ end
26
+
27
+ Dir.glob(File.expand_path("#{__FILE__}/../models/*.rb")).each do |model|
28
+ require_relative model
29
+ end
30
+
31
+ Sequel::Model.db
32
+ end
33
+
34
+
35
+ # will be executed if `can(user_urn, :capability) { }` will not be executed
36
+ # due to missing capabilities
37
+ def self.cannot_block(&block)
38
+ if !block
39
+ raise ConfigCannotBlockError.new("CanDo#cannot_block expects block")
40
+ end
41
+ if block.arity != 2
42
+ raise ConfigCannotBlockError.new("CanDo#cannot_block expects block expecting two arguments |user_urn, capability|")
43
+ end
44
+
45
+ @@cannot_block_proc = block
46
+ end
47
+
48
+ def self.connect(connection)
49
+ if connection =~ /sqlite/
50
+ raise ConfigMysqlDBError.new("sqlite is not supported as it misses certain constraints")
51
+ end
52
+
53
+ begin
54
+ @db = Sequel.connect(connection)
55
+ @db.test_connection
56
+ @db
57
+ rescue => e
58
+ raise ConfigMysqlConnectionError.new(<<-EOF
59
+ Error connecting to database. Be sure to pass in a databse config like 'mysql://user:passwd@host/database':
60
+ #{e.message}
61
+ EOF
62
+ )
63
+ end
64
+ end
65
+
66
+ def can(user_urn, capability)
67
+ user = CanDo::User.find(:id => user_urn)
68
+ has_permission = user && user.can(capability)
69
+ if block_given?
70
+ if has_permission
71
+ return yield
72
+ end
73
+ if @@cannot_block_proc
74
+ @@cannot_block_proc.call(user_urn, capability)
75
+ end
76
+ end
77
+
78
+ has_permission
79
+ end
80
+
81
+ def define_role(role, capabilities)
82
+ CanDo::Role.define_role(role, capabilities)
83
+ end
84
+
85
+ def assign_roles(user, roles)
86
+ CanDo::User.find_or_create(:id => user).assign_roles(roles)
87
+ end
88
+ end
@@ -0,0 +1,14 @@
1
+ module CanDo
2
+ class Capability < Sequel::Model(:cando_capabilities)
3
+ many_to_many :roles, :join_table => :cando_capabilities_roles
4
+ unrestrict_primary_key
5
+
6
+ def self.cleanup
7
+ Capability.all do |cap|
8
+ if cap.roles.count == 0
9
+ cap.destroy
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,27 @@
1
+ module CanDo
2
+ class Role < Sequel::Model(:cando_roles)
3
+ many_to_many :users, :join_table => :cando_roles_users
4
+ many_to_many :capabilities, :join_table => :cando_capabilities_roles
5
+ unrestrict_primary_key
6
+
7
+ def before_destroy
8
+ self.remove_all_capabilities
9
+ self.remove_all_users
10
+ Capability.cleanup
11
+ end
12
+
13
+ def self.define_role(name, capabilities)
14
+ role = Role.find_or_create(:id => name)
15
+ role.remove_all_capabilities
16
+ capabilities.each do |capability|
17
+ role.add_capability( Capability.find_or_create(:id => capability.to_s) )
18
+ end
19
+
20
+ role
21
+ end
22
+
23
+ def to_s
24
+ "#{id}\t#{capabilities.map(&:id).join(",")}"
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,40 @@
1
+ module CanDo
2
+ class UndefinedRole < Exception; end
3
+ class User < Sequel::Model(:cando_users)
4
+ many_to_many :roles, :join_table => :cando_roles_users
5
+ unrestrict_primary_key
6
+
7
+ def capabilities
8
+ roles.inject([]){|a,role| a << role.capabilities}.flatten.uniq.map(&:id)
9
+ end
10
+
11
+ def can(capability)
12
+ capabilities.include?(capability.to_s)
13
+ end
14
+
15
+ def assign_roles(roles)
16
+ self.class.db.transaction do
17
+ self.remove_all_roles
18
+ roles.each do |r|
19
+ begin
20
+ role = r.is_a?(CanDo::Role) ? r : CanDo::Role.where(:id => r).first!
21
+ self.add_role(role)
22
+ rescue Sequel::UniqueConstraintViolation => e
23
+ puts "user already has role '#{r}'"
24
+ rescue Sequel::NoMatchingRow
25
+ raise UndefinedRole.new("Role '#{r}' does not exist")
26
+ end
27
+ end
28
+ end
29
+ self
30
+ end
31
+
32
+ def role_names
33
+ roles.map(&:id)
34
+ end
35
+
36
+ def to_s
37
+ "#{id}\t#{role_names.join(",")}\t#{capabilities.join(",")}"
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,138 @@
1
+ require 'tmpdir'
2
+
3
+ CANDO_MIGRATION_DIR = "db/cando-migrations"
4
+ CANDO_SCHEMA = File.join(File.dirname(File.dirname(File.dirname(__FILE__))), "contrib", "initial_schema.rb")
5
+
6
+ namespace :cando do
7
+ desc "Initialize cando (creates schema and runs migration)"
8
+ task :init do
9
+ if Dir.glob("#{CANDO_MIGRATION_DIR}/*#{File.basename(CANDO_SCHEMA)}*").empty?
10
+ puts green("Copying cando schema to local cando migrations folder '#{CANDO_MIGRATION_DIR}'")
11
+ cando_schema_migration = File.join(CANDO_MIGRATION_DIR, "#{Time.now.strftime("%Y%m%d%H%M%S")}_#{File.basename(CANDO_SCHEMA)}")
12
+
13
+ FileUtils.mkdir_p(CANDO_MIGRATION_DIR) unless File.exists?(CANDO_MIGRATION_DIR)
14
+ FileUtils.cp(CANDO_SCHEMA, cando_schema_migration)
15
+ else
16
+ $stderr.puts red("skipping copying cando schema migration file: already exists")
17
+ end
18
+
19
+ Sequel::Migrator.run(connect_to_db, CANDO_MIGRATION_DIR, { allow_missing_migration_files: true} )
20
+
21
+ puts <<EOF
22
+ #{green("Success!")}
23
+
24
+ In order to add, update or remove a role, call
25
+
26
+ rake cando:add role=<rolename> capabilities=<cap1>,<cap2>,<cap3>
27
+ rake cando:update role=<rolename> capabilities=<cap1>,<cap2>,<cap3>
28
+ rake cando:remove role=<rolename>
29
+
30
+ When adding or updating a roles it doesn't matter whether the passed in capabilities
31
+ exist or not -- if not existant, they will be created automatically
32
+
33
+ For more cando rake tasks execute
34
+
35
+ rake -T cando
36
+
37
+ EOF
38
+
39
+ end
40
+
41
+ desc "Add a new role (pass in role name and capabilities with role=<name> capabilities=<cap1>,<cap2>,... )"
42
+ task :add do
43
+ add_role(false)
44
+ end
45
+
46
+ desc "Update role (pass in role name and capabilities with role=<name> capabilities=<cap1>,<cap2>,... )"
47
+ task :update do
48
+ add_role(true)
49
+ end
50
+
51
+
52
+ desc "Remove role (pass in role name with role=<name>)"
53
+ task :remove do
54
+ unless ENV['role']
55
+ $stderr.puts red("usage: rake cando:remove role=<rolename>")
56
+ exit 1
57
+ end
58
+
59
+ connect_to_db
60
+ unless r = CanDo::Role.find(:id => ENV['role'])
61
+ $stderr.puts red("role '#{args.role}' does not exist")
62
+ exit 1
63
+ end
64
+
65
+ r.destroy
66
+ end
67
+
68
+ desc "Assign role to user (args: roles=<r1>,<r2>,<rn> user=<user_urn>)"
69
+ task :assign do
70
+ roles = ENV['roles']
71
+ user_urn = ENV['user']
72
+
73
+ unless roles && user_urn
74
+ $stderr.puts red("usage: rake cando:assign user=<user_urn> roles=<role1>,<role2>,... ")
75
+ end
76
+
77
+ connect_to_db
78
+ include CanDo
79
+ assign_roles(user_urn, roles.split(","))
80
+ end
81
+
82
+ desc "List roles"
83
+ task :list do
84
+ connect_to_db
85
+ puts "ROLE\tCAPABILITIES"
86
+ CanDo::Role.all.each do |role|
87
+ puts role
88
+ end
89
+ end
90
+
91
+ desc "List users and their roles"
92
+ task :users do
93
+ connect_to_db
94
+ puts "USER_URN\tROLES\tCAPABILITIES"
95
+ CanDo::User.all.each do |user|
96
+ puts user
97
+ end
98
+ end
99
+
100
+ end
101
+
102
+ def green(text)
103
+ "\033[1;32;48m#{text}\033[m"
104
+ end
105
+
106
+ def red(text)
107
+ "\033[1;31;48m#{text}\033[m"
108
+ end
109
+
110
+ def add_role(force = false)
111
+ role = ENV['role']
112
+ capabilities = ENV['capabilities'] && ENV['capabilities'].split(",")
113
+
114
+ unless role && capabilities
115
+ puts red("usage: rake cando:add role=<rolename> capabilities=<cap1>,<cap2>,<cap3>")
116
+ exit 1
117
+ end
118
+
119
+ connect_to_db
120
+ if !force && CanDo::Role.find(:id => role)
121
+ puts red("Role '#{role}' already exists!")
122
+ puts "If you want to update '#{role}', please use 'rake cando:update'"
123
+ exit 1
124
+ end
125
+
126
+ include CanDo
127
+ define_role(role, capabilities)
128
+ end
129
+
130
+ def connect_to_db
131
+ unless ENV['CANDO_DB']
132
+ raise "Please pass in database config, e.g. CANDO_DB=mysql://user:passwd@host/database bundle exec rake cando:<command>"
133
+ end
134
+
135
+ CanDo.init do
136
+ connect(ENV['CANDO_DB'])
137
+ end
138
+ end
@@ -0,0 +1,126 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ #describe "Cando" do
4
+ # it "fails" do
5
+ # fail "hey buddy, you should probably rename this file and start specing for real"
6
+ # end
7
+ #end
8
+ #
9
+ describe "CanDo module methods" do
10
+ context "CanDo.cannot_block expects block accepting two parameters" do
11
+ it { expect{ CanDo.cannot_block }.to raise_error(CanDo::ConfigCannotBlockError) }
12
+ it { expect{ CanDo.cannot_block{|x| x} }.to raise_error(CanDo::ConfigCannotBlockError) }
13
+ it { expect{ CanDo.cannot_block{|x,y,z| x} }.to raise_error(CanDo::ConfigCannotBlockError) }
14
+
15
+ it { expect{ CanDo.cannot_block{|x,y| x} }.to_not raise_error(CanDo::ConfigCannotBlockError) }
16
+ end
17
+
18
+ context "CanDo.connect" do
19
+ it { expect{ CanDo.connect(nil) }.to raise_error(CanDo::ConfigMysqlConnectionError) }
20
+ it { expect{ CanDo.connect("sqlite::memory:") }.to raise_error(CanDo::ConfigMysqlDBError) }
21
+ it { CanDo.connect(ENV['CANDO_TEST_DB']).test_connection be_true }
22
+ end
23
+
24
+ context "CanDo.init" do
25
+ context "CanDo.cannot_block called from config" do
26
+ it { expect{ CanDo.init{ cannot_block }}.to raise_error(CanDo::ConfigCannotBlockError) }
27
+ end
28
+ end
29
+
30
+ context "CanDo exported methods" do
31
+ include CanDo
32
+
33
+ context "CanDo#define_role" do
34
+ context "empty role (no capabilities)" do
35
+ it { expect{ define_role("role", []) }.to change{CanDo::Role.count}.from(0).to(1) }
36
+ it { expect{ define_role("role", []) }.to change{CanDo::Capability.count}.by(0) }
37
+ end
38
+
39
+ context "standard situation" do
40
+ it { expect{ define_role("role", [:capability1, :capability2]) }.to change{CanDo::Role.count}.from(0).to(1) }
41
+ it { expect{ define_role("role", [:capability1, :capability2]) }.to change{CanDo::Capability.count}.from(0).to(2) }
42
+ end
43
+
44
+ context "duplication" do
45
+ before(:each) { define_role("role", [:capability]) }
46
+
47
+ it { expect{ define_role("role", [:new_capability]) }.to change{CanDo::Role.count}.by(0) }
48
+ it { expect{ define_role("new_role", [:capability]) }.to change{CanDo::Capability.count}.by(0) }
49
+ end
50
+
51
+ context "cleanup" do
52
+ before(:each) do
53
+ @role = define_role("role", [:capabilty])
54
+ end
55
+
56
+ it { expect{ @role.destroy }.to change{CanDo::Role.count}.from(1).to(0) }
57
+ it { expect{ @role.destroy }.to change{CanDo::Capability.count}.from(1).to(0) }
58
+ end
59
+ end
60
+
61
+ context "CanDo#assign_roles" do
62
+ include CanDo
63
+
64
+ context "user without roles" do
65
+ it { expect{ assign_roles("user", []) }.to change{CanDo::User.count}.from(0).to(1) }
66
+ it { expect{ assign_roles("user", []) }.to change{CanDo::Role.count}.by(0) }
67
+ end
68
+
69
+ context "invalid roles" do
70
+ it { expect{ assign_roles("user", ["non-existant-role"]) }.to raise_error(CanDo::UndefinedRole) }
71
+ it { expect{ assign_roles("user", [nil]) }.to raise_error(CanDo::UndefinedRole) }
72
+ end
73
+
74
+ context "standard situation" do
75
+ before(:each) do
76
+ @r1 = define_role("r1", [:c1, :c2])
77
+ @r2 = define_role("r2", [:c2, :c3])
78
+ end
79
+
80
+ it { expect{ assign_roles("user", ["r1"]) }.to change{CanDo::User.count}.from(0).to(1) }
81
+ it { expect{ assign_roles("user", ["r1","r2"]) }.to change{CanDo::User.count}.from(0).to(1) }
82
+
83
+ it { expect{ assign_roles("user", [@r1]) }.to change{CanDo::User.count}.from(0).to(1) }
84
+ it { expect{ assign_roles("user", ["r1",@r2]) }.to change{CanDo::User.count}.from(0).to(1) }
85
+ end
86
+
87
+ context "cleanup" do
88
+ before(:each) do
89
+ @role = define_role("role", [:capabilty])
90
+ @user = assign_roles("user", [@role])
91
+ end
92
+
93
+ it { expect{ @user.destroy }.to change{CanDo::User.count}.from(1).to(0) }
94
+ it { expect{ @user.destroy }.to change{CanDo::Role.count}.by(0) }
95
+ end
96
+ end
97
+
98
+ context "CanDo#can" do
99
+ include CanDo
100
+
101
+ before(:each) do
102
+ @role = define_role("role", [:capability1, :capabilty2])
103
+ @user = assign_roles("user", [@role])
104
+ end
105
+
106
+ context "CanDo#can" do
107
+ it { can("user", :capability1).should be_true }
108
+ it { can("user", :undefined_capability).should be_false }
109
+
110
+ it { expect{ |can| can("user", :capability1, &can) }.to yield_control }
111
+
112
+ context "CanDo.cannot_block" do
113
+ class DummyException < RuntimeError; end
114
+ before(:all) do
115
+ CanDo.cannot_block do |user, capabilty|
116
+ raise DummyException
117
+ end
118
+ end
119
+
120
+ it { expect{ can("user", :undefined_capability){} }.to raise_error(DummyException) }
121
+ it { expect{ can("user", :undefined_capability)}.to_not raise_error(DummyException) }
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,48 @@
1
+ require 'simplecov'
2
+ require 'cando'
3
+
4
+ module SimpleCov::Configuration
5
+ def clean_filters
6
+ @filters = []
7
+ end
8
+ end
9
+
10
+ SimpleCov.configure do
11
+ clean_filters
12
+ load_profile 'test_frameworks'
13
+ end
14
+
15
+ ENV["COVERAGE"] && SimpleCov.start do
16
+ add_filter "/.rvm/"
17
+ end
18
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
19
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
20
+
21
+ require 'rspec'
22
+ require 'cando'
23
+
24
+ # Requires supporting files with custom matchers and macros, etc,
25
+ # in ./support/ and its subdirectories.
26
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
27
+
28
+ ENV['CANDO_TEST_DB'] or raise "Please set CANDO_TEST_DB to a valid configuration similar to 'mysql://user:passwd@localhost/database'"
29
+
30
+ RSpec.configure do |config|
31
+ Sequel.extension :migration
32
+ migration = eval(File.read(File.join(File.dirname(File.dirname(__FILE__)), "contrib", "initial_schema.rb")))
33
+ db = nil
34
+
35
+ config.before(:suite) do
36
+ db = CanDo.init do
37
+ connect ENV['CANDO_TEST_DB']
38
+ end
39
+ end
40
+
41
+ config.before(:each) do
42
+ db.drop_table(*db.tables)
43
+ migration.apply(db, :up)
44
+ end
45
+ end
46
+
47
+ def connect
48
+ end
metadata ADDED
@@ -0,0 +1,180 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cando
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Daniel Bornkessel
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-05-20 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: sequel
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 2.10.0
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 2.10.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: rdoc
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '3.12'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '3.12'
78
+ - !ruby/object:Gem::Dependency
79
+ name: bundler
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: '1.0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: '1.0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: jeweler
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ~>
100
+ - !ruby/object:Gem::Version
101
+ version: 2.0.1
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ version: 2.0.1
110
+ - !ruby/object:Gem::Dependency
111
+ name: simplecov
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ description: cando description
127
+ email: daniel@soundcloud.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files:
131
+ - LICENSE.txt
132
+ - README.md
133
+ files:
134
+ - .document
135
+ - .rspec
136
+ - .travis.yml
137
+ - Gemfile
138
+ - Gemfile.lock
139
+ - LICENSE.txt
140
+ - README.md
141
+ - Rakefile
142
+ - VERSION
143
+ - cando.gemspec
144
+ - contrib/initial_schema.rb
145
+ - lib/cando.rb
146
+ - lib/models/capability.rb
147
+ - lib/models/role.rb
148
+ - lib/models/user.rb
149
+ - lib/tasks/cando.rake
150
+ - spec/cando_spec.rb
151
+ - spec/spec_helper.rb
152
+ homepage: http://github.com/kesselborn/cando
153
+ licenses:
154
+ - MIT
155
+ post_install_message:
156
+ rdoc_options: []
157
+ require_paths:
158
+ - lib
159
+ required_ruby_version: !ruby/object:Gem::Requirement
160
+ none: false
161
+ requirements:
162
+ - - ! '>='
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ segments:
166
+ - 0
167
+ hash: -3220119119017063207
168
+ required_rubygems_version: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ requirements: []
175
+ rubyforge_project:
176
+ rubygems_version: 1.8.23
177
+ signing_key:
178
+ specification_version: 3
179
+ summary: Simple roles helper
180
+ test_files: []