zpm-packages/server-access
A Laravel/PHP package from ZPM Packages for managing server access. It offers a simple integration for handling access control and server operations within your application, helping keep access workflows organized, maintainable, and easy to integrate into Laravel projects.
Start by pulling the package and checking what it actually ships before wiring it into anything production-facing:
composer require zpm-packages/server-access
php artisan vendor:publish --provider="ZpmPackages\ServerAccess\ServerAccessServiceProvider"
First places to look, in order:
README.md — confirm the intended scope (IP/host allowlisting, SSH credential handling, server-side gate checks, or some mix). A one-star package with no dependents means the README is the spec.src/ — check for a ServiceProvider, a Facade, and any Http/Middleware directory. The presence or absence of these tells you how opinionated the package is.config/server-access.php (or equivalent) — this is almost always where the real behavior lives. Note every key and every default before you change anything.database/migrations/ — if present, look at what tables it adds (allowed hosts, access logs, credentials). Migrations are the fastest way to infer the package's data model.First use case: gate a single non-critical route (e.g. a /admin/health endpoint) behind the package's middleware or access check, confirm it allows/denies as expected in local and production, then expand. Do not start by applying it globally — you want a rollback path that doesn't require a deploy.
Middleware-first integration Register the package middleware on a route group rather than globally, so you can reason about blast radius:
Route::middleware(['web', 'server-access'])->group(function () {
Route::get('/internal/status', StatusController::class);
});
If the package exposes an alias in its service provider, prefer the alias over the FQCN — the alias survives class renames in minor releases.
Binding the access checker via the container Wrap the package's service in a thin app-level interface so your controllers and jobs depend on your contract, not theirs:
interface ServerAccessChecker {
public function allows(Request $request): bool;
}
// In AppServiceProvider
$this->app->bind(ServerAccessChecker::class, fn ($app) =>
new PackageServerAccessAdapter($app->make(\ZpmPackages\ServerAccess\AccessManager::class))
);
This is the single most valuable pattern for a low-adoption dependency: it gives you one file to change if the package is abandoned or its API shifts.
Gate / Policy composition If the package exposes a boolean check, wrap it in a Gate so the rest of the app uses Laravel-native authorization:
Gate::define('access-server', fn (User $user, Server $server) =>
$user->isAdmin() || app(ServerAccessChecker::class)->allowsFor($server)
);
Then @can('access-server', $server) and $this->authorize(...) work everywhere, and the package becomes an implementation detail of one Gate.
Config-driven allowlists
Keep environment-specific values in config/server-access.php reading from env(), and add the keys to .env.example. Anything hardcoded is a future incident. If the package stores rules in the database, seed them via a dedicated seeder rather than migrations.
Testing Fake the package at the container level in feature tests:
$this->mock(ServerAccessChecker::class, fn ($m) => $m->shouldReceive('allows')->andReturnTrue());
Keep a small, separate suite that exercises the real package against a known allow/deny matrix — that's your canary when you bump versions.
"zpm-packages/server-access": "1.2.3") and treat every upgrade as a code review of the diff, not a composer update.src/ once. Look specifically for: eval, unserialize of untrusted input, shell_exec/exec/proc_open, hardcoded credentials, and anything comparing IPs with str_contains or regex instead of ip2long/inet_pton. For a package literally named "server access," assume the security surface is the whole point.config:cache ordering. If you edit the config after caching, nothing changes and you'll debug for an hour. Always php artisan config:clear in local; make sure deploys run config:cache after publishing.$request->ip() is the proxy. Verify the package uses TrustProxies or check X-Forwarded-For handling explicitly, or you'll allowlist the wrong address in production and lock everyone out.auth, any user-aware logic inside it sees null. Place it after StartSession/Authenticate in bootstrap/app.php (Laravel 11+) or $middlewarePriority (10 and below).ServerAccessDenied, etc.), hook those for logging rather than editing vendor code.Log::warning('server-access denied', ['ip' => ..., 'path' => ...])). The package may or may not log; you don't want to discover a misconfiguration from a support ticket.TrustedProxy + Gate implementation should be a one-afternoon job, not a refactor.How can I help you explore Laravel packages today?