tuersteher 0.5.2 → 0.6.0

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.
data/.gitignore CHANGED
@@ -2,3 +2,4 @@ pkg
2
2
  .idea
3
3
  tuersteher*.gem
4
4
  nbproject
5
+ .rvmrc
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.2
1
+ 0.6.0
data/lib/tuersteher.rb CHANGED
@@ -7,6 +7,7 @@
7
7
  #
8
8
 
9
9
  require 'singleton'
10
+ require 'logger'
10
11
 
11
12
  module Tuersteher
12
13
 
@@ -143,7 +144,10 @@ module Tuersteher
143
144
  Tuersteher::TLogger.logger.info "extend_path_rules_with_prefix: #{prefix}"
144
145
  @path_prefix = prefix
145
146
  path_rules.each do |rule|
146
- rule.path = "#{prefix}#{rule.path}" unless rule.path == :all
147
+ path_spec = rule.path_spezification
148
+ if path_spec
149
+ path_spec.path = "#{prefix}#{path_spec.path}"
150
+ end
147
151
  end
148
152
  end
149
153
 
@@ -302,7 +306,7 @@ module Tuersteher
302
306
  url_path = request.send(@@url_path_method)
303
307
  unless path_access?(url_path, req_method)
304
308
  usr_id = current_user && current_user.respond_to?(:id) ? current_user.id : current_user.object_id
305
- msg = "Tuersteher#check_access: access denied for #{request.request_uri} :#{req_method} user.id=#{usr_id}"
309
+ msg = "Tuersteher#check_access: access denied for #{url_path} :#{req_method} user.id=#{usr_id}"
306
310
  Tuersteher::TLogger.logger.warn msg
307
311
  logger.warn msg # log message also for Rails-Default logger
308
312
  access_denied # Methode aus dem authenticated_system, welche ein redirect zum login auslöst
@@ -360,25 +364,148 @@ module Tuersteher
360
364
  end # of module ModelExtensions
361
365
 
362
366
 
367
+ # The Classes for the separate Rule-Specifications
368
+ class PathSpecification
369
+ attr_reader :path
370
+
371
+ def initialize path, negation
372
+ @negation = negation
373
+ self.path = path
374
+ end
375
+
376
+ def path= url_path
377
+ @path = url_path
378
+ # url_path in regex ^#{path} wandeln ausser bei "/",
379
+ # dies darf keine Regex mit ^/ werden, da diese dann ja immer matchen wuerde
380
+ if url_path == "/"
381
+ @path_regex = /^\/$/
382
+ else
383
+ @path_regex = /^#{url_path}/
384
+ end
385
+ end
386
+
387
+ def grant? path_or_model, method, login_ctx
388
+ rc = @path_regex =~ path_or_model
389
+ rc = !rc if @negation
390
+ rc
391
+ end
392
+ end
393
+
394
+ class ModelSpecification
395
+ def initialize clazz, negation
396
+ @clazz, @negation = clazz, negation
397
+ end
398
+
399
+ def grant? path_or_model, method, login_ctx
400
+ m_class = path_or_model.instance_of?(Class) ? path_or_model : path_or_model.class
401
+ rc = @clazz == m_class
402
+ rc = !rc if @negation
403
+ rc
404
+ end
405
+ end
406
+
407
+ class RolesSpecification
408
+ attr_reader :roles, :negation
409
+
410
+ def initialize role, negation
411
+ @negation = negation
412
+ @roles = [role]
413
+ end
414
+
415
+ def grant? path_or_model, method, login_ctx
416
+ return false if login_ctx.nil?
417
+ # roles sind or verknüpft
418
+ rc = @roles.any?{|role| login_ctx.has_role?(role) }
419
+ rc = !rc if @negation
420
+ rc
421
+ end
422
+ end
423
+
424
+ class MethodSpecification
425
+ def initialize method, negation
426
+ @method, @negation = method, negation
427
+ end
428
+
429
+ def grant? path_or_model, method, login_ctx
430
+ rc = @method==method
431
+ rc = !rc if @negation
432
+ rc
433
+ end
434
+ end
435
+
436
+ class ExtensionSpecification
437
+ def initialize method_name, negation, expected_value=nil
438
+ @method, @negation, @expected_value = method_name, negation, expected_value
439
+ end
440
+
441
+ def grant? path_or_model, method, login_ctx
442
+ rc = false
443
+ if path_or_model.is_a?(String)
444
+ # path-variante
445
+ return false if login_ctx.nil?
446
+ unless login_ctx.respond_to?(@method)
447
+ Tuersteher::TLogger.logger.warn("#{to_s}.grant? => false why Login-Context have not method '#{@method}'!")
448
+ return false
449
+ end
450
+ if @expected_value
451
+ rc = login_ctx.send(@method, @expected_value)
452
+ else
453
+ rc = login_ctx.send(@method)
454
+ end
455
+ else
456
+ # model-variante
457
+ unless path_or_model.respond_to?(@method)
458
+ m_msg = path_or_model.instance_of?(Class) ? "Class '#{path_or_model.name}'" : "Object '#{path_or_model.class}'"
459
+ Tuersteher::TLogger.logger.warn("#{to_s}.grant? => false why #{m_msg} have not method '#{@method}'!")
460
+ return false
461
+ end
462
+ if @expected_value
463
+ rc = path_or_model.send(@method, login_ctx, @expected_value)
464
+ else
465
+ rc = path_or_model.send(@method, login_ctx)
466
+ end
467
+ end
468
+ rc = !rc if @negation
469
+ rc
470
+ end
471
+ end
472
+
473
+
363
474
 
