♻️ backend: adding freeform instanceid to identify multiple instances of plugin (#525)
This commit is contained in:
@@ -135,6 +135,14 @@ const OnlyOfficeSafeDocKeyBase64 = {
|
||||
},
|
||||
};
|
||||
|
||||
function checkFieldValue(field: string, value: string) {
|
||||
if (!/^[0-9a-zA-Z_-]+$/m.test(value))
|
||||
throw new Error(
|
||||
`Invalid ${field} value (${JSON.stringify(
|
||||
value,
|
||||
)}). Must be short and only alpha numeric or '_' and '-'`,
|
||||
);
|
||||
}
|
||||
/** Reference implementation for generating then parsing the {@link DriveFile.editing_session_key} field */
|
||||
export const EditingSessionKeyFormat = {
|
||||
// OnlyOffice key limits: 128 chars, [0-9a-zA-Z.=_-]
|
||||
@@ -145,13 +153,15 @@ export const EditingSessionKeyFormat = {
|
||||
// common denominator to all plugin/interop systems. Plugins that
|
||||
// require something even stricter have the option of maintaining
|
||||
// a look up table to an acceptable value.
|
||||
generate(applicationId: string, companyId: string, userId: string, overrideTimeStamp?: Date) {
|
||||
if (!/^[0-9a-zA-Z_-]+$/m.test(applicationId))
|
||||
throw new Error(
|
||||
`Invalid applicationId string (${JSON.stringify(
|
||||
applicationId,
|
||||
)}). Must be short and only alpha numeric`,
|
||||
);
|
||||
generate(
|
||||
applicationId: string,
|
||||
instanceId: string,
|
||||
companyId: string,
|
||||
userId: string,
|
||||
overrideTimeStamp?: Date,
|
||||
) {
|
||||
checkFieldValue("applicationId", applicationId);
|
||||
checkFieldValue("instanceId", instanceId);
|
||||
const isoUTCDateNoSpecialCharsNoMS = (overrideTimeStamp ?? new Date())
|
||||
.toISOString()
|
||||
.replace(/\..+$/, "")
|
||||
@@ -162,7 +172,7 @@ export const EditingSessionKeyFormat = {
|
||||
const idsString = OnlyOfficeSafeDocKeyBase64.fromBuffer(
|
||||
Buffer.concat([companyIdBuffer, userIdBuffer, entropyBuffer]),
|
||||
);
|
||||
const newKey = [isoUTCDateNoSpecialCharsNoMS, applicationId, idsString].join("=");
|
||||
const newKey = [isoUTCDateNoSpecialCharsNoMS, applicationId, instanceId, idsString].join("=");
|
||||
if (newKey.length > 128 || !/^[0-9a-zA-Z=_-]+$/m.test(newKey))
|
||||
throw new Error(
|
||||
`Invalid generated editingSessionKey (${JSON.stringify(
|
||||
@@ -174,14 +184,14 @@ export const EditingSessionKeyFormat = {
|
||||
|
||||
parse(editingSessionKey: string) {
|
||||
const parts = editingSessionKey.split("=");
|
||||
const expectedParts = 3;
|
||||
const expectedParts = 4;
|
||||
if (parts.length !== expectedParts)
|
||||
throw new Error(
|
||||
`Invalid editingSessionKey (${JSON.stringify(
|
||||
editingSessionKey,
|
||||
)}). Expected ${expectedParts} parts`,
|
||||
);
|
||||
const [timestampStr, appId, idsOOBase64String] = parts;
|
||||
const [timestampStr, applicationId, instanceId, idsOOBase64String] = parts;
|
||||
const timestampMatch = timestampStr.match(
|
||||
/^(?<year>\d{4})(?<month>\d\d)(?<day>\d\d)(?<hour>\d\d)(?<minute>\d\d)(?<second>\d\d)$/,
|
||||
);
|
||||
@@ -199,7 +209,8 @@ export const EditingSessionKeyFormat = {
|
||||
timestamp: new Date(
|
||||
Date.parse(`${[year, month, day].join("-")}T${[hour, minute, second].join(":")}Z`),
|
||||
),
|
||||
applicationId: appId,
|
||||
applicationId,
|
||||
instanceId,
|
||||
companyId,
|
||||
userId,
|
||||
};
|
||||
|
||||
@@ -967,6 +967,7 @@ export class DocumentsService {
|
||||
beginEditing = async (
|
||||
id: string,
|
||||
editorApplicationId: string,
|
||||
appInstanceId: string,
|
||||
context: DriveExecutionContext,
|
||||
) => {
|
||||
if (!context) {
|
||||
@@ -978,6 +979,7 @@ export class DocumentsService {
|
||||
try {
|
||||
newKey = EditingSessionKeyFormat.generate(
|
||||
editorApplicationId,
|
||||
appInstanceId,
|
||||
context.company.id,
|
||||
context.user.id,
|
||||
);
|
||||
|
||||
@@ -351,7 +351,7 @@ export class DocumentsController {
|
||||
request: FastifyRequest<{
|
||||
Params: ItemRequestParams;
|
||||
//TODO application id should be received from the token that we have during the login
|
||||
Body: { editorApplicationId: string };
|
||||
Body: { editorApplicationId: string; instanceId: string };
|
||||
}>,
|
||||
) => {
|
||||
try {
|
||||
@@ -366,6 +366,7 @@ export class DocumentsController {
|
||||
return await globalResolver.services.documents.documents.beginEditing(
|
||||
id,
|
||||
request.body.editorApplicationId,
|
||||
request.body.instanceId || "",
|
||||
context,
|
||||
);
|
||||
} catch (error) {
|
||||
|
||||
+8
-6
@@ -7,6 +7,7 @@ describe('DriveFile EditingSessionKeyFormat', () => {
|
||||
const mockAppId = 'tdrive_random_application_id';
|
||||
const mockCompanyId = randomUUID();
|
||||
const mockUserId = randomUUID();
|
||||
const mockInstanceId = "super-instance-id";
|
||||
const mockTimestamp = new Date();
|
||||
const mockTimestampWith0MS = mockTimestamp.getTime() - (mockTimestamp.getTime() % 1000);
|
||||
|
||||
@@ -23,31 +24,32 @@ describe('DriveFile EditingSessionKeyFormat', () => {
|
||||
test('generates a valid value that can be parsed', async () => {
|
||||
checkIsUUID(mockUserId);
|
||||
checkIsUUID(mockCompanyId);
|
||||
const key = EditingSessionKeyFormat.generate(mockAppId, mockCompanyId, mockUserId, mockTimestamp);
|
||||
const key = EditingSessionKeyFormat.generate(mockAppId, mockInstanceId, mockCompanyId, mockUserId, mockTimestamp);
|
||||
checkKeyIsOOCompatible(key);
|
||||
const parsed = EditingSessionKeyFormat.parse(key);
|
||||
expect(parsed.applicationId).toBe(mockAppId);
|
||||
expect(parsed.instanceId).toBe(mockInstanceId);
|
||||
expect(parsed.userId).toBe(mockUserId);
|
||||
expect(parsed.companyId).toBe(mockCompanyId);
|
||||
expect(parsed.timestamp.getTime()).toBe(mockTimestampWith0MS);
|
||||
});
|
||||
|
||||
test('generates unique values', async () => {
|
||||
const key = EditingSessionKeyFormat.generate(mockAppId, mockCompanyId, mockUserId, mockTimestamp);
|
||||
const key2 = EditingSessionKeyFormat.generate(mockAppId, mockCompanyId, mockUserId, mockTimestamp);
|
||||
const key = EditingSessionKeyFormat.generate(mockAppId, mockInstanceId, mockCompanyId, mockUserId, mockTimestamp);
|
||||
const key2 = EditingSessionKeyFormat.generate(mockAppId, mockInstanceId, mockCompanyId, mockUserId, mockTimestamp);
|
||||
expect(key).not.toBe(key2);
|
||||
});
|
||||
|
||||
test('checks the appId', async () => {
|
||||
expect(() => {
|
||||
EditingSessionKeyFormat.generate('invalid app id !', mockCompanyId, mockUserId);
|
||||
}).toThrow('Invalid applicationId string');
|
||||
EditingSessionKeyFormat.generate('invalid app id !', mockInstanceId, mockCompanyId, mockUserId);
|
||||
}).toThrow('Invalid applicationId value');
|
||||
});
|
||||
|
||||
test('checks final length', async () => {
|
||||
expect(() => {
|
||||
const tooLongAppID = new Array(100).join('x');
|
||||
EditingSessionKeyFormat.generate(tooLongAppID, mockCompanyId, mockUserId);
|
||||
EditingSessionKeyFormat.generate(tooLongAppID, mockInstanceId, mockCompanyId, mockUserId);
|
||||
}).toThrow('Must be <128 chars,');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user