Skip to content

Commit 2df5697

Browse files
committed
chore: add KernalModeTrust.Entitlements API
1 parent 8ad72f5 commit 2df5697

File tree

5 files changed

+183
-3
lines changed

5 files changed

+183
-3
lines changed

src/services/entitlements.js

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/*
2+
* GNU AGPL-3.0 License
3+
*
4+
* Copyright (c) 2021 - present core.ai . All rights reserved.
5+
*
6+
* This program is free software: you can redistribute it and/or modify it under
7+
* the terms of the GNU Affero General Public License as published by the Free
8+
* Software Foundation, either version 3 of the License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11+
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the GNU Affero General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Affero General Public License
15+
* along with this program. If not, see https://opensource.org/licenses/AGPL-3.0.
16+
*
17+
*/
18+
19+
/**
20+
* Entitlements Service
21+
*
22+
* This module provides a dedicated API for managing user entitlements,
23+
* including plan details, trial status, and feature entitlements.
24+
*/
25+
26+
define(function (require, exports, module) {
27+
const KernalModeTrust = window.KernalModeTrust;
28+
if(!KernalModeTrust){
29+
// integrated extensions will have access to kernal mode, but not external extensions
30+
throw new Error("Login service should have access to KernalModeTrust. Cannot boot without trust ring");
31+
}
32+
33+
const EventDispatcher = require("utils/EventDispatcher"),
34+
Strings = require("strings");
35+
36+
const MS_IN_DAY = 24 * 60 * 60 * 1000;
37+
const FREE_PLAN_VALIDITY_DAYS = 10000;
38+
39+
let LoginService;
40+
41+
// Create secure exports and set up event dispatcher
42+
const Entitlements = {};
43+
EventDispatcher.makeEventDispatcher(Entitlements);
44+
45+
// Event constants
46+
const EVENT_ENTITLEMENTS_CHANGED = "entitlements_changed";
47+
48+
/**
49+
* Check if user is logged in
50+
* @returns {*}
51+
*/
52+
function isLoggedIn() {
53+
return LoginService.isLoggedIn();
54+
}
55+
56+
/**
57+
* Get the plan details from entitlements with fallback to free plan defaults
58+
* @returns {Promise<Object>} Plan details object
59+
*/
60+
async function getPlanDetails() {
61+
const entitlements = await LoginService.getEffectiveEntitlements();
62+
63+
if (entitlements && entitlements.plan) {
64+
return entitlements.plan;
65+
}
66+
67+
// Fallback to free plan defaults
68+
const currentDate = Date.now();
69+
return {
70+
paidSubscriber: false,
71+
name: Strings.USER_FREE_PLAN_NAME,
72+
validTill: currentDate + (FREE_PLAN_VALIDITY_DAYS * MS_IN_DAY)
73+
};
74+
}
75+
76+
/**
77+
* Check if user is in a pro trial
78+
* @returns {Promise<boolean>} True if user is in pro trial, false otherwise
79+
*/
80+
async function isInProTrial() {
81+
const entitlements = await LoginService.getEffectiveEntitlements();
82+
return !!(entitlements && entitlements.isInProTrial);
83+
}
84+
85+
/**
86+
* Get remaining trial days
87+
* @returns {Promise<number>} Number of remaining trial days
88+
*/
89+
async function getTrialRemainingDays() {
90+
const entitlements = await LoginService.getEffectiveEntitlements();
91+
return entitlements && entitlements.trialDaysRemaining ? entitlements.trialDaysRemaining : 0;
92+
}
93+
94+
/**
95+
* Get raw entitlements from server
96+
* @returns {Promise<Object|null>} Raw entitlements object or null
97+
*/
98+
async function getRawEntitlements() {
99+
return await LoginService.getEntitlements();
100+
}
101+
102+
/**
103+
* Get live edit entitlement with fallback defaults
104+
* @returns {Promise<Object>} Live edit entitlement object
105+
*/
106+
async function getLiveEditEntitlement() {
107+
const entitlements = await LoginService.getEffectiveEntitlements();
108+
109+
if (entitlements && entitlements.entitlements && entitlements.entitlements.liveEdit) {
110+
return entitlements.entitlements.liveEdit;
111+
}
112+
113+
// Fallback defaults when live edit entitlement is not available from API
114+
return {
115+
activated: false,
116+
subscribeURL: brackets.config.purchase_url,
117+
upgradeToPlan: brackets.config.main_pro_plan
118+
};
119+
}
120+
121+
// Set up KernalModeTrust.Entitlements
122+
KernalModeTrust.Entitlements = Entitlements;
123+
124+
let inited = false;
125+
function init() {
126+
if(inited){
127+
return;
128+
}
129+
inited = true;
130+
LoginService = KernalModeTrust.loginService;
131+
// Set up event forwarding from LoginService
132+
LoginService.on(LoginService.EVENT_ENTITLEMENTS_CHANGED, function() {
133+
Entitlements.trigger(EVENT_ENTITLEMENTS_CHANGED);
134+
});
135+
}
136+
137+
// Test-only exports for integration testing
138+
if (Phoenix.isTestWindow) {
139+
window._test_entitlements_exports = {
140+
EntitlementsService: Entitlements,
141+
isLoggedIn,
142+
getPlanDetails,
143+
isInProTrial,
144+
getTrialRemainingDays,
145+
getRawEntitlements,
146+
getLiveEditEntitlement
147+
};
148+
}
149+
150+
exports.init = init;
151+
// no public exports to prevent extension tampering
152+
153+
// Add functions to secure exports
154+
Entitlements.isLoggedIn = isLoggedIn;
155+
Entitlements.getPlanDetails = getPlanDetails;
156+
Entitlements.isInProTrial = isInProTrial;
157+
Entitlements.getTrialRemainingDays = getTrialRemainingDays;
158+
Entitlements.getRawEntitlements = getRawEntitlements;
159+
Entitlements.getLiveEditEntitlement = getLiveEditEntitlement;
160+
Entitlements.EVENT_ENTITLEMENTS_CHANGED = EVENT_ENTITLEMENTS_CHANGED;
161+
});

