ixtlan-core 0.6.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/README.md +64 -0
  2. data/features/headers.feature +12 -0
  3. data/features/step_definitions/simple_steps.rb +1 -0
  4. data/lib/ixtlan/core/active_record.rb +22 -0
  5. data/lib/ixtlan/core/active_record.rb~ +24 -0
  6. data/lib/ixtlan/core/controllers/configuration_controller.rb~ +9 -0
  7. data/lib/ixtlan/core/data_mapper.rb +23 -0
  8. data/lib/ixtlan/core/data_mapper.rb~ +18 -0
  9. data/lib/ixtlan/core/optimistic_active_record.rb~ +21 -0
  10. data/lib/ixtlan/core/{optimistic_data_mapper.rb → optimistic_data_mapper.rb~} +1 -1
  11. data/lib/ixtlan/core/railtie.rb +6 -6
  12. data/lib/ixtlan/core/x_content_headers.rb~ +32 -0
  13. data/lib/ixtlan/core/x_content_type_headers.rb~ +30 -0
  14. data/lib/ixtlan/core/x_xss_protection_headers.rb~ +30 -0
  15. data/spec/cache_headers_spec.rb~ +52 -0
  16. data/spec/configuration_manager_spec.rb~ +40 -0
  17. data/spec/controller.rb~ +8 -0
  18. data/spec/x_headers_spec.rb~ +74 -0
  19. metadata +24 -37
  20. data/lib/generators/ixtlan/base.rb +0 -45
  21. data/lib/generators/ixtlan/configuration_model/configuration_model_generator.rb +0 -12
  22. data/lib/generators/ixtlan/configuration_scaffold/configuration_scaffold_generator.rb +0 -12
  23. data/lib/generators/ixtlan/setup/setup_generator.rb +0 -38
  24. data/lib/generators/ixtlan/setup/templates/application_layout.html.erb +0 -17
  25. data/lib/generators/ixtlan/setup/templates/database.yml.example +0 -48
  26. data/lib/generators/ixtlan/setup/templates/error.html.erb +0 -1
  27. data/lib/generators/ixtlan/setup/templates/error_with_session.html.erb +0 -1
  28. data/lib/generators/ixtlan/setup/templates/gitignore +0 -2
  29. data/lib/generators/ixtlan/setup/templates/initializer.rb +0 -64
  30. data/lib/generators/ixtlan/setup/templates/preinitializer.rb +0 -31
  31. data/lib/generators/ixtlan/setup/templates/production.yml.example +0 -8
  32. data/lib/generators/ixtlan/setup/templates/stale.html.erb +0 -2
  33. data/lib/generators/model/model_generator.rb +0 -12
  34. data/lib/generators/rails/active_record/active_record_generator.rb +0 -40
  35. data/lib/generators/rails/active_record/model/migration.rb +0 -19
  36. data/lib/generators/rails/active_record/model/model.rb +0 -16
  37. data/lib/generators/rails/erb/erb_generator.rb +0 -37
  38. data/lib/generators/rails/erb/scaffold/_form.html.erb +0 -28
  39. data/lib/generators/rails/erb/scaffold/edit.html.erb +0 -24
  40. data/lib/generators/rails/erb/scaffold/index.html.erb +0 -49
  41. data/lib/generators/rails/erb/scaffold/new.html.erb +0 -11
  42. data/lib/generators/rails/erb/scaffold/show.html.erb +0 -30
  43. data/lib/generators/rails/scaffold_controller/scaffold_controller/controller.rb +0 -129
  44. data/lib/generators/rails/scaffold_controller/scaffold_controller/singleton_controller.rb +0 -43
  45. data/lib/generators/scaffold/scaffold_generator.rb +0 -35
  46. data/lib/generators/scaffold_controller/scaffold_controller_generator.rb +0 -34
  47. data/lib/ixtlan/core/optimistic_active_record.rb +0 -18
