diff --git a/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseClient.cs b/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseClient.cs index 74186a8f..018c48e8 100644 --- a/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseClient.cs +++ b/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseClient.cs @@ -16,6 +16,14 @@ /// 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 00000000..d5f4bd1f --- /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 0aa4c920..a463212a 100644 --- a/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseClient.cs +++ b/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseClient.cs @@ -13,6 +13,7 @@ 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 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 00000000..f096e496 --- /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 10d63457..41db9810 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 d0f878da..2a01d4da 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 ce3efdbe..c3855b70 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 607a71eb..4c69b57f 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 00000000..edbca496 --- /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 9a1ab54e..63323012 100644 --- a/Octokit.Tests.Integration/EnterpriseHelper.cs +++ b/Octokit.Tests.Integration/EnterpriseHelper.cs @@ -124,6 +124,22 @@ namespace Octokit.Tests.Integration 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 00000000..3b910a2a --- /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 58c53d05..badd2b56 100644 --- a/Octokit.Tests.Integration/Helpers/GithubClientExtensions.cs +++ b/Octokit.Tests.Integration/Helpers/GithubClientExtensions.cs @@ -29,5 +29,12 @@ namespace Octokit.Tests.Integration.Helpers 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 329cae99..d12eff12 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 00000000..08806162 --- /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 00000000..d5ea1c4d --- /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 bc124880..cca0f7ae 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 00000000..a1fc9769 --- /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 614d9bce..aa5bb9d4 100644 --- a/Octokit/Clients/Enterprise/EnterpriseClient.cs +++ b/Octokit/Clients/Enterprise/EnterpriseClient.cs @@ -15,6 +15,7 @@ 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 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 00000000..6357da50 --- /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 899751c0..28a5db22 100644 --- a/Octokit/Clients/Enterprise/IEnterpriseClient.cs +++ b/Octokit/Clients/Enterprise/IEnterpriseClient.cs @@ -16,6 +16,14 @@ /// 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 00000000..68f30cdd --- /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 30436b85..2c7e131c 100644 --- a/Octokit/Helpers/ApiUrls.cs +++ b/Octokit/Helpers/ApiUrls.cs @@ -1664,6 +1664,26 @@ namespace Octokit 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 5821e327..33a77f6e 100644 --- a/Octokit/Http/ApiConnection.cs +++ b/Octokit/Http/ApiConnection.cs @@ -161,6 +161,21 @@ namespace Octokit 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 071758a5..dabee271 100644 --- a/Octokit/Http/Connection.cs +++ b/Octokit/Http/Connection.cs @@ -237,6 +237,13 @@ namespace Octokit 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 c111707b..5805df83 100644 --- a/Octokit/Http/IApiConnection.cs +++ b/Octokit/Http/IApiConnection.cs @@ -100,6 +100,15 @@ namespace Octokit /// 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 69bdde9b..137f488e 100644 --- a/Octokit/Http/IConnection.cs +++ b/Octokit/Http/IConnection.cs @@ -105,6 +105,15 @@ namespace Octokit /// 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 00000000..d5832728 --- /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 00000000..2ed74f9a --- /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 314c1b0a..0cb7d7df 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 @@ namespace Octokit { 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 @@ namespace Octokit MembersCount = membersCount; ReposCount = reposCount; Organization = organization; + LdapDistinguishedName = ldapDistinguishedName; } /// @@ -58,6 +61,12 @@ namespace Octokit /// 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 31b3d40a..14bc3e96 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 @@ namespace Octokit { 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 @@ namespace Octokit /// 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 de0d43fa..674e22ac 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 8ce11a6e..15d146fc 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 5add86a9..98e4beee 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 8a39aea9..eedca311 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 5dc2d664..8478f6f5 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 d6ef33fa..45a35727 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 +