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,
|
"clearSiteDataOnLogout": true,
|
||||||
"cookieName": "unleash-session",
|
"cookieName": "unleash-session",
|
||||||
"db": true,
|
"db": true,
|
||||||
|
"maxParallelSessions": 5,
|
||||||
"ttlHours": 48,
|
"ttlHours": 48,
|
||||||
},
|
},
|
||||||
"strategySegmentsLimit": 5,
|
"strategySegmentsLimit": 5,
|
||||||
|
@ -266,7 +266,7 @@ const defaultDbOptions: WithOptional<IDBOption, 'user' | 'password' | 'host'> =
|
|||||||
applicationName: process.env.DATABASE_APPLICATION_NAME || 'unleash',
|
applicationName: process.env.DATABASE_APPLICATION_NAME || 'unleash',
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultSessionOption: ISessionOption = {
|
const defaultSessionOption = (isEnterprise: boolean): ISessionOption => ({
|
||||||
ttlHours: parseEnvVarNumber(process.env.SESSION_TTL_HOURS, 48),
|
ttlHours: parseEnvVarNumber(process.env.SESSION_TTL_HOURS, 48),
|
||||||
clearSiteDataOnLogout: parseEnvVarBoolean(
|
clearSiteDataOnLogout: parseEnvVarBoolean(
|
||||||
process.env.SESSION_CLEAR_SITE_DATA_ON_LOGOUT,
|
process.env.SESSION_CLEAR_SITE_DATA_ON_LOGOUT,
|
||||||
@ -274,7 +274,16 @@ const defaultSessionOption: ISessionOption = {
|
|||||||
),
|
),
|
||||||
cookieName: 'unleash-session',
|
cookieName: 'unleash-session',
|
||||||
db: true,
|
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 = {
|
const defaultServerOption: IServerOption = {
|
||||||
pipe: undefined,
|
pipe: undefined,
|
||||||
@ -533,11 +542,6 @@ export function createConfig(options: IUnleashOptions): IUnleashConfig {
|
|||||||
options.db || {},
|
options.db || {},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const session: ISessionOption = mergeAll([
|
|
||||||
defaultSessionOption,
|
|
||||||
options.session || {},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const logLevel =
|
const logLevel =
|
||||||
options.logLevel || LogLevel[process.env.LOG_LEVEL ?? LogLevel.error];
|
options.logLevel || LogLevel[process.env.LOG_LEVEL ?? LogLevel.error];
|
||||||
const getLogger = options.getLogger || getDefaultLogProvider(logLevel);
|
const getLogger = options.getLogger || getDefaultLogProvider(logLevel);
|
||||||
@ -638,6 +642,12 @@ export function createConfig(options: IUnleashOptions): IUnleashConfig {
|
|||||||
ui.environment,
|
ui.environment,
|
||||||
isTest,
|
isTest,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const session: ISessionOption = mergeAll([
|
||||||
|
defaultSessionOption(isEnterprise),
|
||||||
|
options.session || {},
|
||||||
|
]);
|
||||||
|
|
||||||
const metricsRateLimiting = loadMetricsRateLimitingConfig(options);
|
const metricsRateLimiting = loadMetricsRateLimitingConfig(options);
|
||||||
|
|
||||||
const rateLimiting = loadRateLimitingConfig(options);
|
const rateLimiting = loadRateLimitingConfig(options);
|
||||||
|
@ -98,6 +98,8 @@ class UserService {
|
|||||||
|
|
||||||
readonly unleashUrl: string;
|
readonly unleashUrl: string;
|
||||||
|
|
||||||
|
readonly maxParallelSessions: number;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
stores: Pick<IUnleashStores, 'userStore'>,
|
stores: Pick<IUnleashStores, 'userStore'>,
|
||||||
{
|
{
|
||||||
@ -106,6 +108,7 @@ class UserService {
|
|||||||
authentication,
|
authentication,
|
||||||
eventBus,
|
eventBus,
|
||||||
flagResolver,
|
flagResolver,
|
||||||
|
session,
|
||||||
}: Pick<
|
}: Pick<
|
||||||
IUnleashConfig,
|
IUnleashConfig,
|
||||||
| 'getLogger'
|
| 'getLogger'
|
||||||
@ -113,6 +116,7 @@ class UserService {
|
|||||||
| 'server'
|
| 'server'
|
||||||
| 'eventBus'
|
| 'eventBus'
|
||||||
| 'flagResolver'
|
| 'flagResolver'
|
||||||
|
| 'session'
|
||||||
>,
|
>,
|
||||||
services: {
|
services: {
|
||||||
accessService: AccessService;
|
accessService: AccessService;
|
||||||
@ -133,6 +137,7 @@ class UserService {
|
|||||||
this.sessionService = services.sessionService;
|
this.sessionService = services.sessionService;
|
||||||
this.settingService = services.settingService;
|
this.settingService = services.settingService;
|
||||||
this.flagResolver = flagResolver;
|
this.flagResolver = flagResolver;
|
||||||
|
this.maxParallelSessions = session.maxParallelSessions;
|
||||||
|
|
||||||
process.nextTick(() => this.initAdminUser(authentication));
|
process.nextTick(() => this.initAdminUser(authentication));
|
||||||
|
|
||||||
@ -431,22 +436,14 @@ class UserService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteStaleUserSessions = this.flagResolver.getVariant(
|
// subtract current user session that will be created
|
||||||
'deleteStaleUserSessions',
|
const deletedSessionsCount =
|
||||||
);
|
await this.sessionService.deleteStaleSessionsForUser(
|
||||||
if (deleteStaleUserSessions.feature_enabled) {
|
user.id,
|
||||||
const allowedSessions = Number(
|
Math.max(this.maxParallelSessions - 1, 0),
|
||||||
deleteStaleUserSessions.payload?.value || 5,
|
|
||||||
);
|
);
|
||||||
// subtract current user session that will be created
|
user.deletedSessions = deletedSessionsCount;
|
||||||
const deletedSessionsCount =
|
user.activeSessions = this.maxParallelSessions;
|
||||||
await this.sessionService.deleteStaleSessionsForUser(
|
|
||||||
user.id,
|
|
||||||
Math.max(allowedSessions - 1, 0),
|
|
||||||
);
|
|
||||||
user.deletedSessions = deletedSessionsCount;
|
|
||||||
user.activeSessions = allowedSessions;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.eventBus.emit(USER_LOGIN, { loginOrder });
|
this.eventBus.emit(USER_LOGIN, { loginOrder });
|
||||||
return user;
|
return user;
|
||||||
|
@ -54,7 +54,6 @@ export type IFlagKey =
|
|||||||
| 'enterprise-payg'
|
| 'enterprise-payg'
|
||||||
| 'flagOverviewRedesign'
|
| 'flagOverviewRedesign'
|
||||||
| 'showUserDeviceCount'
|
| 'showUserDeviceCount'
|
||||||
| 'deleteStaleUserSessions'
|
|
||||||
| 'memorizeStats'
|
| 'memorizeStats'
|
||||||
| 'granularAdminPermissions'
|
| 'granularAdminPermissions'
|
||||||
| 'streaming'
|
| 'streaming'
|
||||||
|
@ -45,6 +45,7 @@ export interface ISessionOption {
|
|||||||
db: boolean;
|
db: boolean;
|
||||||
clearSiteDataOnLogout: boolean;
|
clearSiteDataOnLogout: boolean;
|
||||||
cookieName: string;
|
cookieName: string;
|
||||||
|
maxParallelSessions: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IVersionOption {
|
export interface IVersionOption {
|
||||||
|
@ -18,7 +18,6 @@ import PasswordMismatch from '../../../lib/error/password-mismatch';
|
|||||||
import type { EventService } from '../../../lib/services';
|
import type { EventService } from '../../../lib/services';
|
||||||
import {
|
import {
|
||||||
CREATE_ADDON,
|
CREATE_ADDON,
|
||||||
type IFlagResolver,
|
|
||||||
type IUnleashStores,
|
type IUnleashStores,
|
||||||
type IUserStore,
|
type IUserStore,
|
||||||
SYSTEM_USER_AUDIT,
|
SYSTEM_USER_AUDIT,
|
||||||
@ -51,7 +50,9 @@ const allowedSessions = 2;
|
|||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('user_service_serial', getLogger);
|
db = await dbInit('user_service_serial', getLogger);
|
||||||
stores = db.stores;
|
stores = db.stores;
|
||||||
const config = createTestConfig();
|
const config = createTestConfig({
|
||||||
|
session: { maxParallelSessions: allowedSessions },
|
||||||
|
});
|
||||||
eventBus = config.eventBus;
|
eventBus = config.eventBus;
|
||||||
eventService = createEventsService(db.rawDatabase, config);
|
eventService = createEventsService(db.rawDatabase, config);
|
||||||
const groupService = new GroupService(stores, config, eventService);
|
const groupService = new GroupService(stores, config, eventService);
|
||||||
@ -66,31 +67,14 @@ beforeAll(async () => {
|
|||||||
sessionService = new SessionService(stores, config);
|
sessionService = new SessionService(stores, config);
|
||||||
settingService = new SettingService(stores, config, eventService);
|
settingService = new SettingService(stores, config, eventService);
|
||||||
|
|
||||||
const flagResolver = {
|
userService = new UserService(stores, config, {
|
||||||
isEnabled() {
|
accessService,
|
||||||
return true;
|
resetTokenService,
|
||||||
},
|
emailService,
|
||||||
getVariant() {
|
eventService,
|
||||||
return {
|
sessionService,
|
||||||
feature_enabled: true,
|
settingService,
|
||||||
payload: {
|
});
|
||||||
value: String(allowedSessions),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
} as unknown as IFlagResolver;
|
|
||||||
userService = new UserService(
|
|
||||||
stores,
|
|
||||||
{ ...config, flagResolver },
|
|
||||||
{
|
|
||||||
accessService,
|
|
||||||
resetTokenService,
|
|
||||||
emailService,
|
|
||||||
eventService,
|
|
||||||
sessionService,
|
|
||||||
settingService,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
userStore = stores.userStore;
|
userStore = stores.userStore;
|
||||||
const rootRoles = await accessService.getRootRoles();
|
const rootRoles = await accessService.getRootRoles();
|
||||||
adminRole = rootRoles.find((r) => r.name === RoleName.ADMIN)!;
|
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
|
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`
|
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'.
|
- _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`.
|
- **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
|
- **versionCheck** - the object deciding where to check for latest version
|
||||||
- `url` - The url to check version (Defaults to `https://version.unleash.run`) - Overridable
|
- `url` - The url to check version (Defaults to `https://version.unleash.run`) - Overridable
|
||||||
|
Loading…
Reference in New Issue
Block a user