Merge pull request 'Add Microsoft Store integration for Windows devices' (#443) from navodzoysa/device-mgt-core:issue-10635/store-integration into master

Reviewed-on: community/device-mgt-core#443
master
Pahansith Gunathilake 4 months ago
commit 03457250fa

@ -778,6 +778,8 @@ public class ApplicationManagerImpl implements ApplicationManager {
return Constants.GOOGLE_PLAY_STORE_URL; return Constants.GOOGLE_PLAY_STORE_URL;
} else if (DeviceTypes.IOS.toString().equalsIgnoreCase(deviceType)) { } else if (DeviceTypes.IOS.toString().equalsIgnoreCase(deviceType)) {
return Constants.APPLE_STORE_URL; return Constants.APPLE_STORE_URL;
} else if (DeviceTypes.WINDOWS.toString().equalsIgnoreCase(deviceType)) {
return Constants.MICROSOFT_STORE_URL;
} else { } else {
throw new IllegalArgumentException("No such device with the name " + deviceType); throw new IllegalArgumentException("No such device with the name " + deviceType);
} }

@ -74,6 +74,7 @@ public class Constants {
public static final String IS_USER_ABLE_TO_VIEW_ALL_ROLES = "isUserAbleToViewAllRoles"; public static final String IS_USER_ABLE_TO_VIEW_ALL_ROLES = "isUserAbleToViewAllRoles";
public static final String GOOGLE_PLAY_STORE_URL = "https://play.google.com/store/apps/details?id="; public static final String GOOGLE_PLAY_STORE_URL = "https://play.google.com/store/apps/details?id=";
public static final String APPLE_STORE_URL = "https://itunes.apple.com/country/app/app-name/id"; public static final String APPLE_STORE_URL = "https://itunes.apple.com/country/app/app-name/id";
public static final String MICROSOFT_STORE_URL = "https://apps.microsoft.com/detail/";
public static final String GOOGLE_PLAY_SYNCED_APP = "GooglePlaySyncedApp"; public static final String GOOGLE_PLAY_SYNCED_APP = "GooglePlaySyncedApp";
// Subscription task related constants // Subscription task related constants

@ -58,6 +58,8 @@ public class MDMAppConstants {
} }
public static final String INSTALL_ENTERPRISE_APPLICATION = "INSTALL_ENTERPRISE_APPLICATION"; public static final String INSTALL_ENTERPRISE_APPLICATION = "INSTALL_ENTERPRISE_APPLICATION";
public static final String UNINSTALL_ENTERPRISE_APPLICATION = "UNINSTALL_ENTERPRISE_APPLICATION"; public static final String UNINSTALL_ENTERPRISE_APPLICATION = "UNINSTALL_ENTERPRISE_APPLICATION";
public static final String INSTALL_STORE_APPLICATION = "INSTALL_STORE_APPLICATION";
public static final String UNINSTALL_STORE_APPLICATION = "UNINSTALL_STORE_APPLICATION";
public static final String INSTALL_WEB_CLIP_APPLICATION = "INSTALL_WEB_CLIP"; public static final String INSTALL_WEB_CLIP_APPLICATION = "INSTALL_WEB_CLIP";
public static final String UNINSTALL_WEB_CLIP_APPLICATION = "UNINSTALL_WEB_CLIP"; public static final String UNINSTALL_WEB_CLIP_APPLICATION = "UNINSTALL_WEB_CLIP";
//App type constants related to window device type //App type constants related to window device type

