From 9d567e8efd4d46f10bcae2feeabfc2f868672be3 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Wed, 10 Jul 2024 13:35:15 +0200 Subject: [PATCH 01/16] disable collabora chat Signed-off-by: Christian Richter --- services/collaboration/pkg/service/grpc/v0/service.go | 1 + 1 file changed, 1 insertion(+) diff --git a/services/collaboration/pkg/service/grpc/v0/service.go b/services/collaboration/pkg/service/grpc/v0/service.go index 8c203b10c7..2232f1dd89 100644 --- a/services/collaboration/pkg/service/grpc/v0/service.go +++ b/services/collaboration/pkg/service/grpc/v0/service.go @@ -151,6 +151,7 @@ func (s *Service) OpenInApp( q := u.Query() q.Add("WOPISrc", wopiSrcURL.String()) + q.Add("dchat", "1") qs := q.Encode() u.RawQuery = qs From 7d6d57d997930d36ab25dba8f3498c4388cada00 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Wed, 10 Jul 2024 14:38:26 +0200 Subject: [PATCH 02/16] adapt test Signed-off-by: Christian Richter --- services/collaboration/pkg/service/grpc/v0/service_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/collaboration/pkg/service/grpc/v0/service_test.go b/services/collaboration/pkg/service/grpc/v0/service_test.go index c8b6544bcd..04f724315a 100644 --- a/services/collaboration/pkg/service/grpc/v0/service_test.go +++ b/services/collaboration/pkg/service/grpc/v0/service_test.go @@ -173,7 +173,7 @@ var _ = Describe("Discovery", func() { Expect(err).To(Succeed()) Expect(resp.GetStatus().GetCode()).To(Equal(rpcv1beta1.Code_CODE_OK)) Expect(resp.GetAppUrl().GetMethod()).To(Equal("POST")) - Expect(resp.GetAppUrl().GetAppUrl()).To(Equal("https://test.server.prv/hosting/wopi/word/edit?WOPISrc=https%3A%2F%2Fwopiserver.test.prv%2Fwopi%2Ffiles%2F2f6ec18696dd1008106749bd94106e5cfad5c09e15de7b77088d03843e71b43e")) + Expect(resp.GetAppUrl().GetAppUrl()).To(Equal("https://test.server.prv/hosting/wopi/word/edit?WOPISrc=https%3A%2F%2Fwopiserver.test.prv%2Fwopi%2Ffiles%2F2f6ec18696dd1008106749bd94106e5cfad5c09e15de7b77088d03843e71b43e&dchat=1")) Expect(resp.GetAppUrl().GetFormParameters()["access_token_ttl"]).To(Equal(strconv.FormatInt(nowTime.Add(5*time.Hour).Unix()*1000, 10))) }) }) From 0ce6a01874fc5723731771295636cd721fa9bd75 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Wed, 10 Jul 2024 15:13:13 +0200 Subject: [PATCH 03/16] make sonarcloud happy Signed-off-by: Christian Richter --- .../pkg/service/grpc/v0/service.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/services/collaboration/pkg/service/grpc/v0/service.go b/services/collaboration/pkg/service/grpc/v0/service.go index 2232f1dd89..b5a8a76c9e 100644 --- a/services/collaboration/pkg/service/grpc/v0/service.go +++ b/services/collaboration/pkg/service/grpc/v0/service.go @@ -25,7 +25,10 @@ import ( // NewHandler creates a new grpc service implementing the OpenInApp interface func NewHandler(opts ...Option) (*Service, func(), error) { - teardown := func() {} + teardown := func() { + /* this is required as a argument for the return value to satisfy the interface */ + /* in case you are wondering about the necessity of this comment, sonarcloud is asking for it */ + } options := newOptions(opts...) gwc := options.Gwc @@ -93,18 +96,18 @@ func (s *Service) OpenInApp( var viewAppURL string var editAppURL string if viewCommentAppURLs, ok := s.appURLs["view_comment"]; ok { - if url := viewCommentAppURLs[fileExt]; ok { - viewCommentAppURL = url + if u, ok := viewCommentAppURLs[fileExt]; ok { + viewCommentAppURL = u } } if viewAppURLs, ok := s.appURLs["view"]; ok { - if url := viewAppURLs[fileExt]; ok { - viewAppURL = url + if u, ok := viewAppURLs[fileExt]; ok { + viewAppURL = u } } if editAppURLs, ok := s.appURLs["edit"]; ok { - if url, ok := editAppURLs[fileExt]; ok { - editAppURL = url + if u, ok := editAppURLs[fileExt]; ok { + editAppURL = u } } if editAppURL == "" && viewAppURL == "" && viewCommentAppURL == "" { From c3ae99dea2c9bd27d0c964ebe279950882b50a2a Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Thu, 11 Jul 2024 15:12:38 +0200 Subject: [PATCH 04/16] Add missing language variables Co-authored-by: Michael Barz Signed-off-by: Christian Richter --- services/collaboration/pkg/service/grpc/v0/service.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/collaboration/pkg/service/grpc/v0/service.go b/services/collaboration/pkg/service/grpc/v0/service.go index b5a8a76c9e..f6b460951b 100644 --- a/services/collaboration/pkg/service/grpc/v0/service.go +++ b/services/collaboration/pkg/service/grpc/v0/service.go @@ -152,9 +152,14 @@ func (s *Service) OpenInApp( return "", err } + lang := utils.ReadPlainFromOpaque(req.GetOpaque(), "lang") + q := u.Query() q.Add("WOPISrc", wopiSrcURL.String()) q.Add("dchat", "1") + q.Add("ui", lang) // OnlyOffice + q.Add("lang", lang) // Collabora, Impact on the default document language of OnlyOffice + q.Add("UI_LLCC", lang) // Office365 qs := q.Encode() u.RawQuery = qs From 73c7a4a1a53c8fcad59a569c3aeaa3869ce88598 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Thu, 11 Jul 2024 15:13:05 +0200 Subject: [PATCH 05/16] Make disable chat configurable Co-authored-by: Michael Barz Signed-off-by: Christian Richter --- services/app-provider/pkg/config/config.go | 2 +- services/collaboration/pkg/config/wopi.go | 5 +++-- services/collaboration/pkg/service/grpc/v0/service.go | 6 +++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/services/app-provider/pkg/config/config.go b/services/app-provider/pkg/config/config.go index cb462cded7..4421ca692a 100644 --- a/services/app-provider/pkg/config/config.go +++ b/services/app-provider/pkg/config/config.go @@ -62,7 +62,7 @@ type WOPIDriver struct { AppInternalURL string `yaml:"app_internal_url" env:"APP_PROVIDER_WOPI_APP_INTERNAL_URL" desc:"Internal URL to the app, like in your DMZ." introductionVersion:"pre5.0"` AppName string `yaml:"app_name" env:"APP_PROVIDER_WOPI_APP_NAME" desc:"Human readable app name." introductionVersion:"pre5.0"` AppURL string `yaml:"app_url" env:"APP_PROVIDER_WOPI_APP_URL" desc:"URL for end users to access the app." introductionVersion:"pre5.0"` - AppDisableChat bool `yaml:"app_disable_chat" env:"APP_PROVIDER_WOPI_DISABLE_CHAT" desc:"Disable the chat functionality of the office app." introductionVersion:"pre5.0"` + AppDisableChat bool `yaml:"app_disable_chat" env:"APP_PROVIDER_WOPI_DISABLE_CHAT;OCIS_WOPI_DISABLE_CHAT" desc:"Disable the chat functionality of the office app." introductionVersion:"pre5.0"` Insecure bool `yaml:"insecure" env:"APP_PROVIDER_WOPI_INSECURE" desc:"Disable TLS certificate validation for requests to the WOPI server and the web office application. Do not set this in production environments." introductionVersion:"pre5.0"` IopSecret string `yaml:"wopi_server_iop_secret" env:"APP_PROVIDER_WOPI_WOPI_SERVER_IOP_SECRET" desc:"Shared secret of the CS3org WOPI server." introductionVersion:"pre5.0"` WopiURL string `yaml:"wopi_server_external_url" env:"APP_PROVIDER_WOPI_WOPI_SERVER_EXTERNAL_URL" desc:"External url of the CS3org WOPI server." introductionVersion:"pre5.0"` diff --git a/services/collaboration/pkg/config/wopi.go b/services/collaboration/pkg/config/wopi.go index c0f4b01fc1..51e4caa3bd 100644 --- a/services/collaboration/pkg/config/wopi.go +++ b/services/collaboration/pkg/config/wopi.go @@ -2,6 +2,7 @@ package config // Wopi defines the available configuration for the WOPI endpoint. type Wopi struct { - WopiSrc string `yaml:"wopisrc" env:"COLLABORATION_WOPI_SRC" desc:"The WOPISrc 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.owncloud.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"` + WopiSrc string `yaml:"wopisrc" env:"COLLABORATION_WOPI_SRC" desc:"The WOPISrc 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.owncloud.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;OCIS_WOPI_DISABLE_CHAT" desc:"Disable chat in the frontend." introductionVersion:"%%NEXT%%"` } diff --git a/services/collaboration/pkg/service/grpc/v0/service.go b/services/collaboration/pkg/service/grpc/v0/service.go index f6b460951b..7dbb2e794e 100644 --- a/services/collaboration/pkg/service/grpc/v0/service.go +++ b/services/collaboration/pkg/service/grpc/v0/service.go @@ -156,7 +156,11 @@ func (s *Service) OpenInApp( q := u.Query() q.Add("WOPISrc", wopiSrcURL.String()) - q.Add("dchat", "1") + + if s.config.Wopi.DisableChat { + q.Add("dchat", "1") + } + q.Add("ui", lang) // OnlyOffice q.Add("lang", lang) // Collabora, Impact on the default document language of OnlyOffice q.Add("UI_LLCC", lang) // Office365 From 16ced06cad92ee5301820c3fc8486b84a3b34142 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Thu, 11 Jul 2024 15:27:30 +0200 Subject: [PATCH 06/16] adapt test Signed-off-by: Christian Richter --- .../pkg/service/grpc/v0/service_test.go | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/services/collaboration/pkg/service/grpc/v0/service_test.go b/services/collaboration/pkg/service/grpc/v0/service_test.go index 04f724315a..c1467ee321 100644 --- a/services/collaboration/pkg/service/grpc/v0/service_test.go +++ b/services/collaboration/pkg/service/grpc/v0/service_test.go @@ -173,7 +173,50 @@ var _ = Describe("Discovery", func() { Expect(err).To(Succeed()) Expect(resp.GetStatus().GetCode()).To(Equal(rpcv1beta1.Code_CODE_OK)) Expect(resp.GetAppUrl().GetMethod()).To(Equal("POST")) - Expect(resp.GetAppUrl().GetAppUrl()).To(Equal("https://test.server.prv/hosting/wopi/word/edit?WOPISrc=https%3A%2F%2Fwopiserver.test.prv%2Fwopi%2Ffiles%2F2f6ec18696dd1008106749bd94106e5cfad5c09e15de7b77088d03843e71b43e&dchat=1")) + Expect(resp.GetAppUrl().GetAppUrl()).To(Equal("https://test.server.prv/hosting/wopi/word/edit?UI_LLCC=&WOPISrc=https%3A%2F%2Fwopiserver.test.prv%2Fwopi%2Ffiles%2F2f6ec18696dd1008106749bd94106e5cfad5c09e15de7b77088d03843e71b43e&lang=&ui=")) + Expect(resp.GetAppUrl().GetFormParameters()["access_token_ttl"]).To(Equal(strconv.FormatInt(nowTime.Add(5*time.Hour).Unix()*1000, 10))) + }) + + It("Success", func() { + ctx := context.Background() + nowTime := time.Now() + + cfg.Wopi.WopiSrc = "https://wopiserver.test.prv" + cfg.Wopi.Secret = "my_supa_secret" + cfg.Wopi.DisableChat = true + + myself := &userv1beta1.User{ + Id: &userv1beta1.UserId{ + Idp: "myIdp", + OpaqueId: "opaque001", + Type: userv1beta1.UserType_USER_TYPE_PRIMARY, + }, + Username: "username", + } + + req := &appproviderv1beta1.OpenInAppRequest{ + ResourceInfo: &providerv1beta1.ResourceInfo{ + Id: &providerv1beta1.ResourceId{ + StorageId: "myStorage", + OpaqueId: "storageOpaque001", + SpaceId: "SpaceA", + }, + Path: "/path/to/file.docx", + }, + ViewMode: appproviderv1beta1.ViewMode_VIEW_MODE_READ_WRITE, + AccessToken: MintToken(myself, cfg.Wopi.Secret, nowTime), + } + + gatewayClient.On("WhoAmI", mock.Anything, mock.Anything).Times(1).Return(&gatewayv1beta1.WhoAmIResponse{ + Status: status.NewOK(ctx), + User: myself, + }, nil) + + resp, err := srv.OpenInApp(ctx, req) + Expect(err).To(Succeed()) + Expect(resp.GetStatus().GetCode()).To(Equal(rpcv1beta1.Code_CODE_OK)) + Expect(resp.GetAppUrl().GetMethod()).To(Equal("POST")) + Expect(resp.GetAppUrl().GetAppUrl()).To(Equal("https://test.server.prv/hosting/wopi/word/edit?UI_LLCC=&WOPISrc=https%3A%2F%2Fwopiserver.test.prv%2Fwopi%2Ffiles%2F2f6ec18696dd1008106749bd94106e5cfad5c09e15de7b77088d03843e71b43e&dchat=1&lang=&ui=")) Expect(resp.GetAppUrl().GetFormParameters()["access_token_ttl"]).To(Equal(strconv.FormatInt(nowTime.Add(5*time.Hour).Unix()*1000, 10))) }) }) From d4fef06ef9c0828f69e768760bfeaafce22f8c52 Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Thu, 11 Jul 2024 21:59:18 +0200 Subject: [PATCH 07/16] tests: add lang to opaque --- services/collaboration/pkg/service/grpc/v0/service_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/collaboration/pkg/service/grpc/v0/service_test.go b/services/collaboration/pkg/service/grpc/v0/service_test.go index c1467ee321..de2efbd287 100644 --- a/services/collaboration/pkg/service/grpc/v0/service_test.go +++ b/services/collaboration/pkg/service/grpc/v0/service_test.go @@ -5,6 +5,7 @@ import ( "strconv" "time" + "github.com/cs3org/reva/v2/pkg/utils" "github.com/golang-jwt/jwt/v4" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -163,6 +164,7 @@ var _ = Describe("Discovery", func() { ViewMode: appproviderv1beta1.ViewMode_VIEW_MODE_READ_WRITE, AccessToken: MintToken(myself, cfg.Wopi.Secret, nowTime), } + req.Opaque = utils.AppendPlainToOpaque(req.Opaque, "lang", "de") gatewayClient.On("WhoAmI", mock.Anything, mock.Anything).Times(1).Return(&gatewayv1beta1.WhoAmIResponse{ Status: status.NewOK(ctx), @@ -173,7 +175,7 @@ var _ = Describe("Discovery", func() { Expect(err).To(Succeed()) Expect(resp.GetStatus().GetCode()).To(Equal(rpcv1beta1.Code_CODE_OK)) Expect(resp.GetAppUrl().GetMethod()).To(Equal("POST")) - Expect(resp.GetAppUrl().GetAppUrl()).To(Equal("https://test.server.prv/hosting/wopi/word/edit?UI_LLCC=&WOPISrc=https%3A%2F%2Fwopiserver.test.prv%2Fwopi%2Ffiles%2F2f6ec18696dd1008106749bd94106e5cfad5c09e15de7b77088d03843e71b43e&lang=&ui=")) + Expect(resp.GetAppUrl().GetAppUrl()).To(Equal("https://test.server.prv/hosting/wopi/word/edit?UI_LLCC=de&WOPISrc=https%3A%2F%2Fwopiserver.test.prv%2Fwopi%2Ffiles%2F2f6ec18696dd1008106749bd94106e5cfad5c09e15de7b77088d03843e71b43e&lang=de&ui=de")) Expect(resp.GetAppUrl().GetFormParameters()["access_token_ttl"]).To(Equal(strconv.FormatInt(nowTime.Add(5*time.Hour).Unix()*1000, 10))) }) From e93a132e170104bd19667bc82029ae269c1e9cc3 Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Thu, 11 Jul 2024 22:02:57 +0200 Subject: [PATCH 08/16] docs: add changelog --- changelog/unreleased/missing-wopi-keys.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelog/unreleased/missing-wopi-keys.md diff --git a/changelog/unreleased/missing-wopi-keys.md b/changelog/unreleased/missing-wopi-keys.md new file mode 100644 index 0000000000..cab48f9c23 --- /dev/null +++ b/changelog/unreleased/missing-wopi-keys.md @@ -0,0 +1,5 @@ +Enhancement: Add missing WOPI features + +We added the feature to disable the chat for onlyoffice and added the missing language parameters to the wopi app url. + +https://github.com/owncloud/ocis/pull/9580 From cfb145f03cfa5778e5cb89af7390dc1b89dafb08 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Wed, 10 Jul 2024 13:35:15 +0200 Subject: [PATCH 09/16] disable collabora chat Signed-off-by: Christian Richter --- services/collaboration/pkg/service/grpc/v0/service.go | 1 + 1 file changed, 1 insertion(+) diff --git a/services/collaboration/pkg/service/grpc/v0/service.go b/services/collaboration/pkg/service/grpc/v0/service.go index 8c203b10c7..2232f1dd89 100644 --- a/services/collaboration/pkg/service/grpc/v0/service.go +++ b/services/collaboration/pkg/service/grpc/v0/service.go @@ -151,6 +151,7 @@ func (s *Service) OpenInApp( q := u.Query() q.Add("WOPISrc", wopiSrcURL.String()) + q.Add("dchat", "1") qs := q.Encode() u.RawQuery = qs From 543f9ac08b4375465ad5538d1d15765cb91cbf3c Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Wed, 10 Jul 2024 14:38:26 +0200 Subject: [PATCH 10/16] adapt test Signed-off-by: Christian Richter --- services/collaboration/pkg/service/grpc/v0/service_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/collaboration/pkg/service/grpc/v0/service_test.go b/services/collaboration/pkg/service/grpc/v0/service_test.go index c8b6544bcd..04f724315a 100644 --- a/services/collaboration/pkg/service/grpc/v0/service_test.go +++ b/services/collaboration/pkg/service/grpc/v0/service_test.go @@ -173,7 +173,7 @@ var _ = Describe("Discovery", func() { Expect(err).To(Succeed()) Expect(resp.GetStatus().GetCode()).To(Equal(rpcv1beta1.Code_CODE_OK)) Expect(resp.GetAppUrl().GetMethod()).To(Equal("POST")) - Expect(resp.GetAppUrl().GetAppUrl()).To(Equal("https://test.server.prv/hosting/wopi/word/edit?WOPISrc=https%3A%2F%2Fwopiserver.test.prv%2Fwopi%2Ffiles%2F2f6ec18696dd1008106749bd94106e5cfad5c09e15de7b77088d03843e71b43e")) + Expect(resp.GetAppUrl().GetAppUrl()).To(Equal("https://test.server.prv/hosting/wopi/word/edit?WOPISrc=https%3A%2F%2Fwopiserver.test.prv%2Fwopi%2Ffiles%2F2f6ec18696dd1008106749bd94106e5cfad5c09e15de7b77088d03843e71b43e&dchat=1")) Expect(resp.GetAppUrl().GetFormParameters()["access_token_ttl"]).To(Equal(strconv.FormatInt(nowTime.Add(5*time.Hour).Unix()*1000, 10))) }) }) From a0528d4dfba90f920e1f62743af94f8a81a864f0 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Wed, 10 Jul 2024 15:13:13 +0200 Subject: [PATCH 11/16] make sonarcloud happy Signed-off-by: Christian Richter --- .../pkg/service/grpc/v0/service.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/services/collaboration/pkg/service/grpc/v0/service.go b/services/collaboration/pkg/service/grpc/v0/service.go index 2232f1dd89..b5a8a76c9e 100644 --- a/services/collaboration/pkg/service/grpc/v0/service.go +++ b/services/collaboration/pkg/service/grpc/v0/service.go @@ -25,7 +25,10 @@ import ( // NewHandler creates a new grpc service implementing the OpenInApp interface func NewHandler(opts ...Option) (*Service, func(), error) { - teardown := func() {} + teardown := func() { + /* this is required as a argument for the return value to satisfy the interface */ + /* in case you are wondering about the necessity of this comment, sonarcloud is asking for it */ + } options := newOptions(opts...) gwc := options.Gwc @@ -93,18 +96,18 @@ func (s *Service) OpenInApp( var viewAppURL string var editAppURL string if viewCommentAppURLs, ok := s.appURLs["view_comment"]; ok { - if url := viewCommentAppURLs[fileExt]; ok { - viewCommentAppURL = url + if u, ok := viewCommentAppURLs[fileExt]; ok { + viewCommentAppURL = u } } if viewAppURLs, ok := s.appURLs["view"]; ok { - if url := viewAppURLs[fileExt]; ok { - viewAppURL = url + if u, ok := viewAppURLs[fileExt]; ok { + viewAppURL = u } } if editAppURLs, ok := s.appURLs["edit"]; ok { - if url, ok := editAppURLs[fileExt]; ok { - editAppURL = url + if u, ok := editAppURLs[fileExt]; ok { + editAppURL = u } } if editAppURL == "" && viewAppURL == "" && viewCommentAppURL == "" { From 04529e18e92d1e3800754302d7ac510dce08d382 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Thu, 11 Jul 2024 15:12:38 +0200 Subject: [PATCH 12/16] Add missing language variables Co-authored-by: Michael Barz Signed-off-by: Christian Richter --- services/collaboration/pkg/service/grpc/v0/service.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/collaboration/pkg/service/grpc/v0/service.go b/services/collaboration/pkg/service/grpc/v0/service.go index b5a8a76c9e..f6b460951b 100644 --- a/services/collaboration/pkg/service/grpc/v0/service.go +++ b/services/collaboration/pkg/service/grpc/v0/service.go @@ -152,9 +152,14 @@ func (s *Service) OpenInApp( return "", err } + lang := utils.ReadPlainFromOpaque(req.GetOpaque(), "lang") + q := u.Query() q.Add("WOPISrc", wopiSrcURL.String()) q.Add("dchat", "1") + q.Add("ui", lang) // OnlyOffice + q.Add("lang", lang) // Collabora, Impact on the default document language of OnlyOffice + q.Add("UI_LLCC", lang) // Office365 qs := q.Encode() u.RawQuery = qs From 010cccde918d43971ce01e531300efa97ab842e3 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Thu, 11 Jul 2024 15:13:05 +0200 Subject: [PATCH 13/16] Make disable chat configurable Co-authored-by: Michael Barz Signed-off-by: Christian Richter --- services/app-provider/pkg/config/config.go | 2 +- services/collaboration/pkg/config/wopi.go | 5 +++-- services/collaboration/pkg/service/grpc/v0/service.go | 6 +++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/services/app-provider/pkg/config/config.go b/services/app-provider/pkg/config/config.go index cb462cded7..4421ca692a 100644 --- a/services/app-provider/pkg/config/config.go +++ b/services/app-provider/pkg/config/config.go @@ -62,7 +62,7 @@ type WOPIDriver struct { AppInternalURL string `yaml:"app_internal_url" env:"APP_PROVIDER_WOPI_APP_INTERNAL_URL" desc:"Internal URL to the app, like in your DMZ." introductionVersion:"pre5.0"` AppName string `yaml:"app_name" env:"APP_PROVIDER_WOPI_APP_NAME" desc:"Human readable app name." introductionVersion:"pre5.0"` AppURL string `yaml:"app_url" env:"APP_PROVIDER_WOPI_APP_URL" desc:"URL for end users to access the app." introductionVersion:"pre5.0"` - AppDisableChat bool `yaml:"app_disable_chat" env:"APP_PROVIDER_WOPI_DISABLE_CHAT" desc:"Disable the chat functionality of the office app." introductionVersion:"pre5.0"` + AppDisableChat bool `yaml:"app_disable_chat" env:"APP_PROVIDER_WOPI_DISABLE_CHAT;OCIS_WOPI_DISABLE_CHAT" desc:"Disable the chat functionality of the office app." introductionVersion:"pre5.0"` Insecure bool `yaml:"insecure" env:"APP_PROVIDER_WOPI_INSECURE" desc:"Disable TLS certificate validation for requests to the WOPI server and the web office application. Do not set this in production environments." introductionVersion:"pre5.0"` IopSecret string `yaml:"wopi_server_iop_secret" env:"APP_PROVIDER_WOPI_WOPI_SERVER_IOP_SECRET" desc:"Shared secret of the CS3org WOPI server." introductionVersion:"pre5.0"` WopiURL string `yaml:"wopi_server_external_url" env:"APP_PROVIDER_WOPI_WOPI_SERVER_EXTERNAL_URL" desc:"External url of the CS3org WOPI server." introductionVersion:"pre5.0"` diff --git a/services/collaboration/pkg/config/wopi.go b/services/collaboration/pkg/config/wopi.go index c0f4b01fc1..51e4caa3bd 100644 --- a/services/collaboration/pkg/config/wopi.go +++ b/services/collaboration/pkg/config/wopi.go @@ -2,6 +2,7 @@ package config // Wopi defines the available configuration for the WOPI endpoint. type Wopi struct { - WopiSrc string `yaml:"wopisrc" env:"COLLABORATION_WOPI_SRC" desc:"The WOPISrc 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.owncloud.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"` + WopiSrc string `yaml:"wopisrc" env:"COLLABORATION_WOPI_SRC" desc:"The WOPISrc 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.owncloud.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;OCIS_WOPI_DISABLE_CHAT" desc:"Disable chat in the frontend." introductionVersion:"%%NEXT%%"` } diff --git a/services/collaboration/pkg/service/grpc/v0/service.go b/services/collaboration/pkg/service/grpc/v0/service.go index f6b460951b..7dbb2e794e 100644 --- a/services/collaboration/pkg/service/grpc/v0/service.go +++ b/services/collaboration/pkg/service/grpc/v0/service.go @@ -156,7 +156,11 @@ func (s *Service) OpenInApp( q := u.Query() q.Add("WOPISrc", wopiSrcURL.String()) - q.Add("dchat", "1") + + if s.config.Wopi.DisableChat { + q.Add("dchat", "1") + } + q.Add("ui", lang) // OnlyOffice q.Add("lang", lang) // Collabora, Impact on the default document language of OnlyOffice q.Add("UI_LLCC", lang) // Office365 From 5359d6ddeeff6e98f68c72f28d114a46d4428e91 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Thu, 11 Jul 2024 15:27:30 +0200 Subject: [PATCH 14/16] adapt test Signed-off-by: Christian Richter --- .../pkg/service/grpc/v0/service_test.go | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/services/collaboration/pkg/service/grpc/v0/service_test.go b/services/collaboration/pkg/service/grpc/v0/service_test.go index 04f724315a..c1467ee321 100644 --- a/services/collaboration/pkg/service/grpc/v0/service_test.go +++ b/services/collaboration/pkg/service/grpc/v0/service_test.go @@ -173,7 +173,50 @@ var _ = Describe("Discovery", func() { Expect(err).To(Succeed()) Expect(resp.GetStatus().GetCode()).To(Equal(rpcv1beta1.Code_CODE_OK)) Expect(resp.GetAppUrl().GetMethod()).To(Equal("POST")) - Expect(resp.GetAppUrl().GetAppUrl()).To(Equal("https://test.server.prv/hosting/wopi/word/edit?WOPISrc=https%3A%2F%2Fwopiserver.test.prv%2Fwopi%2Ffiles%2F2f6ec18696dd1008106749bd94106e5cfad5c09e15de7b77088d03843e71b43e&dchat=1")) + Expect(resp.GetAppUrl().GetAppUrl()).To(Equal("https://test.server.prv/hosting/wopi/word/edit?UI_LLCC=&WOPISrc=https%3A%2F%2Fwopiserver.test.prv%2Fwopi%2Ffiles%2F2f6ec18696dd1008106749bd94106e5cfad5c09e15de7b77088d03843e71b43e&lang=&ui=")) + Expect(resp.GetAppUrl().GetFormParameters()["access_token_ttl"]).To(Equal(strconv.FormatInt(nowTime.Add(5*time.Hour).Unix()*1000, 10))) + }) + + It("Success", func() { + ctx := context.Background() + nowTime := time.Now() + + cfg.Wopi.WopiSrc = "https://wopiserver.test.prv" + cfg.Wopi.Secret = "my_supa_secret" + cfg.Wopi.DisableChat = true + + myself := &userv1beta1.User{ + Id: &userv1beta1.UserId{ + Idp: "myIdp", + OpaqueId: "opaque001", + Type: userv1beta1.UserType_USER_TYPE_PRIMARY, + }, + Username: "username", + } + + req := &appproviderv1beta1.OpenInAppRequest{ + ResourceInfo: &providerv1beta1.ResourceInfo{ + Id: &providerv1beta1.ResourceId{ + StorageId: "myStorage", + OpaqueId: "storageOpaque001", + SpaceId: "SpaceA", + }, + Path: "/path/to/file.docx", + }, + ViewMode: appproviderv1beta1.ViewMode_VIEW_MODE_READ_WRITE, + AccessToken: MintToken(myself, cfg.Wopi.Secret, nowTime), + } + + gatewayClient.On("WhoAmI", mock.Anything, mock.Anything).Times(1).Return(&gatewayv1beta1.WhoAmIResponse{ + Status: status.NewOK(ctx), + User: myself, + }, nil) + + resp, err := srv.OpenInApp(ctx, req) + Expect(err).To(Succeed()) + Expect(resp.GetStatus().GetCode()).To(Equal(rpcv1beta1.Code_CODE_OK)) + Expect(resp.GetAppUrl().GetMethod()).To(Equal("POST")) + Expect(resp.GetAppUrl().GetAppUrl()).To(Equal("https://test.server.prv/hosting/wopi/word/edit?UI_LLCC=&WOPISrc=https%3A%2F%2Fwopiserver.test.prv%2Fwopi%2Ffiles%2F2f6ec18696dd1008106749bd94106e5cfad5c09e15de7b77088d03843e71b43e&dchat=1&lang=&ui=")) Expect(resp.GetAppUrl().GetFormParameters()["access_token_ttl"]).To(Equal(strconv.FormatInt(nowTime.Add(5*time.Hour).Unix()*1000, 10))) }) }) From 58c3c664294cc8e14514b8c434551a18006c7810 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Fri, 12 Jul 2024 09:30:54 +0200 Subject: [PATCH 15/16] ignore empty language setting & adapt tests Signed-off-by: Christian Richter --- .../collaboration/pkg/service/grpc/v0/service.go | 12 +++++++----- .../pkg/service/grpc/v0/service_test.go | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/services/collaboration/pkg/service/grpc/v0/service.go b/services/collaboration/pkg/service/grpc/v0/service.go index 7dbb2e794e..d0e6361447 100644 --- a/services/collaboration/pkg/service/grpc/v0/service.go +++ b/services/collaboration/pkg/service/grpc/v0/service.go @@ -152,8 +152,6 @@ func (s *Service) OpenInApp( return "", err } - lang := utils.ReadPlainFromOpaque(req.GetOpaque(), "lang") - q := u.Query() q.Add("WOPISrc", wopiSrcURL.String()) @@ -161,9 +159,13 @@ func (s *Service) OpenInApp( q.Add("dchat", "1") } - q.Add("ui", lang) // OnlyOffice - q.Add("lang", lang) // Collabora, Impact on the default document language of OnlyOffice - q.Add("UI_LLCC", lang) // Office365 + lang := utils.ReadPlainFromOpaque(req.GetOpaque(), "lang") + + if lang != "" { + q.Add("ui", lang) // OnlyOffice + q.Add("lang", lang) // Collabora, Impact on the default document language of OnlyOffice + q.Add("UI_LLCC", lang) // Office365 + } qs := q.Encode() u.RawQuery = qs diff --git a/services/collaboration/pkg/service/grpc/v0/service_test.go b/services/collaboration/pkg/service/grpc/v0/service_test.go index ca433f035f..e2361c090c 100644 --- a/services/collaboration/pkg/service/grpc/v0/service_test.go +++ b/services/collaboration/pkg/service/grpc/v0/service_test.go @@ -218,7 +218,7 @@ var _ = Describe("Discovery", func() { Expect(err).To(Succeed()) Expect(resp.GetStatus().GetCode()).To(Equal(rpcv1beta1.Code_CODE_OK)) Expect(resp.GetAppUrl().GetMethod()).To(Equal("POST")) - Expect(resp.GetAppUrl().GetAppUrl()).To(Equal("https://test.server.prv/hosting/wopi/word/edit?UI_LLCC=&WOPISrc=https%3A%2F%2Fwopiserver.test.prv%2Fwopi%2Ffiles%2F2f6ec18696dd1008106749bd94106e5cfad5c09e15de7b77088d03843e71b43e&lang=&ui=")) + Expect(resp.GetAppUrl().GetAppUrl()).To(Equal("https://test.server.prv/hosting/wopi/word/edit?WOPISrc=https%3A%2F%2Fwopiserver.test.prv%2Fwopi%2Ffiles%2F2f6ec18696dd1008106749bd94106e5cfad5c09e15de7b77088d03843e71b43e&dchat=1")) Expect(resp.GetAppUrl().GetFormParameters()["access_token_ttl"]).To(Equal(strconv.FormatInt(nowTime.Add(5*time.Hour).Unix()*1000, 10))) }) @@ -261,7 +261,7 @@ var _ = Describe("Discovery", func() { Expect(err).To(Succeed()) Expect(resp.GetStatus().GetCode()).To(Equal(rpcv1beta1.Code_CODE_OK)) Expect(resp.GetAppUrl().GetMethod()).To(Equal("POST")) - Expect(resp.GetAppUrl().GetAppUrl()).To(Equal("https://test.server.prv/hosting/wopi/word/edit?UI_LLCC=&WOPISrc=https%3A%2F%2Fwopiserver.test.prv%2Fwopi%2Ffiles%2F2f6ec18696dd1008106749bd94106e5cfad5c09e15de7b77088d03843e71b43e&dchat=1&lang=&ui=")) + Expect(resp.GetAppUrl().GetAppUrl()).To(Equal("https://test.server.prv/hosting/wopi/word/edit?WOPISrc=https%3A%2F%2Fwopiserver.test.prv%2Fwopi%2Ffiles%2F2f6ec18696dd1008106749bd94106e5cfad5c09e15de7b77088d03843e71b43e&dchat=1")) Expect(resp.GetAppUrl().GetFormParameters()["access_token_ttl"]).To(Equal(strconv.FormatInt(nowTime.Add(5*time.Hour).Unix()*1000, 10))) }) }) From c99550984e5eb3644e9308de281e62ebcfa27fa1 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Fri, 12 Jul 2024 12:00:18 +0200 Subject: [PATCH 16/16] change providerv1beta1.Reference to pointer Signed-off-by: Christian Richter --- .../pkg/connector/contentconnector.go | 6 +++--- .../pkg/connector/contentconnector_test.go | 4 ++-- .../pkg/connector/fileconnector.go | 18 +++++++++--------- .../pkg/connector/fileconnector_test.go | 2 +- .../pkg/middleware/wopicontext.go | 2 +- .../pkg/service/grpc/v0/service.go | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/services/collaboration/pkg/connector/contentconnector.go b/services/collaboration/pkg/connector/contentconnector.go index 5de55c7963..f450ef5c47 100644 --- a/services/collaboration/pkg/connector/contentconnector.go +++ b/services/collaboration/pkg/connector/contentconnector.go @@ -71,7 +71,7 @@ func (c *ContentConnector) GetFile(ctx context.Context, writer io.Writer) error // Initiate download request req := &providerv1beta1.InitiateFileDownloadRequest{ - Ref: &wopiContext.FileReference, + Ref: wopiContext.FileReference, } if wopiContext.ViewMode == appproviderv1beta1.ViewMode_VIEW_MODE_VIEW_ONLY && wopiContext.ViewOnlyToken != "" { @@ -206,7 +206,7 @@ func (c *ContentConnector) PutFile(ctx context.Context, stream io.Reader, stream // We need a stat call on the target file in order to get both the lock // (if any) and the current size of the file statRes, err := c.gwc.Stat(ctx, &providerv1beta1.StatRequest{ - Ref: &wopiContext.FileReference, + Ref: wopiContext.FileReference, }) if err != nil { logger.Error().Err(err).Msg("PutFile: stat failed") @@ -254,7 +254,7 @@ func (c *ContentConnector) PutFile(ctx context.Context, stream io.Reader, stream req := &providerv1beta1.InitiateFileUploadRequest{ Opaque: opaque, - Ref: &wopiContext.FileReference, + Ref: wopiContext.FileReference, LockId: lockID, Options: &providerv1beta1.InitiateFileUploadRequest_IfMatch{ IfMatch: statRes.GetInfo().GetEtag(), diff --git a/services/collaboration/pkg/connector/contentconnector_test.go b/services/collaboration/pkg/connector/contentconnector_test.go index 581bb3b78d..aaf9fecf7d 100644 --- a/services/collaboration/pkg/connector/contentconnector_test.go +++ b/services/collaboration/pkg/connector/contentconnector_test.go @@ -44,7 +44,7 @@ var _ = Describe("ContentConnector", func() { wopiCtx = middleware.WopiContext{ AccessToken: "abcdef123456", - FileReference: providerv1beta1.Reference{ + FileReference: &providerv1beta1.Reference{ ResourceId: &providerv1beta1.ResourceId{ StorageId: "abc", OpaqueId: "12345", @@ -178,7 +178,7 @@ var _ = Describe("ContentConnector", func() { wopiCtx = middleware.WopiContext{ AccessToken: "abcdef123456", ViewOnlyToken: "view.only.123456", - FileReference: providerv1beta1.Reference{ + FileReference: &providerv1beta1.Reference{ ResourceId: &providerv1beta1.ResourceId{ StorageId: "abc", OpaqueId: "12345", diff --git a/services/collaboration/pkg/connector/fileconnector.go b/services/collaboration/pkg/connector/fileconnector.go index 720757f5cf..59689286c9 100644 --- a/services/collaboration/pkg/connector/fileconnector.go +++ b/services/collaboration/pkg/connector/fileconnector.go @@ -89,7 +89,7 @@ func (f *FileConnector) GetLock(ctx context.Context) (string, error) { logger := zerolog.Ctx(ctx) req := &providerv1beta1.GetLockRequest{ - Ref: &wopiContext.FileReference, + Ref: wopiContext.FileReference, } resp, err := f.gwc.GetLock(ctx, req) @@ -158,7 +158,7 @@ func (f *FileConnector) Lock(ctx context.Context, lockID, oldLockID string) (str if oldLockID == "" { // If the oldLockID is empty, this is a "LOCK" request req := &providerv1beta1.SetLockRequest{ - Ref: &wopiContext.FileReference, + Ref: wopiContext.FileReference, Lock: &providerv1beta1.Lock{ LockId: lockID, AppName: f.cfg.App.LockName + "." + f.cfg.App.Name, @@ -179,7 +179,7 @@ func (f *FileConnector) Lock(ctx context.Context, lockID, oldLockID string) (str // If the oldLockID isn't empty, this is a "UnlockAndRelock" request. We'll // do a "RefreshLock" in reva and provide the old lock req := &providerv1beta1.RefreshLockRequest{ - Ref: &wopiContext.FileReference, + Ref: wopiContext.FileReference, Lock: &providerv1beta1.Lock{ LockId: lockID, AppName: f.cfg.App.LockName + "." + f.cfg.App.Name, @@ -211,7 +211,7 @@ func (f *FileConnector) Lock(ctx context.Context, lockID, oldLockID string) (str // In both cases, we need to get the current lock to return it in a // 409 response if needed req := &providerv1beta1.GetLockRequest{ - Ref: &wopiContext.FileReference, + Ref: wopiContext.FileReference, } resp, err := f.gwc.GetLock(ctx, req) @@ -292,7 +292,7 @@ func (f *FileConnector) RefreshLock(ctx context.Context, lockID string) (string, } req := &providerv1beta1.RefreshLockRequest{ - Ref: &wopiContext.FileReference, + Ref: wopiContext.FileReference, Lock: &providerv1beta1.Lock{ LockId: lockID, AppName: f.cfg.App.LockName + "." + f.cfg.App.Name, @@ -330,7 +330,7 @@ func (f *FileConnector) RefreshLock(ctx context.Context, lockID string) (string, // Either the file is unlocked or there is no lock // We need to return 409 with the current lock req := &providerv1beta1.GetLockRequest{ - Ref: &wopiContext.FileReference, + Ref: wopiContext.FileReference, } resp, err := f.gwc.GetLock(ctx, req) @@ -400,7 +400,7 @@ func (f *FileConnector) UnLock(ctx context.Context, lockID string) (string, erro } req := &providerv1beta1.UnlockRequest{ - Ref: &wopiContext.FileReference, + Ref: wopiContext.FileReference, Lock: &providerv1beta1.Lock{ LockId: lockID, AppName: f.cfg.App.LockName + "." + f.cfg.App.Name, @@ -424,7 +424,7 @@ func (f *FileConnector) UnLock(ctx context.Context, lockID string) (string, erro case rpcv1beta1.Code_CODE_LOCKED: // We need to return 409 with the current lock req := &providerv1beta1.GetLockRequest{ - Ref: &wopiContext.FileReference, + Ref: wopiContext.FileReference, } resp, err := f.gwc.GetLock(ctx, req) @@ -485,7 +485,7 @@ func (f *FileConnector) CheckFileInfo(ctx context.Context) (fileinfo.FileInfo, e logger := zerolog.Ctx(ctx) statRes, err := f.gwc.Stat(ctx, &providerv1beta1.StatRequest{ - Ref: &wopiContext.FileReference, + Ref: wopiContext.FileReference, }) if err != nil { logger.Error().Err(err).Msg("CheckFileInfo: stat failed") diff --git a/services/collaboration/pkg/connector/fileconnector_test.go b/services/collaboration/pkg/connector/fileconnector_test.go index 7669e8a570..75cead43bf 100644 --- a/services/collaboration/pkg/connector/fileconnector_test.go +++ b/services/collaboration/pkg/connector/fileconnector_test.go @@ -39,7 +39,7 @@ var _ = Describe("FileConnector", func() { wopiCtx = middleware.WopiContext{ AccessToken: "abcdef123456", - FileReference: providerv1beta1.Reference{ + FileReference: &providerv1beta1.Reference{ ResourceId: &providerv1beta1.ResourceId{ StorageId: "abc", OpaqueId: "12345", diff --git a/services/collaboration/pkg/middleware/wopicontext.go b/services/collaboration/pkg/middleware/wopicontext.go index dfba554058..e9998c0d75 100644 --- a/services/collaboration/pkg/middleware/wopicontext.go +++ b/services/collaboration/pkg/middleware/wopicontext.go @@ -25,7 +25,7 @@ const ( type WopiContext struct { AccessToken string ViewOnlyToken string - FileReference providerv1beta1.Reference + FileReference *providerv1beta1.Reference User *userv1beta1.User ViewMode appproviderv1beta1.ViewMode EditAppUrl string diff --git a/services/collaboration/pkg/service/grpc/v0/service.go b/services/collaboration/pkg/service/grpc/v0/service.go index d0e6361447..4dfa90a280 100644 --- a/services/collaboration/pkg/service/grpc/v0/service.go +++ b/services/collaboration/pkg/service/grpc/v0/service.go @@ -214,7 +214,7 @@ func (s *Service) OpenInApp( wopiContext := middleware.WopiContext{ AccessToken: cryptedReqAccessToken, ViewOnlyToken: utils.ReadPlainFromOpaque(req.GetOpaque(), "viewOnlyToken"), - FileReference: providerFileRef, + FileReference: &providerFileRef, User: user, ViewMode: req.GetViewMode(), EditAppUrl: editAppURL,