Skip to content

Commit

Permalink
Keys Sync implementation (#6490)
Browse files Browse the repository at this point in the history
* sync implementation

* changes from other PRs

* PR feedback
  • Loading branch information
maririos authored Jun 7, 2019
1 parent 756b072 commit 6d3cabc
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 23 deletions.
96 changes: 73 additions & 23 deletions sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeyClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ public KeyClient(Uri vaultUri, TokenCredential credential, KeyClientOptions opti

public virtual Response<Key> CreateKey(string name, KeyType keyType, KeyCreateOptions keyOptions = default, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
if (string.IsNullOrEmpty(name)) throw new ArgumentException($"{nameof(name)} can't be empty or null");
if (keyType == default) throw new ArgumentNullException(nameof(keyType));

var parameters = new KeyRequestParameters(keyType, keyOptions);

return SendRequest(HttpPipelineMethod.Put, parameters, () => new Key(name), cancellationToken, KeysPath, name, "create");
}

public virtual async Task<Response<Key>> CreateKeyAsync(string name, KeyType keyType, KeyCreateOptions keyOptions = default, CancellationToken cancellationToken = default)
Expand All @@ -53,12 +58,16 @@ public virtual async Task<Response<Key>> CreateKeyAsync(string name, KeyType key

var parameters = new KeyRequestParameters(keyType, keyOptions);

return await SendRequestAsync(HttpPipelineMethod.Put, parameters, () => new Key(name), cancellationToken, KeysPath, name);
return await SendRequestAsync(HttpPipelineMethod.Put, parameters, () => new Key(name), cancellationToken, KeysPath, name, "create");
}

public virtual Response<Key> CreateEcKey(EcKeyCreateOptions ecKey, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
if (ecKey == default) throw new ArgumentNullException(nameof(ecKey));

var parameters = new KeyRequestParameters(ecKey);

return SendRequest(HttpPipelineMethod.Put, parameters, () => new Key(ecKey.Name), cancellationToken, KeysPath, ecKey.Name, "create");
}

public virtual async Task<Response<Key>> CreateEcKeyAsync(EcKeyCreateOptions ecKey, CancellationToken cancellationToken = default)
Expand All @@ -67,12 +76,16 @@ public virtual async Task<Response<Key>> CreateEcKeyAsync(EcKeyCreateOptions ecK

var parameters = new KeyRequestParameters(ecKey);

return await SendRequestAsync(HttpPipelineMethod.Put, parameters, () => new Key(ecKey.Name), cancellationToken, KeysPath, ecKey.Name);
return await SendRequestAsync(HttpPipelineMethod.Put, parameters, () => new Key(ecKey.Name), cancellationToken, KeysPath, ecKey.Name, "create");
}

public virtual Response<Key> CreateRsaKey(RsaKeyCreateOptions rsaKey, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
if (rsaKey == default) throw new ArgumentNullException(nameof(rsaKey));

var parameters = new KeyRequestParameters(rsaKey);

return SendRequest(HttpPipelineMethod.Put, parameters, () => new Key(rsaKey.Name), cancellationToken, KeysPath, rsaKey.Name, "create");
}

public virtual async Task<Response<Key>> CreateRsaKeyAsync(RsaKeyCreateOptions rsaKey, CancellationToken cancellationToken = default)
Expand All @@ -81,12 +94,17 @@ public virtual async Task<Response<Key>> CreateRsaKeyAsync(RsaKeyCreateOptions r

var parameters = new KeyRequestParameters(rsaKey);

return await SendRequestAsync(HttpPipelineMethod.Put, parameters, () => new Key(rsaKey.Name), cancellationToken, KeysPath, rsaKey.Name);
return await SendRequestAsync(HttpPipelineMethod.Put, parameters, () => new Key(rsaKey.Name), cancellationToken, KeysPath, rsaKey.Name, "create");
}

public virtual Response<Key> UpdateKey(KeyBase key, IEnumerable<KeyOperations> keyOperations, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
if (string.IsNullOrEmpty(key?.Name)) throw new ArgumentException($"{nameof(key.Name)} can't be empty or null");
if (string.IsNullOrEmpty(key?.Version)) throw new ArgumentException($"{nameof(key.Version)} can't be empty or null");

var parameters = new KeyRequestParameters(key, keyOperations);

return SendRequest(HttpPipelineMethod.Patch, parameters, () => new Key(key.Name), cancellationToken, KeysPath, key.Name, key.Version);
}

public virtual async Task<Response<Key>> UpdateKeyAsync(KeyBase key, IEnumerable<KeyOperations> keyOperations, CancellationToken cancellationToken = default)
Expand All @@ -101,7 +119,9 @@ public virtual async Task<Response<Key>> UpdateKeyAsync(KeyBase key, IEnumerable

public virtual Response<Key> GetKey(string name, string version = null, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
if (string.IsNullOrEmpty(name)) throw new ArgumentException($"{nameof(name)} can't be empty or null");

return SendRequest(HttpPipelineMethod.Get, () => new Key(name), cancellationToken, KeysPath, name, version);
}

public virtual async Task<Response<Key>> GetKeyAsync(string name, string version = null, CancellationToken cancellationToken = default)
Expand All @@ -113,7 +133,9 @@ public virtual async Task<Response<Key>> GetKeyAsync(string name, string version

public virtual IEnumerable<Response<KeyBase>> GetKeys(CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
Uri firstPageUri = CreateFirstPageUri(KeysPath);

return PageResponseEnumerator.CreateEnumerable(nextLink => GetPage(firstPageUri, nextLink, () => new KeyBase(), cancellationToken));
}

public virtual IAsyncEnumerable<Response<KeyBase>> GetKeysAsync(CancellationToken cancellationToken = default)
Expand All @@ -125,33 +147,41 @@ public virtual IAsyncEnumerable<Response<KeyBase>> GetKeysAsync(CancellationToke

public virtual IEnumerable<Response<KeyBase>> GetKeyVersions(string name, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
if (string.IsNullOrEmpty(name)) throw new ArgumentException($"{nameof(name)} can't be empty or null");

Uri firstPageUri = CreateFirstPageUri($"{KeysPath}{name}/versions");

return PageResponseEnumerator.CreateEnumerable(nextLink => GetPage(firstPageUri, nextLink, () => new KeyBase(), cancellationToken));
}

public virtual IAsyncEnumerable<Response<KeyBase>> GetKeyVersionsAsync(string name, CancellationToken cancellationToken = default)
{
if (string.IsNullOrEmpty(name)) throw new ArgumentException($"{nameof(name)} can't be empty or null");

Uri firstPageUri = CreateFirstPageUri(KeysPath);
Uri firstPageUri = CreateFirstPageUri($"{KeysPath}{name}/versions");

return PageResponseEnumerator.CreateAsyncEnumerable(nextLink => GetPageAsync(firstPageUri, nextLink, () => new KeyBase(), cancellationToken));
}

public virtual Response<DeletedKey> GetDeletedKey(string name, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
if (string.IsNullOrEmpty(name)) throw new ArgumentException($"{nameof(name)} can't be empty or null");

return SendRequest(HttpPipelineMethod.Get, () => new DeletedKey(name), cancellationToken, DeletedKeysPath, name);
}

public virtual async Task<Response<DeletedKey>> GetDeletedKeyAsync(string name, CancellationToken cancellationToken = default)
{
if (string.IsNullOrEmpty(name)) throw new ArgumentException($"{nameof(name)} can't be empty or null");

return await SendRequestAsync(HttpPipelineMethod.Get, () => new DeletedKey(name), cancellationToken, KeysPath, name);
return await SendRequestAsync(HttpPipelineMethod.Get, () => new DeletedKey(name), cancellationToken, DeletedKeysPath, name);
}

public virtual Response<DeletedKey> DeleteKey(string name, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
if (string.IsNullOrEmpty(name)) throw new ArgumentException($"{nameof(name)} can't be empty or null");

return SendRequest(HttpPipelineMethod.Delete, () => new DeletedKey(name), cancellationToken, KeysPath, name);
}

public virtual async Task<Response<DeletedKey>> DeleteKeyAsync(string name, CancellationToken cancellationToken = default)
Expand All @@ -163,7 +193,9 @@ public virtual async Task<Response<DeletedKey>> DeleteKeyAsync(string name, Canc

public virtual IEnumerable<Response<DeletedKey>> GetDeletedKeys(CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
Uri firstPageUri = CreateFirstPageUri(DeletedKeysPath);

return PageResponseEnumerator.CreateEnumerable(nextLink => GetPage(firstPageUri, nextLink, () => new DeletedKey(), cancellationToken));
}

public virtual IAsyncEnumerable<Response<DeletedKey>> GetDeletedKeysAsync(CancellationToken cancellationToken = default)
Expand All @@ -175,7 +207,9 @@ public virtual IAsyncEnumerable<Response<DeletedKey>> GetDeletedKeysAsync(Cancel

public virtual Response PurgeDeletedKey(string name, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
if (string.IsNullOrEmpty(name)) throw new ArgumentException($"{nameof(name)} can't be empty or null");

return SendRequest(HttpPipelineMethod.Delete, cancellationToken, DeletedKeysPath, name);
}

public virtual async Task<Response> PurgeDeletedKeyAsync(string name, CancellationToken cancellationToken = default)
Expand All @@ -187,18 +221,25 @@ public virtual async Task<Response> PurgeDeletedKeyAsync(string name, Cancellati

public virtual Response<Key> RecoverDeletedKey(string name, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
if (string.IsNullOrEmpty(name)) throw new ArgumentException($"{nameof(name)} can't be empty or null");

return SendRequest(HttpPipelineMethod.Post, () => new Key(name), cancellationToken, DeletedKeysPath, name, "recover");
}

public virtual async Task<Response<Key>> RecoverDeletedKeyAsync(string name, CancellationToken cancellationToken = default)
{
await Task.CompletedTask;
throw new NotImplementedException();
if (string.IsNullOrEmpty(name)) throw new ArgumentException($"{nameof(name)} can't be empty or null");

return await SendRequestAsync(HttpPipelineMethod.Post, () => new Key(name), cancellationToken, DeletedKeysPath, name, "recover");
}

public virtual Response<byte[]> BackupKey(string name, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
if (string.IsNullOrEmpty(name)) throw new ArgumentException($"{nameof(name)} can't be empty or null");

var backup = SendRequest(HttpPipelineMethod.Post, () => new KeyBackup(), cancellationToken, KeysPath, name, "backup");

return new Response<byte[]>(backup.GetRawResponse(), backup.Value.Value);
}

public virtual async Task<Response<byte[]>> BackupKeyAsync(string name, CancellationToken cancellationToken = default)
Expand All @@ -212,7 +253,9 @@ public virtual async Task<Response<byte[]>> BackupKeyAsync(string name, Cancella

public virtual Response<Key> RestoreKey(byte[] backup, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
if (backup == null) throw new ArgumentNullException(nameof(backup));

return SendRequest(HttpPipelineMethod.Post, new KeyBackup { Value = backup }, () => new Key(), cancellationToken, KeysPath, "restore");
}

public virtual async Task<Response<Key>> RestoreKeyAsync(byte[] backup, CancellationToken cancellationToken = default)
Expand All @@ -224,7 +267,12 @@ public virtual async Task<Response<Key>> RestoreKeyAsync(byte[] backup, Cancella

public virtual Response<Key> ImportKey(string name, JsonWebKey keyMaterial, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
if (string.IsNullOrEmpty(name)) throw new ArgumentException($"{nameof(name)} can't be empty or null");
if (keyMaterial == default) throw new ArgumentNullException(nameof(keyMaterial));

var keyImportOptions = new KeyImportOptions(name, keyMaterial);

return SendRequest(HttpPipelineMethod.Put, keyImportOptions, () => new Key(name), cancellationToken, KeysPath, name);
}

public virtual async Task<Response<Key>> ImportKeyAsync(string name, JsonWebKey keyMaterial, CancellationToken cancellationToken = default)
Expand All @@ -239,7 +287,9 @@ public virtual async Task<Response<Key>> ImportKeyAsync(string name, JsonWebKey

public virtual Response<Key> ImportKey(KeyImportOptions keyImportOptions, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
if (keyImportOptions == default) throw new ArgumentNullException(nameof(keyImportOptions));

return SendRequest(HttpPipelineMethod.Put, keyImportOptions, () => new Key(keyImportOptions.Name), cancellationToken, KeysPath, keyImportOptions.Name);
}

public virtual async Task<Response<Key>> ImportKeyAsync(KeyImportOptions keyImportOptions, CancellationToken cancellationToken = default)
Expand Down
69 changes: 69 additions & 0 deletions sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeyClient_private.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,53 @@ private async Task<Response> SendRequestAsync(Request request, CancellationToken
}
}

private Response<TResult> SendRequest<TContent, TResult>(HttpPipelineMethod method, TContent content, Func<TResult> resultFactory, CancellationToken cancellationToken, params string[] path)
where TContent : Model
where TResult : Model
{
using (Request request = CreateRequest(method, path))
{
request.Content = HttpPipelineRequestContent.Create(content.Serialize());

Response response = SendRequest(request, cancellationToken);

return CreateResponse(response, resultFactory());
}
}

private Response<TResult> SendRequest<TResult>(HttpPipelineMethod method, Func<TResult> resultFactory, CancellationToken cancellationToken, params string[] path)
where TResult : Model
{
using (Request request = CreateRequest(method, path))
{
Response response = SendRequest(request, cancellationToken);

return CreateResponse(response, resultFactory());
}
}

private Response SendRequest(HttpPipelineMethod method, CancellationToken cancellationToken, params string[] path)
{
using (Request request = CreateRequest(method, path))
{
return SendRequest(request, cancellationToken);
}
}

private Response SendRequest(Request request, CancellationToken cancellationToken)
{
var response = _pipeline.SendRequest(request, cancellationToken);

switch (response.Status)
{
case 200:
case 201:
return response;
default:
throw response.CreateRequestFailedException();
}
}

private Request CreateRequest(HttpPipelineMethod method, params string[] path)
{
// duplicating the code from the overload which takes a URI here because there is currently a bug in
Expand Down Expand Up @@ -136,5 +183,27 @@ private async Task<PageResponse<T>> GetPageAsync<T>(Uri firstPageUri, string nex
return new PageResponse<T>(responseAsPage.Items.ToArray(), response, responseAsPage.NextLink?.ToString());
}
}

private PageResponse<T> GetPage<T>(Uri firstPageUri, string nextLink, Func<T> itemFactory, CancellationToken cancellationToken)
where T : Model
{
// if we don't have a nextLink specified, use firstPageUri
if (nextLink != null)
{
firstPageUri = new Uri(nextLink);
}

using (Request request = CreateRequest(HttpPipelineMethod.Get, firstPageUri))
{
Response response = SendRequest(request, cancellationToken);

// read the respose
Page<T> responseAsPage = new Page<T>(itemFactory);
responseAsPage.Deserialize(response.ContentStream);

// convert from the Page<T> to PageResponse<T>
return new PageResponse<T>(responseAsPage.Items.ToArray(), response, responseAsPage.NextLink?.ToString());
}
}
}
}

0 comments on commit 6d3cabc

Please sign in to comment.