diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c22a4a..01f6ae4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 1.0.2 (2024-05-) + +- feat: add `sid` method + ## 1.0.1 (2024-04-03) - `machineId`: Fix bug on Windows diff --git a/README.md b/README.md index 159b934..f831367 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,20 @@ This method returns the same value for every user on the system. console.log(await machineId()); // a642d9e1-6063-4da7-8ea8-2298f989d01d ``` +### `sid (Promise)` + +Gets the Secutify Identifier (SID) value for the current user on the device. Throws an error if the value is not obtained. + +The SID value is only supported on Windows and macOS. Other OSes throw an error. + +Also, the SID value used on macOS is a value created for the directory service. If you don't trust this value, use the `machineId` method instead. + +This value can be changed by the user. + +```javascript +console.log(await sid()); // S-1-5-21-406418252-5582013529-1321253100-2001 +``` + ## Contribute You can report issues on [GitHub Issue Tracker](https://github.com/jooy2/machini/issues). You can also request a pull to fix bugs and add frequently used features. diff --git a/lib/index.ts b/lib/index.ts index 01efde3..56948e4 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,6 +1,8 @@ -import { platform } from 'os'; +import { platform, homedir, userInfo } from 'os'; import { exec as processExec } from 'child_process'; +declare type AnyValueObject = { [key: string]: any }; + const execCommand = (platformName: string, command: string): Promise => new Promise((resolve, reject) => { const execCommandProcess = processExec( @@ -53,8 +55,76 @@ export default class Machini { throw new Error('Failed to get machine id'); } + + static async sid(): Promise { + const platformName = platform(); + + if (platformName === 'win32') { + try { + const profileLists = await execCommand( + platformName, + `REG QUERY "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList" /s` + ); + + const profiles: AnyValueObject = {}; + const sections = profileLists.split('HKEY_LOCAL_MACHINE\\'); + + sections.forEach((section) => { + const lines = section + .split('\n') + .map((line) => line.trim()) + .filter((line) => line); + + if (lines.length > 0) { + const keyName: string = lines[0].split('\\').pop() || 'Unknown'; + const profileImagePath = lines.find((line) => line.startsWith('ProfileImagePath')); + + if (profileImagePath) { + profiles[keyName] = profileImagePath.split(' ').pop() || 'Unknown'; + } + } + }); + + for (let i = 0; i < Object.keys(profiles).length; i += 1) { + const key = Object.keys(profiles)[i]; + const value = profiles[key]; + + if (value === homedir()) { + return key; + } + } + } catch (error) { + if (error instanceof Error) { + throw new Error(error?.message); + } + } + + throw new Error('Failed to get machine id'); + } + + if (platformName === 'darwin') { + try { + const execResult = await execCommand( + platformName, + `dsmemberutil getsid -U ${userInfo().username}` + ); + + if (execResult) { + return execResult.replace(/\r?\n/g, ''); + } + } catch (error) { + if (error instanceof Error) { + throw new Error(error?.message); + } + } + + throw new Error('Failed to get machine id'); + } + + throw new Error('Not supported on this operating system.'); + } } export { Machini }; -export const { machineId } = Machini; +export const { machineId, sid } = Machini; diff --git a/test/index.spec.ts b/test/index.spec.ts index 203ce13..2e0eaf8 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -1,13 +1,13 @@ import assert from 'assert'; -import { machineId } from '../dist'; - -/* - * Sample Response: - * Windows: a642d9e1-6063-4da7-8ea8-2298f989d01d - * Linux: 5c6ee51d3e514eb4883e4373e320192c - * macOS: BAC04154-124A-56E1-BFEB-D6D94FE5DBC0 - */ +import { machineId, sid } from '../dist'; + describe('Module Test', () => { + /* + * Sample Response: + * Windows: a642d9e1-6063-4da7-8ea8-2298f989d01d + * Linux: 5c6ee51d3e514eb4883e4373e320192c + * macOS: BAC04154-124A-56E1-BFEB-D6D94FE5DBC0 + */ it('machineId', async () => { const mId = await machineId(); @@ -26,4 +26,17 @@ describe('Module Test', () => { assert.match(mId, regex); }); + + // Example: S-1-5-21-406418252-5582013529-1321253100-2001 + it('sid', async () => { + const { platform } = process; + + if (platform !== 'win32' && platform !== 'darwin') { + return; + } + + const sidResult = await sid(); + + assert.match(sidResult, /^S-1-[0-59]-\d{2}-\d{8,10}-\d{8,10}-\d{8,10}-[1-9]\d{3}/); + }); });