diff --git a/.drone.star b/.drone.star index ad34ae75d..bf1a0d78b 100644 --- a/.drone.star +++ b/.drone.star @@ -96,6 +96,10 @@ config = { "skip": False, "earlyFail": True, }, + "wopiValidatorTests": { + "skip": False, + "earlyFail": True, + }, "localApiTests": { "skip": False, "earlyFail": True, @@ -343,6 +347,8 @@ def testPipelines(ctx): if "skip" not in config["cs3ApiTests"] or not config["cs3ApiTests"]["skip"]: pipelines.append(cs3ApiTests(ctx, "ocis", "default")) + if "skip" not in config["wopiValidatorTests"] or not config["wopiValidatorTests"]["skip"]: + pipelines.append(wopiValidatorTests(ctx, "ocis", "default")) if "skip" not in config["localApiTests"] or not config["localApiTests"]["skip"]: pipelines += [ localApiTests(ctx, "ocis", "apiAccountsHashDifficulty"), @@ -750,6 +756,100 @@ def cs3ApiTests(ctx, storage, accounts_hash_difficulty = 4): }, } +def wopiValidatorTests(ctx, storage, accounts_hash_difficulty = 4): + early_fail = config["wopiValidatorTests"]["earlyFail"] if "earlyFail" in config["wopiValidatorTests"] else False + + testgroups = [ + "BaseWopiViewing", + "CheckFileInfoSchema", + "EditFlows", + "Locks", + "AccessTokens", + "GetLock", + "ExtendedLockLength", + "FileVersion", + "Features", + ] + + validatorTests = [] + + for testgroup in testgroups: + validatorTests.append({ + "name": "wopiValidatorTests-%s-%s" % (storage, testgroup), + "image": "owncloudci/wopi-validator", + "commands": [ + "export WOPI_TOKEN=$(cat accesstoken)", + "echo $WOPI_TOKEN", + "export WOPI_TTL=$(cat accesstokenttl)", + "echo $WOPI_TTL", + "export WOPI_SRC=$(cat wopisrc)", + "echo $WOPI_SRC", + "cd /app", + "/app/Microsoft.Office.WopiValidator -s -t $WOPI_TOKEN -w $WOPI_SRC -l $WOPI_TTL --testgroup %s" % testgroup, + ], + }) + + return { + "kind": "pipeline", + "type": "docker", + "name": "wopiValidatorTests-%s" % (storage), + "platform": { + "os": "linux", + "arch": "amd64", + }, + "steps": skipIfUnchanged(ctx, "acceptance-tests") + + restoreBuildArtifactCache(ctx, "ocis-binary-amd64", "ocis/bin") + + [ + { + "name": "fakeoffice", + "image": OC_CI_ALPINE, + "detach": True, + "environment": {}, + "commands": [ + "sh %s/tests/config/drone/serve-hosting-discovery.sh" % (dirs["base"]), + ], + }, + { + "name": "wopiserver", + "image": "cs3org/wopiserver:v9.2.5", + "detach": True, + "commands": [ + "cp %s/tests/config/drone/wopiserver.conf /etc/wopi/wopiserver.conf" % (dirs["base"]), + "echo 123 > /etc/wopi/wopisecret", + "/app/wopiserver.py", + ], + }, + ] + + ocisServer(storage, accounts_hash_difficulty, [], [], "wopi_validator") + + [ + { + "name": "prepare-test-file-%s" % (storage), + "image": OC_CI_ALPINE, + "environment": {}, + "commands": [ + "curl -k 'https://ocis-server:9200/remote.php/webdav/test.wopitest' --fail --retry-connrefused --retry 7 --retry-all-errors -X PUT -u admin:admin -D headers.txt", + "export FILE_ID=$(cat headers.txt | sed -n -e 's/^.*Oc-Fileid: //p')", + "export URL=\"https://ocis-server:9200/app/open?app_name=FakeOffice&file_id=$FILE_ID\"", + "export URL=$(echo $URL | tr -d '[:cntrl:]')", + "curl -k -X POST \"$URL\" -u admin:admin -v > open.json", + "cat open.json | jq .form_parameters.access_token | tr -d '\"' > accesstoken", + "cat open.json | jq .form_parameters.access_token_ttl | tr -d '\"' > accesstokenttl", + "echo -n 'http://wopiserver:8880/wopi/files/' > wopisrc", + "cat open.json | jq .app_url | sed -n -e 's/^.*files%2F//p' | tr -d '\"' >> wopisrc", + ], + }, + ] + + validatorTests + + failEarly(ctx, early_fail), + "depends_on": getPipelineNames([buildOcisBinaryForTesting(ctx)]), + "trigger": { + "ref": [ + "refs/heads/master", + "refs/pull/**", + ], + }, + } + def coreApiTests(ctx, part_number = 1, number_of_parts = 1, storage = "ocis", accounts_hash_difficulty = 4): early_fail = config["apiTests"]["earlyFail"] if "earlyFail" in config["apiTests"] else False filterTags = "~@skipOnGraph&&~@skipOnOcis&&~@notToImplementOnOCIS&&~@toImplementOnOCIS&&~comments-app-required&&~@federation-app-required&&~@notifications-app-required&&~systemtags-app-required&&~@local_storage&&~@skipOnOcis-%s-Storage&&~@caldav&&~@carddav" % ("OC" if storage == "owncloud" else "OCIS") @@ -1874,8 +1974,8 @@ def notify(): }, } -def ocisServer(storage, accounts_hash_difficulty = 4, volumes = [], depends_on = [], testing_parallel_deploy = False): - if not testing_parallel_deploy: +def ocisServer(storage, accounts_hash_difficulty = 4, volumes = [], depends_on = [], deploy_type = ""): + if deploy_type == "": user = "0:0" environment = { "OCIS_URL": OCIS_URL, @@ -1902,7 +2002,43 @@ def ocisServer(storage, accounts_hash_difficulty = 4, volumes = [], depends_on = ], "depends_on": depends_on, } - else: + + if deploy_type == "wopi_validator": + user = "0:0" + environment = { + "OCIS_URL": OCIS_URL, + "OCIS_CONFIG_DIR": "/root/.ocis/config", + "STORAGE_USERS_DRIVER": "%s" % (storage), + "STORAGE_USERS_DRIVER_LOCAL_ROOT": "%s/local/root" % dirs["ocis"], + "STORAGE_USERS_DRIVER_OCIS_ROOT": "%s/storage/users" % dirs["ocis"], + "STORAGE_SYSTEM_DRIVER_OCIS_ROOT": "%s/storage/metadata" % dirs["ocis"], + "SHARING_USER_JSON_FILE": "%s/shares.json" % dirs["ocis"], + "PROXY_ENABLE_BASIC_AUTH": True, + "WEB_UI_CONFIG": "%s/%s" % (dirs["base"], dirs["ocisConfig"]), + "OCIS_LOG_LEVEL": "error", + "SETTINGS_DATA_PATH": "%s/settings" % dirs["ocis"], + "IDM_CREATE_DEMO_USERS": True, + "IDM_ADMIN_PASSWORD": "admin", # override the random admin password from `ocis init` + "FRONTEND_SEARCH_MIN_LENGTH": "2", + "GATEWAY_GRPC_ADDR": "0.0.0.0:9142", # make gateway available to wopi server + "APP_PROVIDER_EXTERNAL_ADDR": "127.0.0.1:9164", + "APP_PROVIDER_DRIVER": "wopi", + "APP_PROVIDER_WOPI_APP_NAME": "FakeOffice", + "APP_PROVIDER_WOPI_APP_URL": "http://fakeoffice:8080", + "APP_PROVIDER_WOPI_INSECURE": "true", + "APP_PROVIDER_WOPI_WOPI_SERVER_EXTERNAL_URL": "http://wopiserver:8880", + "APP_PROVIDER_WOPI_FOLDER_URL_BASE_URL": "https://ocis-server:9200", + } + wait_for_ocis = { + "name": "wait-for-ocis-server", + "image": OC_CI_WAIT_FOR, + "commands": [ + "wait-for -it ocis-server:9200 -t 300", + ], + "depends_on": depends_on, + } + + if deploy_type == "parallel": user = "33:33" environment = { # Keycloak IDP specific configuration @@ -2527,7 +2663,7 @@ def parallelDeployAcceptancePipeline(ctx): 4, [stepVolumeOC10OCISData, stepVolumeOCISConfig], ["fix-shared-data-permissions"], - True, + "parallel", ) + parallelAcceptance(environment) + failEarly(ctx, early_fail), diff --git a/tests/config/drone/hosting-discovery.xml b/tests/config/drone/hosting-discovery.xml new file mode 100644 index 000000000..f663da91d --- /dev/null +++ b/tests/config/drone/hosting-discovery.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tests/config/drone/serve-hosting-discovery.sh b/tests/config/drone/serve-hosting-discovery.sh new file mode 100644 index 000000000..b59757866 --- /dev/null +++ b/tests/config/drone/serve-hosting-discovery.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +while true; do + echo -e "HTTP/1.1 200 OK\n\n$(cat /drone/src/tests/config/drone/hosting-discovery.xml)" | nc -l -k -p 8080 +done diff --git a/tests/config/drone/wopiserver.conf b/tests/config/drone/wopiserver.conf new file mode 100644 index 000000000..af6220415 --- /dev/null +++ b/tests/config/drone/wopiserver.conf @@ -0,0 +1,131 @@ +# +# This config is based on https://github.com/cs3org/wopiserver/blob/master/wopiserver.conf +# +# wopiserver.conf +# +# Default configuration file for the WOPI server for oCIS +# +############################################################## + +[general] +# Storage access layer to be loaded in order to operate this WOPI server +# only "cs3" is supported with oCIS +storagetype = cs3 + +# Port where to listen for WOPI requests +port = 8880 + +# Logging level. Debug enables the Flask debug mode as well. +# Valid values are: Debug, Info, Warning, Error. +loglevel = Debug +loghandler = stream +logdest = stdout + +# URL of your WOPI server or your HA proxy in front of it +wopiurl = http://wopiserver + +# URL for direct download of files. The complete URL that is sent +# to clients will include the access_token argument +downloadurl = http://wopiserver/wopi/cbox/download + +# The internal server engine to use (defaults to flask). +# Set to waitress for production installations. +internalserver = waitress + +# List of file extensions deemed incompatible with LibreOffice: +# interoperable locking will be disabled for such files +nonofficetypes = .md .zmd .txt .epd + +# List of file extensions to be supported by Collabora (deprecated) +codeofficetypes = .odt .ott .ods .ots .odp .otp .odg .otg .doc .dot .xls .xlt .xlm .ppt .pot .pps .vsd .dxf .wmf .cdr .pages .number .key + +brandingname=CS3org WOPI server +brandingurl=https://github.com/cs3org/wopiserver + +# WOPI access token expiration time [seconds] +tokenvalidity = 86400 + +# WOPI lock expiration time [seconds] +wopilockexpiration = 3600 + +# WOPI lock strict check: if True, WOPI locks will be compared according to specs, +# that is their representation must match. False (default) allows for a more relaxed +# comparison, which compensates incorrect lock requests from Microsoft Office Online +# on-premise setups. +wopilockstrictcheck = False + +# Enable support of rename operations from WOPI apps. This is currently +# disabled by default as it has been observed that both MS Office and Collabora +# Online do not play well with this feature. +# Not supported with oCIS, must always be set to "False" +enablerename = False + +# Detection of external Microsoft Office or LibreOffice locks. By default, lock files +# compatible with Office for Desktop applications are detected, assuming that the +# underlying storage can be mounted as a remote filesystem: in this case, WOPI GetLock +# and SetLock operations return such locks and prevent online apps from entering edit mode. +# This feature can be disabled in order to operate a pure WOPI server for online apps. +# Not supported with oCIS, must always be set to "False" +detectexternallocks = False + +# Location of the webconflict files. By default, such files are stored in the same path +# as the original file. If that fails (e.g. because of missing permissions), +# an attempt is made to store such files in this path if specified, otherwise +# the system falls back to the recovery space (cf. io|recoverypath). +# The keywords and are replaced with the actual username's +# initial letter and the actual username, respectively, so you can use e.g. +# /your_storage/home/user_initial/username +#conflictpath = / + +# ownCloud's WOPI proxy configuration. Disabled by default. +#wopiproxy = https://external-wopi-proxy.com +#wopiproxysecretfile = /path/to/your/shared-key-file +#proxiedappname = Name of your proxied app + +[security] +# Location of the secret files. Requires a restart of the +# WOPI server when either the files or their content change. +wopisecretfile = /etc/wopi/wopisecret +# iop secret is not used for cs3 storage type +#iopsecretfile = /etc/wopi/iopsecret + +# Use https as opposed to http (requires certificate) +usehttps = no + +# Certificate and key for https. Requires a restart +# to apply a change. +wopicert = /etc/grid-security/host.crt +wopikey = /etc/grid-security/host.key + +[bridge] +# SSL certificate check for the connected apps +sslverify = True + +# Minimal time interval between two consecutive save operations [seconds] +#saveinterval = 200 + +# Minimal time interval before a closed file is WOPI-unlocked [seconds] +#unlockinterval = 90 + +# CodiMD: disable creating zipped bundles when files contain pictures +#disablezip = False + +[io] +# Size used for buffered reads [bytes] +chunksize = 4194304 + +# Path to a recovery space in case of I/O errors when reaching to the remote storage. +# This is expected to be a local path, and it is provided in order to ease user support. +# Defaults to the indicated spool folder. +recoverypath = /var/spool/wopirecovery + +[cs3] +# Host and port of the Reva(-like) CS3-compliant GRPC gateway endpoint +revagateway = ocis-server:9142 + +# Reva/gRPC authentication token expiration time [seconds] +# The default value matches Reva's default +authtokenvalidity = 3600 + +# SSL certificate check for Reva +sslverify = False