1
0
mirror of https://github.com/bitwarden/server.git synced 2025-02-22 02:51:33 +01:00

refactor policy apis

This commit is contained in:
Kyle Spearrin 2020-01-20 08:53:09 -05:00
parent c5ae1b8283
commit f3f1ac57d2
11 changed files with 112 additions and 120 deletions

View File

@ -8,6 +8,7 @@ using Bit.Core.Models.Api;
using Bit.Core.Exceptions;
using Bit.Core.Services;
using Bit.Core;
using Bit.Core.Enums;
namespace Bit.Api.Controllers
{
@ -29,11 +30,16 @@ namespace Bit.Api.Controllers
_currentContext = currentContext;
}
[HttpGet("{id}")]
public async Task<PolicyResponseModel> Get(string orgId, string id)
[HttpGet("{type}")]
public async Task<PolicyResponseModel> Get(string orgId, int type)
{
var policy = await _policyRepository.GetByIdAsync(new Guid(id));
if(policy == null || !_currentContext.OrganizationAdmin(policy.OrganizationId))
var orgIdGuid = new Guid(orgId);
if(!_currentContext.OrganizationAdmin(orgIdGuid))
{
throw new NotFoundException();
}
var policy = await _policyRepository.GetByOrganizationIdTypeAsync(orgIdGuid, (PolicyType)type);
if(policy == null)
{
throw new NotFoundException();
}
@ -55,45 +61,26 @@ namespace Bit.Api.Controllers
return new ListResponseModel<PolicyResponseModel>(responses);
}
[HttpPost("")]
public async Task<PolicyResponseModel> Post(string orgId, [FromBody]PolicyRequestModel model)
[HttpPut("{type}")]
public async Task<PolicyResponseModel> Put(string orgId, int type, [FromBody]PolicyRequestModel model)
{
var orgIdGuid = new Guid(orgId);
if(!_currentContext.OrganizationAdmin(orgIdGuid))
{
throw new NotFoundException();
}
var policy = await _policyRepository.GetByOrganizationIdTypeAsync(new Guid(orgId), (PolicyType)type);
if(policy == null)
{
policy = model.ToPolicy(orgIdGuid);
}
else
{
policy = model.ToPolicy(policy);
}
var policy = model.ToPolicy(orgIdGuid);
await _policyService.SaveAsync(policy);
return new PolicyResponseModel(policy);
}
[HttpPut("{id}")]
[HttpPost("{id}")]
public async Task<PolicyResponseModel> Put(string orgId, string id, [FromBody]PolicyRequestModel model)
{
var policy = await _policyRepository.GetByIdAsync(new Guid(id));
if(policy == null || !_currentContext.OrganizationAdmin(policy.OrganizationId))
{
throw new NotFoundException();
}
await _policyService.SaveAsync(model.ToPolicy(policy));
return new PolicyResponseModel(policy);
}
[HttpDelete("{id}")]
[HttpPost("{id}/delete")]
public async Task Delete(string orgId, string id)
{
var policy = await _policyRepository.GetByIdAsync(new Guid(id));
if(policy == null || !_currentContext.OrganizationAdmin(policy.OrganizationId))
{
throw new NotFoundException();
}
await _policyService.DeleteAsync(policy);
}
}
}

View File

