Merge branch 'application-mgt-new' into 'application-mgt-new'

Merge with upstream master

See merge request entgra/carbon-device-mgt-plugins!43
revert-dabc3590
Saad Sahibjan 6 years ago
commit 03cf82642a

@ -0,0 +1,54 @@
/*
* Copyright (c) 2019, Entgra (Pvt) Ltd. (http://www.entgra.io) 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 java.io.Serializable;
/**
* This class represents the information of sending application config operation.
*/
@ApiModel(value = "ApplicationRestriction",
description = "Details related to application config passed to device.")
public class ApplicationRestriction extends AndroidOperation implements Serializable {
private static final long serialVersionUID = 1995401458L;
@ApiModelProperty(name = "appIdentifier", value = "The application identifier to be sent.", required = true)
private String appIdentifier;
@ApiModelProperty(name = "restrictionPayload", value = "The restriction payload to be sent.", required = true)
private String restrictionPayload;
public String getAppIdentifier() {
return appIdentifier;
}
public void setAppIdentifier(String appIdentifier) {
this.appIdentifier = appIdentifier;
}
public String getRestrictionPayload() {
return restrictionPayload;
}
public void setRestrictionPayload(String restrictionPayload) {
this.restrictionPayload = restrictionPayload;
}
}

@ -0,0 +1,55 @@
/*
* Copyright (c) 2019, Entgra (Pvt) Ltd. (http://www.entgra.io) 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.ApplicationRestriction;
import java.util.List;
/**
* This class is used to wrap the Notification bean with devices.
*/
@ApiModel(value = "ApplicationRestrictionBeanWrapper",
description = "Mapping between application restriction operation and device list to be applied.")
public class ApplicationRestrictionBeanWrapper {
@ApiModelProperty(name = "deviceIDs", value = "List of device Ids", required = true)
private List<String> deviceIDs;
@ApiModelProperty(name = "operation", value = "The information of application restriction operation",
required = true)
private ApplicationRestriction operation;
public List<String> getDeviceIDs() {
return deviceIDs;
}
public void setDeviceIDs(List<String> deviceIDs) {
this.deviceIDs = deviceIDs;
}
public ApplicationRestriction getOperation() {
return operation;
}
public void setOperation(ApplicationRestriction operation) {
this.operation = operation;
}
}

