diff --git a/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseClient.cs b/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseClient.cs index 74186a8f67..018c48e8de 100644 --- a/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseClient.cs +++ b/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseClient.cs @@ -16,6 +16,14 @@ public interface IObservableEnterpriseClient /// IObservableEnterpriseAdminStatsClient AdminStats { get; } + /// + /// A client for GitHub's Enterprise LDAP API + /// + /// + /// See the Enterprise LDAP API documentation for more information. + /// + IObservableEnterpriseLdapClient Ldap { get; } + /// /// A client for GitHub's Enterprise License API /// diff --git a/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseLdapClient.cs b/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseLdapClient.cs new file mode 100644 index 0000000000..d5f4bd1f68 --- /dev/null +++ b/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseLdapClient.cs @@ -0,0 +1,58 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Reactive; +using System.Reactive.Threading.Tasks; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Enterprise LDAP API + /// + /// + /// See the Enterprise LDAP API documentation for more information. + /// + public interface IObservableEnterpriseLdapClient + { + /// + /// Update the LDAP mapping for a user on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-user + /// + /// The username to update LDAP mapping + /// The + /// The object. + IObservable UpdateUserMapping(string userName, NewLdapMapping newLdapMapping); + + /// + /// Queue an LDAP Sync job for a user on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-user + /// + /// The username to sync LDAP mapping + /// The of the queue request. + IObservable QueueSyncUserMapping(string userName); + + /// + /// Update the LDAP mapping for a team on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-team + /// + /// The teamId to update LDAP mapping + /// The + /// The object. + IObservable UpdateTeamMapping(int teamId, NewLdapMapping newLdapMapping); + + /// + /// Queue an LDAP Sync job for a team on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-team + /// + /// The teamId to update LDAP mapping + /// The of the queue request. + IObservable QueueSyncTeamMapping(int teamId); + } +} diff --git a/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseClient.cs b/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseClient.cs index 0aa4c9201f..a463212a72 100644 --- a/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseClient.cs +++ b/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseClient.cs @@ -13,6 +13,7 @@ public ObservableEnterpriseClient(IGitHubClient client) Ensure.ArgumentNotNull(client, "client"); AdminStats = new ObservableEnterpriseAdminStatsClient(client); + Ldap = new ObservableEnterpriseLdapClient(client); License = new ObservableEnterpriseLicenseClient(client); Organization = new ObservableEnterpriseOrganizationClient(client); SearchIndexing = new ObservableEnterpriseSearchIndexingClient(client); @@ -26,6 +27,14 @@ public ObservableEnterpriseClient(IGitHubClient client) /// public IObservableEnterpriseAdminStatsClient AdminStats { get; private set; } + /// + /// A client for GitHub's Enterprise LDAP API + /// + /// + /// See the Enterprise LDAP API documentation for more information. + /// + public IObservableEnterpriseLdapClient Ldap { get; private set; } + /// /// A client for GitHub's Enterprise License API /// diff --git a/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseLdapClient.cs b/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseLdapClient.cs new file mode 100644 index 0000000000..f096e496ab --- /dev/null +++ b/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseLdapClient.cs @@ -0,0 +1,80 @@ +using System; +using System.Reactive; +using System.Reactive.Threading.Tasks; +using Octokit; + + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Enterprise LDAP API + /// + /// + /// See the Enterprise LDAP API documentation for more information. + /// + public class ObservableEnterpriseLdapClient : IObservableEnterpriseLdapClient + { + readonly IEnterpriseLdapClient _client; + + public ObservableEnterpriseLdapClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, "client"); + + _client = client.Enterprise.Ldap; + } + + /// + /// Update the LDAP mapping for a user on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-user + /// + /// The username to update LDAP mapping + /// The + /// The object. + public IObservable UpdateUserMapping(string userName, NewLdapMapping newLdapMapping) + { + return _client.UpdateUserMapping(userName, newLdapMapping).ToObservable(); + } + + /// + /// Queue an LDAP Sync job for a user on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-user + /// + /// The username to sync LDAP mapping + /// The of the queue request. + public IObservable QueueSyncUserMapping(string userName) + { + return _client.QueueSyncUserMapping(userName).ToObservable(); + } + + /// + /// Update the LDAP mapping for a team on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-team + /// + /// The teamId to update LDAP mapping + /// The + /// The object. + public IObservable UpdateTeamMapping(int teamId, NewLdapMapping newLdapMapping) + { + return _client.UpdateTeamMapping(teamId, newLdapMapping).ToObservable(); + } + + /// + /// Queue an LDAP Sync job for a team on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-team + /// + /// The teamId to update LDAP mapping + /// The of the queue request. + public IObservable QueueSyncTeamMapping(int teamId) + { + return _client.QueueSyncTeamMapping(teamId).ToObservable(); + } + } +} diff --git a/Octokit.Reactive/Octokit.Reactive-Mono.csproj b/Octokit.Reactive/Octokit.Reactive-Mono.csproj index 10d634574d..41db98100a 100644 --- a/Octokit.Reactive/Octokit.Reactive-Mono.csproj +++ b/Octokit.Reactive/Octokit.Reactive-Mono.csproj @@ -171,6 +171,8 @@ + + diff --git a/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj b/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj index d0f878da13..2a01d4da5a 100644 --- a/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj +++ b/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj @@ -179,6 +179,8 @@ + + diff --git a/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj b/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj index ce3efdbed0..c3855b708a 100644 --- a/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj +++ b/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj @@ -175,6 +175,8 @@ + + diff --git a/Octokit.Reactive/Octokit.Reactive.csproj b/Octokit.Reactive/Octokit.Reactive.csproj index 607a71eb74..4c69b57fee 100644 --- a/Octokit.Reactive/Octokit.Reactive.csproj +++ b/Octokit.Reactive/Octokit.Reactive.csproj @@ -75,6 +75,7 @@ Properties\SolutionInfo.cs + @@ -82,6 +83,7 @@ + diff --git a/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLdapClientTests.cs b/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLdapClientTests.cs new file mode 100644 index 0000000000..edbca49664 --- /dev/null +++ b/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLdapClientTests.cs @@ -0,0 +1,89 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Octokit; +using Octokit.Tests.Integration; +using Octokit.Tests.Integration.Helpers; +using Xunit; + +public class EnterpriseLdapClientTests : IDisposable +{ + readonly IGitHubClient _github; + + readonly string _testUser = "test-user"; + readonly string _distinguishedNameUser = "uid=test-user,ou=users,dc=company,dc=com"; + + readonly EnterpriseTeamContext _context; + readonly string _distinguishedNameTeam = "cn=test-team,ou=groups,dc=company,dc=com"; + + public EnterpriseLdapClientTests() + { + _github = EnterpriseHelper.GetAuthenticatedClient(); + + NewTeam newTeam = new NewTeam(Helper.MakeNameWithTimestamp("test-team")) { Description = "Test Team" }; + _context = _github.CreateEnterpriseTeamContext(EnterpriseHelper.Organization, newTeam).Result; + } + + [GitHubEnterpriseTest] + public async Task CanUpdateUserMapping() + { + var newLDAPMapping = new NewLdapMapping(_distinguishedNameUser); + var ldapUser = await + _github.Enterprise.Ldap.UpdateUserMapping(_testUser, newLDAPMapping); + + Assert.NotNull(ldapUser); + Assert.NotNull(ldapUser.LdapDistinguishedName); + Assert.Equal(ldapUser.LdapDistinguishedName, _distinguishedNameUser); + + // Get user and check mapping was updated + var checkUser = await _github.User.Get(_testUser); + Assert.Equal(checkUser.Login, ldapUser.Login); + Assert.Equal(checkUser.LdapDistinguishedName, _distinguishedNameUser); + } + + [GitHubEnterpriseTest] + public async Task CanQueueSyncUserMapping() + { + var response = await + _github.Enterprise.Ldap.QueueSyncUserMapping(_testUser); + + // Check response message indicates LDAP sync was queued + Assert.NotNull(response); + Assert.NotNull(response.Status); + Assert.True(response.Status == "queued"); + } + + [GitHubEnterpriseTest] + public async Task CanUpdateTeamMapping() + { + var newLDAPMapping = new NewLdapMapping(_distinguishedNameTeam); + var ldapTeam = await + _github.Enterprise.Ldap.UpdateTeamMapping(_context.TeamId, newLDAPMapping); + + Assert.NotNull(ldapTeam); + Assert.NotNull(ldapTeam.LdapDistinguishedName); + Assert.Equal(ldapTeam.LdapDistinguishedName, _distinguishedNameTeam); + + // Get Team and check mapping was updated + var checkTeam = await _github.Organization.Team.Get(_context.TeamId); + Assert.Equal(checkTeam.Name, ldapTeam.Name); + Assert.Equal(checkTeam.LdapDistinguishedName, _distinguishedNameTeam); + } + + [GitHubEnterpriseTest] + public async Task CanQueueSyncTeamMapping() + { + var response = await + _github.Enterprise.Ldap.QueueSyncTeamMapping(_context.TeamId); + + // Check response message indicates LDAP sync was queued + Assert.NotNull(response); + Assert.NotNull(response.Status); + Assert.True(response.Status == "queued"); + } + + public void Dispose() + { + _context.Dispose(); + } +} diff --git a/Octokit.Tests.Integration/EnterpriseHelper.cs b/Octokit.Tests.Integration/EnterpriseHelper.cs index 9a1ab54e06..63323012cc 100644 --- a/Octokit.Tests.Integration/EnterpriseHelper.cs +++ b/Octokit.Tests.Integration/EnterpriseHelper.cs @@ -124,6 +124,22 @@ public static void DeleteRepo(string owner, string name) catch { } } + public static void DeleteTeam(Team team) + { + if (team != null) + DeleteTeam(team.Id); + } + + public static void DeleteTeam(int teamId) + { + var api = GetAuthenticatedClient(); + try + { + api.Organization.Team.Delete(teamId).Wait(TimeSpan.FromSeconds(15)); + } + catch { } + } + public static IGitHubClient GetAuthenticatedClient() { return new GitHubClient(new ProductHeaderValue("OctokitEnterpriseTests"), GitHubEnterpriseUrl) diff --git a/Octokit.Tests.Integration/Helpers/EnterpriseTeamContext.cs b/Octokit.Tests.Integration/Helpers/EnterpriseTeamContext.cs new file mode 100644 index 0000000000..3b910a2af3 --- /dev/null +++ b/Octokit.Tests.Integration/Helpers/EnterpriseTeamContext.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Octokit.Tests.Integration.Helpers +{ + internal sealed class EnterpriseTeamContext : IDisposable + { + internal EnterpriseTeamContext(Team team) + { + Team = team; + TeamId = team.Id; + TeamName = team.Name; + } + + internal int TeamId { get; private set; } + internal string TeamName { get; private set; } + + internal Team Team { get; private set; } + + public void Dispose() + { + EnterpriseHelper.DeleteTeam(Team); + } + } +} diff --git a/Octokit.Tests.Integration/Helpers/GithubClientExtensions.cs b/Octokit.Tests.Integration/Helpers/GithubClientExtensions.cs index 58c53d0512..badd2b5623 100644 --- a/Octokit.Tests.Integration/Helpers/GithubClientExtensions.cs +++ b/Octokit.Tests.Integration/Helpers/GithubClientExtensions.cs @@ -29,5 +29,12 @@ internal async static Task CreateRepositoryContext(this IGitH return new RepositoryContext(repo); } + + internal async static Task CreateEnterpriseTeamContext(this IGitHubClient client, string organization, NewTeam newTeam) + { + var team = await client.Organization.Team.Create(organization, newTeam); + + return new EnterpriseTeamContext(team); + } } } \ No newline at end of file diff --git a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj index 329cae99d6..d12eff12fd 100644 --- a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj +++ b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj @@ -77,6 +77,7 @@ + @@ -118,6 +119,7 @@ + @@ -128,6 +130,7 @@ + diff --git a/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs b/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs new file mode 100644 index 0000000000..0880616259 --- /dev/null +++ b/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs @@ -0,0 +1,97 @@ +using System; +using System.Linq; +using System.Reactive.Linq; +using System.Threading.Tasks; +using Octokit.Reactive; +using Octokit.Tests.Integration.Helpers; +using Xunit; + +namespace Octokit.Tests.Integration +{ + public class ObservableEnterpriseLdapClientTests : IDisposable + { + readonly IObservableGitHubClient _github; + + readonly string _testUser = "test-user"; + readonly string _distinguishedNameUser = "uid=test-user,ou=users,dc=company,dc=com"; + + readonly EnterpriseTeamContext _context; + readonly string _distinguishedNameTeam = "cn=test-team,ou=groups,dc=company,dc=com"; + + public ObservableEnterpriseLdapClientTests() + { + var gitHub = EnterpriseHelper.GetAuthenticatedClient(); + _github = new ObservableGitHubClient(gitHub); + + NewTeam newTeam = new NewTeam(Helper.MakeNameWithTimestamp("test-team")) { Description = "Test Team" }; + _context = gitHub.CreateEnterpriseTeamContext(EnterpriseHelper.Organization, newTeam).Result; + } + + [GitHubEnterpriseTest] + public async Task CanUpdateUserMapping() + { + var newLDAPMapping = new NewLdapMapping(_distinguishedNameUser); + var observable = + _github.Enterprise.Ldap.UpdateUserMapping(_testUser, newLDAPMapping); + var ldapUser = await observable; + + Assert.NotNull(ldapUser); + Assert.NotNull(ldapUser.LdapDistinguishedName); + Assert.Equal(ldapUser.LdapDistinguishedName, _distinguishedNameUser); + + // Get user and check mapping was updated + var checkUser = await _github.User.Get(_testUser); + Assert.Equal(checkUser.Login, ldapUser.Login); + Assert.Equal(checkUser.LdapDistinguishedName, _distinguishedNameUser); + } + + [GitHubEnterpriseTest] + public async Task CanQueueSyncUserMapping() + { + var observable = + _github.Enterprise.Ldap.QueueSyncUserMapping(_testUser); + var response = await observable; + + // Check response message indicates LDAP sync was queued + Assert.NotNull(response); + Assert.NotNull(response.Status); + Assert.True(response.Status == "queued"); + } + + [GitHubEnterpriseTest] + public async Task CanUpdateTeamMapping() + { + var newLDAPMapping = new NewLdapMapping(_distinguishedNameTeam); + var observable = + _github.Enterprise.Ldap.UpdateTeamMapping(_context.TeamId, newLDAPMapping); + var ldapTeam = await observable; + + Assert.NotNull(ldapTeam); + Assert.NotNull(ldapTeam.LdapDistinguishedName); + Assert.Equal(ldapTeam.LdapDistinguishedName, _distinguishedNameTeam); + + // Get Team and check mapping was updated + var checkTeam = await _github.Organization.Team.Get(_context.TeamId); + Assert.Equal(checkTeam.Name, ldapTeam.Name); + Assert.Equal(checkTeam.LdapDistinguishedName, _distinguishedNameTeam); + } + + [GitHubEnterpriseTest] + public async Task CanQueueSyncTeamMapping() + { + var observable = + _github.Enterprise.Ldap.QueueSyncTeamMapping(_context.TeamId); + var response = await observable; + + // Check response message indicates LDAP sync was queued + Assert.NotNull(response); + Assert.NotNull(response.Status); + Assert.True(response.Status == "queued"); + } + + public void Dispose() + { + _context.Dispose(); + } + } +} diff --git a/Octokit.Tests/Clients/Enterprise/EnterpriseLdapClientTests.cs b/Octokit.Tests/Clients/Enterprise/EnterpriseLdapClientTests.cs new file mode 100644 index 0000000000..d5ea1c4d4e --- /dev/null +++ b/Octokit.Tests/Clients/Enterprise/EnterpriseLdapClientTests.cs @@ -0,0 +1,134 @@ +using System; +using System.Threading.Tasks; +using NSubstitute; +using Xunit; + +namespace Octokit.Tests.Clients +{ + public class EnterpriseLdapClientTests + { + public class TheUpdateUserMappingMethod + { + readonly string _distinguishedNameUser = "uid=test-user,ou=users,dc=company,dc=com"; + + [Fact] + public void RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new EnterpriseLdapClient(connection); + + string expectedUri = "admin/ldap/users/test-user/mapping"; + client.UpdateUserMapping("test-user", new NewLdapMapping(_distinguishedNameUser)); + + connection.Received().Patch(Arg.Is(u => u.ToString() == expectedUri), Arg.Any()); + } + + [Fact] + public void PassesRequestObject() + { + var connection = Substitute.For(); + var client = new EnterpriseLdapClient(connection); + + client.UpdateUserMapping("test-user", new NewLdapMapping(_distinguishedNameUser)); + + connection.Received().Patch( + Arg.Any(), + Arg.Is(a => + a.LdapDistinguishedName == _distinguishedNameUser)); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new EnterpriseLdapClient(connection); + + await Assert.ThrowsAsync(() => client.UpdateUserMapping(null, new NewLdapMapping(_distinguishedNameUser))); + await Assert.ThrowsAsync(() => client.UpdateUserMapping("test-user", null)); + } + } + + public class TheQueueSyncUserMappingMethod + { + [Fact] + public void RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new EnterpriseLdapClient(connection); + + string expectedUri = "admin/ldap/users/test-user/sync"; + client.QueueSyncUserMapping("test-user"); + + connection.Connection.Received().Post( + Arg.Is(u => u.ToString() == expectedUri)); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new EnterpriseLdapClient(connection); + + await Assert.ThrowsAsync(() => client.QueueSyncUserMapping(null)); + } + } + + public class TheUpdateTeamMappingMethod + { + readonly string _distinguishedNameTeam = "uid=DG-Test-Team,ou=groups,dc=company,dc=com"; + + [Fact] + public void RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new EnterpriseLdapClient(connection); + + string expectedUri = "admin/ldap/teams/1/mapping"; + client.UpdateTeamMapping(1, new NewLdapMapping(_distinguishedNameTeam)); + + connection.Received().Patch( + Arg.Is(u => u.ToString() == expectedUri), + Arg.Any()); + } + + [Fact] + public void PassesRequestObject() + { + var connection = Substitute.For(); + var client = new EnterpriseLdapClient(connection); + + client.UpdateTeamMapping(1, new NewLdapMapping(_distinguishedNameTeam)); + + connection.Received().Patch( + Arg.Any(), + Arg.Is(a => + a.LdapDistinguishedName == _distinguishedNameTeam)); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new EnterpriseLdapClient(connection); + + await Assert.ThrowsAsync(() => client.UpdateTeamMapping(1, null)); + } + } + + public class TheQueueSyncTeamMappingMethod + { + [Fact] + public void RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new EnterpriseLdapClient(connection); + + string expectedUri = "admin/ldap/teams/1/sync"; + client.QueueSyncTeamMapping(1); + + connection.Connection.Received().Post( + Arg.Is(u => u.ToString() == expectedUri)); + } + } + } +} diff --git a/Octokit.Tests/Octokit.Tests.csproj b/Octokit.Tests/Octokit.Tests.csproj index bc124880c8..cca0f7ae69 100644 --- a/Octokit.Tests/Octokit.Tests.csproj +++ b/Octokit.Tests/Octokit.Tests.csproj @@ -86,6 +86,7 @@ + @@ -191,6 +192,7 @@ + diff --git a/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs b/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs new file mode 100644 index 0000000000..a1fc976947 --- /dev/null +++ b/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs @@ -0,0 +1,74 @@ +using System; +using NSubstitute; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests +{ + public class ObservableEnterpriseLDAPClientTests + { + public class TheUpdateUserMappingMethod + { + readonly string _distinguishedName = "uid=test-user,ou=users,dc=company,dc=com"; + + [Fact] + public void CallsIntoClient() + { + var github = Substitute.For(); + var client = new ObservableEnterpriseLdapClient(github); + + client.UpdateUserMapping("test-user", new NewLdapMapping(_distinguishedName)); + github.Enterprise.Ldap.Received(1).UpdateUserMapping( + Arg.Is(a => a == "test-user"), + Arg.Is(a => + a.LdapDistinguishedName == _distinguishedName)); + } + } + + public class TheQueueSyncUserMappingMethod + { + [Fact] + public void CallsIntoClient() + { + var github = Substitute.For(); + var client = new ObservableEnterpriseLdapClient(github); + + client.QueueSyncUserMapping("test-user"); + github.Enterprise.Ldap.Received(1).QueueSyncUserMapping( + Arg.Is(a => a == "test-user")); + } + } + + public class TheUpdateTeamMappingMethod + { + readonly string _distinguishedName = "cn=test-team,ou=groups,dc=company,dc=com"; + + [Fact] + public void CallsIntoClient() + { + var github = Substitute.For(); + var client = new ObservableEnterpriseLdapClient(github); + + client.UpdateTeamMapping(1, new NewLdapMapping(_distinguishedName)); + github.Enterprise.Ldap.Received(1).UpdateTeamMapping( + Arg.Is(a => a == 1), + Arg.Is(a => + a.LdapDistinguishedName == _distinguishedName)); + } + } + + public class TheQueueSyncTeamMappingMethod + { + [Fact] + public void CallsIntoClient() + { + var github = Substitute.For(); + var client = new ObservableEnterpriseLdapClient(github); + + client.QueueSyncTeamMapping(1); + github.Enterprise.Ldap.Received(1).QueueSyncTeamMapping( + Arg.Is(a => a == 1)); + } + } + } +} diff --git a/Octokit/Clients/Enterprise/EnterpriseClient.cs b/Octokit/Clients/Enterprise/EnterpriseClient.cs index 614d9bce4b..aa5bb9d49d 100644 --- a/Octokit/Clients/Enterprise/EnterpriseClient.cs +++ b/Octokit/Clients/Enterprise/EnterpriseClient.cs @@ -15,6 +15,7 @@ public class EnterpriseClient : ApiClient, IEnterpriseClient public EnterpriseClient(IApiConnection apiConnection) : base(apiConnection) { AdminStats = new EnterpriseAdminStatsClient(apiConnection); + Ldap = new EnterpriseLdapClient(apiConnection); License = new EnterpriseLicenseClient(apiConnection); Organization = new EnterpriseOrganizationClient(apiConnection); SearchIndexing = new EnterpriseSearchIndexingClient(apiConnection); @@ -28,6 +29,14 @@ public EnterpriseClient(IApiConnection apiConnection) : base(apiConnection) /// public IEnterpriseAdminStatsClient AdminStats { get; private set; } + /// + /// A client for GitHub's Enterprise LDAP API + /// + /// + /// See the Enterprise LDAP API documentation for more information. + /// + public IEnterpriseLdapClient Ldap { get; private set; } + /// /// A client for GitHub's Enterprise License API /// diff --git a/Octokit/Clients/Enterprise/EnterpriseLdapClient.cs b/Octokit/Clients/Enterprise/EnterpriseLdapClient.cs new file mode 100644 index 0000000000..6357da504e --- /dev/null +++ b/Octokit/Clients/Enterprise/EnterpriseLdapClient.cs @@ -0,0 +1,102 @@ +using System.Net; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Enterprise LDAP API + /// + /// + /// See the Enterprise LDAP API documentation for more information. + /// + public class EnterpriseLdapClient : ApiClient, IEnterpriseLdapClient + { + public EnterpriseLdapClient(IApiConnection apiConnection) + : base(apiConnection) + { } + + /// + /// Update the LDAP mapping for a user on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-user + /// + /// The username to update LDAP mapping + /// The + /// The object. + public Task UpdateUserMapping(string userName, NewLdapMapping newLdapMapping) + { + Ensure.ArgumentNotNull(userName, "userName"); + Ensure.ArgumentNotNull(newLdapMapping, "newLdapMapping"); + + var endpoint = ApiUrls.EnterpriseLdapUserMapping(userName); + + return ApiConnection.Patch(endpoint, newLdapMapping); + } + + /// + /// Queue an LDAP Sync job for a user on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-user + /// + /// The userName to sync LDAP mapping + /// The of the queue request. + public async Task QueueSyncUserMapping(string userName) + { + Ensure.ArgumentNotNull(userName, "userName"); + + var endpoint = ApiUrls.EnterpriseLdapUserSync(userName); + + var response = await Connection.Post(endpoint); + if (response.HttpResponse.StatusCode != HttpStatusCode.Created) + { + throw new ApiException("Invalid Status Code returned. Expected a 201", response.HttpResponse.StatusCode); + } + + return response.Body; + } + + /// + /// Update the LDAP mapping for a team on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-team + /// + /// The teamId to update LDAP mapping + /// The + /// The object. + public Task UpdateTeamMapping(int teamId, NewLdapMapping newLdapMapping) + { + Ensure.ArgumentNotNull(teamId, "teamId"); + Ensure.ArgumentNotNull(newLdapMapping, "newLdapMapping"); + + var endpoint = ApiUrls.EnterpriseLdapTeamMapping(teamId); + + return ApiConnection.Patch(endpoint, newLdapMapping); + } + + /// + /// Queue an LDAP Sync job for a team on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-team + /// + /// The teamId to update LDAP mapping + /// The of the queue request. + public async Task QueueSyncTeamMapping(int teamId) + { + Ensure.ArgumentNotNull(teamId, "teamId"); + + var endpoint = ApiUrls.EnterpriseLdapTeamSync(teamId); + + var response = await Connection.Post(endpoint); + if (response.HttpResponse.StatusCode != HttpStatusCode.Created) + { + throw new ApiException("Invalid Status Code returned. Expected a 201", response.HttpResponse.StatusCode); + } + + return response.Body; + } + } +} diff --git a/Octokit/Clients/Enterprise/IEnterpriseClient.cs b/Octokit/Clients/Enterprise/IEnterpriseClient.cs index 899751c063..28a5db2205 100644 --- a/Octokit/Clients/Enterprise/IEnterpriseClient.cs +++ b/Octokit/Clients/Enterprise/IEnterpriseClient.cs @@ -16,6 +16,14 @@ public interface IEnterpriseClient /// IEnterpriseAdminStatsClient AdminStats { get; } + /// + /// A client for GitHub's Enterprise LDAP API + /// + /// + /// See the Enterprise LDAP API documentation for more information. + /// + IEnterpriseLdapClient Ldap { get; } + /// /// A client for GitHub's Enterprise License API /// diff --git a/Octokit/Clients/Enterprise/IEnterpriseLdapClient.cs b/Octokit/Clients/Enterprise/IEnterpriseLdapClient.cs new file mode 100644 index 0000000000..68f30cdd45 --- /dev/null +++ b/Octokit/Clients/Enterprise/IEnterpriseLdapClient.cs @@ -0,0 +1,55 @@ +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Enterprise LDAP API + /// + /// + /// See the Enterprise LDAP API documentation for more information. + /// + public interface IEnterpriseLdapClient + { + /// + /// Update the LDAP mapping for a user on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-user + /// + /// The username to update LDAP mapping + /// The + /// The object. + Task UpdateUserMapping(string userName, NewLdapMapping newLdapMapping); + + /// + /// Queue an LDAP Sync job for a user on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-user + /// + /// The username to sync LDAP mapping + /// The of the queue request. + Task QueueSyncUserMapping(string userName); + + /// + /// Update the LDAP mapping for a team on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-team + /// + /// The teamId to update LDAP mapping + /// The + /// The object. + Task UpdateTeamMapping(int teamId, NewLdapMapping newLdapMapping); + + /// + /// Queue an LDAP Sync job for a team on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/ldap/#sync-ldap-mapping-for-a-team + /// + /// The teamId to update LDAP mapping + /// The of the queue request. + Task QueueSyncTeamMapping(int teamId); + } +} diff --git a/Octokit/Helpers/ApiUrls.cs b/Octokit/Helpers/ApiUrls.cs index 30436b85a0..2c7e131c38 100644 --- a/Octokit/Helpers/ApiUrls.cs +++ b/Octokit/Helpers/ApiUrls.cs @@ -1664,6 +1664,26 @@ public static Uri EnterpriseAdminStatsAll() return EnterpriseAdminStats("all"); } + public static Uri EnterpriseLdapTeamMapping(int teamId) + { + return "admin/ldap/teams/{0}/mapping".FormatUri(teamId); + } + + public static Uri EnterpriseLdapTeamSync(int teamId) + { + return "admin/ldap/teams/{0}/sync".FormatUri(teamId); + } + + public static Uri EnterpriseLdapUserMapping(string userName) + { + return "admin/ldap/users/{0}/mapping".FormatUri(userName); + } + + public static Uri EnterpriseLdapUserSync(string userName) + { + return "admin/ldap/users/{0}/sync".FormatUri(userName); + } + public static Uri EnterpriseLicense() { return "enterprise/settings/license".FormatUri(); diff --git a/Octokit/Http/ApiConnection.cs b/Octokit/Http/ApiConnection.cs index 5821e327fe..33a77f6e4f 100644 --- a/Octokit/Http/ApiConnection.cs +++ b/Octokit/Http/ApiConnection.cs @@ -161,6 +161,21 @@ public Task Post(Uri uri) return Connection.Post(uri); } + /// + /// Creates a new API resource in the list at the specified URI. + /// + /// The API resource's type. + /// URI of the API resource to get + /// The created API resource. + /// Thrown when an API error occurs. + public async Task Post(Uri uri) + { + Ensure.ArgumentNotNull(uri, "uri"); + + var response = await Connection.Post(uri).ConfigureAwait(false); + return response.Body; + } + /// /// Creates a new API resource in the list at the specified URI. /// diff --git a/Octokit/Http/Connection.cs b/Octokit/Http/Connection.cs index 071758a5c5..dabee2710b 100644 --- a/Octokit/Http/Connection.cs +++ b/Octokit/Http/Connection.cs @@ -237,6 +237,13 @@ public async Task Post(Uri uri) return response.HttpResponse.StatusCode; } + public Task> Post(Uri uri) + { + Ensure.ArgumentNotNull(uri, "uri"); + + return SendData(uri, HttpMethod.Post, null, null, null, CancellationToken.None); + } + public Task> Post(Uri uri, object body, string accepts, string contentType) { Ensure.ArgumentNotNull(uri, "uri"); diff --git a/Octokit/Http/IApiConnection.cs b/Octokit/Http/IApiConnection.cs index c111707bd8..5805df83a5 100644 --- a/Octokit/Http/IApiConnection.cs +++ b/Octokit/Http/IApiConnection.cs @@ -100,6 +100,15 @@ public interface IApiConnection /// Thrown when an API error occurs. Task Post(Uri uri); + /// + /// Creates a new API resource in the list at the specified URI. + /// + /// The API resource's type. + /// URI endpoint to send request to + /// The created API resource. + /// Thrown when an API error occurs. + Task Post(Uri uri); + /// /// Creates a new API resource in the list at the specified URI. /// diff --git a/Octokit/Http/IConnection.cs b/Octokit/Http/IConnection.cs index 69bdde9bd3..137f488ee2 100644 --- a/Octokit/Http/IConnection.cs +++ b/Octokit/Http/IConnection.cs @@ -105,6 +105,15 @@ public interface IConnection : IApiInfoProvider /// representing the received HTTP response Task Post(Uri uri); + /// + /// Performs an asynchronous HTTP POST request. + /// Attempts to map the response body to an object of type + /// + /// The type to map the response to + /// URI endpoint to send request to + /// representing the received HTTP response + Task> Post(Uri uri); + /// /// Performs an asynchronous HTTP POST request. /// Attempts to map the response body to an object of type diff --git a/Octokit/Models/Request/Enterprise/NewLdapMapping.cs b/Octokit/Models/Request/Enterprise/NewLdapMapping.cs new file mode 100644 index 0000000000..d583272827 --- /dev/null +++ b/Octokit/Models/Request/Enterprise/NewLdapMapping.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using Octokit.Internal; + +namespace Octokit +{ + /// + /// Describes a new organization to create via the method. + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class NewLdapMapping + { + /// + /// Initializes a new instance of the class. + /// + /// The LDAP Distinguished Name + public NewLdapMapping(string ldapDistinguishedName) + { + Ensure.ArgumentNotNullOrEmptyString(ldapDistinguishedName, "ldapDistinguishedName"); + + LdapDistinguishedName = ldapDistinguishedName; + } + + /// + /// The LDAP Distinguished Name (required) + /// + [Parameter(Key = "ldap_dn")] + public string LdapDistinguishedName { get; private set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "LdapDistinguishedName: {0}", LdapDistinguishedName); + } + } + } +} diff --git a/Octokit/Models/Response/Enterprise/LdapSyncResponse.cs b/Octokit/Models/Response/Enterprise/LdapSyncResponse.cs new file mode 100644 index 0000000000..2ed74f9a0a --- /dev/null +++ b/Octokit/Models/Response/Enterprise/LdapSyncResponse.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class LdapSyncResponse + { + public LdapSyncResponse() { } + + public LdapSyncResponse(string status) + { + Status = status; + } + + public string Status + { + get; + private set; + } + + internal string DebuggerDisplay + { + get + { + return String.Format(CultureInfo.InvariantCulture, "Status: {0}", Status); + } + } + } +} diff --git a/Octokit/Models/Response/Team.cs b/Octokit/Models/Response/Team.cs index 314c1b0aee..0cb7d7df84 100644 --- a/Octokit/Models/Response/Team.cs +++ b/Octokit/Models/Response/Team.cs @@ -1,6 +1,8 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; +using Octokit.Internal; namespace Octokit { @@ -12,7 +14,7 @@ public class Team { public Team() { } - public Team(Uri url, int id, string name, Permission permission, int membersCount, int reposCount, Organization organization) + public Team(Uri url, int id, string name, Permission permission, int membersCount, int reposCount, Organization organization, string ldapDistinguishedName) { Url = url; Id = id; @@ -21,6 +23,7 @@ public Team(Uri url, int id, string name, Permission permission, int membersCoun MembersCount = membersCount; ReposCount = reposCount; Organization = organization; + LdapDistinguishedName = ldapDistinguishedName; } /// @@ -58,6 +61,12 @@ public Team(Uri url, int id, string name, Permission permission, int membersCoun /// public Organization Organization { get; protected set; } + /// + /// LDAP Binding (GitHub Enterprise only) + /// + [Parameter(Key = "ldap_dn")] + public string LdapDistinguishedName { get; protected set; } + internal string DebuggerDisplay { get { return string.Format(CultureInfo.InvariantCulture, "Name: {0} ", Name); } diff --git a/Octokit/Models/Response/User.cs b/Octokit/Models/Response/User.cs index 31b3d40a71..14bc3e96ef 100644 --- a/Octokit/Models/Response/User.cs +++ b/Octokit/Models/Response/User.cs @@ -1,6 +1,8 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; +using Octokit.Internal; namespace Octokit { @@ -12,10 +14,11 @@ public class User : Account { public User() { } - public User(string avatarUrl, string bio, string blog, int collaborators, string company, DateTimeOffset createdAt, int diskUsage, string email, int followers, int following, bool? hireable, string htmlUrl, int totalPrivateRepos, int id, string location, string login, string name, int ownedPrivateRepos, Plan plan, int privateGists, int publicGists, int publicRepos, string url, bool siteAdmin) + public User(string avatarUrl, string bio, string blog, int collaborators, string company, DateTimeOffset createdAt, int diskUsage, string email, int followers, int following, bool? hireable, string htmlUrl, int totalPrivateRepos, int id, string location, string login, string name, int ownedPrivateRepos, Plan plan, int privateGists, int publicGists, int publicRepos, string url, bool siteAdmin, string ldapDistinguishedName) : base(avatarUrl, bio, blog, collaborators, company, createdAt, diskUsage, email, followers, following, hireable, htmlUrl, totalPrivateRepos, id, location, login, name, ownedPrivateRepos, plan, privateGists, publicGists, publicRepos, AccountType.User, url) { SiteAdmin = siteAdmin; + LdapDistinguishedName = ldapDistinguishedName; } /// @@ -23,6 +26,12 @@ public User(string avatarUrl, string bio, string blog, int collaborators, string /// public bool SiteAdmin { get; protected set; } + /// + /// LDAP Binding (GitHub Enterprise only) + /// + [Parameter(Key = "ldap_dn")] + public string LdapDistinguishedName { get; protected set; } + internal string DebuggerDisplay { get diff --git a/Octokit/Octokit-Mono.csproj b/Octokit/Octokit-Mono.csproj index de0d43faa4..674e22acf7 100644 --- a/Octokit/Octokit-Mono.csproj +++ b/Octokit/Octokit-Mono.csproj @@ -448,6 +448,10 @@ + + + + \ No newline at end of file diff --git a/Octokit/Octokit-MonoAndroid.csproj b/Octokit/Octokit-MonoAndroid.csproj index 8ce11a6ed2..15d146fc52 100644 --- a/Octokit/Octokit-MonoAndroid.csproj +++ b/Octokit/Octokit-MonoAndroid.csproj @@ -456,6 +456,10 @@ + + + + \ No newline at end of file diff --git a/Octokit/Octokit-Monotouch.csproj b/Octokit/Octokit-Monotouch.csproj index 5add86a95b..98e4beee9c 100644 --- a/Octokit/Octokit-Monotouch.csproj +++ b/Octokit/Octokit-Monotouch.csproj @@ -452,6 +452,10 @@ + + + + diff --git a/Octokit/Octokit-Portable.csproj b/Octokit/Octokit-Portable.csproj index 8a39aea9db..eedca31146 100644 --- a/Octokit/Octokit-Portable.csproj +++ b/Octokit/Octokit-Portable.csproj @@ -445,6 +445,10 @@ + + + + diff --git a/Octokit/Octokit-netcore45.csproj b/Octokit/Octokit-netcore45.csproj index 5dc2d66447..8478f6f53e 100644 --- a/Octokit/Octokit-netcore45.csproj +++ b/Octokit/Octokit-netcore45.csproj @@ -452,6 +452,10 @@ + + + + diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index d6ef33fac0..45a357276b 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -59,10 +59,12 @@ + + @@ -111,6 +113,7 @@ + @@ -151,6 +154,7 @@ + @@ -484,4 +488,4 @@ --> - \ No newline at end of file +