src/services/login-browser.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
*/
4444

4545
define(function (require, exports, module) {
46-
require("./login-service"); // after this, loginService will be in KernalModeTrust
46+
const LoginServiceDirectImport = require("./login-service"); // after this, loginService will be in KernalModeTrust
4747
const PreferencesManager = require("preferences/PreferencesManager"),
4848
Metrics = require("utils/Metrics"),
4949
Dialogs = require("widgets/Dialogs"),
@@ -389,11 +389,12 @@ define(function (require, exports, module) {
389389
}
390390

391391
function init() {
392-
ProfileMenu.init();
393392
if(Phoenix.isNativeApp){
394393
console.log("Browser login service is not needed for native app");
395394
return;
396395
}
396+
ProfileMenu.init();
397+
LoginServiceDirectImport.init();
397398

398399
// Always verify login on browser app start
399400
_verifyBrowserLogin().catch(console.error);

src/services/login-desktop.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
/*global logger*/
2020

2121
define(function (require, exports, module) {
22-
require("./login-service"); // after this, loginService will be in KernalModeTrust
22+
const LoginServiceDirectImport = require("./login-service"); // after this, loginService will be in KernalModeTrust
2323

2424
const EventDispatcher = require("utils/EventDispatcher"),
2525
PreferencesManager = require("preferences/PreferencesManager"),
@@ -400,6 +400,7 @@ define(function (require, exports, module) {
400400
return;
401401
}
402402
ProfileMenu.init();
403+
LoginServiceDirectImport.init();
403404
_verifyLogin(true).catch(console.error);// todo raise metrics - silent check on init
404405
const pref = PreferencesManager.stateManager.definePreference(PREF_USER_PROFILE_VERSION, 'string', '0');
405406
pref.watchExternalChanges();

src/services/login-service.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ define(function (require, exports, module) {
2929
require("./setup-login-service"); // this adds loginService to KernalModeTrust
3030
require("./promotions");
3131
require("./login-utils");
32+
const EntitlementsDirectImport = require("./entitlements"); // this adds Entitlements to KernalModeTrust
3233

3334
const Metrics = require("utils/Metrics"),
3435
Strings = require("strings");
@@ -570,6 +571,14 @@ define(function (require, exports, module) {
570571
LoginService.getSalt = getSalt;
571572
LoginService.EVENT_ENTITLEMENTS_CHANGED = EVENT_ENTITLEMENTS_CHANGED;
572573

574+
let inited = false;
575+
function init() {
576+
if(inited){
577+
return;
578+
}
579+
inited = true;
580+
EntitlementsDirectImport.init();
581+
}
573582
// Test-only exports for integration testing
574583
if (Phoenix.isTestWindow) {
575584
window._test_login_service_exports = {
@@ -586,4 +595,7 @@ define(function (require, exports, module) {
586595

587596
// Start the entitlements monitor timer
588597
startEntitlementsMonitor();
598+
599+
exports.init = init;
600+
// no public exports to prevent extension tampering
589601
});

src/services/profile-menu.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,12 @@ define(function (require, exports, module) {
598598
}
599599
}
600600

601+
let inited = false;
601602
function init() {
603+
if (inited) {
604+
return;
605+
}
606+
inited = true;
602607
const helpButtonID = "user-profile-button";
603608
$icon = $("<a>")
604609
.attr({

0 commit comments

Comments
 (0)