lotusrb 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -0
  3. data/FEATURES.md +17 -0
  4. data/README.md +16 -355
  5. data/lib/lotus.rb +0 -1
  6. data/lib/lotus/action/csrf_protection.rb +167 -0
  7. data/lib/lotus/application.rb +3 -1
  8. data/lib/lotus/cli.rb +14 -13
  9. data/lib/lotus/commands/console.rb +1 -1
  10. data/lib/lotus/commands/db.rb +102 -0
  11. data/lib/lotus/commands/db/abstract.rb +15 -0
  12. data/lib/lotus/commands/db/apply.rb +14 -0
  13. data/lib/lotus/commands/db/console.rb +1 -5
  14. data/lib/lotus/commands/db/create.rb +14 -0
  15. data/lib/lotus/commands/db/drop.rb +14 -0
  16. data/lib/lotus/commands/db/migrate.rb +19 -0
  17. data/lib/lotus/commands/db/prepare.rb +14 -0
  18. data/lib/lotus/commands/db/version.rb +14 -0
  19. data/lib/lotus/commands/generate.rb +20 -20
  20. data/lib/lotus/commands/new.rb +1 -0
  21. data/lib/lotus/commands/routes.rb +1 -2
  22. data/lib/lotus/configuration.rb +29 -0
  23. data/lib/lotus/container.rb +19 -3
  24. data/lib/lotus/environment.rb +62 -9
  25. data/lib/lotus/frameworks.rb +1 -0
  26. data/lib/lotus/generators/action.rb +46 -10
  27. data/lib/lotus/generators/action/action_spec.minitest.tt +1 -1
  28. data/lib/lotus/generators/action/action_spec.rspec.tt +1 -1
  29. data/lib/lotus/generators/action/view_spec.minitest.tt +2 -1
  30. data/lib/lotus/generators/action/view_spec.rspec.tt +2 -1
  31. data/lib/lotus/generators/app.rb +39 -0
  32. data/lib/lotus/generators/app/.gitkeep +1 -0
  33. data/lib/lotus/generators/application/app.rb +184 -0
  34. data/lib/lotus/generators/application/app/.env.development.tt +3 -0
  35. data/lib/lotus/generators/application/app/.env.test.tt +3 -0
  36. data/lib/lotus/generators/application/{container/config → app}/.env.tt +0 -0
  37. data/lib/lotus/generators/application/app/.gitkeep +1 -0
  38. data/lib/lotus/generators/application/app/Gemfile.tt +35 -0
  39. data/lib/lotus/generators/application/app/Rakefile.minitest.tt +10 -0
  40. data/lib/lotus/generators/application/app/Rakefile.rspec.tt +5 -0
  41. data/lib/lotus/generators/application/app/apps/.gitkeep.tt +1 -0
  42. data/lib/lotus/generators/application/app/capybara.rb.rspec.tt +8 -0
  43. data/lib/lotus/generators/application/app/config.ru.tt +3 -0
  44. data/lib/lotus/generators/application/app/config/application.rb.tt +227 -0
  45. data/lib/lotus/generators/application/app/config/environment.rb.tt +5 -0
  46. data/lib/lotus/generators/application/app/config/routes.rb.tt +2 -0
  47. data/lib/lotus/generators/application/app/db/.gitkeep +1 -0
  48. data/lib/lotus/generators/application/app/features_helper.rb.minitest.tt +11 -0
  49. data/lib/lotus/generators/application/app/features_helper.rb.rspec.tt +12 -0
  50. data/lib/lotus/generators/application/app/gitignore.tt +2 -0
  51. data/lib/lotus/generators/application/app/lib/app_name.rb.tt +47 -0
  52. data/lib/lotus/generators/application/app/lib/chirp/entities/.gitkeep +1 -0
  53. data/lib/lotus/generators/application/app/lib/chirp/repositories/.gitkeep +1 -0
  54. data/lib/lotus/generators/application/app/lib/config/mapping.rb.tt +7 -0
  55. data/lib/lotus/generators/application/app/lotusrc.tt +3 -0
  56. data/lib/lotus/generators/application/app/rspec.rspec.tt +2 -0
  57. data/lib/lotus/generators/application/app/schema.sql.tt +0 -0
  58. data/lib/lotus/generators/application/app/spec_helper.rb.minitest.tt +7 -0
  59. data/lib/lotus/generators/application/app/spec_helper.rb.rspec.tt +100 -0
  60. data/lib/lotus/generators/application/app/templates/application.html.erb.tt +9 -0
  61. data/lib/lotus/generators/application/app/views/application_layout.rb.tt +7 -0
  62. data/lib/lotus/generators/application/container.rb +37 -13
  63. data/lib/lotus/generators/application/container/{config/.env.development.tt → .env.development.tt} +0 -0
  64. data/lib/lotus/generators/application/container/{config/.env.test.tt → .env.test.tt} +0 -0
  65. data/lib/lotus/generators/application/container/.env.tt +1 -0
  66. data/lib/lotus/generators/application/container/lib/app_name.rb.tt +9 -0
  67. data/lib/lotus/generators/application/container/schema.sql.tt +0 -0
  68. data/lib/lotus/generators/migration.rb +58 -0
  69. data/lib/lotus/generators/migration/migration.rb.tt +4 -0
  70. data/lib/lotus/generators/model.rb +10 -7
  71. data/lib/lotus/generators/slice.rb +4 -12
  72. data/lib/lotus/generators/slice/application.rb.tt +3 -19
  73. data/lib/lotus/generators/slice/config/routes.rb.tt +1 -7
  74. data/lib/lotus/loader.rb +15 -1
  75. data/lib/lotus/lotusrc.rb +8 -3
  76. data/lib/lotus/templates/{welcome.html → welcome.html.erb} +4 -3
  77. data/lib/lotus/version.rb +1 -1
  78. data/lib/lotus/welcome.rb +20 -1
  79. data/lotusrb.gemspec +5 -5
  80. metadata +67 -18
  81. data/lib/lotus/generators/slice/action.rb.tt +0 -8
  82. data/lib/lotus/generators/slice/config/mapping.rb.tt +0 -13
  83. data/lib/lotus/generators/slice/templates/template.html.erb.tt +0 -2
  84. data/lib/lotus/generators/slice/view.rb.tt +0 -5
  85. data/lib/lotus/logger.rb +0 -141
