fix: licenses.info endpoint only available for admins (#30644)

pull/30611/head^2
Pierre Lehnen 2 years ago committed by GitHub
parent a0dcc38574
commit 85ddfb24ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      apps/meteor/ee/server/api/licenses.ts
  2. 46
      apps/meteor/tests/end-to-end/api/20-licenses.js
  3. 10
      ee/packages/license/src/definition/LicenseInfo.ts
  4. 11
      ee/packages/license/src/index.ts
  5. 19
      ee/packages/license/src/license.ts
  6. 8
      packages/rest-typings/src/v1/licenses.ts

@ -25,10 +25,13 @@ API.v1.addRoute(
API.v1.addRoute(
'licenses.info',
{ authRequired: true, validateParams: isLicensesInfoProps, permissionsRequired: ['view-privileged-setting'] },
{ authRequired: true, validateParams: isLicensesInfoProps },
{
async get() {
const data = await License.getInfo(Boolean(this.queryParams.loadValues));
const unrestrictedAccess = await hasPermissionAsync(this.userId, 'view-privileged-setting');
const loadCurrentValues = unrestrictedAccess && Boolean(this.queryParams.loadValues);
const data = await License.getInfo({ limits: unrestrictedAccess, license: unrestrictedAccess, currentValues: loadCurrentValues });
return API.v1.success({ data });
},

@ -105,6 +105,52 @@ describe('licenses', function () {
});
});
describe('[/licenses.info]', () => {
it('should fail if not logged in', (done) => {
request
.get(api('licenses.info'))
.expect('Content-Type', 'application/json')
.expect(401)
.expect((res) => {
expect(res.body).to.have.property('status', 'error');
expect(res.body).to.have.property('message');
})
.end(done);
});
it('should return limited information if user is unauthorized', (done) => {
request
.get(api('licenses.info'))
.set(unauthorizedUserCredentials)
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('data').and.to.be.an('object');
expect(res.body.data).to.not.have.property('license');
expect(res.body.data).to.have.property('tags').and.to.be.an('array');
})
.end(done);
});
it('should return unrestricted info if user is logged in and is authorized', (done) => {
request
.get(api('licenses.info'))
.set(credentials)
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('data').and.to.be.an('object');
if (process.env.IS_EE) {
expect(res.body.data).to.have.property('license').and.to.be.an('object');
}
expect(res.body.data).to.have.property('tags').and.to.be.an('array');
})
.end(done);
});
});
describe('[/licenses.isEnterprise]', () => {
it('should fail if not logged in', (done) => {
request

@ -0,0 +1,10 @@
import type { ILicenseTag } from './ILicenseTag';
import type { ILicenseV3, LicenseLimitKind } from './ILicenseV3';
import type { LicenseModule } from './LicenseModule';
export type LicenseInfo = {
license?: ILicenseV3;
activeModules: LicenseModule[];
limits: Record<LicenseLimitKind, { value?: number; max: number }>;
tags: ILicenseTag[];
};

@ -1,5 +1,5 @@
import type { ILicenseV3, LicenseLimitKind } from './definition/ILicenseV3';
import type { LicenseModule } from './definition/LicenseModule';
import type { LicenseLimitKind } from './definition/ILicenseV3';
import type { LicenseInfo } from './definition/LicenseInfo';
import type { LimitContext } from './definition/LimitContext';
import { getAppsConfig, getMaxActiveUsers, getUnmodifiedLicenseAndModules } from './deprecated';
import { onLicense } from './events/deprecated';
@ -24,6 +24,7 @@ export * from './definition/ILicenseTag';
export * from './definition/ILicenseV2';
export * from './definition/ILicenseV3';
export * from './definition/LicenseBehavior';
export * from './definition/LicenseInfo';
export * from './definition/LicenseLimit';
export * from './definition/LicenseModule';
export * from './definition/LicensePeriod';
@ -49,11 +50,7 @@ interface License {
onBehaviorTriggered: typeof onBehaviorTriggered;
revalidateLicense: () => Promise<void>;
getInfo: (loadCurrentValues: boolean) => Promise<{
license: ILicenseV3 | undefined;
activeModules: LicenseModule[];
limits: Record<LicenseLimitKind, { value?: number; max: number }>;
}>;
getInfo: (info: { limits: boolean; currentValues: boolean; license: boolean }) => Promise<LicenseInfo>;
// Deprecated:
onLicense: typeof onLicense;

@ -4,6 +4,7 @@ import { type ILicenseTag } from './definition/ILicenseTag';
import type { ILicenseV2 } from './definition/ILicenseV2';
import type { ILicenseV3, LicenseLimitKind } from './definition/ILicenseV3';
import type { BehaviorWithContext } from './definition/LicenseBehavior';
import type { LicenseInfo } from './definition/LicenseInfo';
import type { LicenseModule } from './definition/LicenseModule';
import type { LicenseValidationOptions } from './definition/LicenseValidationOptions';
import type { LimitContext } from './definition/LimitContext';
@ -291,17 +292,22 @@ export class LicenseManager extends Emitter<LicenseEvents> {
return isBehaviorsInResult(validationResult, ['prevent_action']);
}
public async getInfo(loadCurrentValues = false): Promise<{
license: ILicenseV3 | undefined;
activeModules: LicenseModule[];
limits: Record<LicenseLimitKind, { value?: number; max: number }>;
}> {
public async getInfo({
limits: includeLimits,
currentValues: loadCurrentValues,
license: includeLicense,
}: {
limits: boolean;
currentValues: boolean;
license: boolean;
}): Promise<LicenseInfo> {
const activeModules = getModules.call(this);
const license = this.getLicense();
// Get all limits present in the license and their current value
const limits = (
(license &&
includeLimits &&
(await Promise.all(
globalLimitKinds
.map((limitKey) => ({
@ -322,9 +328,10 @@ export class LicenseManager extends Emitter<LicenseEvents> {
).reduce((prev, curr) => ({ ...prev, ...curr }), {});
return {
license,
license: (includeLicense && license) || undefined,
activeModules,
limits: limits as Record<LicenseLimitKind, { max: number; value: number }>,
tags: license?.information.tags || [],
};
}
}

@ -1,4 +1,4 @@
import type { ILicenseV2, ILicenseV3, LicenseLimitKind } from '@rocket.chat/license';
import type { ILicenseV2, ILicenseV3, LicenseInfo } from '@rocket.chat/license';
import Ajv from 'ajv';
const ajv = new Ajv({
@ -45,11 +45,7 @@ export type LicensesEndpoints = {
};
'/v1/licenses.info': {
GET: (params: licensesInfoProps) => {
data: {
license: ILicenseV3 | undefined;
activeModules: string[];
limits: Record<LicenseLimitKind, { max: number; value?: number }>;
};
data: LicenseInfo;
};
};
'/v1/licenses.add': {

Loading…
Cancel
Save