github.com/cs3org/reva/v2@v2.27.7/pkg/auth/manager/publicshares/publicshares.go (about)

     1  // Copyright 2018-2021 CERN
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  package publicshares
    20  
    21  import (
    22  	"context"
    23  	"strings"
    24  	"time"
    25  
    26  	authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
    27  	user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    28  	rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
    29  	link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
    30  	types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
    31  	"github.com/cs3org/reva/v2/pkg/auth"
    32  	"github.com/cs3org/reva/v2/pkg/auth/manager/registry"
    33  	"github.com/cs3org/reva/v2/pkg/auth/scope"
    34  	"github.com/cs3org/reva/v2/pkg/errtypes"
    35  	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
    36  	"github.com/cs3org/reva/v2/pkg/utils"
    37  	"github.com/mitchellh/mapstructure"
    38  	"github.com/pkg/errors"
    39  )
    40  
    41  func init() {
    42  	registry.Register("publicshares", New)
    43  }
    44  
    45  type manager struct {
    46  	c *config
    47  }
    48  
    49  type config struct {
    50  	GatewayAddr string `mapstructure:"gateway_addr"`
    51  }
    52  
    53  func parseConfig(m map[string]interface{}) (*config, error) {
    54  	c := &config{}
    55  	if err := mapstructure.Decode(m, c); err != nil {
    56  		err = errors.Wrap(err, "error decoding conf")
    57  		return nil, err
    58  	}
    59  	return c, nil
    60  }
    61  
    62  // New returns a new auth Manager.
    63  func New(m map[string]interface{}) (auth.Manager, error) {
    64  	mgr := &manager{}
    65  	err := mgr.Configure(m)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  	return mgr, nil
    70  }
    71  
    72  func (m *manager) Configure(ml map[string]interface{}) error {
    73  	conf, err := parseConfig(ml)
    74  	if err != nil {
    75  		return err
    76  	}
    77  	m.c = conf
    78  	return nil
    79  }
    80  
    81  func (m *manager) Authenticate(ctx context.Context, token, secret string) (*user.User, map[string]*authpb.Scope, error) {
    82  	gwConn, err := pool.GetGatewayServiceClient(m.c.GatewayAddr)
    83  	if err != nil {
    84  		return nil, nil, err
    85  	}
    86  
    87  	var auth *link.PublicShareAuthentication
    88  	if strings.HasPrefix(secret, "password|") {
    89  		secret = strings.TrimPrefix(secret, "password|")
    90  		auth = &link.PublicShareAuthentication{
    91  			Spec: &link.PublicShareAuthentication_Password{
    92  				Password: secret,
    93  			},
    94  		}
    95  	} else if strings.HasPrefix(secret, "signature|") {
    96  		secret = strings.TrimPrefix(secret, "signature|")
    97  		parts := strings.Split(secret, "|")
    98  		sig, expiration := parts[0], parts[1]
    99  		exp, _ := time.Parse(time.RFC3339, expiration)
   100  
   101  		auth = &link.PublicShareAuthentication{
   102  			Spec: &link.PublicShareAuthentication_Signature{
   103  				Signature: &link.ShareSignature{
   104  					Signature: sig,
   105  					SignatureExpiration: &types.Timestamp{
   106  						Seconds: uint64(exp.UnixNano() / 1000000000),
   107  						Nanos:   uint32(exp.UnixNano() % 1000000000),
   108  					},
   109  				},
   110  			},
   111  		}
   112  	}
   113  
   114  	publicShareResponse, err := gwConn.GetPublicShareByToken(ctx, &link.GetPublicShareByTokenRequest{
   115  		Token:          token,
   116  		Authentication: auth,
   117  		Sign:           true,
   118  	})
   119  	switch {
   120  	case err != nil:
   121  		return nil, nil, err
   122  	case publicShareResponse.Status.Code == rpcv1beta1.Code_CODE_NOT_FOUND:
   123  		return nil, nil, errtypes.NotFound(publicShareResponse.Status.Message)
   124  	case publicShareResponse.Status.Code == rpcv1beta1.Code_CODE_PERMISSION_DENIED:
   125  		return nil, nil, errtypes.InvalidCredentials(publicShareResponse.Status.Message)
   126  	case publicShareResponse.Status.Code != rpcv1beta1.Code_CODE_OK:
   127  		return nil, nil, errtypes.InternalError(publicShareResponse.Status.Message)
   128  	}
   129  
   130  	var owner *user.User
   131  	// FIXME use new user type SPACE_OWNER
   132  	if publicShareResponse.GetShare().GetOwner().GetType() == 8 {
   133  		owner = &user.User{Id: publicShareResponse.GetShare().GetOwner(), DisplayName: "Public", Username: "public"}
   134  	} else {
   135  		getUserResponse, err := gwConn.GetUser(ctx, &user.GetUserRequest{
   136  			UserId: publicShareResponse.GetShare().GetCreator(),
   137  		})
   138  		switch {
   139  		case err != nil:
   140  			return nil, nil, err
   141  		case getUserResponse.GetStatus().GetCode() == rpcv1beta1.Code_CODE_NOT_FOUND:
   142  			return nil, nil, errtypes.NotFound(getUserResponse.GetStatus().GetMessage())
   143  		case getUserResponse.GetStatus().GetCode() == rpcv1beta1.Code_CODE_PERMISSION_DENIED:
   144  			return nil, nil, errtypes.InvalidCredentials(getUserResponse.GetStatus().GetMessage())
   145  		case getUserResponse.GetStatus().GetCode() != rpcv1beta1.Code_CODE_OK:
   146  			return nil, nil, errtypes.InternalError(getUserResponse.GetStatus().GetMessage())
   147  		}
   148  		owner = getUserResponse.GetUser()
   149  	}
   150  
   151  	share := publicShareResponse.GetShare()
   152  	role := authpb.Role_ROLE_VIEWER
   153  	roleStr := "viewer"
   154  	if share.Permissions.Permissions.InitiateFileUpload && !share.Permissions.Permissions.InitiateFileDownload {
   155  		role = authpb.Role_ROLE_UPLOADER
   156  		roleStr = "uploader"
   157  	} else if share.Permissions.Permissions.InitiateFileUpload {
   158  		role = authpb.Role_ROLE_EDITOR
   159  		roleStr = "editor"
   160  	}
   161  
   162  	scope, err := scope.AddPublicShareScope(share, role, nil)
   163  	if err != nil {
   164  		return nil, nil, err
   165  	}
   166  
   167  	owner.Opaque = &types.Opaque{
   168  		Map: map[string]*types.OpaqueEntry{
   169  			"public-share-role": {
   170  				Decoder: "plain",
   171  				Value:   []byte(roleStr),
   172  			},
   173  		},
   174  	}
   175  
   176  	u := &user.User{Id: &user.UserId{OpaqueId: token, Idp: "public", Type: user.UserType_USER_TYPE_GUEST}, DisplayName: "Public", Username: "public"}
   177  	owner.Opaque = utils.AppendJSONToOpaque(owner.Opaque, "impersonating-user", u)
   178  
   179  	return owner, scope, nil
   180  }
   181  
   182  // ErrPasswordNotProvided is returned when the public share is password protected, but there was no password on the request
   183  var ErrPasswordNotProvided = errors.New("public share is password protected, but password was not provided")