data/lib/lotus.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require 'lotus/version'
2
2
  require 'lotus/application'
3
3
  require 'lotus/container'
4
- require 'lotus/logger'
5
4
  require 'lotus/environment'
6
5
 
7
6
  # A complete web framework for Ruby
@@ -0,0 +1,167 @@
1
+ require 'securerandom'
2
+
3
+ module Lotus
4
+ module Action
5
+ # Invalid CSRF Token
6
+ #
7
+ # @since 0.4.0
8
+ class InvalidCSRFTokenError < ::StandardError
9
+ end
10
+
11
+ # CSRF Protection
12
+ #
13
+ # This security mechanism is enabled automatically if sessions are turned on.
14
+ #
15
+ # It stores a "challenge" token in session. For each "state changing request"
16
+ # (eg. <tt>POST</tt>, <tt>PATCH</tt> etc..), we should send a special param:
17
+ # <tt>_csrf_token</tt>.
18
+ #
19
+ # If the param matches with the challenge token, the flow can continue.
20
+ # Otherwise the application detects an attack attempt, it reset the session
21
+ # and <tt>Lotus::Action::InvalidCSRFTokenError</tt> is raised.
22
+ #
23
+ # We can specify a custom handling strategy, by overriding <tt>#handle_invalid_csrf_token</tt>.
24
+ #
25
+ # Form helper (<tt>#form_for</tt>) automatically sets a hidden field with the
26
+ # correct token. A special view method (<tt>#csrf_token</tt>) is available in
27
+ # case the form markup is manually crafted.
28
+ #
29
+ # We can disable this check on action basis, by overriding <tt>#verify_csrf_token?</tt>.
30
+ #
31
+ # @since 0.4.0
32
+ #
33
+ # @see https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29
34
+ # @see https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet
35
+ #
36
+ # @example Custom Handling
37
+ # module Web::Controllers::Books
38
+ # class Create
39
+ # include Web::Action
40
+ #
41
+ # def call(params)
42
+ # # ...
43
+ # end
44
+ #
45
+ # private
46
+ #
47
+ # def handle_invalid_csrf_token
48
+ # Web::Logger.warn "CSRF attack: expected #{ session[:_csrf_token] }, was #{ params[:_csrf_token] }"
49
+ # # manual handling
50
+ # end
51
+ # end
52
+ # end
53
+ #
54
+ # @example Bypass Security Check
55
+ # module Web::Controllers::Books
56
+ # class Create
57
+ # include Web::Action
58
+ #
59
+ # def call(params)
60
+ # # ...
61
+ # end
62
+ #
63
+ # private
64
+ #
65
+ # def verify_csrf_token?
66
+ # false
67
+ # end
68
+ # end
69
+ # end
70
+ module CSRFProtection
71
+ # Session and params key for CSRF token.
72
+ #
73
+ # This key is shared with <tt>lotus-controller</tt> and <tt>lotus-helpers</tt>
74
+ #
75
+ # @since 0.4.0
76
+ # @api private
77
+ CSRF_TOKEN = :_csrf_token
78
+
79
+ # Idempotent HTTP methods
80
+ #
81
+ # By default, the check isn't performed if the request method is included
82
+ # in this list.
83
+ #
84
+ # @since 0.4.0
85
+ # @api private
86
+ IDEMPOTENT_HTTP_METHODS = Hash[
87
+ 'GET' => true,
88
+ 'HEAD' => true,
89
+ 'TRACE' => true,
90
+ 'OPTIONS' => true
91
+ ].freeze
92
+
93
+ # @since 0.4.0
94
+ # @api private
95
+ def self.included(action)
96
+ action.class_eval do
97
+ before :set_csrf_token, :verify_csrf_token
98
+ end unless Lotus.env?(:test)
99
+ end
100
+
101
+ private
102
+ # Set CSRF Token in session
103
+ #
104
+ # @since 0.4.0
105
+ # @api private
106
+ def set_csrf_token
107
+ session[CSRF_TOKEN] ||= generate_csrf_token
108
+ end
109
+
110
+ # Verify if CSRF token from params, matches the one stored in session.
111
+ # If not, it raises an error.
112
+ #
113
+ # Don't override this method.
114
+ #
115
+ # To bypass the security check, please override <tt>#verify_csrf_token?</tt>.
116
+ # For custom handling of an attack, please override <tt>#handle_invalid_csrf_token</tt>.
117
+ #
118
+ # @since 0.4.0
119
+ # @api private
120
+ def verify_csrf_token
121
+ handle_invalid_csrf_token if invalid_csrf_token?
122
+ end
123
+
124
+ # Verify if CSRF token from params, matches the one stored in session.
125
+ #
126
+ # Don't override this method.
127
+ #
128
+ # @since 0.4.0
129
+ # @api private
130
+ def invalid_csrf_token?
131
+ verify_csrf_token? &&
132
+ session[CSRF_TOKEN] != params[CSRF_TOKEN]
133
+ end
134
+
135
+ # Generates a random CSRF Token
136
+ #
137
+ # @since 0.4.0
138
+ # @api private
139
+ def generate_csrf_token
140
+ SecureRandom.hex(32)
141
+ end
142
+
143
+ # Decide if perform the check or not.
144
+ #
145
+ # Override and return <tt>false</tt> if you want to bypass security check.
146
+ #
147
+ # @since 0.4.0
148
+ def verify_csrf_token?
149
+ !IDEMPOTENT_HTTP_METHODS[request_method]
150
+ end
151
+
152
+ # Handle CSRF attack.
153
+ #
154
+ # The default policy resets the session and raises an exception.
155
+ #
156
+ # Override this method, for custom handling.
157
+ #
158
+ # @raise [Lotus::Action::InvalidCSRFTokenError]
159
+ #
160
+ # @since 0.4.0
161
+ def handle_invalid_csrf_token
162
+ session.clear
163
+ raise InvalidCSRFTokenError.new
164
+ end
165
+ end
166
+ end
167
+ end
@@ -2,6 +2,7 @@ require 'lotus/utils/class_attribute'
2
2
  require 'lotus/frameworks'
