diff --git a/CDP4Dal.NetCore.Tests/Permission/PermissionServiceTestFixture.cs b/CDP4Dal.NetCore.Tests/Permission/PermissionServiceTestFixture.cs index 8f56c4913..fe79475b2 100644 --- a/CDP4Dal.NetCore.Tests/Permission/PermissionServiceTestFixture.cs +++ b/CDP4Dal.NetCore.Tests/Permission/PermissionServiceTestFixture.cs @@ -1,17 +1,17 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2015-2020 RHEA System S.A. +// Copyright (c) 2015-2023 RHEA System S.A. // // Author: Sam Gerené, Merlin Bieze, Alex Vorobiev, Naron Phou, Alexander van Delft, Yevhen Ikonnykov // -// This file is part of CDP4-SDK Community Edition +// This file is part of CDP4-COMET SDK Community Edition // -// The CDP4-SDK Community Edition is free software; you can redistribute it and/or +// The CDP4-COMET SDK Community Edition is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 3 of the License, or (at your option) any later version. // -// The CDP4-SDK Community Edition is distributed in the hope that it will be useful, +// The CDP4-COMET SDK Community Edition is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. @@ -27,13 +27,17 @@ namespace CDP4Dal.Tests.Permission using System; using System.Collections.Generic; using System.Linq; + using CDP4Common.CommonData; using CDP4Common.EngineeringModelData; using CDP4Common.Exceptions; using CDP4Common.SiteDirectoryData; - using CDP4Dal.Permission; + using CDP4Dal.DAL; + using CDP4Dal.Permission; + using Moq; + using NUnit.Framework; [TestFixture] @@ -133,9 +137,10 @@ public void Setup() this.session.Setup(x => x.ActivePerson).Returns(this.person); this.session.Setup(x => x.Assembler).Returns(this.assembler); + this.session.Setup(x => x.OpenIterations).Returns(new Dictionary> { - {this.iteration, new Tuple(this.domain1,this.participant)} + { this.iteration, new Tuple(this.domain1, this.participant) } }); this.permissionService = new PermissionService(this.session.Object); @@ -146,7 +151,6 @@ public void TearDown() { } - #region Person Permission [Test] public void TestCanWriteFalseWithDefaultPermission() { @@ -280,9 +284,6 @@ public void VerifyThatReadWriteIfParticipantWorks() Assert.IsTrue(this.permissionService.CanRead(this.modelsetup)); Assert.IsTrue(this.permissionService.CanWrite(this.modelsetup)); } - #endregion - - #region PArticipant Permission [Test] public void VerifyReadWriteParticipantPermission() @@ -307,6 +308,7 @@ public void VerifyModifyIfOwnerForIterationsWithoutDomainOfExpertiseAndParticipa var permission = this.participantRole.ParticipantPermission.Single(x => x.ObjectClass == ClassKind.EngineeringModel); + var defpermission = this.participantRole.ParticipantPermission.Single(x => x.ObjectClass == ClassKind.ElementDefinition); @@ -321,7 +323,7 @@ public void VerifyModifyIfOwnerForIterationsWithoutDomainOfExpertiseAndParticipa this.session.Setup(x => x.OpenIterations).Returns(new Dictionary> { - {this.iteration, new Tuple(null,null)} + { this.iteration, new Tuple(null, null) } }); Assert.IsFalse(this.permissionService.CanWrite(this.elementDef)); @@ -337,6 +339,7 @@ public void VerifyModifyIfOwnerForRequirement() var permission = this.participantRole.ParticipantPermission.Single(x => x.ObjectClass == ClassKind.Requirement); + var specPermission = this.participantRole.ParticipantPermission.Single(x => x.ObjectClass == ClassKind.RequirementsSpecification); @@ -371,7 +374,6 @@ public void VerifyModifyIfOwnerForRequirement() this.requirementsSpecification.Owner = this.domain2; Assert.IsFalse(this.permissionService.CanWrite(this.requirementsSpecification)); Assert.IsTrue(this.permissionService.CanRead(this.requirementsSpecification)); - } [Test] @@ -401,7 +403,6 @@ public void VerifyModifyIfOwnerForThingsThatAreDirectlyUnderEngineeringModel() Assert.IsTrue(this.permissionService.CanRead(this.commonFileStore)); } - [Test] public void VerifySameAsSuperclassParticipantPermission() { @@ -429,7 +430,6 @@ public void VerifySameAsContainerParticipantPermission() Assert.IsTrue(this.permissionService.CanWrite(this.valueset)); Assert.IsTrue(this.permissionService.CanRead(this.valueset)); } - #endregion [Test] public void VerifyCanWriteReturnsFalseWithFrozenIterationSetup() @@ -451,5 +451,19 @@ public void VerifyCanWriteReturnsFalseWithFrozenIterationSetup() Assert.IsFalse(this.permissionService.CanWrite(this.iteration)); Assert.IsFalse(this.permissionService.CanWrite(ClassKind.ElementDefinition, this.iteration)); } + + [Test] + public void VerifyCanCreateOverrideReturnsExpectedResult() + { + this.session.Setup(x => x.ActivePersonParticipants).Returns(new List { this.participant }); + + var permission = this.personRole.PersonPermission.Single(x => x.ObjectClass == ClassKind.EngineeringModelSetup); + permission.AccessRight = PersonAccessRightKind.MODIFY; + Assert.That(this.permissionService.CanCreateOverride(ClassKind.EngineeringModelSetup, ClassKind.SiteDirectory), Is.False); + + permission.AccessRight = PersonAccessRightKind.MODIFY_IF_PARTICIPANT; + Assert.That(this.permissionService.CanCreateOverride(ClassKind.EngineeringModelSetup, ClassKind.SiteDirectory), Is.True); + Assert.That(this.permissionService.CanCreateOverride(ClassKind.EngineeringModelSetup, ClassKind.EngineeringModelSetup), Is.False); + } } -} \ No newline at end of file +} diff --git a/CDP4Dal.Tests/Permission/PermissionServiceTestFixture.cs b/CDP4Dal.Tests/Permission/PermissionServiceTestFixture.cs index 8f56c4913..fe79475b2 100644 --- a/CDP4Dal.Tests/Permission/PermissionServiceTestFixture.cs +++ b/CDP4Dal.Tests/Permission/PermissionServiceTestFixture.cs @@ -1,17 +1,17 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2015-2020 RHEA System S.A. +// Copyright (c) 2015-2023 RHEA System S.A. // // Author: Sam Gerené, Merlin Bieze, Alex Vorobiev, Naron Phou, Alexander van Delft, Yevhen Ikonnykov // -// This file is part of CDP4-SDK Community Edition +// This file is part of CDP4-COMET SDK Community Edition // -// The CDP4-SDK Community Edition is free software; you can redistribute it and/or +// The CDP4-COMET SDK Community Edition is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 3 of the License, or (at your option) any later version. // -// The CDP4-SDK Community Edition is distributed in the hope that it will be useful, +// The CDP4-COMET SDK Community Edition is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. @@ -27,13 +27,17 @@ namespace CDP4Dal.Tests.Permission using System; using System.Collections.Generic; using System.Linq; + using CDP4Common.CommonData; using CDP4Common.EngineeringModelData; using CDP4Common.Exceptions; using CDP4Common.SiteDirectoryData; - using CDP4Dal.Permission; + using CDP4Dal.DAL; + using CDP4Dal.Permission; + using Moq; + using NUnit.Framework; [TestFixture] @@ -133,9 +137,10 @@ public void Setup() this.session.Setup(x => x.ActivePerson).Returns(this.person); this.session.Setup(x => x.Assembler).Returns(this.assembler); + this.session.Setup(x => x.OpenIterations).Returns(new Dictionary> { - {this.iteration, new Tuple(this.domain1,this.participant)} + { this.iteration, new Tuple(this.domain1, this.participant) } }); this.permissionService = new PermissionService(this.session.Object); @@ -146,7 +151,6 @@ public void TearDown() { } - #region Person Permission [Test] public void TestCanWriteFalseWithDefaultPermission() { @@ -280,9 +284,6 @@ public void VerifyThatReadWriteIfParticipantWorks() Assert.IsTrue(this.permissionService.CanRead(this.modelsetup)); Assert.IsTrue(this.permissionService.CanWrite(this.modelsetup)); } - #endregion - - #region PArticipant Permission [Test] public void VerifyReadWriteParticipantPermission() @@ -307,6 +308,7 @@ public void VerifyModifyIfOwnerForIterationsWithoutDomainOfExpertiseAndParticipa var permission = this.participantRole.ParticipantPermission.Single(x => x.ObjectClass == ClassKind.EngineeringModel); + var defpermission = this.participantRole.ParticipantPermission.Single(x => x.ObjectClass == ClassKind.ElementDefinition); @@ -321,7 +323,7 @@ public void VerifyModifyIfOwnerForIterationsWithoutDomainOfExpertiseAndParticipa this.session.Setup(x => x.OpenIterations).Returns(new Dictionary> { - {this.iteration, new Tuple(null,null)} + { this.iteration, new Tuple(null, null) } }); Assert.IsFalse(this.permissionService.CanWrite(this.elementDef)); @@ -337,6 +339,7 @@ public void VerifyModifyIfOwnerForRequirement() var permission = this.participantRole.ParticipantPermission.Single(x => x.ObjectClass == ClassKind.Requirement); + var specPermission = this.participantRole.ParticipantPermission.Single(x => x.ObjectClass == ClassKind.RequirementsSpecification); @@ -371,7 +374,6 @@ public void VerifyModifyIfOwnerForRequirement() this.requirementsSpecification.Owner = this.domain2; Assert.IsFalse(this.permissionService.CanWrite(this.requirementsSpecification)); Assert.IsTrue(this.permissionService.CanRead(this.requirementsSpecification)); - } [Test] @@ -401,7 +403,6 @@ public void VerifyModifyIfOwnerForThingsThatAreDirectlyUnderEngineeringModel() Assert.IsTrue(this.permissionService.CanRead(this.commonFileStore)); } - [Test] public void VerifySameAsSuperclassParticipantPermission() { @@ -429,7 +430,6 @@ public void VerifySameAsContainerParticipantPermission() Assert.IsTrue(this.permissionService.CanWrite(this.valueset)); Assert.IsTrue(this.permissionService.CanRead(this.valueset)); } - #endregion [Test] public void VerifyCanWriteReturnsFalseWithFrozenIterationSetup() @@ -451,5 +451,19 @@ public void VerifyCanWriteReturnsFalseWithFrozenIterationSetup() Assert.IsFalse(this.permissionService.CanWrite(this.iteration)); Assert.IsFalse(this.permissionService.CanWrite(ClassKind.ElementDefinition, this.iteration)); } + + [Test] + public void VerifyCanCreateOverrideReturnsExpectedResult() + { + this.session.Setup(x => x.ActivePersonParticipants).Returns(new List { this.participant }); + + var permission = this.personRole.PersonPermission.Single(x => x.ObjectClass == ClassKind.EngineeringModelSetup); + permission.AccessRight = PersonAccessRightKind.MODIFY; + Assert.That(this.permissionService.CanCreateOverride(ClassKind.EngineeringModelSetup, ClassKind.SiteDirectory), Is.False); + + permission.AccessRight = PersonAccessRightKind.MODIFY_IF_PARTICIPANT; + Assert.That(this.permissionService.CanCreateOverride(ClassKind.EngineeringModelSetup, ClassKind.SiteDirectory), Is.True); + Assert.That(this.permissionService.CanCreateOverride(ClassKind.EngineeringModelSetup, ClassKind.EngineeringModelSetup), Is.False); + } } -} \ No newline at end of file +} diff --git a/CDP4Dal/CDP4Dal.csproj b/CDP4Dal/CDP4Dal.csproj index 54fc5fc87..491c3052c 100644 --- a/CDP4Dal/CDP4Dal.csproj +++ b/CDP4Dal/CDP4Dal.csproj @@ -4,7 +4,7 @@ net462;net47;net471;net472;net48;netstandard2.0;netstandard2.1 RHEA System S.A. CDP4Dal Community Edition - 24.1.1 + 24.2.0 CDP4 Data Access Layer library, a consumer of an ECSS-E-TM-10-25 Annex C API Copyright © RHEA System S.A. Sam, Merlin, Alex, Naron, Alexander, Yevhen, Nathanael, Ahmed @@ -20,7 +20,7 @@ CDP COMET ECSS-E-TM-10-25 LGPL-3.0-only - [UPGRADE] CDP4Common 24.1.1 + [UPGRADE] CDP4Common 24.2.0 README.md diff --git a/CDP4Dal/Permission/IPermissionService.cs b/CDP4Dal/Permission/IPermissionService.cs index 61a0631ff..3ddf5a46f 100644 --- a/CDP4Dal/Permission/IPermissionService.cs +++ b/CDP4Dal/Permission/IPermissionService.cs @@ -1,17 +1,17 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2015-2020 RHEA System S.A. +// Copyright (c) 2015-2023 RHEA System S.A. // // Author: Sam Gerené, Merlin Bieze, Alex Vorobiev, Naron Phou // -// This file is part of CDP4-SDK Community Edition +// This file is part of CDP4-COMET SDK Community Edition // -// The CDP4-SDK Community Edition is free software; you can redistribute it and/or +// The CDP4-COMET SDK Community Edition is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 3 of the License, or (at your option) any later version. // -// The CDP4-SDK Community Edition is distributed in the hope that it will be useful, +// The CDP4-COMET SDK Community Edition is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. @@ -61,5 +61,16 @@ public interface IPermissionService /// The to write to /// True if Write operation can be performed. bool CanWrite(ClassKind classKind, Thing containerThing); + + /// + /// Normally permission to Create, Update or Delete is handled by the CanWrite methods. + /// In some cases Create is allowed to be performed based on , but Update and Delete are not. + /// This method is an extra method that can be performed to check if Create is allowed, based on the TopContainer + /// and the of the to create. + /// + /// The that ultimately determines the permissions. + /// The of the top container class where to create the classKind + /// True if Create operation can be performed. + bool CanCreateOverride(ClassKind classKind, ClassKind containerClassKind); } } \ No newline at end of file diff --git a/CDP4Dal/Permission/PermissionService.cs b/CDP4Dal/Permission/PermissionService.cs index 86573d751..0bcb38808 100644 --- a/CDP4Dal/Permission/PermissionService.cs +++ b/CDP4Dal/Permission/PermissionService.cs @@ -1,17 +1,17 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2015-2020 RHEA System S.A. +// Copyright (c) 2015-2023 RHEA System S.A. // // Author: Sam Gerené, Merlin Bieze, Alex Vorobiev, Naron Phou, Alexander van Delft, Yevhen Ikonnykov // -// This file is part of CDP4-SDK Community Edition +// This file is part of CDP4-COMET SDK Community Edition // -// The CDP4-SDK Community Edition is free software; you can redistribute it and/or +// The CDP4-COMET SDK Community Edition is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 3 of the License, or (at your option) any later version. // -// The CDP4-SDK Community Edition is distributed in the hope that it will be useful, +// The CDP4-COMET SDK Community Edition is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. @@ -34,7 +34,7 @@ namespace CDP4Dal.Permission using CDP4Common.Helpers; using CDP4Common.MetaInfo; using CDP4Common.SiteDirectoryData; - + using NLog; /// @@ -123,6 +123,7 @@ private void CheckOwnedThing(Thing thing) private bool CanReadEngineeringModelContainedThing(Thing thing, Type thingType) { var engineeringModel = thing.TopContainer; + var participant = this.Session.ActivePersonParticipants.FirstOrDefault( p => ((EngineeringModelSetup)p.Container).EngineeringModelIid == engineeringModel.Iid); @@ -173,12 +174,14 @@ private bool CanReadEngineeringModelContainedThing(Thing thing, Type thingType) private bool CanReadSiteDirectoryContainedThing(Thing thing, Type thingType) { var person = this.Session.ActivePerson; + if (person == null) { return false; } var personRole = this.Session.ActivePerson.Role; + if (personRole == null) { return false; @@ -219,6 +222,7 @@ private bool CanReadSiteDirectoryContainedThing(Thing thing, Type thingType) var rdl = this.Session.RetrieveSiteDirectory() .Model.SelectMany(ems => this.Session.GetEngineeringModelSetupRdlChain(ems)); + return rdl.Contains(thing); } @@ -256,6 +260,7 @@ private bool CanWrite(Thing thing, Type thingType) this.CheckOwnedThing(thing); var topContainerClassKind = thing.TopContainer.ClassKind; + switch (topContainerClassKind) { case ClassKind.SiteDirectory: @@ -288,6 +293,7 @@ public bool CanWrite(ClassKind classKind, Thing containerThing) this.CheckOwnedThing(containerThing); var topContainerClassKind = containerThing.TopContainer.ClassKind; + switch (topContainerClassKind) { case ClassKind.SiteDirectory: @@ -299,6 +305,32 @@ public bool CanWrite(ClassKind classKind, Thing containerThing) return false; } + /// + /// Normally permission to Create, Update or Delete is handled by the CanWrite methods. + /// In some cases Create is allowed to be performed based on , but Update and Delete are not. + /// This method is an extra method that can be performed to check if Create is allowed, based on the TopContainer + /// and the of the to create. + /// + /// The that ultimately determines the permissions. + /// The of the top container class where to create the classKind + /// True if Create operation can be performed. + public bool CanCreateOverride(ClassKind classKind, ClassKind containerClassKind) + { + logger.Trace("CanCreate invoked on ClassKind {0} and Container ClassKind {1}", classKind, containerClassKind); + + if (this.Session.Dal.IsReadOnly) + { + return false; + } + + if (containerClassKind == ClassKind.SiteDirectory && classKind == ClassKind.EngineeringModelSetup) + { + return this.CanCreateOverrideSiteDirectoryContainedThing(classKind, containerClassKind); + } + + return false; + } + /// /// Returns whether a Write operation can be performed by the active user on the current contained /// based on the supplied . The ultimately determines the access. @@ -313,6 +345,7 @@ private bool CanWriteEngineeringModelContainedThing(Thing thing, Type thingType) var engineeringModel = thing.TopContainer; var iteration = thing is Iteration it ? it : thing.GetContainerOfType(); + if (iteration?.IterationSetup.FrozenOn != null) { return false; @@ -374,8 +407,8 @@ private bool CanWriteEngineeringModelContainedThing(ClassKind classKind, Thing c { var engineeringModel = containerThing.TopContainer as EngineeringModel; - var iteration = containerThing is Iteration it ? it : containerThing.GetContainerOfType(); - + var iteration = containerThing is Iteration it ? it : containerThing.GetContainerOfType(); + if (iteration?.IterationSetup.FrozenOn != null) { return false; @@ -419,12 +452,14 @@ private bool CanWriteEngineeringModelContainedThing(ClassKind classKind, Thing c private bool CanWriteSiteDirectoryContainedThing(Thing thing, Type thingType) { var person = this.Session.ActivePerson; + if (person == null) { return false; } var personRole = this.Session.ActivePerson.Role; + if (personRole == null) { return false; @@ -463,6 +498,7 @@ private bool CanWriteSiteDirectoryContainedThing(Thing thing, Type thingType) var rdl = this.Session.RetrieveSiteDirectory() .Model.SelectMany(ems => this.Session.GetEngineeringModelSetupRdlChain(ems)); + return rdl.Contains(thing); } @@ -486,12 +522,14 @@ private bool CanWriteSiteDirectoryContainedThing(Thing thing, Type thingType) private bool CanWriteSiteDirectoryContainedThing(ClassKind classKind, Thing containerThing, ClassKind thingType) { var person = this.Session.ActivePerson; + if (person == null) { return false; } var personRole = this.Session.ActivePerson.Role; + if (personRole == null) { return false; @@ -511,6 +549,7 @@ private bool CanWriteSiteDirectoryContainedThing(ClassKind classKind, Thing cont case PersonAccessRightKind.MODIFY: return true; case PersonAccessRightKind.MODIFY_IF_PARTICIPANT: + if (containerThing is EngineeringModelSetup setup) { return setup.Participant.Any(x => x.Person == this.Session.ActivePerson); @@ -519,8 +558,9 @@ private bool CanWriteSiteDirectoryContainedThing(ClassKind classKind, Thing cont if (containerThing is SiteReferenceDataLibrary) { var rdl = - this.Session.RetrieveSiteDirectory() - .Model.SelectMany(ems => this.Session.GetEngineeringModelSetupRdlChain(ems)); + this.Session.RetrieveSiteDirectory() + .Model.SelectMany(ems => this.Session.GetEngineeringModelSetupRdlChain(ems)); + return rdl.Contains(containerThing); } @@ -530,6 +570,55 @@ private bool CanWriteSiteDirectoryContainedThing(ClassKind classKind, Thing cont } } + /// + /// Normally permission to Create, Update or Delete is handled by the CanWrite methods. + /// In some cases Create is allowed to be performed based on , but Update and Delete are not. + /// This method is an extra method that can be performed to check if Create is allowed, based on the TopContainer + /// and the of the to create. + /// + /// The that ultimately determines the permissions. + /// The that determine the permission + /// True if Write operation can be performed. + /// + /// This method should only be called to check for a specific (override) situation. + /// It does not cover all the Create scenario's, only the custom/overridden scenario's + /// + private bool CanCreateOverrideSiteDirectoryContainedThing(ClassKind classKind, ClassKind containerClassKind) + { + var person = this.Session.ActivePerson; + + if (person == null) + { + return false; + } + + var personRole = this.Session.ActivePerson.Role; + + if (personRole == null) + { + return false; + } + + var permission = personRole.PersonPermission.SingleOrDefault(p => p.ObjectClass == classKind); + + // if the permission is not found or superclass derivation is used then get the default one. + var accessRightKind = permission?.AccessRight ?? StaticDefaultPermissionProvider.GetDefaultPersonPermission(containerClassKind.ToString()); + + switch (accessRightKind) + { + case PersonAccessRightKind.MODIFY_IF_PARTICIPANT: + if (classKind == ClassKind.EngineeringModelSetup && containerClassKind == ClassKind.SiteDirectory) + { + //If a participant has MODIFY_IF_PARTICIPANT access right on EngineeringModelSetup and SiteDirectory is sent as the Container, he/she is allowed to create a new EngineeringModelSetup. + return true; + } + + return false; + default: + return false; + } + } + /// /// Resolves whether the write operation can be performed on a of /// based on the superclass of @@ -565,25 +654,25 @@ private bool CanWriteIfParticipantOwned(IOwnedThing ownedThing) } //Check if the ownedThing domain is contained in the participant domains - return this.TryGetThingParticipant(thing, out var participant) + return this.TryGetThingParticipant(thing, out var participant) && participant.Domain.Contains(ownedThing.Owner); } - /// - /// Try to get the user's 'participant information for the Iteration where the thing input parameter belongs to - /// - /// General Thing for which the user's participant information is retrieved. - /// outgoing parameter that contains the user's information. + /// + /// Try to get the user's 'participant information for the Iteration where the thing input parameter belongs to + /// + /// General Thing for which the user's participant information is retrieved. + /// outgoing parameter that contains the user's information. /// private bool TryGetThingParticipant(Thing thing, out Participant participant) { var iteration = thing is Iteration it ? it : thing.GetContainerOfType(); participant = null; - if (iteration != null - && this.Session.OpenIterations.TryGetValue(iteration, out var participation) - && participation.Item1 != null - && participation.Item2 != null) + if (iteration != null + && this.Session.OpenIterations.TryGetValue(iteration, out var participation) + && participation.Item1 != null + && participation.Item2 != null) { participant = participation.Item2; return true; @@ -592,4 +681,4 @@ private bool TryGetThingParticipant(Thing thing, out Participant participant) return false; } } -} \ No newline at end of file +}