eucalypt 0.1.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.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +26 -0
- data/Rakefile +2 -0
- data/bin/eucalypt +3 -0
- data/eucalypt.gemspec +39 -0
- data/lib/eucalypt/app.rb +6 -0
- data/lib/eucalypt/controller.rb +10 -0
- data/lib/eucalypt/errors.rb +109 -0
- data/lib/eucalypt/eucalypt-blog/helpers.rb +106 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/__base__.rb +22 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/__require__.rb +1 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/cli/blog.rb +65 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/generators/article.rb +28 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/generators/controller.rb +14 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/generators/helper.rb +12 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/generators/list.rb +74 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/generators/views.rb +20 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/templates/controller/controller.tt +33 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/templates/controller/controller_spec.tt +43 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/templates/helper/helper.tt +5 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/templates/helper/helper_spec.tt +9 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/templates/views/article.erb +1 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/templates/views/article_layout.erb +10 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/templates/views/article_md.tt +9 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/templates/views/articles.erb +1 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/templates/views/articles_layout.erb +10 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/templates/views/search.erb +1 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog-article/cli/article.rb +120 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog-article-edit/cli/edit-datetime.rb +113 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog-article-edit/cli/edit-urltitle.rb +75 -0
- data/lib/eucalypt/eucalypt-core/cli/__base__.rb +11 -0
- data/lib/eucalypt/eucalypt-core/cli/console.rb +15 -0
- data/lib/eucalypt/eucalypt-core/cli/core.rb +6 -0
- data/lib/eucalypt/eucalypt-core/cli/help.rb +11 -0
- data/lib/eucalypt/eucalypt-core/cli/init.rb +71 -0
- data/lib/eucalypt/eucalypt-core/cli/launch.rb +33 -0
- data/lib/eucalypt/eucalypt-core/cli/test.rb +16 -0
- data/lib/eucalypt/eucalypt-core/cli/version.rb +11 -0
- data/lib/eucalypt/eucalypt-core/templates/Gemfile.tt +35 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/.gitignore +48 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/.travis.yml +8 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/Procfile +1 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/Rakefile +7 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/assets/fonts/.empty_directory +0 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/assets/images/.empty_directory +0 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/assets/scripts/application.js +17 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/assets/stylesheets/__partials__.scss +16 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/assets/stylesheets/application.scss +17 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/assets/stylesheets/partials/_mixins.scss +54 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/controllers/application_controller.rb +7 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/helpers/application_helper.rb +3 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/models/.empty_directory +0 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/static/.empty_directory +0 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/static/readme.yml +34 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/views/index.erb +0 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/views/layouts/main.erb +9 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/views/partials/.empty_directory +0 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app.rb +42 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/config/active_record.rb +6 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/config/asset_pipeline.rb +15 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/config/database.yml +16 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/config/initializers/.empty_directory +0 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/config/logging.rb +27 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/config/manifest.rb +15 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/config.ru +10 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/log/.empty_directory +0 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/spec/controllers/application_controller_spec.rb +9 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/spec/helpers/application_helper_spec.rb +9 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/spec/models/.empty_directory +0 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/spec/spec_helper.rb +18 -0
- data/lib/eucalypt/eucalypt-destroy/helpers.rb +77 -0
- data/lib/eucalypt/eucalypt-destroy/namespaces/destroy/cli/destroy-controller.rb +16 -0
- data/lib/eucalypt/eucalypt-destroy/namespaces/destroy/cli/destroy-helper.rb +16 -0
- data/lib/eucalypt/eucalypt-destroy/namespaces/destroy/cli/destroy-model.rb +16 -0
- data/lib/eucalypt/eucalypt-destroy/namespaces/destroy/cli/destroy-scaffold.rb +63 -0
- data/lib/eucalypt/eucalypt-destroy/namespaces/destroy/cli/destroy.rb +21 -0
- data/lib/eucalypt/eucalypt-generate/.gitkeep +0 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate/cli/generate-scaffold.rb +62 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate/cli/generate.rb +24 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-controller/cli/generate-controller.rb +29 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-controller/generators/controller.rb +45 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-controller/templates/controller/controller.tt +3 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-controller/templates/controller/policy_rest_controller.tt +71 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-controller/templates/controller/rest_controller.tt +28 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-controller/templates/controller_spec.tt +9 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-helper/cli/generate-helper.rb +23 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-helper/generators/helper.rb +24 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-helper/templates/helper.tt +3 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-helper/templates/helper_spec.tt +9 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-model/cli/generate-model.rb +26 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-model/generators/model.rb +25 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-model/templates/model.tt +3 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-model/templates/model_spec.tt +8 -0
- data/lib/eucalypt/eucalypt-migration/helpers.rb +93 -0
- data/lib/eucalypt/eucalypt-migration/migration_base.tt +4 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration/cli/migration.rb +39 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-add/cli/add-column.rb +25 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-add/cli/add-index.rb +25 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-add/cli/add.rb +18 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-add/generators/column.rb +46 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-add/generators/index.rb +52 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-blank/cli/blank.rb +22 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-blank/generators/blank.rb +28 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-change/cli/change-column.rb +23 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-change/cli/change.rb +17 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-change/generators/column.rb +46 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-create/cli/create-table.rb +25 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-create/cli/create.rb +17 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-create/generators/table.rb +53 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-drop/cli/drop-column.rb +22 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-drop/cli/drop-index.rb +23 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-drop/cli/drop-table.rb +22 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-drop/cli/drop.rb +19 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-drop/generators/column.rb +38 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-drop/generators/index.rb +48 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-drop/generators/table.rb +37 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-rename/cli/rename-column.rb +22 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-rename/cli/rename-index.rb +22 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-rename/cli/rename-table.rb +24 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-rename/cli/rename.rb +19 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-rename/generators/column.rb +39 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-rename/generators/index.rb +39 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-rename/generators/table.rb +38 -0
- data/lib/eucalypt/eucalypt-security/helpers.rb +22 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security/cli/security.rb +31 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-policy/cli/security-policy.rb +91 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-policy/generators/policy.rb +31 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-policy/templates/create_policy_roles_migration.tt +11 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-policy/templates/policy.tt +16 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-policy-permission/cli/security-policy-permission.rb +62 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-policy-permission/generators/policy-permission.rb +28 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-policy-permission/templates/add_permission_to_policy_migration.tt +5 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-policy-role/cli/security-policy-role.rb +66 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-pundit/cli/security-pundit.rb +79 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-pundit/generators/role.rb +24 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-pundit/templates/create_roles_migration.tt +7 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-pundit/templates/pundit.tt +4 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-warden/cli/security-warden.rb +61 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-warden/generators/auth_controller.rb +34 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-warden/generators/user.rb +37 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-warden/templates/auth_controller.tt +25 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-warden/templates/auth_login.tt +1 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-warden/templates/create_users_table_migration.tt +9 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-warden/templates/user.tt +16 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-warden/templates/warden.tt +35 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-warden/user_confirm.rb +38 -0
- data/lib/eucalypt/helpers/colorize.rb +27 -0
- data/lib/eucalypt/helpers/gemfile.rb +48 -0
- data/lib/eucalypt/helpers/inflect.rb +79 -0
- data/lib/eucalypt/helpers/messages.rb +31 -0
- data/lib/eucalypt/helpers/migration.rb +85 -0
- data/lib/eucalypt/helpers/numeric.rb +10 -0
- data/lib/eucalypt/helpers.rb +6 -0
- data/lib/eucalypt/list.rb +39 -0
- data/lib/eucalypt/static.rb +48 -0
- data/lib/eucalypt/version.rb +3 -0
- data/lib/eucalypt.rb +19 -0
- metadata +373 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'active_support'
|
|
2
|
+
require 'active_support/core_ext'
|
|
3
|
+
require 'thor'
|
|
4
|
+
require 'eucalypt/helpers'
|
|
5
|
+
require 'eucalypt/eucalypt-generate/namespaces/generate/cli/generate'
|
|
6
|
+
|
|
7
|
+
module Eucalypt
|
|
8
|
+
class Security < Thor
|
|
9
|
+
module Helpers
|
|
10
|
+
include Eucalypt::Helpers
|
|
11
|
+
include Eucalypt::Helpers::Messages
|
|
12
|
+
using Colorize
|
|
13
|
+
|
|
14
|
+
def create_config_file(type, directory)
|
|
15
|
+
config_relative = File.join 'config', "#{type}.rb"
|
|
16
|
+
config_file = File.join(directory, config_relative)
|
|
17
|
+
Out.warning "#{type.to_s.capitalize} config file #{config_relative.colorize(:bold)} already exists." if File.file? config_file
|
|
18
|
+
template "#{type}.tt", config_file
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require 'eucalypt/errors'
|
|
2
|
+
require 'eucalypt/eucalypt-security/namespaces/security-warden/cli/security-warden'
|
|
3
|
+
require 'eucalypt/eucalypt-security/namespaces/security-pundit/cli/security-pundit'
|
|
4
|
+
require 'eucalypt/eucalypt-security/namespaces/security-policy/cli/security-policy'
|
|
5
|
+
require 'eucalypt/helpers'
|
|
6
|
+
|
|
7
|
+
module Eucalypt
|
|
8
|
+
class Security < Thor
|
|
9
|
+
include Thor::Actions
|
|
10
|
+
include Eucalypt::Helpers
|
|
11
|
+
using Colorize
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
require 'eucalypt/list'
|
|
15
|
+
include Eucalypt::List
|
|
16
|
+
def banner(task, namespace = false, subcommand = true)
|
|
17
|
+
basename + ' ' + task.formatted_usage(self, true, subcommand).split(':').join(' ')
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
register(Eucalypt::SecurityWarden, 'warden', 'warden [COMMAND]', 'Configure Warden authentication'.colorize(:grey))
|
|
22
|
+
register(Eucalypt::SecurityPundit, 'pundit', 'pundit [COMMAND]', 'Configure Pundit authorization'.colorize(:grey))
|
|
23
|
+
register(Eucalypt::SecurityPolicy, 'policy', 'policy [COMMAND]', 'Pundit policy commands'.colorize(:grey))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class CLI < Thor
|
|
27
|
+
include Eucalypt::Helpers
|
|
28
|
+
using Colorize
|
|
29
|
+
register(Security, 'security', 'security [COMMAND]', 'Manage authentication and authorization'.colorize(:grey))
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
require 'thor'
|
|
2
|
+
require 'eucalypt/errors'
|
|
3
|
+
require 'eucalypt/helpers'
|
|
4
|
+
require 'eucalypt/eucalypt-security/namespaces/security-policy/generators/policy'
|
|
5
|
+
require 'eucalypt/eucalypt-security/namespaces/security-policy-permission/cli/security-policy-permission'
|
|
6
|
+
require 'eucalypt/eucalypt-security/namespaces/security-policy-role/cli/security-policy-role'
|
|
7
|
+
|
|
8
|
+
module Eucalypt
|
|
9
|
+
class SecurityPolicy < Thor
|
|
10
|
+
include Thor::Actions
|
|
11
|
+
include Eucalypt::Helpers
|
|
12
|
+
using Colorize
|
|
13
|
+
|
|
14
|
+
method_option :permissions, type: :array, aliases: '-p', default: [], desc: "Permissions to generate along with the policy"
|
|
15
|
+
desc "generate [NAME]", "Create a new Pundit policy".colorize(:grey)
|
|
16
|
+
def generate(name)
|
|
17
|
+
directory = File.expand_path('.')
|
|
18
|
+
if Eucalypt.app? directory
|
|
19
|
+
# Check for authorization gems
|
|
20
|
+
return unless Gemfile.check(%w[pundit], 'eucalypt security pundit setup', directory)
|
|
21
|
+
|
|
22
|
+
# Check for user model
|
|
23
|
+
unless File.exist? File.join(directory, 'app', 'models', 'user.rb')
|
|
24
|
+
Eucalypt::Error.no_user_model
|
|
25
|
+
return
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Check for role model
|
|
29
|
+
unless File.exist? File.join(directory, 'app', 'models', 'role.rb')
|
|
30
|
+
Eucalypt::Error.no_role_model
|
|
31
|
+
return
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
policy = Inflect.new(:policy, name)
|
|
35
|
+
|
|
36
|
+
policy_generator = Eucalypt::Generators::Policy.new
|
|
37
|
+
policy_generator.destination_root = directory
|
|
38
|
+
|
|
39
|
+
# Generate policy file
|
|
40
|
+
policy_generator.generate(name: name)
|
|
41
|
+
|
|
42
|
+
# Create policy roles table
|
|
43
|
+
policy_generator.generate_policy_roles_migration(policy: policy.resource)
|
|
44
|
+
|
|
45
|
+
# Create policy role model
|
|
46
|
+
Dir.chdir(directory) do
|
|
47
|
+
Eucalypt::CLI.start(['generate', 'model', "#{policy.resource}_role", '--no-spec', '--no-table'])
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Add validation to role model
|
|
51
|
+
role_model_file = File.join directory, 'app', 'models', "#{policy.resource}_role.rb"
|
|
52
|
+
File.open(role_model_file) do |f|
|
|
53
|
+
insert = " validates :permission, uniqueness: true"
|
|
54
|
+
inject_into_class(role_model_file, "#{policy.resource}_role".camelize, "#{insert}\n") unless f.read.include? insert
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Add policy column to user roles table
|
|
58
|
+
Dir.chdir(directory) do
|
|
59
|
+
args = %w[migration add column]
|
|
60
|
+
args << 'roles'
|
|
61
|
+
args << policy.resource
|
|
62
|
+
args << 'string'
|
|
63
|
+
args << %w[-o default:default]
|
|
64
|
+
args.flatten!
|
|
65
|
+
Eucalypt::CLI.start(args)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Generate permissions
|
|
69
|
+
options[:permissions].each do |permission|
|
|
70
|
+
Eucalypt::CLI.start(['security', 'policy', 'permission', 'generate', policy.resource, permission])
|
|
71
|
+
end
|
|
72
|
+
else
|
|
73
|
+
Eucalypt::Error.wrong_directory
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
#def destroy()
|
|
78
|
+
#end
|
|
79
|
+
|
|
80
|
+
class << self
|
|
81
|
+
require 'eucalypt/list'
|
|
82
|
+
include Eucalypt::List
|
|
83
|
+
def banner(task, namespace = false, subcommand = true)
|
|
84
|
+
"#{basename} security #{task.formatted_usage(self, true, subcommand).split(':').join(' ')}"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
register(Eucalypt::SecurityPolicyPermission, 'permission', 'permission [COMMAND]', 'Pundit policy permission commands'.colorize(:grey))
|
|
89
|
+
register(Eucalypt::SecurityPolicyRole, 'role', 'role [COMMAND]', 'Pundit policy role commands'.colorize(:grey))
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require 'active_support'
|
|
2
|
+
require 'active_support/core_ext'
|
|
3
|
+
require 'thor'
|
|
4
|
+
require 'eucalypt/helpers'
|
|
5
|
+
|
|
6
|
+
module Eucalypt
|
|
7
|
+
module Generators
|
|
8
|
+
class Policy < Thor::Group
|
|
9
|
+
include Thor::Actions
|
|
10
|
+
include Eucalypt::Helpers
|
|
11
|
+
|
|
12
|
+
def self.source_root
|
|
13
|
+
File.join File.dirname(__dir__), 'templates'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def generate(name:)
|
|
17
|
+
policy = Inflect.new(:policy, name)
|
|
18
|
+
config = {class_name: policy.class_name, resource: policy.resource, constant: policy.constant}
|
|
19
|
+
template('policy.tt', policy.file_path, config)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def generate_policy_roles_migration(policy:)
|
|
23
|
+
sleep 1
|
|
24
|
+
migration = Eucalypt::Helpers::Migration[title: "create_#{policy}_roles", template: 'create_policy_roles_migration.tt']
|
|
25
|
+
return unless migration.create_anyway? if migration.exists?
|
|
26
|
+
config = {migration_title: migration.title.camelize, policy: policy}
|
|
27
|
+
template migration.template, migration.file_path, config
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class <%= config[:migration_title] %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :<%= config[:policy] %>_roles do |t|
|
|
4
|
+
t.string :permission, null: false
|
|
5
|
+
t.boolean :admin, default: true
|
|
6
|
+
t.boolean :default, default: false
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
add_index :<%= config[:policy] %>_roles, :permission, unique: true
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class <%= config[:class_name] %>
|
|
2
|
+
attr_reader :user, :<%= config[:resource] %>
|
|
3
|
+
|
|
4
|
+
def initialize(user, <%= config[:resource] %>)
|
|
5
|
+
@user = user
|
|
6
|
+
@<%= config[:resource] %> = <%= config[:resource] %>
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
if ActiveRecord::Base.connection.table_exists?(:<%= config[:resource] %>_roles)
|
|
10
|
+
<%= config[:constant] %>Role.pluck(:permission).each do |permission|
|
|
11
|
+
define_method("#{permission}?") do
|
|
12
|
+
<%= config[:constant] %>Role.find_by(permission: permission).send(user.role.<%= config[:resource] %>)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require 'thor'
|
|
2
|
+
require 'eucalypt/errors'
|
|
3
|
+
require 'eucalypt/helpers'
|
|
4
|
+
require 'eucalypt/eucalypt-security/namespaces/security-policy-permission/generators/policy-permission'
|
|
5
|
+
|
|
6
|
+
module Eucalypt
|
|
7
|
+
class SecurityPolicyPermission < Thor
|
|
8
|
+
include Thor::Actions
|
|
9
|
+
include Eucalypt::Helpers
|
|
10
|
+
using Colorize
|
|
11
|
+
|
|
12
|
+
desc "generate [POLICY] [PERMISSION]", "Create a new Pundit policy permission".colorize(:grey)
|
|
13
|
+
def generate(name, permission)
|
|
14
|
+
directory = File.expand_path('.')
|
|
15
|
+
if Eucalypt.app? directory
|
|
16
|
+
# Check for authorization gems
|
|
17
|
+
return unless Gemfile.check(%w[pundit], 'eucalypt security pundit setup', directory)
|
|
18
|
+
|
|
19
|
+
# Check for user model
|
|
20
|
+
unless File.exist? File.join(directory, 'app', 'models', 'user.rb')
|
|
21
|
+
Eucalypt::Error.no_user_model
|
|
22
|
+
return
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Check for role model
|
|
26
|
+
unless File.exist? File.join(directory, 'app', 'models', 'role.rb')
|
|
27
|
+
Eucalypt::Error.no_role_model
|
|
28
|
+
return
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
policy = Inflect.new(:policy, name)
|
|
32
|
+
|
|
33
|
+
# Check for policy file and policy role model
|
|
34
|
+
policy_file = File.join(directory, 'app', 'policies', policy.file_name)
|
|
35
|
+
policy_role_model = File.join(directory, 'app', 'models', "#{policy.resource}_role.rb")
|
|
36
|
+
unless File.exist?(policy_file) && File.exist?(policy_role_model)
|
|
37
|
+
Eucalypt::Error.no_policy(policy.resource)
|
|
38
|
+
return
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
policy_permission = Eucalypt::Generators::PolicyPermission.new
|
|
42
|
+
policy_permission.destination_root = directory
|
|
43
|
+
|
|
44
|
+
# Add permission record to policy role table
|
|
45
|
+
policy_permission.generate(policy_name: policy.resource, permission: Inflect.resource_keep_inflection(permission))
|
|
46
|
+
else
|
|
47
|
+
Eucalypt::Error.wrong_directory
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# def destroy()
|
|
52
|
+
# end
|
|
53
|
+
|
|
54
|
+
class << self
|
|
55
|
+
require 'eucalypt/list'
|
|
56
|
+
include Eucalypt::List
|
|
57
|
+
def banner(task, namespace = false, subcommand = true)
|
|
58
|
+
"#{basename} security policy #{task.formatted_usage(self, true, subcommand).split(':').join(' ')}"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'active_support'
|
|
2
|
+
require 'active_support/core_ext'
|
|
3
|
+
require 'thor'
|
|
4
|
+
require 'eucalypt/helpers'
|
|
5
|
+
|
|
6
|
+
module Eucalypt
|
|
7
|
+
module Generators
|
|
8
|
+
class PolicyPermission < Thor::Group
|
|
9
|
+
include Thor::Actions
|
|
10
|
+
include Eucalypt::Helpers
|
|
11
|
+
|
|
12
|
+
def self.source_root
|
|
13
|
+
File.join File.dirname(__dir__), 'templates'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def generate(policy_name:, permission:)
|
|
17
|
+
sleep 1
|
|
18
|
+
migration = Eucalypt::Helpers::Migration[
|
|
19
|
+
title: "add_#{permission}_permission_to_#{policy_name}_policy",
|
|
20
|
+
template: 'add_permission_to_policy_migration.tt'
|
|
21
|
+
]
|
|
22
|
+
return unless migration.create_anyway? if migration.exists?
|
|
23
|
+
config = {migration_title: migration.title.camelize, policy_name: policy_name, permission: permission}
|
|
24
|
+
template migration.template, migration.file_path, config
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/eucalypt/eucalypt-security/namespaces/security-policy-role/cli/security-policy-role.rb
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require 'thor'
|
|
2
|
+
require 'eucalypt/errors'
|
|
3
|
+
require 'eucalypt/helpers'
|
|
4
|
+
|
|
5
|
+
module Eucalypt
|
|
6
|
+
class SecurityPolicyRole < Thor
|
|
7
|
+
include Thor::Actions
|
|
8
|
+
include Eucalypt::Helpers
|
|
9
|
+
using Colorize
|
|
10
|
+
|
|
11
|
+
desc "generate [POLICY] [ROLE]", "Create a new Pundit policy role".colorize(:grey)
|
|
12
|
+
def generate(name, role)
|
|
13
|
+
directory = File.expand_path('.')
|
|
14
|
+
if Eucalypt.app? directory
|
|
15
|
+
# Check for authorization gems
|
|
16
|
+
return unless Gemfile.check(%w[pundit], 'eucalypt security pundit setup', directory)
|
|
17
|
+
|
|
18
|
+
# Check for user model
|
|
19
|
+
unless File.exist? File.join(directory, 'app', 'models', 'user.rb')
|
|
20
|
+
Eucalypt::Error.no_user_model
|
|
21
|
+
return
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Check for role model
|
|
25
|
+
unless File.exist? File.join(directory, 'app', 'models', 'role.rb')
|
|
26
|
+
Eucalypt::Error.no_role_model
|
|
27
|
+
return
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
policy = Inflect.new(:policy, name)
|
|
31
|
+
|
|
32
|
+
# Check for policy file and policy role model
|
|
33
|
+
policy_file = File.join(directory, 'app', 'policies', policy.file_name)
|
|
34
|
+
policy_role_model = File.join(directory, 'app', 'models', "#{policy.resource}_role.rb")
|
|
35
|
+
unless File.exist?(policy_file) && File.exist?(policy_role_model)
|
|
36
|
+
Eucalypt::Error.no_policy(policy.resource)
|
|
37
|
+
return
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Add role column to policy roles table
|
|
41
|
+
Dir.chdir(directory) do
|
|
42
|
+
args = %w[migration add column]
|
|
43
|
+
args << "#{policy.resource}_roles"
|
|
44
|
+
args << Inflect.resource(role)
|
|
45
|
+
args << 'boolean'
|
|
46
|
+
args << %w[-o default:false]
|
|
47
|
+
args.flatten!
|
|
48
|
+
Eucalypt::CLI.start(args)
|
|
49
|
+
end
|
|
50
|
+
else
|
|
51
|
+
Eucalypt::Error.wrong_directory
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# def destroy()
|
|
56
|
+
# end
|
|
57
|
+
|
|
58
|
+
class << self
|
|
59
|
+
require 'eucalypt/list'
|
|
60
|
+
include Eucalypt::List
|
|
61
|
+
def banner(task, namespace = false, subcommand = true)
|
|
62
|
+
"#{basename} security policy #{task.formatted_usage(self, true, subcommand).split(':').join(' ')}"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
require 'thor'
|
|
2
|
+
require 'eucalypt/helpers'
|
|
3
|
+
require 'eucalypt/eucalypt-security/helpers'
|
|
4
|
+
require 'eucalypt/eucalypt-security/namespaces/security-pundit/generators/role'
|
|
5
|
+
|
|
6
|
+
module Eucalypt
|
|
7
|
+
class SecurityPundit < Thor
|
|
8
|
+
include Thor::Actions
|
|
9
|
+
include Eucalypt::Helpers
|
|
10
|
+
include Eucalypt::Helpers::Messages
|
|
11
|
+
include Eucalypt::Helpers::Gemfile
|
|
12
|
+
include Eucalypt::Security::Helpers
|
|
13
|
+
using Colorize
|
|
14
|
+
|
|
15
|
+
def self.source_root
|
|
16
|
+
File.join File.dirname(__dir__), 'templates'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
desc "setup", "Set up Pundit authorization".colorize(:grey)
|
|
20
|
+
def setup
|
|
21
|
+
directory = File.expand_path('.')
|
|
22
|
+
if Eucalypt.app? directory
|
|
23
|
+
# Check if user model exists
|
|
24
|
+
user_model_file = File.join(directory, 'app', 'models', 'user.rb')
|
|
25
|
+
unless File.file? user_model_file
|
|
26
|
+
Eucalypt::Error.no_user_model
|
|
27
|
+
return
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
Out.setup "Setting up Pundit authorization..."
|
|
31
|
+
|
|
32
|
+
# Add Pundit to Gemfile
|
|
33
|
+
gemfile_add('Authorization', {pundit: '~> 2.0'}, directory)
|
|
34
|
+
|
|
35
|
+
# Create Pundit config file
|
|
36
|
+
create_config_file(:pundit, directory)
|
|
37
|
+
|
|
38
|
+
# Create roles migration
|
|
39
|
+
Eucalypt::Generators::Role.new.generate_roles_migration
|
|
40
|
+
|
|
41
|
+
# Create Role model
|
|
42
|
+
role_model_file = File.join(directory, 'app', 'models', 'role.rb')
|
|
43
|
+
Out.warning "Role model already exists." if File.file? role_model_file
|
|
44
|
+
Dir.chdir(directory) do
|
|
45
|
+
Eucalypt::CLI.start(%w[generate model role --no-spec --no-table])
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Add belongs_to to Role model
|
|
49
|
+
File.open(role_model_file) do |f|
|
|
50
|
+
insert = " belongs_to :user"
|
|
51
|
+
inject_into_class(role_model_file, 'Role', "#{insert}\n") unless f.read.include? insert
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Add relationship to User model
|
|
55
|
+
File.open(user_model_file) do |f|
|
|
56
|
+
contents = f.read
|
|
57
|
+
insert = " has_one :role, dependent: :destroy\n"
|
|
58
|
+
inject_into_file(user_model_file, insert, before: / include BCrypt/) unless contents.include? insert
|
|
59
|
+
insert = " after_save :create_role\n\n"
|
|
60
|
+
inject_into_file(user_model_file, insert, before: / include BCrypt/) unless contents.include? insert
|
|
61
|
+
insert = "\n private\n\n def create_role\n self.role = Role.new\n end\n"
|
|
62
|
+
inject_into_file(user_model_file, insert, before: /^end/) unless contents.include? insert
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
Out.info "Ensure you run `#{'rake db:migrate'.colorize(:bold)}` to create the necessary tables for Pundit."
|
|
66
|
+
else
|
|
67
|
+
Eucalypt::Error.wrong_directory
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
class << self
|
|
72
|
+
require 'eucalypt/list'
|
|
73
|
+
include Eucalypt::List
|
|
74
|
+
def banner(task, namespace = false, subcommand = true)
|
|
75
|
+
"#{basename} security #{task.formatted_usage(self, true, subcommand).split(':').join(' ')}"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'active_support'
|
|
2
|
+
require 'active_support/core_ext'
|
|
3
|
+
require 'thor'
|
|
4
|
+
require 'eucalypt/helpers'
|
|
5
|
+
|
|
6
|
+
module Eucalypt
|
|
7
|
+
module Generators
|
|
8
|
+
class Role < Thor::Group
|
|
9
|
+
include Thor::Actions
|
|
10
|
+
include Eucalypt::Helpers
|
|
11
|
+
|
|
12
|
+
def self.source_root
|
|
13
|
+
File.join File.dirname(__dir__), 'templates'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def generate_roles_migration
|
|
17
|
+
sleep 1
|
|
18
|
+
migration = Eucalypt::Helpers::Migration[title: "create_roles", template: 'create_roles_migration.tt']
|
|
19
|
+
return unless migration.create_anyway? if migration.exists?
|
|
20
|
+
template migration.template, migration.file_path
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require 'thor'
|
|
2
|
+
require 'eucalypt/helpers'
|
|
3
|
+
require 'eucalypt/eucalypt-security/helpers'
|
|
4
|
+
require 'eucalypt/eucalypt-security/namespaces/security-warden/generators/user'
|
|
5
|
+
require 'eucalypt/eucalypt-security/namespaces/security-warden/generators/auth_controller'
|
|
6
|
+
|
|
7
|
+
module Eucalypt
|
|
8
|
+
class SecurityWarden < Thor
|
|
9
|
+
include Thor::Actions
|
|
10
|
+
include Eucalypt::Helpers
|
|
11
|
+
include Eucalypt::Helpers::Messages
|
|
12
|
+
include Eucalypt::Helpers::Gemfile
|
|
13
|
+
include Eucalypt::Security::Helpers
|
|
14
|
+
using Colorize
|
|
15
|
+
|
|
16
|
+
def self.source_root
|
|
17
|
+
File.join File.dirname(__dir__), 'templates'
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
option :controller, type: :boolean, default: true, desc: "Include an authentication controller"
|
|
21
|
+
desc "setup", "Set up Warden authentication".colorize(:grey)
|
|
22
|
+
def setup
|
|
23
|
+
directory = File.expand_path('.')
|
|
24
|
+
if Eucalypt.app? directory
|
|
25
|
+
Out.setup "Setting up Warden authentication..."
|
|
26
|
+
|
|
27
|
+
# Add Warden and BCrypt to Gemfile
|
|
28
|
+
gemfile_add('Authentication and encryption', {warden: '~> 1.2', bcrypt: '~> 3.1'}, directory)
|
|
29
|
+
|
|
30
|
+
# Create Warden config file
|
|
31
|
+
create_config_file(:warden, directory)
|
|
32
|
+
|
|
33
|
+
user = Eucalypt::Generators::User.new
|
|
34
|
+
|
|
35
|
+
# Create user migration
|
|
36
|
+
user.generate_migration
|
|
37
|
+
|
|
38
|
+
# Create user model
|
|
39
|
+
user.generate_model(directory)
|
|
40
|
+
|
|
41
|
+
if options[:controller]
|
|
42
|
+
auth_controller = Eucalypt::Generators::AuthController.new
|
|
43
|
+
auth_controller.destination_root = directory
|
|
44
|
+
auth_controller.generate
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
Out.info "Ensure you run `#{'rake db:migrate'.colorize(:bold)}` to create the necessary tables for Warden."
|
|
48
|
+
else
|
|
49
|
+
Eucalypt::Error.wrong_directory
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
class << self
|
|
54
|
+
require 'eucalypt/list'
|
|
55
|
+
include Eucalypt::List
|
|
56
|
+
def banner(task, namespace = false, subcommand = true)
|
|
57
|
+
"#{basename} security #{task.formatted_usage(self, true, subcommand).split(':').join(' ')}"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'active_support'
|
|
2
|
+
require 'active_support/core_ext'
|
|
3
|
+
require 'thor'
|
|
4
|
+
require 'eucalypt/helpers'
|
|
5
|
+
|
|
6
|
+
module Eucalypt
|
|
7
|
+
module Generators
|
|
8
|
+
class AuthController < Thor::Group
|
|
9
|
+
include Thor::Actions
|
|
10
|
+
include Eucalypt::Helpers
|
|
11
|
+
using String::Builder
|
|
12
|
+
|
|
13
|
+
def self.source_root
|
|
14
|
+
File.join File.dirname(__dir__), 'templates'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def generate
|
|
18
|
+
template 'auth_controller.tt', File.join('app', 'controllers', 'authentication_controller.rb')
|
|
19
|
+
|
|
20
|
+
config = {}
|
|
21
|
+
config[:data] = String.build do |s|
|
|
22
|
+
s << "<%=\n"
|
|
23
|
+
s << " form_for :user, '/auth/login' do\n"
|
|
24
|
+
s << " text_field :username\n"
|
|
25
|
+
s << " password_field :password\n"
|
|
26
|
+
s << " submit 'Login'\n"
|
|
27
|
+
s << " end\n"
|
|
28
|
+
s << "%>"
|
|
29
|
+
end
|
|
30
|
+
template 'auth_login.tt', File.join('app', 'views', 'authentication', 'login.erb'), config
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'active_support'
|
|
2
|
+
require 'active_support/core_ext'
|
|
3
|
+
require 'thor'
|
|
4
|
+
require 'eucalypt/helpers'
|
|
5
|
+
require 'active_record'
|
|
6
|
+
|
|
7
|
+
module Eucalypt
|
|
8
|
+
module Generators
|
|
9
|
+
class User < Thor::Group
|
|
10
|
+
include Thor::Actions
|
|
11
|
+
include Eucalypt::Helpers
|
|
12
|
+
include Eucalypt::Helpers::Messages
|
|
13
|
+
|
|
14
|
+
def self.source_root
|
|
15
|
+
File.join File.dirname(__dir__), 'templates'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def generate_migration
|
|
19
|
+
sleep 1
|
|
20
|
+
migration = Eucalypt::Helpers::Migration[title: 'create_users', template: 'create_users_table_migration.tt']
|
|
21
|
+
return unless migration.create_anyway? if migration.exists?
|
|
22
|
+
template migration.template, migration.file_path
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def generate_model(directory)
|
|
26
|
+
model_file = File.join(directory, 'app', 'models', 'user.rb')
|
|
27
|
+
|
|
28
|
+
if File.file? model_file
|
|
29
|
+
Out.warning 'User model already exists.'
|
|
30
|
+
return
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
template 'user.tt', model_file
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
class AuthenticationController < Eucalypt::Controller(route: '/auth')
|
|
2
|
+
helpers AuthenticationHelper if defined? AuthenticationHelper
|
|
3
|
+
|
|
4
|
+
get '/login' do
|
|
5
|
+
redirect '/' if current_user
|
|
6
|
+
erb :'authentication/login', layout: false
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
post '/login' do
|
|
10
|
+
redirect '/' if current_user
|
|
11
|
+
authenticate
|
|
12
|
+
redirect session[:return_to] || '/'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
get '/logout' do
|
|
16
|
+
env['warden'].logout
|
|
17
|
+
redirect to '/login'
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
post '/invalid' do
|
|
21
|
+
session[:return_to] = env['warden.options'][:attempted_path]
|
|
22
|
+
status 403 # Unauthenticated
|
|
23
|
+
redirect to '/login'
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= config[:data] %>
|