364
- # Astracte base class for Access-Rules
475
+ # Abstracte base class for Access-Rules
365
476
  class BaseAccessRule
477
+ attr_reader :rule_spezifications
366
478
 
367
479
  def initialize
368
- @roles = []
369
- @access_method = :all
480
+ @rule_spezifications = []
481
+ @last_role_specification
370
482
  end
371
483
 
372
484
  # add role
373
485
  def role(role_name)
486
+ return self if role_name==:all # :all is only syntax sugar
374
487
  raise "wrong role '#{role_name}'! Must be a symbol " unless role_name.is_a?(Symbol)
375
- @roles << role_name
488
+ # roles are OR-linked (per default)
489
+ # => add the role to RolesSpecification, create only new RolesSpecification if not exist
490
+ if @last_role_specification
491
+ raise("Mixin of role and not.role are yet not implemented!") if @negation != @last_role_specification.negation
492
+ @last_role_specification.roles << role_name
493
+ else
494
+ @last_role_specification = RolesSpecification.new(role_name, @negation)
495
+ @rule_spezifications << @last_role_specification
496
+ end
497
+ @negation = false if @negation
376
498
  self
377
499
  end
378
500
 
379
501
  # add list of roles
380
502
  def roles(*role_names)
381
- role_names.flatten.each{|role_name| role(role_name)}
503
+ negation_state = @negation
504
+ role_names.flatten.each do |role_name|
505
+ self.role(role_name)
506
+ @negation = negation_state # keep Negation-State for all roles
507
+ end
508
+ @negation = false if @negation
382
509
  self
383
510
  end
384
511
 
@@ -387,11 +514,21 @@ module Tuersteher
387
514
  # method_name: Symbol with the name of the method to call for addional check
388
515
  # expected_value: optional expected value for the result of the with metho_name specified method, defalt is true
389
516
  def extension method_name, expected_value=nil
390
- @check_extensions ||= {}
391
- @check_extensions[method_name] = expected_value
517
+ @rule_spezifications << ExtensionSpecification.new(method_name, @negation, expected_value)
518
+ @negation = false if @negation
392
519
  self
393
520
  end
394
521
 
522
+ # set methode for access
523
+ # access_method Name of Methode for access as Symbol
524
+ def method(access_method)
525
+ return self if access_method==:all # :all is only syntax sugar
526
+ @rule_spezifications << MethodSpecification.new(access_method, @negation)
527
+ @negation = false if @negation
528
+ self
529
+ end
530
+
531
+
395
532
  # mark this rule as grant-rule
396
533
  def grant
397
534
  self
@@ -408,43 +545,34 @@ module Tuersteher
408
545
  @deny
409
546
  end
410
547
 
411
- # set methode for access
412
- # access_method Name of Methode for access as Symbol
413
- def method(access_method)
414
- @access_method = access_method
415
- self
416
- end
417
548
 
418
- # negate role-membership
549
+ # negate role followed rule specification (role or extension
419
550
  def not
420
- @not = true
551
+ @negation = true
421
552
  self
422
553
  end
423
554
 
