Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Admin/Models/OrganizationEditModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models.Business;
using Bit.Core.Models.Data;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Settings;
using Bit.Core.Utilities;

Expand Down
5 changes: 2 additions & 3 deletions src/Admin/Models/OrganizationViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;

namespace Bit.Admin.Models
{
Expand Down
156 changes: 156 additions & 0 deletions src/Api/Controllers/OrganizationConnectionsController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Bit.Api.Models.Request.Organizations;
using Bit.Api.Models.Response.Organizations;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.OrganizationConnectionConfigs;
using Bit.Core.OrganizationFeatures.OrganizationConnections.Interfaces;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace Bit.Api.Controllers
{
[SelfHosted(SelfHostedOnly = true)]
[Authorize("Application")]
[Route("organizations/connections")]
public class OrganizationConnectionsController : Controller
{
private readonly ICreateOrganizationConnectionCommand _createOrganizationConnectionCommand;
private readonly IUpdateOrganizationConnectionCommand _updateOrganizationConnectionCommand;
private readonly IDeleteOrganizationConnectionCommand _deleteOrganizationConnectionCommand;
private readonly IOrganizationConnectionRepository _organizationConnectionRepository;
private readonly ICurrentContext _currentContext;
private readonly IGlobalSettings _globalSettings;

public OrganizationConnectionsController(
ICreateOrganizationConnectionCommand createOrganizationConnectionCommand,
IUpdateOrganizationConnectionCommand updateOrganizationConnectionCommand,
IDeleteOrganizationConnectionCommand deleteOrganizationConnectionCommand,
IOrganizationConnectionRepository organizationConnectionRepository,
ICurrentContext currentContext,
IGlobalSettings globalSettings)
{
_createOrganizationConnectionCommand = createOrganizationConnectionCommand;
_updateOrganizationConnectionCommand = updateOrganizationConnectionCommand;
_deleteOrganizationConnectionCommand = deleteOrganizationConnectionCommand;
_organizationConnectionRepository = organizationConnectionRepository;
_currentContext = currentContext;
_globalSettings = globalSettings;
}

[HttpGet("enabled")]
public bool ConnectionsEnabled()
{
return _globalSettings.SelfHosted && _globalSettings.EnableCloudCommunication;
}

[HttpPost]
public async Task<OrganizationConnectionResponseModel> CreateConnection([FromBody] OrganizationConnectionRequestModel model)
{
if (!await HasPermissionAsync(model?.OrganizationId))
{
throw new BadRequestException("Only the owner of an organization can create a connection.");
}

if (await HasConnectionTypeAsync(model))
{
throw new BadRequestException($"The requested organization already has a connection of type {model.Type}. Only one of each connection type may exist per organization.");
}

switch (model.Type)
{
case OrganizationConnectionType.CloudBillingSync:
var typedModel = new OrganizationConnectionRequestModel<BillingSyncConfig>(model);
var connection = await _createOrganizationConnectionCommand.CreateAsync(typedModel.ToData());
return new OrganizationConnectionResponseModel(connection, typeof(BillingSyncConfig));
default:
throw new BadRequestException($"Unkown Organization connection Type: {model.Type}");
}
}

[HttpPut("{organizationConnectionId}")]
public async Task<OrganizationConnectionResponseModel> UpdateConnection(Guid organizationConnectionId, [FromBody] OrganizationConnectionRequestModel model)
{
if (!await HasPermissionAsync(model?.OrganizationId))
{
throw new BadRequestException("Only the owner of an organization can update a connection.");
}

if (await HasConnectionTypeAsync(model, organizationConnectionId))
{
throw new BadRequestException($"The requested organization already has a connection of type {model.Type}. Only one of each connection type may exist per organization.");
}

switch (model.Type)
{
case OrganizationConnectionType.CloudBillingSync:
var typedModel = new OrganizationConnectionRequestModel<BillingSyncConfig>(model);
var connection = await _updateOrganizationConnectionCommand.UpdateAsync(typedModel.ToData(organizationConnectionId));
return new OrganizationConnectionResponseModel(connection, typeof(BillingSyncConfig));
default:
throw new BadRequestException($"Unkown Organization connection Type: {model.Type}");
}
}

[HttpGet("{organizationId}/{type}")]
public async Task<OrganizationConnectionResponseModel> GetConnection(Guid organizationId, OrganizationConnectionType type)
{
if (!await HasPermissionAsync(organizationId))
{
throw new BadRequestException("Only the owner of an organization can retrieve a connection.");
}

var connections = await GetConnectionsAsync(organizationId);
var connection = connections.FirstOrDefault(c => c.Type == type);

switch (type)
{
case OrganizationConnectionType.CloudBillingSync:
return new OrganizationConnectionResponseModel(connection, typeof(BillingSyncConfig));
default:
throw new BadRequestException($"Unkown Organization connection Type: {type}");
}

}

[HttpDelete("{organizationConnectionId}")]
[HttpPost("{organizationConnectionId}/delete")]
public async Task DeleteConnection(Guid organizationConnectionId)
{
var connection = await _organizationConnectionRepository.GetByIdAsync(organizationConnectionId);

if (connection == null)
{
throw new NotFoundException();
}

if (!await HasPermissionAsync(connection.OrganizationId))
{
throw new BadRequestException("Only the owner of an organization can remove a connection.");
}

await _deleteOrganizationConnectionCommand.DeleteAsync(connection);
}

private async Task<ICollection<OrganizationConnection>> GetConnectionsAsync(Guid organizationId) =>
await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(organizationId, OrganizationConnectionType.CloudBillingSync);

private async Task<bool> HasConnectionTypeAsync(OrganizationConnectionRequestModel model, Guid? connectionId = null)
{
var existingConnections = await GetConnectionsAsync(model.OrganizationId);

return existingConnections.Any(c => c.Type == model.Type && (!connectionId.HasValue || c.Id != connectionId.Value));
}

private async Task<bool> HasPermissionAsync(Guid? organizationId) =>
organizationId.HasValue && await _currentContext.OrganizationOwner(organizationId.Value);
}
}
7 changes: 1 addition & 6 deletions src/Api/Controllers/OrganizationSponsorshipsController.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Bit.Api.Models.Request.Organizations;
using Bit.Api.Models.Response;
using Bit.Api.Utilities;
using Bit.Api.Models.Response.Organizations;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Api.Request.OrganizationSponsorships;
using Bit.Core.Models.Api.Request.OrganizationSponsorships;
using Bit.Core.Models.Api.Response.OrganizationSponsorships;
using Bit.Core.Models.Api.Response.OrganizationSponsorships;
using Bit.Core.Models.Business.Tokenables;
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Tokens;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
Expand Down
3 changes: 2 additions & 1 deletion src/Api/Controllers/OrganizationUsersController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
using System.Threading.Tasks;
using Bit.Api.Models.Request.Organizations;
using Bit.Api.Models.Response;
using Bit.Api.Models.Response.Organizations;
using Bit.Core.Context;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Business;
using Bit.Core.Models.Data;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Microsoft.AspNetCore.Authorization;
Expand Down
1 change: 1 addition & 0 deletions src/Api/Controllers/OrganizationsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Bit.Api.Models.Request.Accounts;
using Bit.Api.Models.Request.Organizations;
using Bit.Api.Models.Response;
using Bit.Api.Models.Response.Organizations;
using Bit.Api.Utilities;
using Bit.Core.Context;
using Bit.Core.Enums;
Expand Down
2 changes: 1 addition & 1 deletion src/Api/Models/Public/MemberBaseModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;

namespace Bit.Api.Models.Public
{
Expand Down
1 change: 1 addition & 0 deletions src/Api/Models/Public/Response/MemberResponseModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;

namespace Bit.Api.Models.Public.Response
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System;
using System.Text.Json;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Data.Organizations.OrganizationConnections;
using Bit.Core.Utilities;

namespace Bit.Api.Models.Request.Organizations
{
public class OrganizationConnectionRequestModel
{
public OrganizationConnectionType Type { get; set; }
public Guid OrganizationId { get; set; }
public bool Enabled { get; set; }
public JsonDocument Config { get; set; }

public OrganizationConnectionRequestModel() { }
}


public class OrganizationConnectionRequestModel<T> : OrganizationConnectionRequestModel where T : new()
{
public T ParsedConfig { get; private set; }

public OrganizationConnectionRequestModel(OrganizationConnectionRequestModel model)
{
Type = model.Type;
OrganizationId = model.OrganizationId;
Enabled = model.Enabled;
Config = model.Config;

try
{
ParsedConfig = model.Config.ToObject<T>(JsonHelpers.IgnoreCase);
}
catch (JsonException)
{
throw new BadRequestException("Organization Connection configuration malformed");
}
}

public OrganizationConnectionData<T> ToData(Guid? id = null) =>
new()
{
Id = id,
Type = Type,
OrganizationId = OrganizationId,
Enabled = Enabled,
Config = ParsedConfig,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Utilities;

namespace Bit.Api.Models.Request.Organizations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using Bit.Core.Enums;
using Bit.Core.Models.Api;

namespace Bit.Api.Models.Response
namespace Bit.Api.Models.Response.Organizations
{
public class OrganizationApiKeyInformation : ResponseModel
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using Bit.Core.Models.Api;

namespace Bit.Api.Models.Response
namespace Bit.Api.Models.Response.Organizations
{
public class OrganizationAutoEnrollStatusResponseModel : ResponseModel
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Text.Json;
using Bit.Core.Entities;
using Bit.Core.Enums;

namespace Bit.Api.Models.Response.Organizations
{
public class OrganizationConnectionResponseModel
{
public Guid? Id { get; set; }
public OrganizationConnectionType Type { get; set; }
public Guid OrganizationId { get; set; }
public bool Enabled { get; set; }
public JsonDocument Config { get; set; }

public OrganizationConnectionResponseModel(OrganizationConnection connection, Type configType)
{
if (connection == null)
{
return;
}

Id = connection.Id;
Type = connection.Type;
OrganizationId = connection.OrganizationId;
Enabled = connection.Enabled;
Config = JsonDocument.Parse(connection.Config);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using Bit.Core.Entities;
using Bit.Core.Models.Api;

namespace Bit.Api.Models.Response
namespace Bit.Api.Models.Response.Organizations
{
public class OrganizationKeysResponseModel : ResponseModel
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using Bit.Core.Models.Business;
using Bit.Core.Utilities;

namespace Bit.Api.Models.Response
namespace Bit.Api.Models.Response.Organizations
{
public class OrganizationResponseModel : ResponseModel
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using Bit.Core.Models.Api;

namespace Bit.Api.Models.Response
namespace Bit.Api.Models.Response.Organizations
{
public class OrganizationSponsorshipSyncStatusResponseModel : ResponseModel
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using Bit.Core.Models.Data;
using Bit.Core.Settings;

namespace Bit.Api.Models.Response
namespace Bit.Api.Models.Response.Organizations
{
public class OrganizationSsoResponseModel : ResponseModel
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
using Bit.Core.Enums;
using Bit.Core.Models.Api;
using Bit.Core.Models.Data;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Utilities;

namespace Bit.Api.Models.Response
namespace Bit.Api.Models.Response.Organizations
{
public class OrganizationUserResponseModel : ResponseModel
{
Expand Down
Loading