mirror of
https://github.com/bitwarden/server.git
synced 2025-04-06 05:28:15 -05:00
remove scim project
This commit is contained in:
parent
61806cd8ac
commit
fffe92e634
@ -45,11 +45,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Icons", "src\Icons\Icons.cs
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Events", "src\Events\Events.csproj", "{994DD611-F266-4BD3-8072-3B1B57267ED5}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Events", "src\Events\Events.csproj", "{994DD611-F266-4BD3-8072-3B1B57267ED5}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Scim", "src\Scim\Scim.csproj", "{B8C5FFEB-186A-46FF-B914-BB3D50AA8D61}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventsProcessor", "src\EventsProcessor\EventsProcessor.csproj", "{2235D24F-E607-47F4-81AD-BB4504ADF9C6}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventsProcessor", "src\EventsProcessor\EventsProcessor.csproj", "{2235D24F-E607-47F4-81AD-BB4504ADF9C6}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Admin", "src\Admin\Admin.csproj", "{B131CEF3-89FB-4C90-ADB0-9E9C4246EB56}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Admin", "src\Admin\Admin.csproj", "{B131CEF3-89FB-4C90-ADB0-9E9C4246EB56}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@ -107,10 +105,6 @@ Global
|
|||||||
{994DD611-F266-4BD3-8072-3B1B57267ED5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{994DD611-F266-4BD3-8072-3B1B57267ED5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{994DD611-F266-4BD3-8072-3B1B57267ED5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{994DD611-F266-4BD3-8072-3B1B57267ED5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{994DD611-F266-4BD3-8072-3B1B57267ED5}.Release|Any CPU.Build.0 = Release|Any CPU
|
{994DD611-F266-4BD3-8072-3B1B57267ED5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{B8C5FFEB-186A-46FF-B914-BB3D50AA8D61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{B8C5FFEB-186A-46FF-B914-BB3D50AA8D61}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{B8C5FFEB-186A-46FF-B914-BB3D50AA8D61}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{B8C5FFEB-186A-46FF-B914-BB3D50AA8D61}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{2235D24F-E607-47F4-81AD-BB4504ADF9C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{2235D24F-E607-47F4-81AD-BB4504ADF9C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{2235D24F-E607-47F4-81AD-BB4504ADF9C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{2235D24F-E607-47F4-81AD-BB4504ADF9C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{2235D24F-E607-47F4-81AD-BB4504ADF9C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{2235D24F-E607-47F4-81AD-BB4504ADF9C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
@ -136,7 +130,6 @@ Global
|
|||||||
{A6C44A84-8E51-4C64-B9C4-7B7C23253345} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
|
{A6C44A84-8E51-4C64-B9C4-7B7C23253345} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
|
||||||
{9CF59342-3912-4B45-A2BA-0F173666586D} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D}
|
{9CF59342-3912-4B45-A2BA-0F173666586D} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D}
|
||||||
{994DD611-F266-4BD3-8072-3B1B57267ED5} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D}
|
{994DD611-F266-4BD3-8072-3B1B57267ED5} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D}
|
||||||
{B8C5FFEB-186A-46FF-B914-BB3D50AA8D61} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D}
|
|
||||||
{2235D24F-E607-47F4-81AD-BB4504ADF9C6} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D}
|
{2235D24F-E607-47F4-81AD-BB4504ADF9C6} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D}
|
||||||
{B131CEF3-89FB-4C90-ADB0-9E9C4246EB56} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D}
|
{B131CEF3-89FB-4C90-ADB0-9E9C4246EB56} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
namespace Bit.Scim
|
|
||||||
{
|
|
||||||
public class Constants
|
|
||||||
{
|
|
||||||
public static class Schemas
|
|
||||||
{
|
|
||||||
public const string User = @"urn:ietf:params:scim:schemas:core:2.0:User";
|
|
||||||
public const string UserEnterprise = @"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User";
|
|
||||||
public const string Group = @"urn:ietf:params:scim:schemas:core:2.0:Group";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Messages
|
|
||||||
{
|
|
||||||
public const string Error = @"urn:ietf:params:scim:api:messages:2.0:Error";
|
|
||||||
public const string PatchOp = @"urn:ietf:params:scim:api:messages:2.0:PatchOp";
|
|
||||||
public const string ListResponse = @"urn:ietf:params:scim:api:messages:2.0:ListResponse";
|
|
||||||
public const string SearchRequest = @"urn:ietf:params:scim:api:messages:2.0:SearchRequest";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using Bit.Core.Models;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace Bit.Scim.Controllers
|
|
||||||
{
|
|
||||||
public class BaseController : Controller
|
|
||||||
{
|
|
||||||
protected ICollection<T> FilterResources<T>(ICollection<T> resources, string filter) where T : IExternal
|
|
||||||
{
|
|
||||||
if(!string.IsNullOrWhiteSpace(filter))
|
|
||||||
{
|
|
||||||
var filterMatch = Regex.Match(filter, "(\\w+) eq \"([^\"]*)\"");
|
|
||||||
if(filterMatch.Success && filterMatch.Groups.Count > 2)
|
|
||||||
{
|
|
||||||
var searchKey = filterMatch.Groups[1].Value;
|
|
||||||
var searchValue = filterMatch.Groups[2].Value;
|
|
||||||
|
|
||||||
if(!string.IsNullOrWhiteSpace(searchKey) && !string.IsNullOrWhiteSpace(searchValue))
|
|
||||||
{
|
|
||||||
var searchKeyLower = searchKey.ToLowerInvariant();
|
|
||||||
if(searchKeyLower == "externalid")
|
|
||||||
{
|
|
||||||
resources = resources.Where(u => u.ExternalId == searchValue).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resources;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,117 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Bit.Core.Repositories;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using System.Linq;
|
|
||||||
using Bit.Scim.Models;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using Bit.Core.Services;
|
|
||||||
using Bit.Core.Exceptions;
|
|
||||||
|
|
||||||
namespace Bit.Scim.Controllers
|
|
||||||
{
|
|
||||||
[Route("groups")]
|
|
||||||
[Route("scim/groups")]
|
|
||||||
public class GroupsController : BaseController
|
|
||||||
{
|
|
||||||
private readonly IGroupRepository _groupRepository;
|
|
||||||
private readonly IGroupService _groupService;
|
|
||||||
private Guid _orgId = new Guid("2933f760-9c0b-4efb-a437-a82a00ed3fc1"); // TODO: come from context
|
|
||||||
|
|
||||||
public GroupsController(
|
|
||||||
IGroupRepository groupRepository,
|
|
||||||
IGroupService groupService)
|
|
||||||
{
|
|
||||||
_groupRepository = groupRepository;
|
|
||||||
_groupService = groupService;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
public async Task<IActionResult> GetAll([FromQuery]string filter, [FromQuery]string excludedAttributes,
|
|
||||||
[FromQuery]string attributes)
|
|
||||||
{
|
|
||||||
var groups = await _groupRepository.GetManyByOrganizationIdAsync(_orgId);
|
|
||||||
groups = FilterResources(groups, filter);
|
|
||||||
var groupsResult = groups.Select(g => new ScimGroup(g));
|
|
||||||
var result = new ScimListResponse(groupsResult);
|
|
||||||
return new OkObjectResult(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{id}")]
|
|
||||||
public async Task<IActionResult> Get(string id)
|
|
||||||
{
|
|
||||||
var group = await _groupRepository.GetByIdAsync(new Guid(id));
|
|
||||||
if(group == null || group.OrganizationId != _orgId)
|
|
||||||
{
|
|
||||||
throw new NotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = new ScimGroup(group);
|
|
||||||
return new OkObjectResult(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
public async Task<IActionResult> Post([FromBody]ScimGroup model)
|
|
||||||
{
|
|
||||||
var group = model.ToGroup(_orgId);
|
|
||||||
await _groupService.SaveAsync(group);
|
|
||||||
var result = new ScimGroup(group);
|
|
||||||
var getUrl = Url.Action("Get", "Groups", new { id = group.Id.ToString() }, Request.Protocol, Request.Host.Value);
|
|
||||||
return new CreatedResult(getUrl, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPut("{id}")]
|
|
||||||
public async Task<IActionResult> Put(string id, [FromBody]ScimGroup model)
|
|
||||||
{
|
|
||||||
var group = await _groupRepository.GetByIdAsync(new Guid(id));
|
|
||||||
if(group == null || group.OrganizationId != _orgId)
|
|
||||||
{
|
|
||||||
throw new NotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
group = model.ToGroup(group);
|
|
||||||
await _groupService.SaveAsync(group);
|
|
||||||
|
|
||||||
var result = new ScimGroup(group);
|
|
||||||
return new OkObjectResult(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPatch("{id}")]
|
|
||||||
public async Task<IActionResult> Patch(string id)
|
|
||||||
{
|
|
||||||
var group = await _groupRepository.GetByIdAsync(new Guid(id));
|
|
||||||
if(group == null || group.OrganizationId != _orgId)
|
|
||||||
{
|
|
||||||
throw new NotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
var memstream = new MemoryStream();
|
|
||||||
Request.Body.CopyTo(memstream);
|
|
||||||
memstream.Position = 0;
|
|
||||||
using(var reader = new StreamReader(memstream))
|
|
||||||
{
|
|
||||||
var text = reader.ReadToEnd();
|
|
||||||
Debug.WriteLine(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Do patch
|
|
||||||
|
|
||||||
var result = new ScimGroup(group);
|
|
||||||
return new OkObjectResult(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpDelete("{id}")]
|
|
||||||
public async Task<IActionResult> Delete(string id)
|
|
||||||
{
|
|
||||||
var group = await _groupRepository.GetByIdAsync(new Guid(id));
|
|
||||||
if(group == null || group.OrganizationId != _orgId)
|
|
||||||
{
|
|
||||||
throw new NotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
await _groupService.DeleteAsync(group);
|
|
||||||
return new OkResult();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,122 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Bit.Core.Enums;
|
|
||||||
using Bit.Core.Repositories;
|
|
||||||
using Bit.Core.Services;
|
|
||||||
using Bit.Scim.Models;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Bit.Core.Models.Data;
|
|
||||||
using Bit.Core.Exceptions;
|
|
||||||
|
|
||||||
namespace Bit.Scim.Controllers
|
|
||||||
{
|
|
||||||
[Route("users")]
|
|
||||||
[Route("scim/users")]
|
|
||||||
public class UsersController : BaseController
|
|
||||||
{
|
|
||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
|
||||||
private readonly IOrganizationService _organizationService;
|
|
||||||
private Guid _orgId = new Guid("2933f760-9c0b-4efb-a437-a82a00ed3fc1"); // TODO: come from context
|
|
||||||
|
|
||||||
public UsersController(
|
|
||||||
IOrganizationUserRepository organizationUserRepository,
|
|
||||||
IOrganizationService organizationService)
|
|
||||||
{
|
|
||||||
_organizationUserRepository = organizationUserRepository;
|
|
||||||
_organizationService = organizationService;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
public async Task<IActionResult> GetAll([FromQuery]string filter, [FromQuery]string excludedAttributes,
|
|
||||||
[FromQuery]string attributes)
|
|
||||||
{
|
|
||||||
var users = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(_orgId);
|
|
||||||
users = FilterResources(users, filter);
|
|
||||||
var usersResult = users.Select(u => new ScimUser(u));
|
|
||||||
var result = new ScimListResponse(usersResult);
|
|
||||||
return new OkObjectResult(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{id}")]
|
|
||||||
public async Task<IActionResult> Get(string id)
|
|
||||||
{
|
|
||||||
var users = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(_orgId);
|
|
||||||
var user = users.SingleOrDefault(u => u.Id == new Guid(id));
|
|
||||||
if(user == null)
|
|
||||||
{
|
|
||||||
throw new NotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = new ScimUser(user);
|
|
||||||
return new OkObjectResult(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
public async Task<IActionResult> Post([FromBody]ScimUser model)
|
|
||||||
{
|
|
||||||
var email = model.Emails?.FirstOrDefault();
|
|
||||||
if(email == null)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("No email address available.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var orgUser = await _organizationService.InviteUserAsync(_orgId, null, email.Value,
|
|
||||||
OrganizationUserType.User, false, model.ExternalId, new List<SelectionReadOnly>());
|
|
||||||
var result = new ScimUser(orgUser);
|
|
||||||
var getUrl = Url.Action("Get", "Users", new { id = orgUser.Id.ToString() }, Request.Protocol, Request.Host.Value);
|
|
||||||
return new CreatedResult(getUrl, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPut("{id}")]
|
|
||||||
public async Task<IActionResult> Put(string id, [FromBody]ScimUser model)
|
|
||||||
{
|
|
||||||
var users = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(_orgId);
|
|
||||||
var user = users.SingleOrDefault(u => u.Id == new Guid(id));
|
|
||||||
if(user == null)
|
|
||||||
{
|
|
||||||
throw new NotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: update
|
|
||||||
|
|
||||||
var result = new ScimUser(user);
|
|
||||||
return new OkObjectResult(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPatch("{id}")]
|
|
||||||
public async Task<IActionResult> Patch(string id)
|
|
||||||
{
|
|
||||||
var users = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(_orgId);
|
|
||||||
var user = users.SingleOrDefault(u => u.Id == new Guid(id));
|
|
||||||
if(user == null)
|
|
||||||
{
|
|
||||||
throw new NotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
var memstream = new MemoryStream();
|
|
||||||
Request.Body.CopyTo(memstream);
|
|
||||||
memstream.Position = 0;
|
|
||||||
using(var reader = new StreamReader(memstream))
|
|
||||||
{
|
|
||||||
var text = reader.ReadToEnd();
|
|
||||||
Debug.WriteLine(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: patch
|
|
||||||
|
|
||||||
var result = new ScimUser(user);
|
|
||||||
return new OkObjectResult(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpDelete("{id}")]
|
|
||||||
public async Task<IActionResult> Delete(string id)
|
|
||||||
{
|
|
||||||
await _organizationService.DeleteUserAsync(_orgId, new Guid(id), null);
|
|
||||||
return new OkResult();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Net;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Bit.Scim.Models
|
|
||||||
{
|
|
||||||
public class ScimError
|
|
||||||
{
|
|
||||||
private IEnumerable<string> _schemas;
|
|
||||||
|
|
||||||
public ScimError()
|
|
||||||
{
|
|
||||||
_schemas = new[] { Constants.Messages.Error };
|
|
||||||
}
|
|
||||||
|
|
||||||
public ScimError(HttpStatusCode status, string detail = null)
|
|
||||||
: this()
|
|
||||||
{
|
|
||||||
Status = (int)status;
|
|
||||||
Detail = detail;
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonProperty("schemas")]
|
|
||||||
public IEnumerable<string> Schemas
|
|
||||||
{
|
|
||||||
get => _schemas;
|
|
||||||
set { _schemas = value; }
|
|
||||||
}
|
|
||||||
[JsonProperty("status")]
|
|
||||||
public int Status { get; set; }
|
|
||||||
[JsonProperty("detail")]
|
|
||||||
public string Detail { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Bit.Core.Models.Table;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Bit.Scim.Models
|
|
||||||
{
|
|
||||||
public class ScimGroup : ScimResource
|
|
||||||
{
|
|
||||||
public ScimGroup() { }
|
|
||||||
|
|
||||||
public ScimGroup(Group group)
|
|
||||||
{
|
|
||||||
Id = group.Id.ToString();
|
|
||||||
ExternalId = group.ExternalId;
|
|
||||||
DisplayName = group.Name;
|
|
||||||
Meta = new ScimResourceMetadata("Group");
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string SchemaIdentifier => Constants.Schemas.Group;
|
|
||||||
[JsonProperty("displayName")]
|
|
||||||
public string DisplayName { get; set; }
|
|
||||||
[JsonProperty("members")]
|
|
||||||
public IEnumerable<ScimMultiValuedAttribute> Members { get; set; }
|
|
||||||
|
|
||||||
public Group ToGroup(Guid orgId)
|
|
||||||
{
|
|
||||||
return new Group
|
|
||||||
{
|
|
||||||
ExternalId = ExternalId,
|
|
||||||
Name = DisplayName,
|
|
||||||
OrganizationId = orgId
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public Group ToGroup(Group group)
|
|
||||||
{
|
|
||||||
group.Name = DisplayName;
|
|
||||||
return group;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Bit.Scim.Models
|
|
||||||
{
|
|
||||||
public class ScimListResponse : ScimSchemaBase
|
|
||||||
{
|
|
||||||
public ScimListResponse(IEnumerable<ScimResource> resources)
|
|
||||||
{
|
|
||||||
Resources = resources;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string SchemaIdentifier => Constants.Messages.ListResponse;
|
|
||||||
[JsonProperty("totalResults", Order = 0)]
|
|
||||||
public int TotalResults => Resources == null ? 0 : Resources.Count();
|
|
||||||
[JsonProperty("Resources", Order = 1)]
|
|
||||||
public IEnumerable<ScimResource> Resources { get; private set; }
|
|
||||||
[JsonProperty("startIndex", Order = 2)]
|
|
||||||
public int StartIndex { get; set; } = 0;
|
|
||||||
[JsonProperty("itemsPerPage", Order = 3)]
|
|
||||||
public int ItemsPerPage => Resources == null ? 0 : Resources.Count();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Bit.Scim.Models
|
|
||||||
{
|
|
||||||
public class ScimMultiValuedAttribute
|
|
||||||
{
|
|
||||||
[JsonProperty("type")]
|
|
||||||
public string Type { get; set; }
|
|
||||||
[JsonProperty("primary")]
|
|
||||||
public bool Primary { get; set; }
|
|
||||||
[JsonProperty("display")]
|
|
||||||
public string Display { get; set; }
|
|
||||||
[JsonProperty("value")]
|
|
||||||
public string Value { get; set; }
|
|
||||||
[JsonProperty("$ref")]
|
|
||||||
public Uri Ref { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Bit.Scim.Models
|
|
||||||
{
|
|
||||||
public abstract class ScimResource : ScimSchemaBase
|
|
||||||
{
|
|
||||||
[JsonProperty(Order = -5, PropertyName = "id")]
|
|
||||||
public string Id { get; set; }
|
|
||||||
[JsonProperty(PropertyName = "externalId")]
|
|
||||||
public string ExternalId { get; set; }
|
|
||||||
[JsonProperty(Order = 9999, PropertyName = "meta")]
|
|
||||||
public ScimResourceMetadata Meta { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Bit.Scim.Models
|
|
||||||
{
|
|
||||||
public class ScimResourceMetadata
|
|
||||||
{
|
|
||||||
private ScimResourceMetadata() { }
|
|
||||||
|
|
||||||
public ScimResourceMetadata(string resourceType)
|
|
||||||
{
|
|
||||||
ResourceType = resourceType;
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonProperty("resourceType")]
|
|
||||||
public string ResourceType { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Bit.Scim.Models
|
|
||||||
{
|
|
||||||
public abstract class ScimSchemaBase
|
|
||||||
{
|
|
||||||
[JsonProperty("schemas", Order = -10)]
|
|
||||||
public virtual ISet<string> Schemas => new HashSet<string>(new[] { SchemaIdentifier });
|
|
||||||
[JsonIgnore]
|
|
||||||
public abstract string SchemaIdentifier { get; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using Bit.Core.Models.Data;
|
|
||||||
using Bit.Core.Models.Table;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Bit.Scim.Models
|
|
||||||
{
|
|
||||||
public class ScimUser : ScimResource
|
|
||||||
{
|
|
||||||
public ScimUser() { }
|
|
||||||
|
|
||||||
public ScimUser(OrganizationUserUserDetails userDetails)
|
|
||||||
{
|
|
||||||
Id = userDetails.Id.ToString();
|
|
||||||
ExternalId = userDetails.ExternalId;
|
|
||||||
UserName = userDetails.Email;
|
|
||||||
Name = new ScimName
|
|
||||||
{
|
|
||||||
Formatted = userDetails.Name
|
|
||||||
};
|
|
||||||
DisplayName = userDetails.Name;
|
|
||||||
Active = true;
|
|
||||||
Emails = new List<ScimMultiValuedAttribute> {
|
|
||||||
new ScimMultiValuedAttribute { Type = "work", Value = userDetails.Email } };
|
|
||||||
Meta = new ScimResourceMetadata("User");
|
|
||||||
}
|
|
||||||
|
|
||||||
public ScimUser(OrganizationUser orgUser)
|
|
||||||
{
|
|
||||||
Id = orgUser.Id.ToString();
|
|
||||||
ExternalId = orgUser.ExternalId;
|
|
||||||
UserName = orgUser.Email;
|
|
||||||
Active = true;
|
|
||||||
Emails = new List<ScimMultiValuedAttribute> {
|
|
||||||
new ScimMultiValuedAttribute { Type = "work", Value = orgUser.Email } };
|
|
||||||
Meta = new ScimResourceMetadata("User");
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string SchemaIdentifier => Constants.Schemas.User;
|
|
||||||
[JsonProperty("userName")]
|
|
||||||
public string UserName { get; set; }
|
|
||||||
[JsonProperty("name")]
|
|
||||||
public ScimName Name { get; set; }
|
|
||||||
[JsonProperty("displayName")]
|
|
||||||
public string DisplayName { get; set; }
|
|
||||||
[JsonProperty("active")]
|
|
||||||
public bool Active { get; set; }
|
|
||||||
[JsonProperty("emails")]
|
|
||||||
public IEnumerable<ScimMultiValuedAttribute> Emails { get; set; }
|
|
||||||
[JsonProperty("groups")]
|
|
||||||
public IEnumerable<ScimMultiValuedAttribute> Groups { get; set; }
|
|
||||||
|
|
||||||
public class ScimName
|
|
||||||
{
|
|
||||||
[JsonProperty("formatted")]
|
|
||||||
public string Formatted { get; set; }
|
|
||||||
[JsonProperty("familyName")]
|
|
||||||
public string FamilyName { get; set; }
|
|
||||||
[JsonProperty("givenName")]
|
|
||||||
public string GivenName { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
using Microsoft.AspNetCore;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
|
|
||||||
namespace Bit.Scim
|
|
||||||
{
|
|
||||||
public class Program
|
|
||||||
{
|
|
||||||
public static void Main(string[] args)
|
|
||||||
{
|
|
||||||
WebHost
|
|
||||||
.CreateDefaultBuilder(args)
|
|
||||||
.UseStartup<Startup>()
|
|
||||||
.Build()
|
|
||||||
.Run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"iisSettings": {
|
|
||||||
"windowsAuthentication": false,
|
|
||||||
"anonymousAuthentication": true,
|
|
||||||
"iisExpress": {
|
|
||||||
"applicationUrl": "http://localhost:9000/",
|
|
||||||
"sslPort": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"profiles": {
|
|
||||||
"IIS Express": {
|
|
||||||
"commandName": "IISExpress",
|
|
||||||
"launchBrowser": false,
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Scim": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"launchBrowser": false,
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
},
|
|
||||||
"applicationUrl": "http://localhost:9000/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<Version>1.20.0</Version>
|
|
||||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
|
||||||
<RootNamespace>Bit.Scim</RootNamespace>
|
|
||||||
<UserSecretsId>bitwarden-Scim</UserSecretsId>
|
|
||||||
<MvcRazorCompileOnPublish>false</MvcRazorCompileOnPublish>
|
|
||||||
<MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\Core\Core.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.6" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0" />
|
|
||||||
<DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="2.0.0" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
@ -1,79 +0,0 @@
|
|||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using Bit.Scim.Utilities;
|
|
||||||
using Microsoft.ApplicationInsights.Extensibility;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Serilog.Events;
|
|
||||||
|
|
||||||
namespace Bit.Scim
|
|
||||||
{
|
|
||||||
public class Startup
|
|
||||||
{
|
|
||||||
public Startup(IConfiguration configuration)
|
|
||||||
{
|
|
||||||
Configuration = configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IConfiguration Configuration { get; }
|
|
||||||
|
|
||||||
public void ConfigureServices(IServiceCollection services)
|
|
||||||
{
|
|
||||||
// Options
|
|
||||||
services.AddOptions();
|
|
||||||
|
|
||||||
// Settings
|
|
||||||
var globalSettings = services.AddGlobalSettingsServices(Configuration);
|
|
||||||
|
|
||||||
// Repositories
|
|
||||||
services.AddSqlServerRepositories(globalSettings);
|
|
||||||
|
|
||||||
// Context
|
|
||||||
services.AddScoped<CurrentContext>();
|
|
||||||
|
|
||||||
// Identity
|
|
||||||
services.AddCustomIdentityServices(globalSettings);
|
|
||||||
|
|
||||||
// Services
|
|
||||||
services.AddBaseServices();
|
|
||||||
services.AddDefaultServices(globalSettings);
|
|
||||||
|
|
||||||
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
|
||||||
|
|
||||||
// Mvc
|
|
||||||
services.AddMvc(config =>
|
|
||||||
{
|
|
||||||
config.Filters.Add(new ExceptionHandlerFilterAttribute());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Configure(
|
|
||||||
IApplicationBuilder app,
|
|
||||||
IHostingEnvironment env,
|
|
||||||
IApplicationLifetime appLifetime,
|
|
||||||
GlobalSettings globalSettings,
|
|
||||||
ILoggerFactory loggerFactory)
|
|
||||||
{
|
|
||||||
// Disable app insights
|
|
||||||
var telConfig = app.ApplicationServices.GetService<TelemetryConfiguration>();
|
|
||||||
telConfig.DisableTelemetry = true;
|
|
||||||
|
|
||||||
loggerFactory.AddSerilog(app, env, appLifetime, globalSettings, (e) => e.Level >= LogEventLevel.Error);
|
|
||||||
|
|
||||||
if(env.IsDevelopment())
|
|
||||||
{
|
|
||||||
app.UseDeveloperExceptionPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default Middleware
|
|
||||||
app.UseDefaultMiddleware(env);
|
|
||||||
|
|
||||||
app.UseMvc();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
using Bit.Core.Exceptions;
|
|
||||||
using Bit.Scim.Models;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace Bit.Scim.Utilities
|
|
||||||
{
|
|
||||||
public class ExceptionHandlerFilterAttribute : ExceptionFilterAttribute
|
|
||||||
{
|
|
||||||
public override void OnException(ExceptionContext context)
|
|
||||||
{
|
|
||||||
var exception = context.Exception;
|
|
||||||
if(exception == null)
|
|
||||||
{
|
|
||||||
// Should never happen.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var error = new ScimError();
|
|
||||||
if(exception is BadRequestException)
|
|
||||||
{
|
|
||||||
context.HttpContext.Response.StatusCode = error.Status = 400;
|
|
||||||
error.Detail = exception.Message;
|
|
||||||
}
|
|
||||||
else if(exception is NotFoundException)
|
|
||||||
{
|
|
||||||
context.HttpContext.Response.StatusCode = error.Status = 404;
|
|
||||||
error.Detail = "Resource not found.";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
context.HttpContext.Response.StatusCode = error.Status = 500;
|
|
||||||
error.Detail = "An unhandled server error has occurred.";
|
|
||||||
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<ExceptionHandlerFilterAttribute>>();
|
|
||||||
logger.LogError(0, exception, exception.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Result = new ObjectResult(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"globalSettings": {
|
|
||||||
"baseServiceUri": {
|
|
||||||
"vault": "https://vault.bitwarden.com",
|
|
||||||
"api": "https://api.bitwarden.com",
|
|
||||||
"identity": "https://identity.bitwarden.com",
|
|
||||||
"admin": "https://admin.bitwarden.com",
|
|
||||||
"internalAdmin": "https://admin.bitwarden.com",
|
|
||||||
"internalIdentity": "https://identity.bitwarden.com",
|
|
||||||
"internalApi": "https://api.bitwarden.com",
|
|
||||||
"internalVault": "https://vault.bitwarden.com"
|
|
||||||
},
|
|
||||||
"braintree": {
|
|
||||||
"production": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
{
|
|
||||||
"globalSettings": {
|
|
||||||
"selfHosted": false,
|
|
||||||
"siteName": "Bitwarden",
|
|
||||||
"projectName": "Billing",
|
|
||||||
"stripeApiKey": "SECRET",
|
|
||||||
"baseServiceUri": {
|
|
||||||
"vault": "https://localhost:8080",
|
|
||||||
"api": "http://localhost:4000",
|
|
||||||
"identity": "http://localhost:33656",
|
|
||||||
"admin": "http://localhost:62911",
|
|
||||||
"internalAdmin": "http://localhost:62911",
|
|
||||||
"internalIdentity": "http://localhost:33656",
|
|
||||||
"internalApi": "http://localhost:4000",
|
|
||||||
"internalVault": "http://localhost:4001"
|
|
||||||
},
|
|
||||||
"sqlServer": {
|
|
||||||
"connectionString": "SECRET"
|
|
||||||
},
|
|
||||||
"mail": {
|
|
||||||
"sendGridApiKey": "SECRET",
|
|
||||||
"replyToEmail": "hello@bitwarden.com"
|
|
||||||
},
|
|
||||||
"identityServer": {
|
|
||||||
"certificateThumbprint": "SECRET"
|
|
||||||
},
|
|
||||||
"dataProtection": {
|
|
||||||
"certificateThumbprint": "SECRET"
|
|
||||||
},
|
|
||||||
"storage": {
|
|
||||||
"connectionString": "SECRET"
|
|
||||||
},
|
|
||||||
"events": {
|
|
||||||
"connectionString": "SECRET"
|
|
||||||
},
|
|
||||||
"documentDb": {
|
|
||||||
"uri": "SECRET",
|
|
||||||
"key": "SECRET"
|
|
||||||
},
|
|
||||||
"sentry": {
|
|
||||||
"dsn": "SECRET"
|
|
||||||
},
|
|
||||||
"notificationHub": {
|
|
||||||
"connectionString": "SECRET",
|
|
||||||
"hubName": "SECRET"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"billingSettings": {
|
|
||||||
"stripeWebhookKey": "SECRET",
|
|
||||||
"stripeWebhookSecret": "SECRET",
|
|
||||||
"braintreeWebhookKey": "SECRET"
|
|
||||||
},
|
|
||||||
"braintree": {
|
|
||||||
"production": false,
|
|
||||||
"merchantId": "SECRET",
|
|
||||||
"publicKey": "SECRET",
|
|
||||||
"privateKey": "SECRET"
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user