mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-01-01 09:52:23 -06:00
feat: add new locks parser for microsoft office online server
This commit is contained in:
@@ -2,7 +2,7 @@ package config
|
||||
|
||||
// App defines the available app configuration.
|
||||
type App struct {
|
||||
Name string `yaml:"name" env:"COLLABORATION_APP_NAME" desc:"The name of the app, either Collabora, OnlyOffice or Microsoft365" introductionVersion:"6.0.0"`
|
||||
Name string `yaml:"name" env:"COLLABORATION_APP_NAME" desc:"The name of the app, either Collabora, OnlyOffice, Microsoft365 or MicrosoftOfficeOnline" introductionVersion:"6.0.0"`
|
||||
Description string `yaml:"description" env:"COLLABORATION_APP_DESCRIPTION" desc:"App description" introductionVersion:"6.0.0"`
|
||||
Icon string `yaml:"icon" env:"COLLABORATION_APP_ICON" desc:"Icon for the app" introductionVersion:"6.0.0"`
|
||||
LockName string `yaml:"lockname" env:"COLLABORATION_APP_LOCKNAME" desc:"Name for the app lock" introductionVersion:"6.0.0"`
|
||||
|
||||
@@ -5,9 +5,11 @@ import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
gatewayv1beta1 "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||
"github.com/owncloud/ocis/v2/services/collaboration/pkg/config"
|
||||
"github.com/owncloud/ocis/v2/services/collaboration/pkg/locks"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
@@ -25,25 +27,34 @@ const (
|
||||
// All operations are expected to follow the definitions found in
|
||||
// https://learn.microsoft.com/en-us/microsoft-365/cloud-storage-partner-program/rest/endpoints
|
||||
type HttpAdapter struct {
|
||||
con ConnectorService
|
||||
con ConnectorService
|
||||
config *config.Config
|
||||
locks locks.LockParser
|
||||
}
|
||||
|
||||
// NewHttpAdapter will create a new HTTP adapter. A new connector using the
|
||||
// provided gateway API client and configuration will be used in the adapter
|
||||
func NewHttpAdapter(gwc gatewayv1beta1.GatewayAPIClient, cfg *config.Config) *HttpAdapter {
|
||||
return &HttpAdapter{
|
||||
httpAdapter := &HttpAdapter{
|
||||
con: NewConnector(
|
||||
NewFileConnector(gwc, cfg),
|
||||
NewContentConnector(gwc, cfg),
|
||||
),
|
||||
}
|
||||
|
||||
httpAdapter.locks = &locks.NoopLockParser{}
|
||||
if strings.ToLower(cfg.App.Name) == "microsoftofficeonline" {
|
||||
httpAdapter.locks = &locks.LegacyLockParser{}
|
||||
}
|
||||
return httpAdapter
|
||||
}
|
||||
|
||||
// NewHttpAdapterWithConnector will create a new HTTP adapter that will use
|
||||
// the provided connector service
|
||||
func NewHttpAdapterWithConnector(con ConnectorService) *HttpAdapter {
|
||||
func NewHttpAdapterWithConnector(con ConnectorService, l locks.LockParser) *HttpAdapter {
|
||||
return &HttpAdapter{
|
||||
con: con,
|
||||
con: con,
|
||||
locks: l,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,8 +86,8 @@ func (h *HttpAdapter) GetLock(w http.ResponseWriter, r *http.Request) {
|
||||
// The operation's response will be sent through the response writer and
|
||||
// the headers according to the spec
|
||||
func (h *HttpAdapter) Lock(w http.ResponseWriter, r *http.Request) {
|
||||
oldLockID := r.Header.Get(HeaderWopiOldLock)
|
||||
lockID := r.Header.Get(HeaderWopiLock)
|
||||
oldLockID := h.locks.ParseLock(r.Header.Get(HeaderWopiOldLock))
|
||||
lockID := h.locks.ParseLock(r.Header.Get(HeaderWopiLock))
|
||||
|
||||
fileCon := h.con.GetFileConnector()
|
||||
newLockID, err := fileCon.Lock(r.Context(), lockID, oldLockID)
|
||||
@@ -103,7 +114,7 @@ func (h *HttpAdapter) Lock(w http.ResponseWriter, r *http.Request) {
|
||||
// The operation's response will be sent through the response writer and
|
||||
// the headers according to the spec
|
||||
func (h *HttpAdapter) RefreshLock(w http.ResponseWriter, r *http.Request) {
|
||||
lockID := r.Header.Get(HeaderWopiLock)
|
||||
lockID := h.locks.ParseLock(r.Header.Get(HeaderWopiLock))
|
||||
|
||||
fileCon := h.con.GetFileConnector()
|
||||
newLockID, err := fileCon.RefreshLock(r.Context(), lockID)
|
||||
@@ -128,7 +139,7 @@ func (h *HttpAdapter) RefreshLock(w http.ResponseWriter, r *http.Request) {
|
||||
// The operation's response will be sent through the response writer and
|
||||
// the headers according to the spec
|
||||
func (h *HttpAdapter) UnLock(w http.ResponseWriter, r *http.Request) {
|
||||
lockID := r.Header.Get(HeaderWopiLock)
|
||||
lockID := h.locks.ParseLock(r.Header.Get(HeaderWopiLock))
|
||||
|
||||
fileCon := h.con.GetFileConnector()
|
||||
newLockID, err := fileCon.UnLock(r.Context(), lockID)
|
||||
@@ -211,7 +222,7 @@ func (h *HttpAdapter) GetFile(w http.ResponseWriter, r *http.Request) {
|
||||
// The operation's response will be sent through the response writer and
|
||||
// the headers according to the spec
|
||||
func (h *HttpAdapter) PutFile(w http.ResponseWriter, r *http.Request) {
|
||||
lockID := r.Header.Get(HeaderWopiLock)
|
||||
lockID := h.locks.ParseLock(r.Header.Get(HeaderWopiLock))
|
||||
|
||||
contentCon := h.con.GetContentConnector()
|
||||
newLockID, err := contentCon.PutFile(r.Context(), r.Body, r.ContentLength, lockID)
|
||||
|
||||
72
services/collaboration/pkg/locks/parser.go
Normal file
72
services/collaboration/pkg/locks/parser.go
Normal file
@@ -0,0 +1,72 @@
|
||||
// Package locks provides functionality to parse lockIDs.
|
||||
//
|
||||
// It can be used to bridge requests from different clients that send lockIDs in different formats.
|
||||
// For example, Microsoft Office Online sends the lockID in a JSON string,
|
||||
// while other clients send the lockID as a plain string.
|
||||
package locks
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// LockParser is the interface that wraps the ParseLock method
|
||||
type LockParser interface {
|
||||
ParseLock(id string) string
|
||||
}
|
||||
|
||||
// LegacyLockParser is a lock parser that can extract the lockID from a JSON string
|
||||
type LegacyLockParser struct{}
|
||||
|
||||
// NoopLockParser is a lock parser that does not change the lockID
|
||||
type NoopLockParser struct{}
|
||||
|
||||
// ParseLock will return the lockID as is
|
||||
func (*NoopLockParser) ParseLock(id string) string {
|
||||
return id
|
||||
}
|
||||
|
||||
// ParseLock extracts the lockID from a JSON string.
|
||||
// For Microsoft Office Online we need to extract the lockID from the JSON string
|
||||
// that is sent by the WOPI client.
|
||||
// The JSON string is expected to have the following format:
|
||||
//
|
||||
// {
|
||||
// "L": "12345678",
|
||||
// "F": 4,
|
||||
// "E": 2,
|
||||
// "C": "",
|
||||
// "P": "3453345345346",
|
||||
// "M": "12345678"
|
||||
// }
|
||||
//
|
||||
// or
|
||||
//
|
||||
// {
|
||||
// "S": "12345678",
|
||||
// "F": 4,
|
||||
// "E": 2,
|
||||
// "C": "",
|
||||
// "P": "3453345345346",
|
||||
// "M": "12345678"
|
||||
// }
|
||||
//
|
||||
// If the JSON string is not in the expected format, the original lockID will be returned.
|
||||
func (*LegacyLockParser) ParseLock(id string) string {
|
||||
var decodedValues map[string]interface{}
|
||||
err := json.NewDecoder(strings.NewReader(id)).Decode(&decodedValues)
|
||||
if err != nil || len(decodedValues) == 0 {
|
||||
return id
|
||||
}
|
||||
if v, ok := decodedValues["L"]; ok {
|
||||
if idString, ok := v.(string); ok {
|
||||
return idString
|
||||
}
|
||||
}
|
||||
if v, ok := decodedValues["S"]; ok {
|
||||
if idString, ok := v.(string); ok {
|
||||
return idString
|
||||
}
|
||||
}
|
||||
return id
|
||||
}
|
||||
@@ -118,8 +118,7 @@ func prepareRoutes(r *chi.Mux, options Options) {
|
||||
r.Use(func(h stdhttp.Handler) stdhttp.Handler {
|
||||
// authentication and wopi context
|
||||
return colabmiddleware.WopiContextAuthMiddleware(options.Config.Wopi.Secret, h)
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
r.Get("/", func(w stdhttp.ResponseWriter, r *stdhttp.Request) {
|
||||
adapter.CheckFileInfo(w, r)
|
||||
|
||||
Reference in New Issue
Block a user