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

Keys Sync implementation #6490

Merged
merged 3 commits into from
Jun 7, 2019
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
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());
}
}
}
}