Skip to content

Commit

Permalink
Merge pull request #115 from Nexmo/secretapi
Browse files Browse the repository at this point in the history
Implement API Secret calls
  • Loading branch information
Rabeb Othmani committed Sep 28, 2018
2 parents b395a29 + d768981 commit 7a7a3ab
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 3 deletions.
98 changes: 98 additions & 0 deletions Nexmo.Api/ApiSecret.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.Net;
using Newtonsoft.Json;
using Nexmo.Api.Request;

namespace Nexmo.Api
{
public static class ApiSecret
{
public class SecretList
{
[JsonProperty("secrets")]
public List<Secret> Secrets { get; set; }
}

public class Secret
{
public HALLinks _links { get; set; }
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("created_at")]
public DateTime? CreatedAt { get; set; }
}

public class SecretRequest
{
[JsonProperty("secret")]
public string Secret { get; set; }
}

/// <summary>
/// List the secrets associated with the provided api key.
/// </summary>
/// <param name="apiKey">The API key to manage secrets for</param>
/// <returns>List of secrets</returns>
public static List<Secret> ListSecrets(string apiKey, Credentials creds = null)
{
var json = ApiRequest.DoRequest(ApiRequest.GetBaseUriFor(typeof(ApiSecret),
$"/accounts/{apiKey}/secrets"),
// TODO: using this method sig allows us to have the api auth injected at the expense of opaque code here
new Dictionary<string, string>(),
creds);

var response = JsonConvert.DeserializeObject<Response<SecretList>>(json);
return response._embedded.Secrets;
}

/// <summary>
/// Obtain the details of the secret identified by the provided secret ID.
/// </summary>
/// <param name="apiKey">The API key to manage secrets for</param>
/// <param name="secretId">ID of the API Secret</param>
/// <returns>The secret</returns>
public static Secret GetSecret(string apiKey, string secretId, Credentials creds = null)
{
var json = ApiRequest.DoRequest(ApiRequest.GetBaseUriFor(typeof(ApiSecret),
$"/accounts/{apiKey}/secrets/{secretId}"),
// TODO: using this method sig allows us to have the api auth injected at the expense of opaque code here
new Dictionary<string, string>(),
creds);

return JsonConvert.DeserializeObject<Secret>(json);
}

/// <summary>
/// Create a secret with the details provided in new secret.
/// </summary>
/// <param name="apiKey">The API key to manage secrets for</param>
/// <param name="newSecret">The new secret must follow these rules:
/// minimum 8 characters
/// maximum 25 characters
/// minimum 1 lower case character
/// minimum 1 upper case character
/// minimum 1 digit
/// </param>
/// <returns>The created secret</returns>
public static Secret CreateSecret(string apiKey, string newSecret, Credentials creds = null)
{
var response = VersionedApiRequest.DoRequest("POST", ApiRequest.GetBaseUriFor(typeof(ApiSecret), $"/accounts/{apiKey}/secrets"), new SecretRequest { Secret = newSecret }, creds);

return JsonConvert.DeserializeObject<Secret>(response.JsonResponse);
}

/// <summary>
/// Delete the secret associated with the provided secret ID.
/// </summary>
/// <param name="apiKey">The API key to manage secrets for</param>
/// <param name="secretId">ID of the API Secret</param>
public static bool DeleteSecret(string apiKey, string secretId, Credentials creds = null)
{
var response = VersionedApiRequest.DoRequest("DELETE", ApiRequest.GetBaseUriFor(typeof(ApiSecret),
$"/accounts/{apiKey}/secrets/{secretId}"), null, creds);

return response.Status == HttpStatusCode.NoContent;
}
}
}
67 changes: 67 additions & 0 deletions Nexmo.Api/Client/ApiSecret.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System.Collections.Generic;
using Nexmo.Api.Request;

