Previously, if a universal route was cloned without a
cloneRoutesWithMiddleware(['universal']) call, i.e. it had both
'clone' and 'universal' flags, with only the former triggering cloning,
the 'universal' flag would be included in the middleware of the cloned
route.
Now, we make sure to remove all context flags -- central, tenant,
universal -- in the first step of processing middleware, before adding
just 'tenant'.
`?` parameters are not supported in these statements, so we have to use
string interpolation like in other related code.
---------
Co-authored-by: Samuel Štancl <samuel@archte.ch>
This is because the CreateTenantStorage listener only runs when
a tenant is created, but in multi-server setups the directory may
need to be created each time a tenant is *used*, not just created.
Also changed the listeners to use TenantEvent instead of specific
events, to make it possible to use them with other events, such as
TenancyBootstrapped.
Also update permission bits in a few mkdir() calls to better scope
data to the current OS user.
Also fix a typo in CacheTenancyBootstrapper (exception message).
This PR makes the expired/invalid tenant impersonation tokens get
deleted instead of just aborting with 403.
The PR also adds a command (ClearExpiredImpersonationTokens) used like
`php artisan tenants:purge-impersonation-tokens`. As the name suggests,
it clears all expired impersonation tokens (= tokens older than
`UserImpersonation::$ttl`).
Resolves#1348
---------
Co-authored-by: Samuel Štancl <samuel@archte.ch>
Before, when using UrlGeneratorBootstrapper, and your app had a
`https://` url, in tenant context, the url would have the `http://`
scheme.
Now, the bootstrapper makes sure that the TenancyUrlGenerator inherits
the original UrlGenerator's scheme. So if your app has e.g. url
"https://some-url.test", `route('home')` in tenant context will return
"http**s**://some-url.test/home" (originally, you'd get
"http://some-url.test/home" - the original scheme - https - wouldn't be
respected in the tenant context).
This PR addresses the issue reported on Discord
(https://discord.com/channels/976506366502006874/976506736120823909/1399012794514411621).
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Samuel Štancl <samuel@archte.ch>
This method lets the user specify default values for custom
non-nullable columns. The primary use case is when the tenants table
has a column like 'slug' and createPending() is called with no
value for 'slug'. This would produce an exception due to the column
having no default value.
Here, getPendingAttributes() can set an initial dummy slug (like a
randomly generated string) before it's overwritten during a pull.
getPendingAttributes() accepts an $attributes array which corresponds
to the attributes passed to createPending(). The array returned from
getPendingAttributes() is ultimately merged with $attributes, so
the user doesn't need to use the $attributes value in
getPendingAttributes(), however it serves to provide more context when
the pending attributes might be dependent on $attributes and therefore
derived from the $attributes actually being used.
Also fixed the `finally` branch in createPending() as it was
potentially referencing the $tenant variable before it was initialized.
This commit adds support for building a docker image based on PHP 8.5
(RC). It also removes some unused code in tests that was triggering
deprecation warnings. For similar deprecation warnings coming from
testbench we have a temporary patch script until this is resolved
upstream.
This commit also adds logic to the DisallowSqliteAttach feature
leveraging the new native setAuthorizer() method, instead of loading
a compiled extension.
We also remove the unused `php` parameter from ci.yml
From the perspective of the master branch, this commit merges in a
few small breaking changes from the dev branch:
6b0066c5ef
- Make pullPendingFromPool() $firstOrCreate arg default to false
(pullPending() is now a direct alias for pullPendingFromPool() with
default $firstOrCreate=true)
- See full commit message for other changes. They shouldn't be breaking
though.
13a2209f11
- Remove $WAL static property. We instead just let Laravel use its
journal_mode config now
This merge also adds a deprecation:
b320f8f33d
- Deprecate TenantConfig feature in favor of TenantConfigBootstrapper
This commit also corrects an Event::fake() call in a separate test, as
general Event::fake() calls without specified events can lead to
incorrect (and difficult to debug) behavior in some cases, since
Tenancy depends on the event system being functional.
The feature was pretty much a soft-bootstrapper -- it listened
to both Bootstrapped and Reverted. Bootstrappers have a few more
protections in terms of error handling and safe reverting, so there's
no point in (badly) re-implementing bootstrapper functionality within
TenantConfig just so it could be a Feature.
Going forward, all Features should be things that are mostly agnostic
of the tenant state, and especially they should not use bootstrapped/
reverted events. Bootstrappers are simply more appropriate and safe.
- (BC BREAK) Remove $WAL static property. We instead just let
Laravel use its journal_mode config now
- Remove journal, wal, and shm files when deleting tenant DB
- Check that the system is 64-bit when using NoAttach (we don't
build 32 bit extensions)
- Use local static instead of a class static property for caching
loadExtensionSupported
Features are now *always* bootstrapped, even if Tenancy is not resolved
from the container.
Previous implementations include
https://github.com/tenancy-for-laravel/v4/pull/19https://github.com/archtechx/tenancy/pull/1021
Bug originally reported here
https://github.com/archtechx/tenancy/issues/949
This implementation is much simpler, we do not distinguish between
features that should be "always bootstrapped" and features that should
only be bootstrapped after Tenancy is resolved. All features should work
without issues if they're bootstrapped when TSP::boot() is called. We
also add a Tenancy::bootstrapFeatures() method that can be used to
bootstrap any features dynamically added at runtime that weren't
bootstrapped in TSP::boot(). The function keeps track of which features
were already bootstrapped so it doesn't bootstrap them again.
The only potentialy risky thing in this implementation is that we're now
resolving Tenancy in TSP::boot() (previously Tenancy was not being
resolved) but that shouldn't be causing any issues.
* Fix ViteBundler not affecting Vite static calls
Replace custom Vite class override with Vite::createAssetPathsUsing() to ensure ViteBundler works for both container and static usage when asset_helper_override is enabled.
Fixes#1388
* Remove redundant logic from tests
* Simplify test further
* Re-add file creation logic
---------
Co-authored-by: Samuel Štancl <samuel@archte.ch>
* Make the `--database` option passed to `tenants:migrate` use the passed connection as the tenant connection template
* Reset template connection regardless of process count
---------
Co-authored-by: Samuel Štancl <samuel@archte.ch>
* Initial implementation (lukinovec)
* Make sure DatabaseCacheBootstrapper runs after DatabaseTenancyBootstrapper, misc wip changes
* Fix withTenantDatabases()
* Add failing test (GlobalCacheTest)
* Configure globalCache's DB stores to use central connection instead of default connection every time it's reinstantiated
* Make GlobalCache facade not cached. Even though it wasn't causing issues
in our existing tests, it likely was flaky, and making it not $cached
makes it now consistent with global_cache() - always getting a new
CacheManager from the globalCache container binding
* Add database connection assertions in GlobalCacheTest
* Run all cached resolver/global cache tests with DatabaseCacheBootstrapper
* Reset adjustCacheManagerUsing in revert() and TestCase
* Reset static $stores property
* Finalize GlobalCache-related changes
* tests: remove pointless cache TTLs
* Refactor DatabaseCacheBootstrapper
* Refactor tests
Co-authored-by: lukinovec <lukinovec@gmail.com>
* Test encrypted cookie identification
* Add Fortify bootstrapper custom query param passing test
* Correct Fortify route bootstrapper test (todo refactor, convoluted)
* Clarify Fortify bootstrapper test
* Fix encrypted cookie identification test
* Move encrypted cookie assertion to "cookie identification works"
* Cover configured tenant model columns in cached resolver tests
* Refactor testing resolver with default vs custom tenant model name config
* Delete resolved todo
* Make code more concise
* Keep initial formatting (minimize diff noise)
* Make dataset/helper method parameter clearer
* Clarify fortify test
* Clarify assertions, improve comments
* Delete excessive comments, make existing comments consistent and clearer
* Make cached resolver test file clearer, update outdated comments
* Use the tenant model column term consistently
* FIx inconsistencies
* Provide more info in comment
* make comment more clear
* static property reset
---------
Co-authored-by: Samuel Štancl <samuel@archte.ch>
* Add test for the new clone action addTenantParameter property
* Add $addTenantParameter properyt to the clone action
* Fix code style (php-cs-fixer)
* Update clone action annotation
* Make addTenantParameter(false) sound by adding domain() logic to route cloning
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Samuel Štancl <samuel@archte.ch>
* Refactor cloning action, update tests
* Delete redundant "should not be cloned" part from shouldBeCloned()
* Use 'clone' instead of a universal route in tenant parameter removal test
* Improve comment
* Add test for cloneRoutesWithMiddleware(), correct existing tests
* Allow cloning specific routes by name
* Fix typo in CloneActionTest
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* clean up CloneRoutesAsTenant, add a todo
* phpstan
* Add test for handling 'clone' in MW groups
* Improve regression test
* Improve regression test
* Handle nested cloning flags in CloneRoutesAsTenant
* Ignore routes that are already considered tenant routes from cloning, update test accordingly
* Clarify cloning logic
* CloneRoutesAsTenant cleanup
* Rewrite clone action annotation, fix fluent usage bug
* Improve tests (comments, use $tenant->id instead of $tenant->getTenantKey())
* Test that the clone action can be used fluently without issues now (could serve as a regression test for the routesToClone change in previous commit)
* Minor annotation improvements
* Improve route cloning action docblock
* Add note about clearing the $routesToClone property
* improve docblock
* clean up tests
* fix typo
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Samuel Štancl <samuel@archte.ch>
* Add test for syncable models with global scopes
* minor fixes
* Make test clearer
* Improve test name
* Clarify scopeGetModelQuery test, document edge case
* Fix assertion
* Delete extra newline
* Update the scopeGetModelQuery test so that it tests a realistic case
* Clarify test
* cleanup
* Try simplifying the tests
* Revert change to test adding unnecessary complexity
* Make test clear, extensively commented and as simple as possible
* Delete unused import
* Make test clearer
* Polish comments
* Improve comment
* Explicitly reset global scopes on models in beforeEach()
* Simplify comments in test
* Revert changes in test
* add assertion
* add global scope reset to afterEach
---------
Co-authored-by: Samuel Štancl <samuel@archte.ch>
* Add `$forceRls` static property to tenants:rls
* Set `$forceRls` in tests where scoping is tested, add non-superuser, non-bypassrls table owner test
* Move DROP TABLE statement
* Remove try/catch
* Put DROP OWNED BY into try/catch
* Static property cleanup in afterEach
* Make with() matrix syntax more clear by using with() multiple times
* Fix typo, improve comment
* Move and update force RLS comment
* Add test for `$forceRls = false`, refactor BYPASSRLS test
* Update link in test comment
* Add a dataset for `$forceRls` in the table owner test, fix BYPASSRLS test
* Correct PR link comment
* minor fixes
* Add test that makes the bypassrls/forceRls behavior clear
* Delete redundant test
* cleanup
* Update tests/RLS/TableManagerTest.php
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Samuel Štancl <samuel@archte.ch>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Test that route model binding works with path identification (closure-based routes)
* Correct test name
* Update tests/PathIdentificationTest.php
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* make assertions more clear
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Samuel Štancl <samuel@archte.ch>
* UrlGenerator: set defaults based on config; request data: move config to config file+resolver
* Claude code adjustments
* improve request data tests, simplify complex test in UrlGeneratorBootstrapperTest
* url generator test: test changing tenant parameter name
* request data identification: add tenant_model_column configuration
* defaultParameterNames -> passQueryParameter
* move comment
* minor refactor in PathIdentificationTest, expand CLAUDE.md to include early identification section
* Fix COLOR_FLAG
* improve test name
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* TenancyUrlGenerator: add a check for queryParameterName being null
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Fix code style (php-cs-fixer)
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* Skip ScopeSessions MW if the current context is central and the route is universal
* Add regressiont test
* Simplify code
---------
Co-authored-by: Samuel Štancl <samuel@archte.ch>
* Add regression test for removing invalid symlinks
* Move commented RemoveStorageSymlinks job to the DeletingTenant pipeline (better default - the symlinks will be removed *before* deleting tenant storage)
* Remove symlink validity check from symlinkExists() (only check for the symlink's existence)
* Delete complete todo0
* Make the symlink assertions more explicit
* update test name
---------
Co-authored-by: Samuel Štancl <samuel@archte.ch>
* Add Laravel 12 support, drop Laravel 11 support
* Fix RLS tree generation (specify schema name in generateTrees())
* ci fixes, use stable virtualcolumn version
---------
Co-authored-by: lukinovec <lukinovec@gmail.com>
* cleanup, resolve todos, add immediate todos
* Improve path_identification_middleware docblock
* rename leave() method in tests
* wip fix hardcoded values making assumptions about the parameters used in routing
* defaultParameterNames
* fix CreatesDatabaseUsers return values
* $tenant -> tenant()
* resolve more todos
* make comment block a complete block
* Correct useTenantRoutesInFortify(), delete unused import
* test fixes
* remove todos
* remove JobPipeline todo
* simplify comment example
* remove todo
* fix VERSION_PREFIX in queue.yml
---------
Co-authored-by: lukinovec <lukinovec@gmail.com>
* fix 1267: early return in runForMultiple if an empty array is passed
* Test that runForMulltiple runs the passed closure for the right tenants
* Correct comment
---------
Co-authored-by: lukinovec <lukinovec@gmail.com>
* Make RootUrlBootstrapper run ONLY in CLI by default (add $rootUrlOverrideInTests), work with resolved UrlGenerator
* Make resolving 'url' return a pre-created generator instance instead of creating it on every app('url') call
* Take care of doubling tenant keys in TenancyUrlGenerator, add regression test for using UrlGenerator and RootUrl bootstrappers together
* Fix code style (php-cs-fixer)
* refactor RootUrlBootstrapper
* add docblock
* clarify docblock
* simplify test: use concrete values instead of overly dynamic code
* Fix bootstrapper order in test, add url('/') assertion
* Use $this->app instead of app()
* Improve TenancyUrlGenerator and RootUrlBootstrapperTest clarity
* Revert attempt to maintain compatibility between the two bootstrappers
* Delete bootstrapper combining test
* Fix code style (php-cs-fixer)
---------
Co-authored-by: lukinovec <lukinovec@gmail.com>
Co-authored-by: PHP CS Fixer <phpcsfixer@example.com>