mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2025-12-30 17:00:57 -06:00
@@ -1,8 +1,8 @@
|
||||
# Collaboration
|
||||
|
||||
The collaboration service connects ocis with document servers such as Collabora, ONLYOFFICE or Microsoft using the WOPI protocol.
|
||||
The collaboration service connects opencloud with document servers such as Collabora, ONLYOFFICE or Microsoft using the WOPI protocol.
|
||||
|
||||
Since this service requires an external document server, it won't start by default when using `ocis server`. You must start it manually with the `ocis collaboration server` command.
|
||||
Since this service requires an external document server, it won't start by default when using `opencloud server`. You must start it manually with the `opencloud collaboration server` command.
|
||||
|
||||
Because the collaboration service needs to be started manually, the following prerequisite applies: On collaboration service startup, particular environment variables are required to be populated. If environment variables have a default like the `MICRO_REGISTRY_ADDRESS`, the default will be used, if not set otherwise. Use for all others the instance values as defined. If these environment variables are not provided or misconfigured, the collaboration service will not start up.
|
||||
|
||||
@@ -20,7 +20,7 @@ The collaboration service requires the target document server (ONLYOFFICE, Colla
|
||||
* The gateway service.
|
||||
* The app-registry service.
|
||||
|
||||
If any of the named services above have not been started or are not reachable, the collaboration service won't start. For the binary or the docker release of Infinite Scale, check with the `ocis list` command if they have been started. If not, you must start them manually upfront before starting the collaboration service.
|
||||
If any of the named services above have not been started or are not reachable, the collaboration service won't start. For the binary or the docker release of Infinite Scale, check with the `opencloud list` command if they have been started. If not, you must start them manually upfront before starting the collaboration service.
|
||||
|
||||
## WOPI Configuration
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ func GetCommands(cfg *config.Config) cli.Commands {
|
||||
func Execute(cfg *config.Config) error {
|
||||
app := clihelper.DefaultApp(&cli.App{
|
||||
Name: "collaboration",
|
||||
Usage: "Serve WOPI for oCIS",
|
||||
Usage: "Serve WOPI for OpenCloud",
|
||||
Commands: GetCommands(cfg),
|
||||
})
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
defer cancel()
|
||||
|
||||
// prepare components
|
||||
if err := helpers.RegisterOcisService(ctx, cfg, logger); err != nil {
|
||||
if err := helpers.RegisterOpenCloudService(ctx, cfg, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"net/url"
|
||||
|
||||
occfg "github.com/opencloud-eu/opencloud/pkg/config"
|
||||
ocisdefaults "github.com/opencloud-eu/opencloud/pkg/config/defaults"
|
||||
ocdefaults "github.com/opencloud-eu/opencloud/pkg/config/defaults"
|
||||
"github.com/opencloud-eu/opencloud/pkg/config/envdecode"
|
||||
"github.com/opencloud-eu/opencloud/pkg/shared"
|
||||
"github.com/opencloud-eu/opencloud/services/collaboration/pkg/config"
|
||||
@@ -49,14 +49,14 @@ func Validate(cfg *config.Config) error {
|
||||
"Make sure your %s config contains the proper values "+
|
||||
"(e.g. by running opencloud init or setting it manually in "+
|
||||
"the config/corresponding environment variable): %s",
|
||||
cfg.Service.Name, ocisdefaults.BaseConfigPath(), err.Error())
|
||||
cfg.Service.Name, ocdefaults.BaseConfigPath(), err.Error())
|
||||
}
|
||||
if url.Path != "" {
|
||||
return fmt.Errorf("The WOPI Src must not contain a path in your config for %s. "+
|
||||
"Make sure your %s config contains the proper values "+
|
||||
"(e.g. by running opencloud init or setting it manually in "+
|
||||
"the config/corresponding environment variable)",
|
||||
cfg.Service.Name, ocisdefaults.BaseConfigPath())
|
||||
cfg.Service.Name, ocdefaults.BaseConfigPath())
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -5,7 +5,7 @@ type Wopi struct {
|
||||
WopiSrc string `yaml:"wopisrc" env:"COLLABORATION_WOPI_SRC" desc:"The WOPI source base URL containing schema, host and port. Set this to the schema and domain where the collaboration service is reachable for the wopi app, such as https://office.example.test." introductionVersion:"6.0.0"`
|
||||
Secret string `yaml:"secret" env:"COLLABORATION_WOPI_SECRET" desc:"Used to mint and verify WOPI JWT tokens and encrypt and decrypt the REVA JWT token embedded in the WOPI JWT token." introductionVersion:"6.0.0"`
|
||||
DisableChat bool `yaml:"disable_chat" env:"COLLABORATION_WOPI_DISABLE_CHAT;OC_WOPI_DISABLE_CHAT" desc:"Disable chat in the office web frontend. This feature applies to OnlyOffice and Microsoft." introductionVersion:"7.0.0"`
|
||||
ProxyURL string `yaml:"proxy_url" env:"COLLABORATION_WOPI_PROXY_URL" desc:"The URL to the ownCloud Office365 WOPI proxy. Optional. To use this feature, you need an office365 proxy subscription. If you become part of the Microsoft CSP program (https://learn.microsoft.com/en-us/partner-center/enroll/csp-overview), you can use WebOffice without a proxy." introductionVersion:"7.0.0"`
|
||||
ProxySecret string `yaml:"proxy_secret" env:"COLLABORATION_WOPI_PROXY_SECRET" desc:"Optional, the secret to authenticate against the ownCloud Office365 WOPI proxy. This secret can be obtained from ownCloud via the office365 proxy subscription." introductionVersion:"7.0.0"`
|
||||
ProxyURL string `yaml:"proxy_url" env:"COLLABORATION_WOPI_PROXY_URL" desc:"The URL to the OpenCloud WOPI proxy. Optional. To use this feature, you need an office365 proxy subscription. If you become part of the Microsoft CSP program (https://learn.microsoft.com/en-us/partner-center/enroll/csp-overview), you can use WebOffice without a proxy." introductionVersion:"7.0.0"`
|
||||
ProxySecret string `yaml:"proxy_secret" env:"COLLABORATION_WOPI_PROXY_SECRET" desc:"Optional, the secret to authenticate against the OpenCloud WOPI proxy. This secret can be obtained from ownCloud via the office365 proxy subscription." introductionVersion:"7.0.0"`
|
||||
ShortTokens bool `yaml:"short_tokens" env:"COLLABORATION_WOPI_SHORTTOKENS" desc:"Use short access tokens for WOPI access. This is useful for office packages, like Microsoft Office Online, which have URL length restrictions. If enabled, a persistent store must be configured." introductionVersion:"7.0.0"`
|
||||
}
|
||||
|
||||
@@ -1215,7 +1215,7 @@ func (f *FileConnector) CheckFileInfo(ctx context.Context) (*ConnectorResponse,
|
||||
breadcrumbFolderName = statRes.GetInfo().GetSpace().GetName()
|
||||
}
|
||||
|
||||
ocisURL, err := url.Parse(f.cfg.Commons.OcisURL)
|
||||
ocURL, err := url.Parse(f.cfg.Commons.OcisURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1224,17 +1224,17 @@ func (f *FileConnector) CheckFileInfo(ctx context.Context) (*ConnectorResponse,
|
||||
return nil, err
|
||||
}
|
||||
privateLinkURL := &url.URL{}
|
||||
*privateLinkURL = *ocisURL
|
||||
privateLinkURL.Path = path.Join(ocisURL.Path, "f", storagespace.FormatResourceID(statRes.GetInfo().GetId()))
|
||||
*privateLinkURL = *ocURL
|
||||
privateLinkURL.Path = path.Join(ocURL.Path, "f", storagespace.FormatResourceID(statRes.GetInfo().GetId()))
|
||||
parentFolderURL := &url.URL{}
|
||||
*parentFolderURL = *ocisURL
|
||||
*parentFolderURL = *ocURL
|
||||
if !isPublicShare {
|
||||
parentFolderURL.Path = path.Join(ocisURL.Path, "f", storagespace.FormatResourceID(statRes.GetInfo().GetParentId()))
|
||||
parentFolderURL.Path = path.Join(ocURL.Path, "f", storagespace.FormatResourceID(statRes.GetInfo().GetParentId()))
|
||||
} else {
|
||||
if scopes, ok := ctxpkg.ContextGetScopes(ctx); ok {
|
||||
publicShare := &link.PublicShare{}
|
||||
if err := f.getScopeByKeyPrefix(scopes, "publicshare:", publicShare); err == nil {
|
||||
parentFolderURL.Path = path.Join(ocisURL.Path, "s", publicShare.GetToken())
|
||||
parentFolderURL.Path = path.Join(ocURL.Path, "s", publicShare.GetToken())
|
||||
} else {
|
||||
logger.Error().Err(err).Msg("CheckFileInfo: error getting public share scope")
|
||||
}
|
||||
@@ -1253,8 +1253,8 @@ func (f *FileConnector) CheckFileInfo(ctx context.Context) (*ConnectorResponse,
|
||||
fileinfo.KeyBreadcrumbFolderName: breadcrumbFolderName,
|
||||
fileinfo.KeyBreadcrumbFolderURL: parentFolderURL.String(),
|
||||
|
||||
fileinfo.KeyHostViewURL: createHostUrl("view", ocisURL, f.cfg.App.Name, statRes.GetInfo()),
|
||||
fileinfo.KeyHostEditURL: createHostUrl("write", ocisURL, f.cfg.App.Name, statRes.GetInfo()),
|
||||
fileinfo.KeyHostViewURL: createHostUrl("view", ocURL, f.cfg.App.Name, statRes.GetInfo()),
|
||||
fileinfo.KeyHostEditURL: createHostUrl("write", ocURL, f.cfg.App.Name, statRes.GetInfo()),
|
||||
fileinfo.KeyFileSharingURL: createShareUrl(privateLinkURL),
|
||||
fileinfo.KeyFileVersionURL: createVersionsUrl(privateLinkURL),
|
||||
|
||||
@@ -1328,20 +1328,20 @@ func (f *FileConnector) createDownloadURL(wopiContext middleware.WopiContext, co
|
||||
return downloadURL.String(), nil
|
||||
}
|
||||
|
||||
func createHostUrl(mode string, ocisUrl *url.URL, appName string, info *providerv1beta1.ResourceInfo) string {
|
||||
webUrl := createAppExternalURL(ocisUrl, appName, info)
|
||||
func createHostUrl(mode string, u *url.URL, appName string, info *providerv1beta1.ResourceInfo) string {
|
||||
webUrl := createAppExternalURL(u, appName, info)
|
||||
addURLParams(webUrl, map[string]string{"view_mode": mode})
|
||||
return webUrl.String()
|
||||
}
|
||||
|
||||
func createShareUrl(ocisURL *url.URL) string {
|
||||
shareURL := *ocisURL
|
||||
func createShareUrl(u *url.URL) string {
|
||||
shareURL := *u
|
||||
addURLParams(&shareURL, map[string]string{"details": "sharing"})
|
||||
return shareURL.String()
|
||||
}
|
||||
|
||||
func createVersionsUrl(ocisURL *url.URL) string {
|
||||
versionsURL := *ocisURL
|
||||
func createVersionsUrl(u *url.URL) string {
|
||||
versionsURL := *u
|
||||
addURLParams(&versionsURL, map[string]string{"details": "versions"})
|
||||
return versionsURL.String()
|
||||
}
|
||||
@@ -1354,11 +1354,11 @@ func addURLParams(u *url.URL, params map[string]string) {
|
||||
u.RawQuery = q.Encode()
|
||||
}
|
||||
|
||||
func createAppExternalURL(ocisURL *url.URL, appName string, info *providerv1beta1.ResourceInfo) *url.URL {
|
||||
func createAppExternalURL(u *url.URL, appName string, info *providerv1beta1.ResourceInfo) *url.URL {
|
||||
spaceAlias := utils.ReadPlainFromOpaque(info.GetSpace().GetOpaque(), "spaceAlias")
|
||||
appExternalURL := *ocisURL
|
||||
appExternalURL.Path = path.Join(ocisURL.Path, "external-"+strings.ToLower(appName), spaceAlias, info.GetPath())
|
||||
q := ocisURL.Query()
|
||||
appExternalURL := *u
|
||||
appExternalURL.Path = path.Join(u.Path, "external-"+strings.ToLower(appName), spaceAlias, info.GetPath())
|
||||
q := u.Query()
|
||||
q.Add("fileId", storagespace.FormatResourceID(info.GetId()))
|
||||
appExternalURL.RawQuery = q.Encode()
|
||||
return &appExternalURL
|
||||
|
||||
@@ -16,10 +16,10 @@ import (
|
||||
"github.com/opencloud-eu/opencloud/services/collaboration/pkg/config"
|
||||
)
|
||||
|
||||
// RegisterOcisService will register this service.
|
||||
// RegisterOpenCloudService will register this service.
|
||||
// There are no explicit requirements for the context, and it will be passed
|
||||
// without changes to the underlying RegisterService method.
|
||||
func RegisterOcisService(ctx context.Context, cfg *config.Config, logger log.Logger) error {
|
||||
func RegisterOpenCloudService(ctx context.Context, cfg *config.Config, logger log.Logger) error {
|
||||
svc := registry.BuildGRPCService(cfg.GRPC.Namespace+"."+cfg.Service.Name+"."+cfg.App.Name, cfg.GRPC.Protocol, cfg.GRPC.Addr, version.GetString())
|
||||
return registry.RegisterService(ctx, logger, svc, cfg.Debug.Addr)
|
||||
}
|
||||
|
||||
@@ -31,19 +31,19 @@ func CollaborationTracingMiddleware(next http.Handler) http.Handler {
|
||||
wopiFile := wopiContext.FileReference
|
||||
|
||||
attrs := []attribute.KeyValue{
|
||||
attribute.String("ocis.wopi.sessionid", r.Header.Get("X-WOPI-SessionId")),
|
||||
attribute.String("ocis.wopi.method", wopiMethod),
|
||||
attribute.String("ocis.wopi.resource.id.storage", wopiFile.GetResourceId().GetStorageId()),
|
||||
attribute.String("ocis.wopi.resource.id.opaque", wopiFile.GetResourceId().GetOpaqueId()),
|
||||
attribute.String("ocis.wopi.resource.id.space", wopiFile.GetResourceId().GetSpaceId()),
|
||||
attribute.String("ocis.wopi.resource.path", wopiFile.GetPath()),
|
||||
attribute.String("wopi.session.id", r.Header.Get("X-WOPI-SessionId")),
|
||||
attribute.String("wopi.method", wopiMethod),
|
||||
attribute.String("cs3.resource.id.storage", wopiFile.GetResourceId().GetStorageId()),
|
||||
attribute.String("cs3.resource.id.opaque", wopiFile.GetResourceId().GetOpaqueId()),
|
||||
attribute.String("cs3.resource.id.space", wopiFile.GetResourceId().GetSpaceId()),
|
||||
attribute.String("cs3.resource.path", wopiFile.GetPath()),
|
||||
}
|
||||
|
||||
if wopiUser, ok := ctxpkg.ContextGetUser(r.Context()); ok {
|
||||
attrs = append(attrs, []attribute.KeyValue{
|
||||
attribute.String("ocis.wopi.user.idp", wopiUser.GetId().GetIdp()),
|
||||
attribute.String("ocis.wopi.user.opaque", wopiUser.GetId().GetOpaqueId()),
|
||||
attribute.String("ocis.wopi.user.type", wopiUser.GetId().GetType().String()),
|
||||
attribute.String("cs3.user.idp", wopiUser.GetId().GetIdp()),
|
||||
attribute.String("cs3.user.opaque", wopiUser.GetId().GetOpaqueId()),
|
||||
attribute.String("cs3.user.type", wopiUser.GetId().GetType().String()),
|
||||
}...)
|
||||
}
|
||||
span.SetAttributes(attrs...)
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
// If no proxy URL and proxy secret are configured, the URL will be generated
|
||||
// as a direct URL that contains the file reference.
|
||||
// Example:
|
||||
// https:/ocis.team/wopi/files/12312678470610632091729803710923
|
||||
// https:/cloud.example.test/wopi/files/12312678470610632091729803710923
|
||||
func GenerateWopiSrc(fileRef string, cfg *config.Config) (*url.URL, error) {
|
||||
wopiSrcURL, err := url.Parse(cfg.Wopi.WopiSrc)
|
||||
if err != nil {
|
||||
|
||||
@@ -16,7 +16,7 @@ var _ = Describe("Wopisrc Test", func() {
|
||||
BeforeEach(func() {
|
||||
c = &config.Config{
|
||||
Wopi: config.Wopi{
|
||||
WopiSrc: "https://ocis.team/wopi/files",
|
||||
WopiSrc: "https://cloud.example.test/wopi/files",
|
||||
ProxyURL: "https://cloud.proxy.com",
|
||||
ProxySecret: "secret",
|
||||
},
|
||||
@@ -25,7 +25,7 @@ var _ = Describe("Wopisrc Test", func() {
|
||||
When("WopiSrc URL is incorrect", func() {
|
||||
c = &config.Config{
|
||||
Wopi: config.Wopi{
|
||||
WopiSrc: "https:&//ocis.team/wopi/files",
|
||||
WopiSrc: "https:&//cloud.example.test/wopi/files",
|
||||
},
|
||||
}
|
||||
url, err := wopisrc.GenerateWopiSrc("123456", c)
|
||||
@@ -35,7 +35,7 @@ var _ = Describe("Wopisrc Test", func() {
|
||||
When("proxy URL is incorrect", func() {
|
||||
c = &config.Config{
|
||||
Wopi: config.Wopi{
|
||||
WopiSrc: "https://ocis.team/wopi/files",
|
||||
WopiSrc: "https://cloud.example.test/wopi/files",
|
||||
ProxyURL: "cloud",
|
||||
ProxySecret: "secret",
|
||||
},
|
||||
@@ -48,7 +48,7 @@ var _ = Describe("Wopisrc Test", func() {
|
||||
It("should generate a WOPI src URL as a jwt token", func() {
|
||||
url, err := wopisrc.GenerateWopiSrc("123456", c)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(url.String()).To(Equal("https://cloud.proxy.com/wopi/files/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1IjoiaHR0cHM6Ly9vY2lzLnRlYW0vd29waS9maWxlcy8iLCJmIjoiMTIzNDU2In0.6ol9PQXGKktKfAri8tsJ4X_a9rIeosJ7id6KTQW6Ui0"))
|
||||
Expect(url.String()).To(Equal("https://cloud.proxy.com/wopi/files/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1IjoiaHR0cHM6Ly9jbG91ZC5leGFtcGxlLnRlc3Qvd29waS9maWxlcy8iLCJmIjoiMTIzNDU2In0.LzyGPanHKxjLlIPoyfGU4cAUxzy3FAmBqMIqLCSHclg"))
|
||||
})
|
||||
})
|
||||
When("proxy URL and proxy secret are not configured", func() {
|
||||
@@ -57,7 +57,7 @@ var _ = Describe("Wopisrc Test", func() {
|
||||
c.Wopi.ProxySecret = ""
|
||||
url, err := wopisrc.GenerateWopiSrc("123456", c)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(url.String()).To(Equal("https://ocis.team/wopi/files/123456"))
|
||||
Expect(url.String()).To(Equal("https://cloud.example.test/wopi/files/123456"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user