cmor_system 0.0.59.pre → 0.0.60.pre
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 +4 -4
- data/app/controllers/cmor/system/rack/attack/banned_ips_controller.rb +27 -0
- data/app/models/cmor/system/active_collection/base.rb +153 -0
- data/app/models/cmor/system/rack/attack/banned_ip.rb +72 -0
- data/app/views/cmor/system/rack/attack/banned_ips/_form.html.haml +1 -0
- data/app/views/cmor/system/rack/attack/banned_ips/_index_table.html.haml +3 -0
- data/app/views/cmor/system/rack/attack/banned_ips/_index_table_actions.html.haml~ +17 -0
- data/app/views/cmor/system/rack/attack/banned_ips/_show_table.html.haml +3 -0
- data/config/locales/de.yml +8 -0
- data/config/locales/en.yml +19 -0
- data/config/routes.rb +6 -0
- data/lib/cmor/system/configuration.rb +2 -0
- data/lib/generators/cmor/system/install/install_generator.rb +2 -0
- data/lib/generators/cmor/system/install/templates/initializer.rb +12 -2
- data/spec/factories/rack/attack/banned_ips.rb +5 -0
- metadata +28 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 88abc5af2082a34262abc4b0c6037885bb61e8573f5f1367c99dfd48a867d767
|
4
|
+
data.tar.gz: c1a4ca647b591bdd90bbc2a3001dfb79ff23e4f99f0e28246190718ec5bcb43a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d9a328962dab3ed5821e59b6c43a19270df06c224a95721577679b0abd317e1a66ce4270b7182ef3ab82801668eb5b95a49c6347e49d9ae0c11acd6b0c4bd34
|
7
|
+
data.tar.gz: 4baec010a03a40cbfa995f2bd70c49a51d20c1d91c9c2f2ae501091f371d4a90aff5a6ac2f2434e1565d5a8a8b9882b92b65a3974d5b62ec135e4031433d386d
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Cmor
|
2
|
+
module System
|
3
|
+
module Rack
|
4
|
+
module Attack
|
5
|
+
class BannedIpsController < Cmor::Core::Backend::ResourcesController::Base
|
6
|
+
def self.engine_class
|
7
|
+
Cmor::System::Engine
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.resource_class
|
11
|
+
Cmor::System::Rack::Attack::BannedIp
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.available_rest_actions
|
15
|
+
%i(index show new create destroy)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def permitted_params
|
21
|
+
params.require(:rack_attack_banned_ip).permit(:key)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
module Cmor
|
2
|
+
module System
|
3
|
+
module ActiveCollection
|
4
|
+
class Base
|
5
|
+
if Object.const_defined?('Kaminari')
|
6
|
+
class Collection < Kaminari::PaginatableArray
|
7
|
+
end
|
8
|
+
else
|
9
|
+
class Collection < Array
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
extend ActiveModel::Model
|
14
|
+
extend ActiveModel::Naming
|
15
|
+
include ActiveModel::Conversion
|
16
|
+
extend ActiveModel::Translation
|
17
|
+
include ActiveModel::Validations
|
18
|
+
include Markup::Rails::ActiveRecord
|
19
|
+
|
20
|
+
module AttributesConcern
|
21
|
+
extend ActiveSupport::Concern
|
22
|
+
|
23
|
+
included do
|
24
|
+
class_attribute :attribute_names
|
25
|
+
attr_accessor :attributes
|
26
|
+
|
27
|
+
delegate :[], to: :attributes
|
28
|
+
end
|
29
|
+
|
30
|
+
def method_missing(m, *args, &block)
|
31
|
+
if self.attribute_names.map(&:to_s).include?(m.to_s)
|
32
|
+
return @attributes[m.to_s]
|
33
|
+
elsif self.attribute_names.map { |an| "#{an}=" }.include?(m.to_s)
|
34
|
+
return @attributes[m.to_s[0..-2]] = args.first
|
35
|
+
else
|
36
|
+
super
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def initialize(attributes = {})
|
41
|
+
@attributes = {}.with_indifferent_access
|
42
|
+
attributes.each do |k, v|
|
43
|
+
self.send("#{k}=", v)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
include AttributesConcern
|
49
|
+
|
50
|
+
module ActiveRecordLikeConcern
|
51
|
+
extend ActiveSupport::Concern
|
52
|
+
|
53
|
+
class_methods do
|
54
|
+
def reorder(order)
|
55
|
+
key = order.first[0].to_sym
|
56
|
+
value = order.first[1].to_sym
|
57
|
+
sorted = _all.sort { |a, b| a.send(key) <=> b.send(key) }
|
58
|
+
value == :asc ? sorted : sorted.reverse
|
59
|
+
Collection.new(value == 'asc' ? sorted : sorted.reverse)
|
60
|
+
end
|
61
|
+
|
62
|
+
def count
|
63
|
+
all.size
|
64
|
+
end
|
65
|
+
|
66
|
+
def first
|
67
|
+
all.first
|
68
|
+
end
|
69
|
+
|
70
|
+
def last
|
71
|
+
all.last
|
72
|
+
end
|
73
|
+
|
74
|
+
def _all
|
75
|
+
raise "Child class responsiblity"
|
76
|
+
end
|
77
|
+
|
78
|
+
def all
|
79
|
+
Collection.new(_all)
|
80
|
+
end
|
81
|
+
|
82
|
+
def destroy_all
|
83
|
+
all.map(&:destroy)
|
84
|
+
end
|
85
|
+
|
86
|
+
def find(id)
|
87
|
+
all.select { |cl| cl.id == id }.first || raise(ActiveRecord::RecordNotFound)
|
88
|
+
end
|
89
|
+
|
90
|
+
def columns_hash
|
91
|
+
@columns_hash ||= attribute_names.each_with_object({}) { |e, m| m[name] = {} }
|
92
|
+
end
|
93
|
+
|
94
|
+
def create(attributes = {})
|
95
|
+
new(attributes).save
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def ==(other)
|
100
|
+
return false unless other.respond_to?(:attributes)
|
101
|
+
self.attributes == other.attributes
|
102
|
+
end
|
103
|
+
|
104
|
+
def to_key
|
105
|
+
[id]
|
106
|
+
end
|
107
|
+
|
108
|
+
def to_param
|
109
|
+
id
|
110
|
+
end
|
111
|
+
|
112
|
+
def persisted?
|
113
|
+
true
|
114
|
+
end
|
115
|
+
|
116
|
+
def read_attribute(name)
|
117
|
+
@attributes[name.to_sym]
|
118
|
+
end
|
119
|
+
|
120
|
+
def write_attribute(name, value)
|
121
|
+
@attributes[name.to_sym] = value
|
122
|
+
end
|
123
|
+
|
124
|
+
def update(attributes)
|
125
|
+
raise "Child class responsibility"
|
126
|
+
end
|
127
|
+
|
128
|
+
def save
|
129
|
+
raise "Child class responsibility"
|
130
|
+
end
|
131
|
+
|
132
|
+
def save!
|
133
|
+
save
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
include ActiveRecordLikeConcern
|
138
|
+
|
139
|
+
module PaginationConcern
|
140
|
+
extend ActiveSupport::Concern
|
141
|
+
|
142
|
+
class_methods do
|
143
|
+
def page(page)
|
144
|
+
all.page(page)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
include PaginationConcern
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Cmor
|
2
|
+
module System
|
3
|
+
module Rack
|
4
|
+
module Attack
|
5
|
+
class BannedIp < Cmor::System::ActiveCollection::Base
|
6
|
+
self.attribute_names = %w(id key value)
|
7
|
+
|
8
|
+
validates :key, presence: true
|
9
|
+
|
10
|
+
def human
|
11
|
+
key
|
12
|
+
end
|
13
|
+
|
14
|
+
def self._all
|
15
|
+
_keys.collect { |k| new(key: k) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def self._store
|
19
|
+
::Rack::Attack.cache.store
|
20
|
+
end
|
21
|
+
|
22
|
+
def self._keys
|
23
|
+
if _store.is_a?(ActiveSupport::Cache::MemoryStore)
|
24
|
+
_store.instance_variable_get(:@data).keys
|
25
|
+
elsif _store.is_a?(::Rack::Attack::StoreProxy::RedisCacheStoreProxy)
|
26
|
+
_store.redis.keys
|
27
|
+
elsif _store.is_a?(::Rack::Attack::StoreProxy::RedisProxy)
|
28
|
+
_store.keys
|
29
|
+
elsif _store.is_a?(ActiveSupport::Cache::NullStore)
|
30
|
+
[]
|
31
|
+
end.find_all { |k| k.to_s.include?(":ban:") }
|
32
|
+
end
|
33
|
+
|
34
|
+
def key=(value)
|
35
|
+
write_attribute('key', value)
|
36
|
+
if value.nil?
|
37
|
+
write_attribute('id', nil)
|
38
|
+
else
|
39
|
+
write_attribute('id', Digest::SHA1.hexdigest(key))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def destroy
|
44
|
+
self.class._store.delete(key)
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def update(attributes)
|
49
|
+
self.class._store.write(attributes['key'], attributes['value'])
|
50
|
+
if self.key != attributes['key']
|
51
|
+
self.class._store.delete(key)
|
52
|
+
self.key = attributes['key']
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def persisted?
|
57
|
+
key.present? && self.class._keys.include?(key)
|
58
|
+
end
|
59
|
+
|
60
|
+
def save
|
61
|
+
!!self.class._store.write(attributes['key'], attributes['value'])
|
62
|
+
end
|
63
|
+
|
64
|
+
def ip
|
65
|
+
return unless key.respond_to?(:split)
|
66
|
+
key.split(":").last
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
= form.input :key
|
@@ -0,0 +1,17 @@
|
|
1
|
+
= table.column(:actions, title: false) do |resource|
|
2
|
+
- capture_haml do
|
3
|
+
.w-100.d-flex
|
4
|
+
.btn-group.ml-auto
|
5
|
+
- if available_rest_actions.include?(:show)
|
6
|
+
- binding.pry
|
7
|
+
= link_to(url_for([resource_namespace, resource]), class: 'show btn btn-xs btn-responsive btn-primary') do
|
8
|
+
%i.fas.fa-eye
|
9
|
+
%span.btn-text= t('.show')
|
10
|
+
- if available_rest_actions.include?(:edit)
|
11
|
+
= link_to(url_for([:edit, resource_namespace, resource]), class: 'edit btn btn-xs btn-responsive btn-secondary') do
|
12
|
+
%i.fas.fa-edit
|
13
|
+
%span.btn-text= t('.edit')
|
14
|
+
- if available_rest_actions.include?(:destroy)
|
15
|
+
= link_to(url_for([resource_namespace, resource]), class: 'destroy btn btn-xs btn-responsive btn-danger', method: :delete, 'data-confirm': I18n.t('administrador.controller.confirmations.destroy')) do
|
16
|
+
%i.fas.fa-fire
|
17
|
+
%span.btn-text= t('.destroy')
|
data/config/locales/de.yml
CHANGED
@@ -4,12 +4,20 @@ de:
|
|
4
4
|
cmor/system/changelog:
|
5
5
|
one: Changelog
|
6
6
|
other: Changelogs
|
7
|
+
cmor/system/rack/attack/banned_ip:
|
8
|
+
one: Geblockte IP
|
9
|
+
other: Geblockte IPs
|
7
10
|
attributes:
|
8
11
|
cmor/system/changelog:
|
9
12
|
id: ID
|
10
13
|
file: Datei
|
11
14
|
content: Inhalt
|
12
15
|
version: Version
|
16
|
+
cmor/system/rack/attack/banned_ip:
|
17
|
+
id: ID
|
18
|
+
ip: IP Adresse
|
19
|
+
key: Schlüssel
|
20
|
+
value: Wert
|
13
21
|
activerecord:
|
14
22
|
models:
|
15
23
|
active_storage/attachment:
|
data/config/locales/en.yml
CHANGED
@@ -1,4 +1,23 @@
|
|
1
1
|
en:
|
2
|
+
activemodel:
|
3
|
+
models:
|
4
|
+
cmor/system/changelog:
|
5
|
+
one: Changelog
|
6
|
+
other: Changelogs
|
7
|
+
cmor/system/rack/attack/banned_ip:
|
8
|
+
one: Blocked IP
|
9
|
+
other: Blocked IPs
|
10
|
+
attributes:
|
11
|
+
cmor/system/changelog:
|
12
|
+
id: ID
|
13
|
+
file: Datei
|
14
|
+
content: Inhalt
|
15
|
+
version: Version
|
16
|
+
cmor/system/rack/attack/banned_ip:
|
17
|
+
id: ID
|
18
|
+
ip: IP Address
|
19
|
+
key: Schlüssel
|
20
|
+
value: Wert
|
2
21
|
activerecord:
|
3
22
|
models:
|
4
23
|
active_storage/attachment:
|
data/config/routes.rb
CHANGED
@@ -10,5 +10,11 @@ Cmor::System::Engine.routes.draw do
|
|
10
10
|
end
|
11
11
|
end if Cmor::System::Configuration.enable_active_storage_backend
|
12
12
|
|
13
|
+
namespace :rack do
|
14
|
+
namespace :attack do
|
15
|
+
resources :banned_ips, only: [:index, :show, :new, :create, :destroy]
|
16
|
+
end
|
17
|
+
end if Cmor::System::Configuration.enable_active_storage_backend
|
18
|
+
|
13
19
|
root to: 'home#index'
|
14
20
|
end
|
@@ -14,11 +14,13 @@ module Cmor
|
|
14
14
|
mattr_accessor(:enable_delayed_job_backend) { false }
|
15
15
|
mattr_accessor(:record_factory_name) { 'user' }
|
16
16
|
mattr_accessor(:record_attachment_name) { 'asset' }
|
17
|
+
mattr_accessor(:enable_rack_attack_backend) { false }
|
17
18
|
|
18
19
|
def self.registered_controllers
|
19
20
|
rc = @@registered_controllers.call
|
20
21
|
rc.reject! { |c| c.name =~ /.*ActiveStorage.*/ } unless enable_active_storage_backend
|
21
22
|
rc.reject! { |c| c.name =~ /.*Delayed.*/ } unless enable_delayed_job_backend
|
23
|
+
rc.reject! { |c| c.name =~ /.*Rack::Attack.*/ } unless enable_rack_attack_backend
|
22
24
|
-> { rc }
|
23
25
|
end
|
24
26
|
end
|
@@ -6,6 +6,7 @@ module Cmor
|
|
6
6
|
|
7
7
|
source_root File.expand_path('../templates', __FILE__)
|
8
8
|
|
9
|
+
attr_reader :cmor_system_enable_rack_attack
|
9
10
|
attr_reader :cmor_system_enable_active_storage
|
10
11
|
attr_reader :cmor_system_enable_delayed_job
|
11
12
|
attr_reader :record_factory_name
|
@@ -13,6 +14,7 @@ module Cmor
|
|
13
14
|
|
14
15
|
def initialize(*args)
|
15
16
|
super
|
17
|
+
@cmor_system_enable_rack_attack = ENV.fetch('CMOR_SYSTEM_ENABLE_RACK_ATTACK') { false }
|
16
18
|
@cmor_system_enable_active_storage = ENV.fetch('CMOR_SYSTEM_ENABLE_ACTIVE_STORAGE') { false }
|
17
19
|
@cmor_system_enable_delayed_job = ENV.fetch('CMOR_SYSTEM_ENABLE_DELAYED_JOB') { false }
|
18
20
|
@record_factory_name = ENV.fetch('CMOR_SYSTEM_RECORD_FACTORY_NAME') { 'post' }
|
@@ -2,15 +2,19 @@ Cmor::System.configure do |config|
|
|
2
2
|
# Set the resources, that will be shown in the backend menu.
|
3
3
|
#
|
4
4
|
# Default: config.registered_controllers = -> {[
|
5
|
+
# Cmor::System::ChangelogsController,
|
5
6
|
# Cmor::System::DelayedBackendActiveRecordJobsController,
|
6
7
|
# Cmor::System::ActiveStorage::BlobsController,
|
7
|
-
# Cmor::System::ActiveStorage::AttachmentsController
|
8
|
+
# Cmor::System::ActiveStorage::AttachmentsController,
|
9
|
+
# Cmor::System::Rack::Attack::BannedIpsController
|
8
10
|
# ]}
|
9
11
|
#
|
10
12
|
config.registered_controllers = -> {[
|
13
|
+
Cmor::System::ChangelogsController,
|
11
14
|
Cmor::System::DelayedBackendActiveRecordJobsController,
|
12
15
|
Cmor::System::ActiveStorage::BlobsController,
|
13
|
-
Cmor::System::ActiveStorage::AttachmentsController
|
16
|
+
Cmor::System::ActiveStorage::AttachmentsController,
|
17
|
+
Cmor::System::Rack::Attack::BannedIpsController
|
14
18
|
]}
|
15
19
|
|
16
20
|
# Set the services, that will be shown in the backend menu.
|
@@ -44,4 +48,10 @@ Cmor::System.configure do |config|
|
|
44
48
|
# default: config.enable_delayed_job_backend = false
|
45
49
|
#
|
46
50
|
config.enable_delayed_job_backend = <%= cmor_system_enable_delayed_job %>
|
51
|
+
|
52
|
+
# Enable support for Rack::Attack.
|
53
|
+
#
|
54
|
+
# default: config.enable_rack_attack_backend = false
|
55
|
+
#
|
56
|
+
config.enable_rack_attack_backend = <%= cmor_system_enable_rack_attack %>
|
47
57
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cmor_system
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.60.pre
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Roberto Vasquez Angel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-10-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -30,28 +30,28 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - '='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.0.
|
33
|
+
version: 0.0.60.pre
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - '='
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.0.
|
40
|
+
version: 0.0.60.pre
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: cmor_core_backend
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - '='
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 0.0.
|
47
|
+
version: 0.0.60.pre
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - '='
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 0.0.
|
54
|
+
version: 0.0.60.pre
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: sqlite3
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -346,6 +346,20 @@ dependencies:
|
|
346
346
|
- - ">="
|
347
347
|
- !ruby/object:Gem::Version
|
348
348
|
version: '0'
|
349
|
+
- !ruby/object:Gem::Dependency
|
350
|
+
name: rack-attack
|
351
|
+
requirement: !ruby/object:Gem::Requirement
|
352
|
+
requirements:
|
353
|
+
- - ">="
|
354
|
+
- !ruby/object:Gem::Version
|
355
|
+
version: '0'
|
356
|
+
type: :development
|
357
|
+
prerelease: false
|
358
|
+
version_requirements: !ruby/object:Gem::Requirement
|
359
|
+
requirements:
|
360
|
+
- - ">="
|
361
|
+
- !ruby/object:Gem::Version
|
362
|
+
version: '0'
|
349
363
|
description:
|
350
364
|
email:
|
351
365
|
- roberto@vasquez-angel.de
|
@@ -368,7 +382,10 @@ files:
|
|
368
382
|
- app/controllers/cmor/system/changelogs_controller.rb
|
369
383
|
- app/controllers/cmor/system/delayed_backend_active_record_jobs_controller.rb
|
370
384
|
- app/controllers/cmor/system/home_controller.rb
|
385
|
+
- app/controllers/cmor/system/rack/attack/banned_ips_controller.rb
|
386
|
+
- app/models/cmor/system/active_collection/base.rb
|
371
387
|
- app/models/cmor/system/changelog.rb
|
388
|
+
- app/models/cmor/system/rack/attack/banned_ip.rb
|
372
389
|
- app/views/cmor/system/active_storage/attachments/_index_table.html.haml
|
373
390
|
- app/views/cmor/system/active_storage/blobs/_index_table.html.haml
|
374
391
|
- app/views/cmor/system/changelogs/_after_show_table.html.haml
|
@@ -376,6 +393,10 @@ files:
|
|
376
393
|
- app/views/cmor/system/changelogs/_show_table.html.haml
|
377
394
|
- app/views/cmor/system/delayed_backend_active_record_jobs/_index_table_actions.html.haml
|
378
395
|
- app/views/cmor/system/delayed_backend_active_record_jobs/_show_actions.html.haml
|
396
|
+
- app/views/cmor/system/rack/attack/banned_ips/_form.html.haml
|
397
|
+
- app/views/cmor/system/rack/attack/banned_ips/_index_table.html.haml
|
398
|
+
- app/views/cmor/system/rack/attack/banned_ips/_index_table_actions.html.haml~
|
399
|
+
- app/views/cmor/system/rack/attack/banned_ips/_show_table.html.haml
|
379
400
|
- config/initializers/assets.rb
|
380
401
|
- config/initializers/cmor.rb
|
381
402
|
- config/locales/de.yml
|
@@ -393,6 +414,7 @@ files:
|
|
393
414
|
- spec/factories/active_storage/blobs.rb
|
394
415
|
- spec/factories/cmor_system_changelogs.rb
|
395
416
|
- spec/factories/delayed/backend_active_record_jobs.rb
|
417
|
+
- spec/factories/rack/attack/banned_ips.rb
|
396
418
|
- spec/files/active_storage/blob/example.png
|
397
419
|
- spec/files/cmor/system/changelog/file/example.md
|
398
420
|
homepage: https://github.com/content-management-on-rails
|