@ -3,6 +3,7 @@ using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Bit.Core;
using Bit.Core.Enums;
using Bit.Core.Models.Api.Public;
using Bit.Core.Repositories;
using Bit.Core.Services;
@ -33,17 +34,17 @@ namespace Bit.Api.Public.Controllers
/// Retrieve a policy.
/// </summary>
/// <remarks>
/// Retrieves the details of an existing policy. You need only supply the unique group identifier
/// that was returned upon policy creation.
/// Retrieves the details of a policy.
/// </remarks>
/// <param name="id">The identifier of the policy to be retrieved.</param>
[HttpGet("{id}")]
/// <param name="type">The type of policy to be retrieved.</param>
[HttpGet("{type}")]
[ProducesResponseType(typeof(GroupResponseModel), (int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
public async Task<IActionResult> Get(Guid id)
public async Task<IActionResult> Get(PolicyType type)
{
var policy = await _policyRepository.GetByIdAsync(id);
if(policy == null || policy.OrganizationId != _currentContext.OrganizationId)
var policy = await _policyRepository.GetByOrganizationIdTypeAsync(
_currentContext.OrganizationId.Value, type);
if(policy == null)
{
return new NotFoundResult();
}
@ -67,69 +68,34 @@ namespace Bit.Api.Public.Controllers
return new JsonResult(response);
}
/// <summary>
/// Create a policy.
/// </summary>
/// <remarks>
/// Creates a new policy object.
/// </remarks>
/// <param name="model">The request model.</param>
[HttpPost]
[ProducesResponseType(typeof(PolicyResponseModel), (int)HttpStatusCode.OK)]
[ProducesResponseType(typeof(ErrorResponseModel), (int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> Post([FromBody]PolicyCreateRequestModel model)
{
var policy = model.ToPolicy(_currentContext.OrganizationId.Value);
await _policyService.SaveAsync(policy);
var response = new PolicyResponseModel(policy);
return new JsonResult(response);
}
/// <summary>
/// Update a policy.
/// </summary>
/// <remarks>
/// Updates the specified policy object. If a property is not provided,
/// Updates the specified policy. If a property is not provided,
/// the value of the existing property will be reset.
/// </remarks>
/// <param name="id">The identifier of the policy to be updated.</param>
/// <param name="type">The type of policy to be updated.</param>
/// <param name="model">The request model.</param>
[HttpPut("{id}")]
[ProducesResponseType(typeof(PolicyResponseModel), (int)HttpStatusCode.OK)]
[ProducesResponseType(typeof(ErrorResponseModel), (int)HttpStatusCode.BadRequest)]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
public async Task<IActionResult> Put(Guid id, [FromBody]PolicyUpdateRequestModel model)
public async Task<IActionResult> Put(PolicyType type, [FromBody]PolicyUpdateRequestModel model)
{
var existingPolicy = await _policyRepository.GetByIdAsync(id);
if(existingPolicy == null || existingPolicy.OrganizationId != _currentContext.OrganizationId)
var policy = await _policyRepository.GetByOrganizationIdTypeAsync(
_currentContext.OrganizationId.Value, type);
if(policy == null)
{
return new NotFoundResult();
policy = model.ToPolicy(_currentContext.OrganizationId.Value);
}
var updatedPolicy = model.ToPolicy(existingPolicy);
await _policyService.SaveAsync(updatedPolicy);
var response = new PolicyResponseModel(updatedPolicy);
else
{
policy = model.ToPolicy(policy);
}
await _policyService.SaveAsync(policy);
var response = new PolicyResponseModel(policy);
return new JsonResult(response);
}
/// <summary>
/// Delete a policy.
/// </summary>
/// <remarks>
/// Permanently deletes a policy. This cannot be undone.
/// </remarks>
/// <param name="id">The identifier of the policy to be deleted.</param>
[HttpDelete("{id}")]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
public async Task<IActionResult> Delete(Guid id)
{
var policy = await _policyRepository.GetByIdAsync(id);
if(policy == null || policy.OrganizationId != _currentContext.OrganizationId)
{
return new NotFoundResult();
}
await _policyRepository.DeleteAsync(policy);
return new OkResult();
}
}
}

View File

@ -13,7 +13,6 @@ namespace Bit.Core.Models.Api.Public
/// <summary>
/// Data for the policy.
/// </summary>
[StringLength(300)]
public Dictionary<string, object> Data { get; set; }
}
}

View File

@ -1,23 +0,0 @@
using System;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Models.Table;
namespace Bit.Core.Models.Api.Public
{
public class PolicyCreateRequestModel : PolicyUpdateRequestModel
{
/// <summary>
/// The type of policy.
/// </summary>
[Required]
public Enums.PolicyType? Type { get; set; }
public Policy ToPolicy(Guid orgId)
{
return ToPolicy(new Policy
{
OrganizationId = orgId
});
}
}
}

View File

@ -1,10 +1,19 @@
using Bit.Core.Models.Table;
using System;
using Bit.Core.Models.Table;
using Newtonsoft.Json;
namespace Bit.Core.Models.Api.Public
{
public class PolicyUpdateRequestModel : PolicyBaseModel
{
public Policy ToPolicy(Guid orgId)
{
return ToPolicy(new Policy
{
OrganizationId = orgId
});
}
public virtual Policy ToPolicy(Policy existingPolicy)
{
existingPolicy.Enabled = Enabled.GetValueOrDefault();

View File

@ -2,11 +2,13 @@
using Bit.Core.Models.Table;
using System.Collections.Generic;
using System.Threading.Tasks;
using Bit.Core.Enums;
namespace Bit.Core.Repositories
{
public interface IPolicyRepository : IRepository<Policy, Guid>
{
Task<Policy> GetByOrganizationIdTypeAsync(Guid organizationId, PolicyType type);
Task<ICollection<Policy>> GetManyByOrganizationIdAsync(Guid organizationId);
}
}

View File

@ -6,6 +6,7 @@ using System.Data.SqlClient;
using System.Data;
using Dapper;
using System.Linq;
using Bit.Core.Enums;
namespace Bit.Core.Repositories.SqlServer
{
@ -18,6 +19,18 @@ namespace Bit.Core.Repositories.SqlServer
public PolicyRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task<Policy> GetByOrganizationIdTypeAsync(Guid organizationId, PolicyType type)
{
using(var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<Policy>(
$"[{Schema}].[{Table}_ReadByOrganizationIdType]",
new { OrganizationId = organizationId, Type = (byte)type },
commandType: CommandType.StoredProcedure);
return results.SingleOrDefault();
}
}
public async Task<ICollection<Policy>> GetManyByOrganizationIdAsync(Guid organizationId)
{

View File

@ -259,5 +259,6 @@
<Build Include="dbo\Stored Procedures\Policy_ReadByOrganizationId.sql" />
<Build Include="dbo\Stored Procedures\Policy_Update.sql" />
<Build Include="dbo\Views\PolicyView.sql" />
<Build Include="dbo\Stored Procedures\Policy_ReadByOrganizationIdType.sql" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,15 @@
CREATE PROCEDURE [dbo].[Policy_ReadByOrganizationIdType]
@OrganizationId UNIQUEIDENTIFIER,
@Type TINYINT
AS
BEGIN
SET NOCOUNT ON
SELECT TOP 1
*
FROM
[dbo].[PolicyView]
WHERE
[OrganizationId] = @OrganizationId
AND [Type] = @Type
END

View File

@ -2,7 +2,7 @@
[Id] UNIQUEIDENTIFIER NOT NULL,
[OrganizationId] UNIQUEIDENTIFIER NOT NULL,
[Type] TINYINT NOT NULL,
[Data] NVARCHAR (MAX) NOT NULL,
[Data] NVARCHAR (MAX) NULL,
[Enabled] BIT NOT NULL,
[CreationDate] DATETIME2 (7) NOT NULL,
[RevisionDate] DATETIME2 (7) NOT NULL,
@ -12,6 +12,6 @@
GO
CREATE NONCLUSTERED INDEX [IX_Policy_OrganizationId_Enabled]
ON [dbo].[Policy]([OrganizationId] ASC, [Enabled] ASC);
CREATE UNIQUE NONCLUSTERED INDEX [IX_Policy_OrganizationId_Type]
ON [dbo].[Policy]([OrganizationId] ASC, [Type] ASC);

View File

@ -4,7 +4,7 @@ BEGIN
[Id] UNIQUEIDENTIFIER NOT NULL,
[OrganizationId] UNIQUEIDENTIFIER NOT NULL,
[Type] TINYINT NOT NULL,
[Data] NVARCHAR (MAX) NOT NULL,
[Data] NVARCHAR (MAX) NULL,
[Enabled] BIT NOT NULL,
[CreationDate] DATETIME2 (7) NOT NULL,
[RevisionDate] DATETIME2 (7) NOT NULL,
@ -12,8 +12,8 @@ BEGIN
CONSTRAINT [FK_Policy_Organization] FOREIGN KEY ([OrganizationId]) REFERENCES [dbo].[Organization] ([Id]) ON DELETE CASCADE
);
CREATE NONCLUSTERED INDEX [IX_Policy_OrganizationId_Enabled]
ON [dbo].[Policy]([OrganizationId] ASC, [Enabled] ASC);
CREATE UNIQUE NONCLUSTERED INDEX [IX_Policy_OrganizationId_Type]
ON [dbo].[Policy]([OrganizationId] ASC, [Type] ASC);
END
GO
@ -128,6 +128,29 @@ BEGIN
END
GO
IF OBJECT_ID('[dbo].[Policy_ReadByOrganizationIdType]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[Policy_ReadByOrganizationIdType]
END
GO
CREATE PROCEDURE [dbo].[Policy_ReadByOrganizationIdType]
@OrganizationId UNIQUEIDENTIFIER,
@Type TINYINT
AS
BEGIN
SET NOCOUNT ON
SELECT TOP 1
*
FROM
[dbo].[PolicyView]
WHERE
[OrganizationId] = @OrganizationId
AND [Type] = @Type
END
GO
IF OBJECT_ID('[dbo].[Policy_Update]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[Policy_Update]