github.com/cs3org/reva/v2@v2.27.7/internal/http/services/owncloud/ocdav/meta.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 "encoding/xml" 23 "fmt" 24 "net/http" 25 "path" 26 "strings" 27 28 rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" 29 provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" 30 "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/config" 31 "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/errors" 32 "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/net" 33 "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/prop" 34 "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/propfind" 35 "github.com/cs3org/reva/v2/pkg/appctx" 36 "github.com/cs3org/reva/v2/pkg/rhttp/router" 37 "github.com/cs3org/reva/v2/pkg/storagespace" 38 ) 39 40 // MetaHandler handles meta requests 41 type MetaHandler struct { 42 VersionsHandler *VersionsHandler 43 } 44 45 func (h *MetaHandler) init(c *config.Config) error { 46 h.VersionsHandler = new(VersionsHandler) 47 return h.VersionsHandler.init(c) 48 } 49 50 // Handler handles requests 51 func (h *MetaHandler) Handler(s *svc) http.Handler { 52 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 53 54 var id string 55 id, r.URL.Path = router.ShiftPath(r.URL.Path) 56 if id == "" { 57 if r.Method != MethodPropfind { 58 w.WriteHeader(http.StatusBadRequest) 59 return 60 } 61 h.handleEmptyID(w, r) 62 return 63 } 64 65 did, err := storagespace.ParseID(id) 66 if err != nil { 67 logger := appctx.GetLogger(r.Context()) 68 logger.Debug().Str("prop", net.PropOcMetaPathForUser).Msg("invalid resource id") 69 w.WriteHeader(http.StatusBadRequest) 70 m := fmt.Sprintf("Invalid resource id %v", id) 71 b, err := errors.Marshal(http.StatusBadRequest, m, "", "") 72 errors.HandleWebdavError(logger, w, b, err) 73 return 74 } 75 if did.StorageId == "" && did.OpaqueId == "" && strings.Count(id, ":") >= 2 { 76 logger := appctx.GetLogger(r.Context()) 77 logger.Warn().Str("id", id).Msg("detected invalid : separated resourceid id, trying to split it ... but fix the client that made the request") 78 // try splitting with : 79 parts := strings.SplitN(id, ":", 3) 80 did.StorageId = parts[0] 81 did.SpaceId = parts[1] 82 did.OpaqueId = parts[2] 83 } 84 85 var head string 86 head, r.URL.Path = router.ShiftPath(r.URL.Path) 87 switch head { 88 case "": 89 if r.Method != MethodPropfind { 90 w.WriteHeader(http.StatusBadRequest) 91 return 92 } 93 h.handlePathForUser(w, r, s, &did) 94 case "v": 95 h.VersionsHandler.Handler(s, &did).ServeHTTP(w, r) 96 default: 97 w.WriteHeader(http.StatusNotFound) 98 } 99 100 }) 101 } 102 103 func (h *MetaHandler) handlePathForUser(w http.ResponseWriter, r *http.Request, s *svc, rid *provider.ResourceId) { 104 ctx, span := appctx.GetTracerProvider(r.Context()).Tracer(tracerName).Start(r.Context(), "meta_propfind") 105 defer span.End() 106 107 id := storagespace.FormatResourceID(rid) 108 sublog := appctx.GetLogger(ctx).With().Str("path", r.URL.Path).Str("resourceid", id).Logger() 109 sublog.Info().Msg("calling get path for user") 110 111 pf, status, err := propfind.ReadPropfind(r.Body) 112 if err != nil { 113 sublog.Debug().Err(err).Msg("error reading propfind request") 114 w.WriteHeader(status) 115 return 116 } 117 118 if ok := hasProp(&pf, net.PropOcMetaPathForUser); !ok { 119 sublog.Debug().Str("prop", net.PropOcMetaPathForUser).Msg("error finding prop in request") 120 w.WriteHeader(http.StatusBadRequest) 121 return 122 } 123 client, err := s.gatewaySelector.Next() 124 if err != nil { 125 sublog.Error().Err(err).Msg("error selecting next client") 126 w.WriteHeader(http.StatusInternalServerError) 127 return 128 } 129 pathReq := &provider.GetPathRequest{ResourceId: rid} 130 pathRes, err := client.GetPath(ctx, pathReq) 131 if err != nil { 132 sublog.Error().Err(err).Msg("could not send GetPath grpc request: transport error") 133 w.WriteHeader(http.StatusInternalServerError) 134 return 135 } 136 137 switch pathRes.Status.Code { 138 case rpc.Code_CODE_NOT_FOUND: 139 sublog.Debug().Str("code", string(pathRes.Status.Code)).Msg("resource not found") 140 w.WriteHeader(http.StatusNotFound) 141 m := fmt.Sprintf("Resource %s not found", id) 142 b, err := errors.Marshal(http.StatusNotFound, m, "", "") 143 errors.HandleWebdavError(&sublog, w, b, err) 144 return 145 case rpc.Code_CODE_PERMISSION_DENIED: 146 // raise StatusNotFound so that resources can't be enumerated 147 sublog.Debug().Str("code", string(pathRes.Status.Code)).Msg("resource access denied") 148 w.WriteHeader(http.StatusNotFound) 149 m := fmt.Sprintf("Resource %s not found", id) 150 b, err := errors.Marshal(http.StatusNotFound, m, "", "") 151 errors.HandleWebdavError(&sublog, w, b, err) 152 return 153 } 154 155 propstatOK := propfind.PropstatXML{ 156 Status: "HTTP/1.1 200 OK", 157 Prop: []prop.PropertyXML{ 158 prop.Escaped("oc:meta-path-for-user", pathRes.Path), 159 prop.Escaped("oc:id", id), 160 prop.Escaped("oc:fileid", id), 161 prop.Escaped("oc:spaceid", storagespace.FormatStorageID(rid.GetStorageId(), rid.GetSpaceId())), 162 }, 163 } 164 baseURI := ctx.Value(net.CtxKeyBaseURI).(string) 165 msr := propfind.NewMultiStatusResponseXML() 166 msr.Responses = []*propfind.ResponseXML{ 167 { 168 Href: net.EncodePath(path.Join(baseURI, id) + "/"), 169 Propstat: []propfind.PropstatXML{ 170 propstatOK, 171 }, 172 }, 173 } 174 propRes, err := xml.Marshal(msr) 175 if err != nil { 176 sublog.Error().Err(err).Msg("error marshalling propfind response xml") 177 w.WriteHeader(http.StatusInternalServerError) 178 return 179 } 180 181 w.Header().Set(net.HeaderDav, "1, 3, extended-mkcol") 182 w.Header().Set(net.HeaderContentType, "application/xml; charset=utf-8") 183 w.WriteHeader(http.StatusMultiStatus) 184 if _, err := w.Write(propRes); err != nil { 185 sublog.Error().Err(err).Msg("error writing propfind response") 186 return 187 } 188 } 189 190 func (h *MetaHandler) handleEmptyID(w http.ResponseWriter, r *http.Request) { 191 ctx, span := appctx.GetTracerProvider(r.Context()).Tracer(tracerName).Start(r.Context(), "meta_propfind") 192 defer span.End() 193 194 sublog := appctx.GetLogger(ctx).With().Str("path", r.URL.Path).Logger() 195 pf, status, err := propfind.ReadPropfind(r.Body) 196 if err != nil { 197 sublog.Debug().Err(err).Msg("error reading propfind request") 198 w.WriteHeader(status) 199 return 200 } 201 202 if ok := hasProp(&pf, net.PropOcMetaPathForUser); !ok { 203 sublog.Debug().Str("prop", net.PropOcMetaPathForUser).Msg("error finding prop in request") 204 w.WriteHeader(http.StatusBadRequest) 205 return 206 } 207 208 propstatNotFound := propfind.PropstatXML{ 209 Status: "HTTP/1.1 404 Not Found", 210 } 211 baseURI := ctx.Value(net.CtxKeyBaseURI).(string) 212 msr := propfind.NewMultiStatusResponseXML() 213 msr.Responses = []*propfind.ResponseXML{ 214 { 215 Href: net.EncodePath(baseURI + "/"), 216 Propstat: []propfind.PropstatXML{ 217 propstatNotFound, 218 }, 219 }, 220 } 221 propRes, err := xml.Marshal(msr) 222 if err != nil { 223 sublog.Error().Err(err).Msg("error marshalling propfind response xml") 224 w.WriteHeader(http.StatusInternalServerError) 225 return 226 } 227 228 w.Header().Set(net.HeaderDav, "1, 3, extended-mkcol") 229 w.Header().Set(net.HeaderContentType, "application/xml; charset=utf-8") 230 w.WriteHeader(http.StatusMultiStatus) 231 if _, err := w.Write(propRes); err != nil { 232 sublog.Error().Err(err).Msg("error writing propfind response") 233 return 234 } 235 } 236 237 func hasProp(pf *propfind.XML, key string) bool { 238 for i := range pf.Prop { 239 k := fmt.Sprintf("%s/%s", pf.Prop[i].Space, pf.Prop[i].Local) 240 if k == key { 241 return true 242 } 243 } 244 return false 245 }