Release 2.35 (#5470)

* open up API for plugins by removing internal
* use constants in entity column definition
* simplified entity management API
* bump packages
* allow installing assets and run database migrations independently
* bump to apidoc-bundle 5
* do not duplicate http method in API operationId
* new security entries in Open API definition
* changed API UI provider for Swagger to Stoplight, improved endpoint titles, hide internal endpoints
* deactivate swagger json endpoint
This commit is contained in:
Kevin Papst
2025-05-24 14:28:39 +02:00
committed by GitHub
parent 6e26a37c4d
commit 1e0fbf0b73
63 changed files with 518 additions and 529 deletions

View File

@@ -17,6 +17,7 @@ use App\WorkingTime\Mode\WorkingTimeModeNone;
use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Exception;
use JMS\Serializer\Annotation as Serializer;
@@ -70,7 +71,7 @@ class User implements UserInterface, EquatableInterface, ThemeUserInterface, Pas
*/
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(name: 'id', type: 'integer')]
#[ORM\Column(name: 'id', type: Types::INTEGER)]
#[Serializer\Expose]
#[Serializer\Groups(['Default'])]
#[Exporter\Expose(label: 'id', type: 'integer')]
@@ -78,7 +79,7 @@ class User implements UserInterface, EquatableInterface, ThemeUserInterface, Pas
/**
* The user alias will be displayed in the frontend instead of the username
*/
#[ORM\Column(name: 'alias', type: 'string', length: 60, nullable: true)]
#[ORM\Column(name: 'alias', type: Types::STRING, length: 60, nullable: true)]
#[Assert\Length(max: 60)]
#[Serializer\Expose]
#[Serializer\Groups(['Default'])]
@@ -87,13 +88,13 @@ class User implements UserInterface, EquatableInterface, ThemeUserInterface, Pas
/**
* Registration date for the user
*/
#[ORM\Column(name: 'registration_date', type: 'datetime', nullable: true)]
#[ORM\Column(name: 'registration_date', type: Types::DATETIME_MUTABLE, nullable: true)]
#[Exporter\Expose(label: 'profile.registration_date', type: 'datetime')]
private ?\DateTime $registeredAt = null;
/**
* An additional title for the user, like the Job position or Department
*/
#[ORM\Column(name: 'title', type: 'string', length: 50, nullable: true)]
#[ORM\Column(name: 'title', type: Types::STRING, length: 50, nullable: true)]
#[Assert\Length(max: 50)]
#[Serializer\Expose]
#[Serializer\Groups(['Default'])]
@@ -102,7 +103,7 @@ class User implements UserInterface, EquatableInterface, ThemeUserInterface, Pas
/**
* URL to the user avatar, will be auto-generated if empty
*/
#[ORM\Column(name: 'avatar', type: 'string', length: 255, nullable: true)]
#[ORM\Column(name: 'avatar', type: Types::STRING, length: 255, nullable: true)]
#[Assert\Length(max: 255, groups: ['Profile'])]
#[Serializer\Expose]
#[Serializer\Groups(['User_Entity'])]
@@ -110,7 +111,7 @@ class User implements UserInterface, EquatableInterface, ThemeUserInterface, Pas
/**
* API token (password) for this user
*/
#[ORM\Column(name: 'api_token', type: 'string', length: 255, nullable: true)]
#[ORM\Column(name: 'api_token', type: Types::STRING, length: 255, nullable: true)]
private ?string $apiToken = null;
/**
* @internal to be set via form, must not be persisted
@@ -148,7 +149,7 @@ class User implements UserInterface, EquatableInterface, ThemeUserInterface, Pas
*
* @internal for internal usage only
*/
#[ORM\Column(name: 'auth', type: 'string', length: 20, nullable: true)]
#[ORM\Column(name: 'auth', type: Types::STRING, length: 20, nullable: true)]
#[Assert\Length(max: 20)]
private ?string $auth = self::AUTH_INTERNAL;
/**
@@ -157,32 +158,32 @@ class User implements UserInterface, EquatableInterface, ThemeUserInterface, Pas
* @internal has no database mapping as the value is calculated from a permission
*/
private ?bool $isAllowedToSeeAllData = null;
#[ORM\Column(name: 'username', type: 'string', length: 180, nullable: false)]
#[ORM\Column(name: 'username', type: Types::STRING, length: 180, nullable: false)]
#[Assert\NotBlank(groups: ['Registration', 'UserCreate', 'Profile'])]
#[Assert\Regex(pattern: '/\//', match: false, groups: ['Registration', 'UserCreate', 'Profile'])]
#[Assert\Length(min: 2, max: 64, groups: ['Registration', 'UserCreate', 'Profile'])]
#[Serializer\Expose]
#[Serializer\Groups(['Default'])]
private ?string $username = null;
#[ORM\Column(name: 'email', type: 'string', length: 180, nullable: false)]
#[ORM\Column(name: 'email', type: Types::STRING, length: 180, nullable: false)]
#[Assert\NotBlank(groups: ['Registration', 'UserCreate', 'Profile'])]
#[Assert\Length(min: 2, max: 180)]
#[Assert\Email(mode: 'html5', groups: ['Registration', 'UserCreate', 'Profile'])]
private ?string $email = null;
#[ORM\Column(name: 'account', type: 'string', length: 30, nullable: true)]
#[ORM\Column(name: 'account', type: Types::STRING, length: 30, nullable: true)]
#[Assert\Length(max: 30)]
#[Serializer\Expose]
#[Serializer\Groups(['Default'])]
#[Exporter\Expose(label: 'account_number')]
private ?string $accountNumber = null;
#[ORM\Column(name: 'enabled', type: 'boolean', nullable: false)]
#[ORM\Column(name: 'enabled', type: Types::BOOLEAN, nullable: false)]
#[Serializer\Expose]
#[Serializer\Groups(['Default'])]
private bool $enabled = false;
/**
* Encrypted password. Must be persisted.
*/
#[ORM\Column(name: 'password', type: 'string', nullable: false)]
#[ORM\Column(name: 'password', type: Types::STRING, nullable: false)]
private ?string $password = null;
/**
* Plain password. Used for model validation, not persisted.
@@ -190,20 +191,20 @@ class User implements UserInterface, EquatableInterface, ThemeUserInterface, Pas
#[Assert\NotBlank(groups: ['Registration', 'PasswordUpdate', 'UserCreate'])]
#[Assert\Length(min: 8, max: 60, groups: ['Registration', 'PasswordUpdate', 'UserCreate', 'ResetPassword', 'ChangePassword'])]
private ?string $plainPassword = null;
#[ORM\Column(name: 'last_login', type: 'datetime', nullable: true)]
#[ORM\Column(name: 'last_login', type: Types::DATETIME_MUTABLE, nullable: true)]
private ?DateTime $lastLogin = null;
/**
* Random string sent to the user email address in order to verify it.
*/
#[ORM\Column(name: 'confirmation_token', type: 'string', length: 180, unique: true, nullable: true)]
#[ORM\Column(name: 'confirmation_token', type: Types::STRING, length: 180, unique: true, nullable: true)]
#[Assert\Length(max: 180)]
private ?string $confirmationToken = null;
#[ORM\Column(name: 'password_requested_at', type: 'datetime_immutable', nullable: true)]
#[ORM\Column(name: 'password_requested_at', type: Types::DATETIME_IMMUTABLE, nullable: true)]
private ?\DateTimeImmutable $passwordRequestedAt = null;
/**
* List of all role names
*/
#[ORM\Column(name: 'roles', type: 'array', nullable: false)]
#[ORM\Column(name: 'roles', type: Types::ARRAY, nullable: false)] // @phpstan-ignore classConstant.deprecated
#[Serializer\Expose]
#[Serializer\Groups(['User_Entity'])]
#[Serializer\Type('array<string>')]
@@ -213,11 +214,11 @@ class User implements UserInterface, EquatableInterface, ThemeUserInterface, Pas
* If not empty two-factor authentication is enabled.
* TODO reduce the length, which was initially forgotten and set to 255, as this is the default for MySQL with Doctrine (see migration Version20230126002049)
*/
#[ORM\Column(name: 'totp_secret', type: 'string', length: 255, nullable: true)]
#[ORM\Column(name: 'totp_secret', type: Types::STRING, length: 255, nullable: true)]
private ?string $totpSecret = null;
#[ORM\Column(name: 'totp_enabled', type: 'boolean', nullable: false, options: ['default' => false])]
#[ORM\Column(name: 'totp_enabled', type: Types::BOOLEAN, nullable: false, options: ['default' => false])]
private bool $totpEnabled = false;
#[ORM\Column(name: 'system_account', type: 'boolean', nullable: false, options: ['default' => false])]
#[ORM\Column(name: 'system_account', type: Types::BOOLEAN, nullable: false, options: ['default' => false])]
private bool $systemAccount = false;
#[ORM\ManyToOne(targetEntity: User::class)]
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]