fix: correct application cmpp credentials
This commit is contained in:
@@ -9,6 +9,7 @@ function createPrismaMock() {
|
||||
name: '应用A',
|
||||
status: 'active',
|
||||
cmppAccount: '100001',
|
||||
cmppEnterpriseCode: 'APP-EC',
|
||||
cmppMaxConnections: 2,
|
||||
cmppWindowSize: 32,
|
||||
queuePriority: 'normal',
|
||||
@@ -21,10 +22,11 @@ function createPrismaMock() {
|
||||
name: '应用A',
|
||||
status: 'active',
|
||||
cmppAccount: '100001',
|
||||
cmppEnterpriseCode: 'APP-EC',
|
||||
cmppMaxConnections: 2,
|
||||
cmppWindowSize: 32,
|
||||
queuePriority: 'normal',
|
||||
secretHash: 'secret-hash',
|
||||
secretHash: '0123456789abcdef',
|
||||
tenant: { id: 'tenant-1', name: '租户A', code: 'TENANT-A' },
|
||||
}),
|
||||
update: jest.fn().mockImplementation(({ data }) => Promise.resolve({ id: 'app-1', tenantId: 'tenant-1', ...data })),
|
||||
@@ -109,6 +111,9 @@ function createPrismaMock() {
|
||||
operationLog: {
|
||||
create: jest.fn(),
|
||||
},
|
||||
tenant: {
|
||||
findUnique: jest.fn().mockResolvedValue({ code: 'TENANT-A' }),
|
||||
},
|
||||
$transaction: jest.fn((callback) => callback({
|
||||
smsApplication: {
|
||||
update: jest.fn().mockImplementation(({ data }) => Promise.resolve({ id: 'app-1', tenantId: 'tenant-1', ...data })),
|
||||
@@ -171,6 +176,8 @@ describe('SmsConfigService', () => {
|
||||
applicationId: 'app-1',
|
||||
tenantName: '租户A',
|
||||
account: '100001',
|
||||
enterpriseCode: 'APP-EC',
|
||||
passwordCipher: '0123456789abcdef',
|
||||
gatewayHost: '127.0.0.1',
|
||||
gatewayPort: 17890,
|
||||
maxConnections: 2,
|
||||
@@ -187,6 +194,8 @@ describe('SmsConfigService', () => {
|
||||
tenantId: 'tenant-1',
|
||||
name: '优先应用',
|
||||
cmppAccount: '123456',
|
||||
cmppEnterpriseCode: 'CUSTOM-EC',
|
||||
passwordCipher: '1234567890abcdef',
|
||||
cmppMaxConnections: 3,
|
||||
cmppWindowSize: 32,
|
||||
queuePriority: 'priority',
|
||||
@@ -198,6 +207,8 @@ describe('SmsConfigService', () => {
|
||||
tenantId: 'tenant-1',
|
||||
name: '优先应用',
|
||||
cmppAccount: '123456',
|
||||
cmppEnterpriseCode: 'CUSTOM-EC',
|
||||
secretHash: '1234567890abcdef',
|
||||
cmppMaxConnections: 3,
|
||||
cmppWindowSize: 32,
|
||||
queuePriority: 'priority',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { randomBytes, randomInt, createHash } from 'node:crypto';
|
||||
import { randomInt, randomUUID } from 'node:crypto';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
|
||||
export interface CreateSmsApplicationDto {
|
||||
@@ -9,6 +9,8 @@ export interface CreateSmsApplicationDto {
|
||||
scene?: string;
|
||||
callbackUrl?: string;
|
||||
cmppAccount?: string;
|
||||
cmppEnterpriseCode?: string;
|
||||
passwordCipher?: string;
|
||||
cmppMaxConnections?: number;
|
||||
cmppWindowSize?: number;
|
||||
dailyLimit?: number;
|
||||
@@ -154,9 +156,10 @@ export class SmsConfigService {
|
||||
}
|
||||
|
||||
async createApplication(data: CreateSmsApplicationDto) {
|
||||
const secret = randomBytes(24).toString('hex');
|
||||
const secret = normalizeApplicationPassword(data.passwordCipher);
|
||||
const queuePriority = normalizeApplicationQueuePriority(data.queuePriority);
|
||||
const cmppAccount = data.cmppAccount ? await this.validateAndReserveCmppAccount(data.cmppAccount) : await this.generateCmppAccount();
|
||||
const cmppEnterpriseCode = await this.resolveCmppEnterpriseCode(data.cmppEnterpriseCode, data.tenantId);
|
||||
return this.prisma.smsApplication.create({
|
||||
data: {
|
||||
tenantId: data.tenantId,
|
||||
@@ -164,7 +167,8 @@ export class SmsConfigService {
|
||||
scene: data.scene,
|
||||
callbackUrl: data.callbackUrl,
|
||||
cmppAccount,
|
||||
secretHash: hashSecret(secret),
|
||||
cmppEnterpriseCode,
|
||||
secretHash: secret,
|
||||
cmppMaxConnections: getPositiveInteger(data.cmppMaxConnections, 1, 'cmppMaxConnections'),
|
||||
cmppWindowSize: getPositiveInteger(data.cmppWindowSize, 16, 'cmppWindowSize'),
|
||||
dailyLimit: data.dailyLimit,
|
||||
@@ -191,6 +195,12 @@ export class SmsConfigService {
|
||||
const cmppAccount = data.cmppAccount === undefined
|
||||
? undefined
|
||||
: await this.validateAndReserveCmppAccount(data.cmppAccount, applicationId);
|
||||
const cmppEnterpriseCode = data.cmppEnterpriseCode === undefined
|
||||
? undefined
|
||||
: normalizeEnterpriseCode(data.cmppEnterpriseCode);
|
||||
const secretHash = data.passwordCipher === undefined
|
||||
? undefined
|
||||
: normalizeApplicationPassword(data.passwordCipher);
|
||||
|
||||
return this.prisma.$transaction(async (tx) => {
|
||||
if (data.ipAllowlist) {
|
||||
@@ -203,6 +213,8 @@ export class SmsConfigService {
|
||||
scene: data.scene,
|
||||
callbackUrl: data.callbackUrl,
|
||||
cmppAccount,
|
||||
cmppEnterpriseCode,
|
||||
secretHash,
|
||||
cmppMaxConnections: data.cmppMaxConnections === undefined ? undefined : getPositiveInteger(data.cmppMaxConnections, 1, 'cmppMaxConnections'),
|
||||
cmppWindowSize: data.cmppWindowSize === undefined ? undefined : getPositiveInteger(data.cmppWindowSize, 16, 'cmppWindowSize'),
|
||||
dailyLimit: data.dailyLimit,
|
||||
@@ -286,10 +298,10 @@ export class SmsConfigService {
|
||||
if (!application) {
|
||||
throw new NotFoundException('Application not found');
|
||||
}
|
||||
const secret = randomBytes(24).toString('hex');
|
||||
const secret = generateApplicationPassword();
|
||||
const updated = await this.prisma.smsApplication.update({
|
||||
where: { id: applicationId },
|
||||
data: { secretHash: hashSecret(secret) },
|
||||
data: { secretHash: secret },
|
||||
});
|
||||
await this.writeOperationLog(application.tenantId, data.operatorId, 'sms_application.secret_reset', 'sms_application', applicationId, {
|
||||
reason: data.reason,
|
||||
@@ -357,7 +369,7 @@ export class SmsConfigService {
|
||||
appCode: application.id,
|
||||
gatewayHost: channel?.gatewayHost ?? '',
|
||||
gatewayPort: channel?.gatewayPort ?? 0,
|
||||
enterpriseCode: channel?.enterpriseCode ?? application.tenant.code,
|
||||
enterpriseCode: application.cmppEnterpriseCode,
|
||||
account: application.cmppAccount,
|
||||
passwordCipher: application.secretHash,
|
||||
srcId: channel?.srcId ?? '',
|
||||
@@ -390,6 +402,17 @@ export class SmsConfigService {
|
||||
throw new BadRequestException('Unable to generate unique CMPP account');
|
||||
}
|
||||
|
||||
private async resolveCmppEnterpriseCode(cmppEnterpriseCode: string | undefined, tenantId: string) {
|
||||
if (cmppEnterpriseCode !== undefined) {
|
||||
return normalizeEnterpriseCode(cmppEnterpriseCode);
|
||||
}
|
||||
const tenant = await this.prisma.tenant.findUnique({ where: { id: tenantId }, select: { code: true } });
|
||||
if (!tenant) {
|
||||
throw new BadRequestException('tenantId does not reference an existing tenant');
|
||||
}
|
||||
return normalizeEnterpriseCode(tenant.code);
|
||||
}
|
||||
|
||||
async disconnectApplicationConnection(applicationId: string, connectionId: string, data: StatusChangeDto = { status: 'disconnected' }) {
|
||||
const application = await this.prisma.smsApplication.findUnique({ where: { id: applicationId } });
|
||||
if (!application) {
|
||||
@@ -760,8 +783,27 @@ interface TemplateVariableInput {
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
function hashSecret(secret: string) {
|
||||
return createHash('sha256').update(secret).digest('hex');
|
||||
function normalizeEnterpriseCode(value: string) {
|
||||
const enterpriseCode = value.trim();
|
||||
if (!enterpriseCode) {
|
||||
throw new BadRequestException('cmppEnterpriseCode is required');
|
||||
}
|
||||
if (enterpriseCode.length > 32) {
|
||||
throw new BadRequestException('cmppEnterpriseCode must be at most 32 characters');
|
||||
}
|
||||
return enterpriseCode;
|
||||
}
|
||||
|
||||
function normalizeApplicationPassword(value: string | undefined) {
|
||||
const password = value?.trim() || generateApplicationPassword();
|
||||
if (password.length !== 16) {
|
||||
throw new BadRequestException('passwordCipher must be 16 characters');
|
||||
}
|
||||
return password;
|
||||
}
|
||||
|
||||
function generateApplicationPassword() {
|
||||
return randomUUID().replace(/-/g, '').slice(0, 16);
|
||||
}
|
||||
|
||||
function estimateBillingUnits(content: string) {
|
||||
|
||||
Reference in New Issue
Block a user