1616 *
1717 */
1818
19- /*global path*/
19+ /*global path, logger */
2020
2121/**
2222 * Shared Login Service
@@ -29,11 +29,16 @@ define(function (require, exports, module) {
2929 require ( "./setup-login-service" ) ; // this adds loginService to KernalModeTrust
3030 require ( "./promotions" ) ;
3131 require ( "./login-utils" ) ;
32+ const NodeUtils = require ( "utils/NodeUtils" ) ,
33+ PreferencesManager = require ( "preferences/PreferencesManager" ) ;
3234 const EntitlementsDirectImport = require ( "./EntitlementsManager" ) ; // this adds Entitlements to KernalModeTrust
3335
3436 const Metrics = require ( "utils/Metrics" ) ,
3537 Strings = require ( "strings" ) ;
3638
39+ const PREF_STATE_LICENSED_DEVICE_CHECK = "LICENSED_DEVICE_CHECK" ;
40+ PreferencesManager . stateManager . definePreference ( PREF_STATE_LICENSED_DEVICE_CHECK , "boolean" , false ) ;
41+
3742 const MS_IN_DAY = 10 * 24 * 60 * 60 * 1000 ;
3843 const TEN_MINUTES = 10 * 60 * 1000 ;
3944 const FREE_PLAN_VALIDITY_DAYS = 10000 ;
@@ -211,6 +216,34 @@ define(function (require, exports, module) {
211216 }
212217 }
213218
219+ async function _getLinuxDeviceID ( ) {
220+ const LINUX_DEVICE_ID_FILE = Phoenix . VFS . getTauriVirtualPath ( '/etc/machine-id' ) ;
221+ const result = await Phoenix . VFS . readFileResolves ( LINUX_DEVICE_ID_FILE , 'utf8' ) ;
222+ if ( result . error || ! result . data ) {
223+ logger . reportError ( result . error , `Failed to read machine-id file for licensing` ) ;
224+ return null ;
225+ }
226+ return KernalModeTrust . generateDataSignature ( result . data . trim ( ) ) ; // \n and spaces are trimmed, just id please
227+ }
228+
229+ let deviceIDCached = null ;
230+ async function getDeviceID ( ) {
231+ if ( ! Phoenix . isNativeApp ) {
232+ // We only grant device licenses to desktop apps. Browsers cannot be uniquely device identified obviously.
233+ return null ;
234+ }
235+ if ( deviceIDCached ) {
236+ return deviceIDCached ;
237+ }
238+ switch ( Phoenix . platform ) {
239+ case 'linux' : deviceIDCached = await _getLinuxDeviceID ( ) ;
240+ }
241+ return deviceIDCached ;
242+ }
243+
244+
245+ let deviceLicensePrimed = false ,
246+ licencedDeviceCredsAvailable = false ;
214247
215248 /**
216249 * Get entitlements from API or disc cache.
@@ -219,8 +252,14 @@ define(function (require, exports, module) {
219252 * Returns null if the user is not logged in
220253 */
221254 async function getEntitlements ( forceRefresh = false ) {
255+ if ( ! deviceLicensePrimed ) {
256+ deviceLicensePrimed = true ;
257+ // we cache this as device license is only checked at app start. As invoves some files in system loactions,
258+ // we dont want file access errors to happen on every entitlement check.
259+ licencedDeviceCredsAvailable = await isLicensedDevice ( ) ;
260+ }
222261 // Return null if not logged in
223- if ( ! LoginService . isLoggedIn ( ) ) {
262+ if ( ! LoginService . isLoggedIn ( ) && ! licencedDeviceCredsAvailable ) {
224263 return null ;
225264 }
226265
@@ -254,7 +293,10 @@ define(function (require, exports, module) {
254293 const language = brackets . getLocale ( ) ;
255294 const currentVersion = window . AppConfig . apiVersion || "1.0.0" ;
256295 let url = `${ accountBaseURL } /getAppEntitlements?lang=${ language } &version=${ currentVersion } ` +
257- `&platform=${ Phoenix . platform } &appType=${ Phoenix . isNativeApp ? "desktop" : "browser" } }` ;
296+ `&platform=${ Phoenix . platform } &appType=${ Phoenix . isNativeApp ? "desktop" : "browser" } ` ;
297+ if ( licencedDeviceCredsAvailable ) {
298+ url += `&deviceID=${ await getDeviceID ( ) } ` ;
299+ }
258300 let fetchOptions = {
259301 method : 'GET' ,
260302 headers : {
@@ -268,7 +310,7 @@ define(function (require, exports, module) {
268310 const profile = LoginService . getProfile ( ) ;
269311 if ( profile && profile . apiKey && profile . validationCode ) {
270312 url += `&appSessionID=${ encodeURIComponent ( profile . apiKey ) } &validationCode=${ encodeURIComponent ( profile . validationCode ) } ` ;
271- } else {
313+ } else if ( ! licencedDeviceCredsAvailable ) {
272314 console . error ( 'Missing appSessionID or validationCode for desktop app entitlements' ) ;
273315 return null ;
274316 }
@@ -596,11 +638,43 @@ define(function (require, exports, module) {
596638 } ;
597639 }
598640
641+ async function addDeviceLicense ( ) {
642+ deviceLicensePrimed = false ;
643+ PreferencesManager . stateManager . set ( PREF_STATE_LICENSED_DEVICE_CHECK , true ) ;
644+ return NodeUtils . addDeviceLicenseSystemWide ( ) ;
645+ }
646+
647+ async function removeDeviceLicense ( ) {
648+ deviceLicensePrimed = false ;
649+ PreferencesManager . stateManager . set ( PREF_STATE_LICENSED_DEVICE_CHECK , false ) ;
650+ return NodeUtils . removeDeviceLicenseSystemWide ( ) ;
651+ }
652+
653+ async function isLicensedDeviceSystemWide ( ) {
654+ return NodeUtils . isLicensedDeviceSystemWide ( ) ;
655+ }
656+
657+ /**
658+ * Checks if app is configured to check for device licenses at app start at system or user level.
659+ *
660+ * @returns {Promise<boolean> } - Resolves with `true` if the device is licensed, `false` otherwise.
661+ */
662+ async function isLicensedDevice ( ) {
663+ const userCheck = PreferencesManager . stateManager . get ( PREF_STATE_LICENSED_DEVICE_CHECK ) ;
664+ const systemCheck = await isLicensedDeviceSystemWide ( ) ;
665+ return userCheck || systemCheck ;
666+ }
667+
599668 // Add functions to secure exports
600669 LoginService . getEntitlements = getEntitlements ;
601670 LoginService . getEffectiveEntitlements = getEffectiveEntitlements ;
602671 LoginService . clearEntitlements = clearEntitlements ;
603672 LoginService . getSalt = getSalt ;
673+ LoginService . addDeviceLicense = addDeviceLicense ;
674+ LoginService . removeDeviceLicense = removeDeviceLicense ;
675+ LoginService . isLicensedDevice = isLicensedDevice ;
676+ LoginService . isLicensedDeviceSystemWide = isLicensedDeviceSystemWide ;
677+ LoginService . getDeviceID = getDeviceID ;
604678 LoginService . EVENT_ENTITLEMENTS_CHANGED = EVENT_ENTITLEMENTS_CHANGED ;
605679
606680 let inited = false ;
0 commit comments