3
3
  require 'lotus/configuration'
4
4
  require 'lotus/loader'
5
+ require 'lotus/logger'
5
6
  require 'lotus/rendering_policy'
6
7
  require 'lotus/middleware'
7
8
 
@@ -105,7 +106,8 @@ module Lotus
105
106
  # @return [Lotus::Application] a new instance of the application
106
107
  #
107
108
  # @since 0.1.0
108
- def initialize
109
+ def initialize(options = {})
110
+ self.class.configuration.path_prefix options[:path_prefix]
109
111
  self.class.load!(self)
110
112
  end
111
113
 
data/lib/lotus/cli.rb CHANGED
@@ -61,14 +61,14 @@ module Lotus
61
61
  end
62
62
 
63
63
  desc 'new', 'generates a new application'
64
- method_option :database, aliases: '-d', desc: 'application database', type: :string, default: 'filesystem'
65
- method_option :architecture, aliases: '-a', desc: 'application architecture', type: :string, default: 'container'
66
- method_option :application, desc: 'application name', type: :string, default: 'web'
67
- method_option :application_base_url, desc: 'application base url', type: :string, default: '/'
68
- method_option :path, desc: 'path', type: :string
69
- method_option :test, desc: 'application test framework (rspec/minitest)', type: :string, default: 'minitest'
70
- method_option :lotus_head, desc: 'use Lotus HEAD', type: :boolean, default: false
71
- method_option :help, aliases: '-h', desc: 'displays the usage method'
64
+ method_option :database, aliases: '-d', desc: 'application database', type: :string, default: 'filesystem'
65
+ method_option :architecture, aliases: ['-a', '--arch'], desc: 'application architecture', type: :string, default: 'container'
66
+ method_option :application, desc: 'application name', type: :string, default: 'web'
67
+ method_option :application_base_url, desc: 'application base url', type: :string, default: '/'
68
+ method_option :path, desc: 'path', type: :string
69
+ method_option :test, desc: 'application test framework (rspec/minitest)', type: :string, default: 'minitest'
70
+ method_option :lotus_head, desc: 'use Lotus HEAD', type: :boolean, default: false
71
+ method_option :help, aliases: '-h', desc: 'displays the usage method'
72
72
 
