github.com/cs3org/reva/v2@v2.27.7/internal/grpc/interceptors/eventsmiddleware/events.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 eventsmiddleware 20 21 import ( 22 "context" 23 "fmt" 24 25 user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" 26 ocmcore "github.com/cs3org/go-cs3apis/cs3/ocm/core/v1beta1" 27 rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" 28 collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" 29 link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1" 30 provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" 31 "github.com/cs3org/reva/v2/pkg/appctx" 32 revactx "github.com/cs3org/reva/v2/pkg/ctx" 33 "github.com/cs3org/reva/v2/pkg/events" 34 "github.com/cs3org/reva/v2/pkg/events/stream" 35 "github.com/cs3org/reva/v2/pkg/rgrpc" 36 "github.com/cs3org/reva/v2/pkg/storagespace" 37 "github.com/cs3org/reva/v2/pkg/utils" 38 "github.com/mitchellh/mapstructure" 39 "google.golang.org/grpc" 40 ) 41 42 const ( 43 defaultPriority = 200 44 ) 45 46 func init() { 47 rgrpc.RegisterUnaryInterceptor("eventsmiddleware", NewUnary) 48 } 49 50 // NewUnary returns a new unary interceptor that emits events when needed 51 // no lint because of the switch statement that should be extendable 52 // 53 //nolint:gocritic 54 func NewUnary(m map[string]interface{}) (grpc.UnaryServerInterceptor, int, error) { 55 publisher, err := publisherFromConfig(m) 56 if err != nil { 57 return nil, 0, err 58 } 59 60 interceptor := func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { 61 // Register a channel in the context to receive the space owner id from the handler(s) further down the stack 62 var ownerID *user.UserId 63 sendOwnerChan := make(chan *user.UserId) 64 ctx = storagespace.ContextRegisterSendOwnerChan(ctx, sendOwnerChan) 65 66 res, err := handler(ctx, req) 67 if err != nil { 68 return res, err 69 } 70 71 // Read the space owner id from the channel 72 select { 73 case ownerID = <-sendOwnerChan: 74 default: 75 } 76 77 executant, _ := revactx.ContextGetUser(ctx) 78 79 // The MoveResponse event is moved to the decomposedfs 80 var ev interface{} 81 switch v := res.(type) { 82 case *collaboration.CreateShareResponse: 83 if isSuccess(v) { 84 ev = ShareCreated(v, executant) 85 } 86 case *collaboration.RemoveShareResponse: 87 if isSuccess(v) { 88 ev = ShareRemoved(v, req.(*collaboration.RemoveShareRequest), executant) 89 } 90 case *collaboration.UpdateShareResponse: 91 if isSuccess(v) { 92 ev = ShareUpdated(v, req.(*collaboration.UpdateShareRequest), executant) 93 } 94 case *collaboration.UpdateReceivedShareResponse: 95 if isSuccess(v) { 96 ev = ReceivedShareUpdated(v, executant) 97 } 98 case *link.CreatePublicShareResponse: 99 if isSuccess(v) { 100 ev = LinkCreated(v, executant) 101 } 102 case *link.UpdatePublicShareResponse: 103 if isSuccess(v) { 104 ev = LinkUpdated(v, req.(*link.UpdatePublicShareRequest), executant) 105 } 106 case *link.RemovePublicShareResponse: 107 if isSuccess(v) { 108 ev = LinkRemoved(v, req.(*link.RemovePublicShareRequest), executant) 109 } 110 case *link.GetPublicShareByTokenResponse: 111 if isSuccess(v) { 112 ev = LinkAccessed(v, executant) 113 } else { 114 ev = LinkAccessFailed(v, req.(*link.GetPublicShareByTokenRequest), executant) 115 } 116 case *ocmcore.CreateOCMCoreShareResponse: 117 if isSuccess(v) { 118 ev = OCMCoreShareCreated(v, req.(*ocmcore.CreateOCMCoreShareRequest), executant) 119 } 120 case *provider.AddGrantResponse: 121 // TODO: update CS3 APIs 122 // FIXME these should be part of the RemoveGrantRequest object 123 // https://github.com/owncloud/ocis/issues/4312 124 r := req.(*provider.AddGrantRequest) 125 if isSuccess(v) && utils.ExistsInOpaque(r.Opaque, "spacegrant") { 126 ev = SpaceShared(v, r, executant) 127 } 128 case *provider.UpdateGrantResponse: 129 r := req.(*provider.UpdateGrantRequest) 130 if isSuccess(v) && utils.ExistsInOpaque(r.Opaque, "spacegrant") { 131 ev = SpaceShareUpdated(v, r, executant) 132 } 133 case *provider.RemoveGrantResponse: 134 r := req.(*provider.RemoveGrantRequest) 135 if isSuccess(v) && utils.ExistsInOpaque(r.Opaque, "spacegrant") { 136 ev = SpaceUnshared(v, req.(*provider.RemoveGrantRequest), executant) 137 } 138 case *provider.CreateContainerResponse: 139 if isSuccess(v) { 140 ev = ContainerCreated(v, req.(*provider.CreateContainerRequest), ownerID, executant) 141 } 142 case *provider.InitiateFileDownloadResponse: 143 if isSuccess(v) { 144 ev = FileDownloaded(v, req.(*provider.InitiateFileDownloadRequest), executant) 145 } 146 case *provider.DeleteResponse: 147 if isSuccess(v) { 148 ev = ItemTrashed(v, req.(*provider.DeleteRequest), ownerID, executant) 149 } 150 case *provider.PurgeRecycleResponse: 151 if isSuccess(v) { 152 ev = ItemPurged(v, req.(*provider.PurgeRecycleRequest), executant) 153 } 154 case *provider.RestoreRecycleItemResponse: 155 if isSuccess(v) { 156 ev = ItemRestored(v, req.(*provider.RestoreRecycleItemRequest), ownerID, executant) 157 } 158 case *provider.RestoreFileVersionResponse: 159 if isSuccess(v) { 160 ev = FileVersionRestored(v, req.(*provider.RestoreFileVersionRequest), ownerID, executant) 161 } 162 case *provider.CreateStorageSpaceResponse: 163 if isSuccess(v) && v.StorageSpace != nil { // TODO: Why are there CreateStorageSpaceResponses with nil StorageSpace? 164 ev = SpaceCreated(v, executant) 165 } 166 case *provider.UpdateStorageSpaceResponse: 167 if isSuccess(v) { 168 r := req.(*provider.UpdateStorageSpaceRequest) 169 if r.StorageSpace.Name != "" { 170 ev = SpaceRenamed(v, r, executant) 171 } else if utils.ExistsInOpaque(r.Opaque, "restore") { 172 ev = SpaceEnabled(v, r, executant) 173 } else { 174 ev = SpaceUpdated(v, r, executant) 175 } 176 } 177 case *provider.DeleteStorageSpaceResponse: 178 if isSuccess(v) { 179 r := req.(*provider.DeleteStorageSpaceRequest) 180 if utils.ExistsInOpaque(r.Opaque, "purge") { 181 ev = SpaceDeleted(v, r, executant) 182 } else { 183 ev = SpaceDisabled(v, r, executant) 184 } 185 } 186 case *provider.TouchFileResponse: 187 if isSuccess(v) { 188 ev = FileTouched(v, req.(*provider.TouchFileRequest), ownerID, executant) 189 } 190 case *provider.SetLockResponse: 191 if isSuccess(v) { 192 ev = FileLocked(v, req.(*provider.SetLockRequest), ownerID, executant) 193 } 194 case *provider.UnlockResponse: 195 if isSuccess(v) { 196 ev = FileUnlocked(v, req.(*provider.UnlockRequest), ownerID, executant) 197 } 198 } 199 200 if ev != nil { 201 if err := events.Publish(ctx, publisher, ev); err != nil { 202 appctx.GetLogger(ctx).Error().Err(err).Interface("event", ev).Msg("publishing event failed") 203 } 204 } 205 206 return res, nil 207 } 208 return interceptor, defaultPriority, nil 209 } 210 211 // NewStream returns a new server stream interceptor 212 // that creates the application context. 213 func NewStream() grpc.StreamServerInterceptor { 214 interceptor := func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { 215 // TODO: Use ss.RecvMsg() and ss.SendMsg() to send events from a stream 216 return handler(srv, ss) 217 } 218 return interceptor 219 } 220 221 // common interface to all responses 222 type su interface { 223 GetStatus() *rpc.Status 224 } 225 226 func isSuccess(res su) bool { 227 return res.GetStatus().Code == rpc.Code_CODE_OK 228 } 229 230 func publisherFromConfig(m map[string]interface{}) (events.Publisher, error) { 231 typ := m["type"].(string) 232 switch typ { 233 default: 234 return nil, fmt.Errorf("stream type '%s' not supported", typ) 235 case "nats": 236 var cfg stream.NatsConfig 237 if err := mapstructure.Decode(m, &cfg); err != nil { 238 return nil, err 239 } 240 name, _ := m["name"].(string) 241 return stream.NatsFromConfig(name, false, cfg) 242 } 243 }