Skip to content
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
64 changes: 64 additions & 0 deletions sdk/keyvault/Azure.Security.KeyVault.Certificates/src/Action.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for
// license information.

using System;

namespace Azure.Security.KeyVault.Certificates
{
/// <summary>
/// An action that will be executed.
/// </summary>
public struct Action
{
private string _value;
internal const string AutoRenewValue = "AutoRenew";
internal const string EmailContactsValue = "EmailContacts";

/// <summary>
/// Initializes a new instance of the Action struct with the specfied value.
/// </summary>
public Action(string Action)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be lowercase "action". Also, for consistency, do we want all these values throughout structs like this to just be "value"?

{
_value = Action;
}

/// <summary>
/// An action that will auto-renew a certificate
/// </summary>
public static readonly Action AutoRenew = new Action(AutoRenewValue);

/// <summary>
/// An action that will email certificate contacts
/// </summary>
public static readonly Action EmailContacts = new Action(EmailContactsValue);

public override bool Equals(object obj)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May as well implement IEquitable<Action> which provides facilities to a lot of other uses (like hashing, comparison, etc.) without the boxing for object.Equals. Then just implement this as:

public override bool Equals(object obj) => obj is Action action && Equals(action);

{
return obj is Action && this.Equals((CertificateKeyType)obj);
}

public bool Equals(Action other)
{
return string.CompareOrdinal(_value, other._value) == 0;
}

public override int GetHashCode()
{
return _value?.GetHashCode() ?? 0;
}

public override string ToString()
{
return _value;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be null, and ToString() should never return null or empty: https://docs.microsoft.com/en-us/dotnet/api/system.object.tostring?view=netframework-4.8#notes-to-inheritors. Perhaps we should not allow either into the constructor to avoid this possibility.

}

public static bool operator ==(Action a, Action b) => a.Equals(b);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would box the Action. See comment above about implementing IEquitable<Action> to avoid this.


public static bool operator !=(Action a, Action b) => !a.Equals(b);

public static implicit operator Action(string value) => new Action(value);

public static implicit operator string(Action o) => o._value;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for
// license information.

using System.Text.Json;

namespace Azure.Security.KeyVault.Certificates
{
/// <summary>
/// Details of an administrator of a certificate <see cref="Issuer"/>
/// </summary>
public class AdministratorDetails
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be implementing IJsonSerializable and IJsonDeserializable?

{
/// <summary>
/// The email address of the administrator
/// </summary>
public string Email { get; set; }

/// <summary>
/// The first name of the administrator
/// </summary>
public string FirstName { get; set; }

/// <summary>
/// The last name of the administrator
/// </summary>
public string LastName { get; set; }

/// <summary>
/// The phone number of the administrator
/// </summary>
public string Phone { get; set; }

private const string FirstNamePropertyName = "first_name";
private const string LastNamePropertyName = "last_name";
private const string EmailPropertyName = "email";
private const string PhonePropertyName = "phone";

internal void ReadProperties(JsonElement json)
{
foreach (JsonProperty prop in json.EnumerateObject())
{
switch (prop.Name)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be a bit of a random comment, but I just got to thinking: for roundtripping, should we store all unhandled properties in a property bag and write them back out? JSON.NET has an easy way to do this, but we could do something similar pretty easy with an IDictionary<string, JsonElement>.

{
case FirstNamePropertyName:
FirstName = prop.Value.GetString();
break;
case LastNamePropertyName:
LastName = prop.Value.GetString();
break;
case EmailPropertyName:
Email = prop.Value.GetString();
break;
case PhonePropertyName:
Phone = prop.Value.GetString();
break;
}
}
}

private static readonly JsonEncodedText FirstNamePropertyNameBytes = JsonEncodedText.Encode(FirstNamePropertyName);
private static readonly JsonEncodedText LastNamePropertyNameBytes = JsonEncodedText.Encode(LastNamePropertyName);
private static readonly JsonEncodedText EmailPropertyNameBytes = JsonEncodedText.Encode(EmailPropertyName);
private static readonly JsonEncodedText PhonePropertyNameBytes = JsonEncodedText.Encode(PhonePropertyName);

internal void WriteProperties(Utf8JsonWriter json)
{
if (!string.IsNullOrEmpty(FirstName))
{
json.WriteString(FirstNamePropertyNameBytes, FirstName);
}

if (!string.IsNullOrEmpty(LastName))
{
json.WriteString(LastNamePropertyNameBytes, LastName);
}

if (!string.IsNullOrEmpty(Email))
{
json.WriteString(EmailPropertyNameBytes, Email);
}

if (!string.IsNullOrEmpty(Phone))
{
json.WriteString(PhonePropertyNameBytes, Phone);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
<PackageTags>Microsoft Azure Key Vault Certificates</PackageTags>
<PackageReleaseNotes>
<![CDATA[
Initial release of the Azure.Security.KeyVault.Certificates enabling:
- Management of Key Vault certififiates
- Management of Certificate Issuers
- Management of Certificate Contacts
]]>
</PackageReleaseNotes>

Expand All @@ -21,13 +25,17 @@
<!-- Import the Azure.Core project -->
<Import Project="$(MSBuildThisFileDirectory)..\..\..\core\Azure.Core\src\Azure.Core.props" />

<Import Project="..\..\Azure.Security.KeyVault.Shared\Azure.Security.KeyVault.Shared.projitems" Label="Shared" />

<ItemGroup>
<PackageReference Include="System.Memory" />
<PackageReference Include="System.Text.Json" />
<PackageReference Include="System.Threading.Tasks.Extensions" />
</ItemGroup>

<ItemGroup>
<Compile Include="..\..\Azure.Security.KeyVault.Shared\ChallengeBasedAuthenticationPolicy.cs" />
<Compile Include="$(AzureCoreSharedSources)ArrayBufferWriter.cs" />
<Compile Include="$(AzureCoreSharedSources)PageResponseEnumerator.cs" />
<Compile Include="..\..\Shared\*" />
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There shouldn't be anything in this directory anymore (i.e. it shouldn't exist).

</ItemGroup>
</Project>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,60 @@
// Licensed under the MIT License. See License.txt in the project root for
// license information.

using System.Text.Json;

namespace Azure.Security.KeyVault.Certificates
{
/// <summary>
/// An Azure Key Vault certificate
/// </summary>
public class Certificate : CertificateBase
{
public CertificatePolicy Policy { get; set; }
public string SecretId { get; set; }
public string KeyId { get; set; }
public string CER { get; set; }
/// <summary>
/// The id of the key vault Key backing the certifcate
/// </summary>
public string KeyId { get; private set; }

/// <summary>
/// The id of the key vault Secret which contains the PEM of PFX formatted content of the certficate and it's private key
/// </summary>
public string SecretId { get; private set; }

/// <summary>
/// The content type of the key vault Secret corresponding to the certificate
/// </summary>
public CertificateContentType ContentType { get; private set; }

/// <summary>
/// The CER formatted public X509 certificate
/// </summary>
public byte[] CER { get; private set; }

private const string KeyIdPropertyName = "kid";
private const string SecretIdPropertyName = "sid";
private const string ContentTypePropertyName = "contentType";
private const string CERPropertyName = "cer";

public Certificate(string name) : base(name) { }
internal override void ReadProperty(JsonProperty prop)
{
switch(prop.Name)
{
case KeyIdPropertyName:
KeyId = prop.Value.GetString();
break;
case SecretIdPropertyName:
SecretId = prop.Value.GetString();
break;
case ContentTypePropertyName:
ContentType = prop.Value.GetString();
break;
case CERPropertyName:
CER = Base64Url.Decode(prop.Value.GetString());
break;
default:
base.ReadProperty(prop);
break;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,87 +43,39 @@ internal struct CertificateAttributes
/// </summary>
public string RecoveryLevel { get; private set; }

private const string EnabledPropertyName = "enabled";
private const string NotBeforePropertyName = "nbf";
private const string ExpiresPropertyName = "exp";
private const string CreatedPropertyName = "created";
private const string UpdatedPropertyName = "updated";
private const string RecoveryLevelPropertyName = "recoveryLevel";

internal void ReadProperties(JsonElement json)
{
if (json.TryGetProperty("enabled", out JsonElement enabled))
{
Enabled = enabled.GetBoolean();
}

if (json.TryGetProperty("nbf", out JsonElement nbf))
{
if (nbf.ValueKind == JsonValueKind.Null)
{
NotBefore = null;
}
else
{
NotBefore = DateTimeOffset.Parse(nbf.GetString(), CultureInfo.InvariantCulture);
}
}

if (json.TryGetProperty("exp", out JsonElement exp))
{
if (exp.ValueKind == JsonValueKind.Null)
{
Expires = null;
}
else
{
Expires = DateTimeOffset.Parse(exp.GetString(), CultureInfo.InvariantCulture);
}
}

if (json.TryGetProperty("created", out JsonElement created))
{
if (created.ValueKind == JsonValueKind.Null)
{
Created = null;
}
else
{
Created = DateTimeOffset.Parse(created.GetString(), CultureInfo.InvariantCulture);
}
}

if (json.TryGetProperty("updated", out JsonElement updated))
foreach (JsonProperty prop in json.EnumerateObject())
{
if (updated.ValueKind == JsonValueKind.Null)
{
Updated = null;
}
else
switch (prop.Name)
{
Updated = DateTimeOffset.Parse(updated.GetString(), CultureInfo.InvariantCulture);
case EnabledPropertyName:
Enabled = prop.Value.GetBoolean();
break;
case NotBeforePropertyName:
NotBefore = DateTimeOffset.FromUnixTimeSeconds(prop.Value.GetInt64());
break;
case ExpiresPropertyName:
Expires = DateTimeOffset.FromUnixTimeSeconds(prop.Value.GetInt64());
break;
case CreatedPropertyName:
Created = DateTimeOffset.FromUnixTimeSeconds(prop.Value.GetInt64());
break;
case UpdatedPropertyName:
Updated = DateTimeOffset.FromUnixTimeSeconds(prop.Value.GetInt64());
break;
case RecoveryLevelPropertyName:
RecoveryLevel = prop.Value.GetString();
break;
}
}

if (json.TryGetProperty("recoveryLevel", out JsonElement recoveryLevel))
{
RecoveryLevel = recoveryLevel.GetString();
}
}

internal void WriteProperties(Utf8JsonWriter json)
{
if (Enabled.HasValue)
{
json.WriteBoolean("enabled", Enabled.Value);
}

if (NotBefore.HasValue)
{
json.WriteNumber("nbf", NotBefore.Value.ToUnixTimeMilliseconds());
}

if (Expires.HasValue)
{
json.WriteNumber("exp", Expires.Value.ToUnixTimeMilliseconds());
}

// Created is read-only don't serialize
// Updated is read-only don't serialize
// RecoveryLevel is read-only don't serialize
}
}
}
Loading