Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat]: Implement Actions OIDC Client #2828

Merged
merged 3 commits into from
Jan 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Octokit.Reactive/Clients/IObservableActionsClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ public interface IObservableActionsClient
/// </summary>
IObservableActionsCacheClient Cache { get; }

/// <summary>
/// Client for the OIDC API.
/// </summary>
IObservableActionsOidcClient Oidc { get; }

/// <summary>
/// Client for the Permissions API.
/// </summary>
Expand Down
58 changes: 58 additions & 0 deletions Octokit.Reactive/Clients/IObservableActionsOidcClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System;
using System.Reactive;
using System.Threading.Tasks;

namespace Octokit.Reactive
{
/// <summary>
/// A client for GitHub's Actions OIDC API.
/// </summary>
/// <remarks>
/// See the <a href="https://developer.github.com/v3/actions/oidc/">Actions OIDC API documentation</a> for more information.
/// </remarks>
public interface IObservableActionsOidcClient
{
/// <summary>
/// Get the customization template for an OIDC subject claim for an organization.
/// </summary>
/// <remarks>
/// https://docs.github.com/en/rest/actions/oidc?apiVersion=2022-11-28#get-the-customization-template-for-an-oidc-subject-claim-for-an-organization
/// </remarks>
/// <param name="organization">The organization name.</param>
IObservable<OrganizationOidcSubjectClaim> GetOrganizationOidcSubjectClaim(string organization);


/// <summary>
/// Set the customization template for an OIDC subject claim for an organization.
/// </summary>
/// <remarks>
/// https://docs.github.com/en/rest/actions/oidc?apiVersion=2022-11-28#set-the-customization-template-for-an-oidc-subject-claim-for-an-organization
/// </remarks>
/// <param name="organization">The organization name.</param>
/// <param name="oidcSubjectClaim">The OIDC subject claim to set for the organization.</param>
IObservable<Unit> SetOrganizationOidcSubjectClaim(string organization, OrganizationOidcSubjectClaimRequest oidcSubjectClaim);

/// <summary>
/// Get the customization template for an OIDC subject claim for a repository.
/// </summary>
/// <remarks>
/// https://docs.github.com/en/rest/actions/oidc?apiVersion=2022-11-28#get-the-customization-template-for-an-oidc-subject-claim-for-a-repository
/// </remarks>
/// <param name="owner">The account owner of the repository.</param>
/// <param name="repository">The name of the repository.</param>
/// <returns></returns>
IObservable<RepositoryOidcSubjectClaim> GetRepositoryOidcSubjectClaim(string owner, string repository);

/// <summary>
///
/// </summary>
/// <remarks>
/// https://docs.github.com/en/rest/actions/oidc?apiVersion=2022-11-28#set-the-customization-template-for-an-oidc-subject-claim-for-a-repository
/// </remarks>
/// <param name="owner">The account owner of the repository.</param>
/// <param name="repository">The name of the repository.</param>
/// <param name="oidcSubjectClaim">The OIDC subject claim to set for the repository.</param>
/// <returns></returns>
IObservable<Unit> SetRepositoryOidcSubjectClaim(string owner, string repository, RepositoryOidcSubjectClaimRequest oidcSubjectClaim);
}
}
7 changes: 7 additions & 0 deletions Octokit.Reactive/Clients/ObservableActionsClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public ObservableActionsClient(IGitHubClient client)

Artifacts = new ObservableActionsArtifactsClient(client);
Cache = new ObservableActionsCacheClient(client);
Oidc = new ObservableActionsOidcClient(client);
Permissions = new ObservableActionsPermissionsClient(client);
SelfHostedRunnerGroups = new ObservableActionsSelfHostedRunnerGroupsClient(client);
SelfHostedRunners = new ObservableActionsSelfHostedRunnersClient(client);
Expand All @@ -34,6 +35,12 @@ public ObservableActionsClient(IGitHubClient client)
/// </summary>
public IObservableActionsCacheClient Cache { get; private set; }


/// <summary>
/// Client for the OIDC API.
/// </summary>
public IObservableActionsOidcClient Oidc { get; private set; }

/// <summary>
/// Client for the Permissions API.
/// </summary>
Expand Down
66 changes: 66 additions & 0 deletions Octokit.Reactive/Clients/ObservableActionsOidcClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System;
using System.Reactive;
using System.Reactive.Threading.Tasks;