@ -0,0 +1,49 @@
/*
* Copyright (c) 2018 - 2024, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. 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 io.entgra.device.mgt.core.device.mgt.common.app.mgt.windows;
import com.google.gson.Gson;
import java.io.Serializable;
public class AppStoreApplication implements Serializable {
private String type;
private String packageIdentifier;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getPackageIdentifier() {
return packageIdentifier;
}
public void setPackageIdentifier(String packageIdentifier) {
this.packageIdentifier = packageIdentifier;
}
public String toJSON() {
Gson gson = new Gson();
return gson.toJson(this);
}
}

@ -27,6 +27,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import io.entgra.device.mgt.core.device.mgt.common.MDMAppConstants; import io.entgra.device.mgt.core.device.mgt.common.MDMAppConstants;
import io.entgra.device.mgt.core.device.mgt.common.app.mgt.App; import io.entgra.device.mgt.core.device.mgt.common.app.mgt.App;
import io.entgra.device.mgt.core.device.mgt.common.app.mgt.windows.AppStoreApplication;
import io.entgra.device.mgt.core.device.mgt.common.app.mgt.windows.EnterpriseApplication; import io.entgra.device.mgt.core.device.mgt.common.app.mgt.windows.EnterpriseApplication;
import io.entgra.device.mgt.core.device.mgt.common.app.mgt.windows.HostedAppxApplication; import io.entgra.device.mgt.core.device.mgt.common.app.mgt.windows.HostedAppxApplication;
import io.entgra.device.mgt.core.device.mgt.common.app.mgt.windows.HostedMSIApplication; import io.entgra.device.mgt.core.device.mgt.common.app.mgt.windows.HostedMSIApplication;
@ -62,64 +63,26 @@ public class MDMWindowsOperationUtil {
switch (application.getType()) { switch (application.getType()) {
case ENTERPRISE: case ENTERPRISE:
operation.setCode(MDMAppConstants.WindowsConstants.INSTALL_ENTERPRISE_APPLICATION);
EnterpriseApplication enterpriseApplication = new EnterpriseApplication(); EnterpriseApplication enterpriseApplication = new EnterpriseApplication();
if (appType.equalsIgnoreCase(MDMAppConstants.WindowsConstants.APPX)) { createEnterpriseAppPayload(appType, metaJsonArray, enterpriseApplication);
HostedAppxApplication hostedAppxApplication = new HostedAppxApplication(); operation.setCode(MDMAppConstants.WindowsConstants.INSTALL_ENTERPRISE_APPLICATION);
List<String> dependencyPackageList = new ArrayList<>();
for (int i = 0; i < metaJsonArray.size(); i++) {
JsonElement metaElement = metaJsonArray.get(i);
JsonObject metaObject = metaElement.getAsJsonObject();
if (MDMAppConstants.WindowsConstants.APPX_PACKAGE_URI.equals(metaObject.get("key").getAsString())) {
hostedAppxApplication.setPackageUri(metaObject.get("value").getAsString().trim());
}
else if (MDMAppConstants.WindowsConstants.APPX_PACKAGE_FAMILY_NAME.equals(metaObject.get("key").getAsString())) {
hostedAppxApplication.setPackageFamilyName(metaObject.get("value").getAsString().trim());
}
else if (MDMAppConstants.WindowsConstants.APPX_DEPENDENCY_PACKAGE_URL.equals(metaObject.get("key").getAsString())
&& metaObject.has("value")) {
dependencyPackageList.add(metaObject.get("value").getAsString().trim());
hostedAppxApplication.setDependencyPackageUri(dependencyPackageList);
}
else if (MDMAppConstants.WindowsConstants.APPX_CERTIFICATE_HASH.equals(metaObject.get("key").getAsString())
&& metaObject.has("value")) {
hostedAppxApplication.setCertificateHash(metaObject.get("value").getAsString().trim());
}
else if (MDMAppConstants.WindowsConstants.APPX_ENCODED_CERT_CONTENT.equals(metaObject.get("key").getAsString())
&& metaObject.has("value")) {
hostedAppxApplication.setEncodedCertificate(metaObject.get("value").getAsString().trim());
}
}
enterpriseApplication.setHostedAppxApplication(hostedAppxApplication);
} else if (appType.equalsIgnoreCase(MDMAppConstants.WindowsConstants.MSI)) {
HostedMSIApplication hostedMSIApplication = new HostedMSIApplication();
for (int i = 0; i < metaJsonArray.size(); i++) {
JsonElement metaElement = metaJsonArray.get(i);
JsonObject metaObject = metaElement.getAsJsonObject();
if (MDMAppConstants.WindowsConstants.MSI_PRODUCT_ID.equals(metaObject.get("key").getAsString())) {
hostedMSIApplication.setProductId(metaObject.get("value").getAsString().trim());
}
else if (MDMAppConstants.WindowsConstants.MSI_CONTENT_URI.equals(metaObject.get("key").getAsString())) {
hostedMSIApplication.setContentUrl(metaObject.get("value").getAsString().trim());
}
else if (MDMAppConstants.WindowsConstants.MSI_FILE_HASH.equals(metaObject.get("key").getAsString())) {
hostedMSIApplication.setFileHash(metaObject.get("value").getAsString().trim());
}
}
enterpriseApplication.setHostedMSIApplication(hostedMSIApplication);
}
operation.setPayLoad(enterpriseApplication.toJSON()); operation.setPayLoad(enterpriseApplication.toJSON());
break; break;
case PUBLIC:
AppStoreApplication appStoreApplication = new AppStoreApplication();
appStoreApplication.setType(application.getType().toString());
appStoreApplication.setPackageIdentifier(application.getIdentifier());
operation.setCode(MDMAppConstants.WindowsConstants.INSTALL_STORE_APPLICATION);
operation.setPayLoad(appStoreApplication.toJSON());
break;
case WEB_CLIP: case WEB_CLIP:
operation.setCode(MDMAppConstants.WindowsConstants.INSTALL_WEB_CLIP_APPLICATION);
WebClipApplication webClipApplication = new WebClipApplication(); WebClipApplication webClipApplication = new WebClipApplication();
webClipApplication.setUrl(application.getLocation()); webClipApplication.setUrl(application.getLocation());
webClipApplication.setName(application.getName()); webClipApplication.setName(application.getName());
webClipApplication.setIcon(application.getIconImage()); webClipApplication.setIcon(application.getIconImage());
webClipApplication.setProperties(application.getProperties()); webClipApplication.setProperties(application.getProperties());
webClipApplication.setType(application.getType().toString()); webClipApplication.setType(application.getType().toString());
operation.setCode(MDMAppConstants.WindowsConstants.INSTALL_WEB_CLIP_APPLICATION);
operation.setPayLoad(webClipApplication.toJSON()); operation.setPayLoad(webClipApplication.toJSON());
break; break;
default: default:
@ -148,14 +111,53 @@ public class MDMWindowsOperationUtil {
switch (application.getType()) { switch (application.getType()) {
case ENTERPRISE: case ENTERPRISE:
operation.setCode(MDMAppConstants.WindowsConstants.UNINSTALL_ENTERPRISE_APPLICATION);
EnterpriseApplication enterpriseApplication = new EnterpriseApplication(); EnterpriseApplication enterpriseApplication = new EnterpriseApplication();
if (appType.equalsIgnoreCase(MDMAppConstants.WindowsConstants.APPX)) { createEnterpriseAppPayload(appType, metaJsonArray, enterpriseApplication);
operation.setCode(MDMAppConstants.WindowsConstants.UNINSTALL_ENTERPRISE_APPLICATION);
operation.setPayLoad(enterpriseApplication.toJSON());
break;
case PUBLIC:
AppStoreApplication appStoreApplication = new AppStoreApplication();
appStoreApplication.setType(application.getType().toString());
appStoreApplication.setPackageIdentifier(application.getIdentifier());
operation.setCode(MDMAppConstants.WindowsConstants.UNINSTALL_STORE_APPLICATION);
operation.setPayLoad(appStoreApplication.toJSON());
break;
case WEB_CLIP:
WebClipApplication webClipApplication = new WebClipApplication();
webClipApplication.setUrl(application.getLocation());
webClipApplication.setName(application.getName());
webClipApplication.setIcon(application.getIconImage());
webClipApplication.setProperties(application.getProperties());
webClipApplication.setType(application.getType().toString());
operation.setCode(MDMAppConstants.WindowsConstants.UNINSTALL_WEB_CLIP_APPLICATION);
operation.setPayLoad(webClipApplication.toJSON());
default:
String msg = "Application type " + application.getType() + " is not supported";
log.error(msg);
throw new UnknownApplicationTypeException(msg);
}
return operation;
}
/**
* Helper method to create enterprise APPX and MSI app payloads for both install and uninstall operations
* @param appType contains whether the app type is APPX or MSI
* @param metaJsonArray JSON array containing metadata of APPX and MSI apps
* @param enterpriseApplication {@link EnterpriseApplication} contains operation payload content that will be sent to the device
*/
private static void createEnterpriseAppPayload(String appType, JsonArray metaJsonArray, EnterpriseApplication enterpriseApplication) {
JsonElement metaElement;
JsonObject metaObject;
if (MDMAppConstants.WindowsConstants.APPX.equalsIgnoreCase(appType)) {
HostedAppxApplication hostedAppxApplication = new HostedAppxApplication(); HostedAppxApplication hostedAppxApplication = new HostedAppxApplication();
List<String> dependencyPackageList = new ArrayList<>(); List<String> dependencyPackageList = new ArrayList<>();
for (int i = 0; i < metaJsonArray.size(); i++) { for (int i = 0; i < metaJsonArray.size(); i++) {
JsonElement metaElement = metaJsonArray.get(i); metaElement = metaJsonArray.get(i);
JsonObject metaObject = metaElement.getAsJsonObject(); metaObject = metaElement.getAsJsonObject();
if (MDMAppConstants.WindowsConstants.APPX_PACKAGE_URI.equals(metaObject.get("key").getAsString())) { if (MDMAppConstants.WindowsConstants.APPX_PACKAGE_URI.equals(metaObject.get("key").getAsString())) {
hostedAppxApplication.setPackageUri(metaObject.get("value").getAsString().trim()); hostedAppxApplication.setPackageUri(metaObject.get("value").getAsString().trim());
@ -179,11 +181,13 @@ public class MDMWindowsOperationUtil {
} }
enterpriseApplication.setHostedAppxApplication(hostedAppxApplication); enterpriseApplication.setHostedAppxApplication(hostedAppxApplication);
} else if (appType.equalsIgnoreCase(MDMAppConstants.WindowsConstants.MSI)) { } else if (MDMAppConstants.WindowsConstants.MSI.equalsIgnoreCase(appType)) {
HostedMSIApplication hostedMSIApplication = new HostedMSIApplication(); HostedMSIApplication hostedMSIApplication = new HostedMSIApplication();
for (int i = 0; i < metaJsonArray.size(); i++) { for (int i = 0; i < metaJsonArray.size(); i++) {
JsonElement metaElement = metaJsonArray.get(i); metaElement = metaJsonArray.get(i);
JsonObject metaObject = metaElement.getAsJsonObject(); metaObject = metaElement.getAsJsonObject();
if (MDMAppConstants.WindowsConstants.MSI_PRODUCT_ID.equals(metaObject.get("key").getAsString())) { if (MDMAppConstants.WindowsConstants.MSI_PRODUCT_ID.equals(metaObject.get("key").getAsString())) {
hostedMSIApplication.setProductId(metaObject.get("value").getAsString().trim()); hostedMSIApplication.setProductId(metaObject.get("value").getAsString().trim());
} }
@ -196,24 +200,6 @@ public class MDMWindowsOperationUtil {
} }
enterpriseApplication.setHostedMSIApplication(hostedMSIApplication); enterpriseApplication.setHostedMSIApplication(hostedMSIApplication);
} }
operation.setPayLoad(enterpriseApplication.toJSON());
break;
case WEB_CLIP:
operation.setCode(MDMAppConstants.WindowsConstants.UNINSTALL_WEB_CLIP_APPLICATION);
WebClipApplication webClipApplication = new WebClipApplication();
webClipApplication.setUrl(application.getLocation());
webClipApplication.setName(application.getName());
webClipApplication.setIcon(application.getIconImage());
webClipApplication.setProperties(application.getProperties());
webClipApplication.setType(application.getType().toString());
operation.setPayLoad(webClipApplication.toJSON());
default:
String msg = "Application type " + application.getType() + " is not supported";
log.error(msg);
throw new UnknownApplicationTypeException(msg);
}
return operation;
} }
/** /**
@ -223,8 +209,7 @@ public class MDMWindowsOperationUtil {
* @return string extension of the windows app types(either appx or msi) * @return string extension of the windows app types(either appx or msi)
*/ */
public static String windowsAppType(String installerName) { public static String windowsAppType(String installerName) {
String extension = installerName.substring(installerName.lastIndexOf(".") + 1); return installerName.substring(installerName.lastIndexOf(".") + 1);
return extension;
} }
/** /**
@ -234,8 +219,7 @@ public class MDMWindowsOperationUtil {
* @return the metaData Json String as Json Array * @return the metaData Json String as Json Array
*/ */
public static JsonArray jsonStringToArray(String metaData) { public static JsonArray jsonStringToArray(String metaData) {
JsonArray metaJsonArray = new JsonParser().parse(metaData).getAsJsonArray(); return new JsonParser().parse(metaData).getAsJsonArray();
return metaJsonArray;
} }

@ -387,6 +387,7 @@
<Scope>win:ops:device-info</Scope> <Scope>win:ops:device-info</Scope>
<Scope>win:ops:security-info</Scope> <Scope>win:ops:security-info</Scope>
<Scope>win:ops:firewall-info</Scope> <Scope>win:ops:firewall-info</Scope>
<Scope>win:microsoft-store:search</Scope>
<Scope>admin:tenant:view</Scope> <Scope>admin:tenant:view</Scope>
<Scope>dm:admin:devices:usage:view</Scope> <Scope>dm:admin:devices:usage:view</Scope>
<Scope>and:ops:clear-app</Scope> <Scope>and:ops:clear-app</Scope>

Loading…
Cancel
Save