The old names of the class and method were misleading. We don't
actually need any relation. And we don't even need a model instance
as we were returning previously -- the only use of that method was
in TriggerSyncingEvents which would immediately use ::class on the
returned value. Therefore, all we are asking for in this interface
is just the central resource class.
Also move pivot record deletion to that listener and improve tests
The 'tenant pivot records are deleted along with the tenants to which
they belong to' test is failing in this commit -- the listener
for deleting mappings when a *tenant* is deleted is only implemented
in the next commit. The only change done here is to re-add FKs
(necessary for passing *in this commit* in that specific dataset
variant) that were removed from the default test migration as we now
have the DeleteResourceMapping listener that's enabled by default.
Previously, tenant identification middleware was typically specified
for the cloned route by "inheriting" it from the central route, which
necessarily meant that the central route had to also be marked as
universal so it could continue working in the central context --
despite presumably not being usable in the tenant context, thus being
universal for no proper reason. In such cases, universal routes were
used mainly as a mechanism for specifying the tenant identification
middleware to use on the cloned tenant route.
Given that recent refactors of the cloning feature have made it more
customizable and a bit nicer to use "multiple times", i.e. run handle()
with a few different configurations of the action, letting the
developer specify the used tenant middleware using a method like this
only makes sense.
The feature also becomes more independently usable and not just a
"hack for universal routes with path identification".
In such a case, the cloned route will actually *override* the original
route, rather than being unused as the original docblock claimed.
Also adds a static make() function for convenience.
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>