snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
This commit is contained in:
3973
ee/apps/account-service/CHANGELOG.md
Normal file
3973
ee/apps/account-service/CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
57
ee/apps/account-service/package.json
Normal file
57
ee/apps/account-service/package.json
Normal file
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"name": "@rocket.chat/account-service",
|
||||
"version": "0.5.1",
|
||||
"private": true,
|
||||
"description": "Rocket.Chat Account service",
|
||||
"keywords": [
|
||||
"rocketchat"
|
||||
],
|
||||
"author": "Rocket.Chat",
|
||||
"main": "./dist/ee/apps/account-service/src/service.js",
|
||||
"files": [
|
||||
"/dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsc -p tsconfig.json",
|
||||
"lint": "eslint .",
|
||||
"ms": "TRANSPORTER=${TRANSPORTER:-TCP} MONGO_URL=${MONGO_URL:-mongodb://localhost:3001/meteor} ts-node --files src/service.ts",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"typecheck": "tsc --noEmit --skipLibCheck -p tsconfig.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@rocket.chat/core-services": "workspace:^",
|
||||
"@rocket.chat/core-typings": "workspace:^",
|
||||
"@rocket.chat/emitter": "workspace:~",
|
||||
"@rocket.chat/logger": "workspace:^",
|
||||
"@rocket.chat/model-typings": "workspace:^",
|
||||
"@rocket.chat/models": "workspace:^",
|
||||
"@rocket.chat/network-broker": "workspace:^",
|
||||
"@rocket.chat/rest-typings": "workspace:^",
|
||||
"@rocket.chat/tools": "workspace:^",
|
||||
"@rocket.chat/tracing": "workspace:^",
|
||||
"@types/node": "~24.13.2",
|
||||
"bcrypt": "^6.0.0",
|
||||
"ejson": "^2.2.3",
|
||||
"eventemitter3": "^5.0.4",
|
||||
"mem": "^8.1.1",
|
||||
"moleculer": "^0.14.35",
|
||||
"mongodb": "6.16.0",
|
||||
"nats": "^2.28.2",
|
||||
"pino": "10.3.1",
|
||||
"polka": "^0.5.2",
|
||||
"prom-client": "^14.2.0",
|
||||
"prometheus-gc-stats": "^1.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rocket.chat/tsconfig": "workspace:*",
|
||||
"@types/bcrypt": "^6.0.0",
|
||||
"@types/polka": "^0.5.8",
|
||||
"@types/prometheus-gc-stats": "^0.6.4",
|
||||
"eslint": "~9.39.5",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "~5.9.3"
|
||||
},
|
||||
"volta": {
|
||||
"extends": "../../../package.json"
|
||||
}
|
||||
}
|
||||
40
ee/apps/account-service/src/Account.ts
Normal file
40
ee/apps/account-service/src/Account.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { ServiceClass, Settings } from '@rocket.chat/core-services';
|
||||
import type { IAccount, ILoginResult } from '@rocket.chat/core-services';
|
||||
import { getLoginExpirationInDays } from '@rocket.chat/tools';
|
||||
|
||||
import { loginViaResume } from './lib/loginViaResume';
|
||||
import { removeSession } from './lib/removeSession';
|
||||
|
||||
export class Account extends ServiceClass implements IAccount {
|
||||
protected name = 'accounts';
|
||||
|
||||
private loginExpiration = 90;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onSettingChanged('Accounts_LoginExpiration', async ({ setting }): Promise<void> => {
|
||||
const { value } = setting;
|
||||
|
||||
this.loginExpiration = getLoginExpirationInDays(value as number);
|
||||
});
|
||||
}
|
||||
|
||||
async login({ resume }: { resume: string }): Promise<false | ILoginResult> {
|
||||
if (resume) {
|
||||
return loginViaResume(resume, this.loginExpiration);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async logout({ userId, token }: { userId: string; token: string }): Promise<void> {
|
||||
return removeSession(userId, token);
|
||||
}
|
||||
|
||||
override async started(): Promise<void> {
|
||||
const expiry = await Settings.get<number>('Accounts_LoginExpiration');
|
||||
|
||||
this.loginExpiration = getLoginExpirationInDays(expiry);
|
||||
}
|
||||
}
|
||||
9
ee/apps/account-service/src/fips.ts
Normal file
9
ee/apps/account-service/src/fips.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import crypto from 'crypto';
|
||||
|
||||
crypto.setFips(true);
|
||||
|
||||
if (!crypto.getFips()) {
|
||||
throw new Error('FIPS mode was not enabled after crypto.setFips(true)');
|
||||
}
|
||||
|
||||
console.log('FIPS COMPLIANCE CHECK: YES');
|
||||
40
ee/apps/account-service/src/lib/loginViaResume.ts
Normal file
40
ee/apps/account-service/src/lib/loginViaResume.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import type { ILoginResult } from '@rocket.chat/core-services';
|
||||
import { MeteorError } from '@rocket.chat/core-services';
|
||||
import type { IUser } from '@rocket.chat/core-typings';
|
||||
import { Users } from '@rocket.chat/models';
|
||||
|
||||
import { _hashLoginToken, _tokenExpiration } from './utils';
|
||||
|
||||
export async function loginViaResume(resume: string, loginExpiration: number): Promise<false | ILoginResult> {
|
||||
const hashedToken = _hashLoginToken(resume);
|
||||
|
||||
const user = await Users.findOne<IUser>(
|
||||
{
|
||||
'services.resume.loginTokens.hashedToken': hashedToken,
|
||||
'active': true,
|
||||
},
|
||||
{
|
||||
projection: {
|
||||
'services.resume.loginTokens': 1,
|
||||
},
|
||||
},
|
||||
);
|
||||
if (!user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const token = user.services?.resume?.loginTokens?.find((token) => token.hashedToken === hashedToken);
|
||||
|
||||
const tokenExpires = token && 'when' in token && _tokenExpiration(token.when, loginExpiration);
|
||||
if (tokenExpires && new Date() >= tokenExpires) {
|
||||
throw new MeteorError(403, 'Your session has expired. Please log in again.');
|
||||
}
|
||||
|
||||
return {
|
||||
uid: user._id,
|
||||
token: resume,
|
||||
hashedToken,
|
||||
type: 'resume',
|
||||
...(tokenExpires && { tokenExpires }),
|
||||
};
|
||||
}
|
||||
14
ee/apps/account-service/src/lib/removeSession.ts
Normal file
14
ee/apps/account-service/src/lib/removeSession.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Users } from '@rocket.chat/models';
|
||||
|
||||
export async function removeSession(uid: string, loginToken: string): Promise<void> {
|
||||
await Users.updateOne(
|
||||
{ _id: uid },
|
||||
{
|
||||
$pull: {
|
||||
'services.resume.loginTokens': {
|
||||
$or: [{ hashedToken: loginToken }, { token: loginToken }],
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
14
ee/apps/account-service/src/lib/saveSession.ts
Normal file
14
ee/apps/account-service/src/lib/saveSession.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Users } from '@rocket.chat/models';
|
||||
|
||||
import type { IHashedStampedToken } from './utils';
|
||||
|
||||
export async function saveSession(uid: string, newToken: IHashedStampedToken): Promise<void> {
|
||||
await Users.updateOne(
|
||||
{ _id: uid },
|
||||
{
|
||||
$push: {
|
||||
'services.resume.loginTokens': newToken.hashedToken,
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
63
ee/apps/account-service/src/lib/utils.ts
Normal file
63
ee/apps/account-service/src/lib/utils.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import crypto from 'crypto';
|
||||
|
||||
import { convertFromDaysToMilliseconds } from '@rocket.chat/tools';
|
||||
import bcrypt from 'bcrypt';
|
||||
|
||||
export interface IStampedToken {
|
||||
token: string;
|
||||
when: Date;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface IHashedStampedToken {
|
||||
when: Date;
|
||||
hashedToken: string;
|
||||
}
|
||||
|
||||
type Password =
|
||||
| string
|
||||
| {
|
||||
digest: string;
|
||||
};
|
||||
|
||||
export const getPassword = (password: Password): string => {
|
||||
if (typeof password === 'string') {
|
||||
return crypto.createHash('sha256').update(password).digest('hex'); // lgtm [js/insufficient-password-hash]
|
||||
}
|
||||
if (typeof password.digest === 'undefined') {
|
||||
throw new Error('invalid password');
|
||||
}
|
||||
return password.digest;
|
||||
};
|
||||
|
||||
// https://github.com/meteor/meteor/blob/c5b51b0fc2a8cef498b9390ebcb4925e02de83e8/packages/accounts-base/accounts_server.js#L934
|
||||
export const _generateStampedLoginToken = (): IStampedToken => ({
|
||||
token: crypto.randomUUID(),
|
||||
when: new Date(),
|
||||
});
|
||||
|
||||
// https://github.com/meteor/meteor/blob/c5b51b0fc2a8cef498b9390ebcb4925e02de83e8/packages/accounts-base/accounts_server.js#L780
|
||||
export const _hashLoginToken = (loginToken: string): string => {
|
||||
const hash = crypto.createHash('sha256');
|
||||
hash.update(loginToken);
|
||||
return hash.digest('base64');
|
||||
};
|
||||
|
||||
// https://github.com/meteor/meteor/blob/c5b51b0fc2a8cef498b9390ebcb4925e02de83e8/packages/accounts-base/accounts_server.js#L787
|
||||
export const _hashStampedToken = (stampedToken: IStampedToken): IHashedStampedToken => {
|
||||
const hashedStampedToken = Object.keys(stampedToken).reduce(
|
||||
(prev, key) => (key === 'token' ? prev : { ...prev, [key]: stampedToken[key] }),
|
||||
{},
|
||||
);
|
||||
|
||||
return {
|
||||
...hashedStampedToken,
|
||||
hashedToken: _hashLoginToken(stampedToken.token),
|
||||
} as IHashedStampedToken;
|
||||
};
|
||||
|
||||
export const validatePassword = (password: string, bcryptPassword: string): Promise<boolean> =>
|
||||
bcrypt.compare(getPassword(password), bcryptPassword);
|
||||
|
||||
export const _tokenExpiration = (when: string | Date, expirationInDays: number): Date =>
|
||||
new Date(new Date(when).getTime() + convertFromDaysToMilliseconds(expirationInDays));
|
||||
38
ee/apps/account-service/src/service.ts
Executable file
38
ee/apps/account-service/src/service.ts
Executable file
@@ -0,0 +1,38 @@
|
||||
import { api, getConnection, getTrashCollection } from '@rocket.chat/core-services';
|
||||
import { registerServiceModels } from '@rocket.chat/models';
|
||||
import { startBroker } from '@rocket.chat/network-broker';
|
||||
import { startTracing } from '@rocket.chat/tracing';
|
||||
import polka from 'polka';
|
||||
|
||||
const PORT = process.env.PORT || 3033;
|
||||
|
||||
void (async () => {
|
||||
const { db, client } = await getConnection();
|
||||
|
||||
startTracing({ service: 'account-service', db: client });
|
||||
|
||||
registerServiceModels(db, await getTrashCollection());
|
||||
|
||||
api.setBroker(startBroker());
|
||||
|
||||
// need to import service after models are registered
|
||||
const { Account } = await import('./Account');
|
||||
|
||||
api.registerService(new Account());
|
||||
|
||||
await api.start();
|
||||
|
||||
polka()
|
||||
.get('/health', async function (_req, res) {
|
||||
try {
|
||||
await api.nodeList();
|
||||
res.end('ok');
|
||||
} catch (err) {
|
||||
console.error('Service not healthy', err);
|
||||
|
||||
res.writeHead(500);
|
||||
res.end('not healthy');
|
||||
}
|
||||
})
|
||||
.listen(PORT);
|
||||
})();
|
||||
19
ee/apps/account-service/tsconfig.json
Normal file
19
ee/apps/account-service/tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"extends": "@rocket.chat/tsconfig/server.json",
|
||||
"compilerOptions": {
|
||||
"incremental": true,
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"strictPropertyInitialization": false,
|
||||
"strictFunctionTypes": false,
|
||||
|
||||
/* Additional Checks */
|
||||
"noFallthroughCasesInSwitch": false,
|
||||
|
||||
"outDir": "./dist/ee/apps/account-service/src",
|
||||
},
|
||||
"include":
|
||||
["./src/**/*.ts",
|
||||
"../../../apps/meteor/definition/externals/meteor"],
|
||||
"exclude": ["./dist"]
|
||||
}
|
||||
Reference in New Issue
Block a user