Skip to content

Commit

Permalink
Store sessions in DB + improve cookies/sessions (#1145)
Browse files Browse the repository at this point in the history
  • Loading branch information
melroy89 authored Sep 27, 2024
1 parent e5a994d commit 3782397
Show file tree
Hide file tree
Showing 25 changed files with 139 additions and 177 deletions.
14 changes: 10 additions & 4 deletions config/packages/framework.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,19 @@ framework:
'x-forwarded-prefix',
]

# Enables session support. Note that the session will ONLY be started if you read or write from it.
# Remove or comment this section to explicitly disable session support.
# Sessions are stored in database, because saving sessions in Redis can give race conditions.
# See last paragraph of https://symfony.com/doc/current/session.html#store-sessions-in-a-key-value-database-redis
#
# PHP session handling is often (in Debian/Ubuntu) not doing gargage collection for sessions
# (session.gc_probability option in PHP).
# Hence we do also not want to set gc_maxlifetime for idle periods.
# We set our cookie session lifetime to the same value as remember_me token.
# More info: https://symfony.com/doc/current/session.html#session-idle-time-keep-alive
session:
handler_id: '%env(REDIS_DNS)%'
handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
cookie_secure: auto
cookie_samesite: lax
gc_maxlifetime: 1814400 # Match the remember_me lifetime
cookie_lifetime: 10512000 # 4 months long lifetime
storage_factory_id: session.storage.factory.native

http_client:
Expand Down
2 changes: 1 addition & 1 deletion config/packages/security.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ security:
user_checker: App\Security\UserChecker
remember_me:
secret: '%kernel.secret%'
lifetime: 1814400
lifetime: 10512000 # 4 Months
path: /
token_provider:
doctrine: true
Expand Down
198 changes: 101 additions & 97 deletions config/services.yaml
Original file line number Diff line number Diff line change
@@ -1,77 +1,76 @@
framework:
serializer:
mapping:
paths: ["%kernel.project_dir%/config/kbin_serialization"]
paths: ['%kernel.project_dir%/config/kbin_serialization']

parameters:
kbin_domain: "%env(KBIN_DOMAIN)%"
kbin_title: "%env(KBIN_TITLE)%"
kbin_meta_title: "%env(KBIN_META_TITLE)%"
kbin_meta_description: "%env(KBIN_META_DESCRIPTION)%"
kbin_meta_keywords: "%env(KBIN_META_KEYWORDS)%"
kbin_contact_email: "%env(KBIN_CONTACT_EMAIL)%"
kbin_sender_email: "%env(KBIN_SENDER_EMAIL)%"
kbin_default_lang: "%env(KBIN_DEFAULT_LANG)%"
kbin_api_items_per_page: "%env(KBIN_API_ITEMS_PER_PAGE)%"
kbin_js_enabled: "%env(bool:KBIN_JS_ENABLED)%"
kbin_federation_enabled: "%env(KBIN_FEDERATION_ENABLED)%"
kbin_registrations_enabled: "%env(KBIN_REGISTRATIONS_ENABLED)%"
kbin_domain: '%env(KBIN_DOMAIN)%'
kbin_title: '%env(KBIN_TITLE)%'
kbin_meta_title: '%env(KBIN_META_TITLE)%'
kbin_meta_description: '%env(KBIN_META_DESCRIPTION)%'
kbin_meta_keywords: '%env(KBIN_META_KEYWORDS)%'
kbin_contact_email: '%env(KBIN_CONTACT_EMAIL)%'
kbin_sender_email: '%env(KBIN_SENDER_EMAIL)%'
kbin_default_lang: '%env(KBIN_DEFAULT_LANG)%'
kbin_api_items_per_page: '%env(KBIN_API_ITEMS_PER_PAGE)%'
kbin_js_enabled: '%env(bool:KBIN_JS_ENABLED)%'
kbin_federation_enabled: '%env(KBIN_FEDERATION_ENABLED)%'
kbin_registrations_enabled: '%env(KBIN_REGISTRATIONS_ENABLED)%'
kbin_ap_route_condition: 'request.getAcceptableContentTypes() and request.getAcceptableContentTypes()[0] in ["application/activity+json", "application/ld+json", "application/json"]'
kbin_storage_url: "%env(KBIN_STORAGE_URL)%"
kbin_storage_url: '%env(KBIN_STORAGE_URL)%'

