Skip to content

Commit

Permalink
Implemented folderurl for WOPI apps (#3494)
Browse files Browse the repository at this point in the history
* Added support for user's scopes and folderurl for WOPI apps

* Updated eos-based docker image and added latest golang
  • Loading branch information
glpatcern authored Dec 1, 2022
1 parent 74bd23c commit f537b02
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 37 deletions.
9 changes: 8 additions & 1 deletion Dockerfile.revad-eos
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
# granted to it by virtue of its status as an Intergovernmental Organization
# or submit itself to any jurisdiction.

FROM gitlab-registry.cern.ch/dss/eos/eos-all:4.8.66
FROM gitlab-registry.cern.ch/dss/eos/eos-fusex:4.8.91

RUN yum -y update && yum clean all

Expand All @@ -25,6 +25,13 @@ RUN yum install -y make git gcc libc-dev bash epel-release golang && \
yum clean all && \
rm -rf /var/cache/yum

ADD https://golang.org/dl/go1.19.linux-amd64.tar.gz \
go1.19.linux-amd64.tar.gz

RUN rm -rf /usr/local/go && \
tar -C /usr/local -xzf go1.19.linux-amd64.tar.gz && \
rm go1.19.linux-amd64.tar.gz

ENV PATH /go/bin:/usr/local/go/bin:$PATH
ENV GOPATH /go

Expand Down
12 changes: 12 additions & 0 deletions changelog/unreleased/folderurl-for-apps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Enhancement: implemented folderurl for WOPI apps

The folderurl is now populated for WOPI apps, such that
for owners and named shares it points to the containing
folder, and for public links it points to the appropriate
public link URL.

On the way, functions to manipulate the user's scope and
extract the eventual public link token(s) have been added,
coauthored with @gmgigi96.

https://github.com/cs3org/reva/pull/3494
28 changes: 18 additions & 10 deletions docs/content/en/docs/config/packages/app/provider/wopi/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,79 +9,87 @@ description: >
# _struct: config_

{{% dir name="mime_types" type="[]string" default=nil %}}
Inherited from the appprovider. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/app/provider/wopi/wopi.go#L58)
Inherited from the appprovider. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/app/provider/wopi/wopi.go#L63)
{{< highlight toml >}}
[app.provider.wopi]
mime_types = nil
{{< /highlight >}}
{{% /dir %}}

{{% dir name="iop_secret" type="string" default="" %}}
The IOP secret used to connect to the wopiserver. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/app/provider/wopi/wopi.go#L59)
The IOP secret used to connect to the wopiserver. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/app/provider/wopi/wopi.go#L64)
{{< highlight toml >}}
[app.provider.wopi]
iop_secret = ""
{{< /highlight >}}
{{% /dir %}}

{{% dir name="wopi_url" type="string" default="" %}}
The wopiserver's URL. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/app/provider/wopi/wopi.go#L60)
The wopiserver's URL. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/app/provider/wopi/wopi.go#L65)
{{< highlight toml >}}
[app.provider.wopi]
wopi_url = ""
{{< /highlight >}}
{{% /dir %}}

{{% dir name="app_name" type="string" default="" %}}
The App user-friendly name. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/app/provider/wopi/wopi.go#L61)
The App user-friendly name. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/app/provider/wopi/wopi.go#L66)
{{< highlight toml >}}
[app.provider.wopi]
app_name = ""
{{< /highlight >}}
{{% /dir %}}

{{% dir name="app_icon_uri" type="string" default="" %}}
A URI to a static asset which represents the app icon. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/app/provider/wopi/wopi.go#L62)
A URI to a static asset which represents the app icon. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/app/provider/wopi/wopi.go#L67)
{{< highlight toml >}}
[app.provider.wopi]
app_icon_uri = ""
{{< /highlight >}}
{{% /dir %}}

{{% dir name="folder_base_url" type="string" default="" %}}
The base URL to generate links to navigate back to the containing folder. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/app/provider/wopi/wopi.go#L68)
{{< highlight toml >}}
[app.provider.wopi]
folder_base_url = ""
{{< /highlight >}}
{{% /dir %}}

{{% dir name="app_url" type="string" default="" %}}
The App URL. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/app/provider/wopi/wopi.go#L63)
The App URL. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/app/provider/wopi/wopi.go#L69)
{{< highlight toml >}}
[app.provider.wopi]
app_url = ""
{{< /highlight >}}
{{% /dir %}}

{{% dir name="app_int_url" type="string" default="" %}}
The internal app URL in case of dockerized deployments. Defaults to AppURL [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/app/provider/wopi/wopi.go#L64)
The internal app URL in case of dockerized deployments. Defaults to AppURL [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/app/provider/wopi/wopi.go#L70)
{{< highlight toml >}}
[app.provider.wopi]
app_int_url = ""
{{< /highlight >}}
{{% /dir %}}

{{% dir name="app_api_key" type="string" default="" %}}
The API key used by the app, if applicable. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/app/provider/wopi/wopi.go#L65)
The API key used by the app, if applicable. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/app/provider/wopi/wopi.go#L71)
{{< highlight toml >}}
[app.provider.wopi]
app_api_key = ""
{{< /highlight >}}
{{% /dir %}}

{{% dir name="jwt_secret" type="string" default="" %}}
The JWT secret to be used to retrieve the token TTL. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/app/provider/wopi/wopi.go#L66)
The JWT secret to be used to retrieve the token TTL. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/app/provider/wopi/wopi.go#L72)
{{< highlight toml >}}
[app.provider.wopi]
jwt_secret = ""
{{< /highlight >}}
{{% /dir %}}

{{% dir name="app_desktop_only" type="bool" default=false %}}
Specifies if the app can be opened only on desktop. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/app/provider/wopi/wopi.go#L67)
Specifies if the app can be opened only on desktop. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/app/provider/wopi/wopi.go#L73)
{{< highlight toml >}}
[app.provider.wopi]
app_desktop_only = false
Expand Down
1 change: 1 addition & 0 deletions examples/storage-references/appprovider-codimd.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ wopi_url = "http://0.0.0.0:8880/"
app_name = "CodiMD"
app_url = "https://your-codimd-server.org:3000"
app_int_url = "https://your-codimd-server.org:3000"
folder_base_url = "https://your-reva-frontend.org"
33 changes: 19 additions & 14 deletions internal/grpc/interceptors/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"time"

"github.com/bluele/gcache"
authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
gatewayv1beta1 "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
"github.com/cs3org/reva/pkg/appctx"
Expand Down Expand Up @@ -103,12 +104,13 @@ func NewUnary(m map[string]interface{}, unprotected []string) (grpc.UnaryServerI
// to decide the storage provider.
tkn, ok := ctxpkg.ContextGetToken(ctx)
if ok {
u, err := dismantleToken(ctx, tkn, req, tokenManager, conf.GatewayAddr, true)
u, scopes, err := dismantleToken(ctx, tkn, req, tokenManager, conf.GatewayAddr, true)
if err == nil {
if blockedUsers.IsBlocked(u.Username) {
return nil, status.Errorf(codes.PermissionDenied, "user %s blocked", u.Username)
}
ctx = ctxpkg.ContextSetUser(ctx, u)
ctx = ctxpkg.ContextSetScopes(ctx, scopes)
}
}
return handler(ctx, req)
Expand All @@ -121,8 +123,8 @@ func NewUnary(m map[string]interface{}, unprotected []string) (grpc.UnaryServerI
return nil, status.Errorf(codes.Unauthenticated, "auth: core access token not found")
}

// validate the token and ensure access to the resource is allowed
u, err := dismantleToken(ctx, tkn, req, tokenManager, conf.GatewayAddr, false)
// scopes, validate the token and ensure access to the resource is allowed
u, scopes, err := dismantleToken(ctx, tkn, req, tokenManager, conf.GatewayAddr, false)
if err != nil {
log.Warn().Err(err).Msg("access token is invalid")
return nil, status.Errorf(codes.PermissionDenied, "auth: core access token is invalid")
Expand All @@ -133,6 +135,7 @@ func NewUnary(m map[string]interface{}, unprotected []string) (grpc.UnaryServerI
}

ctx = ctxpkg.ContextSetUser(ctx, u)
ctx = ctxpkg.ContextSetScopes(ctx, scopes)
return handler(ctx, req)
}
return interceptor, nil
Expand Down Expand Up @@ -174,9 +177,10 @@ func NewStream(m map[string]interface{}, unprotected []string) (grpc.StreamServe
// to decide the storage provider.
tkn, ok := ctxpkg.ContextGetToken(ctx)
if ok {
u, err := dismantleToken(ctx, tkn, ss, tokenManager, conf.GatewayAddr, true)
u, scopes, err := dismantleToken(ctx, tkn, ss, tokenManager, conf.GatewayAddr, true)
if err == nil {
ctx = ctxpkg.ContextSetUser(ctx, u)
ctx = ctxpkg.ContextSetScopes(ctx, scopes)
ss = newWrappedServerStream(ctx, ss)
}
}
Expand All @@ -192,14 +196,15 @@ func NewStream(m map[string]interface{}, unprotected []string) (grpc.StreamServe
}

// validate the token and ensure access to the resource is allowed
u, err := dismantleToken(ctx, tkn, ss, tokenManager, conf.GatewayAddr, false)
u, scopes, err := dismantleToken(ctx, tkn, ss, tokenManager, conf.GatewayAddr, false)
if err != nil {
log.Warn().Err(err).Msg("access token is invalid")
return status.Errorf(codes.PermissionDenied, "auth: core access token is invalid")
}

// store user and core access token in context.
ctx = ctxpkg.ContextSetUser(ctx, u)
ctx = ctxpkg.ContextSetScopes(ctx, scopes)
wrapped := newWrappedServerStream(ctx, ss)
return handler(srv, wrapped)
}
Expand All @@ -219,42 +224,42 @@ func (ss *wrappedServerStream) Context() context.Context {
return ss.newCtx
}

func dismantleToken(ctx context.Context, tkn string, req interface{}, mgr token.Manager, gatewayAddr string, unprotected bool) (*userpb.User, error) {
func dismantleToken(ctx context.Context, tkn string, req interface{}, mgr token.Manager, gatewayAddr string, unprotected bool) (*userpb.User, map[string]*authpb.Scope, error) {
u, tokenScope, err := mgr.DismantleToken(ctx, tkn)
if err != nil {
return nil, err
return nil, nil, err
}

if unprotected {
return u, nil
return u, nil, nil
}

if sharedconf.SkipUserGroupsInToken() {
client, err := pool.GetGatewayServiceClient(pool.Endpoint(gatewayAddr))
if err != nil {
return nil, err
return nil, nil, err
}
groups, err := getUserGroups(ctx, u, client)
if err != nil {
return nil, err
return nil, nil, err
}
u.Groups = groups
}

// Check if access to the resource is in the scope of the token
ok, err := scope.VerifyScope(ctx, tokenScope, req)
if err != nil {
return nil, errtypes.InternalError("error verifying scope of access token")
return nil, nil, errtypes.InternalError("error verifying scope of access token")
}
if ok {
return u, nil
return u, tokenScope, nil
}

if err = expandAndVerifyScope(ctx, req, tokenScope, u, gatewayAddr, mgr); err != nil {
return nil, err
return nil, nil, err
}

return u, nil
return u, tokenScope, nil
}

func getUserGroups(ctx context.Context, u *userpb.User, client gatewayv1beta1.GatewayAPIClient) ([]string, error) {
Expand Down
Loading

0 comments on commit f537b02

Please sign in to comment.