73
73
  def new(name = nil)
74
74
  if options[:help] || name.nil?
@@ -79,11 +79,12 @@ module Lotus
79
79
  end
80
80
  end
81
81
 
82
- desc 'generate', 'generates a new action'
83
- method_option :application_base_url, desc: 'application base url', type: :string
84
- method_option :path, desc: 'applications path', type: :string, default: 'apps'
85
- method_option :skip_view, desc: 'skip the creation of view and templates (only for action)', type: :boolean, default: false
86
- method_option :help, aliases: '-h', desc: 'displays the usage method'
82
+ desc 'generate', 'generates action, model or migration'
83
+ method_option :application_base_url, desc: 'application base url', type: :string
84
+ method_option :path, desc: 'applications path', type: :string
85
+ method_option :url, desc: 'relative URL for action', type: :string
86
+ method_option :skip_view, desc: 'skip the creation of view and templates (only for action)', type: :boolean, default: false
87
+ method_option :help, aliases: '-h', desc: 'displays the usage method'
87
88
 
88
89
  # @since 0.3.0
89
90
  # @api private
@@ -24,7 +24,7 @@ module Lotus
24
24
  def start
25
25
  # Clear out ARGV so Pry/IRB don't attempt to parse the rest
26
26
  ARGV.shift until ARGV.empty?
27
- require @environment.env_config.to_s
27
+ @environment.require_application_environment
28
28
 
29
29
  # Add convenience methods to the main:Object binding
30
30
  TOPLEVEL_BINDING.eval('self').send(:include, Methods)
@@ -17,11 +17,113 @@ module Lotus
17
17
  end
18
18
  end
19
19
 
