Some checks failed
continuous-integration/drone/push Build is failing
- Complete GGZ Ecademy Laravel backend application - RESTful API for learning products, members, filters - Authentication and authorization system - Database migrations and seeders - Custom CRUD generator commands - Email notification system - Integration with frontend applications
214 lines
6.5 KiB
PHP
214 lines
6.5 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Http\Controllers\Response\ApiResponse;
|
|
use Illuminate\Http\Request;
|
|
use App\Services\UserService;
|
|
use App\Services\MemberService;
|
|
use Illuminate\Support\Carbon;
|
|
use App\Http\Requests\User\Login;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use App\Http\Resources\UserLoggedResource;
|
|
use App\Repositories\Role;
|
|
use GuzzleHttp\Exception\ClientException;
|
|
use Illuminate\Support\Facades\Mail;
|
|
use Laravel\Sanctum\PersonalAccessToken;
|
|
|
|
class AuthController extends Controller
|
|
{
|
|
private $userService;
|
|
private $memberService;
|
|
|
|
public function __construct(
|
|
UserService $userService,
|
|
MemberService $memberService
|
|
) {
|
|
|
|
$this->userService = $userService;
|
|
$this->memberService = $memberService;
|
|
|
|
$this->middleware('auth:sanctum', [
|
|
'except' => [
|
|
'get_sso_data',
|
|
'login',
|
|
'signup',
|
|
'sso',
|
|
'status',
|
|
],
|
|
]);
|
|
}
|
|
|
|
public function login(Login $request)
|
|
{
|
|
if (empty($request->token)) {
|
|
$user = $this->userService->getByEmailWith($request->email, ['roles']);
|
|
|
|
if (!$user || !Hash::check($request->password, $user->password)) {
|
|
return response(['error' => ['The provided credentials are incorrect.']]);
|
|
}
|
|
|
|
} else {
|
|
$client = new \GuzzleHttp\Client(['base_uri' => static::determineIdpUri()]);
|
|
|
|
$headers = [
|
|
'Authorization' => 'Bearer ' . $request->token,
|
|
'Accept' => 'application/json',
|
|
];
|
|
|
|
try {
|
|
$response = $client->request('GET', 'oidc/userinfo.php', [
|
|
'headers' => $headers
|
|
]);
|
|
} catch (ClientException $e) {
|
|
$html = 'Login faild with token:' . $request->token;
|
|
Mail::send([], [], function ($message) use ($html) {
|
|
$message
|
|
->to('jasper@3110.nl')
|
|
->subject('Login failed via SSO')
|
|
->setBody($html, 'text/html');
|
|
});
|
|
}
|
|
|
|
$data = $response->getBody()->getContents();
|
|
$data = json_decode($data);
|
|
|
|
if (empty($data->profile) || !stristr($data->profile, 'KG')) {
|
|
return response(['error' => ['The provided credentials are incorrect.']]);
|
|
}
|
|
|
|
$profile_arr = explode(',', $data->profile);
|
|
$organization_id = str_replace('KG_', '', $profile_arr[0]);
|
|
|
|
$member = $this->memberService->get($organization_id);
|
|
|
|
$user = $this->userService->getByEmailWith($data->sub, ['roles']);
|
|
|
|
if (!$user) {
|
|
$new_user = [
|
|
'first_name' => $data->given_name,
|
|
'last_name' => $data->family_name,
|
|
'email' => $data->sub,
|
|
'password' => 'aT@5*Wb*W7gseVhC',
|
|
];
|
|
|
|
$user = $this->userService->save($new_user, false);
|
|
$user->roles()->attach(Role::whereName('user')->firstOrFail());
|
|
}
|
|
|
|
$member->users()->syncWithoutDetaching([$user->id]);
|
|
$member->save();
|
|
}
|
|
|
|
$user->last_login_at = $user->logged_at;
|
|
$user->last_login_ip = $request->getClientIp();
|
|
$user->logged_at = Carbon::now();
|
|
$user->save();
|
|
|
|
$token = $user->createToken('cms-token')->plainTextToken;
|
|
|
|
$response = ['token' => $token];
|
|
|
|
return response($response, 201);
|
|
}
|
|
|
|
public function logout(Request $request)
|
|
{
|
|
$request->user()->tokens()->delete();
|
|
|
|
return response()->json('Logged-out', 201);
|
|
}
|
|
|
|
public function status(Request $request)
|
|
{
|
|
$bearerToken = $request->bearerToken();
|
|
|
|
if (is_null($bearerToken)) {
|
|
return ApiResponse::error(
|
|
new Error\AuthController\MissingBearerTokenError(),
|
|
);
|
|
} else {
|
|
$accessToken = PersonalAccessToken::findToken($bearerToken);
|
|
|
|
return ApiResponse::success([
|
|
'authenticated' => $accessToken instanceof PersonalAccessToken,
|
|
'user' => $accessToken ? $accessToken->tokenable->email : null,
|
|
]);
|
|
}
|
|
}
|
|
|
|
public function me(Request $request)
|
|
{
|
|
return response()->json(new UserLoggedResource($request->user()));
|
|
}
|
|
|
|
private static function dumpLocal(...$values): void
|
|
{
|
|
if (static::isLocal()) {
|
|
dd(...$values);
|
|
}
|
|
}
|
|
|
|
private static function isLocal(): bool
|
|
{
|
|
return $_SERVER['REMOTE_ADDR'] === '89.98.81.170';
|
|
}
|
|
|
|
public function sso()
|
|
{
|
|
$cookieFileHandle = tmpfile();
|
|
$cookieFilePath = stream_get_meta_data($cookieFileHandle)['uri'];
|
|
|
|
$ch = curl_init(static::buildSsoEndpointUri());
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_COOKIESESSION, true);
|
|
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookieFilePath);
|
|
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookieFilePath);
|
|
$result = curl_exec($ch);
|
|
curl_close($ch);
|
|
|
|
fclose($cookieFileHandle);
|
|
|
|
if (static::isLocal()) {
|
|
echo $result; exit;
|
|
}
|
|
echo '<div style="display: none;">' . $result . '</div>';
|
|
}
|
|
|
|
private static function buildSsoEndpointUri(): string
|
|
{
|
|
$endpointQuery = http_build_query([
|
|
'client_id' => config('sso.client_id'),
|
|
'redirect_uri' => config('sso.redirect_uri'),
|
|
'scope' => implode('+', config('sso.scopes')),
|
|
'response_type' => 'id_token+token',
|
|
'response_mode' => 'form_post',
|
|
'nonce' => md5(rand()), // TODO: proper nonce handling
|
|
], '', '&', PHP_QUERY_RFC3986);
|
|
$endpointQuery = str_replace('%2B', '+', $endpointQuery);
|
|
|
|
return sprintf('%s?%s', static::determineIdpUri('oidc/authorize.php'), $endpointQuery);
|
|
}
|
|
|
|
private static function determineIdpUri(string $path = ''): string
|
|
{
|
|
$baseUri = rtrim(config('sso.idp_base_uri'), '/');
|
|
|
|
return sprintf('%s/%s', $baseUri, $path);
|
|
}
|
|
|
|
public function get_sso_data() {
|
|
$client = new \GuzzleHttp\Client([
|
|
'base_uri' => static::determineIdpUri(),
|
|
'headers' => [
|
|
'Authorization' => sprintf('Bearer %s', $_GET['token']),
|
|
'Accept' => 'application/json',
|
|
],
|
|
]);
|
|
|
|
$response = $client->request('GET', 'oidc/userinfo.php');
|
|
|
|
return response($response->getBody()->getContents(), 201);
|
|
}
|
|
}
|