diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Models/AcceptedOrganizationUser.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Models/AcceptedOrganizationUser.cs
index a223c13990..4f596d596a 100644
--- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Models/AcceptedOrganizationUser.cs
+++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Models/AcceptedOrganizationUser.cs
@@ -1,8 +1,8 @@
-using Bit.Core.AdminConsole.Interfaces;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models;
+using Bit.Core.Services;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Models;
@@ -57,13 +57,24 @@ public class AcceptedOrganizationUser : IExternal, IOrganizationUserPermissions
///
public bool AccessSecretsManager { get; set; }
+ ///
+ /// True if the user's access has been revoked, false otherwise.
+ ///
+ public bool Revoked { get; set; }
+
///
/// Transitions this accepted user to a confirmed state when an organization admin confirms them.
///
/// The Organization symmetric key encrypted with the User's public key.
/// A new instance.
+ /// Thrown if the user is revoked.
public ConfirmedOrganizationUser ToConfirmed(string key)
{
+ if (Revoked)
+ {
+ throw new InvalidOperationException("Cannot transition a revoked user to confirmed status");
+ }
+
return new ConfirmedOrganizationUser
{
Id = Id,
@@ -76,14 +87,15 @@ public class AcceptedOrganizationUser : IExternal, IOrganizationUserPermissions
CreationDate = CreationDate,
RevisionDate = DateTime.UtcNow,
Permissions = Permissions,
- AccessSecretsManager = AccessSecretsManager
+ AccessSecretsManager = AccessSecretsManager,
+ Revoked = false
};
}
///
/// Converts this model to an entity.
///
- /// An entity with Status set to Accepted.
+ /// An entity with Status set to Accepted or Revoked based on the Revoked flag.
public OrganizationUser ToEntity()
{
return new OrganizationUser
@@ -94,7 +106,7 @@ public class AcceptedOrganizationUser : IExternal, IOrganizationUserPermissions
Email = null,
Key = null,
ResetPasswordKey = null,
- Status = OrganizationUserStatusType.Accepted,
+ Status = Revoked ? OrganizationUserStatusType.Revoked : OrganizationUserStatusType.Accepted,
Type = Type,
ExternalId = ExternalId,
CreationDate = CreationDate,
@@ -107,16 +119,28 @@ public class AcceptedOrganizationUser : IExternal, IOrganizationUserPermissions
///
/// Creates an from an entity.
///
- /// The entity to convert from. Must have Status = Accepted and UserId must not be null.
+ /// The entity to convert from. Must have Status = Accepted or Revoked (with pre-revoked status of Accepted), and UserId must not be null.
/// A new instance.
- /// Thrown if the entity is not in Accepted status or UserId is null.
+ /// Thrown if the entity status is invalid or UserId is null.
public static AcceptedOrganizationUser FromEntity(OrganizationUser entity)
{
- if (entity.Status != OrganizationUserStatusType.Accepted)
+ var isRevoked = entity.Status == OrganizationUserStatusType.Revoked;
+
+ if (!isRevoked && entity.Status != OrganizationUserStatusType.Accepted)
{
throw new InvalidOperationException($"Cannot create AcceptedOrganizationUser from entity with status {entity.Status}");
}
+ if (isRevoked)
+ {
+ // Validate that the revoked user's pre-revoked status is Accepted
+ var preRevokedStatus = OrganizationService.GetPriorActiveOrganizationUserStatusType(entity);
+ if (preRevokedStatus != OrganizationUserStatusType.Accepted)
+ {
+ throw new InvalidOperationException($"Cannot create AcceptedOrganizationUser from revoked entity with pre-revoked status {preRevokedStatus}");
+ }
+ }
+
if (!entity.UserId.HasValue)
{
throw new InvalidOperationException("Cannot create AcceptedOrganizationUser from entity with null UserId");
@@ -132,7 +156,8 @@ public class AcceptedOrganizationUser : IExternal, IOrganizationUserPermissions
CreationDate = entity.CreationDate,
RevisionDate = entity.RevisionDate,
Permissions = entity.Permissions,
- AccessSecretsManager = entity.AccessSecretsManager
+ AccessSecretsManager = entity.AccessSecretsManager,
+ Revoked = isRevoked
};
}
}
diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Models/ConfirmedOrganizationUser.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Models/ConfirmedOrganizationUser.cs
index b6e1bda29c..4637a2c63f 100644
--- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Models/ConfirmedOrganizationUser.cs
+++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Models/ConfirmedOrganizationUser.cs
@@ -1,8 +1,8 @@
-using Bit.Core.AdminConsole.Interfaces;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models;
+using Bit.Core.Services;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Models;
@@ -69,10 +69,15 @@ public class ConfirmedOrganizationUser : IExternal, IOrganizationUserPermissions
///
public bool AccessSecretsManager { get; set; }
+ ///
+ /// True if the user's access has been revoked, false otherwise.
+ ///
+ public bool Revoked { get; set; }
+
///
/// Converts this model to an entity.
///
- /// An entity with Status set to Confirmed.
+ /// An entity with Status set to Confirmed or Revoked based on the Revoked flag.
public OrganizationUser ToEntity()
{
return new OrganizationUser
@@ -83,7 +88,7 @@ public class ConfirmedOrganizationUser : IExternal, IOrganizationUserPermissions
Email = null,
Key = Key,
ResetPasswordKey = ResetPasswordKey,
- Status = OrganizationUserStatusType.Confirmed,
+ Status = Revoked ? OrganizationUserStatusType.Revoked : OrganizationUserStatusType.Confirmed,
Type = Type,
ExternalId = ExternalId,
CreationDate = CreationDate,
@@ -96,16 +101,28 @@ public class ConfirmedOrganizationUser : IExternal, IOrganizationUserPermissions
///
/// Creates a from an entity.
///
- /// The entity to convert from. Must have Status = Confirmed, UserId and Key must not be null.
+ /// The entity to convert from. Must have Status = Confirmed or Revoked (with pre-revoked status of Confirmed), UserId and Key must not be null.
/// A new instance.
- /// Thrown if the entity is not in Confirmed status, or UserId or Key is null.
+ /// Thrown if the entity status is invalid, or UserId or Key is null.
public static ConfirmedOrganizationUser FromEntity(OrganizationUser entity)
{
- if (entity.Status != OrganizationUserStatusType.Confirmed)
+ var isRevoked = entity.Status == OrganizationUserStatusType.Revoked;
+
+ if (!isRevoked && entity.Status != OrganizationUserStatusType.Confirmed)
{
throw new InvalidOperationException($"Cannot create ConfirmedOrganizationUser from entity with status {entity.Status}");
}
+ if (isRevoked)
+ {
+ // Validate that the revoked user's pre-revoked status is Confirmed
+ var preRevokedStatus = OrganizationService.GetPriorActiveOrganizationUserStatusType(entity);
+ if (preRevokedStatus != OrganizationUserStatusType.Confirmed)
+ {
+ throw new InvalidOperationException($"Cannot create ConfirmedOrganizationUser from revoked entity with pre-revoked status {preRevokedStatus}");
+ }
+ }
+
if (!entity.UserId.HasValue)
{
throw new InvalidOperationException("Cannot create ConfirmedOrganizationUser from entity with null UserId");
@@ -128,7 +145,8 @@ public class ConfirmedOrganizationUser : IExternal, IOrganizationUserPermissions
CreationDate = entity.CreationDate,
RevisionDate = entity.RevisionDate,
Permissions = entity.Permissions,
- AccessSecretsManager = entity.AccessSecretsManager
+ AccessSecretsManager = entity.AccessSecretsManager,
+ Revoked = isRevoked
};
}
}
diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Models/InvitedOrganizationUser.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Models/InvitedOrganizationUser.cs
index 2383dd9a86..e39bf562ae 100644
--- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Models/InvitedOrganizationUser.cs
+++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Models/InvitedOrganizationUser.cs
@@ -2,6 +2,7 @@ using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models;
+using Bit.Core.Services;
using Bit.Core.Utilities;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Models;
@@ -57,6 +58,11 @@ public class InvitedOrganizationUser : IExternal, IOrganizationUserPermissions
///
public bool AccessSecretsManager { get; set; }
+ ///
+ /// True if the user's access has been revoked, false otherwise.
+ ///
+ public bool Revoked { get; set; }
+
public void SetNewId()
{
Id = CoreHelpers.GenerateComb();
@@ -67,8 +73,14 @@ public class InvitedOrganizationUser : IExternal, IOrganizationUserPermissions
///
/// The ID of the User who accepted the invitation.
/// A new instance.
+ /// Thrown if the user is revoked.
public AcceptedOrganizationUser ToAccepted(Guid userId)
{
+ if (Revoked)
+ {
+ throw new InvalidOperationException("Cannot transition a revoked user to accepted status");
+ }
+
return new AcceptedOrganizationUser
{
Id = Id,
@@ -79,14 +91,15 @@ public class InvitedOrganizationUser : IExternal, IOrganizationUserPermissions
CreationDate = CreationDate,
RevisionDate = DateTime.UtcNow,
Permissions = Permissions,
- AccessSecretsManager = AccessSecretsManager
+ AccessSecretsManager = AccessSecretsManager,
+ Revoked = false
};
}
///
/// Converts this model to an entity.
///
- /// An entity with Status set to Invited.
+ /// An entity with Status set to Invited or Revoked based on the Revoked flag.
public OrganizationUser ToEntity()
{
return new OrganizationUser
@@ -97,7 +110,7 @@ public class InvitedOrganizationUser : IExternal, IOrganizationUserPermissions
Email = Email,
Key = null,
ResetPasswordKey = null,
- Status = OrganizationUserStatusType.Invited,
+ Status = Revoked ? OrganizationUserStatusType.Revoked : OrganizationUserStatusType.Invited,
Type = Type,
ExternalId = ExternalId,
CreationDate = CreationDate,
@@ -110,16 +123,28 @@ public class InvitedOrganizationUser : IExternal, IOrganizationUserPermissions
///
/// Creates an from an entity.
///
- /// The entity to convert from. Must have Status = Invited and Email must not be null.
+ /// The entity to convert from. Must have Status = Invited or Revoked (with pre-revoked status of Invited), and Email must not be null.
/// A new instance.
- /// Thrown if the entity is not in Invited status or Email is null.
+ /// Thrown if the entity status is invalid or Email is null.
public static InvitedOrganizationUser FromEntity(OrganizationUser entity)
{
- if (entity.Status != OrganizationUserStatusType.Invited)
+ var isRevoked = entity.Status == OrganizationUserStatusType.Revoked;
+
+ if (!isRevoked && entity.Status != OrganizationUserStatusType.Invited)
{
throw new InvalidOperationException($"Cannot create InvitedOrganizationUser from entity with status {entity.Status}");
}
+ if (isRevoked)
+ {
+ // Validate that the revoked user's pre-revoked status is Invited
+ var preRevokedStatus = OrganizationService.GetPriorActiveOrganizationUserStatusType(entity);
+ if (preRevokedStatus != OrganizationUserStatusType.Invited)
+ {
+ throw new InvalidOperationException($"Cannot create InvitedOrganizationUser from revoked entity with pre-revoked status {preRevokedStatus}");
+ }
+ }
+
if (string.IsNullOrEmpty(entity.Email))
{
throw new InvalidOperationException("Cannot create InvitedOrganizationUser from entity with null Email");
@@ -135,7 +160,8 @@ public class InvitedOrganizationUser : IExternal, IOrganizationUserPermissions
CreationDate = entity.CreationDate,
RevisionDate = entity.RevisionDate,
Permissions = entity.Permissions,
- AccessSecretsManager = entity.AccessSecretsManager
+ AccessSecretsManager = entity.AccessSecretsManager,
+ Revoked = isRevoked
};
}
}