Skip to content

Commit

Permalink
Merge branch 'main' into bugfix/PPF-499
Browse files Browse the repository at this point in the history
  • Loading branch information
Anahkiasen authored Jul 1, 2024
2 parents 3343c2b + d081a5a commit c4e37aa
Show file tree
Hide file tree
Showing 39 changed files with 682 additions and 223 deletions.
13 changes: 12 additions & 1 deletion .env.ci
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
APP_NAME=publiq-platform
APP_ENV=local
APP_KEY=
APP_KEY=base64:5f6ivEq1QCl8ylgkFEzMpU6npqDi5tGYRuG1LYzilb4=
APP_DEBUG=true
APP_URL=http://localhost
APP_SERVICE=laravel
Expand Down Expand Up @@ -74,6 +74,17 @@ AUTH0_LOGIN_CLIENT_ID=***
AUTH0_LOGIN_CLIENT_SECRET=***
AUTH0_LOGIN_REDIRECT_URI=http://localhost/auth/callback

KEYCLOAK_CREATION_ENABLED=true
KEYCLOAK_LOGIN_ENABLED=false

KEYCLOAK_LOGIN_DOMAIN=account-keycloak-acc.uitid.be
KEYCLOAK_LOGIN_MANAGEMENT_DOMAIN=account-keycloak-acc.uitid.be
KEYCLOAK_LOGIN_CLIENT_ID=
KEYCLOAK_LOGIN_CLIENT_SECRET
KEYCLOAK_LOGIN_REDIRECT_URI=http://localhost/auth/callback
KEYCLOAK_LOGIN_PARAMETERS="locale=nl&referrer=publiq-platform&prompt=login&skip_verify_legacy=true&product_display_name=publiq platform"
KEYCLOAK_LOGIN_REALM_NAME=

AUTH0_CLIENT_CREATION_ENABLED=true

# MUST always be set to DEV tenant config except for the .env for the production app!
Expand Down
2 changes: 1 addition & 1 deletion app/Auth0/Auth0ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public function register(): void
);
});

if (config(KeycloakConfig::IS_ENABLED)) {
if (config(KeycloakConfig::KEYCLOAK_CREATION_ENABLED)) {
// By default, the Auth0 integration is enabled. For testing purposes this can be disabled inside the .env file.

// May always be registered even if there are no configured tenants, because in that case the cluster SDK will
Expand Down
18 changes: 17 additions & 1 deletion app/Domain/Auth/Controllers/LogoutController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace App\Domain\Auth\Controllers;

use App\Keycloak\KeycloakConfig;
use Auth0\SDK\Auth0;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Auth;
Expand All @@ -17,12 +18,27 @@ private function getLogoutLink(): string
/** @var Auth0 $auth0 */
$auth0 = app(Auth0::class);

$idtoken = $auth0->getIdToken();

if (Auth::check()) {
$auth0->logout();
Auth::guard(config('nova.guard'))->logout();
}

return $auth0->authentication()->getLogoutLink(config('app.url'));
$url = config('app.url');

if (config(KeycloakConfig::KEYCLOAK_LOGIN_ENABLED)) {
return sprintf(
'https://%s/realms/%s/protocol/openid-connect/logout?client_id=%s&post_logout_redirect_uri=%s&id_token_hint=%s',
config(KeycloakConfig::KEYCLOAK_DOMAIN),
config(KeycloakConfig::KEYCLOAK_REALM_NAME),
config(KeycloakConfig::KEYCLOAK_CLIENT_ID),
$url,
$idtoken
);
}

return $auth0->authentication()->getLogoutLink($url);
}

public function adminLogout(): JsonResponse
Expand Down
7 changes: 7 additions & 0 deletions app/Domain/Integrations/Controllers/IntegrationController.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use App\Domain\Integrations\IntegrationUrl;
use App\Domain\Integrations\KeyVisibility;
use App\Domain\Integrations\Mappers\OrganizationMapper;
use App\Domain\Integrations\Mappers\OrganizerMapper;
use App\Domain\Integrations\Mappers\StoreContactMapper;
use App\Domain\Integrations\Mappers\StoreIntegrationMapper;
use App\Domain\Integrations\Mappers\StoreIntegrationUrlMapper;
Expand All @@ -33,6 +34,7 @@
use App\Domain\Integrations\Mappers\UpdateIntegrationUrlsMapper;
use App\Domain\Integrations\Repositories\IntegrationRepository;
use App\Domain\Integrations\Repositories\IntegrationUrlRepository;
use App\Domain\Integrations\Repositories\OrganizerRepository;
use App\Domain\KeyVisibilityUpgrades\KeyVisibilityUpgrade;
use App\Domain\Organizations\Repositories\OrganizationRepository;
use App\Domain\Subscriptions\Repositories\SubscriptionRepository;
Expand Down Expand Up @@ -63,6 +65,7 @@ public function __construct(
private readonly ContactRepository $contactRepository,
private readonly ContactKeyVisibilityRepository $contactKeyVisibilityRepository,
private readonly OrganizationRepository $organizationRepository,
private readonly OrganizerRepository $organizerRepository,
private readonly CouponRepository $couponRepository,
private readonly Auth0ClientRepository $auth0ClientRepository,
private readonly UiTiDv1ConsumerRepository $uitidV1ConsumerRepository,
Expand Down Expand Up @@ -172,6 +175,7 @@ public function show(string $id): Response
'oldCredentialsExpirationDate' => $oldCredentialsExpirationDate,
'email' => Auth::user()?->email,
'subscriptions' => $this->subscriptionRepository->all(),
'organizers' => session('organizers'),
]);
}

