From 238f346379f82de7346cefb23acf99c9d4c3357c Mon Sep 17 00:00:00 2001 From: Giuseppe Lo Presti Date: Wed, 26 Oct 2022 18:44:46 +0200 Subject: [PATCH] Moved custom JSON parsing to the appprovider, and removed hardcoded config --- .../grpc/services/appprovider/appprovider.go | 56 +++++++++-- pkg/app/provider/wopi/wopi.go | 96 +++++-------------- 2 files changed, 72 insertions(+), 80 deletions(-) diff --git a/internal/grpc/services/appprovider/appprovider.go b/internal/grpc/services/appprovider/appprovider.go index 881dc4c4b7a..5da7028eab3 100644 --- a/internal/grpc/services/appprovider/appprovider.go +++ b/internal/grpc/services/appprovider/appprovider.go @@ -20,7 +20,10 @@ package appprovider import ( "context" + "encoding/json" "errors" + "fmt" + "io/ioutil" "os" "strconv" "time" @@ -33,6 +36,7 @@ import ( "github.com/cs3org/reva/pkg/app/provider/registry" "github.com/cs3org/reva/pkg/errtypes" "github.com/cs3org/reva/pkg/logger" + "github.com/cs3org/reva/pkg/mime" "github.com/cs3org/reva/pkg/rgrpc" "github.com/cs3org/reva/pkg/rgrpc/status" "github.com/cs3org/reva/pkg/rgrpc/todo/pool" @@ -52,13 +56,14 @@ type service struct { } type config struct { - Driver string `mapstructure:"driver"` - Drivers map[string]map[string]interface{} `mapstructure:"drivers"` - AppProviderURL string `mapstructure:"app_provider_url"` - GatewaySvc string `mapstructure:"gatewaysvc"` - MimeTypes []string `mapstructure:"mime_types"` - Priority uint64 `mapstructure:"priority"` - Language string `mapstructure:"language"` + Driver string `mapstructure:"driver"` + Drivers map[string]map[string]interface{} `mapstructure:"drivers"` + AppProviderURL string `mapstructure:"app_provider_url"` + GatewaySvc string `mapstructure:"gatewaysvc"` + MimeTypes []string `mapstructure:"mime_types" docs:"nil;A list of mime types supported by this app."` + CustomMimeTypesJSON string `mapstructure:"custom_mime_types_json" docs:"nil;An optional mapping file with the list of supported custom file extensions and corresponding mime types."` + Priority uint64 `mapstructure:"priority"` + Language string `mapstructure:"language"` } func (c *config) init() { @@ -85,6 +90,12 @@ func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { return nil, err } + // read and register custom mime types if configured + err = registerMimeTypes(c.CustomMimeTypesJSON) + if err != nil { + return nil, err + } + provider, err := getProvider(c) if err != nil { return nil, err @@ -99,9 +110,31 @@ func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { return service, nil } +func registerMimeTypes(mappingFile string) error { + // TODO(lopresti) this function also exists in the storage provider, to be seen if we want to factor it out, though a + // fileext <-> mimetype "service" would have to be served by the gateway for it to be accessible both by storage providers and app providers. + if mappingFile != "" { + f, err := ioutil.ReadFile(mappingFile) + if err != nil { + return fmt.Errorf("appprovider: error reading the custom mime types file: +%v", err) + } + mimeTypes := map[string]string{} + err = json.Unmarshal(f, &mimeTypes) + if err != nil { + return fmt.Errorf("appprovider: error unmarshalling the custom mime types file: +%v", err) + } + // register all mime types that were read + for e, m := range mimeTypes { + mime.RegisterMime(e, m) + } + } + return nil +} + func (s *service) registerProvider() { // Give the appregistry service time to come up - time.Sleep(2 * time.Second) + // TODO(lopresti) we should register the appproviders after all other microservices + time.Sleep(3 * time.Second) ctx := context.Background() log := logger.New().With().Int("pid", os.Getpid()).Logger() @@ -119,6 +152,7 @@ func (s *service) registerProvider() { mimeTypes = append(mimeTypes, m.(string)) } pInfo.MimeTypes = mimeTypes + log.Info().Msgf("appprovider: %s will support %+v", s.conf.AppProviderURL, mimeTypes) } client, err := pool.GetGatewayServiceClient(pool.Endpoint(s.conf.GatewaySvc)) @@ -165,7 +199,10 @@ func (s *service) Register(ss *grpc.Server) { func getProvider(c *config) (app.Provider, error) { if f, ok := registry.NewFuncs[c.Driver]; ok { - return f(c.Drivers[c.Driver]) + driverConf := c.Drivers[c.Driver] + // share the mime_types config entry to the drivers + driverConf["mime_types"] = c.MimeTypes + return f(driverConf) } return nil, errtypes.NotFound("driver not found: " + c.Driver) } @@ -183,5 +220,4 @@ func (s *service) OpenInApp(ctx context.Context, req *providerpb.OpenInAppReques AppUrl: appURL, } return res, nil - } diff --git a/pkg/app/provider/wopi/wopi.go b/pkg/app/provider/wopi/wopi.go index 724abb090e2..b4dcd3f3cb5 100644 --- a/pkg/app/provider/wopi/wopi.go +++ b/pkg/app/provider/wopi/wopi.go @@ -56,17 +56,17 @@ func init() { } type config struct { - IOPSecret string `mapstructure:"iop_secret" docs:";The IOP secret used to connect to the wopiserver."` - WopiURL string `mapstructure:"wopi_url" docs:";The wopiserver's URL."` - AppName string `mapstructure:"app_name" docs:";The App user-friendly name."` - AppIconURI string `mapstructure:"app_icon_uri" docs:";A URI to a static asset which represents the app icon."` - AppURL string `mapstructure:"app_url" docs:";The App URL."` - AppIntURL string `mapstructure:"app_int_url" docs:";The internal app URL in case of dockerized deployments. Defaults to AppURL"` - AppAPIKey string `mapstructure:"app_api_key" docs:";The API key used by the app, if applicable."` - JWTSecret string `mapstructure:"jwt_secret" docs:";The JWT secret to be used to retrieve the token TTL."` - CustomMimeTypesJSON string `mapstructure:"custom_mime_types_json" docs:"nil;An optional mapping file with the list of supported custom file extensions and corresponding mime types."` - AppDesktopOnly bool `mapstructure:"app_desktop_only" docs:"false;Specifies if the app can be opened only on desktop."` - InsecureConnections bool `mapstructure:"insecure_connections"` + MimeTypes []string `mapstructure:"mime_types" docs:";Inherited from the appprovider."` + IOPSecret string `mapstructure:"iop_secret" docs:";The IOP secret used to connect to the wopiserver."` + WopiURL string `mapstructure:"wopi_url" docs:";The wopiserver's URL."` + AppName string `mapstructure:"app_name" docs:";The App user-friendly name."` + AppIconURI string `mapstructure:"app_icon_uri" docs:";A URI to a static asset which represents the app icon."` + AppURL string `mapstructure:"app_url" docs:";The App URL."` + AppIntURL string `mapstructure:"app_int_url" docs:";The internal app URL in case of dockerized deployments. Defaults to AppURL"` + AppAPIKey string `mapstructure:"app_api_key" docs:";The API key used by the app, if applicable."` + JWTSecret string `mapstructure:"jwt_secret" docs:";The JWT secret to be used to retrieve the token TTL."` + AppDesktopOnly bool `mapstructure:"app_desktop_only" docs:"false;Specifies if the app can be opened only on desktop."` + InsecureConnections bool `mapstructure:"insecure_connections"` } func parseConfig(m map[string]interface{}) (*config, error) { @@ -112,12 +112,6 @@ func New(m map[string]interface{}) (app.Provider, error) { return http.ErrUseLastResponse } - // read and register custom mime types if configured - err = registerMimeTypes(c.CustomMimeTypesJSON) - if err != nil { - return nil, err - } - return &wopiProvider{ conf: c, wopiClient: wopiClient, @@ -265,7 +259,7 @@ func (p *wopiProvider) GetAppURL(ctx context.Context, resource *provider.Resourc } } - log.Info().Msg(fmt.Sprintf("wopi: returning app URL %s", appFullURL)) + log.Info().Msgf("wopi: returning app URL %s", appFullURL) return &appprovider.OpenInAppURL{ AppUrl: appFullURL, Method: method, @@ -296,27 +290,6 @@ func (p *wopiProvider) GetAppProviderInfo(ctx context.Context) (*appregistry.Pro }, nil } -func registerMimeTypes(mappingFile string) error { - // TODO(lopresti) this function also exists in the storage provider, to be seen if we want to factor it out, though a - // fileext <-> mimetype "service" would have to be served by the gateway for it to be accessible both by storage providers and app providers. - if mappingFile != "" { - f, err := ioutil.ReadFile(mappingFile) - if err != nil { - return fmt.Errorf("storageprovider: error reading the custom mime types file: +%v", err) - } - mimeTypes := map[string]string{} - err = json.Unmarshal(f, &mimeTypes) - if err != nil { - return fmt.Errorf("storageprovider: error unmarshalling the custom mime types file: +%v", err) - } - // register all mime types that were read - for e, m := range mimeTypes { - mime.RegisterMime(e, m) - } - } - return nil -} - func getAppURLs(c *config) (map[string]map[string]string, error) { // Initialize WOPI URLs by discovery httpcl := rhttp.GetHTTPClient( @@ -370,15 +343,19 @@ func getAppURLs(c *config) (map[string]map[string]string, error) { return nil, errors.New("Application server at " + c.AppURL + " does not match this AppProvider for " + c.AppName) } - // register the supported mimetypes in the AppRegistry: this is hardcoded for the time being - // TODO(lopresti) move to config - switch c.AppName { - case "CodiMD": - appURLs = getCodimdExtensions(c.AppURL) - case "Etherpad": - appURLs = getEtherpadExtensions(c.AppURL) - default: - return nil, errors.New("Application server " + c.AppName + " running at " + c.AppURL + " is unsupported") + // TODO(lopresti) we don't know if the app is not supported/configured in WOPI + // return nil, errors.New("Application server " + c.AppName + " running at " + c.AppURL + " is unsupported") + + // generate the map of supported extensions + appURLs = make(map[string]map[string]string) + appURLs["view"] = make(map[string]string) + appURLs["edit"] = make(map[string]string) + for _, m := range c.MimeTypes { + exts := mime.GetFileExt(m) + for _, e := range exts { + appURLs["view"]["."+e] = c.AppURL + appURLs["edit"]["."+e] = c.AppURL + } } } return appURLs, nil @@ -394,7 +371,7 @@ func (p *wopiProvider) getAccessTokenTTL(ctx context.Context) (string, error) { } if claims, ok := token.Claims.(*jwt.StandardClaims); ok && token.Valid { - // milliseconds since Jan 1, 1970 UTC as required in https://wopi.readthedocs.io/projects/wopirest/en/latest/concepts.html?highlight=access_token_ttl#term-access-token-ttl + // milliseconds since Jan 1, 1970 UTC as required in https://learn.microsoft.com/en-us/microsoft-365/cloud-storage-partner-program/rest/concepts#the-access_token_ttl-property return strconv.FormatInt(claims.ExpiresAt*1000, 10), nil } @@ -456,24 +433,3 @@ func parseWopiDiscovery(body io.Reader) (map[string]map[string]string, error) { } return appURLs, nil } - -func getCodimdExtensions(appURL string) map[string]map[string]string { - // Register custom mime types - mime.RegisterMime(".zmd", "application/compressed-markdown") - - appURLs := make(map[string]map[string]string) - appURLs["edit"] = map[string]string{ - ".txt": appURL, - ".md": appURL, - ".zmd": appURL, - } - return appURLs -} - -func getEtherpadExtensions(appURL string) map[string]map[string]string { - appURLs := make(map[string]map[string]string) - appURLs["edit"] = map[string]string{ - ".epd": appURL, - } - return appURLs -}