diff --git a/docs/PowerAuth-Server-1.6.0.md b/docs/PowerAuth-Server-1.6.0.md
new file mode 100644
index 000000000..6101a7856
--- /dev/null
+++ b/docs/PowerAuth-Server-1.6.0.md
@@ -0,0 +1,9 @@
+# Migration from 1.5.x to 1.6.0
+
+This guide contains instructions for migration from PowerAuth Server version `1.5.x` to version `1.6.0`.
+
+## Database Changes
+
+### Forbid name duplication for operation templates.
+
+Add unique constraint to `templateName` column in `pa_operation_template` table.
diff --git a/docs/db/changelog/changesets/powerauth-java-server/1.6.x/20231018-add-constraint-operation-template-name.xml b/docs/db/changelog/changesets/powerauth-java-server/1.6.x/20231018-add-constraint-operation-template-name.xml
new file mode 100644
index 000000000..b44048f4d
--- /dev/null
+++ b/docs/db/changelog/changesets/powerauth-java-server/1.6.x/20231018-add-constraint-operation-template-name.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+ Add unique constraint to pa_operation_template.template_name
+
+
+
diff --git a/docs/db/changelog/changesets/powerauth-java-server/1.6.x/db.changelog-version.xml b/docs/db/changelog/changesets/powerauth-java-server/1.6.x/db.changelog-version.xml
new file mode 100644
index 000000000..d362d850e
--- /dev/null
+++ b/docs/db/changelog/changesets/powerauth-java-server/1.6.x/db.changelog-version.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/docs/db/changelog/changesets/powerauth-java-server/db.changelog-module.xml b/docs/db/changelog/changesets/powerauth-java-server/db.changelog-module.xml
index 59f6011e5..cae01c7d2 100644
--- a/docs/db/changelog/changesets/powerauth-java-server/db.changelog-module.xml
+++ b/docs/db/changelog/changesets/powerauth-java-server/db.changelog-module.xml
@@ -14,5 +14,6 @@
+
\ No newline at end of file
diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/OperationTemplateEntity.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/OperationTemplateEntity.java
index 144545087..d2667980c 100644
--- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/OperationTemplateEntity.java
+++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/database/model/entity/OperationTemplateEntity.java
@@ -45,7 +45,7 @@ public class OperationTemplateEntity implements Serializable {
@Column(name = "id")
private Long id;
- @Column(name = "template_name", nullable=false)
+ @Column(name = "template_name", nullable=false, unique = true)
private String templateName;
@Column(name = "operation_type", nullable=false)
diff --git a/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/database/repository/OperationTemplateRepositoryTest.java b/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/database/repository/OperationTemplateRepositoryTest.java
new file mode 100644
index 000000000..2e5efcb1f
--- /dev/null
+++ b/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/database/repository/OperationTemplateRepositoryTest.java
@@ -0,0 +1,76 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 Wultra s.r.o.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package io.getlime.security.powerauth.app.server.database.repository;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.getlime.security.powerauth.app.server.database.model.entity.OperationTemplateEntity;
+import io.getlime.security.powerauth.crypto.lib.enums.PowerAuthSignatureTypes;
+import org.hibernate.exception.ConstraintViolationException;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
+import org.springframework.context.annotation.Import;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Test for {@link OperationTemplateRepository}.
+ *
+ * @author Jan Pesek, jan.pesek@wultra.com
+ */
+@DataJpaTest
+@Import(ObjectMapper.class)
+class OperationTemplateRepositoryTest {
+
+ @Autowired
+ private OperationTemplateRepository repository;
+
+ @Autowired
+ private TestEntityManager entityManager;
+
+ @Test
+ void testDuplicateOperationTemplateCreation() {
+ final String templateName = "login";
+
+ repository.save(createOperationTemplateEntity(templateName));
+ entityManager.flush();
+
+ Optional entity = repository.findTemplateByName(templateName);
+ assertTrue(entity.isPresent());
+ assertEquals(templateName, entity.get().getTemplateName());
+
+ repository.save(createOperationTemplateEntity(templateName));
+ assertThrows(ConstraintViolationException.class, () -> entityManager.flush());
+ }
+
+ private static OperationTemplateEntity createOperationTemplateEntity(String templateName) {
+ final OperationTemplateEntity entity = new OperationTemplateEntity();
+ entity.setTemplateName(templateName);
+ entity.setOperationType(templateName);
+ entity.setDataTemplate("A2");
+ PowerAuthSignatureTypes[] signatureTypes = {PowerAuthSignatureTypes.POSSESSION};
+ entity.setSignatureType(signatureTypes);
+ entity.setMaxFailureCount(5L);
+ entity.setExpiration(300L);
+ return entity;
+ }
+
+}
diff --git a/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationTemplateServiceBehaviorTest.java b/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationTemplateServiceBehaviorTest.java
new file mode 100644
index 000000000..ab4b1782e
--- /dev/null
+++ b/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationTemplateServiceBehaviorTest.java
@@ -0,0 +1,64 @@
+/*
+ * PowerAuth Server and related software components
+ * Copyright (C) 2023 Wultra s.r.o.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package io.getlime.security.powerauth.app.server.service.behavior.tasks;
+
+import com.wultra.security.powerauth.client.model.enumeration.SignatureType;
+import com.wultra.security.powerauth.client.model.request.OperationTemplateCreateRequest;
+import io.getlime.security.powerauth.app.server.service.exceptions.GenericServiceException;
+import io.getlime.security.powerauth.app.server.service.model.ServiceError;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Test for {@link OperationTemplateServiceBehavior}.
+ *
+ * @author Jan Pesek, jan.pesek@wultra.com
+ */
+@SpringBootTest
+class OperationTemplateServiceBehaviorTest {
+
+ @Autowired
+ private OperationTemplateServiceBehavior service;
+
+ @Test
+ void testDuplicateOperationTemplateCreation() throws Exception {
+ final String templateName = "login";
+
+ service.createOperationTemplate(createOperationTemplateCreateRequest(templateName));
+ assertFalse(service.getAllTemplates().isEmpty());
+
+ final GenericServiceException exception = assertThrows(GenericServiceException.class, () ->
+ service.createOperationTemplate(createOperationTemplateCreateRequest(templateName)));
+ assertEquals(ServiceError.OPERATION_TEMPLATE_ALREADY_EXISTS, exception.getCode());
+ }
+
+ private static OperationTemplateCreateRequest createOperationTemplateCreateRequest(String templateName) {
+ final OperationTemplateCreateRequest request = new OperationTemplateCreateRequest();
+ request.setTemplateName(templateName);
+ request.setOperationType(templateName);
+ request.setDataTemplate("A2");
+ request.getSignatureType().add(SignatureType.POSSESSION_KNOWLEDGE);
+ request.setMaxFailureCount(5L);
+ request.setExpiration(300L);
+ return request;
+ }
+
+}