Set up api calls
This commit is contained in:
parent
b9b0efd6c6
commit
e50bb0402a
1
.env
1
.env
|
|
@ -48,6 +48,7 @@ OAUTH_PRIVATE_KEY=%kernel.project_dir%/config/jwt/private.key
|
|||
OAUTH_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.key
|
||||
OAUTH_PASSPHRASE=8170ea18d2e3e05b5c7ae0672a754bf4
|
||||
OAUTH_ENCRYPTION_KEY=f1b7c279f7992205a0df45e295d07066
|
||||
OAUTH_SSO_SECRET='sso-own-secret'
|
||||
###< league/oauth2-server-bundle ###
|
||||
|
||||
###> nelmio/cors-bundle ###
|
||||
|
|
|
|||
|
|
@ -2,10 +2,9 @@ import {Controller} from '@hotwired/stimulus';
|
|||
import { Modal } from "bootstrap";
|
||||
import {TabulatorFull as Tabulator} from 'tabulator-tables';
|
||||
import {eyeIconLink, pencilIcon, TABULATOR_FR_LANG, trashIcon} from "../js/global.js";
|
||||
import base_controller from "./base_controller.js";
|
||||
|
||||
|
||||
export default class extends base_controller {
|
||||
export default class extends Controller {
|
||||
static values = {
|
||||
listProject : Boolean,
|
||||
orgId: Number,
|
||||
|
|
@ -176,13 +175,13 @@ export default class extends base_controller {
|
|||
this.currentProjectId = projectId;
|
||||
|
||||
this.modal.show();
|
||||
|
||||
this.nameInputTarget.disabled = true;
|
||||
this.formTitleTarget.textContent = "Modifier le projet";
|
||||
|
||||
try {
|
||||
// 1. Ensure checkboxes are loaded first
|
||||
const apps = await this.fetchAndRenderApplications(this.appListTarget);
|
||||
await this.loadApplications();
|
||||
|
||||
// 2. Fetch the project data
|
||||
const response = await fetch(`/project/data/${projectId}`);
|
||||
const project = await response.json();
|
||||
|
|
@ -204,13 +203,13 @@ export default class extends base_controller {
|
|||
}
|
||||
}
|
||||
// Update your openCreateModal to reset the state
|
||||
async openCreateModal() {
|
||||
openCreateModal() {
|
||||
this.currentProjectId = null;
|
||||
this.modal.show();
|
||||
this.nameInputTarget.disabled = false;
|
||||
this.nameInputTarget.value = "";
|
||||
this.formTitleTarget.textContent = "Nouveau Projet";
|
||||
await this.fetchAndRenderApplications();
|
||||
this.loadApplications();
|
||||
}
|
||||
|
||||
async deleteProject(event) {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,10 @@ security:
|
|||
dev:
|
||||
pattern: ^/(_(profiler|wdt)|css|images|js)/
|
||||
security: false
|
||||
api_token_validation:
|
||||
pattern: ^/api/validate-token
|
||||
stateless: true
|
||||
oauth2: true
|
||||
oauth_userinfo:
|
||||
pattern: ^/oauth2/userinfo
|
||||
stateless: true
|
||||
|
|
@ -65,6 +69,7 @@ security:
|
|||
# Note: Only the *first* access control that matches will be used
|
||||
access_control:
|
||||
- { path: ^/login, roles: PUBLIC_ACCESS }
|
||||
- { path: ^/api/validate-token, roles: PUBLIC_ACCESS }
|
||||
- { path: ^/password_setup, roles: PUBLIC_ACCESS }
|
||||
- { path: ^/password_reset, roles: PUBLIC_ACCESS }
|
||||
- { path: ^/sso_logout, roles: IS_AUTHENTICATED_FULLY }
|
||||
|
|
@ -76,8 +81,6 @@ security:
|
|||
- { path: ^/oauth2/userinfo, roles: IS_AUTHENTICATED_FULLY }
|
||||
- { path: ^/, roles: ROLE_USER }
|
||||
|
||||
|
||||
|
||||
when@test:
|
||||
security:
|
||||
password_hashers:
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ parameters:
|
|||
app_url: '%env(APP_URL)%'
|
||||
mercure_secret: '%env(MERCURE_JWT_SECRET)%'
|
||||
logos_directory: '%kernel.project_dir%/public/uploads/logos'
|
||||
oauth_sso_secret: '%env(OAUTH_SSO_SECRET)%'
|
||||
|
||||
services:
|
||||
# default configuration for services in *this* file
|
||||
|
|
@ -28,6 +29,10 @@ services:
|
|||
App\MessageHandler\NotificationMessageHandler:
|
||||
arguments:
|
||||
$appUrl: '%app_url%'
|
||||
App\Service\SSO\ProjectService:
|
||||
arguments:
|
||||
$appUrl: '%app_url%'
|
||||
$clientSecret: '%oauth_sso_secret%'
|
||||
App\EventSubscriber\:
|
||||
resource: '../src/EventSubscriber/'
|
||||
tags: ['kernel.event_subscriber']
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use App\Repository\AppsRepository;
|
|||
use App\Repository\OrganizationsRepository;
|
||||
use App\Repository\ProjectRepository;
|
||||
use App\Service\ProjectService;
|
||||
use App\Service\SSO\ProjectService as SSOProjectService;
|
||||
use App\Service\UserService;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
|
|
@ -25,7 +26,9 @@ final class ProjectController extends AbstractController
|
|||
private readonly OrganizationsRepository $organizationsRepository,
|
||||
private readonly ProjectRepository $projectRepository,
|
||||
private readonly ProjectService $projectService,
|
||||
private readonly UserService $userService, private readonly AppsRepository $appsRepository)
|
||||
private readonly AppsRepository $appsRepository,
|
||||
private readonly SSOProjectService $SSOProjectService,
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -61,6 +64,7 @@ final class ProjectController extends AbstractController
|
|||
$project->setOrganization($org);
|
||||
$project->setApplications($data['applications']);
|
||||
$this->entityManager->persist($project);
|
||||
$this->SSOProjectService->createRemoteProject('http://api.solutions-easy.moi', $project);
|
||||
$this->entityManager->flush();
|
||||
return new JsonResponse(['message' => 'Project created successfully'], Response::HTTP_CREATED);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controller\api\Security;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
class SecurityController extends AbstractController{
|
||||
|
||||
#[Route('/api/validate-token', name: 'api_validate_token')]
|
||||
public function validate(): JsonResponse
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
return $this->json([
|
||||
'valid' => true,
|
||||
'email' => ($user instanceof \App\Entity\User) ? $user->getUserIdentifier() : null,
|
||||
'scopes' => $this->container->get('security.token_storage')->getToken()->getScopes(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -32,4 +32,9 @@ final class AccessToken implements AccessTokenEntityInterface
|
|||
->getToken($this->jwtConfiguration->signer(), $this->jwtConfiguration->signingKey());
|
||||
}
|
||||
|
||||
public function setUserIdentifier(?string $userIdentifier): void
|
||||
{
|
||||
$this->userIdentifier = $userIdentifier;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -28,19 +28,37 @@ class LoginSubscriber implements EventSubscriberInterface
|
|||
|
||||
public function onLoginSuccess(LoginSuccessEvent $event): void
|
||||
{
|
||||
$user = $event->getUser();
|
||||
$passportUser = $event->getUser();
|
||||
|
||||
// 1. Check if we have a user at all
|
||||
if (!$passportUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. IMPORTANT: Check if this is a real User entity from your DB.
|
||||
// If it's a Machine/Client login, it will be an instance of
|
||||
// League\Bundle\OAuth2ServerBundle\Security\User\ClientCredentialsUser
|
||||
if (!$passportUser instanceof \App\Entity\User) {
|
||||
// It's a machine (M2M), so we don't track "last connection" or create manual tokens
|
||||
return;
|
||||
}
|
||||
|
||||
// Now we know it's a real human user
|
||||
$user = $this->entityManager->getRepository(User::class)->findOneBy([
|
||||
'email' => $passportUser->getUserIdentifier()
|
||||
]);
|
||||
|
||||
if ($user) {
|
||||
$user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $user->getUserIdentifier()]);
|
||||
$user->setLastConnection(new \DateTime('now', new \DateTimeZone('Europe/Paris')));
|
||||
|
||||
$easySolution = $this->entityManager->getRepository(Client::class)->findOneBy(['name' => 'EasySolution']);
|
||||
if ($easySolution) {
|
||||
$accessToken = new AccessToken(
|
||||
identifier: bin2hex(random_bytes(40)), // Generate unique identifier
|
||||
identifier: bin2hex(random_bytes(40)),
|
||||
expiry: new \DateTimeImmutable('+1 hour', new \DateTimeZone('Europe/Paris')),
|
||||
client: $easySolution,
|
||||
userIdentifier: $user->getUserIdentifier(),
|
||||
scopes: ['email profile openid apps:easySolutions'] // Empty array if no specific scopes needed
|
||||
scopes: ['email', 'profile', 'openid', 'apps:easySolutions']
|
||||
);
|
||||
$this->entityManager->persist($user);
|
||||
$this->entityManager->persist($accessToken);
|
||||
|
|
|
|||
|
|
@ -25,8 +25,7 @@ final class AccessTokenRepository implements AccessTokenRepositoryInterface
|
|||
/** @var int|string|null $userIdentifier */
|
||||
$accessToken = new AccessTokenEntity();
|
||||
$accessToken->setClient($clientEntity);
|
||||
$accessToken->setUserIdentifier($userIdentifier);
|
||||
|
||||
$accessToken->setUserIdentifier($userIdentifier ?? $clientEntity->getIdentifier());
|
||||
foreach ($scopes as $scope) {
|
||||
$accessToken->addScope($scope);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace App\Service\SSO;
|
||||
|
||||
|
||||
use App\Entity\Project;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
|
||||
class ProjectService
|
||||
{
|
||||
public function __construct(private readonly HttpClientInterface $httpClient,
|
||||
private string $appUrl,
|
||||
private string $clientSecret)
|
||||
{
|
||||
}
|
||||
|
||||
// Inside your SSO Server Service
|
||||
public function createRemoteProject(string $clientAppUrl, Project $project): void
|
||||
{
|
||||
// 1. Get a token for "ourselves"
|
||||
$tokenResponse = $this->httpClient->request('POST', $this->appUrl . 'token', [
|
||||
'auth_basic' => ['afc7b28b95b61aeeeae8eaed94c5cfe1', $this->clientSecret], // ID and Secret go here
|
||||
'body' => [
|
||||
'grant_type' => 'client_credentials',
|
||||
// 'scope' => 'project_sync'
|
||||
],
|
||||
]);
|
||||
// if (400 === $tokenResponse->getStatusCode() || 500 === $tokenResponse->getStatusCode()) {
|
||||
// // This will print the actual OAuth2 error (e.g., "invalid_scope" or "unsupported_grant_type")
|
||||
// dd($tokenResponse->getContent(false));
|
||||
// }
|
||||
$accessToken = $tokenResponse->toArray()['access_token'];
|
||||
// data must match easy check database
|
||||
$projectJson = [
|
||||
'id' => $project->getId(),
|
||||
'projet' => $project->getName(),
|
||||
'entity_id' => 3,
|
||||
'bdd' => $project->getBddName(),
|
||||
'isactive' => $project->isActive(),
|
||||
];
|
||||
|
||||
// 2. Call the Client Application's Webhook/API
|
||||
$this->httpClient->request('POST', $clientAppUrl . '/api/v1/project/create', [
|
||||
'headers' => ['Authorization' => 'Bearer ' . $accessToken],
|
||||
'json' => $projectJson
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue