mirror of
https://github.com/Unleash/unleash.git
synced 2025-03-09 00:18:26 +01:00
feat: max parallel sessions config (#9109)
This commit is contained in:
parent
bbbc85245c
commit
e9db8ab8f0
@ -149,6 +149,7 @@ exports[`should create default config 1`] = `
|
||||
"clearSiteDataOnLogout": true,
|
||||
"cookieName": "unleash-session",
|
||||
"db": true,
|
||||
"maxParallelSessions": 5,
|
||||
"ttlHours": 48,
|
||||
},
|
||||
"strategySegmentsLimit": 5,
|
||||
|
@ -266,7 +266,7 @@ const defaultDbOptions: WithOptional<IDBOption, 'user' | 'password' | 'host'> =
|
||||
applicationName: process.env.DATABASE_APPLICATION_NAME || 'unleash',
|
||||
};
|
||||
|
||||
const defaultSessionOption: ISessionOption = {
|
||||
const defaultSessionOption = (isEnterprise: boolean): ISessionOption => ({
|
||||
ttlHours: parseEnvVarNumber(process.env.SESSION_TTL_HOURS, 48),
|
||||
clearSiteDataOnLogout: parseEnvVarBoolean(
|
||||
process.env.SESSION_CLEAR_SITE_DATA_ON_LOGOUT,
|
||||
@ -274,7 +274,16 @@ const defaultSessionOption: ISessionOption = {
|
||||
),
|
||||
cookieName: 'unleash-session',
|
||||
db: true,
|
||||
};
|
||||
// default limit of 100 for enterprise, 5 for pro and oss
|
||||
// at least 1 session should be allowed
|
||||
maxParallelSessions: Math.max(
|
||||
parseEnvVarNumber(
|
||||
process.env.MAX_PARALLEL_SESSIONS,
|
||||
isEnterprise ? 100 : 5,
|
||||
),
|
||||
1,
|
||||
),
|
||||
});
|
||||
|
||||
const defaultServerOption: IServerOption = {
|
||||
pipe: undefined,
|
||||
@ -533,11 +542,6 @@ export function createConfig(options: IUnleashOptions): IUnleashConfig {
|
||||
options.db || {},
|
||||
]);
|
||||
|
||||
const session: ISessionOption = mergeAll([
|
||||
defaultSessionOption,
|
||||
options.session || {},
|
||||
]);
|
||||
|
||||
const logLevel =
|
||||
options.logLevel || LogLevel[process.env.LOG_LEVEL ?? LogLevel.error];
|
||||
const getLogger = options.getLogger || getDefaultLogProvider(logLevel);
|
||||
@ -638,6 +642,12 @@ export function createConfig(options: IUnleashOptions): IUnleashConfig {
|
||||
ui.environment,
|
||||
isTest,
|
||||
);
|
||||
|
||||
const session: ISessionOption = mergeAll([
|
||||
defaultSessionOption(isEnterprise),
|
||||
options.session || {},
|
||||
]);
|
||||
|
||||
const metricsRateLimiting = loadMetricsRateLimitingConfig(options);
|
||||
|
||||
const rateLimiting = loadRateLimitingConfig(options);
|
||||
|
@ -98,6 +98,8 @@ class UserService {
|
||||
|
||||
readonly unleashUrl: string;
|
||||
|
||||
readonly maxParallelSessions: number;
|
||||
|
||||
constructor(
|
||||
stores: Pick<IUnleashStores, 'userStore'>,
|
||||
{
|
||||
@ -106,6 +108,7 @@ class UserService {
|
||||
authentication,
|
||||
eventBus,
|
||||
flagResolver,
|
||||
session,
|
||||
}: Pick<
|
||||
IUnleashConfig,
|
||||
| 'getLogger'
|
||||
@ -113,6 +116,7 @@ class UserService {
|
||||
| 'server'
|
||||
| 'eventBus'
|
||||
| 'flagResolver'
|
||||
| 'session'
|
||||
>,
|
||||
services: {
|
||||
accessService: AccessService;
|
||||
@ -133,6 +137,7 @@ class UserService {
|
||||
this.sessionService = services.sessionService;
|
||||
this.settingService = services.settingService;
|
||||
this.flagResolver = flagResolver;
|
||||
this.maxParallelSessions = session.maxParallelSessions;
|
||||
|
||||
process.nextTick(() => this.initAdminUser(authentication));
|
||||
|
||||
@ -431,22 +436,14 @@ class UserService {
|
||||
);
|
||||
}
|
||||
|
||||
const deleteStaleUserSessions = this.flagResolver.getVariant(
|
||||
'deleteStaleUserSessions',
|
||||
);
|
||||
if (deleteStaleUserSessions.feature_enabled) {
|
||||
const allowedSessions = Number(
|
||||
deleteStaleUserSessions.payload?.value || 5,
|
||||
// subtract current user session that will be created
|
||||
const deletedSessionsCount =
|
||||
await this.sessionService.deleteStaleSessionsForUser(
|
||||
user.id,
|
||||
Math.max(this.maxParallelSessions - 1, 0),
|
||||
);
|
||||
// subtract current user session that will be created
|
||||
const deletedSessionsCount =
|
||||
await this.sessionService.deleteStaleSessionsForUser(
|
||||
user.id,
|
||||
Math.max(allowedSessions - 1, 0),
|
||||
);
|
||||
user.deletedSessions = deletedSessionsCount;
|
||||
user.activeSessions = allowedSessions;
|
||||
}
|
||||
user.deletedSessions = deletedSessionsCount;
|
||||
user.activeSessions = this.maxParallelSessions;
|
||||
|
||||
this.eventBus.emit(USER_LOGIN, { loginOrder });
|
||||
return user;
|
||||
|
@ -54,7 +54,6 @@ export type IFlagKey =
|
||||
| 'enterprise-payg'
|
||||
| 'flagOverviewRedesign'
|
||||
| 'showUserDeviceCount'
|
||||
| 'deleteStaleUserSessions'
|
||||
| 'memorizeStats'
|
||||
| 'granularAdminPermissions'
|
||||
| 'streaming'
|
||||
|
@ -45,6 +45,7 @@ export interface ISessionOption {
|
||||
db: boolean;
|
||||
clearSiteDataOnLogout: boolean;
|
||||
cookieName: string;
|
||||
maxParallelSessions: number;
|
||||
}
|
||||
|
||||
export interface IVersionOption {
|
||||
|
@ -18,7 +18,6 @@ import PasswordMismatch from '../../../lib/error/password-mismatch';
|
||||
import type { EventService } from '../../../lib/services';
|
||||
import {
|
||||
CREATE_ADDON,
|
||||
type IFlagResolver,
|
||||
type IUnleashStores,
|
||||
type IUserStore,
|
||||
SYSTEM_USER_AUDIT,
|
||||
@ -51,7 +50,9 @@ const allowedSessions = 2;
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('user_service_serial', getLogger);
|
||||
stores = db.stores;
|
||||
const config = createTestConfig();
|
||||
const config = createTestConfig({
|
||||
session: { maxParallelSessions: allowedSessions },
|
||||
});
|
||||
eventBus = config.eventBus;
|
||||
eventService = createEventsService(db.rawDatabase, config);
|
||||
const groupService = new GroupService(stores, config, eventService);
|
||||
@ -66,31 +67,14 @@ beforeAll(async () => {
|
||||
sessionService = new SessionService(stores, config);
|
||||
settingService = new SettingService(stores, config, eventService);
|
||||
|
||||
const flagResolver = {
|
||||
isEnabled() {
|
||||
return true;
|
||||
},
|
||||
getVariant() {
|
||||
return {
|
||||
feature_enabled: true,
|
||||
payload: {
|
||||
value: String(allowedSessions),
|
||||
},
|
||||
};
|
||||
},
|
||||
} as unknown as IFlagResolver;
|
||||
userService = new UserService(
|
||||
stores,
|
||||
{ ...config, flagResolver },
|
||||
{
|
||||
accessService,
|
||||
resetTokenService,
|
||||
emailService,
|
||||
eventService,
|
||||
sessionService,
|
||||
settingService,
|
||||
},
|
||||
);
|
||||
userService = new UserService(stores, config, {
|
||||
accessService,
|
||||
resetTokenService,
|
||||
emailService,
|
||||
eventService,
|
||||
sessionService,
|
||||
settingService,
|
||||
});
|
||||
userStore = stores.userStore;
|
||||
const rootRoles = await accessService.getRootRoles();
|
||||
adminRole = rootRoles.find((r) => r.name === RoleName.ADMIN)!;
|
||||
|
@ -201,6 +201,7 @@ unleash.start(unleashOptions);
|
||||
instructing the browser to clear all cookies on the same domain Unleash is running on. If disabled unleash will
|
||||
only destroy and clear the session cookie. Defaults to _true_. `SESSION_CLEAR_SITE_DATA_ON_LOGOUT`
|
||||
- _cookieName_ - Name of the cookies used to hold the session id. Defaults to 'unleash-session'.
|
||||
- _maxParallelSessions_ - The maximum number of parallel user sessions with password based login. `MAX_PARALLEL_SESSIONS`
|
||||
- **ui** (object) - Set of UI specific overrides. You may set the following keys: `environment`, `slogan`.
|
||||
- **versionCheck** - the object deciding where to check for latest version
|
||||
- `url` - The url to check version (Defaults to `https://version.unleash.run`) - Overridable
|
||||
|
Loading…
Reference in New Issue
Block a user