Skip to content

Commit

Permalink
Add option to map user API groups to teams
Browse files Browse the repository at this point in the history
- Adds option to map groups to teams in ODC admin section.
- Teams are retrieved and created during login.
- Teams are assigned and unassigned based on group memberships.
  • Loading branch information
melegiul committed Aug 16, 2024
1 parent 1ace325 commit a7b7596
Show file tree
Hide file tree
Showing 14 changed files with 249 additions and 36 deletions.
6 changes: 6 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,9 @@ APP_DEMO=0
###> LaF ###
laF_version=2.0.0-dev
###< LaF ###

### Group Mapper API ###
GROUP_API_URI=http://localhost
GROUP_API_KEY=CHANGEME
GROUP_API_ROLE=CHANGEME
GROUP_API_USER_ID=CHANGEME
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,5 @@ docker.conf
###> phpstan/phpstan ###
phpstan.neon
###< phpstan/phpstan ###

.php-version
7 changes: 7 additions & 0 deletions config/packages/framework.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ framework:
cookie_secure: auto
cookie_samesite: lax

http_client:
scoped_clients:
group.client:
base_uri: '%env(GROUP_API_URI)%'
headers:
'X-API-KEY': '%env(default::GROUP_API_KEY)%'

#esi: true
#fragments: true
php_errors:
Expand Down
7 changes: 7 additions & 0 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ parameters:
KEYCLOAK_SECRET: '%env(OAUTH_KEYCLOAK_CLIENT_SECRET)%'
KEYCLOAK_ID: '%env(OAUTH_KEYCLOAK_CLIENT_ID)%'
superAdminRole: '%env(superAdminRole)%'
group_api_user_id: '%env(default::GROUP_API_USER_ID)%'
group_api_role: '%env(default::GROUP_API_ROLE)%'
services:
# default configuration for services in *this* file
_defaults:
Expand Down Expand Up @@ -57,3 +59,8 @@ services:
connection: default
calls:
- [ setAnnotationReader, [ "@annotation_reader" ] ]

