From 1208c837bb975728ecf5019392dbb832fdb71578 Mon Sep 17 00:00:00 2001 From: Sergey Konovalov Date: Mon, 3 Jun 2024 18:07:00 +0300 Subject: [PATCH 01/13] [bug] Change privateKey config param; For bug 66601 --- Common/config/default.json | 8 ++++---- DocService/sources/wopiClient.js | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Common/config/default.json b/Common/config/default.json index 9cca64bc..57d1c12b 100644 --- a/Common/config/default.json +++ b/Common/config/default.json @@ -99,12 +99,12 @@ "slideEdit": ["pptx", "pptm", "odp"], "publicKey": "BgIAAACkAABSU0ExAAgAAAEAAQBpTpiJQ2hD8plpGTfEEmcq4IKyr31HikXpuVSBraMfqyodn2PGXBJ3daNSmdPOc0Nz4HO9Auljn8YYXDPBdpiABptSKvEDPF23Q+Qytg0+vCRyondyBcW91w7KLzXce3fnk8ZfJ8QtbZPL9m11wJIWZueQF+l0HKYx4lty+nccbCanytFTADkGQ3SnmExGEF3rBz6I9+OcrDDK9NKPJgEmCiuyei/d4XbPgKls3EIG0h38X5mVF2VytfWm2Yu850B6z3N4MYhj4b4vsYT62zEC4pMRUeb8dIBy4Jsmr3avtmeO00MUH6DVyPC8nirixj2YIOPKk13CdVqGDSXA3cvl", "modulus": "E5CBDDC0250D865A75C25D93CAE320983DC6E22A9EBCF0C8D5A01F1443D38E67B6AF76AF269BE0728074FCE6511193E20231DBFA84B12FBEE16388317873CF7A40E7BC8BD9A6F5B572651795995FFC1DD20642DC6CA980CF76E1DD2F7AB22B0A2601268FD2F4CA30AC9CE3F7883E07EB5D10464C98A7744306390053D1CAA7266C1C77FA725BE231A61C74E91790E7661692C0756DF6CB936D2DC4275FC693E7777BDC352FCA0ED7BDC5057277A27224BC3E0DB632E443B75D3C03F12A529B06809876C1335C18C69F63E902BD73E0734373CED39952A37577125CC6639F1D2AAB1FA3AD8154B9E9458A477DAFB282E02A6712C437196999F243684389984E69", - "exponent": "65537", - "privateKey": "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tDQpNSUlFdndJQkFEQU5CZ2txaGtpRzl3MEJBUUVGQUFTQ0JLa3dnZ1NsQWdFQUFvSUJBUURseTkzQUpRMkdXblhDDQpYWlBLNHlDWVBjYmlLcDY4OE1qVm9COFVROU9PWjdhdmRxOG1tK0J5Z0hUODVsRVJrK0lDTWR2NmhMRXZ2dUZqDQppREY0Yzg5NlFPZThpOW1tOWJWeVpSZVZtVi84SGRJR1F0eHNxWURQZHVIZEwzcXlLd29tQVNhUDB2VEtNS3ljDQo0L2VJUGdmclhSQkdUSmluZEVNR09RQlQwY3FuSm13Y2QvcHlXK0l4cGh4MDZSZVE1MllXa3NCMWJmYkxrMjB0DQp4Q2RmeHBQbmQzdmNOUy9LRHRlOXhRVnlkNkp5Skx3K0RiWXk1RU8zWFR3RDhTcFNtd2FBbUhiQk0xd1l4cDlqDQo2UUs5YytCelEzUE8wNWxTbzNWM0VsekdZNThkS3FzZm82MkJWTG5wUllwSGZhK3lndUFxWnhMRU54bHBtZkpEDQphRU9KbUU1cEFnTUJBQUVDZ2dFQUxpTCtSS09yMFh1OEJPZ1EwajFEd0EwM0x4VnJoWGU2ZXRtSkkrSnlTVGNkDQpnS0VOald6aVpWclJJaTJEdlVtNXFNTWw3V2hTd3NsS0sxZWV4eFpKWTd4QVNxU3hjRW9Jd2d6MTdUMDcvanhtDQpmSWRVQmlVS0RaMUt2OFBXbUlyM29LVytma1hXaS9tMXpsSWUwcVhScFRtc0dORXNIUUxFcWkwcm1haVhUWE9SDQovMkxkd2k2a1pSM3NXRng5N1lTNE14L3B1ZUdKVFhFYWk2QVZFWnpONUdvZzZ4RDhIWFIxUnZxK2hoZCtNb2NHDQpmblU0SGdpbEtSZm9KbFdkOUZPc2NnU3VmS0cwTDNWaU80ZlNLVTQ2bDVhdWxsRFlVazVFQ01XaXd1S1NxU0U3DQpxRDQ1akkzbWJPcmU3UzR1M1MzVFdkRDNsendpWEw0OUxkd0tsRUM0bVFLQmdRRDBzTHIwR0g0V3IrUVgyeEpFDQp1QS9DYjhRVzQxbDhpU0NCVFJaWlIvc0pPZCtvM3JiY1ZpZGx6Ty9FYlpibFhHNFpQRG1SamdCQ0dLSVA1RVppDQowRHNMK1d2MzJXT280NExweEpHaHFFeGJtMEgxaVoxelo5N2wwUDhmdkloSEU0MmdtYUxUb09JR0RoUFNYR3Z2DQp6bHFPSGJHYnE0anNFUmMxanAxYmVqNXE2d0tCZ1FEd2F1ZUljNHBSY2hIOThRWWlkY3lyOFZ3ZzlLaGJuZllYDQp5M1c0UlBsWnRCZEYzNGlKYWlvK0FTenVnby96eTFSVGNWcnNDc2tZV1h5S0RVUXoxeXUwaUNuZytmRENVblRtDQpYR21Fb0VHTmhrNHZUSk90N2hCYXYxL0phL2RVaXBHZjZtWFV1YW53SjBlKzEvRXQvQjBhaDVYMVVtNUF5TlpJDQpNK1N5UnozdSt3S0JnUUNqdnRVTlhvcWFnaENCQ21CNlRqWjFwcmV4bldrWUZ1Z0N2MlNTVU1JazFXN2dJbEo2DQp0c2pjcmoxUjFRaWk2cXpmQkZkK0dXb0EwVjA2aDBlMi9xUlZDZy8vcDZHeXRyVzMzSXljZ3ZTK1pQTEo3dExJDQpGUjJyNjZXZlJscG9QaVNMOGVSdC9QN2trRzBoWENuN0s3dWIyVEV1L0thL1cxeU53YWQ2UFI4aUN3S0JnUUM4DQpYY1pTcnRRc3hBYzh3OTllbUpWb0VvOXdjc0NHSjlsdEEwaVV1OVh5WnB2bGJ5SjNKK3M0OFlyV3hRMHNvcDdMDQpVZ0UrOTZSZm81MWtQTWkzSlZ0azgxcDhudGY0S01yV3dva2FGTVhIc1BjSk1DSjFJQlZJUkxFMEM1ZVpjWWh2DQpseU41N0k0dFQxbHpPWllKeFlLNENvdC96cm43b0YvajZtVEJHZmg0aVFLQmdRQ2lKTVV4UnowMS9jekgvWFNYDQpnbzNkVmJIUTRGRU91ZlduRTNFYjkzUzhyMC9lcTFSTTExOHJiMFRxenVpYWRXMnhZRFU0bnVjV1FscmxtcTBkDQpGWS9tK0h5OTdwcXlrNmptb1U1SS9EK3NzQklvWUhXTG5IOS94ZnZERWsySkdTSlNIdHp1MEQ0RURDL3JnUTQ5DQpNYllzTzVvVXJGOHRQbGhqNXZ6YmYzR0tMQT09DQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tDQo=", + "exponent": 65537, + "privateKey": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDly93AJQ2GWnXC\nXZPK4yCYPcbiKp688MjVoB8UQ9OOZ7avdq8mm+BygHT85lERk+ICMdv6hLEvvuFj\niDF4c896QOe8i9mm9bVyZReVmV/8HdIGQtxsqYDPduHdL3qyKwomASaP0vTKMKyc\n4/eIPgfrXRBGTJindEMGOQBT0cqnJmwcd/pyW+Ixphx06ReQ52YWksB1bfbLk20t\nxCdfxpPnd3vcNS/KDte9xQVyd6JyJLw+DbYy5EO3XTwD8SpSmwaAmHbBM1wYxp9j\n6QK9c+BzQ3PO05lSo3V3ElzGY58dKqsfo62BVLnpRYpHfa+yguAqZxLENxlpmfJD\naEOJmE5pAgMBAAECggEALiL+RKOr0Xu8BOgQ0j1DwA03LxVrhXe6etmJI+JySTcd\ngKENjWziZVrRIi2DvUm5qMMl7WhSwslKK1eexxZJY7xASqSxcEoIwgz17T07/jxm\nfIdUBiUKDZ1Kv8PWmIr3oKW+fkXWi/m1zlIe0qXRpTmsGNEsHQLEqi0rmaiXTXOR\n/2Ldwi6kZR3sWFx97YS4Mx/pueGJTXEai6AVEZzN5Gog6xD8HXR1Rvq+hhd+MocG\nfnU4HgilKRfoJlWd9FOscgSufKG0L3ViO4fSKU46l5aullDYUk5ECMWiwuKSqSE7\nqD45jI3mbOre7S4u3S3TWdD3lzwiXL49LdwKlEC4mQKBgQD0sLr0GH4Wr+QX2xJE\nuA/Cb8QW41l8iSCBTRZZR/sJOd+o3rbcVidlzO/EbZblXG4ZPDmRjgBCGKIP5EZi\n0DsL+Wv32WOo44LpxJGhqExbm0H1iZ1zZ97l0P8fvIhHE42gmaLToOIGDhPSXGvv\nzlqOHbGbq4jsERc1jp1bej5q6wKBgQDwaueIc4pRchH98QYidcyr8Vwg9KhbnfYX\ny3W4RPlZtBdF34iJaio+ASzugo/zy1RTcVrsCskYWXyKDUQz1yu0iCng+fDCUnTm\nXGmEoEGNhk4vTJOt7hBav1/Ja/dUipGf6mXUuanwJ0e+1/Et/B0ah5X1Um5AyNZI\nM+SyRz3u+wKBgQCjvtUNXoqaghCBCmB6TjZ1prexnWkYFugCv2SSUMIk1W7gIlJ6\ntsjcrj1R1Qii6qzfBFd+GWoA0V06h0e2/qRVCg//p6GytrW33IycgvS+ZPLJ7tLI\nFR2r66WfRlpoPiSL8eRt/P7kkG0hXCn7K7ub2TEu/Ka/W1yNwad6PR8iCwKBgQC8\nXcZSrtQsxAc8w99emJVoEo9wcsCGJ9ltA0iUu9XyZpvlbyJ3J+s48YrWxQ0sop7L\nUgE+96Rfo51kPMi3JVtk81p8ntf4KMrWwokaFMXHsPcJMCJ1IBVIRLE0C5eZcYhv\nlyN57I4tT1lzOZYJxYK4Cot/zrn7oF/j6mTBGfh4iQKBgQCiJMUxRz01/czH/XSX\ngo3dVbHQ4FEOufWnE3Eb93S8r0/eq1RM118rb0TqzuiadW2xYDU4nucWQlrlmq0d\nFY/m+Hy97pqyk6jmoU5I/D+ssBIoYHWLnH9/xfvDEk2JGSJSHtzu0D4EDC/rgQ49\nMbYsO5oUrF8tPlhj5vzbf3GKLA==\n-----END PRIVATE KEY-----\n", "publicKeyOld": "BgIAAACkAABSU0ExAAgAAAEAAQBpTpiJQ2hD8plpGTfEEmcq4IKyr31HikXpuVSBraMfqyodn2PGXBJ3daNSmdPOc0Nz4HO9Auljn8YYXDPBdpiABptSKvEDPF23Q+Qytg0+vCRyondyBcW91w7KLzXce3fnk8ZfJ8QtbZPL9m11wJIWZueQF+l0HKYx4lty+nccbCanytFTADkGQ3SnmExGEF3rBz6I9+OcrDDK9NKPJgEmCiuyei/d4XbPgKls3EIG0h38X5mVF2VytfWm2Yu850B6z3N4MYhj4b4vsYT62zEC4pMRUeb8dIBy4Jsmr3avtmeO00MUH6DVyPC8nirixj2YIOPKk13CdVqGDSXA3cvl", "modulusOld": "E5CBDDC0250D865A75C25D93CAE320983DC6E22A9EBCF0C8D5A01F1443D38E67B6AF76AF269BE0728074FCE6511193E20231DBFA84B12FBEE16388317873CF7A40E7BC8BD9A6F5B572651795995FFC1DD20642DC6CA980CF76E1DD2F7AB22B0A2601268FD2F4CA30AC9CE3F7883E07EB5D10464C98A7744306390053D1CAA7266C1C77FA725BE231A61C74E91790E7661692C0756DF6CB936D2DC4275FC693E7777BDC352FCA0ED7BDC5057277A27224BC3E0DB632E443B75D3C03F12A529B06809876C1335C18C69F63E902BD73E0734373CED39952A37577125CC6639F1D2AAB1FA3AD8154B9E9458A477DAFB282E02A6712C437196999F243684389984E69", - "exponentOld": "65537", - "privateKeyOld": "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tDQpNSUlFdndJQkFEQU5CZ2txaGtpRzl3MEJBUUVGQUFTQ0JLa3dnZ1NsQWdFQUFvSUJBUURseTkzQUpRMkdXblhDDQpYWlBLNHlDWVBjYmlLcDY4OE1qVm9COFVROU9PWjdhdmRxOG1tK0J5Z0hUODVsRVJrK0lDTWR2NmhMRXZ2dUZqDQppREY0Yzg5NlFPZThpOW1tOWJWeVpSZVZtVi84SGRJR1F0eHNxWURQZHVIZEwzcXlLd29tQVNhUDB2VEtNS3ljDQo0L2VJUGdmclhSQkdUSmluZEVNR09RQlQwY3FuSm13Y2QvcHlXK0l4cGh4MDZSZVE1MllXa3NCMWJmYkxrMjB0DQp4Q2RmeHBQbmQzdmNOUy9LRHRlOXhRVnlkNkp5Skx3K0RiWXk1RU8zWFR3RDhTcFNtd2FBbUhiQk0xd1l4cDlqDQo2UUs5YytCelEzUE8wNWxTbzNWM0VsekdZNThkS3FzZm82MkJWTG5wUllwSGZhK3lndUFxWnhMRU54bHBtZkpEDQphRU9KbUU1cEFnTUJBQUVDZ2dFQUxpTCtSS09yMFh1OEJPZ1EwajFEd0EwM0x4VnJoWGU2ZXRtSkkrSnlTVGNkDQpnS0VOald6aVpWclJJaTJEdlVtNXFNTWw3V2hTd3NsS0sxZWV4eFpKWTd4QVNxU3hjRW9Jd2d6MTdUMDcvanhtDQpmSWRVQmlVS0RaMUt2OFBXbUlyM29LVytma1hXaS9tMXpsSWUwcVhScFRtc0dORXNIUUxFcWkwcm1haVhUWE9SDQovMkxkd2k2a1pSM3NXRng5N1lTNE14L3B1ZUdKVFhFYWk2QVZFWnpONUdvZzZ4RDhIWFIxUnZxK2hoZCtNb2NHDQpmblU0SGdpbEtSZm9KbFdkOUZPc2NnU3VmS0cwTDNWaU80ZlNLVTQ2bDVhdWxsRFlVazVFQ01XaXd1S1NxU0U3DQpxRDQ1akkzbWJPcmU3UzR1M1MzVFdkRDNsendpWEw0OUxkd0tsRUM0bVFLQmdRRDBzTHIwR0g0V3IrUVgyeEpFDQp1QS9DYjhRVzQxbDhpU0NCVFJaWlIvc0pPZCtvM3JiY1ZpZGx6Ty9FYlpibFhHNFpQRG1SamdCQ0dLSVA1RVppDQowRHNMK1d2MzJXT280NExweEpHaHFFeGJtMEgxaVoxelo5N2wwUDhmdkloSEU0MmdtYUxUb09JR0RoUFNYR3Z2DQp6bHFPSGJHYnE0anNFUmMxanAxYmVqNXE2d0tCZ1FEd2F1ZUljNHBSY2hIOThRWWlkY3lyOFZ3ZzlLaGJuZllYDQp5M1c0UlBsWnRCZEYzNGlKYWlvK0FTenVnby96eTFSVGNWcnNDc2tZV1h5S0RVUXoxeXUwaUNuZytmRENVblRtDQpYR21Fb0VHTmhrNHZUSk90N2hCYXYxL0phL2RVaXBHZjZtWFV1YW53SjBlKzEvRXQvQjBhaDVYMVVtNUF5TlpJDQpNK1N5UnozdSt3S0JnUUNqdnRVTlhvcWFnaENCQ21CNlRqWjFwcmV4bldrWUZ1Z0N2MlNTVU1JazFXN2dJbEo2DQp0c2pjcmoxUjFRaWk2cXpmQkZkK0dXb0EwVjA2aDBlMi9xUlZDZy8vcDZHeXRyVzMzSXljZ3ZTK1pQTEo3dExJDQpGUjJyNjZXZlJscG9QaVNMOGVSdC9QN2trRzBoWENuN0s3dWIyVEV1L0thL1cxeU53YWQ2UFI4aUN3S0JnUUM4DQpYY1pTcnRRc3hBYzh3OTllbUpWb0VvOXdjc0NHSjlsdEEwaVV1OVh5WnB2bGJ5SjNKK3M0OFlyV3hRMHNvcDdMDQpVZ0UrOTZSZm81MWtQTWkzSlZ0azgxcDhudGY0S01yV3dva2FGTVhIc1BjSk1DSjFJQlZJUkxFMEM1ZVpjWWh2DQpseU41N0k0dFQxbHpPWllKeFlLNENvdC96cm43b0YvajZtVEJHZmg0aVFLQmdRQ2lKTVV4UnowMS9jekgvWFNYDQpnbzNkVmJIUTRGRU91ZlduRTNFYjkzUzhyMC9lcTFSTTExOHJiMFRxenVpYWRXMnhZRFU0bnVjV1FscmxtcTBkDQpGWS9tK0h5OTdwcXlrNmptb1U1SS9EK3NzQklvWUhXTG5IOS94ZnZERWsySkdTSlNIdHp1MEQ0RURDL3JnUTQ5DQpNYllzTzVvVXJGOHRQbGhqNXZ6YmYzR0tMQT09DQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tDQo=", + "exponentOld": 65537, + "privateKeyOld": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDly93AJQ2GWnXC\nXZPK4yCYPcbiKp688MjVoB8UQ9OOZ7avdq8mm+BygHT85lERk+ICMdv6hLEvvuFj\niDF4c896QOe8i9mm9bVyZReVmV/8HdIGQtxsqYDPduHdL3qyKwomASaP0vTKMKyc\n4/eIPgfrXRBGTJindEMGOQBT0cqnJmwcd/pyW+Ixphx06ReQ52YWksB1bfbLk20t\nxCdfxpPnd3vcNS/KDte9xQVyd6JyJLw+DbYy5EO3XTwD8SpSmwaAmHbBM1wYxp9j\n6QK9c+BzQ3PO05lSo3V3ElzGY58dKqsfo62BVLnpRYpHfa+yguAqZxLENxlpmfJD\naEOJmE5pAgMBAAECggEALiL+RKOr0Xu8BOgQ0j1DwA03LxVrhXe6etmJI+JySTcd\ngKENjWziZVrRIi2DvUm5qMMl7WhSwslKK1eexxZJY7xASqSxcEoIwgz17T07/jxm\nfIdUBiUKDZ1Kv8PWmIr3oKW+fkXWi/m1zlIe0qXRpTmsGNEsHQLEqi0rmaiXTXOR\n/2Ldwi6kZR3sWFx97YS4Mx/pueGJTXEai6AVEZzN5Gog6xD8HXR1Rvq+hhd+MocG\nfnU4HgilKRfoJlWd9FOscgSufKG0L3ViO4fSKU46l5aullDYUk5ECMWiwuKSqSE7\nqD45jI3mbOre7S4u3S3TWdD3lzwiXL49LdwKlEC4mQKBgQD0sLr0GH4Wr+QX2xJE\nuA/Cb8QW41l8iSCBTRZZR/sJOd+o3rbcVidlzO/EbZblXG4ZPDmRjgBCGKIP5EZi\n0DsL+Wv32WOo44LpxJGhqExbm0H1iZ1zZ97l0P8fvIhHE42gmaLToOIGDhPSXGvv\nzlqOHbGbq4jsERc1jp1bej5q6wKBgQDwaueIc4pRchH98QYidcyr8Vwg9KhbnfYX\ny3W4RPlZtBdF34iJaio+ASzugo/zy1RTcVrsCskYWXyKDUQz1yu0iCng+fDCUnTm\nXGmEoEGNhk4vTJOt7hBav1/Ja/dUipGf6mXUuanwJ0e+1/Et/B0ah5X1Um5AyNZI\nM+SyRz3u+wKBgQCjvtUNXoqaghCBCmB6TjZ1prexnWkYFugCv2SSUMIk1W7gIlJ6\ntsjcrj1R1Qii6qzfBFd+GWoA0V06h0e2/qRVCg//p6GytrW33IycgvS+ZPLJ7tLI\nFR2r66WfRlpoPiSL8eRt/P7kkG0hXCn7K7ub2TEu/Ka/W1yNwad6PR8iCwKBgQC8\nXcZSrtQsxAc8w99emJVoEo9wcsCGJ9ltA0iUu9XyZpvlbyJ3J+s48YrWxQ0sop7L\nUgE+96Rfo51kPMi3JVtk81p8ntf4KMrWwokaFMXHsPcJMCJ1IBVIRLE0C5eZcYhv\nlyN57I4tT1lzOZYJxYK4Cot/zrn7oF/j6mTBGfh4iQKBgQCiJMUxRz01/czH/XSX\ngo3dVbHQ4FEOufWnE3Eb93S8r0/eq1RM118rb0TqzuiadW2xYDU4nucWQlrlmq0d\nFY/m+Hy97pqyk6jmoU5I/D+ssBIoYHWLnH9/xfvDEk2JGSJSHtzu0D4EDC/rgQ49\nMbYsO5oUrF8tPlhj5vzbf3GKLA==\n-----END PRIVATE KEY-----\n", "refreshLockInterval": "10m", "dummy" : { "enable": false, diff --git a/DocService/sources/wopiClient.js b/DocService/sources/wopiClient.js index 628a8f0d..6adc83b8 100644 --- a/DocService/sources/wopiClient.js +++ b/DocService/sources/wopiClient.js @@ -278,8 +278,8 @@ function discovery(req, res) { //end section for collabora nexcloud connectors let xmlDiscovery = xmlZone.up(); if (tenWopiPublicKeyOld && tenWopiPublicKey) { - let exponent = numberToBase64(tenWopiExponent - 0); - let exponentOld = numberToBase64(tenWopiExponentOld - 0); + let exponent = numberToBase64(tenWopiExponent); + let exponentOld = numberToBase64(tenWopiExponentOld); xmlDiscovery.ele('proof-key', { oldvalue: tenWopiPublicKeyOld, oldmodulus: tenWopiModulusOld, oldexponent: exponentOld, value: tenWopiPublicKey, modulus: tenWopiModulus, exponent: exponent @@ -920,8 +920,8 @@ async function fillStandardHeaders(ctx, headers, url, access_token) { const tenWopiPrivateKey = ctx.getCfg('wopi.privateKey', cfgWopiPrivateKey); const tenWopiPrivateKeyOld = ctx.getCfg('wopi.privateKeyOld', cfgWopiPrivateKeyOld); if (tenWopiPrivateKey && tenWopiPrivateKeyOld) { - headers['X-WOPI-Proof'] = await generateProofSign(url, access_token, timeStamp, Buffer.from(tenWopiPrivateKey, 'base64')); - headers['X-WOPI-ProofOld'] = await generateProofSign(url, access_token, timeStamp, Buffer.from(tenWopiPrivateKeyOld, 'base64')); + headers['X-WOPI-Proof'] = await generateProofSign(url, access_token, timeStamp, tenWopiPrivateKey); + headers['X-WOPI-ProofOld'] = await generateProofSign(url, access_token, timeStamp, tenWopiPrivateKeyOld); headers['X-WOPI-TimeStamp'] = timeStamp; headers['X-WOPI-ClientVersion'] = commonDefines.buildVersion + '.' + commonDefines.buildNumber; // todo From fd3722afa75e9f34ae55b028ac03c9166b9e908d Mon Sep 17 00:00:00 2001 From: Sergey Konovalov Date: Wed, 5 Jun 2024 01:57:27 +0300 Subject: [PATCH 02/13] [bug] Fix bug with wopi checkFileInfo on one shard and websocket on other shard --- DocService/sources/DocsCoServer.js | 55 ++++++++++++++++-------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/DocService/sources/DocsCoServer.js b/DocService/sources/DocsCoServer.js index d001a3a8..6938fbce 100644 --- a/DocService/sources/DocsCoServer.js +++ b/DocService/sources/DocsCoServer.js @@ -2494,13 +2494,37 @@ exports.install = function(server, callbackFunction) { let docId = data.docid; const user = data.user; - let wopiParams = null; + let wopiParams = null, openedAtStr; if (data.documentCallbackUrl) { wopiParams = wopiClient.parseWopiCallback(ctx, data.documentCallbackUrl); if (wopiParams && wopiParams.userAuth) { conn.access_token_ttl = wopiParams.userAuth.access_token_ttl; } } + let cmd = null; + if (data.openCmd) { + cmd = new commonDefines.InputCommand(data.openCmd); + cmd.setDocId(docId); + if (isDecoded) { + cmd.setWithAuthorization(true); + } + } + //todo minimize select calls on opening + let result = yield taskResult.select(ctx, docId); + let resultRow = result.length > 0 ? result[0] : null; + if (wopiParams) { + let wopiParamsFull; + if (resultRow && resultRow.callback) { + wopiParamsFull = wopiClient.parseWopiCallback(ctx, data.documentCallbackUrl, resultRow.callback); + cmd?.setWopiParams(wopiParamsFull); + } + if (!wopiParamsFull || !wopiParamsFull.userAuth || !wopiParamsFull.commonInfo) { + ctx.logger.warn('invalid wopi callback (maybe postgres<9.5) %j', wopiParams); + sendDataDisconnectReason(ctx, conn, constants.DROP_CODE, constants.DROP_REASON); + conn.disconnect(true); + return; + } + } //get user index const bIsRestore = null != data.sessionId; let upsertRes = null; @@ -2535,7 +2559,8 @@ exports.install = function(server, callbackFunction) { task.key = docId; if (undefined !== data.timezoneOffset) { //todo duplicate created_at because CURRENT_TIMESTAMP uses server timezone - task.additional = sqlBase.DocumentAdditional.prototype.setOpenedAt(Date.now(), data.timezoneOffset); + openedAtStr = sqlBase.DocumentAdditional.prototype.setOpenedAt(Date.now(), data.timezoneOffset); + task.additional = openedAtStr; } if (ctx.shardKey) { task.additional += sqlBase.DocumentAdditional.prototype.setShardKey(ctx.shardKey); @@ -2619,15 +2644,6 @@ exports.install = function(server, callbackFunction) { } } - let cmd = null; - if (data.openCmd) { - cmd = new commonDefines.InputCommand(data.openCmd); - cmd.fillFromConnection(conn); - if (isDecoded) { - cmd.setWithAuthorization(true); - } - } - // Situation when the user is already disabled from co-authoring if (bIsRestore && data.isCloseCoAuthoring) { conn.sessionId = data.sessionId;//restore old @@ -2648,20 +2664,6 @@ exports.install = function(server, callbackFunction) { } return; } - let result = yield taskResult.select(ctx, docId); - let resultRow = result.length > 0 ? result[0] : null; - if (cmd && resultRow && resultRow.callback) { - let userAuthStr = sqlBase.UserCallback.prototype.getCallbackByUserIndex(ctx, resultRow.callback, curIndexUser); - let wopiParams = wopiClient.parseWopiCallback(ctx, userAuthStr, resultRow.callback); - cmd.setWopiParams(wopiParams); - if (wopiParams) { - documentCallback = null; - if (!wopiParams.userAuth || !wopiParams.commonInfo) { - yield* sendFileErrorAuth(ctx, conn, data.sessionId, `invalid wopi callback (maybe postgres<9.5) ${JSON.stringify(wopiParams)}`); - return; - } - } - } if (conn.user.idOriginal.length > constants.USER_ID_MAX_LENGTH) { //todo refactor DB and remove restrictions ctx.logger.warn('auth user id too long actual = %s; max = %s', curUserIdOriginal.length, constants.USER_ID_MAX_LENGTH); @@ -2758,7 +2760,8 @@ exports.install = function(server, callbackFunction) { } } else { conn.sessionId = conn.id; - const endAuthRes = yield* endAuth(ctx, conn, false, documentCallback, canvasService.getOpenedAt(resultRow)); + let openedAt = openedAtStr ? sqlBase.DocumentAdditional.prototype.getOpenedAt(openedAtStr) : canvasService.getOpenedAt(resultRow); + const endAuthRes = yield* endAuth(ctx, conn, false, documentCallback, openedAt); if (endAuthRes && cmd) { yield canvasService.openDocument(ctx, conn, cmd, upsertRes, bIsRestore); } From 0b6768cb7099b68d9e14ce3167de9b61edf05a1b Mon Sep 17 00:00:00 2001 From: Sergey Konovalov Date: Wed, 5 Jun 2024 15:39:53 +0300 Subject: [PATCH 03/13] [bug] Fix bug with LastModifiedTime changing after putFile(nextcloud) --- DocService/sources/canvasservice.js | 18 ++---- DocService/sources/wopiClient.js | 85 ++++++++++++++++++++--------- 2 files changed, 64 insertions(+), 39 deletions(-) diff --git a/DocService/sources/canvasservice.js b/DocService/sources/canvasservice.js index 594a730d..034b4e66 100644 --- a/DocService/sources/canvasservice.js +++ b/DocService/sources/canvasservice.js @@ -1234,19 +1234,13 @@ function* processWopiPutFile(ctx, docId, wopiParams, savePathDoc, userLastChange let streamObj = yield storage.createReadStream(ctx, savePathDoc); let postRes = yield wopiClient.putFile(ctx, wopiParams, null, streamObj.readStream, metadata.ContentLength, userLastChangeId, isModifiedByUser, isAutosave, isExitSave); if (postRes) { - if (postRes.body) { - try { - let body = JSON.parse(postRes.body); - //collabora nexcloud connector - if (body.LastModifiedTime) { - let lastModifiedTimeInfo = wopiClient.getWopiModifiedMarker(wopiParams, body.LastModifiedTime); - yield commandOpenStartPromise(ctx, docId, undefined, lastModifiedTimeInfo); - } - } catch (e) { - ctx.logger.debug('processWopiPutFile error: %s', e.stack); - } - } res = '{"error": 0}'; + let body = wopiClient.parsePutFileResponse(ctx, postRes); + //collabora nexcloud connector + if (body?.LastModifiedTime) { + let lastModifiedTimeInfo = wopiClient.getWopiModifiedMarker(wopiParams, body.LastModifiedTime); + yield commandOpenStartPromise(ctx, docId, undefined, lastModifiedTimeInfo); + } } return res; } diff --git a/DocService/sources/wopiClient.js b/DocService/sources/wopiClient.js index 6adc83b8..145d8bdf 100644 --- a/DocService/sources/wopiClient.js +++ b/DocService/sources/wopiClient.js @@ -447,6 +447,59 @@ function checkAndInvalidateCache(ctx, docId, fileInfo) { return res; }); } +function parsePutFileResponse(ctx, postRes) { + let body = null + if (postRes.body) { + try { + //collabora nexcloud connector + body = JSON.parse(postRes.body); + } catch (e) { + ctx.logger.debug('wopi PutFile body parse error: %s', e.stack); + } + } + return body; +} +async function checkAndReplaceEmptyFile(ctx, fileInfo, wopiSrc, access_token, access_token_ttl, lang, ui, fileType) { + // TODO: throw error if format not supported? + if (fileInfo.Size === 0 && fileType.length !== 0) { + const tenNewFileTemplate = ctx.getCfg('services.CoAuthoring.server.newFileTemplate', cfgNewFileTemplate); + + //Create new files using Office for the web + const wopiParams = getWopiParams(undefined, fileInfo, wopiSrc, access_token, access_token_ttl); + + if (templatesFolderLocalesCache === null) { + const dirContent = await readdir(`${tenNewFileTemplate}/`, {withFileTypes: true}); + templatesFolderLocalesCache = dirContent.filter(dirObject => dirObject.isDirectory()) + .map(dirObject => dirObject.name); + } + + const localePrefix = lang || ui || 'en'; + let locale = constants.TEMPLATES_FOLDER_LOCALE_COLLISON_MAP[localePrefix] ?? + templatesFolderLocalesCache.find(locale => locale.startsWith(localePrefix)); + if (locale === undefined) { + locale = constants.TEMPLATES_DEFAULT_LOCALE; + } + + const filePath = `${tenNewFileTemplate}/${locale}/new.${fileType}`; + if (!templateFilesSizeCache[filePath]) { + templateFilesSizeCache[filePath] = await lstat(filePath); + } + + const templateFileInfo = templateFilesSizeCache[filePath]; + const templateFileStream = createReadStream(filePath); + let postRes = await putFile(ctx, wopiParams, undefined, templateFileStream, templateFileInfo.size, fileInfo.UserId, false, false, false); + if (postRes) { + //update Size + fileInfo.Size = templateFileInfo.size; + let body = parsePutFileResponse(ctx, postRes); + //collabora nexcloud connector + if (body?.LastModifiedTime) { + //update LastModifiedTime + fileInfo.LastModifiedTime = body.LastModifiedTime; + } + } + } +} function getEditorHtml(req, res) { return co(function*() { let params = {key: undefined, fileInfo: {}, userAuth: {}, queryParams: req.query, token: undefined, documentType: undefined}; @@ -454,7 +507,6 @@ function getEditorHtml(req, res) { try { ctx.initFromRequest(req); yield ctx.initTenantCache(); - const tenNewFileTemplate = ctx.getCfg('services.CoAuthoring.server.newFileTemplate', cfgNewFileTemplate); const tenTokenEnableBrowser = ctx.getCfg('services.CoAuthoring.token.enable.browser', cfgTokenEnableBrowser); const tenTokenOutboxAlgorithm = ctx.getCfg('services.CoAuthoring.token.outbox.algorithm', cfgTokenOutboxAlgorithm); const tenTokenOutboxExpires = ctx.getCfg('services.CoAuthoring.token.outbox.expires', cfgTokenOutboxExpires); @@ -483,6 +535,10 @@ function getEditorHtml(req, res) { params.fileInfo = {}; return; } + const fileType = getFileTypeByInfo(fileInfo); + if (!shutdownFlag) { + yield checkAndReplaceEmptyFile(ctx, fileInfo, wopiSrc, access_token, access_token_ttl, lang, ui, fileType); + } if (!fileInfo.UserCanWrite) { mode = 'view'; @@ -518,38 +574,12 @@ function getEditorHtml(req, res) { } if (!shutdownFlag) { //save common info - const fileType = getFileTypeByInfo(fileInfo); if (undefined === lockId) { lockId = crypto.randomBytes(16).toString('base64'); let commonInfo = JSON.stringify({lockId: lockId, fileInfo: fileInfo}); yield canvasService.commandOpenStartPromise(ctx, docId, utils.getBaseUrlByRequest(ctx, req), commonInfo, fileType); } - // TODO: throw error if format not supported? - if (fileInfo.Size === 0 && fileType.length !== 0) { - const wopiParams = getWopiParams(undefined, fileInfo, wopiSrc, access_token, access_token_ttl); - - if (templatesFolderLocalesCache === null) { - const dirContent = yield readdir(`${tenNewFileTemplate}/`, { withFileTypes: true }); - templatesFolderLocalesCache = dirContent.filter(dirObject => dirObject.isDirectory()).map(dirObject => dirObject.name); - } - - const localePrefix = lang || ui || 'en'; - let locale = constants.TEMPLATES_FOLDER_LOCALE_COLLISON_MAP[localePrefix] ?? templatesFolderLocalesCache.find(locale => locale.startsWith(localePrefix)); - if (locale === undefined) { - locale = constants.TEMPLATES_DEFAULT_LOCALE; - } - - const filePath = `${tenNewFileTemplate}/${locale}/new.${fileType}`; - if (!templateFilesSizeCache[filePath]) { - templateFilesSizeCache[filePath] = yield lstat(filePath); - } - - const templateFileInfo = templateFilesSizeCache[filePath]; - const templateFileStream = createReadStream(filePath); - yield putFile(ctx, wopiParams, undefined, templateFileStream, templateFileInfo.size, fileInfo.UserId, false, false, false); - } - //Lock if ('view' !== mode) { let lockRes = yield lock(ctx, 'LOCK', lockId, fileInfo, userAuth); @@ -1030,6 +1060,7 @@ exports.parseWopiCallback = parseWopiCallback; exports.getEditorHtml = getEditorHtml; exports.getConverterHtml = getConverterHtml; exports.putFile = putFile; +exports.parsePutFileResponse = parsePutFileResponse; exports.putRelativeFile = putRelativeFile; exports.renameFile = renameFile; exports.lock = lock; From 1c274edd9b4f6431b179cb4a5ca7285f0c32ca80 Mon Sep 17 00:00:00 2001 From: Sergey Konovalov Date: Thu, 6 Jun 2024 15:30:43 +0300 Subject: [PATCH 04/13] [feature] Add docs_api_config input param for wopi; For bug 58764 --- DocService/sources/wopiClient.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/DocService/sources/wopiClient.js b/DocService/sources/wopiClient.js index 145d8bdf..d08e8bca 100644 --- a/DocService/sources/wopiClient.js +++ b/DocService/sources/wopiClient.js @@ -502,7 +502,7 @@ async function checkAndReplaceEmptyFile(ctx, fileInfo, wopiSrc, access_token, ac } function getEditorHtml(req, res) { return co(function*() { - let params = {key: undefined, fileInfo: {}, userAuth: {}, queryParams: req.query, token: undefined, documentType: undefined}; + let params = {key: undefined, fileInfo: {}, userAuth: {}, queryParams: req.query, token: undefined, documentType: undefined, docs_api_config: {}}; let ctx = new operationContext.Context(); try { ctx.initFromRequest(req); @@ -528,6 +528,10 @@ function getEditorHtml(req, res) { let ui = req.query['ui']; let access_token = req.body['access_token'] || ""; let access_token_ttl = parseInt(req.body['access_token_ttl']) || 0; + let docs_api_config = req.body['docs_api_config']; + if (docs_api_config) { + params.docs_api_config = JSON.parse(docs_api_config); + } let fileInfo = params.fileInfo = yield checkFileInfo(ctx, wopiSrc, access_token, sc); From 2a2efaf1ca6be83a322ca2f551c010db98e498ba Mon Sep 17 00:00:00 2001 From: Sergey Konovalov Date: Fri, 7 Jun 2024 19:28:52 +0300 Subject: [PATCH 05/13] [bug] Check result of wopi unlock; Fix bug 68424 --- DocService/sources/DocsCoServer.js | 4 +- DocService/sources/wopiClient.js | 59 +++++++++++++++--------------- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/DocService/sources/DocsCoServer.js b/DocService/sources/DocsCoServer.js index 6938fbce..54875406 100644 --- a/DocService/sources/DocsCoServer.js +++ b/DocService/sources/DocsCoServer.js @@ -1300,9 +1300,9 @@ let unlockWopiDoc = co.wrap(function*(ctx, docId, opt_userIndex) { //wopi unlock var getRes = yield getCallback(ctx, docId, opt_userIndex); if (getRes && getRes.wopiParams && getRes.wopiParams.userAuth && 'view' !== getRes.wopiParams.userAuth.mode) { - yield wopiClient.unlock(ctx, getRes.wopiParams); + let unlockRes = yield wopiClient.unlock(ctx, getRes.wopiParams); let unlockInfo = wopiClient.getWopiUnlockMarker(getRes.wopiParams); - if (unlockInfo) { + if (unlockInfo && unlockRes) { yield canvasService.commandOpenStartPromise(ctx, docId, undefined, unlockInfo); } } diff --git a/DocService/sources/wopiClient.js b/DocService/sources/wopiClient.js index d08e8bca..e0ff2076 100644 --- a/DocService/sources/wopiClient.js +++ b/DocService/sources/wopiClient.js @@ -876,40 +876,41 @@ function lock(ctx, command, lockId, fileInfo, userAuth) { return res; }); } -function unlock(ctx, wopiParams) { - return co(function* () { - try { - ctx.logger.info('wopi Unlock start'); - const tenCallbackRequestTimeout = ctx.getCfg('services.CoAuthoring.server.callbackRequestTimeout', cfgCallbackRequestTimeout); +async function unlock(ctx, wopiParams) { + let res = false; + try { + ctx.logger.info('wopi Unlock start'); + const tenCallbackRequestTimeout = ctx.getCfg('services.CoAuthoring.server.callbackRequestTimeout', cfgCallbackRequestTimeout); - if (!wopiParams.userAuth || !wopiParams.commonInfo) { + if (!wopiParams.userAuth || !wopiParams.commonInfo) { + return; + } + let fileInfo = wopiParams.commonInfo.fileInfo; + if (fileInfo && fileInfo.SupportsLocks) { + let wopiSrc = wopiParams.userAuth.wopiSrc; + let lockId = wopiParams.commonInfo.lockId; + let access_token = wopiParams.userAuth.access_token; + let uri = `${wopiSrc}?access_token=${access_token}`; + let filterStatus = await checkIpFilter(ctx, uri); + if (0 !== filterStatus) { return; } - let fileInfo = wopiParams.commonInfo.fileInfo; - if (fileInfo && fileInfo.SupportsLocks) { - let wopiSrc = wopiParams.userAuth.wopiSrc; - let lockId = wopiParams.commonInfo.lockId; - let access_token = wopiParams.userAuth.access_token; - let uri = `${wopiSrc}?access_token=${access_token}`; - let filterStatus = yield checkIpFilter(ctx, uri); - if (0 !== filterStatus) { - return; - } - let headers = {"X-WOPI-Override": "UNLOCK", "X-WOPI-Lock": lockId}; - yield fillStandardHeaders(ctx, headers, uri, access_token); - ctx.logger.debug('wopi Unlock request uri=%s headers=%j', uri, headers); - let postRes = yield utils.postRequestPromise(ctx, uri, undefined, undefined, undefined, tenCallbackRequestTimeout, undefined, headers); - ctx.logger.debug('wopi Unlock response headers=%j', postRes.response.headers); - } else { - ctx.logger.info('wopi SupportsLocks = false'); - } - } catch (err) { - ctx.logger.error('wopi error Unlock:%s', err.stack); - } finally { - ctx.logger.info('wopi Unlock end'); + let headers = {"X-WOPI-Override": "UNLOCK", "X-WOPI-Lock": lockId}; + await fillStandardHeaders(ctx, headers, uri, access_token); + ctx.logger.debug('wopi Unlock request uri=%s headers=%j', uri, headers); + let postRes = await utils.postRequestPromise(ctx, uri, undefined, undefined, undefined, tenCallbackRequestTimeout, undefined, headers); + ctx.logger.debug('wopi Unlock response headers=%j', postRes.response.headers); + res = true; + } else { + ctx.logger.info('wopi SupportsLocks = false'); } - }); + } catch (err) { + ctx.logger.error('wopi error Unlock:%s', err.stack); + } finally { + ctx.logger.info('wopi Unlock end'); + } + return res; } function generateProofBuffer(url, accessToken, timeStamp) { const accessTokenBytes = Buffer.from(accessToken, 'utf8'); From d644a44be74b81629e98e9142202a3bce7ea48c2 Mon Sep 17 00:00:00 2001 From: Sergey Konovalov Date: Mon, 10 Jun 2024 12:10:43 +0300 Subject: [PATCH 06/13] [bug] Add stubs for shardkey params until integrators pass these parameters to all requests --- Common/sources/storage-fs.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Common/sources/storage-fs.js b/Common/sources/storage-fs.js index 4729b2b8..3257dc2f 100644 --- a/Common/sources/storage-fs.js +++ b/Common/sources/storage-fs.js @@ -44,6 +44,10 @@ const constants = require('./../../Common/sources/constants'); const cfgExpSessionAbsolute = ms(config.get('services.CoAuthoring.expire.sessionabsolute')); +//Stubs are needed until integrators pass these parameters to all requests +let shardKeyCached; +let wopiSrcCached; + function getFilePath(storageCfg, strPath) { const storageFolderPath = storageCfg.fs.folderPath; return path.join(storageFolderPath, strPath); @@ -142,10 +146,15 @@ async function getSignedUrl(ctx, storageCfg, baseUrl, strPath, urlType, optFilen url += '?md5=' + encodeURIComponent(md5); url += '&expires=' + encodeURIComponent(expires); if (ctx.shardKey) { + shardKeyCached = ctx.shardKey; url += `&${constants.SHARD_KEY_API_NAME}=${encodeURIComponent(ctx.shardKey)}`; - } - if (ctx.wopiSrc) { + } else if (ctx.wopiSrc) { + wopiSrcCached = ctx.wopiSrc; url += `&${constants.SHARD_KEY_WOPI_NAME}=${encodeURIComponent(ctx.wopiSrc)}`; + } else if (shardKeyCached) { + url += `&${constants.SHARD_KEY_API_NAME}=${encodeURIComponent(shardKeyCached)}`; + } else if (wopiSrcCached) { + url += `&${constants.SHARD_KEY_WOPI_NAME}=${encodeURIComponent(wopiSrcCached)}`; } url += '&filename=' + userFriendlyName; return url; From 96a5d76fb7e9c68961f0c55744fceaa5718562e4 Mon Sep 17 00:00:00 2001 From: Sergey Konovalov Date: Mon, 10 Jun 2024 17:59:01 +0300 Subject: [PATCH 07/13] [bug] Use deterministic(not random) lockId to fix issues with forgotten openings due to integrator failures; For bug 68424 --- DocService/sources/wopiClient.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DocService/sources/wopiClient.js b/DocService/sources/wopiClient.js index e0ff2076..7d985b64 100644 --- a/DocService/sources/wopiClient.js +++ b/DocService/sources/wopiClient.js @@ -579,7 +579,8 @@ function getEditorHtml(req, res) { if (!shutdownFlag) { //save common info if (undefined === lockId) { - lockId = crypto.randomBytes(16).toString('base64'); + //Use deterministic(not random) lockId to fix issues with forgotten openings due to integrator failures + lockId = docId; let commonInfo = JSON.stringify({lockId: lockId, fileInfo: fileInfo}); yield canvasService.commandOpenStartPromise(ctx, docId, utils.getBaseUrlByRequest(ctx, req), commonInfo, fileType); } From bf8ff4daa84bd635cb2b98b4235449900d072dd1 Mon Sep 17 00:00:00 2001 From: Sergey Konovalov Date: Tue, 11 Jun 2024 19:18:00 +0300 Subject: [PATCH 08/13] [bug] Add storage test with tenant; Fix bug 68563 --- Common/sources/storage-base.js | 6 ++--- Common/sources/tenantManager.js | 8 ++++++- .../withServerInstance/storage.tests.js | 23 ++++++++++++++++--- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/Common/sources/storage-base.js b/Common/sources/storage-base.js index c6665a90..9f2228b8 100644 --- a/Common/sources/storage-base.js +++ b/Common/sources/storage-base.js @@ -65,17 +65,17 @@ function isDiffrentPersistentStorage() { async function headObject(ctx, strPath, opt_specialDir) { let storage = getStorage(opt_specialDir); let storageCfg = getStorageCfg(ctx, opt_specialDir); - return await storage.headObject(storageCfg, getStoragePath(storageCfg, strPath, opt_specialDir)); + return await storage.headObject(storageCfg, getStoragePath(ctx, strPath, opt_specialDir)); } async function getObject(ctx, strPath, opt_specialDir) { let storage = getStorage(opt_specialDir); let storageCfg = getStorageCfg(ctx, opt_specialDir); - return await storage.getObject(storageCfg, getStoragePath(storageCfg, strPath, opt_specialDir)); + return await storage.getObject(storageCfg, getStoragePath(ctx, strPath, opt_specialDir)); } async function createReadStream(ctx, strPath, opt_specialDir) { let storage = getStorage(opt_specialDir); let storageCfg = getStorageCfg(ctx, opt_specialDir); - return await storage.createReadStream(storageCfg, getStoragePath(storageCfg, strPath, opt_specialDir)); + return await storage.createReadStream(storageCfg, getStoragePath(ctx, strPath, opt_specialDir)); } async function putObject(ctx, strPath, buffer, contentLength, opt_specialDir) { let storage = getStorage(opt_specialDir); diff --git a/Common/sources/tenantManager.js b/Common/sources/tenantManager.js index 58c24aa8..51eb5aed 100644 --- a/Common/sources/tenantManager.js +++ b/Common/sources/tenantManager.js @@ -262,8 +262,13 @@ function getTenantLicense(ctx) { function getServerLicense(ctx) { return licenseInfo; } +let hasBaseDir = !!cfgTenantsBaseDir; function isMultitenantMode(ctx) { - return !!cfgTenantsBaseDir; + return hasBaseDir; +} +function setMultitenantMode(val) { + //for tests only!! + return hasBaseDir = val; } function isDefaultTenant(ctx) { return ctx.tenant === cfgTenantsDefaultTenant; @@ -405,4 +410,5 @@ exports.getTenantLicense = getTenantLicense; exports.getServerLicense = getServerLicense; exports.setDefLicense = setDefLicense; exports.isMultitenantMode = isMultitenantMode; +exports.setMultitenantMode = setMultitenantMode; exports.isDefaultTenant = isDefaultTenant; diff --git a/tests/integration/withServerInstance/storage.tests.js b/tests/integration/withServerInstance/storage.tests.js index 5477f03c..b86b3bea 100644 --- a/tests/integration/withServerInstance/storage.tests.js +++ b/tests/integration/withServerInstance/storage.tests.js @@ -16,6 +16,7 @@ jest.mock("fs/promises", () => ({ const { cp } = require('fs/promises'); const operationContext = require('../../../Common/sources/operationContext'); +const tenantManager = require('../../../Common/sources/tenantManager'); const storage = require('../../../Common/sources/storage-base'); const utils = require('../../../Common/sources/utils'); const commonDefines = require("../../../Common/sources/commondefines"); @@ -52,8 +53,12 @@ function request(url) { }); }); } -function runTestForDir(specialDir) { +function runTestForDir(ctx, isMultitenantMode, specialDir) { + let oldMultitenantMode = tenantManager.isMultitenantMode(); test("start listObjects", async () => { + //todo set in all tests do not rely on test order + tenantManager.setMultitenantMode(isMultitenantMode); + let list = await storage.listObjects(ctx, testDir, specialDir); expect(list).toEqual([]); }); @@ -213,20 +218,32 @@ function runTestForDir(specialDir) { list = await storage.listObjects(ctx, testDir, specialDir); expect(list.sort()).toEqual([].sort()); + + tenantManager.setMultitenantMode(oldMultitenantMode); }); } // Assumed, that server is already up. describe('storage common dir', function () { - runTestForDir(specialDirCache); + runTestForDir(ctx, false, specialDirCache); }); describe('storage forgotten dir', function () { - runTestForDir(specialDirForgotten); + runTestForDir(ctx, false, specialDirForgotten); +}); + +describe('storage common dir with tenants', function () { + runTestForDir(ctx, true, specialDirCache); +}); + +describe('storage forgotten dir with tenants', function () { + runTestForDir(ctx, true, specialDirForgotten); }); describe('storage mix common and forgotten dir', function () { test("putObject", async () => { + tenantManager.setMultitenantMode(false); + let buffer = Buffer.from(testFileData1); let res = await storage.putObject(ctx, testFile1, buffer, buffer.length, specialDirCache); expect(res).toEqual(undefined); From 004bab7daa4d6dd2f00c08496c59bd1d24470b2c Mon Sep 17 00:00:00 2001 From: Sergey Konovalov Date: Thu, 13 Jun 2024 13:28:25 +0300 Subject: [PATCH 09/13] [bug] Fix bug with opening after editing with the condition wopi SupportsLocks=false; Fix bug 68501 --- DocService/sources/wopiClient.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DocService/sources/wopiClient.js b/DocService/sources/wopiClient.js index 7d985b64..f024e764 100644 --- a/DocService/sources/wopiClient.js +++ b/DocService/sources/wopiClient.js @@ -420,7 +420,7 @@ function checkAndInvalidateCache(ctx, docId, fileInfo) { ctx.logger.debug('wopiEditor unlockMarkStr=%s', unlockMarkStr); let hasUnlockMarker = isWopiUnlockMarker(unlockMarkStr); ctx.logger.debug('wopiEditor hasUnlockMarker=%s', hasUnlockMarker); - if (hasUnlockMarker) { + if (hasUnlockMarker || !commonInfo.fileInfo.SupportsLocks) { let fileInfoVersion = fileInfo.Version; let cacheVersion = commonInfo.fileInfo.Version; let fileInfoModified = fileInfo.LastModifiedTime; From 6c8220877d597dfbed59eb1b7bcc7cc5a8e4fbe7 Mon Sep 17 00:00:00 2001 From: Sergey Konovalov Date: Fri, 14 Jun 2024 01:43:32 +0300 Subject: [PATCH 10/13] [bug] Move copyOrigin logic from x2t; Fix bug 68600 --- FileConverter/sources/converter.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/FileConverter/sources/converter.js b/FileConverter/sources/converter.js index fc14c271..436f87f5 100644 --- a/FileConverter/sources/converter.js +++ b/FileConverter/sources/converter.js @@ -91,6 +91,7 @@ var exitCodesReturn = [constants.CONVERT_PARAMS, constants.CONVERT_NEED_PARAMS, var exitCodesMinorError = [constants.CONVERT_NEED_PARAMS, constants.CONVERT_DRM, constants.CONVERT_DRM_UNSUPPORTED, constants.CONVERT_PASSWORD]; var exitCodesUpload = [constants.NO_ERROR, constants.CONVERT_CORRUPTED, constants.CONVERT_NEED_PARAMS, constants.CONVERT_DRM, constants.CONVERT_DRM_UNSUPPORTED]; +var exitCodesCopyOrigin = [constants.CONVERT_NEED_PARAMS, constants.CONVERT_DRM]; let inputLimitsXmlCache; function TaskQueueDataConvert(ctx, task) { @@ -906,6 +907,13 @@ function* postProcess(ctx, cmd, dataConvert, tempDirs, childRes, error, isTimeou ctx.logger.debug('ExitCode (code=%d;signal=%s;error:%d)', exitCode, exitSignal, error); } if (-1 !== exitCodesUpload.indexOf(error)) { + if (-1 !== exitCodesCopyOrigin.indexOf(error)) { + let originPath = path.join(path.dirname(dataConvert.fileTo), "origin" + path.extname(dataConvert.fileFrom)); + if (!fs.existsSync(dataConvert.fileTo)) { + fs.copyFileSync(dataConvert.fileFrom, originPath); + ctx.logger.debug('copyOrigin complete'); + } + } //todo clarify calcChecksum conditions let calcChecksum = (0 === (constants.AVS_OFFICESTUDIO_FILE_CANVAS & cmd.getOutputFormat())); yield* processUploadToStorage(ctx, tempDirs.result, dataConvert.key, calcChecksum); From a92237c0fd48615a18e1d80420569e07e40e49d1 Mon Sep 17 00:00:00 2001 From: Sergey Konovalov Date: Sun, 16 Jun 2024 00:21:45 +0300 Subject: [PATCH 11/13] [bug] Use outputFormat in id instead of outputType; Fix bug 68653 --- DocService/sources/converterservice.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/DocService/sources/converterservice.js b/DocService/sources/converterservice.js index 5dad2001..2cf7781f 100644 --- a/DocService/sources/converterservice.js +++ b/DocService/sources/converterservice.js @@ -261,8 +261,7 @@ function convertRequest(req, res, isJson) { } let filetype = params.filetype || params.fileType || ''; let outputtype = params.outputtype || params.outputType || ''; - let docId = 'conv_' + params.key + '_' + outputtype; - ctx.setDocId(docId); + ctx.setDocId(params.key); if (params.key && !constants.DOC_ID_REGEX.test(params.key)) { ctx.logger.warn('convertRequest unexpected key = %s', params.key); @@ -291,6 +290,8 @@ function convertRequest(req, res, isJson) { outputFormat = constants.AVS_OFFICESTUDIO_FILE_DOCUMENT_OFORM_PDF; } } + //todo use hash of params as id + let docId = 'conv_' + params.key + '_' + outputFormat; var cmd = new commonDefines.InputCommand(); cmd.setCommand('conv'); cmd.setUrl(params.url); From 373671902fa35b9259f4da652da5c283e40b59a5 Mon Sep 17 00:00:00 2001 From: Sergey Konovalov Date: Sun, 16 Jun 2024 01:45:20 +0300 Subject: [PATCH 12/13] [bug] Fix crash; For bug 68645 --- DocService/sources/canvasservice.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DocService/sources/canvasservice.js b/DocService/sources/canvasservice.js index 034b4e66..2b9c44e3 100644 --- a/DocService/sources/canvasservice.js +++ b/DocService/sources/canvasservice.js @@ -1789,7 +1789,7 @@ exports.saveFromChanges = function(ctx, docId, statusInfo, optFormat, opt_userId async function processWopiSaveAs(ctx, cmd) { const info = await docsCoServer.getCallback(ctx, cmd.getDocId(), cmd.getUserIndex()); // info.wopiParams is null if it is not wopi - if (info.wopiParams) { + if (info?.wopiParams) { const suggestedTargetType = `.${formatChecker.getStringFromFormat(cmd.getOutputFormat())}`; const storageFilePath = `${cmd.getSaveKey()}/${cmd.getOutputPath()}`; const stream = await storage.createReadStream(ctx, storageFilePath); From 87f2847aba9651495f22e558b2d79cbed0c2ee4a Mon Sep 17 00:00:00 2001 From: Sergey Konovalov Date: Sun, 16 Jun 2024 22:20:45 +0300 Subject: [PATCH 13/13] [bug] Add maxRetries option to fix unexpected ENOTEMPTY during rm --- Common/sources/storage-fs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/sources/storage-fs.js b/Common/sources/storage-fs.js index 3257dc2f..5c0a299f 100644 --- a/Common/sources/storage-fs.js +++ b/Common/sources/storage-fs.js @@ -117,7 +117,7 @@ async function deleteObject(storageCfg, strPath) { async function deletePath(storageCfg, strPath) { const fsPath = getFilePath(storageCfg, strPath); - return rm(fsPath, {force: true, recursive: true}); + return rm(fsPath, {force: true, recursive: true, maxRetries: 3}); } async function getSignedUrl(ctx, storageCfg, baseUrl, strPath, urlType, optFilename, opt_creationDate) {