@ -245,6 +245,12 @@ import java.util.List;
description = "Transferring a file to android devices",
key = "perm:android:file-transfer",
permissions = {"/device-mgt/devices/owning-device/operations/android/file-transfer"}
),
@Scope(
name = "Send app restrictions",
description = "Send app restrictions to an application in the device",
key = "perm:android:send-app-restrictions",
permissions = {"/device-mgt/devices/owning-device/operations/android/send-app-conf"}
)
}
)
@ -1856,6 +1862,68 @@ public interface DeviceManagementAdminService {
value = "The properties to set the web clip.",
required = true)
WebClipBeanWrapper webClipBeanWrapper);
@POST
@Path("/send-app-conf")
@ApiOperation(
consumes = MediaType.APPLICATION_JSON,
httpMethod = "POST",
value = "Sending an app restrictions to Android Devices",
notes = "Send application restrictions to Android devices.",
response = Activity.class,
tags = "Android Device Management Administrative Service",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = AndroidConstants.SCOPE,
value = "perm:android:send-app-restrictions")
})
}
)
@ApiResponses(value = {
@ApiResponse(
code = 201,
message = "Created. \n Successfully sent the application configuration.",
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."),
@ApiResponse(
code = 500,
message = "Internal Server Error. \n " +
"Server error occurred while adding a new send application config operation.")
})
Response sendApplicationConfiguration(
@ApiParam(
name = "notification",
value = "The properties required to send application restrictions. Provide the restriction you " +
"wish to send and the ID of the Android device. Multiple device IDs can be added by using" +
" comma separated values.",
required = true)
ApplicationRestrictionBeanWrapper applicationRestrictionBeanWrapper);
@POST
@Path("/configure-global-proxy")

@ -37,7 +37,7 @@ package org.wso2.carbon.mdm.services.android.services.impl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONException;
import org.wso2.carbon.device.mgt.common.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.InvalidDeviceException;
import org.wso2.carbon.device.mgt.common.operation.mgt.Activity;
import org.wso2.carbon.device.mgt.common.operation.mgt.Operation;
@ -45,6 +45,7 @@ import org.wso2.carbon.device.mgt.common.operation.mgt.OperationManagementExcept
import org.wso2.carbon.device.mgt.core.operation.mgt.CommandOperation;
import org.wso2.carbon.device.mgt.core.operation.mgt.ProfileOperation;
import org.wso2.carbon.mdm.services.android.bean.ApplicationInstallation;
import org.wso2.carbon.mdm.services.android.bean.ApplicationRestriction;
import org.wso2.carbon.mdm.services.android.bean.ApplicationUninstallation;
import org.wso2.carbon.mdm.services.android.bean.ApplicationUpdate;
import org.wso2.carbon.mdm.services.android.bean.BlacklistApplications;
@ -63,6 +64,7 @@ import org.wso2.carbon.mdm.services.android.bean.WebClip;
import org.wso2.carbon.mdm.services.android.bean.Wifi;
import org.wso2.carbon.mdm.services.android.bean.WipeData;
import org.wso2.carbon.mdm.services.android.bean.wrapper.ApplicationInstallationBeanWrapper;
import org.wso2.carbon.mdm.services.android.bean.wrapper.ApplicationRestrictionBeanWrapper;
import org.wso2.carbon.mdm.services.android.bean.wrapper.ApplicationUninstallationBeanWrapper;
import org.wso2.carbon.mdm.services.android.bean.wrapper.ApplicationUpdateBeanWrapper;
import org.wso2.carbon.mdm.services.android.bean.wrapper.BlacklistApplicationsBeanWrapper;
@ -82,6 +84,7 @@ import org.wso2.carbon.mdm.services.android.bean.wrapper.WipeDataBeanWrapper;
import org.wso2.carbon.mdm.services.android.exception.BadRequestException;
import org.wso2.carbon.mdm.services.android.exception.UnexpectedServerErrorException;
import org.wso2.carbon.mdm.services.android.services.DeviceManagementAdminService;
import org.wso2.carbon.mdm.services.android.util.AndroidAPIUtils;
import org.wso2.carbon.mdm.services.android.util.AndroidConstants;
import org.wso2.carbon.mdm.services.android.util.AndroidDeviceUtils;
@ -528,7 +531,8 @@ public class DeviceManagementAdminServiceImpl implements DeviceManagementAdminSe
@POST
@Path("/install-application")
@Override
public Response installApplication(ApplicationInstallationBeanWrapper applicationInstallationBeanWrapper) {
public Response installApplication(
ApplicationInstallationBeanWrapper applicationInstallationBeanWrapper) {
if (log.isDebugEnabled()) {
log.debug("Invoking 'InstallApplication' operation");
}
@ -554,11 +558,6 @@ public class DeviceManagementAdminServiceImpl implements DeviceManagementAdminSe
Activity activity = AndroidDeviceUtils
.getOperationResponse(applicationInstallationBeanWrapper.getDeviceIDs(), operation);
return Response.status(Response.Status.CREATED).entity(activity).build();
} catch (JSONException e) {
String errorMessage = "Invalid payload for the operation.";
log.error(errorMessage);
throw new BadRequestException(
new ErrorResponse.ErrorResponseBuilder().setCode(400l).setMessage(errorMessage).build());
} catch (InvalidDeviceException e) {
String errorMessage = "Invalid Device Identifiers found.";
log.error(errorMessage, e);
@ -616,7 +615,8 @@ public class DeviceManagementAdminServiceImpl implements DeviceManagementAdminSe
@POST
@Path("/uninstall-application")
@Override
public Response uninstallApplication(ApplicationUninstallationBeanWrapper applicationUninstallationBeanWrapper) {
public Response uninstallApplication(
ApplicationUninstallationBeanWrapper applicationUninstallationBeanWrapper) {
if (log.isDebugEnabled()) {
log.debug("Invoking 'UninstallApplication' operation");
}
@ -1022,6 +1022,46 @@ public class DeviceManagementAdminServiceImpl implements DeviceManagementAdminSe
new ErrorResponse.ErrorResponseBuilder().setCode(500L).setMessage(errorMessage).build());
}
}
@POST
@Path("/send-app-conf")
@Override
public Response sendApplicationConfiguration(
ApplicationRestrictionBeanWrapper applicationRestrictionBeanWrapper) {
if (log.isDebugEnabled()) {
log.debug("Invoking 'send application configuration' operation");
}
try {
if (applicationRestrictionBeanWrapper == null || applicationRestrictionBeanWrapper.getOperation() == null) {
String errorMessage = "The payload of the application configuration operation is incorrect";
log.error(errorMessage);
throw new BadRequestException(
new ErrorResponse.ErrorResponseBuilder().setCode(400l).setMessage(errorMessage).build());
}
ApplicationRestriction applicationRestriction = applicationRestrictionBeanWrapper.getOperation();
ProfileOperation operation = new ProfileOperation();
operation.setCode(AndroidConstants.OperationCodes.REMOTE_APP_CONFIG);
operation.setType(Operation.Type.PROFILE);
operation.setPayLoad(applicationRestriction.toJSON());
return (Response) AndroidAPIUtils.getOperationResponse(applicationRestrictionBeanWrapper.getDeviceIDs(),
operation);
} catch (InvalidDeviceException e) {
String errorMessage = "Invalid Device Identifiers 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";
log.error(errorMessage, e);
throw new UnexpectedServerErrorException(
new ErrorResponse.ErrorResponseBuilder().setCode(500l).setMessage(errorMessage).build());
} catch (DeviceManagementException e) {
String errorMessage = "Issue in retrieving device management service instance";
log.error(errorMessage, e);
throw new UnexpectedServerErrorException(
new ErrorResponse.ErrorResponseBuilder().setCode(500l).setMessage(errorMessage).build());
}
}
private static void validateApplicationUrl(String apkUrl) {
try {

@ -23,12 +23,26 @@ import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.analytics.api.AnalyticsDataAPI;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.device.mgt.analytics.data.publisher.service.EventsPublisherService;
import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.DeviceManagementConstants;
import org.wso2.carbon.device.mgt.common.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.InvalidDeviceException;
import org.wso2.carbon.device.mgt.common.notification.mgt.NotificationManagementService;
import org.wso2.carbon.device.mgt.common.operation.mgt.Activity;
import org.wso2.carbon.device.mgt.common.operation.mgt.Operation;
import org.wso2.carbon.device.mgt.common.operation.mgt.OperationManagementException;
import org.wso2.carbon.device.mgt.core.app.mgt.ApplicationManagementProviderService;
import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceInformationManager;
import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService;
import org.wso2.carbon.mdm.services.android.bean.ErrorResponse;
import org.wso2.carbon.mdm.services.android.exception.BadRequestException;
import org.wso2.carbon.policy.mgt.core.PolicyManagerService;
import javax.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.List;
/**
* AndroidAPIUtil class provides utility functions used by Android REST-API classes.
*/
@ -135,4 +149,25 @@ public class AndroidAPIUtils {
return analyticsDataAPI;
}
public static Response getOperationResponse(List<String> deviceIDs, Operation operation)
throws DeviceManagementException, OperationManagementException, InvalidDeviceException {
if (deviceIDs == null || deviceIDs.size() == 0) {
String errorMessage = "Device identifier list is empty";
log.error(errorMessage);
throw new BadRequestException(
new ErrorResponse.ErrorResponseBuilder().setCode(400l).setMessage(errorMessage).build());
}
DeviceIdentifier deviceIdentifier;
List<DeviceIdentifier> deviceIdentifiers = new ArrayList<>();
for (String deviceId : deviceIDs) {
deviceIdentifier = new DeviceIdentifier();
deviceIdentifier.setId(deviceId);
deviceIdentifier.setType(AndroidConstants.DEVICE_TYPE_ANDROID);
deviceIdentifiers.add(deviceIdentifier);
}
Activity activity = getDeviceManagementService().addOperation(
DeviceManagementConstants.MobileDeviceTypes.MOBILE_DEVICE_TYPE_ANDROID, operation, deviceIdentifiers);
return Response.status(Response.Status.CREATED).entity(activity).build();
}
}

@ -140,6 +140,7 @@ public final class AndroidConstants {
public static final String WORK_PROFILE = "WORK_PROFILE";
public static final String NOTIFIER_FREQUENCY = "NOTIFIER_FREQUENCY";
public static final String GLOBAL_PROXY = "SET_GLOBAL_PROXY";
public static final String REMOTE_APP_CONFIG = "REMOTE_APP_CONFIG";
}
public final class StatusCodes {

@ -21,6 +21,7 @@ package org.wso2.carbon.mdm.services.android.mocks;
import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.DeviceTypeNotFoundException;
import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
import org.wso2.carbon.device.mgt.common.FeatureManager;
import org.wso2.carbon.device.mgt.common.InvalidDeviceException;
@ -289,7 +290,7 @@ public class DeviceManagementProviderServiceMock implements DeviceManagementProv
}
@Override
public FeatureManager getFeatureManager(String s) throws DeviceManagementException {
public FeatureManager getFeatureManager(String s) throws DeviceTypeNotFoundException {
return null;
}

@ -418,7 +418,8 @@ var generatePayload = function (operationCode, operationData, deviceList) {
payload = {
"operation": {
"type": operationData["type"],
"openvpn_config": operationData["openvpn_config"]
"openvpn_config": operationData["openvpn_config"],
"packageName": operationData["packageName"]
}
};
break;
@ -491,6 +492,15 @@ var generatePayload = function (operationCode, operationData, deviceList) {
}
};
break;
case androidOperationConstants["APP_RESTRICTION_OPERATION_CODE"]:
operationType = operationTypeConstants["PROFILE"];
payload = {
"operation": {
"appIdentifier": operationData["app-id"],
"restrictionPayload": operationData["payload"]
}
};
break;
default:
// If the operation is neither of above, it is a command operation
operationType = operationTypeConstants["COMMAND"];
@ -554,5 +564,6 @@ var androidOperationConstants = {
"APPLICATION_OPERATION_CODE": "APP-RESTRICTION",
"SYSTEM_UPDATE_POLICY_CODE": "SYSTEM_UPDATE_POLICY",
"KIOSK_APPS_CODE": "KIOSK_APPS",
"FILE_TRANSFER": "FILE_TRANSFER"
"FILE_TRANSFER": "FILE_TRANSFER",
"APP_RESTRICTION_OPERATION_CODE": "REMOTE_APP_CONFIG"
};

@ -167,7 +167,8 @@ var androidOperationModule = function () {
case androidOperationConstants["VPN_OPERATION_CODE"]:
payload = {
"type": operationPayload["type"],
"openvpn_config": operationPayload["openvpn_config"]
"openvpn_config": operationPayload["openvpn_config"],
"packageName": operationPayload["packageName"]
};
break;
case androidOperationConstants["APPLICATION_OPERATION_CODE"]:
@ -425,7 +426,8 @@ var androidOperationModule = function () {
payload = {
"operation": {
"type": operationData["type"],
"openvpn_config": operationData["openvpn_config"]
"openvpn_config": operationData["openvpn_config"],
"packageName": operationData["packageName"]
}
};
break;

@ -17,6 +17,11 @@
}}
<!-- android -->
<div class="panel panel-default">
<div class="panel-heading" role="tab">
<h2 class="sub-title panel-title">
Platform Configuration - Windows
</h2>
</div>
<div id="android-config-body" class="panel-collapse panel-body" role="tabpanel">
<div id="android-config-error-msg" class="alert alert-danger hidden" role="alert">
<i class="icon fw fw-error"></i><span></span>

@ -92,7 +92,7 @@ var kioskConfigs = {
$(document).ready(function () {
$("#fcm-inputs").hide();
tinymce.init({
selector: "textarea",
selector: "#android-eula",
height:500,
theme: "modern",
plugins: [

@ -590,15 +590,35 @@ var validatePolicyProfile = function () {
// initializing continueToCheckNextInputs to true
continueToCheckNextInputs = true;
var openvpnConfig = $("#openvpn-config").val();
let openVpnConfigEnabled = document.getElementById('vpn-body').classList.contains('in');
if (openVpnConfigEnabled) {
var openvpnConfig = $("textarea#openvpn-config").val();
if (!openvpnConfig || openvpnConfig === '') {
validationStatus = {
"error": true,
"subErrorMsg": "ovpn config required. You cannot proceed.",
"subErrorMsg": "ovpn config file is required. You cannot proceed.",
"erroneousFeature": operation
};
continueToCheckNextInputs = false;
}
}
if (validationStatus) {
validationStatusArray.push(validationStatus);
}
let alwaysOnVpnEnabled = document.getElementById('always-on-vpn-body').classList.contains('in');
if (alwaysOnVpnEnabled) {
var alwaysOnConfig = $("input#vpn-client-app").val();
if (!alwaysOnConfig || alwaysOnConfig === '') {
validationStatus = {
"error": true,
"subErrorMsg": "Add a valid package name for the VPN client",
"erroneousFeature": "always-on-vpn"
};
continueToCheckNextInputs = false;
}
}
// at-last, if the value of continueToCheckNextInputs is still true
// this means that no error is found

@ -1246,6 +1246,62 @@
<input id="vpn-type" class="form-control operationDataKeys" type="hidden" data-key="type" value="OpenVPN" />
</div>
</div>
<!-- Always on VPN connection settings -->
<div id="always-on-vpn-heading" class="panel-heading" role="tab">
<h2 class="sub-title panel-title">
Always On VPN Settings
<label class="wr-input-control switch" data-toggle="collapse" data-target="#always-on-vpn-body">
<input type="checkbox"/>
<span class="helper"></span>
<span class="text"></span>
</label>
</h2>
<div class="panel-title-description">
Configure an always-on VPN connection through a specific VPN client application
</div>
</div>
<div id="always-on-vpn-body" class="panel-collapse panel-body collapse" role="tabpanel"
aria-labelledby="vpn-body">
<hr/>
<div class="always-on-vpn-message">
<ul class="message message-info">
<i class="icon fw fw-info"></i>
<a id="always-on-vpn-status-msg"> Below configurations are valid only when the Agent is
<b>work-profile owner</b> or <b>device owner</b>.</a>
</ul>
</div>
<br/>
<div class="wr-input-control">
<label class="wr-input-label" for="vpn-client-app">
VPN Client Application Package Name*
<span class="helper" title="Package name of the VPN client application to be configured.">
<span class="wr-help-tip glyphicon glyphicon-question-sign"></span>
</span>
</label>
<input id="vpn-client-app" type="text" class="form-control operationDataKeys"
data-key="packageName" maxlength="200" placeholder="[ Should be a valid package name ]"/>
</div>
<!--<div class="wr-input-control">-->
<!--<label class="wr-input-control checkbox">-->
<!--<input id="vpn-lock-down" type="checkbox" class="operationDataKeys"-->
<!--data-key="lockDownEnable" checked="checked"/>-->
<!--<span class="helper"-->
<!--title="True to disallow networking when the VPN is not connected or false otherwise">-->
<!--Lock Down VPN-->
<!--<span class="wr-help-tip glyphicon glyphicon-question-sign"></span>-->
<!--</span>-->
<!--</label>-->
<!--</div>-->
</div>
<!-- Always on VPN connection settings -->
</div>
</div>
<!-- /VPN -->

@ -2142,6 +2142,62 @@
data-key="openvpn_config" style="height: 400px;" disabled></textarea>
</div>
</div>
<!-- Always on VPN connection settings -->
<div id="always-on-vpn-heading" class="panel-heading" role="tab">
<h2 class="sub-title panel-title">
Always On VPN Settings
<label class="wr-input-control switch" data-toggle="collapse" data-target="#always-on-vpn-body">
<input type="checkbox"/>
<span class="helper"></span>
<span class="text"></span>
</label>
</h2>
<div class="panel-title-description">
Configure an always-on VPN connection through a specific VPN client application
</div>
</div>
<div id="always-on-vpn-body" class="panel-collapse panel-body collapse" role="tabpanel"
aria-labelledby="vpn-body">
<hr/>
<div class="always-on-vpn-message">
<ul class="message message-info">
<i class="icon fw fw-info"></i>
<a id="always-on-vpn-status-msg"> Below configurations are valid only when the Agent is
<b>work-profile owner</b> or <b>device owner</b>.</a>
</ul>
</div>
<br/>
<div class="wr-input-control">
<label class="wr-input-label" for="vpn-client-app">
VPN Client Application Package Name
<span class="helper" title="Package name of the VPN client application to be configured.">
<span class="wr-help-tip glyphicon glyphicon-question-sign"></span>
</span>
</label>
<input id="vpn-client-app" type="text" class="form-control operationDataKeys"
data-key="packageName" maxlength="200" placeholder="[ Should be a valid package name ]"/>
</div>
<!--<div class="wr-input-control">-->
<!--<label class="wr-input-control checkbox">-->
<!--<input id="vpn-lock-down" type="checkbox" class="operationDataKeys"-->
<!--data-key="lockDownEnable" checked="checked"/>-->
<!--<span class="helper"-->
<!--title="True to disallow networking when the VPN is not connected or false otherwise">-->
<!--Lock Down VPN-->
<!--<span class="wr-help-tip glyphicon glyphicon-question-sign"></span>-->
<!--</span>-->
<!--</label>-->
<!--</div>-->
</div>
<!-- Always on VPN connection settings -->
</div>
</div>
<!-- /VPN -->

@ -524,7 +524,8 @@ var validatePolicyProfile = function () {
operation = androidOperationConstants["VPN_OPERATION"];
// initializing continueToCheckNextInputs to true
continueToCheckNextInputs = true;
let openVpnConfigEnabled = document.getElementById('vpn-body').classList.contains('in');
if (openVpnConfigEnabled) {
var openvpnConfig = $("input#openvpn-config").val();
if (!openvpnConfig || openvpnConfig === '') {
validationStatus = {
@ -534,7 +535,24 @@ var validatePolicyProfile = function () {
};
continueToCheckNextInputs = false;
}
}
if (validationStatus) {
validationStatusArray.push(validationStatus);
}
let alwaysOnVpnEnabled = document.getElementById('always-on-vpn-body').classList.contains('in');
if (alwaysOnVpnEnabled) {
var alwaysOnConfig = $("input#vpn-client-app").val();
if (!alwaysOnConfig || alwaysOnConfig === '') {
validationStatus = {
"error": true,
"subErrorMsg": "Add a valid package name for the VPN client",
"erroneousFeature": "always-on-vpn"
};
continueToCheckNextInputs = false;
}
}
// at-last, if the value of continueToCheckNextInputs is still true
// this means that no error is found
if (continueToCheckNextInputs) {

@ -2160,8 +2160,68 @@
<input id="openvpn-config" class="form-control operationDataKeys" type="hidden" data-key="openvpn_config" />
<input id="vpn-type" class="form-control operationDataKeys" type="hidden" data-key="type" value="OpenVPN" />
</div>
<br/>
</div>
<!-- Always on VPN connection settings -->
<div id="always-on-vpn-heading" class="panel-heading" role="tab">
<h2 class="sub-title panel-title">
Always On VPN Settings
<label class="wr-input-control switch" data-toggle="collapse" data-target="#always-on-vpn-body">
<input type="checkbox"/>
<span class="helper"></span>
<span class="text"></span>
</label>
</h2>
<div class="panel-title-description">
Configure an always-on VPN connection through a specific VPN client application
</div>
</div>
<div id="always-on-vpn-body" class="panel-collapse panel-body collapse" role="tabpanel"
aria-labelledby="vpn-body">
<hr/>
<div class="always-on-vpn-message">
<ul class="message message-info">
<i class="icon fw fw-info"></i>
<a id="always-on-vpn-status-msg"> Below configurations are valid only when the Agent is
<b>work-profile owner</b> or <b>device owner</b>.</a>
</ul>
</div>
<br/>
<div id="always-on-vpn-feature-error-msg" class="alert alert-danger hidden" role="alert">
<i class="icon fw fw-error"></i><span></span>
</div>
<div class="wr-input-control">
<label class="wr-input-label" for="vpn-client-app">
VPN Client Application Package Name
<span class="helper" title="Package name of the VPN client application to be configured.">
<span class="wr-help-tip glyphicon glyphicon-question-sign"></span>
</span>
</label>
<input id="vpn-client-app" type="text" class="form-control operationDataKeys"
data-key="packageName" maxlength="200" placeholder="[ Should be a valid package name ]"/>
</div>
<!--<div class="wr-input-control">-->
<!--<label class="wr-input-control checkbox">-->
<!--<input id="vpn-lock-down" type="checkbox" class="operationDataKeys"-->
<!--data-key="lockDownEnable" checked="checked"/>-->
<!--<span class="helper"-->
<!--title="True to disallow networking when the VPN is not connected or false otherwise">-->
<!--Lock Down VPN-->
<!--<span class="wr-help-tip glyphicon glyphicon-question-sign"></span>-->
<!--</span>-->
<!--</label>-->
<!--</div>-->
</div>
<!-- Always on VPN connection settings -->
</div>
</div>
<!-- /VPN -->

@ -36,7 +36,8 @@
"perm:android:unlock-devices",
"perm:android:control-camera",
"perm:android:reboot",
"perm:android:logcat"
"perm:android:logcat",
"perm:android:send-app-restrictions"
],
"features": {
"DEVICE_RING": {
@ -214,6 +215,24 @@
}
],
"permission": "/device-mgt/devices/owning-device/operations/android/wipe"
},
"REMOTE_APP_CONFIG": {
"icon": "fw-lock",
"formParams": [
{
"type": "text",
"id": "app-id",
"optional": false,
"label": "Application identifier"
},
{
"type": "text",
"id": "payload",
"optional": false,
"label": "Application restriction payload"
}
],
"permission": "/device-mgt/devices/owning-device/operations/android/send-app-conf"
}
}
}

@ -17,6 +17,11 @@
}}
<!-- windows -->
<div class="panel panel-default">
<div class="panel-heading" role="tab">
<h2 class="sub-title panel-title">
Platform Configuration - Windows
</h2>
</div>
<div id="windows-config-body" class="panel-collapse panel-body" role="tabpanel">
<div id="windows-config-error-msg" class="alert alert-danger hidden" role="alert">
<i class="icon fw fw-error"></i><span></span>

@ -72,7 +72,7 @@ function promptErrorPolicyPlatform(errorMsg) {
$(document).ready(function () {
tinymce.init({
selector: "textarea",
selector: "#windows-eula",
height:500,
theme: "modern",
plugins: [

@ -347,6 +347,13 @@
<Name>Unlock the device</Name>
<Description>Unlock the device</Description>
</Feature>
<Feature code="REMOTE_APP_CONFIG">
<Name>Send app restriction</Name>
<Description>Send remote configurations to app</Description>
<Operation context="/api/device-mgt/android/v1.0/admin/devices/send-app-conf" method="POST"
type="application/json">
</Operation>
</Feature>
</Features>
<TaskConfiguration>
<Enable>true</Enable>

Loading…
Cancel
Save