App\Security\KeycloakAuthenticator:
arguments:
$groupApiUserId: '%group_api_user_id%'
$groupApiRole: '%group_api_role%'
3 changes: 1 addition & 2 deletions src/Controller/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ public function manage(
$errors = array();

if ($form->isSubmitted() && $form->isValid()) {
$newSettings = $form->getData();
$settings->setUseKeycloakGroups($newSettings->getUseKeycloakGroups());
$settings = $form->getData();
$errors = $validator->validate($settings);
if (count($errors) == 0) {
$em->persist($settings);
Expand Down
15 changes: 7 additions & 8 deletions src/Controller/TeamController.php
Original file line number Diff line number Diff line change
Expand Up @@ -348,15 +348,14 @@ public function edit(

$availableTeams = $currentTeamService->getTeamsWithoutCurrentHierarchy($user, $team);

$form = $this->createForm(
TeamType::class,
$team,
['teams' => $availableTeams,]
);
$form = $this->createForm(TeamType::class, $team, [
'teams' => $availableTeams,
'disabled' => $team->isImmutable(),
]);
$form->handleRequest($request);

$errors = array();
if ($form->isSubmitted() && $form->isValid()) {
if ($form->isSubmitted() && $form->isValid() && !$team->isImmutable()) {
$nTeam = $form->getData();
$errors = $validator->validate($nTeam);
if (count($errors) == 0) {
Expand Down Expand Up @@ -391,7 +390,7 @@ public function manage(
{
$user = $this->getUser();
$settings = $settingsRepository->findOne();
$useKeycloakGroups = $settings ? $settings->getUseKeycloakGroups() : false;
$useKeycloakGroups = $settings && $settings->getUseKeycloakGroups();

if (!$securityService->superAdminCheck($user)) {
return $this->redirectToRoute('dashboard');
Expand Down Expand Up @@ -440,7 +439,7 @@ public function teamDelete(
$teamId = $request->get('id');
$team = $teamId ? $teamRepository->find($teamId) : $currentTeamService->getCurrentAdminTeam($user);

if ($securityService->superAdminCheck($user) === false) {
if ($securityService->superAdminCheck($user) === false || $team->isImmutable()) {
return $this->redirectToRoute('dashboard');
}

Expand Down
2 changes: 1 addition & 1 deletion src/Controller/TeamMemberController.php
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ public function mitgliederAdd(
$teamId = $request->get('id');
$currentTeam = null;
$settings = $settingsRepository->findOne();
$useKeycloakGroups = $settings ? $settings->getUseKeycloakGroups() : false;
$useKeycloakGroups = $settings && $settings->getUseKeycloakGroups();
$this->setBackButton($this->generateUrl('manage_teams'));

if ($teamId) {
Expand Down
32 changes: 24 additions & 8 deletions src/Entity/Settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,47 @@
namespace App\Entity;

use App\Repository\SettingsRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

#[ORM\Entity(repositoryClass: SettingsRepository::class)]
class Settings
{
const NO_GROUP_MAPPING = 0;

const KEYCLOAK_GROUP_MAPPING = 1;

const API_GROUP_MAPPING = 2;

#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;

#[ORM\Column(type: 'boolean', nullable: true)]
private $useKeycloakGroups;
#[ORM\Column(options: [
'default' => 0,
])]
#[Assert\Choice([
self::NO_GROUP_MAPPING,
self::KEYCLOAK_GROUP_MAPPING,
self::API_GROUP_MAPPING,
])]
private int $groupMapping = 0;

public function getUseKeycloakGroups(): ?bool
public function getGroupMapping(): int
{
return $this->useKeycloakGroups;
return $this->groupMapping;
}

public function setUseKeycloakGroups(?bool $useKeycloakGroups): self
public function setGroupMapping(int $groupMapping): static
{
$this->useKeycloakGroups = $useKeycloakGroups;
$this->groupMapping = $groupMapping;

return $this;
}

public function getUseKeycloakGroups(): bool
{
return $this->groupMapping === self::KEYCLOAK_GROUP_MAPPING;
}
}
22 changes: 20 additions & 2 deletions src/Entity/Team.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Tree\Entity\Repository\NestedTreeRepository;
use phpDocumentor\Reflection\Types\Boolean;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
use Gedmo\Mapping\Annotation as Gedmo;
Expand Down Expand Up @@ -233,6 +232,11 @@ class Team
#[ORM\ManyToMany(targetEntity: AuditTomZiele::class, mappedBy: 'ignoredInTeams')]
private $ignoredAuditGoals;

#[ORM\Column(options: [
'default' => 0,
])]
private bool $immutable = false;

public function __construct()
{
$this->members = new ArrayCollection();
Expand Down Expand Up @@ -1393,9 +1397,11 @@ public function getRoot(): ?self
return $this->root;
}

public function setParent(self $parent = null): void
public function setParent(self $parent = null): static
{
$this->parent = $parent;

return $this;
}

public function getParent(): ?self
Expand Down Expand Up @@ -1659,4 +1665,16 @@ public function removeIgnoredProduct(Produkte $product): self

return $this;
}

public function isImmutable(): bool
{
return $this->immutable;
}

public function setImmutable(bool $immutable): static
{
$this->immutable = $immutable;

return $this;
}
}
26 changes: 16 additions & 10 deletions src/Form/Type/SettingsType.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,10 @@

namespace App\Form\Type;

use App\Entity\AuditTom;
use App\Entity\AuditTomAbteilung;
use App\Entity\AuditTomStatus;
use App\Entity\AuditTomZiele;
use App\Entity\Settings;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

Expand All @@ -28,8 +20,22 @@ class SettingsType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('useKeycloakGroups', CheckboxType::class, ['label' => 'useKeycloakGroups', 'help'=> 'useKeycloakGroupsHelp', 'required' => false, 'translation_domain' => 'form'])
->add('save', SubmitType::class, ['attr' => array('class' => 'btn'),'label' => 'save', 'translation_domain' => 'form']);
->add('groupMapping', ChoiceType::class, [
'label' => 'groupMapping',
'help'=> 'groupMappingHelp',
'translation_domain' => 'form',
'expanded' => true,
'choices' => [
'noGroupMapping' => Settings::NO_GROUP_MAPPING,
'useKeycloakGroups' => Settings::KEYCLOAK_GROUP_MAPPING,
'useApiGroups' => Settings::API_GROUP_MAPPING,
]
])
->add('save', SubmitType::class, [
'attr' => array('class' => 'btn'),
'label' => 'save',
'translation_domain' => 'form'
]);
}

public function configureOptions(OptionsResolver $resolver)
Expand Down
37 changes: 37 additions & 0 deletions src/Migrations/Version20240807153157.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240807153157 extends AbstractMigration
{
public function getDescription(): string
{
return 'Add API group mapping option';
}

public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE settings ADD group_mapping INT DEFAULT 0 NOT NULL');
$this->addSql('UPDATE settings SET group_mapping = use_keycloak_groups WHERE use_keycloak_groups IS NOT NULL');
$this->addSql('ALTER TABLE settings DROP use_keycloak_groups');
$this->addSql('ALTER TABLE team ADD immutable TINYINT(1) DEFAULT 0 NOT NULL');
}

public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE settings ADD use_keycloak_groups TINYINT(1) DEFAULT NULL');
$this->addSql('UPDATE settings SET use_keycloak_groups = group_mapping WHERE group_mapping = 1');
$this->addSql('ALTER TABLE settings DROP group_mapping');
$this->addSql('ALTER TABLE team DROP immutable');
}
}
Loading

0 comments on commit a7b7596

Please sign in to comment.