20
+ desc 'db create', 'create database'
21
+
22
+ desc 'create', 'create database for current environment'
23
+ method_option :environment, desc: 'path to environment configuration (config/environment.rb)'
24
+
25
+ def create
26
+ if options[:help]
27
+ invoke :help, ['create']
28
+ else
29
+ assert_allowed_environment!
30
+ require 'lotus/commands/db/create'
31
+ Lotus::Commands::DB::Create.new(environment).start
32
+ end
33
+ end
34
+
35
+ desc 'db drop', 'drop database'
36
+
37
+ desc 'drop', 'drop database for current environment'
38
+ method_option :environment, desc: 'path to environment configuration (config/environment.rb)'
39
+
40
+ def drop
41
+ if options[:help]
42
+ invoke :help, ['drop']
43
+ else
44
+ assert_allowed_environment!
45
+ require 'lotus/commands/db/drop'
46
+ Lotus::Commands::DB::Drop.new(environment).start
47
+ end
48
+ end
49
+
50
+ desc 'db migrate', 'migrate database'
51
+
52
+ desc 'migrate', 'migrate database for current environment'
53
+ method_option :environment, desc: 'path to environment configuration (config/environment.rb)'
54
+
55
+ def migrate(version = nil)
56
+ if options[:help]
57
+ invoke :help, ['migrate']
58
+ else
59
+ require 'lotus/commands/db/migrate'
60
+ Lotus::Commands::DB::Migrate.new(environment, version).start
61
+ end
62
+ end
63
+
64
+ desc 'db apply', 'apply database changes'
65
+
66
+ desc 'apply', 'migrate, dump schema, delete migrations (experimental)'
67
+ method_option :environment, desc: 'path to environment configuration (config/environment.rb)'
68
+
69
+ def apply
70
+ if options[:help]
71
+ invoke :help, ['apply']
72
+ else
73
+ assert_development_environment!
74
+ require 'lotus/commands/db/apply'
75
+ Lotus::Commands::DB::Apply.new(environment).start
76
+ end
77
+ end
78
+
79
+ desc 'db prepare', 'prepare database'
80
+
81
+ desc 'prepare', 'create and migrate database'
82
+ method_option :environment, desc: 'path to environment configuration (config/environment.rb)'
83
+
84
+ def prepare
85
+ if options[:help]
86
+ invoke :help, ['prepare']
87
+ else
88
+ assert_allowed_environment!
89
+ require 'lotus/commands/db/prepare'
90
+ Lotus::Commands::DB::Prepare.new(environment).start
91
+ end
92
+ end
93
+
94
+ desc 'db version', 'database version'
95
+
96
+ desc 'version', 'current database version'
97
+ method_option :environment, desc: 'path to environment configuration (config/environment.rb)'
98
+
99
+ def version
100
+ if options[:help]
101
+ invoke :help, ['version']
102
+ else
103
+ require 'lotus/commands/db/version'
104
+ Lotus::Commands::DB::Version.new(environment).start
105
+ end
106
+ end
107
+
20
108
  private
21
109
 
22
110
  def environment
23
111
  Lotus::Environment.new(options)
24
112
  end
113
+
114
+ def assert_allowed_environment!
115
+ if environment.environment?(:production)
116
+ puts "Can't run this command in production mode"
117
+ exit 1
118
+ end
119
+ end
120
+
121
+ def assert_development_environment!
122
+ unless environment.environment?(:development)
123
+ puts "This command can be ran only in development mode"
124
+ exit 1
125
+ end
126
+ end
25
127
  end
26
128
  end
27
129
  end
@@ -0,0 +1,15 @@
1
+ module Lotus
2
+ module Commands
3
+ class DB
4
+ class Abstract
5
+ def initialize(environment)
6
+ environment.require_application_environment
7
+ end
8
+
9
+ def start
10
+ raise NotImplementedError
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ require 'lotus/commands/db/abstract'
2
+
3
+ module Lotus
4
+ module Commands
5
+ class DB
6
+ class Apply < Abstract
7
+ def start
8
+ require 'lotus/model/migrator'
9
+ Lotus::Model::Migrator.apply
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -10,7 +10,7 @@ module Lotus
10
10
  @name = name
11
11
  @environment = environment
12
12
  @env_options = environment.to_options
13
- load_config
13
+ @environment.require_application_environment
14
14
  end
15
15
 
16
16
  def start
@@ -44,10 +44,6 @@ module Lotus
44
44
  def connection_string
45
45
  adapter_class.new(mapper, adapter_config.uri).connection_string
46
46
  end
47
-
48
- def load_config
49
- require @env_options[:env_config]
50
- end
51
47
  end
52
48
  end
53
49
  end
@@ -0,0 +1,14 @@
1
+ require 'lotus/commands/db/abstract'
2
+
3
+ module Lotus
4
+ module Commands
5
+ class DB
6
+ class Create < Abstract
7
+ def start
8
+ require 'lotus/model/migrator'
9
+ Lotus::Model::Migrator.create
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ require 'lotus/commands/db/abstract'
2
+
3
+ module Lotus
4
+ module Commands
5
+ class DB
6
+ class Drop < Abstract
7
+ def start
8
+ require 'lotus/model/migrator'
9
+ Lotus::Model::Migrator.drop
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ require 'lotus/commands/db/abstract'
2
+
3
+ module Lotus
4
+ module Commands
5
+ class DB
6
+ class Migrate < Abstract
7
+ def initialize(environment, version)
8
+ super(environment)
9
+ @version = version
10
+ end
11
+
12
+ def start
13
+ require 'lotus/model/migrator'
14
+ Lotus::Model::Migrator.migrate(version: @version)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end