Skip to content

Commit

Permalink
Merge branch 'release/0.2.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
Jericho committed May 11, 2020
2 parents c43573c + 32e4aa9 commit 9c3eeeb
Show file tree
Hide file tree
Showing 14 changed files with 313 additions and 61 deletions.
2 changes: 1 addition & 1 deletion Source/ZoomNet.IntegrationTests/IIntegrationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ namespace ZoomNet.IntegrationTests
{
public interface IIntegrationTest
{
Task RunAsync(string userId, IClient client, TextWriter log, CancellationToken cancellationToken);
Task RunAsync(string userId, IZoomClient client, TextWriter log, CancellationToken cancellationToken);
}
}
2 changes: 1 addition & 1 deletion Source/ZoomNet.IntegrationTests/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ private static LoggingConfiguration GetNLogConfiguration()
{
var logzioTarget = new LogzioTarget { Token = logzioToken };
logzioTarget.ContextProperties.Add(new TargetPropertyWithContext("source", "ZoomNet_integration_tests"));
logzioTarget.ContextProperties.Add(new TargetPropertyWithContext("ZoomNet-Version", ZoomNet.Client.Version));
logzioTarget.ContextProperties.Add(new TargetPropertyWithContext("ZoomNet-Version", ZoomNet.ZoomClient.Version));

nLogConfig.AddTarget("Logzio", logzioTarget);
nLogConfig.AddRule(NLog.LogLevel.Debug, NLog.LogLevel.Fatal, "Logzio", "*");
Expand Down
2 changes: 1 addition & 1 deletion Source/ZoomNet.IntegrationTests/Tests/Meetings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace ZoomNet.IntegrationTests.Tests
{
public class Meetings : IIntegrationTest
{
public async Task RunAsync(string userId, IClient client, TextWriter log, CancellationToken cancellationToken)
public async Task RunAsync(string userId, IZoomClient client, TextWriter log, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested) return;

Expand Down
30 changes: 26 additions & 4 deletions Source/ZoomNet.IntegrationTests/TestsRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ private enum ResultCodes
Cancelled = 1223
}

private enum ConnectionMethods
{
Jwt = 0,
OAuth = 1
}

private readonly ILoggerFactory _loggerFactory;

public TestsRunner(ILoggerFactory loggerFactory)
Expand All @@ -35,14 +41,30 @@ public async Task<int> RunAsync()
// -----------------------------------------------------------------------------
// Do you want to proxy requests through Fiddler? Can be useful for debugging.
var useFiddler = true;
// -----------------------------------------------------------------------------

// Do you want to use JWT or OAuth?
var connectionMethod = ConnectionMethods.OAuth;
// -----------------------------------;------------------------------------------

// Configure ZoomNet client
var apiKey = Environment.GetEnvironmentVariable("ZOOM_APIKEY");
var apiSecret = Environment.GetEnvironmentVariable("ZOOM_APISECRET");
IConnectionInfo connectionInfo;
if (connectionMethod == ConnectionMethods.Jwt)
{
var apiKey = Environment.GetEnvironmentVariable("ZOOM_JWT_APIKEY");
var apiSecret = Environment.GetEnvironmentVariable("ZOOM_JWT_APISECRET");
connectionInfo = new JwtConnectionInfo(apiKey, apiSecret);
}
else
{
var clientId = Environment.GetEnvironmentVariable("ZOOM_OAUTH_CLIENTID");
var clientSecret = Environment.GetEnvironmentVariable("ZOOM_OAUTH_CLIENTSECRET");
var authorizationCode = Environment.GetEnvironmentVariable("ZOOM_OAUTH_AUTHORIZATIONCODE");
connectionInfo = new OAuthConnectionInfo(clientId, clientSecret, authorizationCode);
}

var userId = Environment.GetEnvironmentVariable("ZOOM_USERID");
var proxy = useFiddler ? new WebProxy("http://localhost:8888") : null;
var client = new Client(apiKey, apiSecret, proxy, null, _loggerFactory.CreateLogger<Client>());
var client = new ZoomClient(connectionInfo, proxy, null, _loggerFactory.CreateLogger<ZoomClient>());

// Configure Console
var source = new CancellationTokenSource();
Expand Down
6 changes: 3 additions & 3 deletions Source/ZoomNet.sln
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.421
# Visual Studio Version 16
VisualStudioVersion = 16.0.30011.22
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZoomNet", "ZoomNet\ZoomNet.csproj", "{1F1336D3-20EE-4EFD-868B-A5FD6E9F260D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZoomNet.IntegrationTests", "ZoomNet.IntegrationTests\ZoomNet.IntegrationTests.csproj", "{86BE46FC-FD82-45B3-8092-0C9AC1A94E8A}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZoomNet.IntegrationTests", "ZoomNet.IntegrationTests\ZoomNet.IntegrationTests.csproj", "{86BE46FC-FD82-45B3-8092-0C9AC1A94E8A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{8D45A893-7A48-4D45-9FD7-424B12D4C672}"
EndProject
Expand Down
9 changes: 9 additions & 0 deletions Source/ZoomNet/IConnectionInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace ZoomNet
{
/// <summary>
/// Interface for connection information.
/// </summary>
public interface IConnectionInfo
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace ZoomNet
/// <summary>
/// Interface for the Zoom REST client.
/// </summary>
public interface IClient
public interface IZoomClient
{
/// <summary>
/// Gets the resource which allows you to manage meetings.
Expand Down
29 changes: 29 additions & 0 deletions Source/ZoomNet/JwtConnectionInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace ZoomNet
{
/// <summary>
/// Connect using JWT.
/// </summary>
public class JwtConnectionInfo : IConnectionInfo
{
/// <summary>
/// Gets the API Key.
/// </summary>
public string ApiKey { get; }

/// <summary>
/// Gets the API Secret.
/// </summary>
public string ApiSecret { get; }

/// <summary>
/// Initializes a new instance of the <see cref="JwtConnectionInfo"/> class.
/// </summary>
/// <param name="apiKey">Your JWT app API Key.</param>
/// <param name="apiSecret">Your JWT app API Secret.</param>
public JwtConnectionInfo(string apiKey, string apiSecret)
{
ApiKey = apiKey;
ApiSecret = apiSecret;
}
}
}
22 changes: 22 additions & 0 deletions Source/ZoomNet/Models/OAuthGrantType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.Runtime.Serialization;

namespace ZoomNet.Models
{
/// <summary>
/// Enumeration to indicate the OAuth grant type.
/// </summary>
public enum OAuthGrantType
{
/// <summary>
/// Authorization code. This is the most commonly used grant type for Zoom APIs.
/// </summary>
[EnumMember(Value = "authorization_code")]
AuthorizationCode,

/// <summary>
/// Client Credentials.
/// </summary>
[EnumMember(Value = "client_credentials")]
ClientCredentials
}
}
70 changes: 70 additions & 0 deletions Source/ZoomNet/OAuthConnectionInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System;
using ZoomNet.Models;

namespace ZoomNet
{
/// <summary>
/// Connect using OAuth.
/// </summary>
public class OAuthConnectionInfo : IConnectionInfo
{
/// <summary>
/// Gets the client id.
/// </summary>
public string ClientId { get; }

/// <summary>
/// Gets the client secret.
/// </summary>
public string ClientSecret { get; }

/// <summary>
/// Gets the grant type.
/// </summary>
public OAuthGrantType GrantType { get; }

/// <summary>
/// Gets the authorization code.
/// </summary>
/// <remarks>This value is relevant only if the grant type is "AuthorizationCode".</remarks>
public string AuthorizationCode { get; }

/// <summary>
/// Initializes a new instance of the <see cref="OAuthConnectionInfo"/> class.
/// </summary>
/// <remarks>
/// This constructor is used to get access token for APIs that do not
/// need a user’s permission, but rather a service’s permission.
/// Within the realm of Zoom APIs, Client Credentials grant should be
/// used to get access token from the Chatbot Service in order to use
/// the "Send Chatbot Messages API". See the "Using OAuth 2.0 / Client
/// Credentials" section in the "Using Zoom APIs" document for more details
/// (https://marketplace.zoom.us/docs/api-reference/using-zoom-apis).
/// </remarks>
/// <param name="clientId">Your Client Id.</param>
/// <param name="clientSecret">Your Client Secret.</param>
public OAuthConnectionInfo(string clientId, string clientSecret)
{
ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
ClientSecret = clientSecret ?? throw new ArgumentNullException(nameof(clientSecret));
GrantType = OAuthGrantType.ClientCredentials;
}

/// <summary>
/// Initializes a new instance of the <see cref="OAuthConnectionInfo"/> class.
/// </summary>
/// <remarks>
/// This is the most commonly used grant type for Zoom APIs.
/// </remarks>
/// <param name="clientId">Your Client Id.</param>
/// <param name="clientSecret">Your Client Secret.</param>
/// <param name="authorizationCode">The authorization code.</param>
public OAuthConnectionInfo(string clientId, string clientSecret, string authorizationCode)
{
ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
ClientSecret = clientSecret ?? throw new ArgumentNullException(nameof(clientSecret));
AuthorizationCode = authorizationCode ?? throw new ArgumentNullException(nameof(authorizationCode));
GrantType = OAuthGrantType.AuthorizationCode;
}
}
}
5 changes: 2 additions & 3 deletions Source/ZoomNet/Utilities/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -360,9 +360,9 @@ public static void AddPropertyIfValue<T>(this JObject jsonObject, string propert
jsonObject.Add(propertyName, JArray.FromObject(value.ToArray(), jsonSerializer));
}

public static T GetPropertyValue<T>(this JToken item, string name)
public static T GetPropertyValue<T>(this JToken item, string name, T defaultValue = default)
{
if (item[name] == null) return default;
if (item[name] == null) return defaultValue;
return item[name].Value<T>();
}

Expand Down Expand Up @@ -507,7 +507,6 @@ public static void CheckForZoomErrors(this IResponse response)
{
try
{
// Check for the presence of property called 'errors'
var jObject = JObject.Parse(responseContent);
var codeProperty = jObject["code"];
var messageProperty = jObject["message"];
Expand Down
29 changes: 16 additions & 13 deletions Source/ZoomNet/Utilities/JwtTokenHandler.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Jose;
using Jose;
using Pathoschild.Http.Client;
using Pathoschild.Http.Client.Extensibility;
using System;
Expand All @@ -15,20 +15,22 @@ internal class JwtTokenHandler : IHttpFilter
{
private static readonly object _lock = new object();

private readonly string _apiKey;
private readonly string _apiSecret;
private readonly TimeSpan _clockSkew = TimeSpan.FromMinutes(5);
private readonly TimeSpan _tokenLifeSpan = TimeSpan.FromMinutes(30);
private readonly DateTime _jwtTokenExpiration = DateTime.MinValue;
private readonly JwtConnectionInfo _connectionInfo;
private readonly TimeSpan _clockSkew;
private readonly TimeSpan _tokenLifeSpan;

private string _jwtToken;
private DateTime _tokenExpiration;

public JwtTokenHandler(string apiKey, string apiSecret, TimeSpan? tokenLifeSpan = null, TimeSpan? clockSkew = null)
public JwtTokenHandler(JwtConnectionInfo connectionInfo, TimeSpan? tokenLifeSpan = null, TimeSpan? clockSkew = null)
{
_apiKey = apiKey;
_apiSecret = apiSecret;
if (string.IsNullOrEmpty(connectionInfo.ApiKey)) throw new ArgumentNullException(nameof(connectionInfo.ApiKey));
if (string.IsNullOrEmpty(connectionInfo.ApiSecret)) throw new ArgumentNullException(nameof(connectionInfo.ApiSecret));

_connectionInfo = connectionInfo;
_tokenLifeSpan = tokenLifeSpan.GetValueOrDefault(TimeSpan.FromMinutes(30));
_clockSkew = clockSkew.GetValueOrDefault(TimeSpan.FromMinutes(5));
_tokenExpiration = DateTime.MinValue;
}

/// <summary>Method invoked just before the HTTP request is submitted. This method can modify the outgoing HTTP request.</summary>
Expand All @@ -52,20 +54,21 @@ private void RefreshTokenIfNecessary()
{
if (TokenIsExpired())
{
_tokenExpiration = DateTime.UtcNow.Add(_tokenLifeSpan);
var jwtPayload = new Dictionary<string, object>()
{
{ "iss", _apiKey },
{ "exp", DateTime.UtcNow.Add(_tokenLifeSpan).ToUnixTime() }
{ "iss", _connectionInfo.ApiKey },
{ "exp", _tokenExpiration.ToUnixTime() }
};
_jwtToken = JWT.Encode(jwtPayload, Encoding.ASCII.GetBytes(_apiSecret), JwsAlgorithm.HS256);
_jwtToken = JWT.Encode(jwtPayload, Encoding.ASCII.GetBytes(_connectionInfo.ApiSecret), JwsAlgorithm.HS256);
}
}
}
}

private bool TokenIsExpired()
{
return _jwtTokenExpiration <= DateTime.UtcNow.Add(_clockSkew);
return _tokenExpiration <= DateTime.UtcNow.Add(_clockSkew);
}
}
}
Loading

0 comments on commit 9c3eeeb

Please sign in to comment.