github.com/cs3org/reva/v2@v2.27.7/internal/http/services/owncloud/ocdav/delete.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 ocdav
    20  
    21  import (
    22  	"context"
    23  	"errors"
    24  	"net/http"
    25  	"path"
    26  
    27  	rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
    28  	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    29  	"github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/net"
    30  	"github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/spacelookup"
    31  	"github.com/cs3org/reva/v2/pkg/appctx"
    32  	"github.com/cs3org/reva/v2/pkg/errtypes"
    33  	rstatus "github.com/cs3org/reva/v2/pkg/rgrpc/status"
    34  	"github.com/cs3org/reva/v2/pkg/utils"
    35  )
    36  
    37  func (s *svc) handlePathDelete(w http.ResponseWriter, r *http.Request, ns string) (status int, err error) {
    38  	ctx := r.Context()
    39  	ctx, span := appctx.GetTracerProvider(r.Context()).Tracer(tracerName).Start(ctx, "path_delete")
    40  	defer span.End()
    41  
    42  	if r.Body != http.NoBody {
    43  		return http.StatusUnsupportedMediaType, errors.New("body must be empty")
    44  	}
    45  
    46  	fn := path.Join(ns, r.URL.Path)
    47  
    48  	space, rpcStatus, err := spacelookup.LookUpStorageSpaceForPath(ctx, s.gatewaySelector, fn)
    49  	switch {
    50  	case err != nil:
    51  		span.RecordError(err)
    52  		return http.StatusInternalServerError, err
    53  	case rpcStatus.Code != rpc.Code_CODE_OK:
    54  		return rstatus.HTTPStatusFromCode(rpcStatus.Code), errtypes.NewErrtypeFromStatus(rpcStatus)
    55  	}
    56  
    57  	return s.handleDelete(ctx, w, r, spacelookup.MakeRelativeReference(space, fn, false))
    58  }
    59  
    60  func (s *svc) handleDelete(ctx context.Context, w http.ResponseWriter, r *http.Request, ref *provider.Reference) (status int, err error) {
    61  	ctx, span := appctx.GetTracerProvider(r.Context()).Tracer(tracerName).Start(ctx, "delete")
    62  	defer span.End()
    63  
    64  	req := &provider.DeleteRequest{
    65  		Ref:    ref,
    66  		LockId: requestLockToken(r),
    67  	}
    68  
    69  	// FIXME the lock token is part of the application level protocol, it should be part of the DeleteRequest message not the opaque
    70  	ih, ok := parseIfHeader(r.Header.Get(net.HeaderIf))
    71  	if ok {
    72  		if len(ih.lists) == 1 && len(ih.lists[0].conditions) == 1 {
    73  			req.Opaque = utils.AppendPlainToOpaque(req.Opaque, "lockid", ih.lists[0].conditions[0].Token)
    74  		}
    75  	} else if r.Header.Get(net.HeaderIf) != "" {
    76  		return http.StatusBadRequest, errtypes.BadRequest("invalid if header")
    77  	}
    78  
    79  	client, err := s.gatewaySelector.Next()
    80  	if err != nil {
    81  		return http.StatusInternalServerError, errtypes.InternalError(err.Error())
    82  	}
    83  
    84  	res, err := client.Delete(ctx, req)
    85  	switch {
    86  	case err != nil:
    87  		span.RecordError(err)
    88  		return http.StatusInternalServerError, err
    89  	case res.Status.Code == rpc.Code_CODE_OK:
    90  		return http.StatusNoContent, nil
    91  	case res.Status.Code == rpc.Code_CODE_NOT_FOUND:
    92  		//lint:ignore ST1005 mimic the exact oc10 error message
    93  		return http.StatusNotFound, errors.New("Resource not found")
    94  	case res.Status.Code == rpc.Code_CODE_PERMISSION_DENIED:
    95  		status = http.StatusForbidden
    96  		if lockID := utils.ReadPlainFromOpaque(res.Opaque, "lockid"); lockID != "" {
    97  			// http://www.webdav.org/specs/rfc4918.html#HEADER_Lock-Token says that the
    98  			// Lock-Token value is a Coded-URL. We add angle brackets.
    99  			w.Header().Set("Lock-Token", "<"+lockID+">")
   100  			status = http.StatusLocked
   101  		}
   102  		// check if user has access to resource
   103  		sRes, err := client.Stat(ctx, &provider.StatRequest{Ref: ref})
   104  		if err != nil {
   105  			span.RecordError(err)
   106  			return http.StatusInternalServerError, err
   107  		}
   108  		if sRes.Status.Code != rpc.Code_CODE_OK {
   109  			// return not found error so we do not leak existence of a file
   110  			// TODO hide permission failed for users without access in every kind of request
   111  			// TODO should this be done in the driver?
   112  			//lint:ignore ST1005 mimic the exact oc10 error message
   113  			return http.StatusNotFound, errors.New("Resource not found")
   114  		}
   115  		return status, errors.New("") // mimic the oc10 error messages which have an empty message in this case
   116  	case res.Status.Code == rpc.Code_CODE_INTERNAL && res.Status.Message == "can't delete mount path":
   117  		// 405 must generate an Allow header
   118  		w.Header().Set("Allow", "PROPFIND, MOVE, COPY, POST, PROPPATCH, HEAD, GET, OPTIONS, LOCK, UNLOCK, REPORT, SEARCH, PUT")
   119  		return http.StatusMethodNotAllowed, errtypes.PermissionDenied(res.Status.Message)
   120  	}
   121  	return rstatus.HTTPStatusFromCode(res.Status.Code), errtypes.NewErrtypeFromStatus(res.Status)
   122  }
   123  
   124  func (s *svc) handleSpacesDelete(w http.ResponseWriter, r *http.Request, spaceID string) (status int, err error) {
   125  	ctx := r.Context()
   126  	ctx, span := appctx.GetTracerProvider(r.Context()).Tracer(tracerName).Start(ctx, "spaces_delete")
   127  	defer span.End()
   128  
   129  	if r.Body != http.NoBody {
   130  		return http.StatusUnsupportedMediaType, errors.New("body must be empty")
   131  	}
   132  
   133  	ref, err := spacelookup.MakeStorageSpaceReference(spaceID, r.URL.Path)
   134  	if err != nil {
   135  		return http.StatusBadRequest, err
   136  	}
   137  
   138  	// do not allow deleting spaces via dav endpoint - use graph endpoint instead
   139  	// we get a relative reference coming from the space root
   140  	// so if the path is "empty" and no opaque id is present or the opaque id equals
   141  	// the space id, we are referencing the space
   142  	rid := ref.GetResourceId()
   143  	if ref.GetPath() == "." &&
   144  		(rid.GetOpaqueId() == "" || rid.GetOpaqueId() == rid.GetSpaceId()) {
   145  		return http.StatusMethodNotAllowed, errors.New("deleting spaces via dav is not allowed")
   146  	}
   147  
   148  	return s.handleDelete(ctx, w, r, &ref)
   149  }