mirror of
https://github.com/bitwarden/server.git
synced 2025-01-22 21:51:22 +01:00
api adjustments for manager role and collections
This commit is contained in:
parent
ca175e7dd8
commit
7db36e0005
@ -8,7 +8,7 @@ using Bit.Core.Models.Api;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Table;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
{
|
||||
@ -36,26 +36,40 @@ namespace Bit.Api.Controllers
|
||||
[HttpGet("{id}")]
|
||||
public async Task<CollectionResponseModel> Get(string orgId, string id)
|
||||
{
|
||||
var collection = await _collectionRepository.GetByIdAsync(new Guid(id));
|
||||
if(collection == null || !_currentContext.OrganizationAdmin(collection.OrganizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var collection = await GetCollectionAsync(new Guid(id), new Guid(orgId));
|
||||
return new CollectionResponseModel(collection);
|
||||
}
|
||||
|
||||
[HttpGet("{id}/details")]
|
||||
public async Task<CollectionGroupDetailsResponseModel> GetDetails(string orgId, string id)
|
||||
{
|
||||
var collectionDetails = await _collectionRepository.GetByIdWithGroupsAsync(new Guid(id));
|
||||
if(collectionDetails?.Item1 == null || !_currentContext.OrganizationAdmin(collectionDetails.Item1.OrganizationId))
|
||||
var orgIdGuid = new Guid(orgId);
|
||||
if(!_currentContext.OrganizationManager(orgIdGuid))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var idGuid = new Guid(id);
|
||||
if(_currentContext.OrganizationAdmin(orgIdGuid))
|
||||
{
|
||||
var collectionDetails = await _collectionRepository.GetByIdWithGroupsAsync(idGuid);
|
||||
if(collectionDetails?.Item1 == null || collectionDetails.Item1.OrganizationId != orgIdGuid)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
return new CollectionGroupDetailsResponseModel(collectionDetails.Item1, collectionDetails.Item2);
|
||||
}
|
||||
else
|
||||
{
|
||||
var collectionDetails = await _collectionRepository.GetByIdWithGroupsAsync(idGuid,
|
||||
_currentContext.UserId.Value);
|
||||
if(collectionDetails?.Item1 == null || collectionDetails.Item1.OrganizationId != orgIdGuid)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
return new CollectionGroupDetailsResponseModel(collectionDetails.Item1, collectionDetails.Item2);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<CollectionResponseModel>> Get(string orgId)
|
||||
@ -72,17 +86,10 @@ namespace Bit.Api.Controllers
|
||||
}
|
||||
|
||||
[HttpGet("~/collections")]
|
||||
public async Task<ListResponseModel<CollectionDetailsResponseModel>> GetUser([FromQuery]bool writeOnly = false)
|
||||
public async Task<ListResponseModel<CollectionDetailsResponseModel>> GetUser()
|
||||
{
|
||||
var collections = await _collectionRepository.GetManyByUserIdAsync(
|
||||
_userService.GetProperUserId(User).Value);
|
||||
|
||||
// TODO: Deprecated. writeOnly flag can be removed after v1.21.0
|
||||
if(writeOnly)
|
||||
{
|
||||
collections = collections.Where(c => !c.ReadOnly).ToList();
|
||||
}
|
||||
|
||||
var responses = collections.Select(c => new CollectionDetailsResponseModel(c));
|
||||
return new ListResponseModel<CollectionDetailsResponseModel>(responses);
|
||||
}
|
||||
@ -90,13 +97,7 @@ namespace Bit.Api.Controllers
|
||||
[HttpGet("{id}/users")]
|
||||
public async Task<ListResponseModel<CollectionUserResponseModel>> GetUsers(string orgId, string id)
|
||||
{
|
||||
var idGuid = new Guid(id);
|
||||
var collection = await _collectionRepository.GetByIdAsync(idGuid);
|
||||
if(collection == null || !_currentContext.OrganizationAdmin(collection.OrganizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var collection = await GetCollectionAsync(new Guid(id), new Guid(orgId));
|
||||
var collectionUsers = await _collectionRepository.GetManyUserDetailsByIdAsync(collection.OrganizationId,
|
||||
collection.Id);
|
||||
var responses = collectionUsers.Select(c => new CollectionUserResponseModel(c));
|
||||
@ -107,13 +108,14 @@ namespace Bit.Api.Controllers
|
||||
public async Task<CollectionResponseModel> Post(string orgId, [FromBody]CollectionRequestModel model)
|
||||
{
|
||||
var orgIdGuid = new Guid(orgId);
|
||||
if(!_currentContext.OrganizationAdmin(orgIdGuid))
|
||||
if(!_currentContext.OrganizationManager(orgIdGuid))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var collection = model.ToCollection(orgIdGuid);
|
||||
await _collectionService.SaveAsync(collection, model.Groups?.Select(g => g.ToSelectionReadOnly()));
|
||||
await _collectionService.SaveAsync(collection, model.Groups?.Select(g => g.ToSelectionReadOnly()),
|
||||
!_currentContext.OrganizationAdmin(orgIdGuid) ? _currentContext.UserId : null);
|
||||
return new CollectionResponseModel(collection);
|
||||
}
|
||||
|
||||
@ -121,12 +123,7 @@ namespace Bit.Api.Controllers
|
||||
[HttpPost("{id}")]
|
||||
public async Task<CollectionResponseModel> Put(string orgId, string id, [FromBody]CollectionRequestModel model)
|
||||
{
|
||||
var collection = await _collectionRepository.GetByIdAsync(new Guid(id));
|
||||
if(collection == null || !_currentContext.OrganizationAdmin(collection.OrganizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var collection = await GetCollectionAsync(new Guid(id), new Guid(orgId));
|
||||
await _collectionService.SaveAsync(model.ToCollection(collection),
|
||||
model.Groups?.Select(g => g.ToSelectionReadOnly()));
|
||||
return new CollectionResponseModel(collection);
|
||||
@ -136,12 +133,7 @@ namespace Bit.Api.Controllers
|
||||
[HttpPost("{id}/delete")]
|
||||
public async Task Delete(string orgId, string id)
|
||||
{
|
||||
var collection = await _collectionRepository.GetByIdAsync(new Guid(id));
|
||||
if(collection == null || !_currentContext.OrganizationAdmin(collection.OrganizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var collection = await GetCollectionAsync(new Guid(id), new Guid(orgId));
|
||||
await _collectionService.DeleteAsync(collection);
|
||||
}
|
||||
|
||||
@ -149,13 +141,26 @@ namespace Bit.Api.Controllers
|
||||
[HttpPost("{id}/delete-user/{orgUserId}")]
|
||||
public async Task Delete(string orgId, string id, string orgUserId)
|
||||
{
|
||||
var collection = await _collectionRepository.GetByIdAsync(new Guid(id));
|
||||
if(collection == null || !_currentContext.OrganizationAdmin(collection.OrganizationId))
|
||||
var collection = await GetCollectionAsync(new Guid(id), new Guid(orgId));
|
||||
await _collectionService.DeleteUserAsync(collection, new Guid(orgUserId));
|
||||
}
|
||||
|
||||
private async Task<Collection> GetCollectionAsync(Guid id, Guid orgId)
|
||||
{
|
||||
if(!_currentContext.OrganizationManager(orgId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await _collectionService.DeleteUserAsync(collection, new Guid(orgUserId));
|
||||
var collection = _currentContext.OrganizationAdmin(orgId) ?
|
||||
await _collectionRepository.GetByIdAsync(id) :
|
||||
await _collectionRepository.GetByIdAsync(id, _currentContext.UserId.Value);
|
||||
if(collection == null || collection.OrganizationId != orgId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ namespace Bit.Api.Controllers
|
||||
public async Task<ListResponseModel<GroupResponseModel>> Get(string orgId)
|
||||
{
|
||||
var orgIdGuid = new Guid(orgId);
|
||||
if(!_currentContext.OrganizationAdmin(orgIdGuid))
|
||||
if(!_currentContext.OrganizationManager(orgIdGuid))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ namespace Bit.Api.Controllers
|
||||
public async Task<ListResponseModel<OrganizationUserUserDetailsResponseModel>> Get(string orgId)
|
||||
{
|
||||
var orgGuidId = new Guid(orgId);
|
||||
if(!_currentContext.OrganizationAdmin(orgGuidId))
|
||||
if(!_currentContext.OrganizationManager(orgGuidId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
@ -111,6 +111,16 @@ namespace Bit.Core
|
||||
Type = OrganizationUserType.User
|
||||
}));
|
||||
}
|
||||
|
||||
if(claimsDict.ContainsKey("orgmanager"))
|
||||
{
|
||||
Organizations.AddRange(claimsDict["orgmanager"].Select(c =>
|
||||
new CurrentContentOrganization
|
||||
{
|
||||
Id = new Guid(c.Value),
|
||||
Type = OrganizationUserType.Manager
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
public bool OrganizationUser(Guid orgId)
|
||||
@ -118,6 +128,13 @@ namespace Bit.Core
|
||||
return Organizations?.Any(o => o.Id == orgId) ?? false;
|
||||
}
|
||||
|
||||
public bool OrganizationManager(Guid orgId)
|
||||
{
|
||||
return Organizations?.Any(o => o.Id == orgId &&
|
||||
(o.Type == OrganizationUserType.Owner || o.Type == OrganizationUserType.Admin ||
|
||||
o.Type == OrganizationUserType.Manager)) ?? false;
|
||||
}
|
||||
|
||||
public bool OrganizationAdmin(Guid orgId)
|
||||
{
|
||||
return Organizations?.Any(o => o.Id == orgId &&
|
||||
|
@ -4,6 +4,7 @@
|
||||
{
|
||||
Owner = 0,
|
||||
Admin = 1,
|
||||
User = 2
|
||||
User = 2,
|
||||
Manager = 3,
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ namespace Bit.Core.IdentityServer
|
||||
"device",
|
||||
"orgowner",
|
||||
"orgadmin",
|
||||
"orgmanager",
|
||||
"orguser"
|
||||
}),
|
||||
new ApiResource("internal", new string[] { JwtClaimTypes.Subject }),
|
||||
|
@ -75,6 +75,12 @@ namespace Bit.Core.IdentityServer
|
||||
newClaims.Add(new Claim("orgadmin", org.Id.ToString()));
|
||||
}
|
||||
break;
|
||||
case Enums.OrganizationUserType.Manager:
|
||||
foreach(var org in group)
|
||||
{
|
||||
newClaims.Add(new Claim("orgmanager", org.Id.ToString()));
|
||||
}
|
||||
break;
|
||||
case Enums.OrganizationUserType.User:
|
||||
foreach(var org in group)
|
||||
{
|
||||
|
@ -10,11 +10,14 @@ namespace Bit.Core.Repositories
|
||||
{
|
||||
Task<int> GetCountByOrganizationIdAsync(Guid organizationId);
|
||||
Task<Tuple<Collection, ICollection<SelectionReadOnly>>> GetByIdWithGroupsAsync(Guid id);
|
||||
Task<Tuple<CollectionDetails, ICollection<SelectionReadOnly>>> GetByIdWithGroupsAsync(Guid id, Guid userId);
|
||||
Task<ICollection<Collection>> GetManyByOrganizationIdAsync(Guid organizationId);
|
||||
Task<CollectionDetails> GetByIdAsync(Guid id, Guid userId);
|
||||
Task<ICollection<CollectionDetails>> GetManyByUserIdAsync(Guid userId);
|
||||
Task<ICollection<CollectionUserDetails>> GetManyUserDetailsByIdAsync(Guid organizationId, Guid collectionId);
|
||||
Task CreateAsync(Collection obj, IEnumerable<SelectionReadOnly> groups);
|
||||
Task ReplaceAsync(Collection obj, IEnumerable<SelectionReadOnly> groups);
|
||||
Task DeleteUserAsync(Guid collectionId, Guid organizationUserId);
|
||||
Task UpdateUsersAsync(Guid id, IEnumerable<SelectionReadOnly> users);
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,23 @@ namespace Bit.Core.Repositories.SqlServer
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Tuple<CollectionDetails, ICollection<SelectionReadOnly>>> GetByIdWithGroupsAsync(
|
||||
Guid id, Guid userId)
|
||||
{
|
||||
using(var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.QueryMultipleAsync(
|
||||
$"[{Schema}].[Collection_ReadWithGroupsByIdUserId]",
|
||||
new { Id = id, UserId = userId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
var collection = await results.ReadFirstOrDefaultAsync<CollectionDetails>();
|
||||
var groups = (await results.ReadAsync<SelectionReadOnly>()).ToList();
|
||||
|
||||
return new Tuple<CollectionDetails, ICollection<SelectionReadOnly>>(collection, groups);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ICollection<Collection>> GetManyByOrganizationIdAsync(Guid organizationId)
|
||||
{
|
||||
using(var connection = new SqlConnection(ConnectionString))
|
||||
@ -64,6 +81,19 @@ namespace Bit.Core.Repositories.SqlServer
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<CollectionDetails> GetByIdAsync(Guid id, Guid userId)
|
||||
{
|
||||
using(var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.QueryAsync<CollectionDetails>(
|
||||
$"[{Schema}].[Collection_ReadByIdUserId]",
|
||||
new { Id = id, UserId = userId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
return results.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ICollection<CollectionDetails>> GetManyByUserIdAsync(Guid userId)
|
||||
{
|
||||
using(var connection = new SqlConnection(ConnectionString))
|
||||
@ -108,7 +138,7 @@ namespace Bit.Core.Repositories.SqlServer
|
||||
using(var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.ExecuteAsync(
|
||||
$"[{Schema}].[Collection_CreateWithGroups]",
|
||||
$"[{Schema}].[Collection_CreateWithGroupsAndUsers]",
|
||||
objWithGroups,
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
@ -128,6 +158,17 @@ namespace Bit.Core.Repositories.SqlServer
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CreateUserAsync(Guid collectionId, Guid organizationUserId)
|
||||
{
|
||||
using(var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.ExecuteAsync(
|
||||
$"[{Schema}].[CollectionUser_Create]",
|
||||
new { CollectionId = collectionId, OrganizationUserId = organizationUserId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteUserAsync(Guid collectionId, Guid organizationUserId)
|
||||
{
|
||||
using(var connection = new SqlConnection(ConnectionString))
|
||||
@ -139,6 +180,17 @@ namespace Bit.Core.Repositories.SqlServer
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateUsersAsync(Guid id, IEnumerable<SelectionReadOnly> users)
|
||||
{
|
||||
using(var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.ExecuteAsync(
|
||||
$"[{Schema}].[Collection_UpdateUsers]",
|
||||
new { Id = id, Users = users.ToArrayTVP() },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
}
|
||||
|
||||
public class CollectionWithGroups : Collection
|
||||
{
|
||||
public DataTable Groups { get; set; }
|
||||
|
@ -8,7 +8,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
public interface ICollectionService
|
||||
{
|
||||
Task SaveAsync(Collection collection, IEnumerable<SelectionReadOnly> groups = null);
|
||||
Task SaveAsync(Collection collection, IEnumerable<SelectionReadOnly> groups = null, Guid? assignUserId = null);
|
||||
Task DeleteAsync(Collection collection);
|
||||
Task DeleteUserAsync(Collection collection, Guid organizationUserId);
|
||||
}
|
||||
|
@ -33,7 +33,8 @@ namespace Bit.Core.Services
|
||||
_mailService = mailService;
|
||||
}
|
||||
|
||||
public async Task SaveAsync(Collection collection, IEnumerable<SelectionReadOnly> groups = null)
|
||||
public async Task SaveAsync(Collection collection, IEnumerable<SelectionReadOnly> groups = null,
|
||||
Guid? assignUserId = null)
|
||||
{
|
||||
var org = await _organizationRepository.GetByIdAsync(collection.OrganizationId);
|
||||
if(org == null)
|
||||
@ -62,6 +63,18 @@ namespace Bit.Core.Services
|
||||
await _collectionRepository.CreateAsync(collection, groups);
|
||||
}
|
||||
|
||||
// Assign a user to the newly created collection.
|
||||
if(assignUserId.HasValue)
|
||||
{
|
||||
var orgUser = await _organizationUserRepository.GetByOrganizationAsync(org.Id, assignUserId.Value);
|
||||
if(orgUser != null && orgUser.Status == Enums.OrganizationUserStatusType.Confirmed)
|
||||
{
|
||||
await _collectionRepository.UpdateUsersAsync(collection.Id,
|
||||
new List<SelectionReadOnly> {
|
||||
new SelectionReadOnly { Id = orgUser.Id, ReadOnly = false } });
|
||||
}
|
||||
}
|
||||
|
||||
await _eventService.LogCollectionEventAsync(collection, Enums.EventType.Collection_Created);
|
||||
}
|
||||
else
|
||||
|
@ -153,7 +153,6 @@
|
||||
<Build Include="dbo\Stored Procedures\Device_ReadByUserId.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Device_Update.sql" />
|
||||
<Build Include="dbo\Stored Procedures\User_Create.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Collection_CreateWithGroups.sql" />
|
||||
<Build Include="dbo\Stored Procedures\User_DeleteById.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Collection_ReadWithGroupsById.sql" />
|
||||
<Build Include="dbo\Stored Procedures\User_ReadByEmail.sql" />
|
||||
@ -233,5 +232,10 @@
|
||||
<Build Include="dbo\Stored Procedures\User_ReadKdfByEmail.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Cipher_DeleteByOrganizationId.sql" />
|
||||
<Build Include="dbo\Stored Procedures\AzureSQLMaintenance.sql" />
|
||||
<Build Include="dbo\Functions\UserCollectionDetails.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Collection_ReadByIdUserId.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Collection_ReadWithGroupsByIdUserId.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Collection_CreateWithGroups.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Collection_UpdateUsers.sql" />
|
||||
</ItemGroup>
|
||||
</Project>
|
38
src/Sql/dbo/Functions/UserCollectionDetails.sql
Normal file
38
src/Sql/dbo/Functions/UserCollectionDetails.sql
Normal file
@ -0,0 +1,38 @@
|
||||
CREATE FUNCTION [dbo].[UserCollectionDetails](@UserId UNIQUEIDENTIFIER)
|
||||
RETURNS TABLE
|
||||
AS RETURN
|
||||
SELECT
|
||||
C.*,
|
||||
CASE
|
||||
WHEN
|
||||
OU.[AccessAll] = 1
|
||||
OR G.[AccessAll] = 1
|
||||
OR CU.[ReadOnly] = 0
|
||||
OR CG.[ReadOnly] = 0
|
||||
THEN 0
|
||||
ELSE 1
|
||||
END [ReadOnly]
|
||||
FROM
|
||||
[dbo].[CollectionView] C
|
||||
INNER JOIN
|
||||
[dbo].[OrganizationUser] OU ON C.[OrganizationId] = OU.[OrganizationId]
|
||||
INNER JOIN
|
||||
[dbo].[Organization] O ON O.[Id] = C.[OrganizationId]
|
||||
LEFT JOIN
|
||||
[dbo].[CollectionUser] CU ON OU.[AccessAll] = 0 AND CU.[CollectionId] = C.[Id] AND CU.[OrganizationUserId] = [OU].[Id]
|
||||
LEFT JOIN
|
||||
[dbo].[GroupUser] GU ON CU.[CollectionId] IS NULL AND OU.[AccessAll] = 0 AND GU.[OrganizationUserId] = OU.[Id]
|
||||
LEFT JOIN
|
||||
[dbo].[Group] G ON G.[Id] = GU.[GroupId]
|
||||
LEFT JOIN
|
||||
[dbo].[CollectionGroup] CG ON G.[AccessAll] = 0 AND CG.[CollectionId] = C.[Id] AND CG.[GroupId] = GU.[GroupId]
|
||||
WHERE
|
||||
OU.[UserId] = @UserId
|
||||
AND OU.[Status] = 2 -- 2 = Confirmed
|
||||
AND O.[Enabled] = 1
|
||||
AND (
|
||||
OU.[AccessAll] = 1
|
||||
OR CU.[CollectionId] IS NOT NULL
|
||||
OR G.[AccessAll] = 1
|
||||
OR CG.[CollectionId] IS NOT NULL
|
||||
)
|
15
src/Sql/dbo/Stored Procedures/Collection_ReadByIdUserId.sql
Normal file
15
src/Sql/dbo/Stored Procedures/Collection_ReadByIdUserId.sql
Normal file
@ -0,0 +1,15 @@
|
||||
CREATE PROCEDURE [dbo].[Collection_ReadByIdUserId]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@UserId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
SELECT TOP 1
|
||||
*
|
||||
FROM
|
||||
[dbo].[UserCollectionDetails](@UserId)
|
||||
WHERE
|
||||
[Id] = @Id
|
||||
ORDER BY
|
||||
[ReadOnly] ASC
|
||||
END
|
@ -5,38 +5,7 @@ BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
C.*,
|
||||
CASE
|
||||
WHEN
|
||||
OU.[AccessAll] = 1
|
||||
OR G.[AccessAll] = 1
|
||||
OR CU.[ReadOnly] = 0
|
||||
OR CG.[ReadOnly] = 0
|
||||
THEN 0
|
||||
ELSE 1
|
||||
END [ReadOnly]
|
||||
*
|
||||
FROM
|
||||
[dbo].[CollectionView] C
|
||||
INNER JOIN
|
||||
[dbo].[OrganizationUser] OU ON C.[OrganizationId] = OU.[OrganizationId]
|
||||
INNER JOIN
|
||||
[dbo].[Organization] O ON O.[Id] = C.[OrganizationId]
|
||||
LEFT JOIN
|
||||
[dbo].[CollectionUser] CU ON OU.[AccessAll] = 0 AND CU.[CollectionId] = C.[Id] AND CU.[OrganizationUserId] = [OU].[Id]
|
||||
LEFT JOIN
|
||||
[dbo].[GroupUser] GU ON CU.[CollectionId] IS NULL AND OU.[AccessAll] = 0 AND GU.[OrganizationUserId] = OU.[Id]
|
||||
LEFT JOIN
|
||||
[dbo].[Group] G ON G.[Id] = GU.[GroupId]
|
||||
LEFT JOIN
|
||||
[dbo].[CollectionGroup] CG ON G.[AccessAll] = 0 AND CG.[CollectionId] = C.[Id] AND CG.[GroupId] = GU.[GroupId]
|
||||
WHERE
|
||||
OU.[UserId] = @UserId
|
||||
AND OU.[Status] = 2 -- 2 = Confirmed
|
||||
AND O.[Enabled] = 1
|
||||
AND (
|
||||
OU.[AccessAll] = 1
|
||||
OR CU.[CollectionId] IS NOT NULL
|
||||
OR G.[AccessAll] = 1
|
||||
OR CG.[CollectionId] IS NOT NULL
|
||||
)
|
||||
[dbo].[UserCollectionDetails](@UserId)
|
||||
END
|
@ -0,0 +1,17 @@
|
||||
CREATE PROCEDURE [dbo].[Collection_ReadWithGroupsByIdUserId]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@UserId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
EXEC [dbo].[Collection_ReadByIdUserId] @Id, @UserId
|
||||
|
||||
SELECT
|
||||
[GroupId] [Id],
|
||||
[ReadOnly]
|
||||
FROM
|
||||
[dbo].[CollectionGroup]
|
||||
WHERE
|
||||
[CollectionId] = @Id
|
||||
END
|
41
src/Sql/dbo/Stored Procedures/Collection_UpdateUsers.sql
Normal file
41
src/Sql/dbo/Stored Procedures/Collection_UpdateUsers.sql
Normal file
@ -0,0 +1,41 @@
|
||||
CREATE PROCEDURE [dbo].[Collection_UpdateUsers]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@Users AS [dbo].[SelectionReadOnlyArray] READONLY
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
DECLARE @OrganizationId UNIQUEIDENTIFIER = (SELECT [OrganizationId] FROM [dbo].[Collection] WHERE [Id] = @Id)
|
||||
|
||||
;WITH [AvailableUsersCTE] AS(
|
||||
SELECT
|
||||
Id
|
||||
FROM
|
||||
[dbo].[OrganizationUser]
|
||||
WHERE
|
||||
OrganizationId = @OrganizationId
|
||||
)
|
||||
MERGE
|
||||
[dbo].[CollectionUser] AS [Target]
|
||||
USING
|
||||
@Users AS [Source]
|
||||
ON
|
||||
[Target].[CollectionId] = @Id
|
||||
AND [Target].[OrganizationUserId] = [Source].[Id]
|
||||
WHEN NOT MATCHED BY TARGET
|
||||
AND [Source].[Id] IN (SELECT [Id] FROM [AvailableUsersCTE]) THEN
|
||||
INSERT VALUES
|
||||
(
|
||||
@Id,
|
||||
[Source].[Id],
|
||||
[Source].[ReadOnly]
|
||||
)
|
||||
WHEN MATCHED AND [Target].[ReadOnly] != [Source].[ReadOnly] THEN
|
||||
UPDATE SET [Target].[ReadOnly] = [Source].[ReadOnly]
|
||||
WHEN NOT MATCHED BY SOURCE
|
||||
AND [Target].[CollectionId] = @Id THEN
|
||||
DELETE
|
||||
;
|
||||
|
||||
EXEC [dbo].[User_BumpAccountRevisionDateByCollectionId] @Id, @OrganizationId
|
||||
END
|
161
util/Setup/DbScripts/2018-10-17_00_ManagerRole.sql
Normal file
161
util/Setup/DbScripts/2018-10-17_00_ManagerRole.sql
Normal file
@ -0,0 +1,161 @@
|
||||
IF OBJECT_ID('[dbo].[UserCollectionDetails]') IS NOT NULL
|
||||
BEGIN
|
||||
DROP FUNCTION [dbo].[UserCollectionDetails]
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE FUNCTION [dbo].[UserCollectionDetails](@UserId UNIQUEIDENTIFIER)
|
||||
RETURNS TABLE
|
||||
AS RETURN
|
||||
SELECT
|
||||
C.*,
|
||||
CASE
|
||||
WHEN
|
||||
OU.[AccessAll] = 1
|
||||
OR G.[AccessAll] = 1
|
||||
OR CU.[ReadOnly] = 0
|
||||
OR CG.[ReadOnly] = 0
|
||||
THEN 0
|
||||
ELSE 1
|
||||
END [ReadOnly]
|
||||
FROM
|
||||
[dbo].[CollectionView] C
|
||||
INNER JOIN
|
||||
[dbo].[OrganizationUser] OU ON C.[OrganizationId] = OU.[OrganizationId]
|
||||
INNER JOIN
|
||||
[dbo].[Organization] O ON O.[Id] = C.[OrganizationId]
|
||||
LEFT JOIN
|
||||
[dbo].[CollectionUser] CU ON OU.[AccessAll] = 0 AND CU.[CollectionId] = C.[Id] AND CU.[OrganizationUserId] = [OU].[Id]
|
||||
LEFT JOIN
|
||||
[dbo].[GroupUser] GU ON CU.[CollectionId] IS NULL AND OU.[AccessAll] = 0 AND GU.[OrganizationUserId] = OU.[Id]
|
||||
LEFT JOIN
|
||||
[dbo].[Group] G ON G.[Id] = GU.[GroupId]
|
||||
LEFT JOIN
|
||||
[dbo].[CollectionGroup] CG ON G.[AccessAll] = 0 AND CG.[CollectionId] = C.[Id] AND CG.[GroupId] = GU.[GroupId]
|
||||
WHERE
|
||||
OU.[UserId] = @UserId
|
||||
AND OU.[Status] = 2 -- 2 = Confirmed
|
||||
AND O.[Enabled] = 1
|
||||
AND (
|
||||
OU.[AccessAll] = 1
|
||||
OR CU.[CollectionId] IS NOT NULL
|
||||
OR G.[AccessAll] = 1
|
||||
OR CG.[CollectionId] IS NOT NULL
|
||||
)
|
||||
GO
|
||||
|
||||
IF OBJECT_ID('[dbo].[Collection_ReadByIdUserId]') IS NOT NULL
|
||||
BEGIN
|
||||
DROP PROCEDURE [dbo].[Collection_ReadByIdUserId]
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE PROCEDURE [dbo].[Collection_ReadByIdUserId]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@UserId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
SELECT TOP 1
|
||||
*
|
||||
FROM
|
||||
[dbo].[UserCollectionDetails](@UserId)
|
||||
WHERE
|
||||
[Id] = @Id
|
||||
ORDER BY
|
||||
[ReadOnly] ASC
|
||||
END
|
||||
GO
|
||||
|
||||
IF OBJECT_ID('[dbo].[Collection_ReadByUserId]') IS NOT NULL
|
||||
BEGIN
|
||||
DROP PROCEDURE [dbo].[Collection_ReadByUserId]
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE PROCEDURE [dbo].[Collection_ReadByUserId]
|
||||
@UserId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
[dbo].[UserCollectionDetails](@UserId)
|
||||
END
|
||||
GO
|
||||
|
||||
IF OBJECT_ID('[dbo].[Collection_ReadWithGroupsByIdUserId]') IS NOT NULL
|
||||
BEGIN
|
||||
DROP PROCEDURE [dbo].[Collection_ReadWithGroupsByIdUserId]
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE PROCEDURE [dbo].[Collection_ReadWithGroupsByIdUserId]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@UserId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
EXEC [dbo].[Collection_ReadByIdUserId] @Id, @UserId
|
||||
|
||||
SELECT
|
||||
[GroupId] [Id],
|
||||
[ReadOnly]
|
||||
FROM
|
||||
[dbo].[CollectionGroup]
|
||||
WHERE
|
||||
[CollectionId] = @Id
|
||||
END
|
||||
GO
|
||||
|
||||
IF OBJECT_ID('[dbo].[Collection_UpdateUsers]') IS NOT NULL
|
||||
BEGIN
|
||||
DROP PROCEDURE [dbo].[Collection_UpdateUsers]
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE PROCEDURE [dbo].[Collection_UpdateUsers]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@Users AS [dbo].[SelectionReadOnlyArray] READONLY
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
DECLARE @OrganizationId UNIQUEIDENTIFIER = (SELECT [OrganizationId] FROM [dbo].[Collection] WHERE [Id] = @Id)
|
||||
|
||||
;WITH [AvailableUsersCTE] AS(
|
||||
SELECT
|
||||
Id
|
||||
FROM
|
||||
[dbo].[OrganizationUser]
|
||||
WHERE
|
||||
OrganizationId = @OrganizationId
|
||||
)
|
||||
MERGE
|
||||
[dbo].[CollectionUser] AS [Target]
|
||||
USING
|
||||
@Users AS [Source]
|
||||
ON
|
||||
[Target].[CollectionId] = @Id
|
||||
AND [Target].[OrganizationUserId] = [Source].[Id]
|
||||
WHEN NOT MATCHED BY TARGET
|
||||
AND [Source].[Id] IN (SELECT [Id] FROM [AvailableUsersCTE]) THEN
|
||||
INSERT VALUES
|
||||
(
|
||||
@Id,
|
||||
[Source].[Id],
|
||||
[Source].[ReadOnly]
|
||||
)
|
||||
WHEN MATCHED AND [Target].[ReadOnly] != [Source].[ReadOnly] THEN
|
||||
UPDATE SET [Target].[ReadOnly] = [Source].[ReadOnly]
|
||||
WHEN NOT MATCHED BY SOURCE
|
||||
AND [Target].[CollectionId] = @Id THEN
|
||||
DELETE
|
||||
;
|
||||
|
||||
EXEC [dbo].[User_BumpAccountRevisionDateByCollectionId] @Id, @OrganizationId
|
||||
END
|
||||
GO
|
@ -14,6 +14,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="DbScripts\2018-09-25_00_OrgPurge.sql" />
|
||||
<None Remove="DbScripts\2018-10-17_00_ManagerRole.sql" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
Loading…
Reference in New Issue
Block a user