diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/java/org/wso2/carbon/device/application/mgt/api/services/ApplicationManagementAPI.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/java/org/wso2/carbon/device/application/mgt/api/services/ApplicationManagementAPI.java index 4ddbb34f849..9e5927eb1ef 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/java/org/wso2/carbon/device/application/mgt/api/services/ApplicationManagementAPI.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/java/org/wso2/carbon/device/application/mgt/api/services/ApplicationManagementAPI.java @@ -69,6 +69,13 @@ import java.util.List; key = "perm:application:create", permissions = {"/device-mgt/application/create"} ), + @Scope( + name = "Create an Application", + description = "Create an application", + key = "perm:application-mgt:login", + permissions = {"/device-mgt/application-mgt/login"} + ) + } ) @Path("/applications") @@ -132,6 +139,45 @@ public interface ApplicationManagementAPI { @QueryParam("searchQuery") String searchQuery ); + @GET + @Path("/{uuid}") + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @ApiOperation( + consumes = MediaType.APPLICATION_JSON, + produces = MediaType.APPLICATION_JSON, + httpMethod = "GET", + value = "get the application specified by the UUID", + notes = "This will get the application identified by the UUID, if exists", + tags = "Application Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = SCOPE, value = "perm:application:get") + }) + } + ) + @ApiResponses( + value = { + @ApiResponse( + code = 200, + message = "OK. \n Successfully retrieved relevant application.", + response = Application.class), + @ApiResponse( + code = 404, + message = "Application not found"), + @ApiResponse( + code = 500, + message = "Internal Server Error. \n Error occurred while getting relevant application.", + response = ErrorResponse.class) + }) + Response getApplication( + @ApiParam( + name = "uuid", + value = "UUID of the application", + required = true) + @PathParam("uuid") String uuid + ); + @POST @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @@ -157,7 +203,8 @@ public interface ApplicationManagementAPI { @ApiResponse( code = 304, message = "Not Modified. \n " + - "Empty body because the client already has the latest version of the requested resource."), + "Empty body because the client already has the latest version of the requested " + + "resource."), @ApiResponse( code = 500, message = "Internal Server Error. \n Error occurred while getting the application list.", @@ -179,7 +226,12 @@ public interface ApplicationManagementAPI { httpMethod = "PUT", value = "Change the life cycle state of the application", notes = "This will change the life-cycle state of the application", - tags = "Application Management" + tags = "Application Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = SCOPE, value = "perm:application-mgt:login") + }) + } ) @ApiResponses( value = { @@ -213,7 +265,12 @@ public interface ApplicationManagementAPI { value = "Change the life cycle state of the application", notes = "This will retrieve the next life cycle states of the application based on the user and the " + "current state", - tags = "Application Management" + tags = "Application Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = SCOPE, value = "perm:application-mgt:login") + }) + } ) @ApiResponses( value = { @@ -226,7 +283,7 @@ public interface ApplicationManagementAPI { message = "Internal Server Error. \n Error occurred while getting the life-cycle states.", response = ErrorResponse.class) }) - Response getLifeCycleStates( + Response getNextLifeCycleStates( @ApiParam( name = "UUID", value = "Unique identifier of the Application", diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/java/org/wso2/carbon/device/application/mgt/api/services/impl/ApplicationManagementAPIImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/java/org/wso2/carbon/device/application/mgt/api/services/impl/ApplicationManagementAPIImpl.java index 8158956e3c6..ef6e4209fc3 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/java/org/wso2/carbon/device/application/mgt/api/services/impl/ApplicationManagementAPIImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/java/org/wso2/carbon/device/application/mgt/api/services/impl/ApplicationManagementAPIImpl.java @@ -20,27 +20,36 @@ package org.wso2.carbon.device.application.mgt.api.services.impl; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.application.mgt.api.APIUtil; import org.wso2.carbon.device.application.mgt.api.services.ApplicationManagementAPI; -import org.wso2.carbon.device.application.mgt.common.*; +import org.wso2.carbon.device.application.mgt.common.Application; +import org.wso2.carbon.device.application.mgt.common.ApplicationList; +import org.wso2.carbon.device.application.mgt.common.Filter; +import org.wso2.carbon.device.application.mgt.common.User; import org.wso2.carbon.device.application.mgt.common.exception.ApplicationManagementException; import org.wso2.carbon.device.application.mgt.common.services.ApplicationManager; -import org.wso2.carbon.device.application.mgt.api.APIUtil; -import org.wso2.carbon.device.application.mgt.core.exception.ApplicationManagementDAOException; +import org.wso2.carbon.device.application.mgt.core.util.Constants; +import java.util.Arrays; import javax.validation.Valid; -import javax.ws.rs.*; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; -import java.util.Date; + @Produces({"application/json"}) @Consumes({"application/json"}) @Path("/applications") -public class ApplicationManagementAPIImpl implements ApplicationManagementAPI{ - - public static final int DEFAULT_LIMIT = 20; - - public static final String APPLICATION_UPLOAD_EXTENSION = "ApplicationUploadExtension"; +public class ApplicationManagementAPIImpl implements ApplicationManagementAPI { + private static final int DEFAULT_LIMIT = 20; private static Log log = LogFactory.getLog(ApplicationManagementAPIImpl.class); @GET @@ -71,20 +80,39 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI{ @Path("/{uuid}") public Response getApplication(@PathParam("uuid") String uuid) { ApplicationManager applicationManager = APIUtil.getApplicationManager(); - return null; + try { + Application application = applicationManager.getApplication(uuid); + if (application == null) { + return Response.status(Response.Status.NOT_FOUND) + .entity("Application with UUID " + uuid + " not found").build(); + } + return Response.status(Response.Status.OK).entity(application).build(); + } catch (ApplicationManagementException e) { + log.error("Error occurred while getting application with the uuid " + uuid, e); + return APIUtil.getResponse(e, Response.Status.INTERNAL_SERVER_ERROR); + } } @PUT @Consumes("application/json") @Path("/{uuid}/lifecycle") - public Response changeLifecycleState(@PathParam("uuid") String applicationUUID, @QueryParam("state") String state) { + public Response changeLifecycleState(@PathParam("uuid") String applicationUUID, + @QueryParam("state") String state) { ApplicationManager applicationManager = APIUtil.getApplicationManager(); + + if (!Arrays.asList(Constants.LIFE_CYCLES).contains(state)) { + log.warn("Provided lifecycle state " + state + " is not valid. Please select one from" + + Arrays.toString(Constants.LIFE_CYCLES)); + return Response.status(Response.Status.BAD_REQUEST) + .entity("Provided lifecycle state " + state + " is not valid. Please select one from " + + Arrays.toString(Constants.LIFE_CYCLES)).build(); + } try { applicationManager.changeLifecycle(applicationUUID, state); } catch (ApplicationManagementException e) { String msg = "Error occurred while changing the lifecycle of application: " + applicationUUID; log.error(msg, e); - return Response.status(Response.Status.BAD_REQUEST).build(); + return APIUtil.getResponse(e, Response.Status.BAD_REQUEST); } return Response.status(Response.Status.OK) .entity("Successfully changed the lifecycle state of the application: " + applicationUUID).build(); @@ -93,9 +121,21 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI{ @GET @Path("/{uuid}/lifecycle") @Override - public Response getLifeCycleStates(String applicationUUID) { + public Response getNextLifeCycleStates(@PathParam("uuid") String applicationUUID) { ApplicationManager applicationManager = APIUtil.getApplicationManager(); try { + if (applicationManager.getApplication(applicationUUID) == null) { + if (log.isDebugEnabled()) { + log.debug("Application with the UUID '" + applicationUUID + "' is not found."); + } + return Response.status(Response.Status.NOT_FOUND).entity("Application with the UUID '" + + applicationUUID + "' is not found.").build(); + } + + if (log.isDebugEnabled()) { + log.debug("Application with UUID '" + applicationUUID + "' is found. Request received for getting " + + "next life-cycle states for the particular application."); + } return Response.status(Response.Status.OK).entity(applicationManager.getLifeCycleStates(applicationUUID)) .build(); } catch (ApplicationManagementException e) { diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationManager.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationManager.java index b9be209eba1..0bbd63c1cad 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationManager.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationManager.java @@ -59,7 +59,15 @@ public interface ApplicationManager { */ public ApplicationList getApplications(Filter filter) throws ApplicationManagementException; - void changeLifecycle(String applicationUUID, String lifecycleIdentifier) throws ApplicationManagementException; + /** + * To change the lifecycle of the Application. + * + * @param applicationUUID UUID of the Application + * @param lifecycleIdentifier New life-cycle that need to be changed. + * @throws ApplicationManagementException Application Management Exception. + */ + public void changeLifecycle(String applicationUUID, String lifecycleIdentifier) throws + ApplicationManagementException; /** * To get the next possible life-cycle states for the application. @@ -70,4 +78,14 @@ public interface ApplicationManager { */ public List getLifeCycleStates(String applicationUUID) throws ApplicationManagementException; + + /** + * To get Application with the given UUID. + * + * @param uuid UUID of the Application + * @return the Application identified by the UUID + * @throws ApplicationManagementException Application Management Exception. + */ + public Application getApplication(String uuid) throws ApplicationManagementException; + } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/pom.xml b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/pom.xml index a971d147e64..495bc9c9731 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/pom.xml +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/pom.xml @@ -74,7 +74,8 @@ org.apache.axis2.*, org.wso2.carbon.user.core.*, org.wso2.carbon.user.api.*, - org.wso2.carbon.ndatasource.core + org.wso2.carbon.ndatasource.core, + org.wso2.carbon !org.wso2.carbon.device.application.mgt.core.internal.*, @@ -158,6 +159,10 @@ org.wso2.carbon org.wso2.carbon.ndatasource.core + + org.wso2.carbon + org.wso2.carbon.core + diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/ApplicationDAO.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/ApplicationDAO.java index f6bdbde5c50..450c90001f0 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/ApplicationDAO.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/ApplicationDAO.java @@ -18,12 +18,19 @@ */ package org.wso2.carbon.device.application.mgt.core.dao; -import org.wso2.carbon.device.application.mgt.common.*; +import org.wso2.carbon.device.application.mgt.common.Application; +import org.wso2.carbon.device.application.mgt.common.ApplicationList; +import org.wso2.carbon.device.application.mgt.common.ApplicationRelease; +import org.wso2.carbon.device.application.mgt.common.Filter; +import org.wso2.carbon.device.application.mgt.common.LifecycleStateTransition; import org.wso2.carbon.device.application.mgt.core.exception.ApplicationManagementDAOException; import java.util.List; import java.util.Map; +/** + * ApplicationDAO is responsible for handling all the Database related operations related with Application Management. + */ public interface ApplicationDAO { Application createApplication(Application application) throws ApplicationManagementDAOException; @@ -48,11 +55,10 @@ public interface ApplicationDAO { void deleteTags(int applicationId) throws ApplicationManagementDAOException; - void changeLifeCycle(LifecycleState lifecycleState) throws ApplicationManagementDAOException; - void addRelease(ApplicationRelease release) throws ApplicationManagementDAOException; - void changeLifecycle(String applicationUUID, String lifecycleIdentifier) throws ApplicationManagementDAOException; + void changeLifecycle(String applicationUUID, String lifecycleIdentifier, String username) throws + ApplicationManagementDAOException; List getNextLifeCycleStates(String applicationUUID, int tenantId) throws ApplicationManagementDAOException; diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/common/Util.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/common/Util.java index aebcd3e22ce..ddc60a1d491 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/common/Util.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/common/Util.java @@ -24,6 +24,7 @@ import org.json.JSONException; import org.wso2.carbon.device.application.mgt.common.Application; import org.wso2.carbon.device.application.mgt.common.Category; import org.wso2.carbon.device.application.mgt.common.Platform; +import org.wso2.carbon.device.application.mgt.common.User; import org.wso2.carbon.device.application.mgt.core.util.JSONUtil; import java.sql.PreparedStatement; @@ -54,6 +55,7 @@ public class Util { application.setScreenshots(JSONUtil.jsonArrayStringToList(rs.getString("SCREENSHOTS"))); application.setCreatedAt(rs.getDate("CREATED_AT")); application.setModifiedAt(rs.getDate("MODIFIED_AT")); + application.setUser(new User(rs.getString("CREATED_AT"), rs.getInt("TENANT_ID"))); Platform platform = new Platform(); platform.setName(rs.getString("APL_NAME")); diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/AbstractApplicationDAOImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/AbstractApplicationDAOImpl.java index 6370a2f7cb4..784220f53e3 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/AbstractApplicationDAOImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/AbstractApplicationDAOImpl.java @@ -20,8 +20,10 @@ package org.wso2.carbon.device.application.mgt.core.dao.impl.application; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.json.JSONException; import org.wso2.carbon.device.application.mgt.common.Application; import org.wso2.carbon.device.application.mgt.common.Filter; +import org.wso2.carbon.device.application.mgt.common.LifecycleStateTransition; import org.wso2.carbon.device.application.mgt.common.exception.DBConnectionException; import org.wso2.carbon.device.application.mgt.core.dao.ApplicationDAO; import org.wso2.carbon.device.application.mgt.core.dao.impl.AbstractDAOImpl; @@ -31,7 +33,9 @@ import org.wso2.carbon.device.application.mgt.core.util.ConnectionManagerUtil; import org.wso2.carbon.device.application.mgt.core.util.JSONUtil; import java.sql.*; +import java.util.ArrayList; import java.util.Iterator; +import java.util.List; import java.util.Map; public abstract class AbstractApplicationDAOImpl extends AbstractDAOImpl implements ApplicationDAO { @@ -182,4 +186,130 @@ public abstract class AbstractApplicationDAOImpl extends AbstractDAOImpl impleme } return count; } + + @Override + public Application getApplication(String uuid) throws ApplicationManagementDAOException { + if (log.isDebugEnabled()) { + log.debug("Getting application with the UUID(" + uuid + ") from the database"); + } + Connection conn = null; + PreparedStatement stmt = null; + ResultSet rs = null; + String sql = ""; + Application application = null; + try { + + conn = this.getDBConnection(); + sql += "SELECT APP.*, APL.NAME AS APL_NAME, APL.IDENTIFIER AS APL_IDENTIFIER, " + + "CAT.ID AS CAT_ID, CAT.NAME AS CAT_NAME FROM APPM_APPLICATION AS APP INNER JOIN APPM_PLATFORM AS " + + "APL ON APP.PLATFORM_ID = APL.ID INNER JOIN APPM_APPLICATION_CATEGORY AS CAT ON " + + "APP.APPLICATION_CATEGORY_ID = CAT.ID WHERE UUID = ?"; + + stmt = conn.prepareStatement(sql); + stmt.setString(1, uuid); + rs = stmt.executeQuery(); + + if (log.isDebugEnabled()) { + log.debug("Successfully retrieved basic details of the application with the UUID " + uuid); + } + + if (rs.next()) { + application = new Application(); + //Getting properties + sql = "SELECT * FROM APPM_APPLICATION_PROPERTY WHERE APPLICATION_ID=?"; + stmt = conn.prepareStatement(sql); + stmt.setInt(1, rs.getInt("ID")); + ResultSet rsProperties = stmt.executeQuery(); + + //Getting tags + sql = "SELECT * FROM APPM_APPLICATION_TAG WHERE APPLICATION_ID=?"; + stmt = conn.prepareStatement(sql); + stmt.setInt(1, rs.getInt("ID")); + ResultSet rsTags = stmt.executeQuery(); + + application = Util.loadApplication(rs, rsProperties, rsTags); + Util.cleanupResources(null, rsProperties); + Util.cleanupResources(null, rsTags); + } + } catch (SQLException e) { + throw new ApplicationManagementDAOException("Error occurred while getting application List", e); + } catch (JSONException e) { + throw new ApplicationManagementDAOException("Error occurred while parsing JSON", e); + } catch (DBConnectionException e) { + throw new ApplicationManagementDAOException("Error occurred while obtaining the DB connection.", e); + } finally { + Util.cleanupResources(stmt, rs); + } + return application; + } + + @Override + public void changeLifecycle(String applicationUUID, String lifecycleIdentifier, String userName) throws + ApplicationManagementDAOException { + if (log.isDebugEnabled()) { + log.debug("Change Life cycle status change " + lifecycleIdentifier + "request received to the DAO " + + "level for the application with " + "the UUID '" + applicationUUID + "' from the user " + + userName); + } + Connection conn; + PreparedStatement stmt = null; + try { + conn = this.getDBConnection(); + String sql = "UPDATE APPM_APPLICATION SET " + + "LIFECYCLE_STATE_ID = (SELECT ID FROM APPM_LIFECYCLE_STATE WHERE IDENTIFIER = ?), " + + "LIFECYCLE_STATE_MODIFIED_BY = ?, LIFECYCLE_STATE_MODIFIED_AT = ? WHERE UUID = ?"; + stmt = conn.prepareStatement(sql); + stmt.setString(1, lifecycleIdentifier); + stmt.setString(2, userName); + stmt.setDate(3, new Date(System.currentTimeMillis())); + stmt.setString(4, applicationUUID); + stmt.executeUpdate(); + } catch (DBConnectionException e) { + throw new ApplicationManagementDAOException("Error occurred while obtaining the DB connection.", e); + } catch (SQLException e) { + throw new ApplicationManagementDAOException( + "Error occurred while changing lifecycle of application: " + applicationUUID + " to: " + + lifecycleIdentifier + " state.", e); + } finally { + Util.cleanupResources(stmt, null); + } + } + + @Override + public List getNextLifeCycleStates(String applicationUUID, int tenantId) + throws ApplicationManagementDAOException { + Connection connection = null; + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + + String sql = "SELECT STATE.NAME, TRANSITION.DESCRIPTION, TRANSITION.PERMISSION FROM ( SELECT * FROM " + + "APPM_LIFECYCLE_STATE ) STATE RIGHT JOIN (SELECT * FROM APPM_LIFECYCLE_STATE_TRANSITION WHERE " + + "INITIAL_STATE = (SELECT LIFECYCLE_STATE_ID FROM APPM_APPLICATION WHERE UUID = ?)) " + + "TRANSITION ON TRANSITION.NEXT_STATE = STATE.ID"; + + try { + connection = this.getDBConnection(); + preparedStatement = connection.prepareStatement(sql); + preparedStatement.setString(1, applicationUUID); + resultSet = preparedStatement.executeQuery(); + + List lifecycleStateTransitions = new ArrayList<>(); + + while(resultSet.next()) { + LifecycleStateTransition lifecycleStateTransition = new LifecycleStateTransition(); + lifecycleStateTransition.setDescription(resultSet.getString(2)); + lifecycleStateTransition.setNextState(resultSet.getString(1)); + lifecycleStateTransition.setPermission(resultSet.getString(3)); + lifecycleStateTransitions.add(lifecycleStateTransition); + } + return lifecycleStateTransitions; + } catch (DBConnectionException e) { + throw new ApplicationManagementDAOException("Error while getting the DBConnection for getting the life " + + "cycle states for the application with the UUID : " + applicationUUID, e); + } catch (SQLException e) { + throw new ApplicationManagementDAOException("SQL exception while executing the query '" + sql + "'.", e); + } finally { + Util.cleanupResources(preparedStatement, resultSet); + } + } } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/H2ApplicationDAOImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/H2ApplicationDAOImpl.java index 709a0c8e65a..025d96ea2db 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/H2ApplicationDAOImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/H2ApplicationDAOImpl.java @@ -42,49 +42,6 @@ public class H2ApplicationDAOImpl extends AbstractApplicationDAOImpl { private static final Log log = LogFactory.getLog(H2ApplicationDAOImpl.class); - @Override - public void changeLifecycle(String applicationUUID, String lifecycleIdentifier) throws ApplicationManagementDAOException { - - } - - @Override - public List getNextLifeCycleStates(String applicationUUID, int tenantId) - throws ApplicationManagementDAOException { - Connection connection = null; - PreparedStatement preparedStatement = null; - ResultSet resultSet = null; - - String sql = "SELECT STATE.NAME, TRANSITION.DESCRIPTION, TRANSITION.PERMISSION FROM ( SELECT * FROM " - + "APPM_LIFECYCLE_STATE ) STATE RIGHT JOIN (SELECT * FROM APPM_LIFECYCLE_STATE_TRANSITION WHERE " - + "INITIAL_STATE = (SELECT LIFECYCLE_STATE_ID FROM APPM_APPLICATION WHERE UUID = ?)) " - + "TRANSITION ON TRANSITION.NEXT_STATE = STATE.ID"; - - try { - connection = this.getDBConnection(); - preparedStatement = connection.prepareStatement(sql); - preparedStatement.setString(1, applicationUUID); - resultSet = preparedStatement.executeQuery(); - - List lifecycleStateTransitions = new ArrayList<>(); - - while(resultSet.next()) { - LifecycleStateTransition lifecycleStateTransition = new LifecycleStateTransition(); - lifecycleStateTransition.setDescription(resultSet.getString(2)); - lifecycleStateTransition.setNextState(resultSet.getString(1)); - lifecycleStateTransition.setPermission(resultSet.getString(3)); - lifecycleStateTransitions.add(lifecycleStateTransition); - } - return lifecycleStateTransitions; - } catch (DBConnectionException e) { - throw new ApplicationManagementDAOException("Error while getting the DBConnection for getting the life " - + "cycle states for the application with the UUID : " + applicationUUID, e); - } catch (SQLException e) { - throw new ApplicationManagementDAOException("SQL exception while executing the query '" + sql + "'.", e); - } finally { - Util.cleanupResources(preparedStatement, resultSet); - } - } - @Override public ApplicationList getApplications(Filter filter) throws ApplicationManagementDAOException { @@ -173,11 +130,6 @@ public class H2ApplicationDAOImpl extends AbstractApplicationDAOImpl { return applicationList; } - @Override - public Application getApplication(String uuid) throws ApplicationManagementDAOException { - return null; - } - @Override public int getApplicationId(String uuid) throws ApplicationManagementDAOException { return 0; @@ -213,11 +165,6 @@ public class H2ApplicationDAOImpl extends AbstractApplicationDAOImpl { } - @Override - public void changeLifeCycle(LifecycleState lifecycleState) throws ApplicationManagementDAOException { - - } - @Override public void addRelease(ApplicationRelease release) throws ApplicationManagementDAOException { diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/MySQLApplicationDAOImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/MySQLApplicationDAOImpl.java index 73bdb55dd1c..bbf30ab2fb5 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/MySQLApplicationDAOImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/MySQLApplicationDAOImpl.java @@ -134,60 +134,6 @@ public class MySQLApplicationDAOImpl extends AbstractApplicationDAOImpl { return applicationList; } - @Override - public Application getApplication(String uuid) throws ApplicationManagementDAOException { - if (log.isDebugEnabled()) { - log.debug("Getting application data from the database"); - } - - Connection conn = null; - PreparedStatement stmt = null; - ResultSet rs = null; - String sql = ""; - Application application = new Application(); - try { - - conn = this.getConnection(); - - sql += "SELECT SQL_CALC_FOUND_ROWS APP.*, APL.NAME AS APL_NAME, APL.IDENTIFIER AS APL_IDENTIFIER, "; - sql += "CAT.ID AS CAT_ID, CAT.NAME AS CAT_NAME "; - sql += "FROM APPM_APPLICATION AS APP "; - sql += "INNER JOIN APPM_PLATFORM AS APL ON APP.PLATFORM_ID = APL.ID "; - sql += "INNER JOIN APPM_APPLICATION_CATEGORY AS CAT ON APP.APPLICATION_CATEGORY_ID = CAT.ID "; - sql += "WHERE UUID = ?"; - - stmt = conn.prepareStatement(sql); - stmt.setString(1, uuid); - rs = stmt.executeQuery(); - - if (rs.next()) { - //Getting properties - sql = "SELECT * FROM APPM_APPLICATION_PROPERTY WHERE APPLICATION_ID=?"; - stmt = conn.prepareStatement(sql); - stmt.setInt(1, rs.getInt("ID")); - ResultSet rsProperties = stmt.executeQuery(); - - //Getting tags - sql = "SELECT * FROM APPM_APPLICATION_TAG WHERE APPLICATION_ID=?"; - stmt = conn.prepareStatement(sql); - stmt.setInt(1, rs.getInt("ID")); - ResultSet rsTags = stmt.executeQuery(); - - application = Util.loadApplication(rs, rsProperties, rsTags); - Util.cleanupResources(null, rsProperties); - Util.cleanupResources(null, rsTags); - } - } catch (SQLException e) { - throw new ApplicationManagementDAOException("Error occurred while getting application List", e); - } catch (JSONException e) { - throw new ApplicationManagementDAOException("Error occurred while parsing JSON", e); - } catch (DBConnectionException e) { - throw new ApplicationManagementDAOException("Error occurred while obtaining the DB connection.", e); - } finally { - Util.cleanupResources(stmt, rs); - } - return application; - } @Override public void deleteApplication(String uuid) throws ApplicationManagementDAOException { @@ -373,40 +319,6 @@ public class MySQLApplicationDAOImpl extends AbstractApplicationDAOImpl { } } - @Override - public void changeLifecycle(String applicationUUID, String lifecycleIdentifier) throws ApplicationManagementDAOException { - Connection conn = null; - PreparedStatement stmt = null; - ResultSet rs = null; - try { - conn = this.getDBConnection(); - String sql = "UPDATE APPM_APPLICATION SET " + - "LIFECYCLE_STATE_ID = (SELECT ID FROM APPM_LIFECYCLE_STATE WHERE IDENTIFIER = ?), " + - "LIFECYCLE_STATE_MODIFIED_BY = ?, " + - "LIFECYCLE_STATE_MODIFIED_AT = ? " + - "WHERE UUID = ?"; - stmt = conn.prepareStatement(sql); - stmt.setString(1, lifecycleIdentifier); - stmt.setString(2, "admin"); - stmt.setDate(3, new Date(System.currentTimeMillis())); - stmt.setString(4, applicationUUID); - stmt.executeUpdate(); - - } catch (DBConnectionException e) { - throw new ApplicationManagementDAOException("Error occurred while obtaining the DB connection.", e); - } catch (SQLException e) { - throw new ApplicationManagementDAOException("Error occurred while changing lifecycle of application: " + applicationUUID + " to: " + lifecycleIdentifier + " state.", e); - } finally { - Util.cleanupResources(stmt, rs); - } - } - - @Override - public List getNextLifeCycleStates(String applicationUUID, int tenantId) - throws ApplicationManagementDAOException { - return null; - } - @Override public void deleteTags(int applicationId) throws ApplicationManagementDAOException { @@ -429,11 +341,6 @@ public class MySQLApplicationDAOImpl extends AbstractApplicationDAOImpl { } } - @Override - public void changeLifeCycle(LifecycleState lifecycleState) throws ApplicationManagementDAOException { - - } - @Override public void addRelease(ApplicationRelease release) throws ApplicationManagementDAOException { diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationManagerImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationManagerImpl.java index e4139c74a15..358770320d1 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationManagerImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationManagerImpl.java @@ -20,6 +20,7 @@ package org.wso2.carbon.device.application.mgt.core.impl; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.CarbonConstants; import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.device.application.mgt.common.Application; import org.wso2.carbon.device.application.mgt.common.ApplicationList; @@ -38,16 +39,21 @@ import org.wso2.carbon.device.application.mgt.core.dao.common.DAOFactory; import org.wso2.carbon.device.application.mgt.core.exception.ApplicationManagementDAOException; import org.wso2.carbon.device.application.mgt.core.exception.NotFoundException; import org.wso2.carbon.device.application.mgt.core.exception.ValidationException; +import org.wso2.carbon.device.application.mgt.core.internal.DataHolder; import org.wso2.carbon.device.application.mgt.core.util.ConnectionManagerUtil; import org.wso2.carbon.device.application.mgt.core.util.HelperUtil; +import org.wso2.carbon.user.api.UserRealm; +import org.wso2.carbon.user.api.UserStoreException; +import org.wso2.carbon.utils.multitenancy.MultitenantUtils; +import java.util.ArrayList; import java.util.Date; import java.util.List; public class ApplicationManagerImpl implements ApplicationManager { private static final Log log = LogFactory.getLog(ApplicationManagerImpl.class); - public static final String CREATED = "CREATED"; + private static final String CREATED = "CREATED"; @Override public Application createApplication(Application application) throws ApplicationManagementException { @@ -154,21 +160,45 @@ public class ApplicationManagerImpl implements ApplicationManager { public ApplicationList getApplications(Filter filter) throws ApplicationManagementException { try { - ConnectionManagerUtil.openConnection(); + ConnectionManagerUtil.openDBConnection(); ApplicationDAO applicationDAO = DAOFactory.getApplicationDAO(); return applicationDAO.getApplications(filter); } finally { - ConnectionManagerUtil.closeConnection(); + ConnectionManagerUtil.closeDBConnection(); } } @Override - public void changeLifecycle(String applicationUUID, String lifecycleIdentifier) throws ApplicationManagementException { + public void changeLifecycle(String applicationUUID, String lifecycleIdentifier) throws + ApplicationManagementException { + boolean isAvailableNextState = false; + String userName = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername(); + List nextLifeCycles = getLifeCycleStates(applicationUUID); + + for (LifecycleStateTransition lifecycleStateTransition : nextLifeCycles) { + if (log.isDebugEnabled()) { + log.debug("Lifecycle state of the application " + applicationUUID + " can be changed to" + + lifecycleStateTransition.getNextState()); + } + if (lifecycleStateTransition.getNextState().equalsIgnoreCase(lifecycleIdentifier)) { + isAvailableNextState = true; + break; + } + } + if (!isAvailableNextState) { + throw new ApplicationManagementException("User " + userName + " does not have the permission to change " + + "the lifecycle state of the application " + applicationUUID + " to lifecycle state " + + lifecycleIdentifier); + } try { - ConnectionManagerUtil.openDBConnection(); + ConnectionManagerUtil.beginDBTransaction(); ApplicationDAO applicationDAO = DAOFactory.getApplicationDAO(); - applicationDAO.changeLifecycle(applicationUUID, lifecycleIdentifier); + applicationDAO.changeLifecycle(applicationUUID, lifecycleIdentifier, userName); + ConnectionManagerUtil.commitDBTransaction(); + } catch (ApplicationManagementDAOException e) { + ConnectionManagerUtil.rollbackDBTransaction(); + throw e; } finally { ConnectionManagerUtil.closeDBConnection(); } @@ -177,15 +207,109 @@ public class ApplicationManagerImpl implements ApplicationManager { @Override public List getLifeCycleStates(String applicationUUID) throws ApplicationManagementException { + int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true); + String userName = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername(); + boolean isAdminOrApplicationOwner = isApplicationOwnerOrAdmin(applicationUUID, userName, tenantId); + + if (log.isDebugEnabled()) { + log.debug("User " + userName + " in tenant " + tenantId + " is an Admin or Application owner of the " + + "application " + applicationUUID); + } + try { + ConnectionManagerUtil.openDBConnection(); + List transitions = DAOFactory.getApplicationDAO() + .getNextLifeCycleStates(applicationUUID, tenantId); + List filteredTransitions = new ArrayList<>(); + + if (log.isDebugEnabled()) { + log.debug("Lifecycle of the application with UUID : " + applicationUUID + " can be changed to " + + transitions.size() + ". The number may vary according to the permission level of user : " + + userName + " of tenant " + tenantId); + } + for (LifecycleStateTransition transition : transitions) { + String permission = transition.getPermission(); + if (permission != null) { + if (log.isDebugEnabled()) { + log.debug("In order to make the state change to " + transition.getNextState() + " permission " + + permission + " is required"); + } + if (isAuthorized(userName, tenantId, permission)) { + filteredTransitions.add(transition); + } else { + if (log.isDebugEnabled()) { + log.debug("User " + userName + " does not have the permission " + permission + " to " + + "change the life-cycle state to " + transition.getNextState() + " of the " + + "application " + applicationUUID); + } + } + } else if (isAdminOrApplicationOwner) { + filteredTransitions.add(transition); + } + } + if (log.isDebugEnabled()) { + log.debug("User " + userName + " can do " + filteredTransitions.size() + " life-cyle state changes " + + "currently on application with the UUID " + applicationUUID); + } + return filteredTransitions; + } catch (UserStoreException e) { + throw new ApplicationManagementException( + "Userstore exception while checking whether user " + userName + " from tenant " + tenantId + + " is authorized to do a life-cycle status change in an application ", e); + } finally { + ConnectionManagerUtil.closeDBConnection(); + } + } + + @Override + public Application getApplication(String uuid) throws ApplicationManagementException { + try { + ConnectionManagerUtil.openDBConnection(); + return DAOFactory.getApplicationDAO().getApplication(uuid); + } finally { + ConnectionManagerUtil.closeDBConnection(); + } + } + + /** + * To check whether current user is application owner or admin. + * + * @param applicationUUID UUID of the Application. + * @return true if the current user is application owner or admin, unless false. + * @throws ApplicationManagementException Application Management Exception. + */ + private boolean isApplicationOwnerOrAdmin(String applicationUUID, String userName, int tenantId) + throws ApplicationManagementException { try { - int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true); + if (isAuthorized(userName, tenantId, CarbonConstants.UI_ADMIN_PERMISSION_COLLECTION)) { + return true; + } + } catch (UserStoreException e) { + throw new ApplicationManagementException("Userstore exception while checking whether user is an admin", e); + } try { ConnectionManagerUtil.openDBConnection(); - return DAOFactory.getApplicationDAO().getNextLifeCycleStates(applicationUUID, tenantId); + Application application = DAOFactory.getApplicationDAO().getApplication(applicationUUID); + return application.getUser().getUserName().equals(userName) + && application.getUser().getTenantId() == tenantId; } finally { ConnectionManagerUtil.closeDBConnection(); } } + /** + * To check whether current user has the permission to do some secured operation. + * + * @param username Name of the User. + * @param tenantId ID of the tenant. + * @param permission Permission that need to be checked. + * @return true if the current user has the permission, otherwise false. + * @throws UserStoreException UserStoreException + */ + private boolean isAuthorized (String username, int tenantId, String permission) throws UserStoreException { + UserRealm userRealm = DataHolder.getInstance().getRealmService().getTenantUserRealm(tenantId); + return userRealm != null && userRealm.getAuthorizationManager() != null && userRealm.getAuthorizationManager() + .isUserAuthorized(MultitenantUtils.getTenantAwareUsername(username), + permission, CarbonConstants.UI_PERMISSION_ACTION); + } /** * To validate the application * @@ -210,7 +334,5 @@ public class ApplicationManagerImpl implements ApplicationManager { if (application.getPlatform() == null || application.getPlatform().getIdentifier() == null) { throw new ValidationException("Platform identifier cannot be empty"); } - - } } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/Constants.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/Constants.java index ace86220101..192fd11324e 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/Constants.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/Constants.java @@ -31,11 +31,12 @@ public class Constants { public static final String PLATFORMS_DEPLOYMENT_DIR_NAME = "platforms"; public static final String PLATFORM_DEPLOYMENT_EXT = ".xml"; + /** + * Database types supported by Application Management. + */ public static final class DataBaseTypes { - private DataBaseTypes() { } - public static final String DB_TYPE_MYSQL = "MySQL"; public static final String DB_TYPE_ORACLE = "Oracle"; public static final String DB_TYPE_MSSQL = "Microsoft SQL Server"; @@ -43,4 +44,10 @@ public class Constants { public static final String DB_TYPE_H2 = "H2"; public static final String DB_TYPE_POSTGRESQL = "PostgreSQL"; } + + /** + * Lifecycle states of the application life-cycle. + */ + public static final String[] LIFE_CYCLES = {"CREATED", "IN REVIEW", "APPROVED", "REJECTED", "PUBLISHED", + "UNPUBLISHED", "RETIRED"}; } diff --git a/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/h2.sql b/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/h2.sql index fdfe7ef13da..c2b9a615b49 100644 --- a/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/h2.sql +++ b/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/h2.sql @@ -67,7 +67,7 @@ CREATE TABLE IF NOT EXISTS APPM_LIFECYCLE_STATE ( INSERT INTO APPM_LIFECYCLE_STATE (NAME, IDENTIFIER, DESCRIPTION) VALUES ('CREATED', 'CREATED', 'Application creation initial state'); INSERT INTO APPM_LIFECYCLE_STATE (NAME, IDENTIFIER, DESCRIPTION) -VALUES ('IN REVIEW', 'IN REFVIEW', 'Application is in in-review state'); +VALUES ('IN REVIEW', 'IN REVIEW', 'Application is in in-review state'); INSERT INTO APPM_LIFECYCLE_STATE (NAME, IDENTIFIER, DESCRIPTION) VALUES ('APPROVED', 'APPROVED', 'State in which Application is approved after reviewing.'); INSERT INTO APPM_LIFECYCLE_STATE (NAME, IDENTIFIER, DESCRIPTION) @@ -139,7 +139,7 @@ CREATE TABLE IF NOT EXISTS `APPM_APPLICATION` ( `LIFECYCLE_STATE_ID` INT NOT NULL, `LIFECYCLE_STATE_MODIFIED_BY` VARCHAR(255) NULL, `LIFECYCLE_STATE_MODIFIED_AT` DATETIME NULL, - `TENANT_ID` INT NULL, + `TENANT_ID` INT NOT NULL, `PLATFORM_ID` INT NOT NULL, PRIMARY KEY (`ID`, `APPLICATION_CATEGORY_ID`, `LIFECYCLE_STATE_ID`, `PLATFORM_ID`), UNIQUE INDEX `UUID_UNIQUE` (`UUID` ASC),