Expand Down Expand Up @@ -288,6 +292,9 @@ public function requestActivation(string $id, RequestActivationRequest $request)
$organization = OrganizationMapper::mapActivationRequest($request);
$this->organizationRepository->save($organization);

$organizers = OrganizerMapper::map($request, $id);
$this->organizerRepository->create(...$organizers);

$this->integrationRepository->requestActivation(Uuid::fromString($id), $organization->id, $request->input('coupon'));

return Redirect::back();
Expand Down
42 changes: 42 additions & 0 deletions app/Domain/Integrations/Controllers/OrganizerController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace App\Domain\Integrations\Controllers;

use App\Domain\Integrations\FormRequests\GetOrganizersRequest;
use App\Http\Controllers\Controller;
use App\Search\Sapi3\SearchService;
use CultuurNet\SearchV3\ValueObjects\Organizer;
use Illuminate\Http\JsonResponse;

final class OrganizerController extends Controller
{
public function __construct(private readonly SearchService $searchService)
{
}

public function index(GetOrganizersRequest $request): JsonResponse
{
try {
$organizerName = $request->input('name');

$data = $this->searchService->searchUiTPASOrganizer($organizerName)->getMember()?->getItems() ?? [];

return new JsonResponse(
array_map(function (Organizer $organizer) {
return [
'id' => $organizer->getCdbid(),
'name' => $organizer->getName()?->getValues(),
];
}, $data)
);

} catch (\Exception $e) {
return new JsonResponse(
['exception' => $e->getMessage()]
);
;
}
}
}
20 changes: 20 additions & 0 deletions app/Domain/Integrations/FormRequests/GetOrganizersRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace App\Domain\Integrations\FormRequests;

use Illuminate\Foundation\Http\FormRequest;

final class GetOrganizersRequest extends FormRequest
{
/**
* @return array<string, mixed>
*/
public function rules(): array
{
return [
'name' => ['required', 'string'],
];
}
}
26 changes: 23 additions & 3 deletions app/Domain/Integrations/FormRequests/RequestActivationRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace App\Domain\Integrations\FormRequests;

use App\Domain\Integrations\Integration;
use App\Domain\Integrations\IntegrationType;
use App\Domain\Integrations\Repositories\IntegrationRepository;
use App\Domain\Subscriptions\Repositories\SubscriptionRepository;
Expand All @@ -24,25 +25,44 @@ public function rules(): array
$rules = collect([
...(new CreateOrganizationRequest())->rules(),
'coupon' => ['nullable', 'string', 'max:255'],
'organizers' => ['required','array'],
'organizers.*.name' => ['required', 'string'],
'organizers.*.id' => ['required', 'string'],
]);

if (!$this->isAccountingInfoRequired()) {
if (!$this->isAccountingInfoRequired() || $this->isUITPAS()) {
$rules->forget(['organization.invoiceEmail', 'organization.vat', 'coupon']);
}

if (!$this->isUITPAS()) {
$rules->forget(['organizers']);
}

return $rules->toArray();
}

private function isAccountingInfoRequired(): bool
private function fetchIntegration(): Integration
{
/** @var IntegrationRepository $integrationRepository */
$integrationRepository = App::get(IntegrationRepository::class);
$integration = $integrationRepository->getById(Uuid::fromString($this->id));
return $integrationRepository->getById(Uuid::fromString($this->id));
}


private function isAccountingInfoRequired(): bool
{
$integration = $this->fetchIntegration();

/** @var SubscriptionRepository $subscriptionRepository */
$subscriptionRepository = App::get(SubscriptionRepository::class);
$subscription = $subscriptionRepository->getById($integration->subscriptionId);

return $integration->type !== IntegrationType::EntryApi || $subscription->price > 0.0;
}

