mirror of
https://github.com/cs3org/wopiserver.git
synced 2025-04-18 13:04:00 +03:00
Updated the whole repo to remove all vendor/storage-specific references.
This is in preparation to join the CS3 Organisation
This commit is contained in:
parent
9077610692
commit
25788c0fe6
16
README.md
16
README.md
@ -1,22 +1,14 @@
|
||||
# WOPI Server
|
||||
|
||||
A Web-application Open Platform Interface (WOPI) gateway for CERNBox
|
||||
to embed Microsoft Office Online into the CERNBox/OwnCloud web interface
|
||||
A Vendor-neutral Web-application Open Platform Interface (WOPI) gateway for EFSS,
|
||||
to integrate several Office Online platforms including Microsoft Office Online and Collabora Online.
|
||||
|
||||
Author: Giuseppe.LoPresti@cern.ch <br/>
|
||||
Contributions: Michael.DSilva@aarnet.edu.au
|
||||
|
||||
Initial revision: December 2016 <br/>
|
||||
First production version: September 2017
|
||||
First production version for CERNBox: September 2017 <br/>
|
||||
Integration in the CS3 Organisation: April 2020
|
||||
|
||||
This project has been presented at the [ownCloud Conference 2017](https://occon17.owncloud.org).
|
||||
Slides available at [slideshare.net](https://www.slideshare.net/giuseppelopresti/collaborative-editing-and-more-in-cernbox).
|
||||
|
||||
## Environment variables
|
||||
|
||||
A docker container is provided, which takes the following variables from the environment:
|
||||
|
||||
- __CONFIGURATION__ - HTTP URL pointing to the configuration files in your-personal-internal-area
|
||||
- __OCSECRET__ - password between oc and wopiserver
|
||||
- __WOPISECRET__ - password between wopiserver and oos
|
||||
|
||||
|
@ -2,16 +2,15 @@
|
||||
# cernbox-wopi-server spec file
|
||||
#
|
||||
Name: cernbox-wopi-server
|
||||
Summary: A WOPI server to support Office online suites on CERNBox
|
||||
Version: 4.2
|
||||
Release: 4%{?dist}
|
||||
Summary: A WOPI server to support Office online suites for the ScienceMesh IOP
|
||||
Version: 5.0
|
||||
Release: 0%{?dist}
|
||||
License: GPLv3
|
||||
Buildroot: %{_tmppath}/%{name}-buildroot
|
||||
Group: CERN-IT/ST
|
||||
BuildArch: noarch
|
||||
Source: %{name}-%{version}.tar.gz
|
||||
|
||||
# The required Python version makes this package depend on Fedora 29 or similar recent distros to compile and run.
|
||||
BuildRequires: python(abi) >= 3.6
|
||||
Requires: python(abi) >= 3.6, python36-pip
|
||||
# pip3-installed "Requires": python3-flask, python3-jwt, python3-pyOpenSSL, requests
|
||||
@ -20,7 +19,10 @@ Requires: python(abi) >= 3.6, python36-pip
|
||||
AutoReq: no
|
||||
|
||||
%description
|
||||
This RPM provides a Flask-based web server to implement the WOPI protocol for CERNBox
|
||||
This RPM provides a Flask-based reference implementation of the WOPI protocol for the CS3 ScienceMesh IOP.
|
||||
|
||||
Support for the IOP via CS3 APIs is being developed. The server supports local storage for testing purposes,
|
||||
as well as legacy xrootd-based storages currently used in production at CERN for CERNBox.
|
||||
|
||||
# Don't do any post-install weirdness, especially compiling .py files
|
||||
%define __os_install_post %{nil}
|
||||
@ -60,7 +62,7 @@ rm -rf %buildroot/
|
||||
|
||||
%post
|
||||
touch /etc/wopi/wopisecret
|
||||
touch /etc/wopi/ocsecret
|
||||
touch /etc/wopi/iopsecret
|
||||
|
||||
%files
|
||||
%defattr(-,root,root,-)
|
||||
@ -72,6 +74,10 @@ touch /etc/wopi/ocsecret
|
||||
%_python_lib/*
|
||||
|
||||
%changelog
|
||||
* Wed Apr 22 2020 Giuseppe Lo Presti <lopresti@cern.ch> 5.0
|
||||
- General refactoring of the code base and evolution to
|
||||
become fully vendor-neutral
|
||||
- Moved to the CS3 Organisation
|
||||
* Wed Apr 08 2020 Giuseppe Lo Presti <lopresti@cern.ch> 4.2
|
||||
- Introduced two new lock-related endpoints to cover interoperability
|
||||
with OnlyOffice
|
||||
|
@ -4,7 +4,7 @@
|
||||
#
|
||||
# This script can be used to generate a docker image of the WOPI server.
|
||||
# Prior to run it, you need to collect here a valid wopiserver.conf and
|
||||
# an ocsecret file that contains the shared secret used by your OwnCloud
|
||||
# an iopsecret file that contains the shared secret used by your EFSS
|
||||
# servers to authenticate to the WOPI server.
|
||||
#
|
||||
# If you want the WOPI server to run in secure mode, you need to generate
|
||||
|
@ -16,7 +16,7 @@ usehttps = no
|
||||
# location of the secret files. Requires a restart of the
|
||||
# WOPI server when either the files or their content change.
|
||||
wopisecretfile = /etc/wopi/wopisecret
|
||||
ocsecretfile = /etc/wopi/ocsecret
|
||||
iopsecretfile = /etc/wopi/iopsecret
|
||||
|
||||
[local]
|
||||
storagehomepath = /var/wopi_local_storage
|
||||
|
@ -3,7 +3,7 @@
|
||||
echo "${WOPISECRET:-$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 12 | head -n 1)}" > /etc/wopi/wopisecret
|
||||
#cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 12 | head -n 1 > /etc/wopi/wopisecret
|
||||
|
||||
echo "${OCSECRET:-password}" > /etc/wopi/ocsecret
|
||||
echo "${IOPSECRET:-password}" > /etc/wopi/iopsecret
|
||||
|
||||
/usr/bin/curl -o /etc/wopi/wopiserver.conf ${CONFIGURATION}/wopi/wopiserver.conf
|
||||
|
||||
|
@ -1,24 +1,24 @@
|
||||
# Dockerfile for Wopi Server
|
||||
# Dockerfile for WOPI Server
|
||||
#
|
||||
# Please, build and run via docker-compose file: wopiserver.yaml
|
||||
|
||||
#FROM cern/c8-base:latest # this will eventually be the default
|
||||
#FROM python:3 # this will eventually be the default, but for now we need the yum infrastructure to install xrootd
|
||||
FROM cern/cc7-base:latest
|
||||
|
||||
LABEL maintainer="cernbox-admins@cern.ch" name="wopiserver: The CERNBox WOPI server" version="1.0"
|
||||
LABEL maintainer="cernbox-admins@cern.ch" name="The ScienceMesh IOP WOPI server" version="1.0"
|
||||
|
||||
MAINTAINER Michael D'Silva <md@aarnet.edu.au>, Giuseppe Lo Presti <lopresti@cern.ch>
|
||||
MAINTAINER Giuseppe Lo Presti <lopresti@cern.ch>
|
||||
|
||||
ADD cernbox-wopi*rpm /tmp
|
||||
RUN yum -y install \
|
||||
sudo \
|
||||
python36 \
|
||||
python36-pip \
|
||||
python36-devel \
|
||||
openssl-devel \
|
||||
xrootd-client \
|
||||
python3-xrootd \
|
||||
/tmp/cernbox-wopi*rpm
|
||||
sudo \
|
||||
python36 \
|
||||
python36-pip \
|
||||
python36-devel \
|
||||
openssl-devel \
|
||||
xrootd-client \
|
||||
python3-xrootd \
|
||||
/tmp/cernbox-wopi*rpm
|
||||
|
||||
RUN pip3 install flask pyOpenSSL PyJWT requests
|
||||
|
||||
@ -30,3 +30,4 @@ RUN chmod 777 /var/log/wopi
|
||||
|
||||
#CMD /scripts/entrypoint
|
||||
CMD ["python3", "/usr/bin/wopiserver.py"]
|
||||
|
||||
|
@ -1,2 +1,2 @@
|
||||
curl --header "Authorization: Bearer "`cat /etc/wopi/ocsecret` https://`hostname`:8443/wopi/cbox/open/list
|
||||
curl --header "Authorization: Bearer "`cat /etc/wopi/iopsecret` https://`hostname`:8443/wopi/cbox/open/list
|
||||
echo
|
||||
|
@ -84,7 +84,7 @@ class Wopi:
|
||||
cls.port = int(cls.config.get('general', 'port'))
|
||||
cls.log.setLevel(cls.loglevels[cls.config.get('general', 'loglevel')])
|
||||
cls.wopisecret = open(cls.config.get('security', 'wopisecretfile')).read().strip('\n')
|
||||
cls.ocsecret = open(cls.config.get('security', 'ocsecretfile')).read().strip('\n')
|
||||
cls.iopsecret = open(cls.config.get('security', 'iopsecretfile')).read().strip('\n')
|
||||
cls.tokenvalidity = cls.config.getint('general', 'tokenvalidity')
|
||||
storage.init(cls.config, cls.log) # initialize the xroot client module
|
||||
cls.config.get('general', 'allowedclients') # read this to make sure it is configured
|
||||
@ -456,7 +456,7 @@ def cboxOpen():
|
||||
Wopi.refreshconfig()
|
||||
req = flask.request
|
||||
# if running in https mode, first check if the shared secret matches ours
|
||||
if Wopi.useHttps and ('Authorization' not in req.headers or req.headers['Authorization'] != 'Bearer ' + Wopi.ocsecret):
|
||||
if Wopi.useHttps and ('Authorization' not in req.headers or req.headers['Authorization'] != 'Bearer ' + Wopi.iopsecret):
|
||||
Wopi.log.warning('msg="cboxOpen: unauthorized access attempt, missing authorization token" client="%s"' % req.remote_addr)
|
||||
return 'Client not authorized', http.client.UNAUTHORIZED
|
||||
# now validate the user identity and deny root access
|
||||
@ -469,7 +469,7 @@ def cboxOpen():
|
||||
Wopi.log.warning('msg="cboxOpen: invalid user/group in request" client="%s" user="%s:%s"' % \
|
||||
(req.remote_addr, req.args['ruid'], req.args['rgid']))
|
||||
return 'Client not authorized', http.client.UNAUTHORIZED
|
||||
# then resolve the client: only our CERNBox/ownCloud servers shall use this API
|
||||
# then resolve the client and reject unauthorized ones
|
||||
allowedclients = Wopi.config.get('general', 'allowedclients').split()
|
||||
for c in allowedclients:
|
||||
try:
|
||||
@ -533,7 +533,7 @@ def cboxDownload():
|
||||
|
||||
@Wopi.app.route("/wopi/cbox/endpoints", methods=['GET'])
|
||||
def cboxEndPoints():
|
||||
'''Returns the office apps end-points registered with this WOPI server. This is used by the OwnCloud
|
||||
'''Returns the office apps end-points registered with this WOPI server. This is used by the EFSS
|
||||
client to discover which Apps frontends can be used with this WOPI server.
|
||||
Note that if the end-points are relocated and the corresponding configuration entry updated,
|
||||
the WOPI server must be restarted.'''
|
||||
@ -548,7 +548,7 @@ def cboxGetOpenFiles():
|
||||
This call is protected by the same shared secret as the /wopi/cbox/open call.'''
|
||||
req = flask.request
|
||||
# first check if the shared secret matches ours
|
||||
if 'Authorization' not in req.headers or req.headers['Authorization'] != 'Bearer ' + Wopi.ocsecret:
|
||||
if 'Authorization' not in req.headers or req.headers['Authorization'] != 'Bearer ' + Wopi.iopsecret:
|
||||
Wopi.log.warning('msg="cboxGetOpenFiles: unauthorized access attempt, missing authorization token" client="%s"' % req.remote_addr)
|
||||
return 'Client not authorized', http.client.UNAUTHORIZED
|
||||
# first convert the sets into lists, otherwise sets cannot be serialized in JSON format
|
||||
@ -580,7 +580,7 @@ def cboxLock():
|
||||
'''
|
||||
req = flask.request
|
||||
# first check if the shared secret matches ours
|
||||
if 'Authorization' not in req.headers or req.headers['Authorization'] != 'Bearer ' + Wopi.ocsecret:
|
||||
if 'Authorization' not in req.headers or req.headers['Authorization'] != 'Bearer ' + Wopi.iopsecret:
|
||||
Wopi.log.warning('msg="cboxLock: unauthorized access attempt, missing authorization token" client="%s"' % req.remote_addr)
|
||||
return 'Client not authorized', http.client.UNAUTHORIZED
|
||||
filename = req.args['filename']
|
||||
@ -658,7 +658,7 @@ def cboxUnlock():
|
||||
'''
|
||||
req = flask.request
|
||||
# first check if the shared secret matches ours
|
||||
if 'Authorization' not in req.headers or req.headers['Authorization'] != 'Bearer ' + Wopi.ocsecret:
|
||||
if 'Authorization' not in req.headers or req.headers['Authorization'] != 'Bearer ' + Wopi.iopsecret:
|
||||
Wopi.log.warning('msg="cboxUnlock: unauthorized access attempt, missing authorization token" client="%s"' % req.remote_addr)
|
||||
return 'Client not authorized', http.client.UNAUTHORIZED
|
||||
filename = req.args['filename']
|
||||
@ -728,12 +728,9 @@ def wopiCheckFileInfo(fileid):
|
||||
(Wopi.config.get('general', 'downloadurl'), flask.request.args['access_token'])
|
||||
filemd['HostViewUrl'] = '%s&%s' % (Wopi.ENDPOINTS[fExt]['view'], wopiSrc)
|
||||
filemd['HostEditUrl'] = '%s&%s' % (Wopi.ENDPOINTS[fExt]['edit'], wopiSrc)
|
||||
# the following is to enable the 'Edit in Word/Excel/PowerPoint' (desktop) action
|
||||
# the following is to enable the 'Edit in Word/Excel/PowerPoint' (desktop) action (probably broken)
|
||||
try:
|
||||
# a path 'a-la owncloud' includes '/files/', which has to be stripped off.
|
||||
# XXX This is temporary code for the AARNet config. Note this is not robust as a user path including '/files/' will be broken.
|
||||
filemd['ClientUrl'] = Wopi.config.get('general', 'webdavurl') + '/' + \
|
||||
(acctok['filename'].split("/files/", 1)[1] if '/files/' in acctok['filename'] else acctok['filename'])
|
||||
filemd['ClientUrl'] = Wopi.config.get('general', 'webdavurl') + '/' + acctok['filename']
|
||||
except configparser.NoOptionError:
|
||||
# if no WebDAV URL is provided, ignore this setting
|
||||
pass
|
||||
@ -1100,7 +1097,7 @@ def wopiPutFile(fileid):
|
||||
except IOError:
|
||||
# either the file was deleted or it was updated/overwritten by others: force conflict
|
||||
newname, ext = os.path.splitext(acctok['filename'])
|
||||
# !!! note the OwnCloud format is '<filename>_conflict-<date>-<time>', but it is not synchronized back !!!
|
||||
# !!! typical EFSS formats are like '<filename>_conflict-<date>-<time>', but they're not synchronized back !!!
|
||||
newname = '%s-conflict-%s%s' % (newname, time.strftime('%Y%m%d-%H%M%S'), ext.strip())
|
||||
_storeWopiFile(flask.request, acctok, newname)
|
||||
# keep track of this action in the original file's xattr, to avoid looping (see below)
|
||||
@ -1117,9 +1114,8 @@ def wopiPutFile(fileid):
|
||||
(acctok['ruid'], acctok['rgid'], flask.request.args['access_token'], acctok['filename']))
|
||||
return 'Conflicting copy already created', http.client.INTERNAL_SERVER_ERROR
|
||||
# Go for overwriting the file. Note that the entire check+write operation should be atomic,
|
||||
# but the previous check still gives the opportunity of a race condition. We just live with it
|
||||
# as OwnCloud does not seem to provide anything better...
|
||||
# Anyhow, previous versions are all stored and recoverable by the user.
|
||||
# but the previous check still gives the opportunity of a race condition. We just live with it.
|
||||
# Anyhow, the EFSS should support versioning for such cases.
|
||||
_storeWopiFile(flask.request, acctok)
|
||||
Wopi.log.info('msg="File successfully written" action="edit" user="%s:%s" filename="%s" token="%s"' % \
|
||||
(acctok['ruid'], acctok['rgid'], acctok['filename'], flask.request.args['access_token'][-20:]))
|
||||
|
@ -65,7 +65,7 @@ lockrgid = 0
|
||||
# Location of the secret files. Requires a restart of the
|
||||
# WOPI server when either the files or their content change.
|
||||
wopisecretfile = /etc/wopi/wopisecret
|
||||
ocsecretfile = /etc/wopi/ocsecret
|
||||
iopsecretfile = /etc/wopi/iopsecret
|
||||
|
||||
# Use https as opposed to http (requires certificate)
|
||||
usehttps = no
|
||||
|
@ -1,5 +1,5 @@
|
||||
[Unit]
|
||||
Description=WOPI Server for CERNBox
|
||||
Description=WOPI Server for Reva IOP
|
||||
After=syslog.target
|
||||
|
||||
[Service]
|
||||
|
Loading…
x
Reference in New Issue
Block a user