1
0
mirror of https://github.com/bitwarden/server.git synced 2025-05-13 15:42:17 -05:00

[PM-21097] Fix: Prevent admin-added sponsored families from appearing in individual vault settings (#5767)

* Changes to resolve sponsorship showing in individual vault

* Resolve the failing unit test

Signed-off-by: Cy Okeke <cokeke@bitwarden.com>

* Resolve the failing test

* Resolve the failing test

* Resolve the failing test

* fix make IsAdminInitiated nullable

Signed-off-by: Cy Okeke <cokeke@bitwarden.com>

* Add the isAdminInitiated property

Signed-off-by: Cy Okeke <cokeke@bitwarden.com>

* Resolve the database error

Signed-off-by: Cy Okeke <cokeke@bitwarden.com>

* Resolve the failing unit test

Signed-off-by: Cy Okeke <cokeke@bitwarden.com>

* Resolve the scan error

Signed-off-by: Cy Okeke <cokeke@bitwarden.com>

* Resolve the database issue

* resolve the database build error

* Resolve the database build error

* Resolve the synchronization issue

---------

Signed-off-by: Cy Okeke <cokeke@bitwarden.com>
This commit is contained in:
cyprain-okeke 2025-05-12 18:21:07 +01:00 committed by GitHub
parent 3a848d5747
commit e4359f071c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 294 additions and 12 deletions

View File

@ -58,7 +58,8 @@ public class ProfileOrganizationResponseModel : ResponseModel
ProviderName = organization.ProviderName;
ProviderType = organization.ProviderType;
FamilySponsorshipFriendlyName = organization.FamilySponsorshipFriendlyName;
FamilySponsorshipAvailable = FamilySponsorshipFriendlyName == null &&
IsAdminInitiated = organization.IsAdminInitiated ?? false;
FamilySponsorshipAvailable = (FamilySponsorshipFriendlyName == null || IsAdminInitiated) &&
StaticStore.GetSponsoredPlan(PlanSponsorshipType.FamiliesForEnterprise)
.UsersCanSponsor(organization);
ProductTierType = organization.PlanType.GetProductTier();
@ -157,4 +158,5 @@ public class ProfileOrganizationResponseModel : ResponseModel
public bool UserIsClaimedByOrganization { get; set; }
public bool UseRiskInsights { get; set; }
public bool UseAdminSponsoredFamilies { get; set; }
public bool IsAdminInitiated { get; set; }
}

View File

@ -271,8 +271,11 @@ public class OrganizationSponsorshipsController : Controller
}
var sponsorships = await _organizationSponsorshipRepository.GetManyBySponsoringOrganizationAsync(sponsoringOrgId);
return new ListResponseModel<OrganizationSponsorshipInvitesResponseModel>(sponsorships.Select(s =>
new OrganizationSponsorshipInvitesResponseModel(new OrganizationSponsorshipData(s))));
return new ListResponseModel<OrganizationSponsorshipInvitesResponseModel>(
sponsorships
.Where(s => s.IsAdminInitiated)
.Select(s => new OrganizationSponsorshipInvitesResponseModel(new OrganizationSponsorshipData(s)))
);
}

View File

@ -60,4 +60,5 @@ public class OrganizationUserOrganizationDetails
public bool AllowAdminAccessToAllCollectionItems { get; set; }
public bool UseRiskInsights { get; set; }
public bool UseAdminSponsoredFamilies { get; set; }
public bool? IsAdminInitiated { get; set; }
}

View File

