Repository: urlaubsverwaltung/urlaubsverwaltung (java)

Issue:
Person mit Leerzeichen am Anfang/Ende des Namens kann nicht gelöscht werden
**Describe the bug**

Eine Person mit Leerzeichen am Anfang oder Ende des Namens kann quasi nicht gelöscht werden.

Der Hinweis zum Löschen zeigt die Leerzeichen nicht an.  Dort steht:

> Bitte gebe zur Bestätigung "Hans Dampf" ein.

Obwohl der Name in der Datenbank `Hans ` `Dampf ` ist.

**To Reproduce**
Steps to reproduce the behavior:
1. Starte Anwendung mit Demo Daten
2. Füge in der Datenbank ein Leereichen z. B. an den Vornamen von "Hans Dampf"
3. Melde dich mit `office` an
4. Löscche die Person "Hans Dampf"

**Expected behavior**

Person wird gelöscht

Pull request:
trim person username, firstName, lastName and email
fix #4767

Patch:
diff --git a/src/main/java/org/synyx/urlaubsverwaltung/person/PersonServiceImpl.java b/src/main/java/org/synyx/urlaubsverwaltung/person/PersonServiceImpl.java
index 561a9432c..c358137fd 100644
--- a/src/main/java/org/synyx/urlaubsverwaltung/person/PersonServiceImpl.java
+++ b/src/main/java/org/synyx/urlaubsverwaltung/person/PersonServiceImpl.java
@@ -99,7 +99,7 @@ class PersonServiceImpl implements PersonService {
     public Person create(String username, String firstName, String lastName, String email,
                          List<MailNotification> notifications, List<Role> permissions) {
 
-        final Person person = new Person(username, lastName, firstName, email);
+        final Person person = normalizePerson(new Person(username, lastName, firstName, email));
         person.setNotifications(notifications);
         person.setPermissions(permissions);
 
@@ -121,7 +121,7 @@ class PersonServiceImpl implements PersonService {
             throw new IllegalArgumentException("Can not update a person that is not persisted yet");
         }
 
-        final Person updatedPerson = personRepository.save(person);
+        final Person updatedPerson = personRepository.save(normalizePerson(person));
         LOG.info("Updated person: {}", updatedPerson);
 
         if (updatedPerson.isInactive()) {
@@ -251,6 +251,21 @@ class PersonServiceImpl implements PersonService {
         return personRepository.countByPermissionsContainingAndIdNotIn(OFFICE, List.of(excludingId));
     }
 
+    private Person normalizePerson(Person person) {
+        final Person normalized = new Person();
+
+        normalized.setUsername(person.getUsername().strip());
+        normalized.setLastName(person.getLastName().strip());
+        normalized.setFirstName(person.getFirstName().strip());
+        normalized.setEmail(person.getEmail().strip());
+
+        normalized.setId(person.getId());
+        normalized.setPermissions(person.getPermissions());
+        normalized.setNotifications(person.getNotifications());
+
+        return normalized;
+    }
+
     private static Sort mapToImplicitPersonSort(Sort requestedSort) {
         final Sort.Order firstNameOrder = requestedSort.getOrderFor("firstName");
         final Sort.Order lastNameOrder = requestedSort.getOrderFor("lastName");
diff --git a/src/main/resources/dbchangelogs/changelog-5.6.0-trim-person.xml b/src/main/resources/dbchangelogs/changelog-5.6.0-trim-person.xml
new file mode 100644
index 000000000..bb4f15f27
--- /dev/null
+++ b/src/main/resources/dbchangelogs/changelog-5.6.0-trim-person.xml
@@ -0,0 +1,41 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.27.xsd">
+
+  <changeSet author="seber" id="strip-person-username">
+    <preConditions>
+      <columnExists tableName="person" columnName="username"/>
+    </preConditions>
+    <sql>
+      update person set username = TRIM(username);
+    </sql>
+  </changeSet>
+
+  <changeSet author="seber" id="trim-person-firstname">
+    <preConditions>
+      <columnExists tableName="person" columnName="first_name"/>
+    </preConditions>
+    <sql>
+      update person set first_name = TRIM(first_name);
+    </sql>
+  </changeSet>
+
+  <changeSet author="seber" id="trim-person-lastname">
+    <preConditions>
+      <columnExists tableName="person" columnName="last_name"/>
+    </preConditions>
+    <sql>
+      update person set last_name = TRIM(last_name);
+    </sql>
+  </changeSet>
+
+  <changeSet author="seber" id="trim-person-email">
+    <preConditions>
+      <columnExists tableName="person" columnName="email"/>
+    </preConditions>
+    <sql>
+      update person set email = TRIM(email);
+    </sql>
+  </changeSet>
+</databaseChangeLog>
diff --git a/src/main/resources/dbchangelogs/changelogmaster.xml b/src/main/resources/dbchangelogs/changelogmaster.xml
index 7251a2f1c..7bdc517b1 100644
--- a/src/main/resources/dbchangelogs/changelogmaster.xml
+++ b/src/main/resources/dbchangelogs/changelogmaster.xml
@@ -10,4 +10,5 @@
   <include relativeToChangelogFile="true" file="changelog-5.0.0-RC1-migrate-calender-settings.xml"/>
   <include relativeToChangelogFile="true" file="changelog-5.1.0-add-expiry-date-to-account-settings.xml"/>
   <include relativeToChangelogFile="true" file="changelog-5.4.0-sicknote-submission-by-user.xml"/>
+  <include relativeToChangelogFile="true" file="changelog-5.6.0-trim-person.xml"/>
 </databaseChangeLog>


Test patch:
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/person/PersonServiceImplTest.java b/src/test/java/org/synyx/urlaubsverwaltung/person/PersonServiceImplTest.java
index 15497dfdf..d5f187eb7 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/person/PersonServiceImplTest.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/person/PersonServiceImplTest.java
@@ -94,6 +94,42 @@ class PersonServiceImplTest {
         assertThat(personCreatedEventArgumentCaptor.getValue().getUsername()).isEqualTo(person.getUsername());
     }
 
+    @Test
+    void ensureCreatedPersonHasStrippedUsername() {
+
+        when(personRepository.save(any(Person.class))).thenAnswer(returnsFirstArg());
+
+        final Person createdPerson = sut.create("  rick ", "", "", "r", List.of(), List.of());
+        assertThat(createdPerson.getUsername()).isEqualTo("rick");
+    }
+
+    @Test
+    void ensureCreatedPersonHasStrippedFirstName() {
+
+        when(personRepository.save(any(Person.class))).thenAnswer(returnsFirstArg());
+
+        final Person createdPerson = sut.create("", " Rick  ", "", "r", List.of(), List.of());
+        assertThat(createdPerson.getFirstName()).isEqualTo("Rick");
+    }
+
+    @Test
+    void ensureCreatedPersonHasStrippedLastName() {
+
+        when(personRepository.save(any(Person.class))).thenAnswer(returnsFirstArg());
+
+        final Person createdPerson = sut.create("", "", " Grimes  ", "r", List.of(), List.of());
+        assertThat(createdPerson.getLastName()).isEqualTo("Grimes");
+    }
+
+    @Test
+    void ensureCreatedPersonHasStrippedEmail() {
+
+        when(personRepository.save(any(Person.class))).thenAnswer(returnsFirstArg());
+
+        final Person createdPerson = sut.create("", "", "", " rick@grimes.de  ", List.of(), List.of());
+        assertThat(createdPerson.getEmail()).isEqualTo("rick@grimes.de");
+    }
+
     @Test
     void ensureCreatedPersonHasCorrectAttributes() {
 
@@ -155,6 +191,50 @@ class PersonServiceImplTest {
         verify(personRepository).save(person);
     }
 
+    @Test
+    void ensureUpdatedPersonHasStrippedUsername() {
+
+        final Person person = new Person(" muster  ", "", "", "");
+        person.setId(1L);
+        when(personRepository.save(person)).thenAnswer(returnsFirstArg());
+
+        final Person updatedPerson = sut.update(person);
+        assertThat(updatedPerson.getUsername()).isEqualTo("muster");
+    }
+
+    @Test
+    void ensureUpdatedPersonHasStrippedFirstName() {
+
+        final Person person = new Person("", "", " Marlene  ", "");
+        person.setId(1L);
+        when(personRepository.save(person)).thenAnswer(returnsFirstArg());
+
+        final Person updatedPerson = sut.update(person);
+        assertThat(updatedPerson.getFirstName()).isEqualTo("Marlene");
+    }
+
+    @Test
+    void ensureUpdatedPersonHasStrippedLastName() {
+
+        final Person person = new Person("", " Muster  ", "", "");
+        person.setId(1L);
+        when(personRepository.save(person)).thenAnswer(returnsFirstArg());
+
+        final Person updatedPerson = sut.update(person);
+        assertThat(updatedPerson.getLastName()).isEqualTo("Muster");
+    }
+
+    @Test
+    void ensureUpdatedPersonHasStrippedEmail() {
+
+        final Person person = new Person("", "", "", " muster@example.org  ");
+        person.setId(1L);
+        when(personRepository.save(person)).thenAnswer(returnsFirstArg());
+
+        final Person updatedPerson = sut.update(person);
+        assertThat(updatedPerson.getEmail()).isEqualTo("muster@example.org");
+    }
+
     @Test
     void ensureThrowsIfPersonToBeUpdatedHasNoID() {