File manager - Edit - /var/www/html/homologBancodetalentos/app/Http/Controllers/Web/TalentBank/AuthWebController.php
Back
<?php namespace App\Http\Controllers\Web\TalentBank; use App\Http\Controllers\Controller; use App\Http\Requests\LoginRequest; use App\Http\Services\AuthService; use App\Http\Services\TalentBank\AngraCompanySyncService; use App\Http\Services\TalentBank\AngraSsoProvisioningService; use App\Http\Services\TalentBank\CandidateService; use App\Http\Services\TalentBank\CompanyService; use App\Http\Services\TalentBank\JobService; use App\Http\Services\UserService; use App\Mail\TalentBank\WelcomeCandidateMail; use Illuminate\Support\Facades\Mail; use App\Models\TalentBank\Company; use App\Support\InactivitySessionTracker; use App\Support\SsoWebSession; use App\Support\TalentBank\IntegrationLog; use App\Support\TalentBank\JobFormOptionsResolver; use App\Support\TalentBankBypassAuth; use Illuminate\Http\Client\ConnectionException; use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Http; use Illuminate\Support\Carbon; use Illuminate\View\View; class AuthWebController extends Controller { private AuthService $authService; private UserService $userService; private CandidateService $candidateService; private CompanyService $companyService; private AngraSsoProvisioningService $angraSsoProvisioning; private JobService $jobService; public function __construct() { $this->authService = new AuthService(); $this->userService = new UserService(); $this->candidateService = new CandidateService(); $this->companyService = new CompanyService(); $this->angraSsoProvisioning = new AngraSsoProvisioningService(); $this->jobService = new JobService(); } public function showLogin(Request $request): View|RedirectResponse { // CTAs da home ("Cadastrar currículo" / "Cadastrar empresa") devem poder abrir /entrar mesmo com sessão ativa // (ex.: trocar de perfil ou reautenticar via Conta Angra), sem redirecionar de volta à home. if (SsoWebSession::hasToken() && ! $request->boolean('from_home')) { return redirect()->route('home'); } if ($request->boolean('session_expired')) { session()->flash('warning', __('auth.session_expired_inactivity')); } return view('pages.entrar'); } public function authStatus(Request $request): JsonResponse { $authenticated = SsoWebSession::hasToken(); if (! $authenticated) { return response()->json([ 'authenticated' => false, ]); } $payload = session('sso.user_payload'); $name = is_array($payload) ? trim((string) data_get($payload, 'data.name', '')) : ''; $body = ['authenticated' => true]; if ($name !== '') { $body['name'] = $name; } return response()->json($body); } public function checkLegacy(Request $request): JsonResponse { $email = mb_strtolower(trim((string) $request->input('email', ''))); if ($email === '' || ! filter_var($email, FILTER_VALIDATE_EMAIL)) { return response()->json(['status' => 'none']); } // Já tem cadastro no BT (legado ou novo) — não precisa de aviso $inBt = \App\Models\TalentBank\Candidate::where('email', $email)->exists() || \App\Models\TalentBank\Company::where('email', $email)->exists(); if ($inBt) { return response()->json(['status' => 'legacy']); } // Tem Conta Angra mas nunca acessou o BT $inAngra = DB::connection('mysql_angra') ->table('users') ->where('email', $email) ->exists(); return response()->json(['status' => $inAngra ? 'angra_only' : 'none']); } public function login(LoginRequest $request): RedirectResponse { $docRaw = (string) $request->input('document', ''); $doc = preg_replace('/\D/', '', $docRaw); $isCompany = strlen($doc) >= 14; $response = $this->authService->login($request); if ($response->status() === JsonResponse::HTTP_UNAUTHORIZED) { return back()->withErrors(['document' => __('auth.failed')])->withInput(); } if ($response->status() < 200 || $response->status() >= 300) { return back()->withErrors(['document' => (string) data_get($response->json(), 'message', __('auth.failed'))])->withInput(); } $payload = $response->json() ?? []; SsoWebSession::storeTokens($payload); if (! SsoWebSession::hasToken()) { return back()->withErrors(['document' => __('auth.failed')])->withInput(); } $request->headers->set('Authorization', ($payload['token_type'] ?? 'Bearer').' '.($payload['access_token'] ?? '')); if ($isCompany) { $company = $this->companyService->getByCustom([ ['column' => 'cnpj', 'value' => $doc], ]); if ($company->count() === 0) { $request->merge(['permissions' => ['talent-bank-user-company']]); $this->userService->selfAssignPermission($request); $sessionResponse = $this->authService->session($request); $session = $sessionResponse->json(); $request->merge([ 'cnpj' => data_get($session, 'data.document'), 'trading_name' => data_get($session, 'data.name'), 'email' => data_get($session, 'data.email'), ]); $this->companyService->store($request); } $this->touchCompanyLogin($doc); } else { $candidate = $this->candidateService->getByCustom([ ['column' => 'cpf', 'value' => $doc], ]); if ($candidate->count() === 0) { $request->merge(['permissions' => ['talent-bank-user-candidate']]); $this->userService->selfAssignPermission($request); $sessionResponse = $this->authService->session($request); $session = $sessionResponse->json(); $request->merge([ 'cpf' => data_get($session, 'data.document'), 'name' => data_get($session, 'data.name'), 'email' => data_get($session, 'data.email'), ]); $newCandidate = $this->candidateService->store($request); $newCandidate->markProfileComplete(); if ($newCandidate->email) { try { Mail::to($newCandidate->email)->queue(new WelcomeCandidateMail($newCandidate)); } catch (\Throwable $e) { \Log::warning('Falha ao enfileirar e-mail de boas-vindas: ' . $e->getMessage()); } } } } $this->refreshSsoUserPayload($request); $withSuccessFlash = ! $request->boolean('_skip_post_login_success'); if ($isCompany) { $redirect = redirect()->route('talent-bank.company.dashboard'); } else { $redirect = redirect()->route('talent-bank.candidate.profile'); } return $withSuccessFlash ? $redirect->with('success', __('validation.success')) : $redirect; } public function logout(Request $request): RedirectResponse { $this->clearInactivityTracker($request); $hasSyntheticSession = SsoWebSession::isLocalDevSession() || SsoWebSession::isAngraSsoSession(); if (SsoWebSession::hasToken() && ! $hasSyntheticSession) { $token = session(SsoWebSession::ACCESS_TOKEN); $request->headers->set('Authorization', 'Bearer '.$token); try { $this->authService->logout($request); } catch (\Exception $e) { // SSO unreachable — still clear local session } } SsoWebSession::forget(); return redirect()->route('home'); } public function logoutCompleto(Request $request): JsonResponse { $this->clearInactivityTracker($request); $hasSyntheticSession = SsoWebSession::isLocalDevSession() || SsoWebSession::isAngraSsoSession(); if (SsoWebSession::hasToken() && ! $hasSyntheticSession) { $token = session(SsoWebSession::ACCESS_TOKEN); $request->headers->set('Authorization', 'Bearer '.$token); try { $this->authService->logout($request); } catch (\Exception $e) { // SSO unreachable — still clear local session } } SsoWebSession::forget(); return response()->json(['ok' => true]); } public function touchActivity(Request $request): JsonResponse { $timeoutMinutes = (int) config('talent_bank.inactivity_timeout_minutes', 60); $token = InactivitySessionTracker::resolveToken($request); if ($timeoutMinutes > 0 && is_string($token) && $token !== '') { InactivitySessionTracker::touch( InactivitySessionTracker::cacheKey($request, $token), $timeoutMinutes ); } return response()->json(['ok' => true]); } public function showRegisterCandidate(): View|RedirectResponse { if (SsoWebSession::hasToken()) { return redirect()->route('home'); } return view('pages.cadastre-se'); } public function registerCandidate(Request $request): RedirectResponse { $docSource = $request->input('document', '') ?: $request->input('cpf', ''); $request->merge([ 'document' => preg_replace('/\D/', '', (string) $docSource), ]); if (! $request->filled('name') && $request->has('full_name')) { $request->merge(['name' => $request->input('full_name')]); } $request->validate([ 'document' => 'required|string|size:11', 'password' => 'required|string|confirmed', 'email' => 'required|email|confirmed', 'name' => 'required|string', ]); if (config('talent_bank.local_register')) { return $this->registerCandidateWithoutSso($request); } try { DB::beginTransaction(); $request->merge([ 'permissions' => ['talent-bank-user-candidate'], 'user_type_id' => 2, 'first_access' => 0, ]); $response = $this->authService->register($request); if ($response->status() === JsonResponse::HTTP_UNPROCESSABLE_ENTITY) { DB::rollBack(); return back()->withErrors(AuthService::validationErrorsFromRegisterResponse($response))->withInput(); } if ($response->status() < 200 || $response->status() >= 300) { DB::rollBack(); return back()->withErrors(AuthService::validationErrorsFromRegisterResponse($response))->withInput(); } $request->merge(['cpf' => $request->input('document')]); $registeredCandidate = $this->candidateService->store($request); // Cadastro novo completo — perfil já está concluído desde o início. $registeredCandidate->markProfileComplete(); DB::commit(); if ($registeredCandidate->email) { try { Mail::to($registeredCandidate->email)->queue(new WelcomeCandidateMail($registeredCandidate)); } catch (\Throwable $e) { \Log::warning('Falha ao enfileirar e-mail de boas-vindas: ' . $e->getMessage()); } } } catch (\Exception $e) { DB::rollBack(); return back()->withErrors(['document' => $e->getMessage()])->withInput(); } $loginRequest = LoginRequest::createFrom($request); $loginRequest->merge([ 'document' => $request->input('document'), 'password' => $request->input('password'), '_skip_post_login_success' => true, ]); return $this->login($loginRequest); } public function showRegisterCompany(): View|RedirectResponse { if (SsoWebSession::hasToken()) { return redirect()->route('home'); } return view('pages.cadastro-empresa', [ 'formOptions' => JobFormOptionsResolver::forView(), ]); } public function registerCompany(Request $request): RedirectResponse { $request->merge([ 'document' => preg_replace('/\D/', '', $request->input('document', '')), ]); $request->validate([ 'document' => 'required|string|size:14', 'password' => 'required|string|confirmed', 'email' => 'required|email|confirmed', 'name' => 'required|string', ]); if (config('talent_bank.local_register')) { return $this->registerCompanyWithoutSso($request); } try { DB::beginTransaction(); $request->merge([ 'permissions' => ['talent-bank-user-company'], 'user_type_id' => 2, 'first_access' => 0, 'module_name' => config('talent_bank.module_name'), ]); $response = $this->authService->register($request); if ($response->status() === JsonResponse::HTTP_UNPROCESSABLE_ENTITY) { DB::rollBack(); return back()->withErrors(AuthService::validationErrorsFromRegisterResponse($response))->withInput(); } if ($response->status() < 200 || $response->status() >= 300) { DB::rollBack(); return back()->withErrors(AuthService::validationErrorsFromRegisterResponse($response))->withInput(); } $request->merge([ 'cnpj' => $request->input('document'), 'corporate_name' => $request->input('name'), ]); $company = $this->companyService->store($request); $this->storeSubmittedVacancies($request, $company); DB::commit(); } catch (\Exception $e) { DB::rollBack(); return back()->withErrors(['document' => $e->getMessage()])->withInput(); } (new AngraCompanySyncService())->syncToAngra( (string) $request->input('document'), (string) $request->input('name'), (string) $request->input('email'), Hash::make((string) $request->input('password')) ); $loginRequest = LoginRequest::createFrom($request); $loginRequest->merge([ 'document' => $request->input('document'), 'password' => $request->input('password'), '_skip_post_login_success' => true, ]); return $this->login($loginRequest); } public function ssoCallback(Request $request): JsonResponse { $request->validate([ 'code' => ['required', 'string'], 'state' => ['required', 'string'], ]); $exchangeUrl = (string) config('services.angra_sso.exchange_url'); $httpClient = Http::acceptJson() ->timeout((int) config('talent_bank.http_timeout_seconds', 20)) ->retry( (int) config('talent_bank.http_retry_times', 2), (int) config('talent_bank.http_retry_sleep_ms', 350), static function ($exception): bool { return $exception instanceof ConnectionException; } ); // Evita que HTTP_PROXY corporativo (ex.: Squid) intercepte chamadas ao portal local (localhost/127.0.0.1). if (preg_match('#^https?://(localhost|127\.0\.0\.1)(:\d+)?(/|$)#i', $exchangeUrl)) { $httpClient = $httpClient->withOptions(['proxy' => '']); } $cid = request()->attributes->get('correlation_id'); if (is_string($cid) && $cid !== '') { $httpClient = $httpClient->withHeaders(['X-Correlation-ID' => $cid]); } try { $exchangeResponse = $httpClient->post($exchangeUrl, [ 'code' => $request->input('code'), 'secret' => config('services.angra_sso.secret'), ]); } catch (ConnectionException $e) { IntegrationLog::error('angra_sso', 'exchange_connection_failed', [ 'message' => $e->getMessage(), ]); return response()->json(['error' => __('angra_sso.exchange_failed')], 503); } if (! $exchangeResponse->successful()) { $status = $exchangeResponse->status(); IntegrationLog::warning('angra_sso', 'exchange_http_error', [ 'http_status' => $status, ]); $message = match (true) { $status === JsonResponse::HTTP_UNAUTHORIZED => __('angra_sso.exchange_unauthorized'), $status === JsonResponse::HTTP_NOT_FOUND => __('angra_sso.exchange_not_found'), default => __('angra_sso.exchange_failed'), }; return response()->json(['error' => $message], $status >= 400 && $status < 600 ? $status : JsonResponse::HTTP_BAD_GATEWAY); } $userData = $exchangeResponse->json(); $cpf = (string) ($userData['cpf'] ?? ''); $name = (string) ($userData['name'] ?? ''); $email = (string) ($userData['email'] ?? ''); $phoneRaw = (string) ($userData['phone'] ?? ''); $doc = preg_replace('/\D/', '', $cpf); if ($doc === '' || trim($email) === '') { return response()->json(['error' => __('angra_sso.invalid_user_data')], JsonResponse::HTTP_UNPROCESSABLE_ENTITY); } $isCompany = strlen($doc) >= 14; try { DB::beginTransaction(); if ($isCompany) { $isNew = $this->angraSsoProvisioning->syncCompany($doc, $name, $email); $permission = 'talent-bank-user-company'; if (! $isNew) { $this->touchCompanyLogin($doc); } } else { $phoneDigits = $phoneRaw !== '' ? preg_replace('/\D/', '', $phoneRaw) : null; $isNew = $this->angraSsoProvisioning->syncCandidate($doc, $name, $email, $phoneDigits); $permission = 'talent-bank-user-candidate'; } DB::commit(); } catch (\Exception $e) { DB::rollBack(); report($e); return response()->json(['error' => __('angra_sso.provisioning_failed')], JsonResponse::HTTP_INTERNAL_SERVER_ERROR); } SsoWebSession::bootstrapAngraSsoUser($doc, $name, $email, $permission); if ($isNew) { // Novo no BT: candidato vai para conclusão de cadastro; empresa vai para alterar-cadastro com aviso. $redirectUrl = $isCompany ? route('talent-bank.company.edit', ['new_account' => '1']) : route('talent-bank.candidate.concluir-cadastro'); } else { // Já existia (legado migrado ou acesso anterior): vai direto para a área. // O middleware profile.complete cuida de candidatos com perfil incompleto. $redirectUrl = $isCompany ? route('talent-bank.company.dashboard') : route('talent-bank.candidate.profile'); } return response()->json(['success' => true, 'redirect_url' => $redirectUrl]); } protected function refreshSsoUserPayload(Request $request): void { if (SsoWebSession::isLocalDevSession() || SsoWebSession::isAngraSsoSession() || TalentBankBypassAuth::enabled()) { return; } try { $response = $this->authService->session($request); if ($response->status() === JsonResponse::HTTP_OK) { session(['sso.user_payload' => $response->json()]); } } catch (ConnectionException $e) { IntegrationLog::warning('sso.web', 'refresh_payload_connection_failed', [ 'message' => $e->getMessage(), ]); } catch (\Exception $e) { // SSO unreachable — payload stays stale } } private function registerCandidateWithoutSso(Request $request): RedirectResponse { try { DB::beginTransaction(); $request->merge(['cpf' => $request->input('document')]); $candidate = $this->candidateService->store($request); $candidate->markProfileComplete(); DB::commit(); } catch (\Exception $e) { DB::rollBack(); return back()->withErrors(['document' => $e->getMessage()])->withInput(); } SsoWebSession::bootstrapLocalTalentBankUser( (string) $request->input('document'), (string) $request->input('name'), (string) $request->input('email'), 'talent-bank-user-candidate', ); return redirect()->route('talent-bank.candidate.profile'); } private function registerCompanyWithoutSso(Request $request): RedirectResponse { try { DB::beginTransaction(); $request->merge([ 'cnpj' => $request->input('document'), 'corporate_name' => $request->input('name'), 'module_name' => config('talent_bank.module_name'), ]); $company = $this->companyService->store($request); $this->storeSubmittedVacancies($request, $company); DB::commit(); } catch (\Exception $e) { DB::rollBack(); return back()->withErrors(['document' => $e->getMessage()])->withInput(); } (new AngraCompanySyncService())->syncToAngra( (string) $company->cnpj, (string) $request->input('name'), (string) $request->input('email'), Hash::make((string) $request->input('password')) ); SsoWebSession::bootstrapLocalTalentBankUser( (string) $company->cnpj, (string) $request->input('name'), (string) $request->input('email'), 'talent-bank-user-company', ); return redirect()->route('talent-bank.company.dashboard'); } private function clearInactivityTracker(Request $request): void { $token = InactivitySessionTracker::resolveToken($request); if (is_string($token) && $token !== '') { InactivitySessionTracker::clear(InactivitySessionTracker::cacheKey($request, $token)); } } private function storeSubmittedVacancies(Request $request, Company $company): void { if (! $request->boolean('has_vacancies')) { return; } $vacancies = $request->input('vacancies', []); if (! is_array($vacancies) || $vacancies === []) { return; } $candidateOptions = $this->candidateService->getOptions(); $jobOptions = $this->jobService->getOptions(); foreach ($vacancies as $vacancy) { if (! is_array($vacancy)) { continue; } $payload = $this->mapVacancyPayload($vacancy, $candidateOptions, $jobOptions); // Mantém o fluxo de cadastro resiliente: só cria vaga quando os vínculos obrigatórios existem. if (! $payload['occupation_id'] || ! $payload['salary_range_id'] || ! $payload['contract_type_id']) { continue; } $this->jobService->store(new Request($payload), $company); } } /** * @param array<string, mixed> $vacancy * @param array<string, mixed> $candidateOptions * @param array<string, mixed> $jobOptions * @return array<string, mixed> */ private function mapVacancyPayload(array $vacancy, array $candidateOptions, array $jobOptions): array { $description = trim((string) ($vacancy['description'] ?? '')); return [ 'occupation_id' => JobFormOptionsResolver::optionId($candidateOptions['occupation'] ?? [], $vacancy['role'] ?? null), 'salary_range_id' => JobFormOptionsResolver::optionId($jobOptions['salaryRange'] ?? [], $vacancy['salary'] ?? null), 'contract_type_id' => JobFormOptionsResolver::optionId($jobOptions['contractType'] ?? [], $vacancy['contract'] ?? null), 'modality_id' => JobFormOptionsResolver::optionId($jobOptions['modality'] ?? [], $vacancy['modality'] ?? null), 'expertise_level_id' => JobFormOptionsResolver::optionId($jobOptions['expertiseLevel'] ?? [], $vacancy['experience_level'] ?? null), 'neighbourhood_id' => JobFormOptionsResolver::optionId($candidateOptions['neighbourhood'] ?? [], $vacancy['neighborhood'] ?? null), 'workload_id' => JobFormOptionsResolver::optionId($jobOptions['workload'] ?? [], $vacancy['workload'] ?? null), 'hide_company' => ((string) ($vacancy['hide_company'] ?? '0')) === '1' ? 1 : 0, 'is_hiring' => ((string) ($vacancy['hiring'] ?? '1')) === '1' ? 1 : 0, 'spots_total' => max(1, (int) ($vacancy['total'] ?? 1)), 'spots_filled' => max(0, (int) ($vacancy['filled'] ?? 0)), 'description' => $description !== '' ? $description : 'Sem descrição informada.', 'requirements_mandatory' => (string) ($vacancy['required_skills'] ?? ''), 'requirements_optional' => (string) ($vacancy['optional_skills'] ?? ''), 'benefits' => (string) ($vacancy['benefits'] ?? ''), 'misc_info' => (string) ($vacancy['other_info'] ?? ''), ]; } private function touchCompanyLogin(string $cnpj): void { try { $company = Company::where('cnpj', $cnpj)->first(); if ($company === null) { return; } $deactivatedByInactivity = $company->jobListings() ->where('is_hiring', false) ->where('deactivated_by_inactivity', true) ->count(); if ($deactivatedByInactivity > 0) { $company->jobListings() ->where('is_hiring', false) ->where('deactivated_by_inactivity', true) ->update(['is_hiring' => true, 'deactivated_by_inactivity' => false]); session()->flash( 'info', "{$deactivatedByInactivity} vaga(s) foram reativadas após o seu retorno. Revise-as e confirme as que ainda estão disponíveis." ); } $company->update(['last_login_at' => Carbon::now()]); } catch (\Throwable $e) { \Log::warning('touchCompanyLogin falhou: ' . $e->getMessage()); } } }
| ver. 1.4 |
Github
|
.
| PHP 8.3.28 | Generation time: 0.02 |
proxy
|
phpinfo
|
Settings