424
- protected
425
-
426
- # check, if this rule granted for specified user
427
- def grant_role? user
428
- return true if @roles.empty?
429
- return false if user.nil?
430
- role = @roles.detect{|r| user.has_role?(r)}
431
- role = !role if @not
432
- return true if role
433
- false
555
+ # check, if this rule fired for specified parameter
556
+ def fired? path_or_model, method, login_ctx
557
+ login_ctx = nil if login_ctx==:false # manche Authenticate-System setzen den login_ctx/user auf :false
558
+ @rule_spezifications.all?{|spec| spec.grant?(path_or_model, method, login_ctx)}
434
559
  end
435
560
 
436
- def grant_access_method? method
437
- return true if @access_method==:all
438
- @access_method == method
439
- end
440
561
 
562
+ def to_s
563
+ s = "#{self.class}["
564
+ s << 'DENY ' if @deny
565
+ s << @rule_spezifications.map(&:to_s).join(', ')
566
+ s << ']'
567
+ s
568
+ end
441
569
  end # of BaseAccessRule
442
570
 
443
571
 
444
572
  class PathAccessRule < BaseAccessRule
445
573
 
446
574
  METHOD_NAMES = [:get, :edit, :put, :delete, :post, :all].freeze
447
- attr_reader :path
575
+ attr_reader :path_spezification
448
576
 
449
577
  # Zugriffsregel
450
578
  #
@@ -453,23 +581,13 @@ module Tuersteher
453
581
  def initialize(path)
454
582
  raise "wrong path '#{path}'! Must be a String or :all ." unless path==:all or path.is_a?(String)
455
583
  super()
456
- self.path = path
457
- end
458
-
459
- def path= url_path
460
- @path = url_path
461
- if url_path != :all
462
- # path in regex ^#{path} wandeln ausser bei "/",
463
- # dies darf keine Regex mit ^/ werden,
464
- # da diese ja immer matchen wuerde
465
- if url_path == "/"
466
- @path_regex = /^\/$/
467
- else
468
- @path_regex = /^#{url_path}/
469
- end
584
+ if path != :all # :all is only syntax sugar
585
+ @path_spezification = PathSpecification.new(path, @negation)
586
+ @rule_spezifications << @path_spezification
470
587
  end
471
588
  end
472
589
 
590
+
473
591
  # set http-methode
474
592
  # http_method http-Method, allowed is :get, :put, :delete, :post, :all
475
593
  def method(http_method)
@@ -478,57 +596,6 @@ module Tuersteher
478
596
  self
479
597
  end
480
598
 
481
-
482
-
483
- # pruefen, ob Zugriff fuer angegebenen
484
- # path / method fuer den current_user erlaubt ist
485
- #
486
- # user ist ein Object (meist der Loginuser),
487
- # welcher die Methode 'has_role?(role)' besitzen muss.
488
- # *roles ist dabei eine Array aus Symbolen
489
- #
490
- def fired?(path, method, user)
491
- user = nil if user==:false # manche Authenticate-System setzen den user auf :false
492
-
493
- if @path!=:all && !(@path_regex =~ path)
494
- return false
495
- end
496
-
497
- return false unless grant_access_method?(method)
498
- return false unless grant_role?(user)
499
- return false unless grant_extension?(user)
500
-
501
- true
502
- end
503
-
504
-
505
- def to_s
506
- s = "PathAccesRule[#{@deny ? 'DENY ' : ''}#{@path}, #{@access_method}, #{@roles.join(' ')}"
507
- s << " #{@check_extensions.inspect}" if @check_extensions
508
- s << ']'
509
- s
510
- end
511
-
512
- private
513
-
514
- # check, if this rule grant the defined extension (if exist)
515
- def grant_extension? user
516
- return true if @check_extensions.nil?
517
- return false if user.nil? # check_extensions need a user
518
- @check_extensions.each do |key, value|
519
- unless user.respond_to?(key)
520
- Tuersteher::TLogger.logger.warn("#{to_s}.fired? => false why user have not check-extension method '#{key}'!")
521
- return false
522
- end
523
- if value
524
- return false unless user.send(key,value)
525
- else
526
- return false unless user.send(key)
527
- end
528
- end
529
- true
530
- end
531
-
532
599
  end
533
600
 
534
601
 
@@ -538,74 +605,13 @@ module Tuersteher
538
605
  # erzeugt neue Object-Zugriffsregel
539
606
  #
540
607
  # clazz Model-Klassenname oder :all fuer alle
