Skip to content

Commit 65a016c

Browse files
store activation code at rest
1 parent a452d78 commit 65a016c

File tree

5 files changed

+40
-24
lines changed

5 files changed

+40
-24
lines changed

packages/shared/src/crypto.ts

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,21 @@ export function encrypt(text: string): { iv: string; encryptedData: string } {
2727
return { iv: iv.toString('hex'), encryptedData: encrypted };
2828
}
2929

30+
export function decrypt(iv: string, encryptedText: string): string {
31+
const encryptionKey = Buffer.from(env.SOURCEBOT_ENCRYPTION_KEY, 'ascii');
32+
33+
const ivBuffer = Buffer.from(iv, 'hex');
34+
const encryptedBuffer = Buffer.from(encryptedText, 'hex');
35+
36+
const decipher = crypto.createDecipheriv(algorithm, encryptionKey, ivBuffer);
37+
38+
let decrypted = decipher.update(encryptedBuffer, undefined, 'utf8');
39+
decrypted += decipher.final('utf8');
40+
41+
return decrypted;
42+
}
43+
44+
3045
export function hashSecret(text: string): string {
3146
return crypto.createHmac('sha256', env.SOURCEBOT_ENCRYPTION_KEY).update(text).digest('hex');
3247
}
@@ -61,20 +76,6 @@ export function generateOAuthRefreshToken(): { token: string; hash: string } {
6176
};
6277
}
6378

64-
export function decrypt(iv: string, encryptedText: string): string {
65-
const encryptionKey = Buffer.from(env.SOURCEBOT_ENCRYPTION_KEY, 'ascii');
66-
67-
const ivBuffer = Buffer.from(iv, 'hex');
68-
const encryptedBuffer = Buffer.from(encryptedText, 'hex');
69-
70-
const decipher = crypto.createDecipheriv(algorithm, encryptionKey, ivBuffer);
71-
72-
let decrypted = decipher.update(encryptedBuffer, undefined, 'utf8');
73-
decrypted += decipher.final('utf8');
74-
75-
return decrypted;
76-
}
77-
7879
export function verifySignature(data: string, signature: string, publicKeyPath: string): boolean {
7980
try {
8081
let publicKey = publicKeyCache.get(publicKeyPath);
@@ -226,3 +227,13 @@ export function decryptOAuthToken(encryptedText: string | null | undefined): str
226227
return encryptedText;
227228
}
228229
}
230+
231+
export function encryptActivationCode(code: string): string {
232+
const { iv, encryptedData } = encrypt(code);
233+
return Buffer.from(JSON.stringify({ iv, encryptedData })).toString('base64');
234+
}
235+
236+
export function decryptActivationCode(encrypted: string): string {
237+
const { iv, encryptedData } = JSON.parse(Buffer.from(encrypted, 'base64').toString('utf8'));
238+
return decrypt(iv, encryptedData);
239+
}

packages/shared/src/index.server.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ export {
4949
verifySignature,
5050
encryptOAuthToken,
5151
decryptOAuthToken,
52+
encryptActivationCode,
53+
decryptActivationCode,
5254
} from "./crypto.js";
5355
export {
5456
getDBConnectionString,

packages/web/src/app/(app)/settings/license/purchaseButton.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ import { LoadingButton } from "@/components/ui/loading-button";
55
import { createCheckoutSession } from "@/ee/features/lighthouse/actions";
66
import { isServiceError } from "@/lib/utils";
77
import { useToast } from "@/components/hooks/use-toast";
8+
import { useRouter } from "next/navigation";
89

910
export function PurchaseButton() {
1011
const [isLoading, setIsLoading] = useState(false);
1112
const { toast } = useToast();
13+
const router = useRouter();
1214

1315
const handleClick = useCallback(() => {
1416
setIsLoading(true);
@@ -23,14 +25,12 @@ export function PurchaseButton() {
2325
description: `Failed to start checkout: ${response.message}`,
2426
variant: "destructive",
2527
});
28+
setIsLoading(false);
2629
} else {
27-
window.location.href = response.url;
30+
router.push(response.url);
2831
}
2932
})
30-
.finally(() => {
31-
setIsLoading(false);
32-
});
33-
}, [toast]);
33+
}, [router, toast]);
3434

3535
return (
3636
<LoadingButton

packages/web/src/ee/features/lighthouse/actions.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { OrgRole } from "@sourcebot/db";
77
import { ServiceError } from "@/lib/serviceError";
88
import { StatusCodes } from "http-status-codes";
99
import { ErrorCode } from "@/lib/errorCodes";
10-
import { env } from "@sourcebot/shared";
10+
import { env, encryptActivationCode } from "@sourcebot/shared";
1111
import { sendServicePing } from "@/ee/features/lighthouse/servicePing";
1212
import { fetchWithRetry } from "@/lib/utils";
1313
import { checkoutResponseSchema } from "./types";
@@ -28,11 +28,10 @@ export const activateLicense = async (activationCode: string): Promise<{ success
2828
} satisfies ServiceError;
2929
}
3030

31-
// Create the license record
3231
await prisma.license.create({
3332
data: {
3433
orgId: org.id,
35-
activationCode,
34+
activationCode: encryptActivationCode(activationCode),
3635
},
3736
});
3837

packages/web/src/ee/features/lighthouse/servicePing.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { fetchWithRetry } from "@/lib/utils";
22
import { __unsafePrisma } from "@/prisma";
3-
import { createLogger, env, SOURCEBOT_VERSION } from "@sourcebot/shared";
3+
import { createLogger, env, SOURCEBOT_VERSION, decryptActivationCode } from "@sourcebot/shared";
44
import { SINGLE_TENANT_ORG_ID } from "@/lib/constants";
55
import { lighthouseResponseSchema } from "./types";
66

@@ -14,10 +14,14 @@ export const sendServicePing = async () => {
1414
where: { orgId: SINGLE_TENANT_ORG_ID },
1515
});
1616

17+
const activationCode = license?.activationCode
18+
? decryptActivationCode(license.activationCode)
19+
: undefined;
20+
1721
const payload = {
1822
installId: env.SOURCEBOT_INSTALL_ID,
1923
version: SOURCEBOT_VERSION,
20-
...(license?.activationCode && { activationCode: license.activationCode }),
24+
...(activationCode && { activationCode }),
2125
};
2226

2327
try {

0 commit comments

Comments
 (0)