Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/core/wopi.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ def checkFileInfo(fileid, acctok):
# extensions for Collabora Online
if 'Collabora' in acctok['appname']:
fmd['EnableOwnerTermination'] = True
fmd['IsAdminUser'] = False
fmd['DisableExport'] = fmd['DisableCopy'] = fmd['DisablePrint'] = acctok['viewmode'] in (utils.ViewMode.VIEW_ONLY,
utils.ViewMode.EMBEDDED)
if srv.config.get('apps', 'codedisableexport', fallback='False').upper() == 'TRUE':
Expand Down
24 changes: 21 additions & 3 deletions src/core/wopiutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,15 +237,32 @@ def generateAccessToken(userid, fileid, viewmode, user, folderurl, endpoint, app
exptime = int(time.time()) + srv.config.getint('general', 'tokenvalidity')
fname = statinfo['filepath']
fext = os.path.splitext(fname)[1].lower()

vmdetails = ''
if 'Collabora' not in appname and viewmode == ViewMode.READ_WRITE and (
fext in ('.doc', '.dot', '.xls', '.ppt', '.pps', '.csv') or
fext[1:3] in ('od', 'ot') and srv.config.get('general', 'disablemswriteodf', fallback='False').upper() == 'TRUE'):
# we're opening a legacy format file or an ODF (`.o[d|t]?`) and the app is not Collabora
log.info(f"msg=\"Forcing read-only access to ODF/legacy formats\" filename=\"{fname}\"")
viewmode = ViewMode.READ_ONLY
vmdetails = 'app does not support edit for this file type'
# if we're opening the file in edit mode, check for existing locks and downgrade to read-only if any is found
if viewmode in (ViewMode.READ_WRITE, ViewMode.PREVIEW) and \
srv.config.get('general', 'detectexternallocks', fallback='True').upper() == 'TRUE':
lock = retrieveWopiLock(fileid, 'genAccessToken', 'NA',
{'filename': fname, 'userid': userid, 'endpoint': endpoint})
if lock[0] is not None and lock[0] != appname:
log.info(f'msg="Forcing read-only access due to existing lock" filename="{fname}" lock="{lock}" app="{appname}"')
viewmode = ViewMode.READ_ONLY
if lock[0] == EXTERNALLOCK:
vmdetails = f'read-only, opened exclusively by {lock[1]}'
else:
vmdetails = f'read-only, use app {lock[1]} to edit instead'
if viewmode == ViewMode.PREVIEW and statinfo['size'] == 0:
# override preview mode when a new file is being created
viewmode = ViewMode.READ_WRITE
vmdetails = 'new file'

tokmd = {
'userid': userid, 'wopiuser': wopiuser, 'usertype': usertype.value, 'filename': fname, 'fileid': fileid,
'username': friendlyname, 'viewmode': viewmode.value, 'folderurl': folderurl, 'endpoint': endpoint,
Expand All @@ -257,11 +274,12 @@ def generateAccessToken(userid, fileid, viewmode, user, folderurl, endpoint, app
tokmd['appviewurl'] = appviewurl
acctok = jwt.encode(tokmd, srv.wopisecret, algorithm='HS256')
srv.allusers.add(userid)

log.info('msg="Access token generated" trace="%s" userid="%s" wopiuser="%s" friendlyname="%s" usertype="%s" mode="%s" '
'endpoint="%s" filename="%s" inode="%s" mtime="%s" folderurl="%s" appname="%s" expiration="%d" token="%s"' %
(trace, userid[-20:], wopiuser, friendlyname, usertype, viewmode, endpoint, fname,
statinfo['inode'], statinfo['mtime'], folderurl, appname, exptime, acctok[-20:]))
return statinfo['inode'], acctok, viewmode
return statinfo['inode'], acctok, viewmode, vmdetails


def encodeLock(lock):
Expand Down Expand Up @@ -294,7 +312,7 @@ def retrieveWopiLock(fileid, operation, lockforlog, acctok, overridefn=None):
mslockstat = st.stat(acctok['endpoint'], getMicrosoftOfficeLockName(acctok['filename']), acctok['userid'])
log.info('msg="Found existing MS Office lock" lockop="%s" user="%s" filename="%s" token="%s" lockmtime="%ld"' %
(operation.title(), acctok['userid'][-20:], acctok['filename'], encacctok, mslockstat['mtime']))
return EXTERNALLOCK, 'Microsoft Office for Desktop'
return EXTERNALLOCK, f'{mslockstat["ownerid"].split("@")[0]} via Microsoft Office for Desktop'
except IOError:
pass
try:
Expand All @@ -308,7 +326,7 @@ def retrieveWopiLock(fileid, operation, lockforlog, acctok, overridefn=None):
'lockmtime="%ld" holder="%s"' %
(operation.title(), acctok['userid'][-20:], acctok['filename'], encacctok,
lolockstat['mtime'], lolockholder))
return EXTERNALLOCK, 'LibreOffice for Desktop'
return EXTERNALLOCK, f'{lolockholder} via LibreOffice for Desktop'
except (IOError, StopIteration):
pass

Expand Down
10 changes: 6 additions & 4 deletions src/wopiserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,9 +343,9 @@ def iopOpenInApp():

try:
userid, wopiuser = storage.getuseridfromcreds(usertoken, wopiuser)
inode, acctok, vm = utils.generateAccessToken(userid, fileid, viewmode, (username, wopiuser, usertype), folderurl,
endpoint, (appname, appurl, appviewurl),
req.headers.get('X-Trace-Id', 'N/A'))
inode, acctok, vm, vmdetails = utils.generateAccessToken(userid, fileid, viewmode, (username, wopiuser, usertype),
folderurl, endpoint, (appname, appurl, appviewurl),
req.headers.get('X-Trace-Id', 'N/A'))
except IOError as e:
Wopi.log.info('msg="iopOpenInApp: remote error on generating token" client="%s" trace="%s" user="%s" '
'friendlyname="%s" mode="%s" endpoint="%s" reason="%s"' %
Expand All @@ -357,7 +357,7 @@ def iopOpenInApp():
try:
res['app-url'], res['form-parameters'] = bridge.appopen(utils.generateWopiSrc(inode), acctok,
(appname, appurl, url_unquote_plus(req.args.get('appinturl', appurl)), req.headers.get('ApiKey')), # noqa: E128
vm, usertoken)
vm, usertoken)
except bridge.FailedOpen as foe:
return foe.msg, foe.statuscode
else:
Expand All @@ -370,6 +370,8 @@ def iopOpenInApp():
# tells the app to enable the business flow if appropriate
res['app-url'] += '&IsLicensedUser=1'
res['form-parameters'] = {'access_token': acctok}
if vm != viewmode:
res['forced-viewmode-reason'] = vmdetails

appforlog = res['app-url']
if appforlog.find('access') > 0:
Expand Down
Loading