github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/sys/workspace/impl_deactivate.go (about) 1 /* 2 * Copyright (c) 2020-present unTill Pro, Ltd. 3 * @author Denis Gribanov 4 */ 5 6 package workspace 7 8 import ( 9 "fmt" 10 "net/http" 11 12 "github.com/voedger/voedger/pkg/appdef" 13 "github.com/voedger/voedger/pkg/goutils/logger" 14 "github.com/voedger/voedger/pkg/istructs" 15 "github.com/voedger/voedger/pkg/istructsmem" 16 "github.com/voedger/voedger/pkg/itokens" 17 payloads "github.com/voedger/voedger/pkg/itokens-payloads" 18 "github.com/voedger/voedger/pkg/state" 19 "github.com/voedger/voedger/pkg/sys/authnz" 20 "github.com/voedger/voedger/pkg/sys/collection" 21 "github.com/voedger/voedger/pkg/sys/invite" 22 coreutils "github.com/voedger/voedger/pkg/utils" 23 "github.com/voedger/voedger/pkg/utils/federation" 24 ) 25 26 func provideDeactivateWorkspace(cfg *istructsmem.AppConfigType, tokensAPI itokens.ITokens, federation federation.IFederation) { 27 28 // c.sys.DeactivateWorkspace 29 // target app, target WSID 30 cfg.Resources.Add(istructsmem.NewCommandFunction( 31 qNameCmdInitiateDeactivateWorkspace, 32 cmdInitiateDeactivateWorkspaceExec, 33 )) 34 35 // c.sys.OnWorkspaceDeactivated 36 // owner app, owner WSID 37 cfg.Resources.Add(istructsmem.NewCommandFunction( 38 appdef.NewQName(appdef.SysPackage, "OnWorkspaceDeactivated"), 39 cmdOnWorkspaceDeactivatedExec, 40 )) 41 42 // c.sys.OnJoinedWorkspaceDeactivated 43 // target app, profile WSID 44 cfg.Resources.Add(istructsmem.NewCommandFunction( 45 appdef.NewQName(appdef.SysPackage, "OnJoinedWorkspaceDeactivated"), 46 cmdOnJoinedWorkspaceDeactivateExec, 47 )) 48 49 // c.sys.OnChildWorkspaceDeactivated 50 // ownerApp/ownerWSID 51 cfg.Resources.Add(istructsmem.NewCommandFunction( 52 appdef.NewQName(appdef.SysPackage, "OnChildWorkspaceDeactivated"), 53 cmdOnChildWorkspaceDeactivatedExec, 54 )) 55 56 // target app, target WSID 57 cfg.AddAsyncProjectors(istructs.Projector{ 58 Name: qNameProjectorApplyDeactivateWorkspace, 59 Func: projectorApplyDeactivateWorkspace(federation, tokensAPI), 60 }) 61 } 62 63 func cmdInitiateDeactivateWorkspaceExec(args istructs.ExecCommandArgs) (err error) { 64 kb, err := args.State.KeyBuilder(state.Record, authnz.QNameCDocWorkspaceDescriptor) 65 if err != nil { 66 // notest 67 return err 68 } 69 kb.PutQName(state.Field_Singleton, authnz.QNameCDocWorkspaceDescriptor) 70 wsDesc, err := args.State.MustExist(kb) 71 if err != nil { 72 // notest 73 return err 74 } 75 status := wsDesc.AsInt32(authnz.Field_Status) 76 if status != int32(authnz.WorkspaceStatus_Active) { 77 return coreutils.NewHTTPErrorf(http.StatusConflict, "Workspace Status is not Active") 78 } 79 80 wsDescUpdater, err := args.Intents.UpdateValue(kb, wsDesc) 81 if err != nil { 82 // notest 83 return err 84 } 85 wsDescUpdater.PutInt32(authnz.Field_Status, int32(authnz.WorkspaceStatus_ToBeDeactivated)) 86 return nil 87 } 88 89 func cmdOnJoinedWorkspaceDeactivateExec(args istructs.ExecCommandArgs) (err error) { 90 invitedToWSID := args.ArgumentObject.AsInt64(field_InvitedToWSID) 91 svCDocJoinedWorkspace, skb, ok, err := invite.GetCDocJoinedWorkspace(args.State, invitedToWSID) 92 if err != nil || !ok { 93 return err 94 } 95 if !svCDocJoinedWorkspace.AsBool(appdef.SystemField_IsActive) { 96 return nil 97 } 98 cdocJoinedWorkspaceUpdater, err := args.Intents.UpdateValue(skb, svCDocJoinedWorkspace) 99 if err != nil { 100 // notest 101 return err 102 } 103 cdocJoinedWorkspaceUpdater.PutBool(appdef.SystemField_IsActive, false) 104 return nil 105 } 106 107 // app/pseudoProfileWSID, ownerApp 108 func cmdOnWorkspaceDeactivatedExec(args istructs.ExecCommandArgs) (err error) { 109 ownerWSID := args.ArgumentObject.AsInt64(Field_OwnerWSID) 110 wsName := args.ArgumentObject.AsString(authnz.Field_WSName) 111 kb, err := args.State.KeyBuilder(state.View, QNameViewWorkspaceIDIdx) 112 if err != nil { 113 // notest 114 return err 115 } 116 kb.PutInt64(Field_OwnerWSID, ownerWSID) 117 kb.PutString(authnz.Field_WSName, wsName) 118 viewRec, ok, err := args.State.CanExist(kb) 119 if err != nil { 120 // notest 121 return err 122 } 123 if !ok { 124 logger.Verbose("workspace", wsName, ":", ownerWSID, "is not mentioned in view.sys.WorkspaceIDId") 125 return 126 } 127 idOfCDocWorkspaceID := viewRec.AsRecordID(field_IDOfCDocWorkspaceID) 128 kb, err = args.State.KeyBuilder(state.Record, QNameCDocWorkspaceID) 129 if err != nil { 130 // notest 131 return err 132 } 133 kb.PutRecordID(state.Field_ID, idOfCDocWorkspaceID) 134 cdocWorkspaceID, err := args.State.MustExist(kb) 135 if err != nil { 136 // notest 137 return err 138 } 139 140 if !cdocWorkspaceID.AsBool(appdef.SystemField_IsActive) { 141 logger.Verbose("cdoc.sys.WorkspaceID is inactive already") 142 return nil 143 } 144 145 cdocWorkspaceIDUpdater, err := args.Intents.UpdateValue(kb, cdocWorkspaceID) 146 if err != nil { 147 // notest 148 return err 149 } 150 cdocWorkspaceIDUpdater.PutBool(appdef.SystemField_IsActive, false) 151 return nil 152 } 153 154 // ownerApp/ownerWSID 155 func cmdOnChildWorkspaceDeactivatedExec(args istructs.ExecCommandArgs) (err error) { 156 ownerID := args.ArgumentObject.AsInt64(Field_OwnerID) 157 kb, err := args.State.KeyBuilder(state.Record, appdef.NullQName) 158 if err != nil { 159 // notest 160 return err 161 } 162 kb.PutRecordID(state.Field_ID, istructs.RecordID(ownerID)) 163 cdocOwnerSV, err := args.State.MustExist(kb) 164 if err != nil { 165 // notest 166 return err 167 } 168 if !cdocOwnerSV.AsBool(appdef.SystemField_IsActive) { 169 return nil 170 } 171 cdocOwnerUpdater, err := args.Intents.UpdateValue(kb, cdocOwnerSV) 172 if err != nil { 173 // notest 174 return err 175 } 176 cdocOwnerUpdater.PutBool(appdef.SystemField_IsActive, false) 177 return nil 178 } 179 180 // target app, target WSID 181 func projectorApplyDeactivateWorkspace(federation federation.IFederation, tokensAPI itokens.ITokens) func(event istructs.IPLogEvent, s istructs.IState, intents istructs.IIntents) (err error) { 182 return func(event istructs.IPLogEvent, s istructs.IState, intents istructs.IIntents) (err error) { 183 kb, err := s.KeyBuilder(state.Record, authnz.QNameCDocWorkspaceDescriptor) 184 if err != nil { 185 // notest 186 return err 187 } 188 kb.PutQName(state.Field_Singleton, authnz.QNameCDocWorkspaceDescriptor) 189 wsDesc, err := s.MustExist(kb) 190 if err != nil { 191 // notest 192 return err 193 } 194 ownerApp := wsDesc.AsString(Field_OwnerApp) 195 ownerWSID := wsDesc.AsInt64(Field_OwnerWSID) 196 ownerID := wsDesc.AsInt64(Field_OwnerID) 197 198 appQName := s.App() 199 200 sysToken, err := payloads.GetSystemPrincipalToken(tokensAPI, appQName) 201 if err != nil { 202 // notest 203 return err 204 } 205 206 // Foreach cdoc.sys.Subject 207 subjectsKB, err := s.KeyBuilder(state.View, collection.QNameCollectionView) 208 if err != nil { 209 // notest 210 return err 211 } 212 subjectsKB.PutInt32(collection.Field_PartKey, collection.PartitionKeyCollection) 213 subjectsKB.PutQName(collection.Field_DocQName, invite.QNameCDocSubject) 214 err = s.Read(subjectsKB, func(_ istructs.IKey, value istructs.IStateValue) (err error) { 215 subject := value.AsRecord(collection.Field_Record) 216 if istructs.SubjectKindType(subject.AsInt32(authnz.Field_SubjectKind)) != istructs.SubjectKind_User { 217 return nil 218 } 219 profileWSID := istructs.WSID(subject.AsInt64(invite.Field_ProfileWSID)) 220 221 // app is always current 222 // impossible to have logins from different apps among subjects (Michael said) 223 url := fmt.Sprintf(`api/%s/%d/c.sys.OnJoinedWorkspaceDeactivated`, appQName, profileWSID) 224 body := fmt.Sprintf(`{"args":{"InvitedToWSID":%d}}`, event.Workspace()) 225 _, err = federation.Func(url, body, coreutils.WithAuthorizeBy(sysToken), coreutils.WithDiscardResponse()) 226 return err 227 }) 228 if err != nil { 229 // notestdebt 230 return err 231 } 232 233 // currentApp/ApplicationWS/c.sys.OnWorkspaceDeactivated(OnwerWSID, WSName) 234 wsName := wsDesc.AsString(authnz.Field_WSName) 235 body := fmt.Sprintf(`{"args":{"OwnerWSID":%d, "WSName":"%s"}}`, ownerWSID, wsName) 236 cdocWorkspaceIDWSID := coreutils.GetPseudoWSID(istructs.WSID(ownerWSID), wsName, event.Workspace().ClusterID()) 237 if _, err := federation.Func(fmt.Sprintf("api/%s/%d/c.sys.OnWorkspaceDeactivated", ownerApp, cdocWorkspaceIDWSID), body, 238 coreutils.WithDiscardResponse(), coreutils.WithAuthorizeBy(sysToken)); err != nil { 239 return fmt.Errorf("c.sys.OnWorkspaceDeactivated failed: %w", err) 240 } 241 242 // c.sys.OnChildWorkspaceDeactivated(ownerID)) 243 body = fmt.Sprintf(`{"args":{"OwnerID":%d}}`, ownerID) 244 if _, err := federation.Func(fmt.Sprintf("api/%s/%d/c.sys.OnChildWorkspaceDeactivated", ownerApp, ownerWSID), body, 245 coreutils.WithDiscardResponse(), coreutils.WithAuthorizeBy(sysToken)); err != nil { 246 return fmt.Errorf("c.sys.OnChildWorkspaceDeactivated failed: %w", err) 247 } 248 249 // cdoc.sys.WorkspaceDescriptor.Status = Inactive 250 body = fmt.Sprintf(`{"cuds":[{"sys.ID":%d,"fields":{"Status":%d}}]}`, wsDesc.AsRecordID(appdef.SystemField_ID), authnz.WorkspaceStatus_Inactive) 251 if _, err := federation.Func(fmt.Sprintf("api/%s/%d/c.sys.CUD", appQName, event.Workspace()), body, 252 coreutils.WithDiscardResponse(), coreutils.WithAuthorizeBy(sysToken)); err != nil { 253 return fmt.Errorf("cdoc.sys.WorkspaceDescriptor.Status=Inactive failed: %w", err) 254 } 255 256 logger.Info("workspace", wsDesc.AsString(authnz.Field_WSName), "deactivated") 257 return nil 258 } 259 }