# Grab the default theme to use from the MBIN_DEFAULT_THEME env var
# with a fall back of light/dark auto detection based on user setting
default_theme: default
mbin_default_theme: "%env(default:default_theme:MBIN_DEFAULT_THEME)%"
mbin_default_theme: '%env(default:default_theme:MBIN_DEFAULT_THEME)%'

amazon.s3.key: "%env(S3_KEY)%"
amazon.s3.secret: "%env(S3_SECRET)%"
amazon.s3.bucket: "%env(S3_BUCKET)%"
amazon.s3.region: "%env(S3_REGION)%"
amazon.s3.version: "%env(S3_VERSION)%"
amazon.s3.endpoint: "%env(S3_ENDPOINT)%"
amazon.s3.key: '%env(S3_KEY)%'
amazon.s3.secret: '%env(S3_SECRET)%'
amazon.s3.bucket: '%env(S3_BUCKET)%'
amazon.s3.region: '%env(S3_REGION)%'
amazon.s3.version: '%env(S3_VERSION)%'
amazon.s3.endpoint: '%env(S3_ENDPOINT)%'

hcaptcha_site_key: "%env(resolve:HCAPTCHA_SITE_KEY)%"
hcaptcha_secret: "%env(resolve:HCAPTCHA_SECRET)%"
hcaptcha_site_key: '%env(resolve:HCAPTCHA_SITE_KEY)%'
hcaptcha_secret: '%env(resolve:HCAPTCHA_SECRET)%'

oauth_azure_id: "%env(default::OAUTH_AZURE_ID)%"
oauth_azure_secret: "%env(OAUTH_AZURE_SECRET)%"
oauth_azure_tenant: "%env(OAUTH_AZURE_TENANT)%"
oauth_azure_id: '%env(default::OAUTH_AZURE_ID)%'
oauth_azure_secret: '%env(OAUTH_AZURE_SECRET)%'
oauth_azure_tenant: '%env(OAUTH_AZURE_TENANT)%'

oauth_facebook_id: "%env(default::OAUTH_FACEBOOK_ID)%"
oauth_facebook_secret: "%env(OAUTH_FACEBOOK_SECRET)%"
oauth_facebook_id: '%env(default::OAUTH_FACEBOOK_ID)%'
oauth_facebook_secret: '%env(OAUTH_FACEBOOK_SECRET)%'

oauth_google_id: "%env(default::OAUTH_GOOGLE_ID)%"
oauth_google_secret: "%env(OAUTH_GOOGLE_SECRET)%"
oauth_google_id: '%env(default::OAUTH_GOOGLE_ID)%'
oauth_google_secret: '%env(OAUTH_GOOGLE_SECRET)%'

oauth_discord_id: "%env(default::OAUTH_DISCORD_ID)%"
oauth_discord_secret: "%env(OAUTH_DISCORD_SECRET)%"
oauth_discord_id: '%env(default::OAUTH_DISCORD_ID)%'
oauth_discord_secret: '%env(OAUTH_DISCORD_SECRET)%'

oauth_github_id: "%env(default::OAUTH_GITHUB_ID)%"
oauth_github_secret: "%env(OAUTH_GITHUB_SECRET)%"
oauth_github_id: '%env(default::OAUTH_GITHUB_ID)%'
oauth_github_secret: '%env(OAUTH_GITHUB_SECRET)%'

oauth_privacyportal_id: "%env(default::OAUTH_PRIVACYPORTAL_ID)%"
oauth_privacyportal_secret: "%env(OAUTH_PRIVACYPORTAL_SECRET)%"
oauth_privacyportal_id: '%env(default::OAUTH_PRIVACYPORTAL_ID)%'
oauth_privacyportal_secret: '%env(OAUTH_PRIVACYPORTAL_SECRET)%'