@ -14,6 +14,7 @@ public class OrganizationSponsorshipResponseModel
public bool ToDelete { get; set; }
public bool CloudSponsorshipRemoved { get; set; }
public bool IsAdminInitiated { get; set; }
public OrganizationSponsorshipResponseModel() { }
@ -27,6 +28,7 @@ public class OrganizationSponsorshipResponseModel
ValidUntil = sponsorshipData.ValidUntil;
ToDelete = sponsorshipData.ToDelete;
CloudSponsorshipRemoved = sponsorshipData.CloudSponsorshipRemoved;
IsAdminInitiated = sponsorshipData.IsAdminInitiated;
}
public OrganizationSponsorshipData ToOrganizationSponsorship()
@ -40,7 +42,8 @@ public class OrganizationSponsorshipResponseModel
LastSyncDate = LastSyncDate,
ValidUntil = ValidUntil,
ToDelete = ToDelete,
CloudSponsorshipRemoved = CloudSponsorshipRemoved
CloudSponsorshipRemoved = CloudSponsorshipRemoved,
IsAdminInitiated = IsAdminInitiated,
};
}

View File

@ -7,8 +7,7 @@ public class OrganizationUserOrganizationDetailsViewQuery : IQuery<OrganizationU
public IQueryable<OrganizationUserOrganizationDetails> Run(DatabaseContext dbContext)
{
var query = from ou in dbContext.OrganizationUsers
join o in dbContext.Organizations on ou.OrganizationId equals o.Id into outerOrganization
from o in outerOrganization.DefaultIfEmpty()
join o in dbContext.Organizations on ou.OrganizationId equals o.Id
join su in dbContext.SsoUsers on new { ou.UserId, OrganizationId = (Guid?)ou.OrganizationId } equals new { UserId = (Guid?)su.UserId, su.OrganizationId } into su_g
from su in su_g.DefaultIfEmpty()
join po in dbContext.ProviderOrganizations on o.Id equals po.OrganizationId into po_g
@ -68,10 +67,11 @@ public class OrganizationUserOrganizationDetailsViewQuery : IQuery<OrganizationU
SmServiceAccounts = o.SmServiceAccounts,
LimitCollectionCreation = o.LimitCollectionCreation,
LimitCollectionDeletion = o.LimitCollectionDeletion,
LimitItemDeletion = o.LimitItemDeletion,
AllowAdminAccessToAllCollectionItems = o.AllowAdminAccessToAllCollectionItems,
UseRiskInsights = o.UseRiskInsights,
UseAdminSponsoredFamilies = o.UseAdminSponsoredFamilies,
LimitItemDeletion = o.LimitItemDeletion,
IsAdminInitiated = os.IsAdminInitiated
};
return query;
}

View File

@ -1,8 +1,9 @@
CREATE PROCEDURE [dbo].[OrganizationSponsorship_ReadBySponsoringOrganizationUserId]
@SponsoringOrganizationUserId UNIQUEIDENTIFIER
@SponsoringOrganizationUserId UNIQUEIDENTIFIER,
@IsAdminInitiated BIT = 0
AS
BEGIN
SET NOCOUNT ON
SET NOCOUNT ON;
SELECT
*
@ -10,5 +11,5 @@ BEGIN
[dbo].[OrganizationSponsorshipView]
WHERE
[SponsoringOrganizationUserId] = @SponsoringOrganizationUserId
END
GO
and [IsAdminInitiated] = @IsAdminInitiated
END

View File

@ -51,7 +51,8 @@ SELECT
O.[AllowAdminAccessToAllCollectionItems],
O.[UseRiskInsights],
O.[UseAdminSponsoredFamilies],
O.[LimitItemDeletion]
O.[LimitItemDeletion],
OS.[IsAdminInitiated]
FROM
[dbo].[OrganizationUser] OU
LEFT JOIN

View File

@ -216,6 +216,12 @@ public class OrganizationSponsorshipsControllerTests
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetManyBySponsoringOrganizationAsync(sponsoringOrg.Id).Returns(sponsorships);
// Set IsAdminInitiated to true for all test sponsorships
foreach (var sponsorship in sponsorships)
{
sponsorship.IsAdminInitiated = true;
}
// Act
var result = await sutProvider.Sut.GetSponsoredOrganizations(sponsoringOrg.Id);

View File