541
- # access_type Zugriffsart (:create, :update, :destroy, :all o.A. selbst definierte Typem)
542
- # roles Aufzählung der erforderliche Rolen (:all für ist egal),
543
- # hier ist auch ein Array von Symbolen möglich
544
- # block optionaler Block, wird mit model und user aufgerufen und muss true oder false liefern
545
- # hier ein Beispiel mit Block:
546
- # <code>
547
- # # Regel, in der sich jeder User selbst aendern darf
548
- # ModelAccessRule.new(User, :update, :all){|model,user| model.id==user.id}
549
- # </code>
550
608
  #
551
609
  def initialize(clazz)
552
610
  raise "wrong clazz '#{clazz}'! Must be a Class or :all ." unless clazz==:all or clazz.is_a?(Class)
553
611
  super()
554
- @clazz = clazz.instance_of?(Symbol) ? clazz : clazz.to_s
555
- end
556
-
557
-
558
- # liefert true, wenn zugriff fuer das angegebene model mit
559
- # der Zugriffsart perm für das security_object hat
560
- #
561
- # model des zupruefende ModelObject
562
- # perm gewunschte Zugriffsart (Symbol :create, :update, :destroy)
563
- #
564
- # user ist ein User-Object (meist der Loginuser),
565
- # welcher die Methode 'has_role?(*roles)' besitzen muss.
566
- # *roles ist dabei eine Array aus Symbolen
567
- #
568
- #
569
- def fired? model, access_method, user
570
- user = nil if user==:false # manche Authenticate-System setzen den user auf :false
571
- m_class = model.instance_of?(Class) ? model : model.class
572
- if @clazz!=m_class.to_s && @clazz!=:all
573
- #Tuersteher::TLogger.logger.debug("#{to_s}.has_access? => false why #{@clazz}!=#{model.class.to_s} && #{@clazz}!=:all")
574
- return false
575
- end
576
-
577
- return false unless grant_access_method?(access_method)
578
- return false unless grant_role?(user)
579
- return false unless grant_extension?(user, model)
580
- true
581
- end
582
-
583
- def to_s
584
- s = "ModelAccessRule[#{@deny ? 'DENY ' : ''}#{@clazz}, #{@access_method}, #{@roles.join(' ')}"
585
- s << " #{@check_extensions.inspect}" if @check_extensions
586
- s << ']'
587
- s
588
- end
589
-
590
- private
591
-
592
- # check, if this rule grant the defined extension (if exist)
593
- def grant_extension? user, model
594
- return true if @check_extensions.nil?
595
- return false if model.nil? # check_extensions need a model
596
- @check_extensions.each do |key, value|
597
- unless model.respond_to?(key)
598
- m_msg = model.instance_of?(Class) ? "Class '#{model.name}'" : "Object '#{model.class}'"
599
- Tuersteher::TLogger.logger.warn("#{to_s}.fired? => false why #{m_msg} have not check-extension method '#{key}'!")
600
- return false
601
- end
602
- if value
603
- return false unless model.send(key,user,value)
604
- else
605
- return false unless model.send(key,user)
606
- end
612
+ if clazz != :all # :all is only syntax sugar
613
+ @rule_spezifications << ModelSpecification.new(clazz, @negation)
607
614
  end
608
- true
609
615
  end
610
616
 
611
617
  end
@@ -58,8 +58,8 @@ end
58
58
  @path_rules = AccessRulesStorage.instance.path_rules
59
59
  end
60
60
 
61
- specify{ @path_rules.first.path.should == :all }
62
- specify{ @path_rules.last.path.should == '/test/special' }
61
+ specify{ @path_rules.first.path_spezification.should be_nil }
62
+ specify{ @path_rules.last.path_spezification.path.should == '/test/special' }
63
63
 
64
64
  end
65
65
  end
@@ -177,7 +177,7 @@ module Tuersteher
177
177
  context 'purge_collection' do
178
178
 
179
179
  class SampleModel
180
- def owner? user; false; end
180
+ def owner?(user); false; end
181
181
  end
182
182
 
183
183
  before do
@@ -150,20 +150,47 @@ module Tuersteher
150
150
  end # of context "deny" do
151
151
 
152
152
 
153
- context "with not as role prefix" do
154
- before(:all) do
155
- @rule = PathAccessRule.new('/admin').deny.not.role(:admin)
156
- @user = stub('user')
157
- end
153
+ context "with not" do
154
+ context "as prefix for role" do
155
+ before(:all) do
156
+ @rule = PathAccessRule.new('/admin').deny.not.role(:admin)
157
+ @user = stub('user')
158
+ end
158
159
 