private function isUITPAS(): bool
{
$integration = $this->fetchIntegration();
return $integration->type === IntegrationType::UiTPAS;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

namespace App\Domain\Integrations\FormRequests;

use App\Domain\Integrations\IntegrationType;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

final class StoreIntegrationRequest extends FormRequest
{
Expand All @@ -26,6 +28,7 @@ public function rules(): array
'lastNameTechnicalContact' => ['required', 'string', 'max:255'],
'emailTechnicalContact' => ['required', 'string', 'email', 'max:255'],
'agreement' => ['required', 'string'],
'uitpasAgreement' => [Rule::requiredIf($this->input('integrationType') === IntegrationType::UiTPAS->value), 'nullable', 'string'],
'coupon' => ['nullable', 'string'],
];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace App\Domain\Integrations\FormRequests;

use App\Domain\Integrations\IntegrationType;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

Expand All @@ -18,7 +19,7 @@ public function rules(): array
'integrationName' => ['required_without:description', 'string', 'max:255'],
'description' => ['required_without:integrationName', 'string', 'max:255'],
'website' => [
Rule::requiredIf($this->input('integrationType') === 'uitpas'),
Rule::requiredIf($this->input('integrationType') === IntegrationType::UiTPAS->value),
'nullable',
'url:http,https',
'max:255',
Expand Down
33 changes: 33 additions & 0 deletions app/Domain/Integrations/Mappers/OrganizerMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace App\Domain\Integrations\Mappers;

use App\Domain\Integrations\FormRequests\RequestActivationRequest;
use App\Domain\Integrations\Organizer;
use Ramsey\Uuid\Uuid;

final class OrganizerMapper
{
/**
* @return Organizer[]
*/
public static function map(RequestActivationRequest $request, string $id): array
{
/**
* @var Organizer[] $organizers
*/
$organizers = [];

foreach ($request->input('organizers') ?? [] as $organizer) {
$organizers[] = new Organizer(
Uuid::uuid4(),
Uuid::fromString($id),
Uuid::fromString($organizer['id'])
);
}

return $organizers;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,20 @@

use App\Domain\Integrations\Models\OrganizerModel;
use App\Domain\Integrations\Organizer;
use Illuminate\Support\Facades\DB;

final class EloquentOrganizerRepository implements OrganizerRepository
{
public function create(Organizer $organizer): void
public function create(Organizer ...$organizers): void
{
OrganizerModel::query()->create([
'id' => $organizer->id->toString(),
'integration_id' => $organizer->integrationId->toString(),
'organizer_id' => $organizer->organizerId->toString(),
]);
DB::transaction(function () use ($organizers): void {
foreach ($organizers as $organizer) {
OrganizerModel::query()->create([
'id' => $organizer->id->toString(),
'integration_id' => $organizer->integrationId->toString(),
'organizer_id' => $organizer->organizerId->toString(),
]);
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@

interface OrganizerRepository
{
public function create(Organizer $organizer): void;
public function create(Organizer ...$organizer): void;
}
2 changes: 1 addition & 1 deletion app/Keycloak/Converters/IntegrationUrlConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,6 @@ public static function buildLogoutUrls(Integration $integration, Environment $en
}

$logoutUrls = $integration->urlsForTypeAndEnvironment(IntegrationUrlType::Logout, $environment);
return implode('#', array_map(static fn ($url) => $url->url, $logoutUrls));
return implode('##', array_map(static fn ($url) => $url->url, $logoutUrls));
}
}
7 changes: 6 additions & 1 deletion app/Keycloak/KeycloakConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,10 @@

final class KeycloakConfig
{
public const IS_ENABLED = 'keycloak.enabled';
public const KEYCLOAK_LOGIN_ENABLED = 'keycloak.loginEnabled';
public const KEYCLOAK_CREATION_ENABLED = 'keycloak.creationEnabled';
public const KEYCLOAK_DOMAIN = 'keycloak.login.domain';
public const KEYCLOAK_CLIENT_ID = 'keycloak.login.clientId';
public const KEYCLOAK_REALM_NAME = 'keycloak.login.realmName';
public const KEYCLOAK_LOGIN_PARAMETERS = 'keycloak.login.parameters';
}
2 changes: 1 addition & 1 deletion app/Keycloak/KeycloakServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public function register(): void

private function bootstrapEventHandling(): void
{
if (!config(KeycloakConfig::IS_ENABLED)) {
if (!config(KeycloakConfig::KEYCLOAK_CREATION_ENABLED)) {
return;
}

Expand Down
6 changes: 3 additions & 3 deletions app/Nova/Resources/Integration.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public static function searchableColumns(): array
$output[] = new SearchableRelation('auth0Clients', 'auth0_client_id');
}

if (config(KeycloakConfig::IS_ENABLED)) {
if (config(KeycloakConfig::KEYCLOAK_CREATION_ENABLED)) {
$output[] = new SearchableRelation('keycloakClients', 'client_id');
}

Expand Down Expand Up @@ -204,7 +204,7 @@ function (Text $field, NovaRequest $request, FormData $formData) {
$fields[] = HasMany::make('UiTiD v2 Client Credentials (Auth0)', 'auth0Clients', Auth0Client::class);
}

if (config(KeycloakConfig::IS_ENABLED)) {
if (config(KeycloakConfig::KEYCLOAK_CREATION_ENABLED)) {
$fields[] = HasMany::make('Keycloak client Credentials', 'keycloakClients', KeycloakClient::class);
}

Expand Down Expand Up @@ -289,7 +289,7 @@ public function actions(NovaRequest $request): array
->canRun(fn (Request $request, IntegrationModel $model) => $model->hasMissingAuth0Clients());
}

if (config(KeycloakConfig::IS_ENABLED)) {
if (config(KeycloakConfig::KEYCLOAK_CREATION_ENABLED)) {
$actions[] = (new CreateMissingKeycloakClients())
->withName('Create missing Keycloak clients')
->exceptOnIndex()
Expand Down
Loading

0 comments on commit c4e37aa

Please sign in to comment.