oauth_keycloak_id: "%env(default::OAUTH_KEYCLOAK_ID)%"
oauth_keycloak_secret: "%env(OAUTH_KEYCLOAK_SECRET)%"
oauth_keycloak_uri: "%env(OAUTH_KEYCLOAK_URI)%"
oauth_keycloak_realm: "%env(OAUTH_KEYCLOAK_REALM)%"
oauth_keycloak_version: "%env(OAUTH_KEYCLOAK_VERSION)%"
oauth_keycloak_id: '%env(default::OAUTH_KEYCLOAK_ID)%'
oauth_keycloak_secret: '%env(OAUTH_KEYCLOAK_SECRET)%'
oauth_keycloak_uri: '%env(OAUTH_KEYCLOAK_URI)%'
oauth_keycloak_realm: '%env(OAUTH_KEYCLOAK_REALM)%'
oauth_keycloak_version: '%env(OAUTH_KEYCLOAK_VERSION)%'

oauth_simplelogin_id: "%env(default::OAUTH_SIMPLELOGIN_ID)%"
oauth_simplelogin_secret: "%env(OAUTH_SIMPLELOGIN_SECRET)%"
oauth_simplelogin_id: '%env(default::OAUTH_SIMPLELOGIN_ID)%'
oauth_simplelogin_secret: '%env(OAUTH_SIMPLELOGIN_SECRET)%'

oauth_zitadel_id: "%env(default::OAUTH_ZITADEL_ID)%"
oauth_zitadel_secret: "%env(OAUTH_ZITADEL_SECRET)%"
oauth_zitadel_base_url: "%env(OAUTH_ZITADEL_BASE_URL)%"
oauth_zitadel_id: '%env(default::OAUTH_ZITADEL_ID)%'
oauth_zitadel_secret: '%env(OAUTH_ZITADEL_SECRET)%'
oauth_zitadel_base_url: '%env(OAUTH_ZITADEL_BASE_URL)%'

oauth_authentik_id: "%env(default::OAUTH_AUTHENTIK_ID)%"
oauth_authentik_secret: "%env(OAUTH_AUTHENTIK_SECRET)%"
oauth_authentik_base_url: "%env(OAUTH_AUTHENTIK_BASE_URL)%"
oauth_authentik_id: '%env(default::OAUTH_AUTHENTIK_ID)%'
oauth_authentik_secret: '%env(OAUTH_AUTHENTIK_SECRET)%'
oauth_authentik_base_url: '%env(OAUTH_AUTHENTIK_BASE_URL)%'


router.request_context.host: "%env(KBIN_DOMAIN)%"
router.request_context.host: '%env(KBIN_DOMAIN)%'
router.request_context.scheme: https

html5_validation: true
Expand All @@ -88,16 +87,16 @@ parameters:

stats_type: general|content|votes

number_regex: "[1-9][0-9]{0,17}"
number_regex: '[1-9][0-9]{0,17}'
username_regex: '\w{2,25}|!deleted\d+'

uploads_dir_name: "media"
uploads_base_url: "/"
uploads_dir_name: 'media'
uploads_base_url: '/'

mercure_public_url: "%env(MERCURE_PUBLIC_URL)%"
mercure_subscriptions_token: "%env(MERCURE_JWT_SECRET)%"
mercure_public_url: '%env(MERCURE_PUBLIC_URL)%'
mercure_subscriptions_token: '%env(MERCURE_JWT_SECRET)%'

sso_only_mode: "%env(bool:default::SSO_ONLY_MODE)%"
sso_only_mode: '%env(bool:default::SSO_ONLY_MODE)%'

exif_default_uploaded: 'sanitize'
exif_default_external: 'none'
Expand All @@ -107,7 +106,7 @@ parameters:
exif_exiftool_path: '%env(default::EXIF_EXIFTOOL_PATH)%'
exif_exiftool_timeout: '%env(int:default::EXIF_EXIFTOOL_TIMEOUT)%'

max_image_bytes: "%env(int:default:max_image_bytes_default:MAX_IMAGE_BYTES)%"
max_image_bytes: '%env(int:default:max_image_bytes_default:MAX_IMAGE_BYTES)%'
max_image_bytes_default: 6000000

mbin_downvotes_mode_default: 'enabled'
Expand All @@ -119,41 +118,41 @@ services:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
bind:
$kbinDomain: "%kbin_domain%"
$html5Validation: "%html5_validation%"
$uploadedAssetsBaseUrl: "%uploads_base_url%"
$mercurePublicUrl: "%mercure_public_url%"
$mercureSubscriptionsToken: "%mercure_subscriptions_token%"
$kbinApiItemsPerPage: "%kbin_api_items_per_page%"
$storageUrl: "%kbin_storage_url%"
$publicDir: "%kernel.project_dir%/public"
$kbinDomain: '%kbin_domain%'
$html5Validation: '%html5_validation%'
$uploadedAssetsBaseUrl: '%uploads_base_url%'
$mercurePublicUrl: '%mercure_public_url%'
$mercureSubscriptionsToken: '%mercure_subscriptions_token%'
$kbinApiItemsPerPage: '%kbin_api_items_per_page%'
$storageUrl: '%kbin_storage_url%'
$publicDir: '%kernel.project_dir%/public'

kbin.s3_client:
class: Aws\S3\S3Client
arguments:
- version: "%amazon.s3.version%"
region: "%amazon.s3.region%"
endpoint: "%amazon.s3.endpoint%"
- version: '%amazon.s3.version%'
region: '%amazon.s3.region%'
endpoint: '%amazon.s3.endpoint%'
#use_path_style_endpoint: true
credentials:
key: "%amazon.s3.key%"
secret: "%amazon.s3.secret%"
key: '%amazon.s3.key%'
secret: '%amazon.s3.secret%'
#proxies: [ 'https://media.domain.tld' ]

# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: "../src/"
resource: '../src/'
exclude:
- "../src/DependencyInjection/"
- "../src/Entity/"
- "../src/Kernel.php"
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'

# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones
App\Controller\:
resource: "../src/Controller/"
tags: ["controller.service_arguments"]
resource: '../src/Controller/'
tags: ['controller.service_arguments']

# App\Http\RequestDTOResolver:
# arguments:
Expand All @@ -164,24 +163,24 @@ services:
# Instance settings
App\Service\SettingsManager:
arguments:
$kbinTitle: "%kbin_title%"
$kbinMetaTitle: "%kbin_meta_title%"
$kbinMetaDescription: "%kbin_meta_description%"
$kbinMetaKeywords: "%kbin_meta_keywords%"
$kbinDefaultLang: "%kbin_default_lang%"
$kbinContactEmail: "%kbin_contact_email%"
$kbinSenderEmail: "%kbin_sender_email%"
$mbinDefaultTheme: "%mbin_default_theme%"
$kbinJsEnabled: "%env(bool:KBIN_JS_ENABLED)%"
$kbinFederationEnabled: "%env(bool:KBIN_FEDERATION_ENABLED)%"
$kbinRegistrationsEnabled: "%env(bool:KBIN_REGISTRATIONS_ENABLED)%"
$kbinHeaderLogo: "%env(bool:KBIN_HEADER_LOGO)%"
$kbinCaptchaEnabled: "%env(bool:KBIN_CAPTCHA_ENABLED)%"
$kbinFederationPageEnabled: "%env(bool:KBIN_FEDERATION_PAGE_ENABLED)%"
$kbinAdminOnlyOauthClients: "%env(bool:KBIN_ADMIN_ONLY_OAUTH_CLIENTS)%"
$mbinSsoOnlyMode: "%sso_only_mode%"
$maxImageBytes: "%max_image_bytes%"
$mbinDownvotesMode: "%mbin_downvotes_mode%"
$kbinTitle: '%kbin_title%'
$kbinMetaTitle: '%kbin_meta_title%'
$kbinMetaDescription: '%kbin_meta_description%'
$kbinMetaKeywords: '%kbin_meta_keywords%'
$kbinDefaultLang: '%kbin_default_lang%'
$kbinContactEmail: '%kbin_contact_email%'
$kbinSenderEmail: '%kbin_sender_email%'
$mbinDefaultTheme: '%mbin_default_theme%'
$kbinJsEnabled: '%env(bool:KBIN_JS_ENABLED)%'
$kbinFederationEnabled: '%env(bool:KBIN_FEDERATION_ENABLED)%'
$kbinRegistrationsEnabled: '%env(bool:KBIN_REGISTRATIONS_ENABLED)%'
$kbinHeaderLogo: '%env(bool:KBIN_HEADER_LOGO)%'
$kbinCaptchaEnabled: '%env(bool:KBIN_CAPTCHA_ENABLED)%'
$kbinFederationPageEnabled: '%env(bool:KBIN_FEDERATION_PAGE_ENABLED)%'
$kbinAdminOnlyOauthClients: '%env(bool:KBIN_ADMIN_ONLY_OAUTH_CLIENTS)%'
$mbinSsoOnlyMode: '%sso_only_mode%'
$maxImageBytes: '%max_image_bytes%'
$mbinDownvotesMode: '%mbin_downvotes_mode%'

