Handled unique constraints.
Change-Id: I0ee968b18b028ded76af04a0473c7997a13f2f11
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/NamedVCLoader.java b/full/src/main/java/de/ids_mannheim/korap/config/NamedVCLoader.java
index fc86f52..21216a8 100644
--- a/full/src/main/java/de/ids_mannheim/korap/config/NamedVCLoader.java
+++ b/full/src/main/java/de/ids_mannheim/korap/config/NamedVCLoader.java
@@ -16,11 +16,8 @@
import de.ids_mannheim.korap.KrillCollection;
import de.ids_mannheim.korap.constant.VirtualCorpusType;
-import de.ids_mannheim.korap.dao.VirtualCorpusDao;
import de.ids_mannheim.korap.exceptions.KustvaktException;
-import de.ids_mannheim.korap.exceptions.StatusCodes;
import de.ids_mannheim.korap.service.VirtualCorpusService;
-import de.ids_mannheim.korap.user.User.CorpusAccess;
import de.ids_mannheim.korap.util.QueryException;
import de.ids_mannheim.korap.web.SearchKrill;
@@ -31,8 +28,6 @@
@Autowired
private SearchKrill searchKrill;
@Autowired
- private VirtualCorpusDao vcDao;
- @Autowired
private VirtualCorpusService vcService;
private static Logger jlog = LogManager.getLogger(NamedVCLoader.class);
@@ -44,7 +39,8 @@
String json = IOUtils.toString(is, "utf-8");
if (json != null) {
cacheVC(json, filename);
- storeVC(filename, json);
+ vcService.storeVC(filename, VirtualCorpusType.SYSTEM, json, null,
+ null, null, true, "system");
}
}
@@ -66,7 +62,8 @@
String json = readFile(file, filename);
if (json != null) {
cacheVC(json, filename);
- storeVC(filename, json);
+ vcService.storeVC(filename, VirtualCorpusType.SYSTEM, json, null,
+ null, null, true, "system");
}
}
}
@@ -113,17 +110,4 @@
+ KrillCollection.cache.calculateInMemorySize());
}
- private void storeVC (String name, String koralQuery)
- throws KustvaktException {
- if (!VirtualCorpusService.wordPattern.matcher(name).matches()) {
- throw new KustvaktException(StatusCodes.INVALID_ARGUMENT,
- "Virtual corpus name must only contains letters, numbers, "
- + "underscores, hypens and spaces",
- name);
- }
- CorpusAccess requiredAccess =
- vcService.determineRequiredAccess(koralQuery);
- vcDao.createVirtualCorpus(name, VirtualCorpusType.SYSTEM,
- requiredAccess, koralQuery, null, null, null, true, "system");
- }
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/dao/RoleDao.java b/full/src/main/java/de/ids_mannheim/korap/dao/RoleDao.java
index 8898685..bd24b21 100644
--- a/full/src/main/java/de/ids_mannheim/korap/dao/RoleDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/dao/RoleDao.java
@@ -1,6 +1,8 @@
package de.ids_mannheim.korap.dao;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@@ -78,8 +80,7 @@
return (Role) q.getSingleResult();
}
- @SuppressWarnings("unchecked")
- public List<Role> retrieveRoleByGroupMemberId (int userId) {
+ public Set<Role> retrieveRoleByGroupMemberId (int userId) {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Role> query = criteriaBuilder.createQuery(Role.class);
@@ -91,7 +92,9 @@
query.where(criteriaBuilder.equal(memberRole.get(UserGroupMember_.id),
userId));
Query q = entityManager.createQuery(query);
- return q.getResultList();
+ @SuppressWarnings("unchecked")
+ List<Role> resultList = q.getResultList();
+ return new HashSet<Role>(resultList);
}
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/entity/Role.java b/full/src/main/java/de/ids_mannheim/korap/entity/Role.java
index 1474316..0eaf767 100644
--- a/full/src/main/java/de/ids_mannheim/korap/entity/Role.java
+++ b/full/src/main/java/de/ids_mannheim/korap/entity/Role.java
@@ -16,9 +16,10 @@
import lombok.Getter;
import lombok.Setter;
-/** Describes user roles for example in managing a group or
- * virtual corpora of a group.
- *
+/**
+ * Describes user roles for example in managing a group or
+ * virtual corpora of a group.
+ *
* @author margaretha
* @see Privilege
*/
@@ -26,7 +27,7 @@
@Getter
@Entity
@Table(name = "role")
-public class Role implements Comparable<Role>{
+public class Role implements Comparable<Role> {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@@ -46,12 +47,29 @@
@Override
public int compareTo (Role o) {
- if (this.getId() > o.getId()){
+ if (this.getId() > o.getId()) {
return 1;
}
- else if (this.getId() < o.getId()){
+ else if (this.getId() < o.getId()) {
return -1;
}
return 0;
}
+
+ @Override
+ public boolean equals (Object obj) {
+ Role r = (Role) obj;
+ if (this.id == r.getId() && this.name.equals(r.getName())) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode () {
+ int hash = 7;
+ hash = 31 * hash + (int) id;
+ hash = 31 * hash + (name == null ? 0 : name.hashCode());
+ return hash;
+ }
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/constant/OAuth2Scope.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/constant/OAuth2Scope.java
index c44cc53..0dc20d5 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/constant/OAuth2Scope.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/constant/OAuth2Scope.java
@@ -24,7 +24,8 @@
DELETE_USER_GROUP_MEMBER,
ADD_USER_GROUP_MEMBER,
- ADD_USER_GROUP_MEMBER_ROLE,
+ EDIT_USER_GROUP_MEMBER_ROLE,
+ ADD_USER_GROUP_MEMBER_ROLE,
DELETE_USER_GROUP_MEMBER_ROLE,
CREATE_VC,
diff --git a/full/src/main/java/de/ids_mannheim/korap/service/UserGroupService.java b/full/src/main/java/de/ids_mannheim/korap/service/UserGroupService.java
index fdcc4ab..9133169 100644
--- a/full/src/main/java/de/ids_mannheim/korap/service/UserGroupService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/service/UserGroupService.java
@@ -32,7 +32,9 @@
import de.ids_mannheim.korap.web.controller.UserGroupController;
import de.ids_mannheim.korap.web.input.UserGroupJson;
-/** UserGroupService defines the logic behind user group web controller.
+/**
+ * UserGroupService defines the logic behind user group web
+ * controller.
*
* @see UserGroupController
*
@@ -42,8 +44,7 @@
@Service
public class UserGroupService {
- private static Logger jlog =
- LogManager.getLogger(UserGroupService.class);
+ private static Logger jlog = LogManager.getLogger(UserGroupService.class);
@Autowired
private UserGroupDao userGroupDao;
@Autowired
@@ -61,10 +62,12 @@
private static Set<Role> memberRoles;
- /** Only users with {@link PredefinedRole#USER_GROUP_ADMIN}
+ /**
+ * Only users with {@link PredefinedRole#USER_GROUP_ADMIN}
* are allowed to see the members of the group.
*
- * @param username username
+ * @param username
+ * username
* @return a list of usergroups
* @throws KustvaktException
*
@@ -166,24 +169,27 @@
}
}
- /** Group owner is automatically added when creating a group.
- * Do not include owners in group members.
- *
- * {@link PredefinedRole#USER_GROUP_MEMBER} and
- * {@link PredefinedRole#VC_ACCESS_MEMBER} roles are
- * automatically assigned to each group member.
- *
- * {@link PredefinedRole#USER_GROUP_MEMBER} restrict users
- * to see other group members and allow users to remove
- * themselves from the groups.
- *
- * {@link PredefinedRole#VC_ACCESS_MEMBER} allow user to
- * read group VC.
+ /**
+ * Group owner is automatically added when creating a group.
+ * Do not include owners in group members.
+ *
+ * {@link PredefinedRole#USER_GROUP_MEMBER} and
+ * {@link PredefinedRole#VC_ACCESS_MEMBER} roles are
+ * automatically assigned to each group member.
+ *
+ * {@link PredefinedRole#USER_GROUP_MEMBER} restrict users
+ * to see other group members and allow users to remove
+ * themselves from the groups.
+ *
+ * {@link PredefinedRole#VC_ACCESS_MEMBER} allow user to
+ * read group VC.
*
* @see /full/src/main/resources/db/predefined/V3.2__insert_predefined_roles.sql
*
- * @param groupJson UserGroupJson object from json
- * @param createdBy the user creating the group
+ * @param groupJson
+ * UserGroupJson object from json
+ * @param createdBy
+ * the user creating the group
* @throws KustvaktException
*
*
@@ -240,19 +246,26 @@
config.isSoftDeleteAutoGroup());
}
- /** Adds a user to the specified usergroup. If the username with
- * {@link GroupMemberStatus} DELETED exists as a member of the group,
- * the entry will be deleted first, and a new entry will be added.
- *
- * If a username with other statuses exists, a KustvaktException will
- * be thrown.
+ /**
+ * Adds a user to the specified usergroup. If the username with
+ * {@link GroupMemberStatus} DELETED exists as a member of the
+ * group,
+ * the entry will be deleted first, and a new entry will be added.
+ *
+ * If a username with other statuses exists, a KustvaktException
+ * will
+ * be thrown.
*
* @see GroupMemberStatus
*
- * @param username a username
- * @param userGroup a user group
- * @param createdBy the user (VCA/system) adding the user the user-group
- * @param status the status of the membership
+ * @param username
+ * a username
+ * @param userGroup
+ * a user group
+ * @param createdBy
+ * the user (VCA/system) adding the user the user-group
+ * @param status
+ * the status of the membership
* @throws KustvaktException
*/
public void inviteGroupMember (String username, UserGroup userGroup,
@@ -357,11 +370,15 @@
return false;
}
- /** Updates the {@link GroupMemberStatus} of a pending member
- * to {@link GroupMemberStatus#ACTIVE} and add default member roles.
+ /**
+ * Updates the {@link GroupMemberStatus} of a pending member
+ * to {@link GroupMemberStatus#ACTIVE} and add default member
+ * roles.
*
- * @param groupId groupId
- * @param username the username of the group member
+ * @param groupId
+ * groupId
+ * @param username
+ * the username of the group member
* @throws KustvaktException
*/
public void acceptInvitation (int groupId, String username)
@@ -452,14 +469,19 @@
}
}
- /** Updates the {@link GroupMemberStatus} of a member to
+ /**
+ * Updates the {@link GroupMemberStatus} of a member to
* {@link GroupMemberStatus#DELETED}
*
- * @param userId user to be deleted
- * @param groupId user-group id
- * @param deletedBy user that issue the delete
- * @param isSoftDelete true if database entry is to be deleted
- * permanently, false otherwise
+ * @param userId
+ * user to be deleted
+ * @param groupId
+ * user-group id
+ * @param deletedBy
+ * user that issue the delete
+ * @param isSoftDelete
+ * true if database entry is to be deleted
+ * permanently, false otherwise
* @throws KustvaktException
*/
private void doDeleteMember (String username, int groupId, String deletedBy,
@@ -495,8 +517,9 @@
}
- public void addMemberRoles (String username, int groupId,
- String memberUsername, List<Integer> roleIds) throws KustvaktException {
+ public void editMemberRoles (String username, int groupId,
+ String memberUsername, List<Integer> roleIds)
+ throws KustvaktException {
ParameterChecker.checkIntegerValue(groupId, "groupId");
ParameterChecker.checkStringValue(username, "username");
@@ -504,9 +527,49 @@
UserGroup userGroup = userGroupDao.retrieveGroupById(groupId, true);
UserGroupStatus groupStatus = userGroup.getStatus();
- if (groupStatus == UserGroupStatus.DELETED){
+ if (groupStatus == UserGroupStatus.DELETED) {
throw new KustvaktException(StatusCodes.GROUP_DELETED,
- "Usergroup has been deleted.");
+ "Usergroup has been deleted.");
+ }
+ else if (isUserGroupAdmin(username, userGroup)
+ || adminDao.isAdmin(username)) {
+
+ UserGroupMember member =
+ groupMemberDao.retrieveMemberById(memberUsername, groupId);
+
+ if (!member.getStatus().equals(GroupMemberStatus.ACTIVE)) {
+ throw new KustvaktException(StatusCodes.GROUP_MEMBER_INACTIVE,
+ memberUsername + " has status " + member.getStatus(),
+ memberUsername, member.getStatus().name());
+ }
+
+ Set<Role> roles = new HashSet<>();
+ for (int i = 0; i < roleIds.size(); i++) {
+ roles.add(roleDao.retrieveRoleById(roleIds.get(i)));
+ }
+ member.setRoles(roles);
+ groupMemberDao.updateMember(member);
+
+ }
+ else {
+ throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
+ "Unauthorized operation for user: " + username, username);
+ }
+ }
+
+ public void addMemberRoles (String username, int groupId,
+ String memberUsername, List<Integer> roleIds)
+ throws KustvaktException {
+
+ ParameterChecker.checkIntegerValue(groupId, "groupId");
+ ParameterChecker.checkStringValue(username, "username");
+ ParameterChecker.checkStringValue(memberUsername, "memberUsername");
+
+ UserGroup userGroup = userGroupDao.retrieveGroupById(groupId, true);
+ UserGroupStatus groupStatus = userGroup.getStatus();
+ if (groupStatus == UserGroupStatus.DELETED) {
+ throw new KustvaktException(StatusCodes.GROUP_DELETED,
+ "Usergroup has been deleted.");
}
else if (isUserGroupAdmin(username, userGroup)
|| adminDao.isAdmin(username)) {
@@ -535,7 +598,8 @@
}
public void deleteMemberRoles (String username, int groupId,
- String memberUsername, List<Integer> roleIds) throws KustvaktException {
+ String memberUsername, List<Integer> roleIds)
+ throws KustvaktException {
ParameterChecker.checkIntegerValue(groupId, "groupId");
ParameterChecker.checkStringValue(username, "username");
@@ -551,8 +615,8 @@
Set<Role> roles = member.getRoles();
Iterator<Role> i = roles.iterator();
- while (i.hasNext()){
- if (roleIds.contains(i.next().getId())){
+ while (i.hasNext()) {
+ if (roleIds.contains(i.next().getId())) {
i.remove();
}
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/service/VirtualCorpusService.java b/full/src/main/java/de/ids_mannheim/korap/service/VirtualCorpusService.java
index 33af77b..5d2ce4b 100644
--- a/full/src/main/java/de/ids_mannheim/korap/service/VirtualCorpusService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/service/VirtualCorpusService.java
@@ -1,5 +1,6 @@
package de.ids_mannheim.korap.service;
+import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
@@ -212,7 +213,7 @@
// check if hidden access exists
if (access == null) {
VirtualCorpus vc = vcDao.retrieveVCById(vcId);
- // create and assign a hidden group
+ // create and assign a new hidden group
int groupId = userGroupService.createAutoHiddenGroup(vcId);
UserGroup autoHidden =
userGroupService.retrieveUserGroupById(groupId);
@@ -220,18 +221,28 @@
VirtualCorpusAccessStatus.HIDDEN);
}
else {
+ // should not happened
jlog.error("Cannot publish VC with id: " + vcId
- + ". There have been hidden accesses for the VC already.");
+ + ". Hidden access exists! Access id: " + access.getId());
}
}
public int storeVC (VirtualCorpusJson vc, String username)
throws KustvaktException {
- ParameterChecker.checkStringValue(vc.getName(), "name");
- ParameterChecker.checkObjectValue(vc.getType(), "type");
ParameterChecker.checkStringValue(vc.getCorpusQuery(), "corpusQuery");
+ String koralQuery = serializeCorpusQuery(vc.getCorpusQuery());
- String name = vc.getName();
+ return storeVC(vc.getName(), vc.getType(), koralQuery,
+ vc.getDefinition(), vc.getDescription(), vc.getStatus(),
+ vc.isCached(), username);
+ }
+
+ public int storeVC (String name, VirtualCorpusType type, String koralQuery,
+ String definition, String description, String status,
+ boolean isCached, String username) throws KustvaktException {
+ ParameterChecker.checkStringValue(name, "name");
+ ParameterChecker.checkObjectValue(type, "type");
+
if (!wordPattern.matcher(name).matches()) {
throw new KustvaktException(StatusCodes.INVALID_ARGUMENT,
"Virtual corpus name must only contains letters, numbers, "
@@ -239,20 +250,36 @@
name);
}
- if (vc.getType().equals(VirtualCorpusType.SYSTEM)
+ if (type.equals(VirtualCorpusType.SYSTEM)
+ && !username.equals("system")
&& !adminDao.isAdmin(username)) {
throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
"Unauthorized operation for user: " + username, username);
}
- String koralQuery = serializeCorpusQuery(vc.getCorpusQuery());
CorpusAccess requiredAccess = determineRequiredAccess(koralQuery);
- int vcId = vcDao.createVirtualCorpus(vc.getName(), vc.getType(),
- requiredAccess, koralQuery, vc.getDefinition(),
- vc.getDescription(), vc.getStatus(), vc.isCached(), username);
+ int vcId = 0;
+ try {
+ vcId = vcDao.createVirtualCorpus(name, type, requiredAccess,
+ koralQuery, definition, description, status, isCached,
+ username);
- if (vc.getType().equals(VirtualCorpusType.PUBLISHED)) {
+ }
+ catch (Exception e) {
+ Throwable cause = e;
+ Throwable lastCause = null;
+ while ((cause = cause.getCause()) != null
+ && !cause.equals(lastCause)) {
+ if (cause instanceof SQLException) {
+ break;
+ }
+ lastCause = cause;
+ }
+ throw new KustvaktException(StatusCodes.DB_INSERT_FAILED,
+ cause.getMessage());
+ }
+ if (type.equals(VirtualCorpusType.PUBLISHED)) {
publishVC(vcId);
}
// EM: should this return anything?
@@ -326,8 +353,23 @@
"Unauthorized operation for user: " + username, username);
}
else {
- accessDao.createAccessToVC(vc, userGroup, username,
- VirtualCorpusAccessStatus.ACTIVE);
+ try {
+ accessDao.createAccessToVC(vc, userGroup, username,
+ VirtualCorpusAccessStatus.ACTIVE);
+ }
+ catch (Exception e) {
+ Throwable cause = e;
+ Throwable lastCause = null;
+ while ((cause = cause.getCause()) != null
+ && !cause.equals(lastCause)) {
+ if (cause instanceof SQLException) {
+ break;
+ }
+ lastCause = cause;
+ }
+ throw new KustvaktException(StatusCodes.DB_INSERT_FAILED,
+ cause.getMessage());
+ }
vcDao.editVirtualCorpus(vc, null, VirtualCorpusType.PUBLISHED, null,
null, null, null, null);
}
@@ -417,8 +459,8 @@
}
- public VirtualCorpus searchVCByName (String username, String vcName, String createdBy)
- throws KustvaktException {
+ public VirtualCorpus searchVCByName (String username, String vcName,
+ String createdBy) throws KustvaktException {
VirtualCorpus vc = vcDao.retrieveVCByName(vcName, createdBy);
checkVCAccess(vc, username);
return vc;
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
index 3d9aeb9..6c9427d 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
@@ -24,12 +24,13 @@
import de.ids_mannheim.korap.constant.UserGroupStatus;
import de.ids_mannheim.korap.dto.UserGroupDto;
import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
import de.ids_mannheim.korap.oauth2.constant.OAuth2Scope;
import de.ids_mannheim.korap.oauth2.service.OAuth2ScopeService;
import de.ids_mannheim.korap.security.context.TokenContext;
import de.ids_mannheim.korap.service.UserGroupService;
-import de.ids_mannheim.korap.web.KustvaktResponseHandler;
import de.ids_mannheim.korap.web.APIVersionFilter;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
import de.ids_mannheim.korap.web.filter.BlockingFilter;
import de.ids_mannheim.korap.web.filter.PiwikFilter;
@@ -267,6 +268,35 @@
}
}
+ @POST
+ @Path("member/role/edit")
+ @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+ public Response editMemberRoles (@Context SecurityContext securityContext,
+ @FormParam("groupId") int groupId,
+ @FormParam("memberUsername") String memberUsername,
+ @FormParam("roleIds") List<Integer> roleIds,
+ @PathParam("version") String version) {
+ double v = Double.valueOf(version.substring(1, version.length()));
+ if (v < 1.1) {
+ throw kustvaktResponseHandler.throwit(new KustvaktException(
+ StatusCodes.UNSUPPORTED_API_VERSION,
+ "Method is not supported in version " + version, version));
+ }
+
+ TokenContext context =
+ (TokenContext) securityContext.getUserPrincipal();
+ try {
+ scopeService.verifyScope(context,
+ OAuth2Scope.EDIT_USER_GROUP_MEMBER_ROLE);
+ service.editMemberRoles(context.getUsername(), groupId,
+ memberUsername, roleIds);
+ return Response.ok().build();
+ }
+ catch (KustvaktException e) {
+ throw kustvaktResponseHandler.throwit(e);
+ }
+ }
+
/**
* Adds roles of an active member of a user-group. Only user-group
* admins
diff --git a/full/src/main/resources/db/new-mysql/V1.1__create_virtual_corpus_tables.sql b/full/src/main/resources/db/new-mysql/V1.1__create_virtual_corpus_tables.sql
index 55f2163..eb9d1df 100644
--- a/full/src/main/resources/db/new-mysql/V1.1__create_virtual_corpus_tables.sql
+++ b/full/src/main/resources/db/new-mysql/V1.1__create_virtual_corpus_tables.sql
@@ -64,6 +64,7 @@
corpus_query TEXT NOT NULL,
definition varchar(255) DEFAULT NULL,
is_cached BOOLEAN DEFAULT 0,
+ UNIQUE INDEX unique_index (name,created_by),
INDEX owner_index (created_by),
INDEX type_index (type)
);
diff --git a/full/src/main/resources/db/new-sqlite/V1.1__create_virtual_corpus_tables.sql b/full/src/main/resources/db/new-sqlite/V1.1__create_virtual_corpus_tables.sql
index 56fc105..0547591 100644
--- a/full/src/main/resources/db/new-sqlite/V1.1__create_virtual_corpus_tables.sql
+++ b/full/src/main/resources/db/new-sqlite/V1.1__create_virtual_corpus_tables.sql
@@ -78,6 +78,8 @@
CREATE INDEX virtual_corpus_owner_index ON virtual_corpus(created_by);
CREATE INDEX virtual_corpus_type_index ON virtual_corpus(type);
+CREATE UNIQUE INDEX virtual_corpus_unique_name
+ ON virtual_corpus(name,created_by);
CREATE TABLE IF NOT EXISTS virtual_corpus_access (
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -97,6 +99,6 @@
CREATE INDEX virtual_corpus_status_index
ON virtual_corpus_access(status);
-CREATE INDEX virtual_corpus_access_unique_index
+CREATE UNIQUE INDEX virtual_corpus_access_unique_index
ON virtual_corpus_access(virtual_corpus_id,user_group_id);
diff --git a/full/src/main/resources/db/new-sqlite/V1.4__oauth2_tables.sql b/full/src/main/resources/db/new-sqlite/V1.4__oauth2_tables.sql
index db238ab..e0c06d6 100644
--- a/full/src/main/resources/db/new-sqlite/V1.4__oauth2_tables.sql
+++ b/full/src/main/resources/db/new-sqlite/V1.4__oauth2_tables.sql
@@ -20,8 +20,6 @@
REFERENCES oauth2_client_url(url_hashcode)
);
-CREATE UNIQUE INDEX client_id_index on oauth2_client(id);
-
CREATE TABLE IF NOT EXISTS oauth2_access_scope (
id VARCHAR(255) PRIMARY KEY NOT NULL
);
diff --git a/full/src/main/resources/kustvakt.conf b/full/src/main/resources/kustvakt.conf
index 3932b82..c5a4958 100644
--- a/full/src/main/resources/kustvakt.conf
+++ b/full/src/main/resources/kustvakt.conf
@@ -12,7 +12,7 @@
ldap.config = file-path-to-ldap-config
# Kustvakt
-current.api.version = v1.0
+current.api.version = v1.1
# multiple versions separated by space
supported.api.version = v1.0