Merge pull request #814 from Nirothipan/fileTransfer

File transfer Feature
merge-requests/1/head
Charitha Goonetilleke 7 years ago committed by GitHub
commit 1ce74f47c9

@ -0,0 +1,76 @@
/*
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.mdm.services.android.bean;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.wso2.carbon.mdm.services.android.bean.AndroidOperation;
import java.io.Serializable;
/**
* This class represents the information of file transfer operation payload.
*/
@ApiModel(value = "FileTransfer",
description = "This class carries all information related to file transfer operation.")
public class FileTransfer extends AndroidOperation implements Serializable {
@ApiModelProperty(name = "fileURL", value = "File URL", required = true)
private String fileURL;
@ApiModelProperty(name = "userName", value = "User Name", required = true)
private String userName;
@ApiModelProperty(name = "ftpPassword", value = "FTP password", required = true)
private String ftpPassword;
@ApiModelProperty(name = "fileLocation", value = "fileLocation", required = true)
private String fileLocation;
public String getFileURL() {
return fileURL;
}
public void setFileURL(String fileURL) {
this.fileURL = fileURL;
}
public String getFtpPassword() {
return ftpPassword;
}
public void setFtpPassword(String ftpPassword) {
this.ftpPassword = ftpPassword;
}
public String getFileLocation() {
return fileLocation;
}
public void setFileLocation(String fileLocation) {
this.fileLocation = fileLocation;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}

@ -0,0 +1,66 @@
/*
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.mdm.services.android.bean.wrapper;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.wso2.carbon.mdm.services.android.bean.FileTransfer;
import java.util.List;
/**
* This class is used to wrap the File Transfer bean with devices.
*/
@ApiModel(value = "FileTransferBeanWrapper",
description = "FileTransfer related Information.")
public class FileTransferBeanWrapper {
@ApiModelProperty(name = "deviceIDs", value = "Device id list of the operation to be executed.", required = true)
private List<String> deviceIDs;
@ApiModelProperty(name = "upload", value = "Upload / Download w.r.t device.",
notes = "upload = true , If file is to be uploaded to the device.", required = true)
private boolean upload;
@ApiModelProperty(name = "operation", value = "Information of the File Transfer Operation.", required = true)
private FileTransfer operation;
public List<String> getDeviceIDs() {
return deviceIDs;
}
public void setDeviceIDs(List<String> deviceIDs) {
this.deviceIDs = deviceIDs;
}
public FileTransfer getOperation() {
return operation;
}
public void setOperation(FileTransfer operation) {
this.operation = operation;
}
public boolean isUpload() {
return upload;
}
public void setUpload(boolean upload) {
this.upload = upload;
}
}

@ -216,11 +216,78 @@ import java.util.List;
description = "Setting a Web Clip on Android Devices",
key = "perm:android:set-webclip",
permissions = {"/device-mgt/devices/owning-device/operations/android/webclip"}
),
@Scope(
name = "File Transfer",
description = "Transferring a file to android devices",
key = "perm:android:file-transfer",
permissions = {"/device-mgt/devices/owning-device/operations/android/file-transfer"}
)
}
)
public interface DeviceManagementAdminService {
@POST
@Path("/file-transfer")
@ApiOperation(
consumes = MediaType.APPLICATION_JSON,
httpMethod = "POST",
value = "Transferring file to the device.",
notes = "Using this API you have the option to transfer a file from SFTP/FTP server or using an " +
"HTTP link to the device or retrieve file from the device to FTP/SFTP server .",
response = Activity.class,
tags = "Android Device Management Administrative Service",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = AndroidConstants.SCOPE, value = "perm:android:file-transfer")
})
}
)
@ApiResponses(value = {
@ApiResponse(
code = 201,
message = "File transferred.",
response = Activity.class,
responseHeaders = {
@ResponseHeader(
name = "Content-Location",
description = "URL of the activity instance that refers to the scheduled operation."),
@ResponseHeader(
name = "Content-Type",
description = "Content type of the body"),
@ResponseHeader(
name = "ETag",
description = "Entity Tag of the response resource.\n" +
"Used by caches, or in conditional requests."),
@ResponseHeader(
name = "Last-Modified",
description = "Date and time the resource was last modified. \n" +
"Used by caches, or in conditional requests.")}),
@ApiResponse(
code = 303,
message = "See Other. \n The source can be retrieved from the URL specified in the location header.",
responseHeaders = {
@ResponseHeader(
name = "Content-Location",
description = "The Source URL of the document.")}),
@ApiResponse(
code = 400,
message = "Bad Request. \n Invalid request or validation error."),
@ApiResponse(
code = 415,
message = "Unsupported media type. \n The format of the requested entity was not supported.\n"),
@ApiResponse(
code = 500,
message = "Internal Server Error. \n " +
"Server error occurred while file transfer operation.")
})
Response fileTransfer(
@ApiParam(
name = "fileTransfer",
value = "Provide the ID of the Android device. Multiple device IDs can be added by using " +
"comma separated values.",
required = true) FileTransferBeanWrapper fileTransferBeanWrapper);
@POST
@Path("/lock-devices")
@ApiOperation(

@ -36,6 +36,7 @@ import org.wso2.carbon.mdm.services.android.bean.Camera;
import org.wso2.carbon.mdm.services.android.bean.DeviceEncryption;
import org.wso2.carbon.mdm.services.android.bean.DeviceLock;
import org.wso2.carbon.mdm.services.android.bean.ErrorResponse;
import org.wso2.carbon.mdm.services.android.bean.FileTransfer;
import org.wso2.carbon.mdm.services.android.bean.LockCode;
import org.wso2.carbon.mdm.services.android.bean.Notification;
import org.wso2.carbon.mdm.services.android.bean.PasscodePolicy;
@ -51,6 +52,7 @@ import org.wso2.carbon.mdm.services.android.bean.wrapper.BlacklistApplicationsBe
import org.wso2.carbon.mdm.services.android.bean.wrapper.CameraBeanWrapper;
import org.wso2.carbon.mdm.services.android.bean.wrapper.DeviceLockBeanWrapper;
import org.wso2.carbon.mdm.services.android.bean.wrapper.EncryptionBeanWrapper;
import org.wso2.carbon.mdm.services.android.bean.wrapper.FileTransferBeanWrapper;
import org.wso2.carbon.mdm.services.android.bean.wrapper.LockCodeBeanWrapper;
import org.wso2.carbon.mdm.services.android.bean.wrapper.NotificationBeanWrapper;
import org.wso2.carbon.mdm.services.android.bean.wrapper.PasswordPolicyBeanWrapper;
@ -89,6 +91,46 @@ public class DeviceManagementAdminServiceImpl implements DeviceManagementAdminSe
private static final Log log = LogFactory.getLog(DeviceManagementAdminServiceImpl.class);
private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZ";
@POST
@Path("/file-transfer")
@Override
public Response fileTransfer(FileTransferBeanWrapper fileTransferBeanWrapper) {
try {
if (fileTransferBeanWrapper == null || fileTransferBeanWrapper.getOperation() == null
|| fileTransferBeanWrapper.getDeviceIDs() == null) {
String errorMessage = "The payload of the file transfer operation is incorrect.";
log.error(errorMessage);
throw new BadRequestException(
new ErrorResponse.ErrorResponseBuilder().setCode(400l).setMessage(errorMessage).build());
}
if (log.isDebugEnabled()) {
log.debug("Invoking Android file transfer operation for " + fileTransferBeanWrapper.getDeviceIDs());
}
FileTransfer file = fileTransferBeanWrapper.getOperation();
ProfileOperation operation = new ProfileOperation();
if (fileTransferBeanWrapper.isUpload()) {
operation.setCode(AndroidConstants.OperationCodes.FILE_DOWNLOAD);
} else {
operation.setCode(AndroidConstants.OperationCodes.FILE_UPLOAD);
}
operation.setType(Operation.Type.PROFILE);
operation.setEnabled(true);
operation.setPayLoad(file.toJSON());
Activity activity = AndroidDeviceUtils.getOperationResponse(fileTransferBeanWrapper.getDeviceIDs(), operation);
return Response.status(Response.Status.CREATED).entity(activity).build();
} catch (InvalidDeviceException e) {
String errorMessage = "Invalid Device Identifiers ( " + fileTransferBeanWrapper.getDeviceIDs() + " ) found.";
log.error(errorMessage, e);
throw new BadRequestException(
new ErrorResponse.ErrorResponseBuilder().setCode(400l).setMessage(errorMessage).build());
} catch (OperationManagementException e) {
String errorMessage = "Issue in retrieving operation management service instance for file transfer operation";
log.error(errorMessage, e);
throw new UnexpectedServerErrorException(
new ErrorResponse.ErrorResponseBuilder().setCode(500l).setMessage(errorMessage).build());
}
}
@POST
@Path("/lock-devices")
@Override

@ -74,6 +74,8 @@ public final class AndroidConstants {
}
public static final String DEVICE_LOCK = "DEVICE_LOCK";
public static final String FILE_DOWNLOAD = "FILE_UPLOAD_TO_THE_DEVICE";
public static final String FILE_UPLOAD = "FILE_DOWNLOAD_FROM_THE_DEVICE";
public static final String DEVICE_UNLOCK = "DEVICE_UNLOCK";
public static final String DEVICE_LOCATION = "DEVICE_LOCATION";
public static final String WIFI = "WIFI";

@ -71,6 +71,10 @@
<i class="icon fw fw-error"></i><span></span>
</div>
<div id="operation-warn-msg" class="info alert-info hidden" role="alert">
<i class="icon fw fw-info"></i><span></span>
</div>
<div id="operation-form">
<form action="{{params.0.uri}}" method="{{params.0.method}}"
style="padding-bottom: 20px;"
@ -94,6 +98,24 @@
<br/>
{{/each}}
{{#each uiParams}}
{{#equal this.type "select"}}
<div class="form-group">
<select class="form-control" id="{{this.id}}">
<option>{{this.valueOne}}</option>
<option>{{this.valueTwo}}</option>
<option>{{this.valueThree}}</option>
</select>
</div>
{{/equal}}
{{#equal this.type "radio"}}
<input type="radio" id="{{this.id}}"
name="{{this.name}}"
value="{{this.value}}"
class="radio"
checked="checked"
data-param-type="form"/>
{{this.value}}
{{/equal}}
{{#equal this.type "checkbox"}}
<input type="{{this.type}}" id="{{this.id}}"
class="checkbox"
@ -102,12 +124,25 @@
{{this.label}}
<br/>
{{/equal}}
{{#equal this.type "password"}}
<input type="{{this.type}}" id="{{this.id}}"
placeholder="{{this.label}}" class="form-control"
data-param-type="form" value=""/>
<br/>
{{/equal}}
{{#equal this.type "text"}}
<input type="{{this.type}}" id="{{this.id}}"
placeholder="{{this.label}}" class="form-control"
data-param-type="form" value=""/>
<br/>
{{/equal}}
{{#equal this.type "info"}}
<div class="form-group" id="{{this.id}}">
<span class="help-block">
+ {{this.value}}
</span>
</div>
{{/equal}}
{{/each}}
<button id="btnSend" type="button" onclick="submitForm('form-{{operation}}')"
class="btn btn-default">Send

@ -24,6 +24,9 @@ function operationSelect(selection) {
$(modalPopupContent).addClass("operation-data");
$(modalPopupContent).html($(" .operation[data-operation-code=" + selection + "]").html());
$(modalPopupContent).data("operation-code", selection);
if (selection === "FILE_TRANSFER") {
fileTransferSelection();
}
showPopup();
}
@ -32,6 +35,81 @@ var resetLoader = function () {
$('#lbl-execution').addClass("hidden");
};
/**
* This function changes/hide/show field respective to the selection.
*/
function fileTransferSelection() {
var userName = document.getElementById('userName');
var password = document.getElementById('ftpPassword');
var infoTxt = document.getElementById('defaultFileLocation');
$(userName).hide();
$(password).hide();
$(infoTxt).hide();
fillUserName();
checkAuth();
changeLabels();
}
/**
* This changes the text box label when the operation is toggled between To device and From device
* and shows an info label for FILE UPLOAD regarding saving location.
*/
function changeLabels() {
var upload = document.getElementById('upload');
var download = document.getElementById('download');
var infoTxt = document.getElementById('defaultFileLocation');
console.log("info text " + infoTxt.value);
jQuery(upload).change(function () {
document.getElementById('fileURL').placeholder = "File URL";
document.getElementById('fileLocation').placeholder = "Location to save file in device";
$(infoTxt).show();
});
jQuery(download).change(function () {
document.getElementById('fileURL').placeholder = "URL to upload file from device";
document.getElementById('fileLocation').placeholder = "File location in the device";
$(infoTxt).hide();
});
}
/**
* This function show/hide username and password text boxes when authentication is toggled.
*/
function checkAuth() {
var auth = document.getElementById('authentication');
var userName = document.getElementById('userName');
var password = document.getElementById('ftpPassword');
jQuery(auth).click(function () {
if (this.checked) {
$(userName).show();
$(password).show();
} else {
$(userName).hide();
$(password).hide();
}
});
}
/**
* This function extracts the user name from the file url and fills it in the user name field.
*/
function fillUserName() {
var inputBox = document.getElementById('fileURL');
var regexp = ':\/\/[^\/]*@';
var pattern = new RegExp(regexp);
jQuery(inputBox).on('input', function () {
var fileUrl = inputBox.value;
var res = pattern.test(fileUrl);
if (res) {
var name = fileUrl.match(regexp).toString();
document.getElementById('userName').value = name.substring(3, name.length - 1);
} else {
document.getElementById('userName').value = "";
document.getElementById('userName').placeholder = "User Name"
}
}
);
}
function submitForm(formId) {
$("#btnSend").addClass("hidden");
$("#lbl-execution").removeClass("hidden");
@ -53,10 +131,12 @@ function submitForm(formId) {
} else if (input.data("param-type") == "form") {
var prefix = (uriencodedFormStr == "") ? "" : "&";
uriencodedFormStr += prefix + input.attr("id") + "=" + input.val();
if (input.attr("type") == "text") {
if (input.attr("type") == "text" || input.attr("type") == "password") {
payload[input.attr("id")] = input.val();
} else if (input.attr("type") == "checkbox") {
payload[input.attr("id")] = input.is(":checked");
} else if (input.attr("type") == "radio") {
payload[input.attr("id")] = input.is(":checked");
}
}
});
@ -179,6 +259,9 @@ function validatePayload(operationCode, payload) {
returnVal = "Message Body Can't be empty !";
}
break;
case "FILE_TRANSFER":
returnVal = validateFileTransferParameters(payload);
break;
default:
break;
@ -186,6 +269,33 @@ function validatePayload(operationCode, payload) {
return returnVal;
}
/**
* This function validates all the parameters that are entered related to the file transfer operation.
* @param payload
* @returns {string}
*/
function validateFileTransferParameters(payload) {
var returnVal = "OK";
var auth = document.getElementById('authentication');
var protocol = $(document.getElementById('protocol')).find("option:selected").text();
if (payload.upload && !payload.fileURL) {
returnVal = "Please enter File URL";
} else if (!payload.upload && !payload.fileURL) {
returnVal = "Please enter the URL to upload file from device";
} else if (protocol === "HTTP" && !(payload.fileURL).startsWith("http:")) {
returnVal = "Please enter HTTP URL"
} else if (protocol === "FTP" && !(payload.fileURL).startsWith("ftp:")) {
returnVal = "Please enter FTP URL"
} else if (protocol === "SFTP" && !(payload.fileURL).startsWith("sftp:")) {
returnVal = "Please enter SFTP URL"
} else if (!payload.upload && !payload.fileLocation) {
returnVal = "Please specify the file location in device";
} else if (auth.checked && !payload.userName) {
returnVal = "Please enter the user name if authentication required"
}
return returnVal;
}
var generatePayload = function (operationCode, operationData, deviceList) {
var payload;
var operationType;
@ -239,6 +349,18 @@ var generatePayload = function (operationCode, operationData, deviceList) {
}
};
break;
case androidOperationConstants["FILE_TRANSFER"]:
operationType = operationTypeConstants["PROFILE"];
payload = {
"operation": {
"fileURL": operationData["fileURL"],
"userName": operationData["userName"],
"ftpPassword": operationData["ftpPassword"],
"fileLocation": operationData["fileLocation"]
},
"upload": operationData["upload"]
};
break;
case androidOperationConstants["ENCRYPT_STORAGE_OPERATION_CODE"]:
operationType = operationTypeConstants["PROFILE"];
payload = {
@ -433,5 +555,6 @@ var androidOperationConstants = {
"SET_STATUS_BAR_DISABLED": "SET_STATUS_BAR_DISABLED",
"APPLICATION_OPERATION_CODE": "APP-RESTRICTION",
"SYSTEM_UPDATE_POLICY_CODE": "SYSTEM_UPDATE_POLICY",
"KIOSK_APPS_CODE": "KIOSK_APPS"
"KIOSK_APPS_CODE": "KIOSK_APPS",
"FILE_TRANSFER": "FILE_TRANSFER"
};

@ -6,6 +6,7 @@
"analyticsEnabled": "false",
"groupingEnabled": "false",
"scopes" : [
"perm:android:file-transfer",
"perm:android:enroll",
"perm:android:wipe",
"perm:android:ring",
@ -135,6 +136,72 @@
"icon": "fw-block",
"permission": "/device-mgt/devices/owning-device/operations/android/enterprise-wipe"
},
"FILE_TRANSFER": {
"icon": "fw-save",
"formParams": [
{
"type": "radio",
"name": "directionSelection",
"id": "upload",
"optional": false,
"value": "To device"
},
{
"type": "radio",
"name": "directionSelection",
"id": "download",
"optional": false,
"value": "From device"
},
{
"type": "select",
"name": "protocolSelection",
"id": "protocol",
"optional": false,
"valueOne": "HTTP",
"valueTwo": "FTP",
"valueThree": "SFTP",
"label": "Protocol"
},
{
"type": "text",
"id": "fileURL",
"optional": false,
"label": "URL to upload file from device"
},
{
"type": "text",
"id": "fileLocation",
"optional": false,
"label": "File location in the device"
},
{
"type": "info",
"id": "defaultFileLocation",
"optional": false,
"value": "File will be saved in Default download directory if not specified."
},
{
"type": "checkbox",
"id": "authentication",
"optional": true,
"label": "Authentication required"
},
{
"type": "text",
"id": "userName",
"optional": false,
"label": "User Name"
},
{
"type": "password",
"id": "ftpPassword",
"optional": false,
"label": "Password (Ignore if not needed)"
}
],
"permission": "/device-mgt/devices/owning-device/operations/android/file-transfer"
},
"WIPE_DATA": {
"icon": "fw-delete",
"formParams": [

@ -141,6 +141,12 @@
<Operation context="/api/device-mgt/android/v1.0/admin/devices/change-lock-code" method="POST" type="application/json">
</Operation>
</Feature>
<Feature code="FILE_TRANSFER">
<Name>File Transfer</Name>
<Description>Transfer file to the device</Description>
<Operation context="/api/device-mgt/android/v1.0/admin/devices/file-transfer" method="POST" type="application/json">
</Operation>
</Feature>
<Feature code="ENTERPRISE_WIPE">
<Name>Enterprise Wipe</Name>
<Description>Remove enterprise applications</Description>

Loading…
Cancel
Save