@ -316,6 +316,29 @@ public class OrganizationUserRepositoryTests
BillingEmail = user1.Email, // TODO: EF does not enforce this being NOT NULl
Plan = "Test", // TODO: EF does not enforce this being NOT NULl
PrivateKey = "privatekey",
UsePolicies = false,
UseSso = false,
UseKeyConnector = false,
UseScim = false,
UseGroups = false,
UseDirectory = false,
UseEvents = false,
UseTotp = false,
Use2fa = false,
UseApi = false,
UseResetPassword = false,
UseSecretsManager = false,
SelfHost = false,
UsersGetPremium = false,
UseCustomPermissions = false,
Enabled = true,
UsePasswordManager = false,
LimitCollectionCreation = false,
LimitCollectionDeletion = false,
LimitItemDeletion = false,
AllowAdminAccessToAllCollectionItems = false,
UseRiskInsights = false,
UseAdminSponsoredFamilies = false
});
var organizationDomain = new OrganizationDomain
@ -335,6 +358,7 @@ public class OrganizationUserRepositoryTests
UserId = user1.Id,
Status = OrganizationUserStatusType.Confirmed,
ResetPasswordKey = "resetpasswordkey1",
AccessSecretsManager = false
});
await organizationUserRepository.CreateAsync(new OrganizationUser

View File

@ -0,0 +1,221 @@
CREATE OR ALTER VIEW [dbo].[OrganizationUserOrganizationDetailsView]
AS
SELECT
OU.[UserId],
OU.[OrganizationId],
OU.[Id] OrganizationUserId,
O.[Name],
O.[Enabled],
O.[PlanType],
O.[UsePolicies],
O.[UseSso],
O.[UseKeyConnector],
O.[UseScim],
O.[UseGroups],
O.[UseDirectory],
O.[UseEvents],
O.[UseTotp],
O.[Use2fa],
O.[UseApi],
O.[UseResetPassword],
O.[SelfHost],
O.[UsersGetPremium],
O.[UseCustomPermissions],
O.[UseSecretsManager],
O.[Seats],
O.[MaxCollections],
O.[MaxStorageGb],
O.[Identifier],
OU.[Key],
OU.[ResetPasswordKey],
O.[PublicKey],
O.[PrivateKey],
OU.[Status],
OU.[Type],
SU.[ExternalId] SsoExternalId,
OU.[Permissions],
PO.[ProviderId],
P.[Name] ProviderName,
P.[Type] ProviderType,
SS.[Data] SsoConfig,
OS.[FriendlyName] FamilySponsorshipFriendlyName,
OS.[LastSyncDate] FamilySponsorshipLastSyncDate,
OS.[ToDelete] FamilySponsorshipToDelete,
OS.[ValidUntil] FamilySponsorshipValidUntil,
OU.[AccessSecretsManager],
O.[UsePasswordManager],
O.[SmSeats],
O.[SmServiceAccounts],
O.[LimitCollectionCreation],
O.[LimitCollectionDeletion],
O.[AllowAdminAccessToAllCollectionItems],
O.[UseRiskInsights],
O.[UseAdminSponsoredFamilies],
O.[LimitItemDeletion],
OS.[IsAdminInitiated]
FROM
[dbo].[OrganizationUser] OU
LEFT JOIN
[dbo].[Organization] O ON O.[Id] = OU.[OrganizationId]
LEFT JOIN
[dbo].[SsoUser] SU ON SU.[UserId] = OU.[UserId] AND SU.[OrganizationId] = OU.[OrganizationId]
LEFT JOIN
[dbo].[ProviderOrganization] PO ON PO.[OrganizationId] = O.[Id]
LEFT JOIN
[dbo].[Provider] P ON P.[Id] = PO.[ProviderId]
LEFT JOIN
[dbo].[SsoConfig] SS ON SS.[OrganizationId] = OU.[OrganizationId]
LEFT JOIN
[dbo].[OrganizationSponsorship] OS ON OS.[SponsoringOrganizationUserID] = OU.[Id]
GO
--Manually refresh [dbo].[OrganizationUserOrganizationDetailsView]
IF OBJECT_ID('[dbo].[OrganizationUserOrganizationDetailsView]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationUserOrganizationDetailsView]';
END
GO
IF OBJECT_ID('[dbo].[OrganizationView]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationView]';
END
GO
IF OBJECT_ID('[dbo].[OrganizationSponsorshipView]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationSponsorshipView]';
END
GO
IF OBJECT_ID('[dbo].[OrganizationUserOrganizationDetailsView]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationUserOrganizationDetailsView]';
END
GO
IF OBJECT_ID('[dbo].[OrganizationSponsorship_OrganizationUserDeleted]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationSponsorship_OrganizationUserDeleted]';
END
GO
IF OBJECT_ID('[dbo].[OrganizationSponsorship_CreateMany]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationSponsorship_CreateMany]';
END
GO
IF OBJECT_ID('[dbo].[OrganizationSponsorship_OrganizationUsersDeleted]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationSponsorship_OrganizationUsersDeleted]';
END
GO
IF OBJECT_ID('[dbo].[OrganizationSponsorship_DeleteExpired]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationSponsorship_DeleteExpired]';
END
GO
IF OBJECT_ID('[dbo].[OrganizationSponsorship_Update]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationSponsorship_Update]';
END
GO
IF OBJECT_ID('[dbo].[OrganizationSponsorship_DeleteById]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationSponsorship_DeleteById]';
END
GO
IF OBJECT_ID('[dbo].[OrganizationSponsorship_Create]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationSponsorship_Create]';
END
GO
IF OBJECT_ID('[dbo].[OrganizationSponsorship_OrganizationDeleted]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationSponsorship_OrganizationDeleted]';
END
GO
IF OBJECT_ID('[dbo].[OrganizationSponsorship_UpdateMany]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationSponsorship_UpdateMany]';
END
GO
IF OBJECT_ID('[dbo].[OrganizationSponsorship_DeleteByIds]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationSponsorship_DeleteByIds]';
END
GO
IF OBJECT_ID('[dbo].[OrganizationSponsorship_ReadLatestBySponsoringOrganizationId]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationSponsorship_ReadLatestBySponsoringOrganizationId]';
END
GO
IF OBJECT_ID('[dbo].[OrganizationSponsorship_ReadByOfferedToEmail]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationSponsorship_ReadByOfferedToEmail]';
END
GO
IF OBJECT_ID('[dbo].[OrganizationSponsorship_ReadBySponsoredOrganizationId]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationSponsorship_ReadBySponsoredOrganizationId]';
END
GO
IF OBJECT_ID('[dbo].[OrganizationSponsorship_ReadBySponsoringOrganizationId]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationSponsorship_ReadBySponsoringOrganizationId]';
END
GO
IF OBJECT_ID('[dbo].[OrganizationSponsorship_ReadById]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationSponsorship_ReadById]';
END
GO
IF OBJECT_ID('[dbo].[OrganizationSponsorship_ReadBySponsoringOrganizationUserId]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationSponsorship_ReadBySponsoringOrganizationUserId]';
END
GO
IF OBJECT_ID('[dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatus]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatus]';
END
GO
IF OBJECT_ID('[dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId]';
END
GO
IF OBJECT_ID('[dbo].[OrganizationUser_DeleteById]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationUser_DeleteById]';
END
GO
IF OBJECT_ID('[dbo].[OrganizationUser_DeleteByIds]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationUser_DeleteByIds]';
END
GO
IF OBJECT_ID('[dbo].[Organization_DeleteById]') IS NOT NULL
BEGIN
EXECUTE sp_refreshsqlmodule N'[dbo].[Organization_DeleteById]';
END
GO

View File

@ -0,0 +1,20 @@
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[OrganizationSponsorship_ReadBySponsoringOrganizationUserId]
@SponsoringOrganizationUserId UNIQUEIDENTIFIER,
@IsAdminInitiated BIT = 0
AS
BEGIN
SET NOCOUNT ON;
SELECT
*
FROM
[dbo].[OrganizationSponsorshipView]
WHERE
[SponsoringOrganizationUserId] = @SponsoringOrganizationUserId
and [IsAdminInitiated] = @IsAdminInitiated
END
GO