allowedActions) {
+ return new UserDevfilePermissionImpl(instanceId, userId, allowedActions);
+ }
+}
diff --git a/multiuser/permission/che-multiuser-permission-userdevfile/src/main/java/org/eclipse/che/multiuser/permission/devfile/server/filters/UserDevfilePermissionsFilter.java b/multiuser/permission/che-multiuser-permission-userdevfile/src/main/java/org/eclipse/che/multiuser/permission/devfile/server/filters/UserDevfilePermissionsFilter.java
new file mode 100644
index 00000000000..4eed25619e5
--- /dev/null
+++ b/multiuser/permission/che-multiuser-permission-userdevfile/src/main/java/org/eclipse/che/multiuser/permission/devfile/server/filters/UserDevfilePermissionsFilter.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2012-2018 Red Hat, Inc.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.multiuser.permission.devfile.server.filters;
+
+import static org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain.DELETE;
+import static org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain.DOMAIN_ID;
+import static org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain.READ;
+import static org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain.UPDATE;
+
+import com.google.common.annotations.VisibleForTesting;
+import javax.inject.Inject;
+import javax.ws.rs.Path;
+import org.eclipse.che.api.core.ForbiddenException;
+import org.eclipse.che.api.devfile.server.DevfileService;
+import org.eclipse.che.api.devfile.server.UserDevfileManager;
+import org.eclipse.che.commons.env.EnvironmentContext;
+import org.eclipse.che.everrest.CheMethodInvokerFilter;
+import org.everrest.core.Filter;
+import org.everrest.core.resource.GenericResourceMethod;
+
+/**
+ * Restricts access to methods of {@link DevfileService} by users' permissions.
+ *
+ * Filter contains rules for protecting of all methods of {@link DevfileService}.
+ * In case when requested method is unknown filter throws {@link ForbiddenException}
+ */
+@Filter
+@Path("/devfile{path:(/.*)?}")
+public class UserDevfilePermissionsFilter extends CheMethodInvokerFilter {
+ private final UserDevfileManager userDevfileManager;
+
+ @Inject
+ public UserDevfilePermissionsFilter(UserDevfileManager userDevfileManager) {
+ this.userDevfileManager = userDevfileManager;
+ }
+
+ @Override
+ public void filter(GenericResourceMethod genericResourceMethod, Object[] arguments)
+ throws ForbiddenException {
+ final String methodName = genericResourceMethod.getMethod().getName();
+ switch (methodName) {
+ case "getById":
+ doCheckPermission(DOMAIN_ID, ((String) arguments[0]), READ);
+ break;
+ case "update":
+ doCheckPermission(DOMAIN_ID, ((String) arguments[0]), UPDATE);
+ break;
+ case "delete":
+ doCheckPermission(DOMAIN_ID, ((String) arguments[0]), DELETE);
+ break;
+ case "create":
+ case "getUserDevfiles":
+ case "getSchema":
+ return;
+ default:
+ throw new ForbiddenException("The user does not have permission to perform this operation");
+ }
+ }
+
+ @VisibleForTesting
+ void doCheckPermission(String domain, String instance, String action) throws ForbiddenException {
+ EnvironmentContext.getCurrent().getSubject().checkPermission(domain, instance, action);
+ }
+}
diff --git a/multiuser/permission/che-multiuser-permission-userdevfile/src/main/java/org/eclipse/che/multiuser/permission/devfile/server/jpa/MultiuserUserDevfileJpaModule.java b/multiuser/permission/che-multiuser-permission-userdevfile/src/main/java/org/eclipse/che/multiuser/permission/devfile/server/jpa/MultiuserUserDevfileJpaModule.java
new file mode 100644
index 00000000000..a7e882d662f
--- /dev/null
+++ b/multiuser/permission/che-multiuser-permission-userdevfile/src/main/java/org/eclipse/che/multiuser/permission/devfile/server/jpa/MultiuserUserDevfileJpaModule.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2012-2018 Red Hat, Inc.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.multiuser.permission.devfile.server.jpa;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.TypeLiteral;
+import com.google.inject.multibindings.Multibinder;
+import org.eclipse.che.api.devfile.server.spi.UserDevfileDao;
+import org.eclipse.che.multiuser.api.permission.server.AbstractPermissionsDomain;
+import org.eclipse.che.multiuser.api.permission.server.model.impl.AbstractPermissions;
+import org.eclipse.che.multiuser.api.permission.server.spi.PermissionsDao;
+import org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain;
+import org.eclipse.che.multiuser.permission.devfile.server.listener.RemoveUserDevfileOnLastUserRemovedEventSubscriber;
+import org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl;
+import org.eclipse.che.multiuser.permission.devfile.server.spi.UserDevfilePermissionDao;
+import org.eclipse.che.multiuser.permission.devfile.server.spi.jpa.JpaUserDevfilePermissionDao;
+import org.eclipse.che.multiuser.permission.devfile.server.spi.jpa.MultiuserJpaUserDevfileDao;
+
+public class MultiuserUserDevfileJpaModule extends AbstractModule {
+
+ @Override
+ protected void configure() {
+ bind(UserDevfilePermissionDao.class).to(JpaUserDevfilePermissionDao.class);
+ bind(UserDevfileDao.class).to(MultiuserJpaUserDevfileDao.class);
+ bind(JpaUserDevfilePermissionDao
+ .RemoveUserDevfilePermissionsBeforeUserDevfuleRemovedEventSubscriber.class)
+ .asEagerSingleton();
+
+ bind(RemoveUserDevfileOnLastUserRemovedEventSubscriber.class).asEagerSingleton();
+
+ bind(new TypeLiteral>() {})
+ .to(UserDevfileDomain.class);
+
+ Multibinder> daos =
+ Multibinder.newSetBinder(
+ binder(), new TypeLiteral>() {});
+ daos.addBinding().to(JpaUserDevfilePermissionDao.class);
+ }
+}
diff --git a/multiuser/permission/che-multiuser-permission-userdevfile/src/main/java/org/eclipse/che/multiuser/permission/devfile/server/listener/RemoveUserDevfileOnLastUserRemovedEventSubscriber.java b/multiuser/permission/che-multiuser-permission-userdevfile/src/main/java/org/eclipse/che/multiuser/permission/devfile/server/listener/RemoveUserDevfileOnLastUserRemovedEventSubscriber.java
new file mode 100644
index 00000000000..81fa0e841ef
--- /dev/null
+++ b/multiuser/permission/che-multiuser-permission-userdevfile/src/main/java/org/eclipse/che/multiuser/permission/devfile/server/listener/RemoveUserDevfileOnLastUserRemovedEventSubscriber.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012-2018 Red Hat, Inc.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.multiuser.permission.devfile.server.listener;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import org.eclipse.che.api.core.ServerException;
+import org.eclipse.che.api.devfile.server.UserDevfileManager;
+import org.eclipse.che.api.user.server.model.impl.UserImpl;
+import org.eclipse.che.multiuser.api.permission.server.jpa.listener.RemovePermissionsOnLastUserRemovedEventSubscriber;
+import org.eclipse.che.multiuser.permission.devfile.server.spi.jpa.JpaUserDevfilePermissionDao;
+
+/**
+ * Listens for {@link UserImpl} removal events, and checks if the removing user is the last who has
+ * "setPermissions" permission to particular user devfile, and if it is, then removes devfile
+ * itself.
+ */
+@Singleton
+public class RemoveUserDevfileOnLastUserRemovedEventSubscriber
+ extends RemovePermissionsOnLastUserRemovedEventSubscriber {
+
+ @Inject private UserDevfileManager userDevfileManager;
+
+ @Override
+ public void remove(String instanceId) throws ServerException {
+ userDevfileManager.removeUserDevfile(instanceId);
+ }
+}
diff --git a/multiuser/permission/che-multiuser-permission-userdevfile/src/main/java/org/eclipse/che/multiuser/permission/devfile/server/model/UserDevfilePermission.java b/multiuser/permission/che-multiuser-permission-userdevfile/src/main/java/org/eclipse/che/multiuser/permission/devfile/server/model/UserDevfilePermission.java
new file mode 100644
index 00000000000..97fdf87cb16
--- /dev/null
+++ b/multiuser/permission/che-multiuser-permission-userdevfile/src/main/java/org/eclipse/che/multiuser/permission/devfile/server/model/UserDevfilePermission.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2012-2018 Red Hat, Inc.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.multiuser.permission.devfile.server.model;
+
+import java.util.List;
+
+public interface UserDevfilePermission {
+ /** Returns user id */
+ String getUserId();
+
+ /** Returns user devfile id */
+ String getUserDevfileId();
+
+ /** Returns list of user devfile actions which can be performed by current user */
+ List getActions();
+}
diff --git a/multiuser/permission/che-multiuser-permission-userdevfile/src/main/java/org/eclipse/che/multiuser/permission/devfile/server/model/impl/UserDevfilePermissionImpl.java b/multiuser/permission/che-multiuser-permission-userdevfile/src/main/java/org/eclipse/che/multiuser/permission/devfile/server/model/impl/UserDevfilePermissionImpl.java
new file mode 100644
index 00000000000..f5c7166602e
--- /dev/null
+++ b/multiuser/permission/che-multiuser-permission-userdevfile/src/main/java/org/eclipse/che/multiuser/permission/devfile/server/model/impl/UserDevfilePermissionImpl.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2012-2018 Red Hat, Inc.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.multiuser.permission.devfile.server.model.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.persistence.CollectionTable;
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.QueryHint;
+import javax.persistence.Table;
+import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
+import org.eclipse.che.multiuser.api.permission.server.model.impl.AbstractPermissions;
+import org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain;
+import org.eclipse.che.multiuser.permission.devfile.server.model.UserDevfilePermission;
+
+/** Data object for {@link UserDevfilePermission} */
+@Entity(name = "UserDevfilePermission")
+@NamedQueries({
+ @NamedQuery(
+ name = "UserDevfilePermission.getByUserDevfileId",
+ query =
+ "SELECT permission "
+ + "FROM UserDevfilePermission permission "
+ + "WHERE permission.userDevfileId = :userDevfileId "),
+ @NamedQuery(
+ name = "UserDevfilePermission.getCountByUserDevfileId",
+ query =
+ "SELECT COUNT(permission) "
+ + "FROM UserDevfilePermission permission "
+ + "WHERE permission.userDevfileId = :userDevfileId "),
+ @NamedQuery(
+ name = "UserDevfilePermission.getByUserId",
+ query =
+ "SELECT permission "
+ + "FROM UserDevfilePermission permission "
+ + "WHERE permission.userId = :userId "),
+ @NamedQuery(
+ name = "UserDevfilePermission.getByUserAndUserDevfileId",
+ query =
+ "SELECT permission "
+ + "FROM UserDevfilePermission permission "
+ + "WHERE permission.userId = :userId "
+ + "AND permission.userDevfileId = :userDevfileId ",
+ hints = {@QueryHint(name = "eclipselink.query-results-cache", value = "true")})
+})
+@Table(name = "che_userdevfile_permissions")
+public class UserDevfilePermissionImpl extends AbstractPermissions
+ implements UserDevfilePermission {
+
+ @Column(name = "userdevfile_id")
+ private String userDevfileId;
+
+ @ManyToOne
+ @JoinColumn(name = "userdevfile_id", insertable = false, updatable = false)
+ private UserDevfileImpl userDevfile;
+
+ @ElementCollection(fetch = FetchType.EAGER)
+ @Column(name = "actions")
+ @CollectionTable(
+ name = "che_userdevfile_permissions_actions",
+ joinColumns = @JoinColumn(name = "userdevfile_permissions_id"))
+ protected List actions;
+
+ public UserDevfilePermissionImpl() {}
+
+ public UserDevfilePermissionImpl(String userDevfileId, String userId, List actions) {
+ super(userId);
+ this.userDevfileId = userDevfileId;
+ if (actions != null) {
+ this.actions = new ArrayList<>(actions);
+ }
+ }
+
+ public UserDevfilePermissionImpl(UserDevfilePermission userDevfilePermission) {
+ this(
+ userDevfilePermission.getUserDevfileId(),
+ userDevfilePermission.getUserId(),
+ userDevfilePermission.getActions());
+ }
+
+ @Override
+ public String getInstanceId() {
+ return userDevfileId;
+ }
+
+ @Override
+ public String getDomainId() {
+ return UserDevfileDomain.DOMAIN_ID;
+ }
+
+ @Override
+ public List getActions() {
+ return actions;
+ }
+
+ @Override
+ public String getUserDevfileId() {
+ return userDevfileId;
+ }
+
+ @Override
+ public String toString() {
+ return "UserDevfilePermissionImpl{"
+ + "userDevfileId='"
+ + userDevfileId
+ + '\''
+ + ", userDevfile="
+ + userDevfile
+ + ", actions="
+ + actions
+ + "} "
+ + super.toString();
+ }
+}
diff --git a/multiuser/permission/che-multiuser-permission-userdevfile/src/main/java/org/eclipse/che/multiuser/permission/devfile/server/spi/UserDevfilePermissionDao.java b/multiuser/permission/che-multiuser-permission-userdevfile/src/main/java/org/eclipse/che/multiuser/permission/devfile/server/spi/UserDevfilePermissionDao.java
new file mode 100644
index 00000000000..402eae79e55
--- /dev/null
+++ b/multiuser/permission/che-multiuser-permission-userdevfile/src/main/java/org/eclipse/che/multiuser/permission/devfile/server/spi/UserDevfilePermissionDao.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2012-2018 Red Hat, Inc.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.multiuser.permission.devfile.server.spi;
+
+import java.util.List;
+import java.util.Optional;
+import org.eclipse.che.api.core.NotFoundException;
+import org.eclipse.che.api.core.Page;
+import org.eclipse.che.api.core.ServerException;
+import org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl;
+
+/** Defines data access object contract for {@link UserDevfilePermissionImpl}. */
+public interface UserDevfilePermissionDao {
+
+ /**
+ * Stores (adds or updates) UserDevfilePermissions.
+ *
+ * @param userDevfilePermissions userDevfilePermissions to store
+ * @return optional with updated userDevfilePermissions, other way empty optional must be returned
+ * @throws NullPointerException when {@code userDevfilePermissions} is null
+ * @throws ServerException when any other error occurs during userDevfilePermissions storing
+ */
+ Optional store(UserDevfilePermissionImpl userDevfilePermissions)
+ throws ServerException;
+
+ /**
+ * Gets userDevfilePermissions by user and userDevfileId
+ *
+ * @param userDevfileId user devfile identifier
+ * @param userId user identifier
+ * @return userDevfilePermissions instance, never null
+ * @throws NullPointerException when {@code workspace} or {@code user} is null
+ * @throws NotFoundException when worker with given {@code workspace} and {@code user} was not
+ * found
+ * @throws ServerException when any other error occurs during worker fetching
+ */
+ UserDevfilePermissionImpl getUserDevfilePermission(String userDevfileId, String userId)
+ throws ServerException, NotFoundException;
+
+ /**
+ * Removes userDevfilePermissions
+ *
+ * Doesn't throw an exception when userDevfilePermissions with given {@code UserDevfile} and
+ * {@code user} does not exist
+ *
+ * @param userDevfileId workspace identifier
+ * @param userId user identifier
+ * @throws NullPointerException when {@code UserDevfile} or {@code user} is null
+ * @throws ServerException when any other error occurs during userDevfilePermissions removing
+ */
+ void removeUserDevfilePermission(String userDevfileId, String userId) throws ServerException;
+
+ /**
+ * Gets userDevfilePermissions by user devfile id.
+ *
+ * @param userDevfileId user devfile identifier
+ * @param maxItems the maximum number of userDevfilePermissions to return
+ * @param skipCount the number of userDevfilePermissions to skip
+ * @return list of userDevfilePermissions instance
+ * @throws NullPointerException when {@code userDevfile} is null
+ * @throws ServerException when any other error occurs during userDevfilePermissions fetching
+ */
+ Page getUserDevfilePermission(
+ String userDevfileId, int maxItems, long skipCount) throws ServerException;
+
+ /**
+ * Gets UserDevfilePermissions by user
+ *
+ * @param userId user identifier
+ * @return list of UserDevfilePermissions instance
+ * @throws NullPointerException when {@code user} is null
+ * @throws ServerException when any other error occurs during UserDevfilePermissions fetching
+ */
+ List getUserDevfilePermissionByUser(String userId)
+ throws ServerException;
+}
diff --git a/multiuser/permission/che-multiuser-permission-userdevfile/src/main/java/org/eclipse/che/multiuser/permission/devfile/server/spi/jpa/JpaUserDevfilePermissionDao.java b/multiuser/permission/che-multiuser-permission-userdevfile/src/main/java/org/eclipse/che/multiuser/permission/devfile/server/spi/jpa/JpaUserDevfilePermissionDao.java
new file mode 100644
index 00000000000..67668c550bb
--- /dev/null
+++ b/multiuser/permission/che-multiuser-permission-userdevfile/src/main/java/org/eclipse/che/multiuser/permission/devfile/server/spi/jpa/JpaUserDevfilePermissionDao.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2012-2018 Red Hat, Inc.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.multiuser.permission.devfile.server.spi.jpa;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.lang.String.format;
+import static java.util.Objects.requireNonNull;
+import static java.util.stream.Collectors.toList;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.inject.persist.Transactional;
+import java.util.List;
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+import org.eclipse.che.api.core.NotFoundException;
+import org.eclipse.che.api.core.Page;
+import org.eclipse.che.api.core.ServerException;
+import org.eclipse.che.api.core.notification.EventService;
+import org.eclipse.che.api.devfile.server.event.BeforeDevfileRemovedEvent;
+import org.eclipse.che.commons.annotation.Nullable;
+import org.eclipse.che.core.db.cascade.CascadeEventSubscriber;
+import org.eclipse.che.multiuser.api.permission.server.AbstractPermissionsDomain;
+import org.eclipse.che.multiuser.api.permission.server.jpa.AbstractJpaPermissionsDao;
+import org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl;
+import org.eclipse.che.multiuser.permission.devfile.server.spi.UserDevfilePermissionDao;
+
+public class JpaUserDevfilePermissionDao
+ extends AbstractJpaPermissionsDao
+ implements UserDevfilePermissionDao {
+
+ @Inject
+ public JpaUserDevfilePermissionDao(
+ AbstractPermissionsDomain supportedDomain) {
+ super(supportedDomain);
+ }
+
+ @Override
+ public UserDevfilePermissionImpl get(String userId, String instanceId)
+ throws ServerException, NotFoundException {
+
+ requireNonNull(instanceId, "User devfile identifier required");
+ requireNonNull(userId, "User identifier required");
+ try {
+ return new UserDevfilePermissionImpl(getEntity(wildcardToNull(userId), instanceId));
+ } catch (RuntimeException x) {
+ throw new ServerException(x.getLocalizedMessage(), x);
+ }
+ }
+
+ @Override
+ public List getByUser(String userId) throws ServerException {
+ requireNonNull(userId, "User identifier required");
+ return doGetByUser(wildcardToNull(userId))
+ .stream()
+ .map(UserDevfilePermissionImpl::new)
+ .collect(toList());
+ }
+
+ @Override
+ @Transactional
+ public Page getByInstance(
+ String instanceId, int maxItems, long skipCount) throws ServerException {
+ requireNonNull(instanceId, "User devfile identifier required");
+ checkArgument(
+ skipCount <= Integer.MAX_VALUE,
+ "The number of items to skip can't be greater than " + Integer.MAX_VALUE);
+
+ try {
+ final EntityManager entityManager = managerProvider.get();
+ final List permissions =
+ entityManager
+ .createNamedQuery(
+ "UserDevfilePermission.getByUserDevfileId", UserDevfilePermissionImpl.class)
+ .setParameter("userDevfileId", instanceId)
+ .setMaxResults(maxItems)
+ .setFirstResult((int) skipCount)
+ .getResultList()
+ .stream()
+ .map(UserDevfilePermissionImpl::new)
+ .collect(toList());
+ final Long workersCount =
+ entityManager
+ .createNamedQuery("UserDevfilePermission.getCountByUserDevfileId", Long.class)
+ .setParameter("userDevfileId", instanceId)
+ .getSingleResult();
+ return new Page<>(permissions, skipCount, maxItems, workersCount);
+ } catch (RuntimeException e) {
+ throw new ServerException(e.getLocalizedMessage(), e);
+ }
+ }
+
+ @Override
+ protected UserDevfilePermissionImpl getEntity(String userId, String instanceId)
+ throws NotFoundException, ServerException {
+ try {
+ return doGet(userId, instanceId);
+ } catch (NoResultException e) {
+ throw new NotFoundException(
+ format(
+ "User devfile permission for devfile '%s' with id '%s' was not found.",
+ instanceId, userId));
+ } catch (RuntimeException e) {
+ throw new ServerException(e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public UserDevfilePermissionImpl getUserDevfilePermission(String userDevfileId, String userId)
+ throws ServerException, NotFoundException {
+ return new UserDevfilePermissionImpl(get(userId, userDevfileId));
+ }
+
+ @Override
+ public void removeUserDevfilePermission(String userDevfileId, String userId)
+ throws ServerException {
+ try {
+ super.remove(userId, userDevfileId);
+ } catch (NotFoundException e) {
+ throw new ServerException(e);
+ }
+ }
+
+ @Override
+ public Page getUserDevfilePermission(
+ String userDevfileId, int maxItems, long skipCount) throws ServerException {
+ return getByInstance(userDevfileId, maxItems, skipCount);
+ }
+
+ @Override
+ public List getUserDevfilePermissionByUser(String userId)
+ throws ServerException {
+ return getByUser(userId);
+ }
+
+ @Transactional
+ protected UserDevfilePermissionImpl doGet(String userId, String instanceId) {
+ return managerProvider
+ .get()
+ .createNamedQuery(
+ "UserDevfilePermission.getByUserAndUserDevfileId", UserDevfilePermissionImpl.class)
+ .setParameter("userDevfileId", instanceId)
+ .setParameter("userId", userId)
+ .getSingleResult();
+ }
+
+ @Transactional
+ protected List doGetByUser(@Nullable String userId)
+ throws ServerException {
+ try {
+ return managerProvider
+ .get()
+ .createNamedQuery("UserDevfilePermission.getByUserId", UserDevfilePermissionImpl.class)
+ .setParameter("userId", userId)
+ .getResultList();
+ } catch (RuntimeException e) {
+ throw new ServerException(e.getLocalizedMessage(), e);
+ }
+ }
+
+ @Singleton
+ public static class RemoveUserDevfilePermissionsBeforeUserDevfuleRemovedEventSubscriber
+ extends CascadeEventSubscriber {
+ private static final int PAGE_SIZE = 100;
+
+ @Inject private EventService eventService;
+ @Inject private UserDevfilePermissionDao userDevfilePermissionDao;
+
+ @PostConstruct
+ public void subscribe() {
+ eventService.subscribe(this, BeforeDevfileRemovedEvent.class);
+ }
+
+ @PreDestroy
+ public void unsubscribe() {
+ eventService.unsubscribe(this, BeforeDevfileRemovedEvent.class);
+ }
+
+ @Override
+ public void onCascadeEvent(BeforeDevfileRemovedEvent event) throws Exception {
+ removeUserDevfilePermissions(event.getUserDevfile().getId(), PAGE_SIZE);
+ }
+
+ @VisibleForTesting
+ void removeUserDevfilePermissions(String userDevfileId, int pageSize) throws ServerException {
+ Page permissionsPage;
+ do {
+ // skip count always equals to 0 because elements will be shifted after removing previous
+ // items
+ permissionsPage =
+ userDevfilePermissionDao.getUserDevfilePermission(userDevfileId, pageSize, 0);
+ for (UserDevfilePermissionImpl permission : permissionsPage.getItems()) {
+ userDevfilePermissionDao.removeUserDevfilePermission(
+ permission.getInstanceId(), permission.getUserId());
+ }
+ } while (permissionsPage.hasNextPage());
+ }
+ }
+}
diff --git a/multiuser/permission/che-multiuser-permission-userdevfile/src/main/java/org/eclipse/che/multiuser/permission/devfile/server/spi/jpa/MultiuserJpaUserDevfileDao.java b/multiuser/permission/che-multiuser-permission-userdevfile/src/main/java/org/eclipse/che/multiuser/permission/devfile/server/spi/jpa/MultiuserJpaUserDevfileDao.java
new file mode 100644
index 00000000000..7c113ee0a5c
--- /dev/null
+++ b/multiuser/permission/che-multiuser-permission-userdevfile/src/main/java/org/eclipse/che/multiuser/permission/devfile/server/spi/jpa/MultiuserJpaUserDevfileDao.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2012-2018 Red Hat, Inc.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.multiuser.permission.devfile.server.spi.jpa;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.util.List;
+import javax.inject.Singleton;
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import org.eclipse.che.api.core.Page;
+import org.eclipse.che.api.core.ServerException;
+import org.eclipse.che.api.core.model.workspace.devfile.UserDevfile;
+import org.eclipse.che.api.devfile.server.jpa.JpaUserDevfileDao;
+import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
+import org.eclipse.che.api.devfile.server.spi.UserDevfileDao;
+import org.eclipse.che.commons.env.EnvironmentContext;
+import org.eclipse.che.commons.lang.Pair;
+import org.eclipse.che.commons.subject.Subject;
+
+/** JPA based implementation of {@link UserDevfileDao}. */
+@Singleton
+public class MultiuserJpaUserDevfileDao extends JpaUserDevfileDao {
+
+ @Override
+ public Page getDevfiles(
+ int maxItems,
+ int skipCount,
+ List> filter,
+ List> order)
+ throws ServerException {
+ checkArgument(maxItems > 0, "The number of items has to be positive.");
+ checkArgument(
+ skipCount >= 0,
+ "The number of items to skip can't be negative or greater than " + Integer.MAX_VALUE);
+
+ final Subject subject = EnvironmentContext.getCurrent().getSubject();
+ if (subject.isAnonymous()) {
+ throw new ServerException("Unexpected state. Current user is not set.");
+ }
+
+ return doGetDevfiles(
+ maxItems,
+ skipCount,
+ filter,
+ order,
+ () ->
+ MultiuserUserDevfileSearchQueryBuilder.newBuilder(managerProvider.get())
+ .withUserId(subject.getUserId()));
+ }
+
+ public static class MultiuserUserDevfileSearchQueryBuilder
+ extends JpaUserDevfileDao.UserDevfileSearchQueryBuilder {
+
+ MultiuserUserDevfileSearchQueryBuilder(EntityManager entityManager) {
+ super(entityManager);
+ }
+
+ public MultiuserUserDevfileSearchQueryBuilder withUserId(String userId) {
+ params.put("userId", userId);
+ return this;
+ }
+
+ public static MultiuserUserDevfileSearchQueryBuilder newBuilder(EntityManager entityManager) {
+ return new MultiuserUserDevfileSearchQueryBuilder(entityManager);
+ }
+
+ @Override
+ public JpaUserDevfileDao.UserDevfileSearchQueryBuilder withFilter(
+ List> filter) {
+ super.withFilter(filter);
+ if (this.filter.isEmpty()) {
+ this.filter = "WHERE permission.userId = :userId AND 'read' MEMBER OF permission.actions";
+ } else {
+ this.filter += " AND permission.userId = :userId AND 'read' MEMBER OF permission.actions";
+ }
+ return this;
+ }
+
+ @Override
+ public TypedQuery buildCountQuery() {
+ StringBuilder query =
+ new StringBuilder()
+ .append("SELECT ")
+ .append(" COUNT(userdevfile) ")
+ .append("FROM UserDevfilePermission permission ")
+ .append("LEFT JOIN permission.userDevfile userdevfile ")
+ .append(filter);
+ TypedQuery typedQuery = entityManager.createQuery(query.toString(), Long.class);
+ params.forEach((k, v) -> typedQuery.setParameter(k, v));
+ return typedQuery;
+ }
+
+ @Override
+ public TypedQuery buildSelectItemsQuery() {
+ StringBuilder query =
+ new StringBuilder()
+ .append("SELECT ")
+ .append(" userdevfile ")
+ .append("FROM UserDevfilePermission permission ")
+ .append("LEFT JOIN permission.userDevfile userdevfile ")
+ .append(filter)
+ .append(order);
+ TypedQuery typedQuery =
+ entityManager
+ .createQuery(query.toString(), UserDevfileImpl.class)
+ .setFirstResult(skipCount)
+ .setMaxResults(maxItems);
+ params.forEach((k, v) -> typedQuery.setParameter(k, v));
+ return typedQuery;
+ }
+ }
+}
diff --git a/multiuser/permission/che-multiuser-permission-userdevfile/src/test/java/org/eclipse/che/multiuser/permission/devfile/server/TestObjectGenerator.java b/multiuser/permission/che-multiuser-permission-userdevfile/src/test/java/org/eclipse/che/multiuser/permission/devfile/server/TestObjectGenerator.java
new file mode 100644
index 00000000000..4b21dd84913
--- /dev/null
+++ b/multiuser/permission/che-multiuser-permission-userdevfile/src/test/java/org/eclipse/che/multiuser/permission/devfile/server/TestObjectGenerator.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2012-2018 Red Hat, Inc.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.multiuser.permission.devfile.server;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+import static java.util.Collections.singletonMap;
+
+import com.google.common.collect.ImmutableMap;
+import org.eclipse.che.api.devfile.server.DtoConverter;
+import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
+import org.eclipse.che.api.devfile.shared.dto.UserDevfileDto;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.ActionImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.CommandImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.ComponentImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.DevfileImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.EndpointImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.EntrypointImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.EnvImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.MetadataImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.ProjectImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.SourceImpl;
+import org.eclipse.che.commons.lang.NameGenerator;
+
+public class TestObjectGenerator {
+
+ public static UserDevfileDto createUserDevfileDto() {
+ return DtoConverter.asDto(createUserDevfile(NameGenerator.generate("name", 6)));
+ }
+
+ public static UserDevfileImpl createUserDevfile() {
+ return createUserDevfile(NameGenerator.generate("name", 6));
+ }
+
+ public static UserDevfileImpl createUserDevfile(String name) {
+ return createUserDevfile(NameGenerator.generate("id", 6), name);
+ }
+
+ public static UserDevfileImpl createUserDevfile(String id, String name) {
+ return new UserDevfileImpl(id, createDevfile(name));
+ }
+
+ public static DevfileImpl createDevfile(String name) {
+
+ SourceImpl source1 =
+ new SourceImpl(
+ "type1",
+ "http://location",
+ "branch1",
+ "point1",
+ "tag1",
+ "commit1",
+ "sparseCheckoutDir1");
+ ProjectImpl project1 = new ProjectImpl("project1", source1, "path1");
+
+ SourceImpl source2 =
+ new SourceImpl(
+ "type2",
+ "http://location",
+ "branch2",
+ "point2",
+ "tag2",
+ "commit2",
+ "sparseCheckoutDir2");
+ ProjectImpl project2 = new ProjectImpl("project2", source2, "path2");
+
+ ActionImpl action1 =
+ new ActionImpl("exec1", "component1", "run.sh", "/home/user/1", null, null);
+ ActionImpl action2 =
+ new ActionImpl("exec2", "component2", "run.sh", "/home/user/2", null, null);
+
+ CommandImpl command1 =
+ new CommandImpl(name + "-1", singletonList(action1), singletonMap("attr1", "value1"), null);
+ CommandImpl command2 =
+ new CommandImpl(name + "-2", singletonList(action2), singletonMap("attr2", "value2"), null);
+
+ EntrypointImpl entrypoint1 =
+ new EntrypointImpl(
+ "parentName1",
+ singletonMap("parent1", "selector1"),
+ "containerName1",
+ asList("command1", "command2"),
+ asList("arg1", "arg2"));
+
+ EntrypointImpl entrypoint2 =
+ new EntrypointImpl(
+ "parentName2",
+ singletonMap("parent2", "selector2"),
+ "containerName2",
+ asList("command3", "command4"),
+ asList("arg3", "arg4"));
+
+ org.eclipse.che.api.workspace.server.model.impl.devfile.VolumeImpl volume1 =
+ new org.eclipse.che.api.workspace.server.model.impl.devfile.VolumeImpl("name1", "path1");
+
+ org.eclipse.che.api.workspace.server.model.impl.devfile.VolumeImpl volume2 =
+ new org.eclipse.che.api.workspace.server.model.impl.devfile.VolumeImpl("name2", "path2");
+
+ EnvImpl env1 = new EnvImpl("name1", "value1");
+ EnvImpl env2 = new EnvImpl("name2", "value2");
+
+ EndpointImpl endpoint1 = new EndpointImpl("name1", 1111, singletonMap("key1", "value1"));
+ EndpointImpl endpoint2 = new EndpointImpl("name2", 2222, singletonMap("key2", "value2"));
+
+ ComponentImpl component1 =
+ new ComponentImpl(
+ "kubernetes",
+ "component1",
+ "eclipse/che-theia/0.0.1",
+ ImmutableMap.of("java.home", "/home/user/jdk11"),
+ "https://mysite.com/registry/somepath1",
+ "/dev.yaml",
+ "refcontent1",
+ ImmutableMap.of("app.kubernetes.io/component", "db"),
+ asList(entrypoint1, entrypoint2),
+ "image",
+ "256G",
+ "128M",
+ "2",
+ "130m",
+ false,
+ false,
+ singletonList("command"),
+ singletonList("arg"),
+ asList(volume1, volume2),
+ asList(env1, env2),
+ asList(endpoint1, endpoint2));
+ component1.setSelector(singletonMap("key1", "value1"));
+
+ ComponentImpl component2 =
+ new ComponentImpl(
+ "kubernetes",
+ "component2",
+ "eclipse/che-theia/0.0.1",
+ ImmutableMap.of(
+ "java.home",
+ "/home/user/jdk11aertwertert",
+ "java.boolean",
+ true,
+ "java.long",
+ 123444L),
+ "https://mysite.com/registry/somepath2",
+ "/dev.yaml",
+ "refcontent2",
+ ImmutableMap.of("app.kubernetes.io/component", "webapp"),
+ asList(entrypoint1, entrypoint2),
+ "image",
+ "256G",
+ "256M",
+ "3",
+ "180m",
+ false,
+ false,
+ singletonList("command"),
+ singletonList("arg"),
+ asList(volume1, volume2),
+ asList(env1, env2),
+ asList(endpoint1, endpoint2));
+ component2.setSelector(singletonMap("key2", "value2"));
+
+ DevfileImpl devfile =
+ new DevfileImpl(
+ "0.0.1",
+ asList(project1, project2),
+ asList(component1, component2),
+ asList(command1, command2),
+ singletonMap("attribute1", "value1"),
+ new MetadataImpl(name));
+
+ return devfile;
+ }
+}
diff --git a/multiuser/permission/che-multiuser-permission-userdevfile/src/test/java/org/eclipse/che/multiuser/permission/devfile/server/filters/UserDevfilePermissionsFilterTest.java b/multiuser/permission/che-multiuser-permission-userdevfile/src/test/java/org/eclipse/che/multiuser/permission/devfile/server/filters/UserDevfilePermissionsFilterTest.java
new file mode 100644
index 00000000000..bb6a0b2b636
--- /dev/null
+++ b/multiuser/permission/che-multiuser-permission-userdevfile/src/test/java/org/eclipse/che/multiuser/permission/devfile/server/filters/UserDevfilePermissionsFilterTest.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 2012-2018 Red Hat, Inc.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.multiuser.permission.devfile.server.filters;
+
+import static com.jayway.restassured.RestAssured.given;
+import static org.everrest.assured.JettyHttpServer.ADMIN_USER_NAME;
+import static org.everrest.assured.JettyHttpServer.ADMIN_USER_PASSWORD;
+import static org.everrest.assured.JettyHttpServer.SECURE_PATH;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.testng.Assert.assertEquals;
+
+import com.jayway.restassured.response.Response;
+import java.util.Collections;
+import java.util.HashSet;
+import org.eclipse.che.api.core.BadRequestException;
+import org.eclipse.che.api.core.ForbiddenException;
+import org.eclipse.che.api.core.NotFoundException;
+import org.eclipse.che.api.core.ServerException;
+import org.eclipse.che.api.core.rest.ApiExceptionMapper;
+import org.eclipse.che.api.core.rest.CheJsonProvider;
+import org.eclipse.che.api.devfile.server.DevfileService;
+import org.eclipse.che.api.devfile.server.UserDevfileManager;
+import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
+import org.eclipse.che.api.devfile.shared.dto.UserDevfileDto;
+import org.eclipse.che.api.workspace.server.devfile.DevfileEntityProvider;
+import org.eclipse.che.api.workspace.server.devfile.DevfileParser;
+import org.eclipse.che.api.workspace.server.devfile.schema.DevfileSchemaProvider;
+import org.eclipse.che.api.workspace.server.devfile.validator.DevfileIntegrityValidator;
+import org.eclipse.che.api.workspace.server.devfile.validator.DevfileSchemaValidator;
+import org.eclipse.che.commons.env.EnvironmentContext;
+import org.eclipse.che.commons.subject.Subject;
+import org.eclipse.che.multiuser.permission.devfile.server.TestObjectGenerator;
+import org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain;
+import org.everrest.assured.EverrestJetty;
+import org.everrest.core.Filter;
+import org.everrest.core.GenericContainerRequest;
+import org.everrest.core.RequestFilter;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.testng.MockitoTestNGListener;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Listeners;
+import org.testng.annotations.Test;
+
+/** Tests for {@link UserDevfilePermissionsFilter}. */
+@Listeners(value = {EverrestJetty.class, MockitoTestNGListener.class})
+public class UserDevfilePermissionsFilterTest {
+ private static final String USERNAME = "userok";
+
+ ApiExceptionMapper mapper;
+
+ CheJsonProvider jsonProvider = new CheJsonProvider(new HashSet<>());
+ private DevfileEntityProvider devfileEntityProvider =
+ new DevfileEntityProvider(
+ new DevfileParser(
+ new DevfileSchemaValidator(new DevfileSchemaProvider()),
+ new DevfileIntegrityValidator(Collections.emptyMap())));
+
+ @SuppressWarnings("unused")
+ private static final EnvironmentFilter FILTER = new EnvironmentFilter();
+
+ @Mock private static Subject subject;
+
+ @Mock private UserDevfileManager userDevfileManager;
+
+ private UserDevfilePermissionsFilter permissionsFilter;
+
+ @Mock private DevfileService devfileService;
+ private UserDevfileDto userDevfileDto = TestObjectGenerator.createUserDevfileDto();
+ private UserDevfileImpl userDevfile = new UserDevfileImpl(userDevfileDto);
+ //
+ @BeforeMethod
+ public void setUp() throws Exception {
+ lenient().when(subject.getUserName()).thenReturn(USERNAME);
+ lenient().when(userDevfileManager.getById(any())).thenReturn(userDevfile);
+
+ permissionsFilter = spy(new UserDevfilePermissionsFilter(userDevfileManager));
+
+ lenient()
+ .doThrow(new ForbiddenException(""))
+ .when(subject)
+ .checkPermission(anyString(), anyString(), anyString());
+ }
+
+ @Test
+ public void shouldNotCheckAnyPermissionOnDevfileCreate() {
+ // given
+ // when
+ final Response response =
+ given()
+ .auth()
+ .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
+ .contentType("application/json")
+ .body(userDevfileDto)
+ .when()
+ .post(SECURE_PATH + "/devfile/");
+ // then
+ assertEquals(response.getStatusCode(), 204);
+ verifyZeroInteractions(subject);
+ }
+
+ @Test
+ public void shouldNotCheckAnyPermissionOnDevfileSearch()
+ throws BadRequestException, ForbiddenException, NotFoundException, ServerException {
+ // given
+ Mockito.when(devfileService.getUserDevfiles(any(), any(), any()))
+ .thenReturn(javax.ws.rs.core.Response.ok().build());
+ // when
+ final Response response =
+ given()
+ .auth()
+ .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
+ .when()
+ .get(SECURE_PATH + "/devfile/list");
+ // then
+ assertEquals(response.getStatusCode(), 200);
+ verifyZeroInteractions(subject);
+ }
+
+ @Test
+ public void shouldNotCheckAnyPermissionOnDevfileSchema()
+ throws BadRequestException, ForbiddenException, NotFoundException, ServerException {
+ // given
+ Mockito.when(devfileService.getSchema()).thenReturn(javax.ws.rs.core.Response.ok().build());
+ // when
+ final Response response =
+ given()
+ .auth()
+ .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
+ .when()
+ .get(SECURE_PATH + "/devfile");
+ // then
+ assertEquals(response.getStatusCode(), 200);
+ verifyZeroInteractions(subject);
+ }
+
+ @Test
+ public void shouldCheckReadPermissionsOnFetchingUserDevfileById() throws Exception {
+ // given
+ Mockito.when(devfileService.getById(eq(userDevfileDto.getId()))).thenReturn(userDevfileDto);
+ doNothing()
+ .when(subject)
+ .checkPermission(
+ eq(UserDevfileDomain.DOMAIN_ID),
+ eq(userDevfileDto.getId()),
+ eq(UserDevfileDomain.READ));
+
+ // when
+ final Response response =
+ given()
+ .auth()
+ .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
+ .contentType("application/json")
+ .when()
+ .get(SECURE_PATH + "/devfile/" + userDevfileDto.getId());
+ // then
+ assertEquals(response.getStatusCode(), 200);
+ verify(devfileService).getById(eq(userDevfileDto.getId()));
+ verify(permissionsFilter)
+ .doCheckPermission(
+ eq(UserDevfileDomain.DOMAIN_ID),
+ eq(userDevfileDto.getId()),
+ eq(UserDevfileDomain.READ));
+ }
+
+ @Test
+ public void shouldBeAbleToFailOnCheckPermissionDevfileReadByID() throws ForbiddenException {
+ // given
+ doThrow(new ForbiddenException("forbidden"))
+ .when(subject)
+ .checkPermission(
+ eq(UserDevfileDomain.DOMAIN_ID),
+ eq(userDevfileDto.getId()),
+ eq(UserDevfileDomain.READ));
+ // when
+ final Response response =
+ given()
+ .auth()
+ .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
+ .when()
+ .get(SECURE_PATH + "/devfile/" + userDevfileDto.getId());
+ // then
+ assertEquals(response.getStatusCode(), 403);
+ verify(permissionsFilter)
+ .doCheckPermission(
+ eq(UserDevfileDomain.DOMAIN_ID),
+ eq(userDevfileDto.getId()),
+ eq(UserDevfileDomain.READ));
+ }
+
+ @Test
+ public void shouldChecksPermissionDevfileUpdate() throws ForbiddenException {
+ // given
+ doNothing()
+ .when(subject)
+ .checkPermission(
+ eq(UserDevfileDomain.DOMAIN_ID),
+ eq(userDevfileDto.getId()),
+ eq(UserDevfileDomain.UPDATE));
+ // when
+ final Response response =
+ given()
+ .auth()
+ .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
+ .contentType("application/json")
+ .body(userDevfileDto)
+ .when()
+ .put(SECURE_PATH + "/devfile/" + userDevfileDto.getId());
+ // then
+ assertEquals(response.getStatusCode(), 204);
+ verify(permissionsFilter)
+ .doCheckPermission(
+ eq(UserDevfileDomain.DOMAIN_ID),
+ eq(userDevfileDto.getId()),
+ eq(UserDevfileDomain.UPDATE));
+ }
+
+ @Test
+ public void shouldBeAbleToFailOnCheckPermissionDevfileUpdate() throws ForbiddenException {
+ // given
+ doThrow(new ForbiddenException("forbidden"))
+ .when(subject)
+ .checkPermission(
+ eq(UserDevfileDomain.DOMAIN_ID),
+ eq(userDevfileDto.getId()),
+ eq(UserDevfileDomain.UPDATE));
+ // when
+ final Response response =
+ given()
+ .auth()
+ .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
+ .contentType("application/json")
+ .body(userDevfileDto)
+ .when()
+ .put(SECURE_PATH + "/devfile/" + userDevfileDto.getId());
+ // then
+ assertEquals(response.getStatusCode(), 403);
+ verify(permissionsFilter)
+ .doCheckPermission(
+ eq(UserDevfileDomain.DOMAIN_ID),
+ eq(userDevfileDto.getId()),
+ eq(UserDevfileDomain.UPDATE));
+ }
+
+ @Test
+ public void shouldChecksPermissionDevfileDelete() throws ForbiddenException {
+ // given
+ doNothing()
+ .when(subject)
+ .checkPermission(
+ eq(UserDevfileDomain.DOMAIN_ID),
+ eq(userDevfileDto.getId()),
+ eq(UserDevfileDomain.DELETE));
+ // when
+ final Response response =
+ given()
+ .auth()
+ .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
+ .when()
+ .delete(SECURE_PATH + "/devfile/" + userDevfileDto.getId());
+ // then
+ assertEquals(response.getStatusCode(), 204);
+ verify(permissionsFilter)
+ .doCheckPermission(
+ eq(UserDevfileDomain.DOMAIN_ID),
+ eq(userDevfileDto.getId()),
+ eq(UserDevfileDomain.DELETE));
+ }
+
+ @Test
+ public void shouldBeAbleToFailOnCheckPermissionDevfileDelete() throws ForbiddenException {
+ // given
+ doThrow(new ForbiddenException("forbidden"))
+ .when(subject)
+ .checkPermission(
+ eq(UserDevfileDomain.DOMAIN_ID),
+ eq(userDevfileDto.getId()),
+ eq(UserDevfileDomain.DELETE));
+ // when
+ final Response response =
+ given()
+ .auth()
+ .basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
+ .when()
+ .delete(SECURE_PATH + "/devfile/" + userDevfileDto.getId());
+
+ // then
+ assertEquals(response.getStatusCode(), 403);
+ verify(permissionsFilter)
+ .doCheckPermission(
+ eq(UserDevfileDomain.DOMAIN_ID),
+ eq(userDevfileDto.getId()),
+ eq(UserDevfileDomain.DELETE));
+ }
+
+ @Filter
+ public static class EnvironmentFilter implements RequestFilter {
+ public void doFilter(GenericContainerRequest request) {
+ EnvironmentContext.getCurrent().setSubject(subject);
+ }
+ }
+}
diff --git a/multiuser/permission/che-multiuser-permission-userdevfile/src/test/java/org/eclipse/che/multiuser/permission/devfile/server/jpa/MultiuserJpaUserDevfileDaoTest.java b/multiuser/permission/che-multiuser-permission-userdevfile/src/test/java/org/eclipse/che/multiuser/permission/devfile/server/jpa/MultiuserJpaUserDevfileDaoTest.java
new file mode 100644
index 00000000000..79c32d646e0
--- /dev/null
+++ b/multiuser/permission/che-multiuser-permission-userdevfile/src/test/java/org/eclipse/che/multiuser/permission/devfile/server/jpa/MultiuserJpaUserDevfileDaoTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2012-2018 Red Hat, Inc.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.multiuser.permission.devfile.server.jpa;
+
+import static org.eclipse.che.commons.lang.NameGenerator.generate;
+import static org.eclipse.che.multiuser.permission.devfile.server.TestObjectGenerator.createDevfile;
+import static org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain.DELETE;
+import static org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain.READ;
+import static org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain.UPDATE;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableList;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import javax.persistence.EntityManager;
+import org.eclipse.che.api.core.ServerException;
+import org.eclipse.che.api.core.model.workspace.devfile.UserDevfile;
+import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
+import org.eclipse.che.api.user.server.model.impl.UserImpl;
+import org.eclipse.che.commons.env.EnvironmentContext;
+import org.eclipse.che.commons.subject.SubjectImpl;
+import org.eclipse.che.commons.test.tck.TckResourcesCleaner;
+import org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl;
+import org.eclipse.che.multiuser.permission.devfile.server.spi.jpa.MultiuserJpaUserDevfileDao;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class MultiuserJpaUserDevfileDaoTest {
+ private TckResourcesCleaner tckResourcesCleaner;
+ private EntityManager manager;
+ private MultiuserJpaUserDevfileDao dao;
+
+ private List permissions;
+ private List users;
+ private List userDevfiles;
+
+ @BeforeClass
+ public void setupEntities() throws Exception {
+ permissions =
+ ImmutableList.of(
+ new UserDevfilePermissionImpl(
+ "devfile_id1", "user1", Arrays.asList(READ, DELETE, UPDATE)),
+ new UserDevfilePermissionImpl("devfile_id2", "user1", Arrays.asList(READ, UPDATE)),
+ new UserDevfilePermissionImpl("devfile_id3", "user1", Arrays.asList(DELETE, UPDATE)),
+ new UserDevfilePermissionImpl(
+ "devfile_id1", "user2", Arrays.asList(READ, DELETE, UPDATE)));
+
+ users =
+ ImmutableList.of(
+ new UserImpl("user1", "user1@com.com", "usr1"),
+ new UserImpl("user2", "user2@com.com", "usr2"));
+
+ userDevfiles =
+ ImmutableList.of(
+ new UserDevfileImpl("devfile_id1", createDevfile(generate("name", 6))),
+ new UserDevfileImpl("devfile_id2", createDevfile(generate("name", 6))),
+ new UserDevfileImpl("devfile_id3", createDevfile(generate("name", 6))));
+ Injector injector = Guice.createInjector(new UserDevfileTckModule());
+ manager = injector.getInstance(EntityManager.class);
+ dao = injector.getInstance(MultiuserJpaUserDevfileDao.class);
+ tckResourcesCleaner = injector.getInstance(TckResourcesCleaner.class);
+ }
+
+ @BeforeMethod
+ public void setUp() throws Exception {
+ manager.getTransaction().begin();
+
+ users.stream().map(UserImpl::new).forEach(manager::persist);
+ userDevfiles.stream().map(UserDevfileImpl::new).forEach(manager::persist);
+ permissions.stream().map(UserDevfilePermissionImpl::new).forEach(manager::persist);
+
+ manager.getTransaction().commit();
+ manager.clear();
+ }
+
+ @AfterMethod
+ public void cleanup() {
+ manager.getTransaction().begin();
+
+ manager
+ .createQuery("SELECT e FROM UserDevfilePermission e", UserDevfilePermissionImpl.class)
+ .getResultList()
+ .forEach(manager::remove);
+
+ manager
+ .createQuery("SELECT w FROM UserDevfile w", UserDevfileImpl.class)
+ .getResultList()
+ .forEach(manager::remove);
+
+ manager
+ .createQuery("SELECT u FROM Usr u", UserImpl.class)
+ .getResultList()
+ .forEach(manager::remove);
+
+ manager.getTransaction().commit();
+ }
+
+ @Test
+ public void shouldGetTotalWorkspaceCount() throws ServerException {
+ assertEquals(dao.getTotalCount(), 3);
+ }
+
+ @AfterClass
+ public void shutdown() throws Exception {
+ tckResourcesCleaner.clean();
+ EnvironmentContext.reset();
+ }
+
+ @Test
+ public void shouldFindDevfilesByByPermissions() throws Exception {
+ EnvironmentContext expected = EnvironmentContext.getCurrent();
+ expected.setSubject(new SubjectImpl("user", users.get(0).getId(), "token", false));
+ List results =
+ dao.getDevfiles(30, 0, Collections.emptyList(), Collections.emptyList()).getItems();
+ assertEquals(results.size(), 2);
+ assertTrue(results.contains(userDevfiles.get(0)));
+ assertTrue(results.contains(userDevfiles.get(1)));
+ }
+}
diff --git a/multiuser/permission/che-multiuser-permission-userdevfile/src/test/java/org/eclipse/che/multiuser/permission/devfile/server/jpa/UserDevfileTckModule.java b/multiuser/permission/che-multiuser-permission-userdevfile/src/test/java/org/eclipse/che/multiuser/permission/devfile/server/jpa/UserDevfileTckModule.java
new file mode 100644
index 00000000000..3690d730c38
--- /dev/null
+++ b/multiuser/permission/che-multiuser-permission-userdevfile/src/test/java/org/eclipse/che/multiuser/permission/devfile/server/jpa/UserDevfileTckModule.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2012-2018 Red Hat, Inc.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.multiuser.permission.devfile.server.jpa;
+
+import com.google.inject.TypeLiteral;
+import org.eclipse.che.account.spi.AccountImpl;
+import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
+import org.eclipse.che.api.user.server.model.impl.UserImpl;
+import org.eclipse.che.api.workspace.server.devfile.SerializableConverter;
+import org.eclipse.che.api.workspace.server.jpa.JpaWorkspaceDao;
+import org.eclipse.che.api.workspace.server.model.impl.CommandImpl;
+import org.eclipse.che.api.workspace.server.model.impl.EnvironmentImpl;
+import org.eclipse.che.api.workspace.server.model.impl.MachineConfigImpl;
+import org.eclipse.che.api.workspace.server.model.impl.ProjectConfigImpl;
+import org.eclipse.che.api.workspace.server.model.impl.RecipeImpl;
+import org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl;
+import org.eclipse.che.api.workspace.server.model.impl.SourceStorageImpl;
+import org.eclipse.che.api.workspace.server.model.impl.VolumeImpl;
+import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl;
+import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.ActionImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.ComponentImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.DevfileImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.EndpointImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.EntrypointImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.EnvImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.ProjectImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.SourceImpl;
+import org.eclipse.che.api.workspace.server.spi.WorkspaceDao;
+import org.eclipse.che.commons.test.db.H2DBTestServer;
+import org.eclipse.che.commons.test.db.H2JpaCleaner;
+import org.eclipse.che.commons.test.db.PersistTestModuleBuilder;
+import org.eclipse.che.commons.test.tck.TckModule;
+import org.eclipse.che.commons.test.tck.TckResourcesCleaner;
+import org.eclipse.che.commons.test.tck.repository.JpaTckRepository;
+import org.eclipse.che.commons.test.tck.repository.TckRepository;
+import org.eclipse.che.core.db.DBInitializer;
+import org.eclipse.che.core.db.h2.jpa.eclipselink.H2ExceptionHandler;
+import org.eclipse.che.core.db.schema.SchemaInitializer;
+import org.eclipse.che.core.db.schema.impl.flyway.FlywaySchemaInitializer;
+import org.eclipse.che.multiuser.api.permission.server.AbstractPermissionsDomain;
+import org.eclipse.che.multiuser.permission.devfile.server.model.UserDevfilePermission;
+import org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl;
+import org.eclipse.che.multiuser.permission.devfile.server.spi.UserDevfilePermissionDao;
+import org.eclipse.che.multiuser.permission.devfile.server.spi.jpa.JpaUserDevfilePermissionDao;
+import org.eclipse.che.multiuser.permission.devfile.server.spi.tck.UserDevfilePermissionDaoTest;
+import org.h2.Driver;
+
+public class UserDevfileTckModule extends TckModule {
+
+ @Override
+ protected void configure() {
+ H2DBTestServer server = H2DBTestServer.startDefault();
+ install(
+ new PersistTestModuleBuilder()
+ .setDriver(Driver.class)
+ .runningOn(server)
+ .addEntityClasses(
+ AccountImpl.class,
+ UserImpl.class,
+ WorkspaceImpl.class,
+ WorkspaceConfigImpl.class,
+ ProjectConfigImpl.class,
+ EnvironmentImpl.class,
+ UserDevfilePermission.class,
+ MachineConfigImpl.class,
+ SourceStorageImpl.class,
+ ServerConfigImpl.class,
+ CommandImpl.class,
+ RecipeImpl.class,
+ VolumeImpl.class,
+ // devfile
+ UserDevfileImpl.class,
+ UserDevfilePermissionImpl.class,
+ ActionImpl.class,
+ org.eclipse.che.api.workspace.server.model.impl.devfile.CommandImpl.class,
+ ComponentImpl.class,
+ DevfileImpl.class,
+ EndpointImpl.class,
+ EntrypointImpl.class,
+ EnvImpl.class,
+ ProjectImpl.class,
+ SourceImpl.class,
+ org.eclipse.che.api.workspace.server.model.impl.devfile.VolumeImpl.class)
+ .addEntityClass(
+ "org.eclipse.che.api.workspace.server.model.impl.ProjectConfigImpl$Attribute")
+ .addClass(SerializableConverter.class)
+ .setExceptionHandler(H2ExceptionHandler.class)
+ .build());
+ bind(DBInitializer.class).asEagerSingleton();
+ bind(SchemaInitializer.class)
+ .toInstance(new FlywaySchemaInitializer(server.getDataSource(), "che-schema"));
+ bind(TckResourcesCleaner.class).toInstance(new H2JpaCleaner(server));
+
+ bind(new TypeLiteral>() {})
+ .toInstance(new JpaTckRepository<>(UserDevfileImpl.class));
+ bind(new TypeLiteral>() {})
+ .toInstance(new JpaTckRepository<>(UserImpl.class));
+ bind(new TypeLiteral>() {})
+ .toInstance(new JpaTckRepository<>(UserDevfilePermissionImpl.class));
+
+ bind(new TypeLiteral>() {})
+ .to(UserDevfilePermissionDaoTest.TestDomain.class);
+
+ bind(UserDevfilePermissionDao.class).to(JpaUserDevfilePermissionDao.class);
+ bind(WorkspaceDao.class).to(JpaWorkspaceDao.class);
+ }
+}
diff --git a/multiuser/permission/che-multiuser-permission-userdevfile/src/test/java/org/eclipse/che/multiuser/permission/devfile/server/spi/jpa/EntityManagerExceptionInterceptor.java b/multiuser/permission/che-multiuser-permission-userdevfile/src/test/java/org/eclipse/che/multiuser/permission/devfile/server/spi/jpa/EntityManagerExceptionInterceptor.java
new file mode 100644
index 00000000000..1d239b804cf
--- /dev/null
+++ b/multiuser/permission/che-multiuser-permission-userdevfile/src/test/java/org/eclipse/che/multiuser/permission/devfile/server/spi/jpa/EntityManagerExceptionInterceptor.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2012-2018 Red Hat, Inc.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.multiuser.permission.devfile.server.spi.jpa;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import javax.persistence.EntityManager;
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+
+public class EntityManagerExceptionInterceptor implements MethodInterceptor {
+ @Inject Provider emf;
+
+ @Override
+ public Object invoke(MethodInvocation methodInvocation) throws Throwable {
+ emf.get().getTransaction().setRollbackOnly();
+ throw new RuntimeException("Database exception");
+ }
+}
diff --git a/multiuser/permission/che-multiuser-permission-userdevfile/src/test/java/org/eclipse/che/multiuser/permission/devfile/server/spi/jpa/JpaTckModule.java b/multiuser/permission/che-multiuser-permission-userdevfile/src/test/java/org/eclipse/che/multiuser/permission/devfile/server/spi/jpa/JpaTckModule.java
new file mode 100644
index 00000000000..c4e620ae9c5
--- /dev/null
+++ b/multiuser/permission/che-multiuser-permission-userdevfile/src/test/java/org/eclipse/che/multiuser/permission/devfile/server/spi/jpa/JpaTckModule.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2012-2018 Red Hat, Inc.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.multiuser.permission.devfile.server.spi.jpa;
+
+import com.google.inject.TypeLiteral;
+import org.eclipse.che.account.spi.AccountImpl;
+import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
+import org.eclipse.che.api.user.server.model.impl.UserImpl;
+import org.eclipse.che.api.workspace.server.devfile.SerializableConverter;
+import org.eclipse.che.api.workspace.server.model.impl.CommandImpl;
+import org.eclipse.che.api.workspace.server.model.impl.EnvironmentImpl;
+import org.eclipse.che.api.workspace.server.model.impl.MachineConfigImpl;
+import org.eclipse.che.api.workspace.server.model.impl.ProjectConfigImpl;
+import org.eclipse.che.api.workspace.server.model.impl.RecipeImpl;
+import org.eclipse.che.api.workspace.server.model.impl.ServerConfigImpl;
+import org.eclipse.che.api.workspace.server.model.impl.SourceStorageImpl;
+import org.eclipse.che.api.workspace.server.model.impl.VolumeImpl;
+import org.eclipse.che.api.workspace.server.model.impl.WorkspaceConfigImpl;
+import org.eclipse.che.api.workspace.server.model.impl.WorkspaceImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.ActionImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.ComponentImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.DevfileImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.EndpointImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.EntrypointImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.EnvImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.ProjectImpl;
+import org.eclipse.che.api.workspace.server.model.impl.devfile.SourceImpl;
+import org.eclipse.che.commons.test.db.H2DBTestServer;
+import org.eclipse.che.commons.test.db.H2JpaCleaner;
+import org.eclipse.che.commons.test.db.PersistTestModuleBuilder;
+import org.eclipse.che.commons.test.tck.TckModule;
+import org.eclipse.che.commons.test.tck.TckResourcesCleaner;
+import org.eclipse.che.commons.test.tck.repository.JpaTckRepository;
+import org.eclipse.che.commons.test.tck.repository.TckRepository;
+import org.eclipse.che.core.db.DBInitializer;
+import org.eclipse.che.core.db.h2.jpa.eclipselink.H2ExceptionHandler;
+import org.eclipse.che.core.db.schema.SchemaInitializer;
+import org.eclipse.che.core.db.schema.impl.flyway.FlywaySchemaInitializer;
+import org.eclipse.che.multiuser.api.permission.server.AbstractPermissionsDomain;
+import org.eclipse.che.multiuser.permission.devfile.server.model.UserDevfilePermission;
+import org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl;
+import org.eclipse.che.multiuser.permission.devfile.server.spi.UserDevfilePermissionDao;
+import org.eclipse.che.multiuser.permission.devfile.server.spi.tck.UserDevfilePermissionDaoTest;
+import org.h2.Driver;
+
+public class JpaTckModule extends TckModule {
+
+ @Override
+ protected void configure() {
+ H2DBTestServer server = H2DBTestServer.startDefault();
+ install(
+ new PersistTestModuleBuilder()
+ .setDriver(Driver.class)
+ .runningOn(server)
+ .addEntityClasses(
+ AccountImpl.class,
+ UserImpl.class,
+ WorkspaceImpl.class,
+ WorkspaceConfigImpl.class,
+ ProjectConfigImpl.class,
+ EnvironmentImpl.class,
+ UserDevfilePermissionImpl.class,
+ MachineConfigImpl.class,
+ SourceStorageImpl.class,
+ ServerConfigImpl.class,
+ CommandImpl.class,
+ RecipeImpl.class,
+ VolumeImpl.class,
+ // devfile
+ ActionImpl.class,
+ org.eclipse.che.api.workspace.server.model.impl.devfile.CommandImpl.class,
+ ComponentImpl.class,
+ DevfileImpl.class,
+ EndpointImpl.class,
+ EntrypointImpl.class,
+ EnvImpl.class,
+ ProjectImpl.class,
+ SourceImpl.class,
+ UserDevfileImpl.class,
+ org.eclipse.che.api.workspace.server.model.impl.devfile.VolumeImpl.class)
+ .addEntityClass(
+ "org.eclipse.che.api.workspace.server.model.impl.ProjectConfigImpl$Attribute")
+ .addClass(SerializableConverter.class)
+ .setExceptionHandler(H2ExceptionHandler.class)
+ .build());
+
+ bind(new TypeLiteral>() {})
+ .to(UserDevfilePermissionDaoTest.TestDomain.class);
+
+ bind(UserDevfilePermissionDao.class).to(JpaUserDevfilePermissionDao.class);
+ bind(new TypeLiteral>() {})
+ .toInstance(new JpaTckRepository<>(UserDevfilePermission.class));
+ bind(new TypeLiteral>() {})
+ .toInstance(new JpaTckRepository<>(UserImpl.class));
+ bind(new TypeLiteral>() {})
+ .toInstance(new JpaTckRepository<>(UserDevfileImpl.class));
+
+ bind(SchemaInitializer.class)
+ .toInstance(new FlywaySchemaInitializer(server.getDataSource(), "che-schema"));
+ bind(DBInitializer.class).asEagerSingleton();
+ bind(TckResourcesCleaner.class).toInstance(new H2JpaCleaner(server));
+ }
+}
diff --git a/multiuser/permission/che-multiuser-permission-userdevfile/src/test/java/org/eclipse/che/multiuser/permission/devfile/server/spi/jpa/JpaUserDevfilePermissionDaoTest.java b/multiuser/permission/che-multiuser-permission-userdevfile/src/test/java/org/eclipse/che/multiuser/permission/devfile/server/spi/jpa/JpaUserDevfilePermissionDaoTest.java
new file mode 100644
index 00000000000..cef84bb3ce0
--- /dev/null
+++ b/multiuser/permission/che-multiuser-permission-userdevfile/src/test/java/org/eclipse/che/multiuser/permission/devfile/server/spi/jpa/JpaUserDevfilePermissionDaoTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2012-2018 Red Hat, Inc.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.che.multiuser.permission.devfile.server.spi.jpa;
+
+import static org.eclipse.che.commons.lang.NameGenerator.generate;
+import static org.eclipse.che.inject.Matchers.names;
+import static org.eclipse.che.multiuser.api.permission.server.AbstractPermissionsDomain.SET_PERMISSIONS;
+import static org.eclipse.che.multiuser.permission.devfile.server.TestObjectGenerator.createDevfile;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.matcher.Matchers;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.persistence.EntityManager;
+import org.aopalliance.intercept.MethodInterceptor;
+import org.eclipse.che.api.core.ServerException;
+import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
+import org.eclipse.che.api.user.server.model.impl.UserImpl;
+import org.eclipse.che.commons.test.tck.TckModule;
+import org.eclipse.che.commons.test.tck.TckResourcesCleaner;
+import org.eclipse.che.multiuser.permission.devfile.server.model.impl.UserDevfilePermissionImpl;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class JpaUserDevfilePermissionDaoTest {
+
+ private JpaUserDevfilePermissionDao userDevfilePermissionsDao;
+ private EntityManager manager;
+ private TckResourcesCleaner tckResourcesCleaner;
+
+ @BeforeMethod
+ private void setUpManager() {
+ final Injector injector =
+ Guice.createInjector(new JpaTckModule(), new ExceptionEntityManagerModule());
+ manager = injector.getInstance(EntityManager.class);
+ userDevfilePermissionsDao = injector.getInstance(JpaUserDevfilePermissionDao.class);
+ tckResourcesCleaner = injector.getInstance(TckResourcesCleaner.class);
+ }
+
+ @AfterMethod
+ private void cleanup() {
+ manager.getTransaction().begin();
+ final List