# Markdown
App\Markdown\Factory\EnvironmentFactory:
Expand All @@ -192,7 +191,7 @@ services:
League\CommonMark\Extension\Strikethrough\StrikethroughExtension: '@League\CommonMark\Extension\Strikethrough\StrikethroughExtension'
League\CommonMark\Extension\Table\TableExtension: '@League\CommonMark\Extension\Table\TableExtension'
App\Markdown\MarkdownExtension: '@App\Markdown\MarkdownExtension'
$config: "%commonmark.configuration%"
$config: '%commonmark.configuration%'

# Language
App\EventListener\LanguageListener:
Expand All @@ -202,7 +201,7 @@ services:
event: kernel.request,
priority: 200,
}
arguments: ["%kbin_default_lang%"]
arguments: ['%kbin_default_lang%']

# Federation
App\EventListener\FederationStatusListener:
Expand Down Expand Up @@ -241,3 +240,8 @@ services:

messenger.failure.add_error_details_stamp_listener:
class: App\Utils\AddErrorDetailsStampListener

# Store session in database using PdoSessionHandler, by providing the DB DSN
Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler:
arguments:
- '%env(DATABASE_URL)%'
27 changes: 27 additions & 0 deletions migrations/Version20240923164233.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

final class Version20240923164233 extends AbstractMigration
{
public function getDescription(): string
{
return 'Introducing the sessions table for session management.';
}

public function up(Schema $schema): void
{
$this->addSql('CREATE TABLE sessions (sess_id VARCHAR(128) NOT NULL, sess_data BYTEA NOT NULL, sess_lifetime INT NOT NULL, sess_time INT NOT NULL, PRIMARY KEY(sess_id))');
$this->addSql('CREATE INDEX sess_lifetime_idx ON sessions (sess_lifetime)');
}

public function down(Schema $schema): void
{
$this->addSql('DROP TABLE sessions');
}
}
3 changes: 0 additions & 3 deletions src/Controller/BoostController.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ public function __construct(
#[IsGranted('ROLE_USER')]
public function __invoke(VotableInterface $subject, Request $request): Response
{
// CSRF is causing a lot of issues, so we disable it for now. See PR: https://github.com/MbinOrg/mbin/pull/1136
// $this->validateCsrf('boost', $request->getPayload()->get('token'));

$this->manager->vote(VotableInterface::VOTE_UP, $subject, $this->getUserOrThrow());

if ($request->isXmlHttpRequest()) {
Expand Down
6 changes: 0 additions & 6 deletions src/Controller/Domain/DomainBlockController.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ public function __construct(
#[IsGranted('ROLE_USER')]
public function block(Domain $domain, Request $request): Response
{
// CSRF is causing a lot of issues, so we disable it for now. See PR: https://github.com/MbinOrg/mbin/pull/1136
// $this->validateCsrf('block', $request->getPayload()->get('token'));

$this->manager->block($domain, $this->getUserOrThrow());

if ($request->isXmlHttpRequest()) {
Expand All @@ -37,9 +34,6 @@ public function block(Domain $domain, Request $request): Response
#[IsGranted('ROLE_USER')]
public function unblock(Domain $domain, Request $request): Response
{
// CSRF is causing a lot of issues, so we disable it for now. See PR: https://github.com/MbinOrg/mbin/pull/1136
// $this->validateCsrf('block', $request->getPayload()->get('token'));

$this->manager->unblock($domain, $this->getUserOrThrow());

if ($request->isXmlHttpRequest()) {
Expand Down
Loading

0 comments on commit 3782397

Please sign in to comment.