159
- it "should not fired for user with role :admin" do
160
- @user.stub(:has_role?){|role| role==:admin}
161
- @rule.fired?("/admin", :get, @user).should_not be_true
160
+ it "should not fired for user with role :admin" do
161
+ @user.stub(:has_role?){|role| role==:admin}
162
+ @rule.fired?("/admin", :get, @user).should_not be_true
163
+ end
164
+
165
+ it "should fired for user with role :user" do
166
+ @user.stub(:has_role?){|role| role==:user}
167
+ @rule.fired?("/admin", :get, @user).should be_true
168
+ end
162
169
  end
163
170
 
164
- it "should fired for user with role :user" do
165
- @user.stub(:has_role?){|role| role==:user}
166
- @rule.fired?("/admin", :get, @user).should be_true
171
+ context "as prefix for extension" do
172
+ before(:all) do
173
+ @rule = PathAccessRule.new('/admin').grant.role(:admin).not.extension(:login_ctx_method)
174
+ @user = stub('user')
175
+ end
176
+
177
+ it "should fired for user with role :admin and false for extension" do
178
+ @user.stub(:has_role?){|role| role==:admin}
179
+ @user.should_receive(:login_ctx_method).and_return(false)
180
+ @rule.fired?("/admin", :get, @user).should be_true
181
+ end
182
+
183
+ it "should not fired for user with role :admin and true for extension" do
184
+ @user.stub(:has_role?){|role| role==:admin}
185
+ @user.should_receive(:login_ctx_method).and_return(true)
186
+ @rule.fired?("/admin", :get, @user).should_not be_true
187
+ end
188
+
189
+ it "should not fired for user with role :user" do
190
+ @user.stub(:has_role?){|role| role==:user}
191
+ @rule.fired?("/admin", :get, @user).should be_false
192
+ end
193
+
167
194
  end
168
195
  end # of context "not" do
169
196
 
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'spec'
1
+ require 'rspec'
2
2
  require 'logger'
3
3
  require File.expand_path(File.dirname(__FILE__) + "/../lib/tuersteher")
4
4
 
data/tuersteher.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{tuersteher}
8
- s.version = "0.5.2"
8
+ s.version = "0.6.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Bernd Ledig"]
12
- s.date = %q{2010-11-07}
12
+ s.date = %q{2011-02-02}
13
13
  s.description = %q{Security-Layer for Rails-Application acts like a firewall.}
14
14
  s.email = %q{bernd@ledig.info}
15
15
  s.extra_rdoc_files = [
@@ -41,12 +41,12 @@ Gem::Specification.new do |s|
41
41
  s.rubygems_version = %q{1.3.7}
42
42
  s.summary = %q{Security-Layer for Rails-Application}
43
43
  s.test_files = [
44
- "spec/spec_helper.rb",
45
- "spec/model_extensions_spec.rb",
46
- "spec/access_rules_spec.rb",
47
- "spec/path_access_rule_spec.rb",
44
+ "spec/model_extensions_spec.rb",
45
+ "spec/acces_rules_storage_spec.rb",
46
+ "spec/spec_helper.rb",
48
47
  "spec/model_access_rule_spec.rb",
49
- "spec/acces_rules_storage_spec.rb"
48
+ "spec/access_rules_spec.rb",
49
+ "spec/path_access_rule_spec.rb"
50
50
  ]
51
51
 
52
52
  if s.respond_to? :specification_version then
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tuersteher
3
3
  version: !ruby/object:Gem::Version
4
- hash: 15
4
+ hash: 7
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 5
9
- - 2
10
- version: 0.5.2
8
+ - 6
9
+ - 0
10
+ version: 0.6.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Bernd Ledig
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-11-07 00:00:00 +01:00
18
+ date: 2011-02-02 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies: []
21
21
 
@@ -81,9 +81,9 @@ signing_key:
81
81
  specification_version: 3
82
82
  summary: Security-Layer for Rails-Application
83
83
  test_files:
84
- - spec/spec_helper.rb
85
84
  - spec/model_extensions_spec.rb
85
+ - spec/acces_rules_storage_spec.rb
86
+ - spec/spec_helper.rb
87
+ - spec/model_access_rule_spec.rb
86
88
  - spec/access_rules_spec.rb
87
89
  - spec/path_access_rule_spec.rb
88
- - spec/model_access_rule_spec.rb
89
- - spec/acces_rules_storage_spec.rb