namespace Nexmo.Api.ClientMethods
{
public class ApiSecret
{
public Credentials Credentials { get; set; }
public ApiSecret(Credentials creds)
{
Credentials = creds;
}

/// <summary>
/// List the secrets associated with the provided api key.
/// </summary>
/// <param name="apiKey">The API key to manage secrets for</param>
/// <param name="creds">(Optional) Overridden credentials for only this request</param>
/// <returns>List of secrets</returns>
public List<Api.ApiSecret.Secret> List(string apiKey, Credentials creds = null)
{
return Api.ApiSecret.ListSecrets(apiKey, creds ?? Credentials);
}

/// <summary>
/// Obtain the details of the secret identified by the provided secret ID.
/// </summary>
/// <param name="apiKey">The API key to manage secrets for</param>
/// <param name="secretId">ID of the API Secret</param>
/// <param name="creds">(Optional) Overridden credentials for only this request</param>
/// <returns>The secret</returns>
public Api.ApiSecret.Secret Get(string apiKey, string secretId, Credentials creds = null)
{
return Api.ApiSecret.GetSecret(apiKey, secretId, creds ?? Credentials);
}

/// <summary>
/// Create a secret with the details provided in new secret.
/// </summary>
/// <param name="apiKey">The API key to manage secrets for</param>
/// <param name="newSecret">The new secret must follow these rules:
/// minimum 8 characters
/// maximum 25 characters
/// minimum 1 lower case character
/// minimum 1 upper case character
/// minimum 1 digit
/// </param>
/// <param name="creds">(Optional) Overridden credentials for only this request</param>
/// <returns>The created secret</returns>
public Api.ApiSecret.Secret Create(string apiKey, string newSecret, Credentials creds = null)
{
return Api.ApiSecret.CreateSecret(apiKey, newSecret, creds ?? Credentials);
}

/// <summary>
/// Delete the secret associated with the provided secret ID.
/// </summary>
/// <param name="apiKey">The API key to manage secrets for</param>
/// <param name="secretId">ID of the API Secret</param>
/// <param name="creds">(Optional) Overridden credentials for only this request</param>
/// <returns>True/False on delete success/failure</returns>
public bool Delete(string apiKey, string secretId, Credentials creds = null)
{
return Api.ApiSecret.DeleteSecret(apiKey, secretId, creds ?? Credentials);
}
}
}
2 changes: 2 additions & 0 deletions Nexmo.Api/Client/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public Client(Credentials creds)
private void PropagateCredentials()
{
Account = new ClientMethods.Account(Credentials);
ApiSecret = new ClientMethods.ApiSecret(Credentials);
Application = new ClientMethods.Application(Credentials);
Call = new ClientMethods.Call(Credentials);
Number = new ClientMethods.Number(Credentials);
Expand All @@ -40,6 +41,7 @@ private void PropagateCredentials()
}

public ClientMethods.Account Account { get; private set; }
public ClientMethods.ApiSecret ApiSecret { get; private set; }
public ClientMethods.Application Application { get; private set; }
public ClientMethods.Call Call { get; private set; }
public ClientMethods.Number Number { get; private set; }
Expand Down
10 changes: 10 additions & 0 deletions Nexmo.Api/Request/ApiRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ internal static Uri GetBaseUriFor(Type component, string url = null)
{
Uri baseUri;
if (typeof(NumberVerify) == component
|| typeof(ApiSecret) == component
|| typeof(Application) == component
|| typeof(Voice.Call) == component
|| typeof(Redact) == component)
Expand Down Expand Up @@ -132,6 +133,15 @@ internal static string DoRequest(Uri uri, Credentials creds)
};
VersionedApiRequest.SetUserAgent(ref req, creds);

// do we need to use basic auth?
// TODO / HACK: this is a newer auth method that needs to be incorporated better in the future
if (uri.AbsolutePath.StartsWith("/accounts/"))
{
var authBytes = Encoding.UTF8.GetBytes(creds.ApiKey + ":" + creds.ApiSecret);
req.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic",
Convert.ToBase64String(authBytes));
}

using (LogProvider.OpenMappedContext("ApiRequest.DoRequest",uri.GetHashCode()))
{
Logger.Debug($"GET {uri}");
Expand Down
8 changes: 8 additions & 0 deletions Nexmo.Api/Request/NexmoResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ public class PaginatedResponse<T> where T : class

////////
// TODO: Handle HAL better

public class Response<T> where T : class
{
public HALLinks _links { get; set; }
public T _embedded { get; set; }
}

public class Link
{
public string href { get; set; }
Expand All @@ -31,5 +38,6 @@ public class HALLinks
public Link first { get; set; }
public Link last { get; set; }
}

////////
}
18 changes: 15 additions & 3 deletions Nexmo.Api/Request/VersionedApiRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,21 @@ public static NexmoResponse DoRequest(string method, Uri uri, object payload, Cr
Method = new HttpMethod(method),
};
SetUserAgent(ref req, creds);
// attempt bearer token auth
req.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer",
Jwt.CreateToken(appId, appKeyPath));

// do we need to use basic auth?
// TODO / HACK: this is a newer auth method that needs to be incorporated better in the future
if (uri.AbsolutePath.StartsWith("/accounts/"))
{
var authBytes = Encoding.UTF8.GetBytes(creds.ApiKey + ":" + creds.ApiSecret);
req.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic",
Convert.ToBase64String(authBytes));
}
else
{
// attempt bearer token auth
req.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer",
Jwt.CreateToken(appId, appKeyPath));
}

var data = Encoding.ASCII.GetBytes(JsonConvert.SerializeObject(payload,
Formatting.None, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore }));
Expand Down

0 comments on commit 7a7a3ab

Please sign in to comment.