namespace Octokit.Reactive
{

/// <summary>
/// A client for GitHub's Actions OIDC API.
/// </summary>
/// <remarks>
/// See the <a href="https://developer.github.com/v3/actions/oidc/">Actions OIDC API documentation</a> for more information.
/// </remarks>
public class ObservableActionsOidcClient : IObservableActionsOidcClient
{
readonly IActionsOidcClient _client;


/// <summary>
/// Initializes a new GitHub Actions OIDC API client
/// </summary>
/// <param name="client">A GitHub client.</param>
public ObservableActionsOidcClient(IGitHubClient client)
{
Ensure.ArgumentNotNull(client, nameof(client));

_client = client.Actions.Oidc;
}


/// <inheritdoc/>
public IObservable<OrganizationOidcSubjectClaim> GetOrganizationOidcSubjectClaim(string organization)
{
Ensure.ArgumentNotNullOrEmptyString(organization, nameof(organization));
return _client.GetOrganizationOidcSubjectClaim(organization).ToObservable();
}

/// <inheritdoc/>
public IObservable<Unit> SetOrganizationOidcSubjectClaim(string organization, OrganizationOidcSubjectClaimRequest oidcSubjectClaim)
{
Ensure.ArgumentNotNullOrEmptyString(organization, nameof(organization));
Ensure.ArgumentNotNull(oidcSubjectClaim, nameof(oidcSubjectClaim));

return _client.SetOrganizationOidcSubjectClaim(organization, oidcSubjectClaim).ToObservable();
}

/// <inheritdoc/>
public IObservable<RepositoryOidcSubjectClaim> GetRepositoryOidcSubjectClaim(string owner, string repository)
{
Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner));
Ensure.ArgumentNotNullOrEmptyString(repository, nameof(repository));

return _client.GetRepositoryOidcSubjectClaim(owner, repository).ToObservable();
}

/// <inheritdoc/>
public IObservable<Unit> SetRepositoryOidcSubjectClaim(string owner, string repository, RepositoryOidcSubjectClaimRequest oidcSubjectClaim)
{
Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner));
Ensure.ArgumentNotNullOrEmptyString(repository, nameof(repository));
Ensure.ArgumentNotNull(oidcSubjectClaim, nameof(oidcSubjectClaim));

return _client.SetRepositoryOidcSubjectClaim(owner, repository, oidcSubjectClaim).ToObservable();
}
}
}
165 changes: 165 additions & 0 deletions Octokit.Tests/Clients/ActionsOidcClientTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
using NSubstitute;
using Octokit.Clients;
using System;
using System.Threading.Tasks;
using Xunit;


