Skip to content

Commit 76a950a

Browse files
authored
Merge pull request #373 from internxt/feat/update-sdk-and-remove-legacy-code
[PB-4836]: feat/update-sdk-and-remove-legacy-code
2 parents 31f2512 + 9f6fe4f commit 76a950a

11 files changed

Lines changed: 49 additions & 363 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"@inquirer/prompts": "7.8.6",
4040
"@internxt/inxt-js": "2.2.9",
4141
"@internxt/lib": "1.3.1",
42-
"@internxt/sdk": "1.11.11",
42+
"@internxt/sdk": "1.11.12",
4343
"@oclif/core": "4.5.4",
4444
"@oclif/plugin-autocomplete": "3.2.35",
4545
"axios": "1.12.2",

src/commands/download-file.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,20 @@ import { CryptoService } from '../services/crypto.service';
77
import { DownloadService } from '../services/network/download.service';
88
import { SdkManager } from '../services/sdk-manager.service';
99
import { createWriteStream } from 'node:fs';
10-
import { UserSettings } from '@internxt/sdk/dist/shared/types/userSettings';
1110
import { DriveFileItem } from '../types/drive.types';
1211
import fs from 'node:fs/promises';
1312
import path from 'node:path';
1413
import { StreamUtils } from '../utils/stream.utils';
15-
import { NotValidDirectoryError, NotValidFileUuidError } from '../types/command.types';
14+
import { LoginUserDetails, NotValidDirectoryError, NotValidFileUuidError } from '../types/command.types';
1615
import { ValidationService } from '../services/validation.service';
1716
import { Environment } from '@internxt/inxt-js';
1817
import { ConfigService } from '../services/config.service';
1918

