['area' => 'procurement', 'areaLabel' => 'Compras', 'label' => 'Compras internas', 'operational' => true], 'procurement.sales' => ['area' => 'procurement', 'areaLabel' => 'Compras', 'label' => 'Venda para terceiros', 'operational' => false], 'people.internal' => ['area' => 'people', 'areaLabel' => 'Pessoas e contratações', 'label' => 'Contratações internas', 'operational' => true], 'people.third_party' => ['area' => 'people', 'areaLabel' => 'Pessoas e contratações', 'label' => 'Contratações para terceiros', 'operational' => false], ]; /** @return list */ public function purchased(User $user): array { if (! in_array(config('work_context.composition'), ['sgc', 'sgf'], true)) { return []; } $keys = config('work_context.entitlements.'.$user->tenant_id, $this->evaluation($user) ? array_keys(self::OPTIONS) : []); if (config('work_context.composition') === 'sgf' && is_array($keys)) { $keys = array_diff($keys, ['people.third_party']); } return is_array($keys) ? array_values(array_intersect(array_keys(self::OPTIONS), $keys)) : []; } /** @return array|null */ public function policy(User $user): ?array { if ($user->tenant_id === null || ! Schema::hasColumn('tenants', 'work_context_policy')) { return null; } $value = DB::table('tenants')->where('id', $user->tenant_id)->value('work_context_policy'); return $value === null ? ($this->evaluation($user) ? ['revision' => 0, 'enabled' => array_keys(self::OPTIONS), 'evaluation_only' => true] : null) : json_decode($value, true, 32, JSON_THROW_ON_ERROR); } private function evaluation(User $user): bool { if (! app()->bound('request') || ! EvaluationNavigation::local(request()) || request()->getPort() !== 8005 || DB::connection()->getDriverName() !== 'pgsql') { return false; } return app(EvaluationAccess::class)->source(request())->tenant_id === $user->tenant_id; } public function enforced(User $user): bool { // Existing installations require explicit, audited activation. // New company delivery initialises an empty policy, never implicit grants. return $this->policy($user) !== null; } /** @return list */ public function available(User $user): array { $enabled = $this->policy($user)['enabled'] ?? []; $granted = []; if (app(OrganizationAccess::class)->active($user->tenant_id)) { $profiles = DB::table('organization_memberships as m')->join('organization_profiles as p', 'p.id', '=', 'm.profile_id') ->where('m.tenant_id', $user->tenant_id)->where('p.tenant_id', $user->tenant_id)->where('m.user_id', $user->id) ->where(fn ($q) => $q->whereNull('m.expires_at')->orWhere('m.expires_at', '>', now()))->get(['p.work_contexts', 'p.permissions']); foreach ($profiles as $profile) { $permissions = json_decode($profile->permissions, true, 32, JSON_THROW_ON_ERROR); foreach (json_decode($profile->work_contexts ?? '[]', true, 32, JSON_THROW_ON_ERROR) as $key) { if ($this->functional($permissions, $key)) { $granted[] = $key; } } } } elseif (app()->environment(['testing', 'payable-testing'])) { // Development roles remain usable locally; never become customer grants. foreach (array_keys(self::OPTIONS) as $key) { if ($this->functional(app(OrganizationAccess::class)->permissions($user), $key)) { $granted[] = $key; } } } return array_values(array_intersect($this->purchased($user), $enabled, array_unique($granted))); } /** @param list $permissions */ private function functional(array $permissions, string $key): bool { if (! isset(self::OPTIONS[$key])) { return false; } $people = array_values(array_filter(array_keys(OrganizationAccess::PERMISSIONS), fn ($p) => str_starts_with($p, 'people_'))); $wanted = self::OPTIONS[$key]['area'] === 'people' ? ['admin', ...$people] : array_diff(array_keys(OrganizationAccess::PERMISSIONS), $people); return array_intersect($permissions, $wanted) !== []; } public function active(Request $request): ?string { $user = $request->user(); $state = $request->session()->get('work_context'); if (! $user instanceof User || ! is_array($state) || ($state['user_id'] ?? null) !== $user->id || ($state['tenant_id'] ?? null) !== $user->tenant_id) { return null; } $key = $state['key'] ?? null; return is_string($key) && in_array($key, $this->available($user), true) ? $key : null; } /** @return array */ public function shared(Request $request): array { $user = $request->user(); $enabled = $user instanceof User && $this->enforced($user); $active = $enabled ? $this->active($request) : null; $permissions = $active !== null ? app(OrganizationAccess::class)->permissions($user, $active) : []; if ($active === 'procurement.internal' && ! app(OrganizationAccess::class)->active($user->tenant_id) && app(OrganizationAccess::class)->canRequest($user)) { $permissions[] = 'requester'; } $paths = []; $sharedPaths = []; foreach (Route::getRoutes()->getRoutes() as $route) { $path = '/'.$route->uri(); if (! str_contains($path, '{') && in_array('GET', $route->methods(), true)) { $area = self::area($path); if ($area === null) { $sharedPaths[] = $path; } if ($area === null || ($active !== null && self::OPTIONS[$active]['operational'] && self::OPTIONS[$active]['area'] === $area)) { $paths[] = $path; } } } return ['enforced' => $enabled, 'active' => $active, 'paths' => $paths, 'sharedPaths' => $sharedPaths, 'permissions' => $permissions, 'options' => self::OPTIONS, 'available' => $enabled ? $this->available($user) : [], 'purchased' => $user instanceof User ? $this->purchased($user) : []]; } public static function area(string $path): ?string { $path = '/'.ltrim(explode('?', $path)[0], '/'); if ($path === '/dashboard' || str_starts_with($path, '/administracao/') || str_starts_with($path, '/empresa') || str_starts_with($path, '/settings') || str_starts_with($path, '/contexto')) { return null; } return $path === '/pessoas' || str_starts_with($path, '/pessoas/') ? 'people' : 'procurement'; } }