namespace Octokit.Tests.Clients
{
public class ActionsOidcClientTests
{
public class TheCtor
{
[Fact]
public void EnsuresNonNullArguments()
{
Assert.Throws<ArgumentNullException>(() => new ActionsOidcClient(null));
}
}

public class GetOrganizationOidcSubjectClaim_Method
{
[Fact]
public async Task RequestsCorrectUrl()
{
var connection = Substitute.For<IApiConnection>();
var client = new ActionsOidcClient(connection);

await client.GetOrganizationOidcSubjectClaim("fake");

connection.Received().Get<OrganizationOidcSubjectClaim>(
Arg.Is<Uri>(u => u.ToString() == "orgs/fake/actions/oidc/customization/sub"));
}

[Fact]
public async Task EnsuresNonNullArguments()
{
var connection = Substitute.For<IApiConnection>();
var client = new ActionsOidcClient(connection);

await Assert.ThrowsAsync<ArgumentNullException>(() => client.GetOrganizationOidcSubjectClaim(null));
}

[Fact]
public async Task EnsuresNonEmptyArguments()
{
var connection = Substitute.For<IApiConnection>();
var client = new ActionsOidcClient(connection);

await Assert.ThrowsAsync<ArgumentException>(() => client.GetOrganizationOidcSubjectClaim(""));
}
}

public class SetOrganizationOidcSubjectClaim_Method
{
[Fact]
public async Task RequestsCorrectUrl()
{
var connection = Substitute.For<IApiConnection>();
var client = new ActionsOidcClient(connection);
var newClaims = new OrganizationOidcSubjectClaimRequest(new System.Collections.Generic.List<string> { "fake" });

await client.SetOrganizationOidcSubjectClaim("fake", newClaims);

connection.Received().Put(
Arg.Is<Uri>(u => u.ToString() == "orgs/fake/actions/oidc/customization/sub"), newClaims);
}

[Fact]
public async Task EnsuresNonNullArguments()
{
var connection = Substitute.For<IApiConnection>();
var client = new ActionsOidcClient(connection);
var newClaims = new OrganizationOidcSubjectClaimRequest(new System.Collections.Generic.List<string> { "fake" });

await Assert.ThrowsAsync<ArgumentNullException>(() => client.SetOrganizationOidcSubjectClaim(null, newClaims));
await Assert.ThrowsAsync<ArgumentNullException>(() => client.SetOrganizationOidcSubjectClaim("fake", null));
}

[Fact]
public async Task EnsuresNonEmptyArguments()
{
var connection = Substitute.For<IApiConnection>();
var client = new ActionsOidcClient(connection);
var newClaims = new OrganizationOidcSubjectClaimRequest(new System.Collections.Generic.List<string> { "fake" });

await Assert.ThrowsAsync<ArgumentException>(() => client.SetOrganizationOidcSubjectClaim("", newClaims));
}
}

public class GetRepositoryOidcSubjectClaim_Method
{
[Fact]
public async Task RequestsCorrectUrl()
{
var connection = Substitute.For<IApiConnection>();
var client = new ActionsOidcClient(connection);

await client.GetRepositoryOidcSubjectClaim("fake", "abc");

connection.Received().Get<RepositoryOidcSubjectClaim>(
Arg.Is<Uri>(u => u.ToString() == "repos/fake/abc/actions/oidc/customization/sub"));
}

[Fact]
public async Task EnsuresNonNullArguments()
{
var connection = Substitute.For<IApiConnection>();
var client = new ActionsOidcClient(connection);

await Assert.ThrowsAsync<ArgumentNullException>(() => client.GetRepositoryOidcSubjectClaim(null, "repo"));
await Assert.ThrowsAsync<ArgumentNullException>(() => client.GetRepositoryOidcSubjectClaim("owner", null));
}

[Fact]
public async Task EnsuresNonEmptyArguments()
{
var connection = Substitute.For<IApiConnection>();
var client = new ActionsOidcClient(connection);

await Assert.ThrowsAsync<ArgumentException>(() => client.GetRepositoryOidcSubjectClaim("", "repo"));
await Assert.ThrowsAsync<ArgumentException>(() => client.GetRepositoryOidcSubjectClaim("owner", ""));
}
}

public class SetRepositoryOidcSubjectClaim_Method
{
[Fact]
public async Task RequestsCorrectUrl()
{
var connection = Substitute.For<IApiConnection>();
var client = new ActionsOidcClient(connection);
var newClaims = new RepositoryOidcSubjectClaimRequest(false, new System.Collections.Generic.List<string> { "fake" });

await client.SetRepositoryOidcSubjectClaim("fake", "abc", newClaims);

connection.Received().Put(
Arg.Is<Uri>(u => u.ToString() == "repos/fake/abc/actions/oidc/customization/sub"), newClaims);
}

[Fact]
public async Task EnsuresNonNullArguments()
{
var connection = Substitute.For<IApiConnection>();
var client = new ActionsOidcClient(connection);
var newClaims = new RepositoryOidcSubjectClaimRequest(false, new System.Collections.Generic.List<string> { "fake" });

await Assert.ThrowsAsync<ArgumentNullException>(() => client.SetRepositoryOidcSubjectClaim(null, "repo", newClaims));
await Assert.ThrowsAsync<ArgumentNullException>(() => client.SetRepositoryOidcSubjectClaim("owner", null, newClaims));
await Assert.ThrowsAsync<ArgumentNullException>(() => client.SetRepositoryOidcSubjectClaim("owner", "repo", null));
}

[Fact]
public async Task EnsuresNonEmptyArguments()
{
var connection = Substitute.For<IApiConnection>();
var client = new ActionsOidcClient(connection);
var newClaims = new RepositoryOidcSubjectClaimRequest(false, new System.Collections.Generic.List<string> { "fake" });

await Assert.ThrowsAsync<ArgumentException>(() => client.SetRepositoryOidcSubjectClaim("", "repo", newClaims));
await Assert.ThrowsAsync<ArgumentException>(() => client.SetRepositoryOidcSubjectClaim("owner", "", newClaims));
}
}
}
}
10 changes: 9 additions & 1 deletion Octokit/Clients/ActionsClient.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Octokit
using Octokit.Clients;

namespace Octokit
{
/// <summary>
/// A client for GitHub's Actions API.
Expand All @@ -17,6 +19,7 @@ public ActionsClient(IApiConnection apiConnection)
{
Artifacts = new ActionsArtifactsClient(apiConnection);
Cache = new ActionsCacheClient(apiConnection);
Oidc = new ActionsOidcClient(apiConnection);
Permissions = new ActionsPermissionsClient(apiConnection);
SelfHostedRunnerGroups = new ActionsSelfHostedRunnerGroupsClient(apiConnection);
SelfHostedRunners = new ActionsSelfHostedRunnersClient(apiConnection);
Expand All @@ -33,6 +36,11 @@ public ActionsClient(IApiConnection apiConnection)
/// </summary>
public IActionsCacheClient Cache { get; private set; }

/// <summary>
/// Client for the OIDC API.
/// </summary>
public IActionsOidcClient Oidc { get; private set; }

/// <summary>
/// Client for the Permissions API.
/// </summary>
Expand Down
Loading
Loading