2019
export default class DownloadFile extends Command {
2120
static readonly args = {};
2221
static readonly description =
23-
// eslint-disable-next-line max-len
24-
'Download and decrypts a file from Internxt Drive to a directory. The file name will be the same as the file name in your Drive.';
22+
'Download and decrypts a file from Internxt Drive to a directory.' +
23+
' The file name will be the same as the file name in your Drive.';
2524
static readonly aliases = ['download:file'];
2625
static readonly examples = ['<%= config.bin %> <%= command.id %>'];
2726
static readonly flags = {
@@ -206,7 +205,7 @@ export default class DownloadFile extends Command {
206205
return downloadPath;
207206
};
208207

209-
private prepareNetwork = async (user: UserSettings, jsonFlag?: boolean) => {
208+
private prepareNetwork = async (user: LoginUserDetails, jsonFlag?: boolean) => {
210209
CLIUtils.doing('Preparing Network', jsonFlag);
211210

212211
const networkModule = SdkManager.instance.getNetwork({

src/services/auth.service.ts

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { LoginDetails } from '@internxt/sdk';
22
import { SdkManager } from './sdk-manager.service';
3-
import { KeysService } from './keys.service';
43
import { CryptoService } from './crypto.service';
54
import { ConfigService } from './config.service';
65
import {
@@ -30,27 +29,13 @@ export class AuthService {
3029
tfaCode: twoFactorCode,
3130
};
3231

33-
const data = await authClient.login(loginDetails, CryptoService.cryptoProvider);
32+
const data = await authClient.loginAccess(loginDetails, CryptoService.cryptoProvider);
3433
const { user, newToken } = data;
35-
const { privateKey, publicKey } = user;
36-
37-
const plainPrivateKeyInBase64 = privateKey
38-
? Buffer.from(KeysService.instance.decryptPrivateKey(privateKey, password)).toString('base64')
39-
: '';
40-
41-
if (privateKey) {
42-
await KeysService.instance.assertPrivateKeyIsValid(privateKey, password);
43-
await KeysService.instance.assertValidateKeys(
44-
Buffer.from(plainPrivateKeyInBase64, 'base64').toString(),
45-
Buffer.from(publicKey, 'base64').toString(),
46-
);
47-
}
4834

4935
const clearMnemonic = CryptoService.instance.decryptTextWithKey(user.mnemonic, password);
50-
const clearUser = {
36+
const clearUser: LoginCredentials['user'] = {
5137
...user,
5238
mnemonic: clearMnemonic,
53-
privateKey: plainPrivateKeyInBase64,
5439
};
5540
return {
5641
user: clearUser,
@@ -118,7 +103,7 @@ export class AuthService {
118103
user: {
119104
...newCreds.user,
120105
mnemonic: oldCreds.user.mnemonic,
121-
privateKey: oldCreds.user.privateKey,
106+
createdAt: new Date(newCreds.user.createdAt).toISOString(),
122107
},
123108
token: newCreds.newToken,
124109
lastLoggedInAt: oldCreds.lastLoggedInAt,

src/services/config.service.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,7 @@ export class ConfigService {
6363
try {
6464
const encryptedCredentials = await fs.readFile(ConfigService.CREDENTIALS_FILE, 'utf8');
6565
const credentialsString = CryptoService.instance.decryptText(encryptedCredentials);
66-
const loginCredentials = JSON.parse(credentialsString, (key, value) => {
67-
if (typeof value === 'string' && key === 'createdAt') {
68-
return new Date(value);
69-
}
70-
return value;
71-
}) as LoginCredentials;
66+
const loginCredentials = JSON.parse(credentialsString) as LoginCredentials;
7267
return loginCredentials;
7368
} catch {
7469
return;

src/services/keys.service.ts

Lines changed: 0 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,10 @@
11
import { aes } from '@internxt/lib';
22
import * as openpgp from 'openpgp';
3-
import {
4-
BadEncodedPrivateKeyError,
5-
CorruptedEncryptedPrivateKeyError,
6-
KeysDoNotMatchError,
7-
WrongIterationsToEncryptPrivateKeyError,
8-
} from '../types/keys.types';
93
import { CryptoUtils } from '../utils/crypto.utils';
104

115
export class KeysService {
126
public static readonly instance: KeysService = new KeysService();
137

14-
/**
15-
* Checks if a private key can be decrypted with a password, otherwise it throws an error
16-
* @param privateKey The encrypted private key
17-
* @param password The password used to encrypt the private key
18-
* @throws {BadEncodedPrivateKeyError} If the PLAIN private key is base64 encoded (known issue introduced in the past)
19-
* @throws {WrongIterationsToEncryptPrivateKeyError} If the ENCRYPTED private key was encrypted using the wrong iterations number (known issue introduced in the past)
20-
* @throws {CorruptedEncryptedPrivateKeyError} If the ENCRYPTED private key is un-decryptable (corrupted)
21-
* @async
22-
*/
23-
public assertPrivateKeyIsValid = async (privateKey: string, password: string): Promise<void> => {
24-
let privateKeyDecrypted: string | undefined;
25-
26-
let badIterations = true;
27-
try {
28-
aes.decrypt(privateKey, password, 9999);
29-
} catch {
30-
badIterations = false;
31-
}
32-
if (badIterations === true) throw new WrongIterationsToEncryptPrivateKeyError();
33-
34-
let badEncrypted = false;
35-
try {
36-
privateKeyDecrypted = this.decryptPrivateKey(privateKey, password);
37-
} catch {
38-
badEncrypted = true;
39-
}
40-
41-
let hasValidFormat = false;
42-
try {
43-
if (privateKeyDecrypted !== undefined) {
44-
hasValidFormat = await this.isValidKey(privateKeyDecrypted);
45-
}
46-
} catch {
47-
/* no op */
48-
}
49-
50-
if (badEncrypted === true) throw new CorruptedEncryptedPrivateKeyError();
51-
if (hasValidFormat === false) throw new BadEncodedPrivateKeyError();
52-
};
53-
548
/**
559
* Encrypts a private key using a password
5610
* @param privateKey The plain private key
@@ -71,52 +25,6 @@ export class KeysService {
7125
return aes.decrypt(privateKey, password);
7226
};
7327

74-
/**
75-
* Checks if a message encrypted with the public key can be decrypted with a private key, otherwise it throws an error
76-
* @param privateKey The plain private key
77-
* @param publicKey The plain public key
78-
* @throws {KeysDoNotMatchError} If the keys can not be used together to encrypt/decrypt a message
79-
* @async
80-
**/
81-
public assertValidateKeys = async (privateKey: string, publicKey: string): Promise<void> => {
82-
const publicKeyArmored = await openpgp.readKey({ armoredKey: publicKey });
83-
const privateKeyArmored = await openpgp.readPrivateKey({ armoredKey: privateKey });
84-
85-
const plainMessage = 'validate-keys';
86-
const originalText = await openpgp.createMessage({ text: plainMessage });
87-
const encryptedMessage = await openpgp.encrypt({
88-
message: originalText,
89-
encryptionKeys: publicKeyArmored,
90-
});
91-
92-
const decryptedMessage = (
93-
await openpgp.decrypt({
94-
message: await openpgp.readMessage({ armoredMessage: encryptedMessage }),
95-
verificationKeys: publicKeyArmored,
96-
decryptionKeys: privateKeyArmored,
97-
})
98-
).data;
99-
100-
if (decryptedMessage !== plainMessage) {
101-
throw new KeysDoNotMatchError();
102-
}
103-
};
104-
105-
/**
106-
* Checks if a pgp key can be read
107-
* @param key The openpgp key to be validated
108-
* @returns True if it can be read, false otherwise
109-
* @async
110-
**/
111-
public isValidKey = async (key: string): Promise<boolean> => {
112-
try {
113-
await openpgp.readKey({ armoredKey: key });
114-
return true;
115-
} catch {
116-
return false;
117-
}
118-
};
119-
12028
/**
12129
* Generates pgp keys adding an AES-encrypted private key property by using a password
12230
* @param password The password for encrypting the private key

src/types/command.types.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,31 @@
1-
import { UserSettings } from '@internxt/sdk/dist/shared/types/userSettings';
1+
export interface LoginUserDetails {
2+
userId: string;
3+
uuid: string;
4+
email: string;
5+
name: string;
6+
lastname: string;
7+
username: string;
8+
bridgeUser: string;
9+
bucket: string;
10+
rootFolderId: string;
11+
mnemonic: string;
12+
keys: {
13+
ecc: {
14+
publicKey: string;
15+
privateKey: string;
16+
};
17+
kyber: {
18+
publicKey: string;
19+
privateKey: string;
20+
};
21+
};
22+
createdAt: string;
23+
avatar: string | null;
24+
emailVerified: boolean;
25+
}
226

327
export interface LoginCredentials {
4-
user: UserSettings;
28+
user: LoginUserDetails;
529
token: string;
630
lastLoggedInAt: string;
731
lastTokenRefreshAt: string;

src/types/keys.types.ts

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,3 @@
1-
export class BadEncodedPrivateKeyError extends Error {
2-
constructor() {
3-
super('Private key is bad encoded');
4-
5-
Object.setPrototypeOf(this, BadEncodedPrivateKeyError.prototype);
6-
}
7-
}
8-
9-
export class WrongIterationsToEncryptPrivateKeyError extends Error {
10-
constructor() {
11-
super('Private key was encrypted using the wrong iterations number');
12-
13-
Object.setPrototypeOf(this, WrongIterationsToEncryptPrivateKeyError.prototype);
14-
}
15-
}
16-
17-
export class CorruptedEncryptedPrivateKeyError extends Error {
18-
constructor() {
19-
super('Private key is corrupted');
20-
21-
Object.setPrototypeOf(this, CorruptedEncryptedPrivateKeyError.prototype);
22-
}
23-
}
24-
25-
export class KeysDoNotMatchError extends Error {
26-
constructor() {
27-
super('Keys do not match');
28-
29-
Object.setPrototypeOf(this, KeysDoNotMatchError.prototype);
30-
}
31-
}
32-
331
export interface AesInit {
342
iv: string;
353
salt: string;

test/fixtures/auth.fixture.ts

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { UserSettings } from '@internxt/sdk/dist/shared/types/userSettings';
21
import crypto from 'node:crypto';
2+
import { LoginUserDetails } from '../../src/types/command.types';
33

4-
export const UserFixture: UserSettings = {
4+
export const UserFixture: LoginUserDetails = {
55
userId: crypto.randomBytes(16).toString('hex'),
66
uuid: crypto.randomBytes(16).toString('hex'),
77
email: crypto.randomBytes(16).toString('hex'),
@@ -10,21 +10,9 @@ export const UserFixture: UserSettings = {
1010
username: crypto.randomBytes(16).toString('hex'),
1111
bridgeUser: crypto.randomBytes(16).toString('hex'),
1212
bucket: crypto.randomBytes(16).toString('hex'),
13-
backupsBucket: crypto.randomBytes(16).toString('hex'),
14-
root_folder_id: crypto.randomInt(1, 9999),
1513
rootFolderId: crypto.randomBytes(16).toString('hex'),
16-
rootFolderUuid: crypto.randomBytes(16).toString('hex'),
17-
sharedWorkspace: false,
18-
credit: crypto.randomInt(1, 9999),
1914
mnemonic: crypto.randomBytes(16).toString('hex'),
20-
privateKey: crypto.randomBytes(16).toString('hex'),
21-
publicKey: crypto.randomBytes(16).toString('hex'),
22-
revocationKey: crypto.randomBytes(16).toString('hex'),
23-
teams: false,
24-
appSumoDetails: null,
25-
registerCompleted: true,
26-
hasReferralsProgram: false,
27-
createdAt: new Date(),
15+
createdAt: new Date().toISOString(),
2816
avatar: crypto.randomBytes(16).toString('hex'),
2917
emailVerified: true,
3018
keys: {
@@ -39,28 +27,16 @@ export const UserFixture: UserSettings = {
3927
},
4028
};
4129

42-
export const UserSettingsFixture: UserSettings = {
30+
export const UserSettingsFixture: LoginUserDetails = {
4331
userId: UserFixture.userId,
4432
email: UserFixture.email,
4533
name: UserFixture.name,
4634
lastname: UserFixture.lastname,
4735
username: UserFixture.username,
4836
bridgeUser: UserFixture.bridgeUser,
4937
bucket: UserFixture.bucket,
50-
backupsBucket: UserFixture.backupsBucket,
51-
root_folder_id: UserFixture.root_folder_id,
5238
rootFolderId: UserFixture.rootFolderId,
53-
rootFolderUuid: UserFixture.rootFolderUuid,
54-
sharedWorkspace: UserFixture.sharedWorkspace,
55-
credit: UserFixture.credit,
5639
mnemonic: UserFixture.mnemonic,
57-
privateKey: UserFixture.privateKey,
58-
publicKey: UserFixture.publicKey,
59-
revocationKey: UserFixture.revocationKey,
60-
teams: UserFixture.teams,
61-
appSumoDetails: UserFixture.appSumoDetails,
62-
registerCompleted: UserFixture.registerCompleted,
63-
hasReferralsProgram: UserFixture.hasReferralsProgram,
6440
createdAt: UserFixture.createdAt,
6541
avatar: UserFixture.avatar,
6642
emailVerified: UserFixture.emailVerified,

test/services/auth.service.test.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
22
import crypto from 'node:crypto';
33
import { Auth, LoginDetails, SecurityDetails } from '@internxt/sdk';
44
import { AuthService } from '../../src/services/auth.service';
5-
import { KeysService } from '../../src/services/keys.service';
65
import { CryptoService } from '../../src/services/crypto.service';
76
import { SdkManager } from '../../src/services/sdk-manager.service';
87
import { ConfigService } from '../../src/services/config.service';
@@ -16,6 +15,7 @@ import {
1615
} from '../../src/types/command.types';
1716
import { UserCredentialsFixture } from '../fixtures/login.fixture';
1817
import { fail } from 'node:assert';
18+
import { paths } from '@internxt/sdk/dist/schema';
1919

2020
describe('Auth service', () => {
2121
beforeEach(() => {
@@ -28,14 +28,11 @@ describe('Auth service', () => {
2828
newToken: crypto.randomBytes(16).toString('hex'),
2929
user: UserFixture,
3030
userTeam: null,
31-
};
31+
} as unknown as paths['/auth/cli/login/access']['post']['responses']['200']['content']['application/json'];
3232
const mockDate = new Date().toISOString();
3333

34-
vi.spyOn(Auth.prototype, 'login').mockResolvedValue(loginResponse);
34+
vi.spyOn(Auth.prototype, 'loginAccess').mockResolvedValue(loginResponse);
3535
vi.spyOn(SdkManager.instance, 'getAuth').mockReturnValue(Auth.prototype);
36-
vi.spyOn(KeysService.instance, 'decryptPrivateKey').mockReturnValue(loginResponse.user.privateKey);
37-
vi.spyOn(KeysService.instance, 'assertPrivateKeyIsValid').mockResolvedValue();
38-
vi.spyOn(KeysService.instance, 'assertValidateKeys').mockResolvedValue();
3936
vi.spyOn(CryptoService.instance, 'decryptTextWithKey').mockReturnValue(loginResponse.user.mnemonic);
4037
vi.spyOn(Date.prototype, 'toISOString').mockReturnValue(mockDate);
4138

@@ -46,7 +43,7 @@ describe('Auth service', () => {
4643
);
4744

4845
const expectedResponseLogin: LoginCredentials = {
49-
user: { ...loginResponse.user, privateKey: Buffer.from(loginResponse.user.privateKey).toString('base64') },
46+
user: { ...loginResponse.user },
5047
token: loginResponse.newToken,
5148
lastLoggedInAt: mockDate,
5249
lastTokenRefreshAt: mockDate,
@@ -61,7 +58,7 @@ describe('Auth service', () => {
6158
tfaCode: crypto.randomInt(1, 999999).toString().padStart(6, '0'),
6259
};
6360

64-
const loginStub = vi.spyOn(Auth.prototype, 'login').mockRejectedValue(new Error('Login failed'));
61+
const loginStub = vi.spyOn(Auth.prototype, 'loginAccess').mockRejectedValue(new Error('Login failed'));
6562
vi.spyOn(SdkManager.instance, 'getAuth').mockReturnValue(Auth.prototype);
6663

6764
try {

0 commit comments

Comments
 (0)