panda-core 0.10.7 → 0.12.2
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/README.md +35 -0
- data/app/assets/tailwind/application.css +5 -0
- data/app/models/concerns/panda/core/has_uuid.rb +24 -0
- data/app/models/panda/core/user.rb +22 -19
- data/config/brakeman.ignore +51 -0
- data/db/migrate/20250809000001_create_panda_core_users.rb +14 -9
- data/db/migrate/20251203100000_rename_is_admin_to_admin_in_panda_core_users.rb +18 -0
- data/lib/panda/core/configuration.rb +4 -0
- data/lib/panda/core/engine/autoload_config.rb +9 -10
- data/lib/panda/core/engine/omniauth_config.rb +82 -36
- data/lib/panda/core/engine/route_config.rb +37 -0
- data/lib/panda/core/engine.rb +46 -41
- data/lib/panda/core/middleware.rb +146 -0
- data/lib/panda/core/shared/inflections_config.rb +1 -0
- data/lib/panda/core/testing/rails_helper.rb +17 -8
- data/lib/panda/core/testing/support/authentication_helpers.rb +6 -6
- data/lib/panda/core/testing/support/authentication_test_helpers.rb +2 -12
- data/lib/panda/core/testing/support/system/browser_console_logger.rb +1 -2
- data/lib/panda/core/testing/support/system/chrome_path.rb +38 -0
- data/lib/panda/core/testing/support/system/cuprite_helpers.rb +79 -0
- data/lib/panda/core/testing/support/system/cuprite_setup.rb +61 -66
- data/lib/panda/core/testing/support/system/system_test_helpers.rb +11 -11
- data/lib/panda/core/version.rb +1 -1
- data/lib/tasks/panda/core/users.rake +3 -3
- data/lib/tasks/panda/shared.rake +31 -5
- data/public/panda-core-assets/favicons/browserconfig.xml +1 -1
- data/public/panda-core-assets/favicons/site.webmanifest +1 -1
- data/public/panda-core-assets/panda-core-0.11.0.css +2 -0
- data/public/panda-core-assets/panda-core.css +1 -1
- metadata +9 -9
- data/db/migrate/20250810120000_add_current_theme_to_panda_core_users.rb +0 -7
- data/db/migrate/20250811120000_add_oauth_avatar_url_to_panda_core_users.rb +0 -7
- data/lib/panda/core/engine/middleware_config.rb +0 -17
- data/lib/panda/core/testing/support/system/better_system_tests.rb +0 -180
- data/lib/panda/core/testing/support/system/capybara_config.rb +0 -64
- data/lib/panda/core/testing/support/system/ci_capybara_config.rb +0 -77
- data/public/panda-core-assets/panda-core-0.10.6.css +0 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1865844f240222fd1894bae360505e3954858ac363b49d4bd0aa59bb8699fdcc
|
|
4
|
+
data.tar.gz: a85741affaa234bd9899c670da3b7a4eab5c7aa65bf09ba89081b49606ecbf66
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a873452e2589d9fc7e4254f7ef81b4bb8cbccc2a57fd19d17f95aed22609d1226711579fe905b5caf269fec5506a203fa93ab16b10073ec372bf033e0ccd70be
|
|
7
|
+
data.tar.gz: c1be83e01a08ff7a6ddce1b2afc321ec780dd5f2b00837220c411838f39e0dd9584419737239c8640a25e15b4d86ea190109fc22c26291ea21d6551317082773
|
data/README.md
CHANGED
|
@@ -5,6 +5,26 @@ Core functionality shared between Panda Software gems:
|
|
|
5
5
|
- [Panda CMS](https://github.com/tastybamboo/panda-cms)
|
|
6
6
|
- [Panda Editor](https://github.com/tastybamboo/panda-editor)
|
|
7
7
|
|
|
8
|
+
## Requirements
|
|
9
|
+
|
|
10
|
+
### Database Support
|
|
11
|
+
|
|
12
|
+
Panda Core supports both PostgreSQL and SQLite3 databases:
|
|
13
|
+
|
|
14
|
+
**PostgreSQL** (recommended for production):
|
|
15
|
+
|
|
16
|
+
- PostgreSQL 12, 15, 17, 18
|
|
17
|
+
- Tested against all versions in CI
|
|
18
|
+
- Uses native UUID generation (`gen_random_uuid()`)
|
|
19
|
+
|
|
20
|
+
**SQLite3** (development/testing):
|
|
21
|
+
|
|
22
|
+
- SQLite 3.x
|
|
23
|
+
- Uses application-level UUID generation
|
|
24
|
+
- Ideal for local development and testing
|
|
25
|
+
|
|
26
|
+
Both databases are tested extensively in CI across multiple Ruby and Rails versions to ensure cross-database compatibility.
|
|
27
|
+
|
|
8
28
|
## Installation
|
|
9
29
|
|
|
10
30
|
Add this line to your application's Gemfile:
|
|
@@ -251,6 +271,21 @@ bundle exec rspec spec/generators
|
|
|
251
271
|
bundle exec rspec spec/system
|
|
252
272
|
```
|
|
253
273
|
|
|
274
|
+
### CI/Docker parity
|
|
275
|
+
|
|
276
|
+
The GitHub Actions workflow now builds a reusable CI image (Chrome + PostgreSQL + Ruby). You can reuse it locally:
|
|
277
|
+
|
|
278
|
+
```
|
|
279
|
+
bin/ci build # build the CI image locally
|
|
280
|
+
bin/ci test # run the suite inside the container
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
To dry-run the workflow with `act` (uses the locally built image tagged `:local`):
|
|
284
|
+
|
|
285
|
+
```
|
|
286
|
+
act -j test-stable
|
|
287
|
+
```
|
|
288
|
+
|
|
254
289
|
## Contributing
|
|
255
290
|
|
|
256
291
|
1. Fork it
|
|
@@ -153,6 +153,11 @@
|
|
|
153
153
|
html[data-theme='sky'] .bg-gradient-admin {
|
|
154
154
|
background: linear-gradient(to bottom right, rgb(20, 32, 74), rgb(42, 102, 159));
|
|
155
155
|
}
|
|
156
|
+
|
|
157
|
+
/* Remove rounded bottom-left corner on admin sidebar */
|
|
158
|
+
.bg-gradient-admin {
|
|
159
|
+
border-bottom-left-radius: 0;
|
|
160
|
+
}
|
|
156
161
|
}
|
|
157
162
|
|
|
158
163
|
/* Form input styles */
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Panda
|
|
4
|
+
module Core
|
|
5
|
+
# Concern for models using UUIDs as primary keys
|
|
6
|
+
# Ensures UUID generation works across all database adapters
|
|
7
|
+
module HasUUID
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
included do
|
|
11
|
+
# Generate UUID before creation if not already set
|
|
12
|
+
# PostgreSQL uses gen_random_uuid() natively
|
|
13
|
+
# SQLite and other databases use SecureRandom.uuid
|
|
14
|
+
before_create :generate_uuid_if_blank
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def generate_uuid_if_blank
|
|
20
|
+
self.id ||= SecureRandom.uuid if id.blank?
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
module Panda
|
|
4
4
|
module Core
|
|
5
5
|
class User < ApplicationRecord
|
|
6
|
+
include HasUUID
|
|
7
|
+
|
|
6
8
|
self.table_name = "panda_core_users"
|
|
7
9
|
|
|
8
10
|
# Active Storage attachment for avatar with variants
|
|
@@ -17,16 +19,15 @@ module Panda
|
|
|
17
19
|
|
|
18
20
|
before_save :downcase_email
|
|
19
21
|
|
|
22
|
+
# Determine which column stores admin flag (supports legacy `admin` and new `is_admin`)
|
|
23
|
+
def self.admin_column
|
|
24
|
+
# Prefer canonical `admin` if available, otherwise fall back to legacy `is_admin`
|
|
25
|
+
@admin_column ||= column_names.include?("admin") ? "admin" : "is_admin"
|
|
26
|
+
end
|
|
27
|
+
|
|
20
28
|
# Scopes
|
|
21
|
-
# Support both 'admin' (newer) and 'is_admin' (older) column names
|
|
22
29
|
scope :admins, -> {
|
|
23
|
-
|
|
24
|
-
where(admin: true)
|
|
25
|
-
elsif column_names.include?("is_admin")
|
|
26
|
-
where(is_admin: true)
|
|
27
|
-
else
|
|
28
|
-
none
|
|
29
|
-
end
|
|
30
|
+
where(admin_column => true)
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
def self.find_or_create_from_auth_hash(auth_hash)
|
|
@@ -46,7 +47,7 @@ module Panda
|
|
|
46
47
|
email: auth_hash.info.email.downcase,
|
|
47
48
|
name: auth_hash.info.name || "Unknown User",
|
|
48
49
|
image_url: auth_hash.info.image,
|
|
49
|
-
|
|
50
|
+
admin_column => User.count.zero? # First user is admin
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
user = create!(attributes)
|
|
@@ -60,21 +61,23 @@ module Panda
|
|
|
60
61
|
end
|
|
61
62
|
|
|
62
63
|
# Admin status check
|
|
63
|
-
# Note: Column is named 'admin' in newer schemas, 'is_admin' in older ones
|
|
64
64
|
def admin?
|
|
65
|
-
|
|
65
|
+
ActiveRecord::Type::Boolean.new.cast(admin)
|
|
66
66
|
end
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
# Support both legacy `admin` and new `is_admin` columns
|
|
69
|
+
def admin
|
|
70
|
+
self[self.class.admin_column]
|
|
70
71
|
end
|
|
71
72
|
|
|
72
|
-
def
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
73
|
+
def admin=(value)
|
|
74
|
+
self[self.class.admin_column] = ActiveRecord::Type::Boolean.new.cast(value)
|
|
75
|
+
end
|
|
76
|
+
alias_method :is_admin, :admin
|
|
77
|
+
alias_method :is_admin=, :admin=
|
|
78
|
+
|
|
79
|
+
def active_for_authentication?
|
|
80
|
+
true
|
|
78
81
|
end
|
|
79
82
|
|
|
80
83
|
# Returns the URL for the user's avatar
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ignored_warnings": [
|
|
3
|
+
{
|
|
4
|
+
"warning_type": "Command Injection",
|
|
5
|
+
"warning_code": 14,
|
|
6
|
+
"fingerprint": "29d5b60b583fc98e293f6ac47f153bbf5db48a03963f8c9a7711bd251ecf2d81",
|
|
7
|
+
"check_name": "Execute",
|
|
8
|
+
"message": "Possible command injection",
|
|
9
|
+
"file": "lib/panda/core/testing/support/system/cuprite_helpers.rb",
|
|
10
|
+
"line": 78,
|
|
11
|
+
"link": "https://brakemanscanner.org/docs/warning_types/command_injection/",
|
|
12
|
+
"code": "system(\"ffmpeg -y -framerate 8 -pattern_type glob -i '#{File.join(dir, \"frames\")}/*.png' -c:v libx264 -pix_fmt yuv420p '#{capybara_artifacts_dir.join(\"#{example.metadata[:full_description].parameterize}.mp4\")}'\\n\")",
|
|
13
|
+
"render_path": null,
|
|
14
|
+
"location": {
|
|
15
|
+
"type": "method",
|
|
16
|
+
"class": "Panda::Core::Testing::CupriteHelpers",
|
|
17
|
+
"method": "record_video!"
|
|
18
|
+
},
|
|
19
|
+
"user_input": "File.join(dir, \"frames\")",
|
|
20
|
+
"confidence": "Medium",
|
|
21
|
+
"cwe_id": [
|
|
22
|
+
77
|
|
23
|
+
],
|
|
24
|
+
"note": "This does not use user input and is based on environment variables and the current folder"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"warning_type": "Command Injection",
|
|
28
|
+
"warning_code": 14,
|
|
29
|
+
"fingerprint": "d3c339f054cbd511956e04eaf8763bc722bd2e4559a1c1fec58749abef9792cd",
|
|
30
|
+
"check_name": "Execute",
|
|
31
|
+
"message": "Possible command injection",
|
|
32
|
+
"file": "lib/panda/core/testing/support/system/chrome_verification.rb",
|
|
33
|
+
"line": 43,
|
|
34
|
+
"link": "https://brakemanscanner.org/docs/warning_types/command_injection/",
|
|
35
|
+
"code": "Process.spawn(*[BrowserPath.resolve, v.nil? ? (\"--#{k}\") : (\"--#{k}=#{v}\"), \"about:blank\"], :out => (File::NULL), :err => (File::NULL))",
|
|
36
|
+
"render_path": null,
|
|
37
|
+
"location": {
|
|
38
|
+
"type": "method",
|
|
39
|
+
"class": "Panda::Core::Testing::Support::System::ChromeVerification",
|
|
40
|
+
"method": "s(:self).verify!"
|
|
41
|
+
},
|
|
42
|
+
"user_input": "k",
|
|
43
|
+
"confidence": "Medium",
|
|
44
|
+
"cwe_id": [
|
|
45
|
+
77
|
|
46
|
+
],
|
|
47
|
+
"note": "Chrome verification uses a fixed set of browser flags from CHROME_FLAGS constant; no user input is accepted"
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
"brakeman_version": "7.1.1"
|
|
51
|
+
}
|
|
@@ -2,16 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
class CreatePandaCoreUsers < ActiveRecord::Migration[7.1]
|
|
4
4
|
def change
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
t.string :name
|
|
8
|
-
t.string :email, null: false
|
|
9
|
-
t.string :image_url
|
|
10
|
-
t.boolean :is_admin, default: false, null: false
|
|
11
|
-
t.timestamps
|
|
12
|
-
end
|
|
5
|
+
# Enable pgcrypto extension for PostgreSQL UUID generation
|
|
6
|
+
enable_extension "pgcrypto" if ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
|
|
13
7
|
|
|
14
|
-
|
|
8
|
+
# Rails 7.1+ supports id: :uuid across databases
|
|
9
|
+
# PostgreSQL uses gen_random_uuid(), SQLite uses application-generated UUIDs
|
|
10
|
+
create_table :panda_core_users, id: :uuid do |t|
|
|
11
|
+
t.string :name
|
|
12
|
+
t.string :email, null: false
|
|
13
|
+
t.string :image_url
|
|
14
|
+
t.boolean :admin, default: false, null: false
|
|
15
|
+
t.string :current_theme
|
|
16
|
+
t.string :oauth_avatar_url
|
|
17
|
+
t.timestamps
|
|
15
18
|
end
|
|
19
|
+
|
|
20
|
+
add_index :panda_core_users, :email, unique: true
|
|
16
21
|
end
|
|
17
22
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class RenameIsAdminToAdminInPandaCoreUsers < ActiveRecord::Migration[7.1]
|
|
4
|
+
def up
|
|
5
|
+
# If apps created an `is_admin` column during the brief rename, move it back
|
|
6
|
+
return unless column_exists?(:panda_core_users, :is_admin)
|
|
7
|
+
return if column_exists?(:panda_core_users, :admin)
|
|
8
|
+
|
|
9
|
+
rename_column :panda_core_users, :is_admin, :admin
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def down
|
|
13
|
+
return unless column_exists?(:panda_core_users, :admin)
|
|
14
|
+
return if column_exists?(:panda_core_users, :is_admin)
|
|
15
|
+
|
|
16
|
+
rename_column :panda_core_users, :admin, :is_admin
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require "active_support/core_ext/numeric/bytes"
|
|
2
|
+
|
|
1
3
|
module Panda
|
|
2
4
|
module Core
|
|
3
5
|
class Configuration
|
|
@@ -7,6 +9,7 @@ module Panda
|
|
|
7
9
|
:cache_store,
|
|
8
10
|
:parent_controller,
|
|
9
11
|
:parent_mailer,
|
|
12
|
+
:auto_mount_engine,
|
|
10
13
|
:mailer_sender,
|
|
11
14
|
:mailer_default_url_options,
|
|
12
15
|
:session_token_cookie,
|
|
@@ -38,6 +41,7 @@ module Panda
|
|
|
38
41
|
@cache_store = :memory_store
|
|
39
42
|
@parent_controller = "ActionController::API"
|
|
40
43
|
@parent_mailer = "ActionMailer::Base"
|
|
44
|
+
@auto_mount_engine = true
|
|
41
45
|
@mailer_sender = "support@example.com"
|
|
42
46
|
@mailer_default_url_options = {host: "localhost:3000"}
|
|
43
47
|
@session_token_cookie = :panda_session
|
|
@@ -3,21 +3,20 @@
|
|
|
3
3
|
module Panda
|
|
4
4
|
module Core
|
|
5
5
|
class Engine < ::Rails::Engine
|
|
6
|
-
# Autoload paths configuration
|
|
7
6
|
module AutoloadConfig
|
|
8
7
|
extend ActiveSupport::Concern
|
|
9
8
|
|
|
10
9
|
included do
|
|
11
|
-
|
|
10
|
+
# These must run BEFORE initialization, so this is allowed
|
|
11
|
+
config.autoload_paths << root.join("app/builders")
|
|
12
|
+
config.autoload_paths << root.join("app/components")
|
|
13
|
+
config.autoload_paths << root.join("app/services")
|
|
14
|
+
config.autoload_paths << root.join("app/models")
|
|
15
|
+
config.autoload_paths << root.join("app/helpers")
|
|
16
|
+
config.autoload_paths << root.join("app/constraints")
|
|
12
17
|
|
|
13
|
-
#
|
|
14
|
-
|
|
15
|
-
# Zeitwerk will automatically discover nested modules from these roots
|
|
16
|
-
config.autoload_paths += Dir[root.join("app", "models")]
|
|
17
|
-
config.autoload_paths += Dir[root.join("app", "controllers")]
|
|
18
|
-
config.autoload_paths += Dir[root.join("app", "builders")]
|
|
19
|
-
config.autoload_paths += Dir[root.join("app", "components")]
|
|
20
|
-
config.autoload_paths += Dir[root.join("app", "services")]
|
|
18
|
+
# Mirror eager-load as needed
|
|
19
|
+
config.eager_load_paths.concat(config.autoload_paths)
|
|
21
20
|
end
|
|
22
21
|
end
|
|
23
22
|
end
|
|
@@ -1,51 +1,97 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "active_support/concern"
|
|
4
|
+
|
|
3
5
|
module Panda
|
|
4
6
|
module Core
|
|
5
7
|
class Engine < ::Rails::Engine
|
|
6
|
-
# OmniAuth configuration
|
|
7
8
|
module OmniauthConfig
|
|
8
9
|
extend ActiveSupport::Concern
|
|
9
10
|
|
|
11
|
+
PROVIDER_REGISTRY = {
|
|
12
|
+
# Microsoft
|
|
13
|
+
"microsoft" => :microsoft_graph,
|
|
14
|
+
"microsoft_graph" => :microsoft_graph,
|
|
15
|
+
|
|
16
|
+
# Google
|
|
17
|
+
"google" => :google_oauth2,
|
|
18
|
+
"google_oauth2" => :google_oauth2,
|
|
19
|
+
"gmail" => :google_oauth2,
|
|
20
|
+
|
|
21
|
+
# GitHub
|
|
22
|
+
"github" => :github,
|
|
23
|
+
"gh" => :github,
|
|
24
|
+
|
|
25
|
+
# Developer
|
|
26
|
+
"developer" => :developer
|
|
27
|
+
}.freeze
|
|
28
|
+
|
|
10
29
|
included do
|
|
11
|
-
initializer
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
configure do |config|
|
|
20
|
-
config.path_prefix = "#{Panda::Core.config.admin_path}/auth"
|
|
21
|
-
# POST-only for CSRF protection (CVE-2015-9284)
|
|
22
|
-
# All login forms use POST via form_tag method: "post"
|
|
23
|
-
config.allowed_request_methods = [:post]
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
Panda::Core.config.authentication_providers.each do |provider_name, settings|
|
|
27
|
-
# Build provider options, allowing custom path name override
|
|
28
|
-
provider_options = settings[:options] || {}
|
|
29
|
-
|
|
30
|
-
# If path_name is specified, use it to override the default strategy name in URLs
|
|
31
|
-
if settings[:path_name].present?
|
|
32
|
-
provider_options = provider_options.merge(name: settings[:path_name])
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
case provider_name.to_s
|
|
36
|
-
when "microsoft_graph"
|
|
37
|
-
provider :microsoft_graph, settings[:client_id], settings[:client_secret], provider_options
|
|
38
|
-
when "google_oauth2"
|
|
39
|
-
provider :google_oauth2, settings[:client_id], settings[:client_secret], provider_options
|
|
40
|
-
when "github"
|
|
41
|
-
provider :github, settings[:client_id], settings[:client_secret], provider_options
|
|
42
|
-
when "developer"
|
|
43
|
-
provider :developer if Rails.env.development?
|
|
44
|
-
end
|
|
45
|
-
end
|
|
30
|
+
if respond_to?(:initializer)
|
|
31
|
+
initializer "panda_core.omniauth" do |app|
|
|
32
|
+
require_relative "../oauth_providers"
|
|
33
|
+
Panda::Core::OAuthProviders.setup
|
|
34
|
+
|
|
35
|
+
load_yaml_provider_overrides!
|
|
36
|
+
configure_omniauth_globals
|
|
37
|
+
mount_omniauth_middleware(app)
|
|
46
38
|
end
|
|
47
39
|
end
|
|
48
40
|
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
# 1. YAML overrides
|
|
45
|
+
def load_yaml_provider_overrides!
|
|
46
|
+
path = Panda::Core::Engine.root.join("config/providers.yml")
|
|
47
|
+
return unless File.exist?(path)
|
|
48
|
+
|
|
49
|
+
yaml = YAML.load_file(path) || {}
|
|
50
|
+
(yaml["providers"] || {}).each do |name, settings|
|
|
51
|
+
Panda::Core.config.authentication_providers[name.to_s] ||= {}
|
|
52
|
+
Panda::Core.config.authentication_providers[name.to_s].deep_merge!(settings)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# 2. Global settings
|
|
57
|
+
def configure_omniauth_globals
|
|
58
|
+
OmniAuth.configure do |c|
|
|
59
|
+
c.allowed_request_methods = [:post]
|
|
60
|
+
c.path_prefix = "#{Panda::Core.config.admin_path}/auth"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# 3. Middleware insertion
|
|
65
|
+
def mount_omniauth_middleware(app)
|
|
66
|
+
ctx = self # Capture the Engine/Concern context
|
|
67
|
+
|
|
68
|
+
Panda::Core::Middleware.use(app, OmniAuth::Builder) do
|
|
69
|
+
Panda::Core.config.authentication_providers.each do |name, settings|
|
|
70
|
+
ctx.send(:configure_provider, self, name, settings)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# 4. Provider builder
|
|
76
|
+
def configure_provider(builder, name, settings)
|
|
77
|
+
symbol = PROVIDER_REGISTRY[name.to_s]
|
|
78
|
+
|
|
79
|
+
unless symbol
|
|
80
|
+
Rails.logger.warn("[panda-core] Unknown OmniAuth provider: #{name.inspect}")
|
|
81
|
+
return
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
return if symbol == :developer && !Rails.env.development?
|
|
85
|
+
|
|
86
|
+
options = (settings[:options] || {}).dup
|
|
87
|
+
options[:name] = settings[:path_name] if settings[:path_name].present?
|
|
88
|
+
|
|
89
|
+
if settings[:client_id] && settings[:client_secret]
|
|
90
|
+
builder.provider symbol, settings[:client_id], settings[:client_secret], options
|
|
91
|
+
else
|
|
92
|
+
builder.provider symbol, options
|
|
93
|
+
end
|
|
94
|
+
end
|
|
49
95
|
end
|
|
50
96
|
end
|
|
51
97
|
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Panda
|
|
4
|
+
module Core
|
|
5
|
+
class Engine < ::Rails::Engine
|
|
6
|
+
# Automatically mount the engine into host applications
|
|
7
|
+
module RouteConfig
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
included do
|
|
11
|
+
# Append Core routes to the host app after initialization so
|
|
12
|
+
# engine routes are available without manual mounting.
|
|
13
|
+
config.after_initialize do |app|
|
|
14
|
+
next unless Panda::Core.config.auto_mount_engine
|
|
15
|
+
|
|
16
|
+
route_set = app.routes
|
|
17
|
+
already_mounted =
|
|
18
|
+
route_set.routes.any? do |route|
|
|
19
|
+
route.app == Panda::Core::Engine ||
|
|
20
|
+
(route.app.respond_to?(:app) && route.app.app == Panda::Core::Engine)
|
|
21
|
+
end
|
|
22
|
+
already_mounted ||= route_set.named_routes.key?(:panda_core)
|
|
23
|
+
|
|
24
|
+
next if already_mounted
|
|
25
|
+
|
|
26
|
+
route_set.append do
|
|
27
|
+
# Re-check inside the mapper to avoid duplicate mounts during reloads
|
|
28
|
+
next if route_set.named_routes.key?(:panda_core)
|
|
29
|
+
|
|
30
|
+
mount Panda::Core::Engine => "/", as: "panda_core"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
data/lib/panda/core/engine.rb
CHANGED
|
@@ -1,82 +1,86 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
require "rails
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails"
|
|
4
4
|
require "omniauth"
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
# Issue: https://github.com/cookpad/omniauth-rails_csrf_protection/issues/23
|
|
9
|
-
# This can be removed once the gem is updated or Rails 8.2 is released
|
|
10
|
-
#
|
|
11
|
-
# We suppress the warning by temporarily redirecting stderr since
|
|
12
|
-
# ActiveSupport::Deprecation.silence was removed in Rails 8.1
|
|
13
|
-
original_stderr = $stderr
|
|
14
|
-
$stderr = StringIO.new
|
|
15
|
-
begin
|
|
16
|
-
require "omniauth/rails_csrf_protection"
|
|
17
|
-
ensure
|
|
18
|
-
$stderr = original_stderr
|
|
19
|
-
end
|
|
6
|
+
require "panda/core/middleware"
|
|
7
|
+
require "panda/core/module_registry"
|
|
20
8
|
|
|
21
|
-
#
|
|
9
|
+
# Shared engine mixins
|
|
22
10
|
require_relative "shared/inflections_config"
|
|
23
11
|
require_relative "shared/generator_config"
|
|
24
12
|
|
|
25
|
-
#
|
|
13
|
+
# Engine mixins
|
|
26
14
|
require_relative "engine/autoload_config"
|
|
27
|
-
require_relative "engine/middleware_config"
|
|
28
15
|
require_relative "engine/importmap_config"
|
|
29
16
|
require_relative "engine/omniauth_config"
|
|
30
17
|
require_relative "engine/phlex_config"
|
|
31
18
|
require_relative "engine/admin_controller_config"
|
|
32
|
-
|
|
33
|
-
# Load module registry
|
|
34
|
-
require_relative "module_registry"
|
|
19
|
+
require_relative "engine/route_config"
|
|
35
20
|
|
|
36
21
|
module Panda
|
|
37
22
|
module Core
|
|
38
23
|
class Engine < ::Rails::Engine
|
|
39
24
|
isolate_namespace Panda::Core
|
|
40
25
|
|
|
41
|
-
#
|
|
26
|
+
#
|
|
27
|
+
# Include shared behaviours
|
|
28
|
+
#
|
|
42
29
|
include Shared::InflectionsConfig
|
|
43
30
|
include Shared::GeneratorConfig
|
|
44
31
|
|
|
45
|
-
#
|
|
32
|
+
#
|
|
33
|
+
# Include engine-level concerns
|
|
34
|
+
#
|
|
46
35
|
include AutoloadConfig
|
|
47
|
-
include MiddlewareConfig
|
|
48
36
|
include ImportmapConfig
|
|
49
37
|
include OmniauthConfig
|
|
50
38
|
include PhlexConfig
|
|
51
39
|
include AdminControllerConfig
|
|
40
|
+
include RouteConfig
|
|
52
41
|
|
|
53
|
-
|
|
54
|
-
|
|
42
|
+
#
|
|
43
|
+
# Misc configuration point
|
|
44
|
+
#
|
|
45
|
+
initializer "panda_core.configuration" do
|
|
46
|
+
# Intentionally quiet — used as a stable anchor point
|
|
55
47
|
end
|
|
56
48
|
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
#
|
|
61
|
-
initializer "
|
|
62
|
-
|
|
63
|
-
|
|
49
|
+
#
|
|
50
|
+
# Static asset handling for:
|
|
51
|
+
# /panda-core-assets
|
|
52
|
+
#
|
|
53
|
+
initializer "panda_core.static_assets" do |app|
|
|
54
|
+
Panda::Core::Middleware.use(
|
|
55
|
+
app,
|
|
56
|
+
Rack::Static,
|
|
64
57
|
urls: ["/panda-core-assets"],
|
|
65
58
|
root: Panda::Core::Engine.root.join("public"),
|
|
66
59
|
header_rules: [
|
|
67
|
-
|
|
68
|
-
|
|
60
|
+
[
|
|
61
|
+
:all,
|
|
62
|
+
{
|
|
63
|
+
"Cache-Control" =>
|
|
64
|
+
Rails.env.development? ?
|
|
65
|
+
"no-cache, no-store, must-revalidate" :
|
|
66
|
+
"public, max-age=31536000"
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
69
|
]
|
|
70
|
+
)
|
|
70
71
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
Panda::Core::Middleware.use(
|
|
73
|
+
app,
|
|
74
|
+
Panda::Core::ModuleRegistry::JavaScriptMiddleware
|
|
75
|
+
)
|
|
74
76
|
end
|
|
75
77
|
end
|
|
76
78
|
end
|
|
77
79
|
end
|
|
78
80
|
|
|
79
|
-
#
|
|
81
|
+
#
|
|
82
|
+
# Register engine with ModuleRegistry
|
|
83
|
+
#
|
|
80
84
|
Panda::Core::ModuleRegistry.register(
|
|
81
85
|
gem_name: "panda-core",
|
|
82
86
|
engine: "Panda::Core::Engine",
|
|
@@ -85,6 +89,7 @@ Panda::Core::ModuleRegistry.register(
|
|
|
85
89
|
components: "app/components/panda/core/**/*.rb",
|
|
86
90
|
helpers: "app/helpers/panda/core/**/*.rb",
|
|
87
91
|
views: "app/views/panda/core/**/*.erb",
|
|
92
|
+
layouts: "app/views/layouts/panda/core/**/*.erb",
|
|
88
93
|
javascripts: "app/assets/javascript/panda/core/**/*.js"
|
|
89
94
|
}
|
|
90
95
|
)
|