data/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # Ixtlan #
2
+
3
+ this gem adds more security related headers to the response for a rails3 application. mainly inspired by
4
+ [google-gets-a-1-for-browser-security](http://www.barracudalabs.com/wordpress/index.php/2011/07/21/google-gets-a-1-for-browser-security-3/)
5
+ and
6
+ [HttpCaching](http://code.google.com/p/doctype/wiki/ArticleHttpCaching).
7
+ and
8
+ [Clickjacking](http://www.owasp.org/index.php/Clickjacking)
9
+
10
+ the extra headers are
11
+
12
+ * x-frame headers
13
+ * x-content-type headers
14
+ * x-xss-protection headers
15
+ * caching headers
16
+
17
+ the main idea is to set the default as strict as possible and the application might relax the setup here and there.
18
+
19
+ ## rails configuration ##
20
+
21
+ in _config/application.rb_ or in one of the _config/environments/*rb_ files or in an initializer. all three x-headers can be configured here, for example
22
+
23
+ config.x_content_type_headers = :nosniff
24
+
25
+ ## controller configuration ##
26
+
27
+ just add in your controller something like
28
+
29
+ x_xss_protection :block
30
+
31
+ ## option for each *render*, *send\_file*, *send\_data* methods
32
+
33
+ an example for an inline render
34
+
35
+ render :inline => 'behappy', :x_frame_headers => :deny
36
+
37
+ ## possible values ##
38
+
39
+ * x\_frame\_headers : `:deny, :sameorigin, :off` default `:deny`
40
+
41
+ * x\_content\_type\_headers : `:nosniff, :off` default `:nosniff`
42
+
43
+ * x\_xss\_protection\_headers : `:block, :disabled, :off` default `:block`
44
+
45
+ ## cache headers
46
+
47
+ the cache headers needs to have a **current\_user**, i.e. the current\_user method of the controller needs to return a non-nil value. further the the method needs to `:get` and the response status an "ok" status,
48
+
49
+ then you can use the controller configuration or the options with *render*, *send\_file* and *send\_data*.
50
+
51
+ ## possible values ##
52
+
53
+ * `:private` : which tells not to cache or store any data except the browser memory: [no caching](http://code.google.com/p/doctype/wiki/ArticleHttpCaching#No_caching)
54
+
55
+ * `:protected` : no caching but the browser: [Only the end user's browser is allowed to cache](http://code.google.com/p/doctype/wiki/ArticleHttpCaching#Only_the_end_user%27s_browser_is_allowed_to_cache)
56
+
57
+ * `:public` : caching is allowed: [Both browser and proxy allowed to cache](http://code.google.com/p/doctype/wiki/ArticleHttpCaching#Both_browser_and_proxy_allowed_to_cache)
58
+
59
+ * `:my_headers` : custom header method like
60
+
61
+ > def my_headers
62
+ no_store = false
63
+ no_caching(no_store)
64
+ end
@@ -0,0 +1,12 @@
1
+ Feature: Generators for Ixtlan Core
2
+
3
+ Scenario: Create a rails application and adding the ixtlan-core adds ixtlan generators
4
+ Given I create new rails application with template "headers.template" and "headers" tests
5
+ And I execute "rails generate scaffold user name:string --skip --migration"
6
+ And I execute "rake db:migrate test"
7
+ Then the output should contain "7 tests, 20 assertions, 0 failures, 0 errors" and "1 tests, 1 assertions, 0 failures, 0 errors"
8
+
9
+ Given me an existing rails application "headers" and "optimistic" files
10
+ And I execute "rails generate scaffold account name:string --skip --migration --timestamps --optimistic --modified-by user"
11
+ And I execute "rake db:migrate test"
12
+ Then the output should contain "14 tests, 30 assertions, 0 failures, 0 errors" and "2 tests, 2 assertions, 0 failures, 0 errors"
@@ -0,0 +1 @@
1
+ require 'maven/cucumber_steps'
@@ -0,0 +1,22 @@
1
+ module Ixtlan
2
+ module Core
3
+ module ActiveRecord
4
+
5
+ def self.included(base)
6
+ base.class_eval do
7
+
8
+ def self.optimistic_find(updated_at, *args)
9
+ if updated_at
10
+ updated_at_date = new(:updated_at => updated_at).updated_at
11
+ # try different ways to use the date
12
+ # TODO maybe there is a nicer way ??
13
+ first(:conditions => ["id = ? and updated_at <= ? and updated_at >= ?", args[0], updated_at, updated_at_date - 0.001]) || first(:conditions => ["id = ? and updated_at <= ? and updated_at >= ?", args[0], updated_at_date, updated_at_date - 0.001])
14
+ # TODO make it work with different PKs
15
+ end
16
+ end
17
+
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,24 @@
1
+ module Ixtlan
2
+ module Core
3
+ module OptimisticActiveRecord
4
+
5
+ def self.included(base)
6
+ base.class_eval do
7
+
8
+ attr_accessor :current_user
9
+
10
+ def self.optimistic_find(updated_at, *args)
11
+ if updated_at
12
+ updated_at_date = new(:updated_at => updated_at).updated_at
13
+ # try different ways to use the date
14
+ # TODO maybe there is a nicer way ??
15
+ first(:conditions => ["id = ? and updated_at <= ? and updated_at >= ?", args[0], updated_at, updated_at_date - 0.001]) || first(:conditions => ["id = ? and updated_at <= ? and updated_at >= ?", args[0], updated_at_date, updated_at_date - 0.001])
16
+ # TODO make it work with different PKs
17
+ end
18
+ end
19
+
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,9 @@
1
+ module Ixtlan
2
+ module Guard
3
+ module Controllers
4
+ module MaintenanceController
5
+
6
+ # GET /configuration
7
+ # GET /configuration.xml
8
+ # GET /configuration.json
9
+ def index
@@ -0,0 +1,23 @@
1
+ module Ixtlan
2
+ module Core
3
+ module DataMapper
4
+
5
+ def self.included(base)
6
+ base.class_eval do
7
+
8
+ attr_accessor :current_user
9
+
10
+ def optimistic_find(updated_at, *args)
11
+ if updated_at
12
+ updated_at = new(:updated_at => updated_at).updated_at
13
+ # TODO make it work with different PKs
14
+ first(:id => args[0], :updated_at => updated_at)
15
+ end
16
+ end
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,18 @@
1
+ module Ixtlan
2
+ module Core
3
+ module OptimisticDataMapper
4
+
5
+ def self.included(base)
6
+ base.class_eval do
7
+ def optimistic_find(updated_at, *args)
8
+ if updated_at
9
+ updated_at = new(:updated_at => updated_at).updated_at
10
+ # TODO make it work with different PKs
11
+ first(:id => args[0], :updated_at => updated_at)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,21 @@
1
+ module Ixtlan
2
+ module Core
3
+ module OptimisticActiveRecord
4
+
5
+ def self.included(base)
6
+ base.class_eval do
7
+ def self.optimistic_find(updated_at, *args)
8
+ p updated_at
9
+ updated_at = new(:updated_at => updated_at).updated_at
10
+ # TODO make it work with different PKs
11
+ r = first(:conditions => ["id = ? and updated_at = ?", args[0], updated_at])
12
+ p args[0]
13
+ p updated_at
14
+ p r
15
+ r
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -7,7 +7,7 @@ module Ixtlan
7
7
  def optimistic_find(updated_at, *args)
8
8
  updated_at = new(:updated_at => updated_at).updated_at
9
9
  # TODO make it work with different PKs
10
- first(:id => args[0], :updated_at => updated_at)
10
+ first(:id => args[0], :updated_at => updated_at])
11
11
  end
12
12
  end
13
13
  end
@@ -3,8 +3,8 @@ require 'ixtlan/core/cache_headers'
3
3
  require 'ixtlan/core/x_frame_headers'
4
4
  require 'ixtlan/core/x_content_type_headers'
5
5
  require 'ixtlan/core/x_xss_protection_headers'
6
- require 'ixtlan/core/optimistic_active_record'
7
- require 'ixtlan/core/optimistic_data_mapper'
6
+ require 'ixtlan/core/active_record'
7
+ require 'ixtlan/core/data_mapper'
8
8
  require 'ixtlan/core/configuration_rack'
9
9
  require 'ixtlan/core/configuration_manager'
10
10
  module Ixtlan
@@ -63,15 +63,15 @@ module Ixtlan
63
63
  app.config.middleware.use Ixtlan::Core::ConfigurationRack
64
64
  end
65
65
  config.after_initialize do |app|
66
- if defined? DataMapper
66
+ if defined? ::DataMapper
67
67
 
68
68
  ::DataMapper::Resource.send(:include,
69
- Ixtlan::Core::OptimisticDataMapper)
69
+ Ixtlan::Core::DataMapper)
70
70
 
71
- elsif defined? ActiveRecord
71
+ elsif defined? ::ActiveRecord
72
72
 
73
73
  ::ActiveRecord::Base.send(:include,
74
- Ixtlan::Core::OptimisticActiveRecord)
74
+ Ixtlan::Core::ActiveRecord)
75
75
 
76
76
  end
77
77
  end
@@ -0,0 +1,32 @@
1
+ module Ixtlan
2
+ module Core
3
+ module XFrameHeaders
4
+
5
+ protected
6
+
7
+ def x_frame_headers(mode = nil)
8
+ case mode || self.class.instance_variable_get(:@_x_frame_mode) || Rails.configuration.x_frame_headers
9
+ when :deny
10
+ response.headers["X-FRAME-OPTIONS"] = "DENY"
11
+ when :sameorigin
12
+ response.headers["X-FRAME-OPTIONS"] = "SAMEORIGIN"
13
+ when :off
14
+ else
15
+ warn "allowed values for x_frame_headers are :deny, :sameorigin, :off"
16
+ end
17
+ end
18
+
19
+ def self.included(base)
20
+ base.class_eval do
21
+ def self.x_frame_headers(mode)
22
+ if(mode)
23
+ @_x_frame_mode = mode.to_sym
24
+ else
25
+ @_x_frame_mode = nil
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,30 @@
1
+ module Ixtlan
2
+ module Core
3
+ module XContentHeaders
4
+
5
+ protected
6
+
7
+ def x_content_headers(mode = nil)
8
+ case mode || self.class.instance_variable_get(:@_x_content_headers) || Rails.configuration.x_content_headers || :nosniff
9
+ when :nosniff
10
+ response.headers["X-Content-Type-Options"] = "nosniff"
11
+ when :off
12
+ else
13
+ warn "allowed values for x_content_headers are :nosniff, :off"
14
+ end
15
+ end
16
+
17
+ def self.included(base)
18
+ base.class_eval do
19
+ def self.x_content_headers(mode)
20
+ if(mode)
21
+ @_x_content_headers = mode.to_sym
22
+ else
23
+ @_x_content_headers = nil
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ module Ixtlan
2
+ module Core
3
+ module XContentHeaders
4
+
5
+ protected
6
+
7
+ def x_content_headers(mode = nil)
8
+ case mode || self.class.instance_variable_get(:@_x_content_headers) || Rails.configuration.x_content_headers || :nosniff
9
+ when :nosniff
10
+ response.headers["X-Content-Type-Options"] = "nosniff"
11
+ when :off
12
+ else
13
+ warn "allowed values for x_content_headers are :nosniff, :off"
14
+ end
15
+ end
16
+
17
+ def self.included(base)
18
+ base.class_eval do
19
+ def self.x_content_headers(mode)
20
+ if(mode)
21
+ @_x_content_headers = mode.to_sym
22
+ else
23
+ @_x_content_headers = nil
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,52 @@
1
+ require 'controller'
2
+
3
+
4
+ class MyControllerWithUser < ControllerWithUser
5
+ cache_headers :private
6
+ end
7
+
8
+ [:render#, :send_file, :send_data
9
+ ].each do |method|
10
+ describe "cache-headers using controller method #{method}" do
11
+ context "with simple controller" do
12
+ subject { ControllerWithUser.new(Object.new) }
13
+
14
+ it 'should use default' do
15
+ subject.send method, :inline => "asd"
16
+ subject.response.headers.should == {"X-Frame-Options"=>"DENY", "X-Content-Type-Options"=>"nosniff", "X-XSS-Protection"=>"1; mode=block"}
17
+ end
18
+ it 'should use given option' do
19
+ subject.send method, :inline => "asd", :cache_headers => :protected
20
+ subject.response.headers.delete("Date").should_not be_nil
21
+ subject.response.headers.delete("Expires").should_not be_nil
22
+ subject.response.headers.should == {"Cache-Control"=>"private, max-age=0", "X-Frame-Options"=>"DENY", "X-Content-Type-Options"=>"nosniff", "X-XSS-Protection"=>"1; mode=block"}
23
+ end
24
+ end
25
+
26
+ context "with controller with header configuration" do
27
+ subject { MyControllerWithUser.new(Object.new) }
28
+
29
+ it 'should use configuration' do
30
+ subject.send method, :inline => "asd"
31
+ subject.response.headers.delete("Date").should_not be_nil
32
+ subject.response.headers.delete("Expires").should_not be_nil
33
+ subject.response.headers.should == {"Pragma"=>"no-cache", "Cache-Control"=>"no-cache, must-revalidate, no-store", "X-Frame-Options"=>"DENY", "X-Content-Type-Options"=>"nosniff", "X-XSS-Protection"=>"1; mode=block"}
34
+ end
35
+ it 'should use given option' do
36
+ subject.send method, :inline => "asd", :cache_headers => :public
37
+ subject.response.headers.delete("Date").should_not be_nil
38
+ subject.response.headers.delete("Expires").should_not be_nil
39
+ subject.response.headers.should == {"Cache-Control"=>"private, max-age=0", "X-Frame-Options"=>"DENY", "X-Content-Type-Options"=>"nosniff", "X-XSS-Protection"=>"1; mode=block"}
40
+ end
41
+ end
42
+
43
+ context "with simple controller without user" do
44
+ subject { MyControllerWithUser.new }
45
+
46
+ it 'should use default' do
47
+ subject.send method, :inline => "asd"
48
+ subject.response.headers.should == {"X-Frame-Options"=>"DENY", "X-Content-Type-Options"=>"nosniff", "X-XSS-Protection"=>"1; mode=block"}
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,40 @@
1
+ require 'ixtlan/core/configuration_manager'
2
+
3
+ class ConfigModel
4
+
5
+ def self.instance
6
+ @instance ||= self.new
7
+ end
8
+
9
+ def self.after_save(method)
10
+ @method = method.to_sym
11
+ end
12
+
13
+ def self.save_method
14
+ @method
15
+ end
16
+
17
+ def save
18
+ sent self.class.save_method
19
+ end
20
+ end
21
+
22
+ describe Ixtlan::Core::ConfigurationManager do
23
+
24
+ # before :all do
25
+ # ConfigModel.sent :include, Ixtlan::Core::ConfigurationManager
26
+ # end
27
+
28
+ it "should" do
29
+ end
30
+
31
+ it "should register listeners and fire change events" do
32
+ # count = 0
33
+ # ConfigModel.instance.register("counter") do
34
+ # count += 1
35
+ # end
36
+ # ConfigModel.instance.save
37
+ # count.should == 1
38
+ end
39
+
40
+ end