Skip to content

Commit

Permalink
[KeyVault] Supported accepting rotation policy in a JSON file (#18448)
Browse files Browse the repository at this point in the history
* Supported accepting rotation policy in a JSON file

* suppress breaking change issues

* upgrade Azure.Security.KeyVault.Keys to 4.3.0
  • Loading branch information
BethanyZhou authored Jun 10, 2022
1 parent d770913 commit c834195
Show file tree
Hide file tree
Showing 13 changed files with 355 additions and 130 deletions.
2 changes: 1 addition & 1 deletion src/CosmosDB/CosmosDB/CosmosDB.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<Import Project="$(MSBuildThisFileDirectory)..\..\Az.props" />
<ItemGroup>
<PackageReference Include="Azure.Security.KeyVault.Keys" Version="4.3.0-beta.7" />
<PackageReference Include="Azure.Security.KeyVault.Keys" Version="4.3.0" />
<PackageReference Include="Microsoft.Azure.Management.CosmosDB" Version="3.7.0-preview" />
</ItemGroup>

Expand Down
24 changes: 24 additions & 0 deletions src/KeyVault/KeyVault.Test/Resources/rotationpolicy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"lifetimeActions": [
{
"trigger": {
"timeAfterCreate": "P18M",
"timeBeforeExpiry": null
},
"action": {
"type": "Rotate"
}
},
{
"trigger": {
"timeBeforeExpiry": "P30D"
},
"action": {
"type": "Notify"
}
}
],
"attributes": {
"expiryTime": "P2Y"
}
}
3 changes: 3 additions & 0 deletions src/KeyVault/KeyVault/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
- Additional information about change #1
-->
## Upcoming Release
* Supported accepting rotation policy in a JSON file
* [Breaking Change] Changed parameter `ExpiresIn` in `Set-AzKeyVaultKeyRotationPolicy` from TimeSpan? to string. It must be an ISO 8601 duration like "P30D" for 30 days.
* [Breaking Change] Changed output properties `ExpiresIn`, `TimeAfterCreate` and `TimeBeforeExpiry` of `Set-AzKeyVaultKeyRotationPolicy` and `Get-AzKeyVaultKeyRotationPolicy` from TimeSpan? to string.
* Supported creating/updating key with release policy in a Managed HSM

## Version 4.5.0
Expand Down
Original file line number Diff line number Diff line change
@@ -1,47 +1,113 @@
using Microsoft.Azure.Commands.KeyVault.Models;
using Microsoft.Azure.Commands.Common.Exceptions;
using Microsoft.Azure.Commands.KeyVault.Models;
using Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters;
using Microsoft.WindowsAzure.Commands.Utilities.Common;

using Newtonsoft.Json;

using System;
using System.Management.Automation;
using Track2Sdk = Azure.Security.KeyVault.Keys;
using System.IO;
using Microsoft.WindowsAzure.Commands.Common;
using Microsoft.Azure.Commands.KeyVault.Properties;
using System.Collections.Generic;

namespace Microsoft.Azure.Commands.KeyVault.Commands.Key
{
/// <summary>
/// Updates the KeyRotationPolicy for the specified key in Key Vault.
/// </summary>
[Cmdlet(VerbsCommon.Set, ResourceManager.Common.AzureRMConstants.AzurePrefix + "KeyVaultKeyRotationPolicy", SupportsShouldProcess = true, DefaultParameterSetName = ByVaultNameParameterSet)]
[Cmdlet(VerbsCommon.Set, ResourceManager.Common.AzureRMConstants.AzurePrefix + "KeyVaultKeyRotationPolicy", SupportsShouldProcess = true, DefaultParameterSetName = SetByExpandedPropertiesViaVaultName)]
[OutputType(typeof(PSKeyRotationPolicy))]
public class SetAzKeyVaultKeyRotationPolicy: KeyVaultOnlyKeyCmdletBase
public class SetAzKeyVaultKeyRotationPolicy: KeyVaultCmdletBase
{
#region Parameter Set Names

internal const string ByKeyRotationPolicyInputObjectParameterSet = "ByKeyRotationPolicyInputObject";
private const string SetByExpandedPropertiesViaVaultName = "ByVaultName";
private const string SetByRotationPolicyFileViaVaultName = "SetByRotationPolicyFileViaVaultName";

private const string SetByExpandedPropertiesViaKeyInputObject = "ByKeyInputObject";
private const string SetByRotationPolicyFileViaKeyInputObject = "SetByRotationPolicyFileViaKeyInputObject";

private const string ByKeyRotationPolicyInputObjectParameterSet = "ByKeyRotationPolicyInputObject";

#endregion

#region Input Parameter Definitions

[Parameter(Mandatory = true,
Position = 0,
ParameterSetName = ByKeyRotationPolicyInputObjectParameterSet,
ValueFromPipeline = true,
HelpMessage = "PSKeyRotationPolicy object.")]
/// <summary>
/// Vault name
/// </summary>
[Parameter(Mandatory = true, Position = 0, ParameterSetName = SetByExpandedPropertiesViaVaultName, HelpMessage = "Vault name.")]
[Parameter(Mandatory = true, Position = 0, ParameterSetName = SetByRotationPolicyFileViaVaultName)]
[ResourceNameCompleter("Microsoft.KeyVault/vaults", "FakeResourceGroupName")]
[ValidateNotNullOrEmpty]
public string VaultName { get; set; }

/// <summary>
/// Key name.
/// </summary>
[Parameter(Mandatory = true, Position = 1, ParameterSetName = SetByExpandedPropertiesViaVaultName, HelpMessage = "Key name.")]
[Parameter(Mandatory = true, Position = 1, ParameterSetName = SetByRotationPolicyFileViaVaultName)]
[ValidateNotNullOrEmpty]
[Alias(Constants.KeyName)]
public string Name { get; set; }

/// <summary>
/// Key object
/// </summary>
[Parameter(Mandatory = true, Position = 0, ParameterSetName = SetByExpandedPropertiesViaKeyInputObject,
ValueFromPipeline = true, HelpMessage = "Key object")]
[Parameter(Mandatory = true, Position = 0, ParameterSetName = SetByRotationPolicyFileViaKeyInputObject,
ValueFromPipeline = true)]
[ValidateNotNullOrEmpty]
[Alias("Key")]
public PSKeyVaultKeyIdentityItem InputObject { get; set; }

[Parameter(Mandatory = true, Position = 0, ParameterSetName = ByKeyRotationPolicyInputObjectParameterSet,
ValueFromPipeline = true, HelpMessage = "PSKeyRotationPolicy object.")]
public PSKeyRotationPolicy KeyRotationPolicy { get; set; }

[Parameter(ParameterSetName = ByVaultNameParameterSet,
HelpMessage = "The time span when the key rotation policy will expire. It should be at least 28 days.")]
[Parameter(ParameterSetName = ByKeyInputObjectParameterSet)]
public TimeSpan ExpiresIn { get; set; }
[Parameter(ParameterSetName = SetByExpandedPropertiesViaVaultName,
HelpMessage = "The expiryTime will be applied on the new key version. It should be at least 28 days. It will be in ISO 8601 Format. Examples: 90 days: P90D, 3 months: P3M, 48 hours: PT48H, 1 year and 10 days: P1Y10D")]
[Parameter(ParameterSetName = SetByExpandedPropertiesViaKeyInputObject)]
public string ExpiresIn { get; set; }


[Parameter(ParameterSetName = ByVaultNameParameterSet,
[Parameter(ParameterSetName = SetByExpandedPropertiesViaVaultName,
HelpMessage = "PSKeyRotationLifetimeAction object.")]
[Parameter(ParameterSetName = ByKeyInputObjectParameterSet)]
[Parameter(ParameterSetName = SetByExpandedPropertiesViaKeyInputObject)]
public PSKeyRotationLifetimeAction[] KeyRotationLifetimeAction { get; set; }

[Parameter(Mandatory = true, ParameterSetName = SetByRotationPolicyFileViaVaultName,
HelpMessage = "A path to the rotation policy file that contains JSON policy definition.")]
[Parameter(Mandatory = true, ParameterSetName = SetByRotationPolicyFileViaKeyInputObject)]
public string PolicyPath { get; set; }
#endregion

internal override void NormalizeParameterSets()
protected override void BeginProcessing()
{
PolicyPath = this.TryResolvePath(PolicyPath);
base.BeginProcessing();
}

internal void ValidateParameters()
{
if((this.ParameterSetName.Equals(SetByExpandedPropertiesViaVaultName) ||
this.ParameterSetName.Equals(SetByExpandedPropertiesViaKeyInputObject)) &&
null == ExpiresIn && null == KeyRotationLifetimeAction)
{
throw new ArgumentException(string.Format("Must specify ExpiresIn or KeyRotationLifetimeAction."));
}

if(this.IsParameterBound(c => c.PolicyPath) && !File.Exists(PolicyPath))
{
throw new AzPSFileNotFoundException(string.Format(Resources.FileNotFound, PolicyPath), nameof(PolicyPath));
}
}

internal void NormalizeParameterSets()
{
if (null != InputObject)
{
Expand All @@ -57,33 +123,84 @@ internal override void NormalizeParameterSets()
}
}

if (!this.ParameterSetName.Equals(ByKeyRotationPolicyInputObjectParameterSet))
switch (this.ParameterSetName)
{

// Only update specified parameter, others keep same
KeyRotationPolicy = Track2DataClient.GetKeyRotationPolicy(VaultName, Name) ??
new PSKeyRotationPolicy()
{
case SetByRotationPolicyFileViaVaultName:
case SetByRotationPolicyFileViaKeyInputObject:
KeyRotationPolicy = ConstructKeyRotationPolicyFromFile(PolicyPath);
break;
case SetByExpandedPropertiesViaVaultName:
case SetByExpandedPropertiesViaKeyInputObject:
KeyRotationPolicy = new PSKeyRotationPolicy()
{
VaultName = VaultName,
KeyName = Name,
ExpiresIn = null,
LifetimeActions = null
ExpiresIn = ExpiresIn ?? Track2DataClient.GetKeyRotationPolicy(VaultName, Name).ExpiresIn,
LifetimeActions = KeyRotationLifetimeAction ?? Track2DataClient.GetKeyRotationPolicy(VaultName, Name).LifetimeActions
};
break;
default:
// do nothing
break;
}

if (MyInvocation.BoundParameters.ContainsKey("ExpiresIn"))
{
KeyRotationPolicy.ExpiresIn = ExpiresIn;
}
}

private PSKeyRotationPolicy ConstructKeyRotationPolicyFromFile(string policyPath)
{
try
{
string content = File.ReadAllText(policyPath);
// first-level dictionary
var dict = JsonUtilities.DeserializeJson(content, true);

if (MyInvocation.BoundParameters.ContainsKey("KeyRotationLifetimeAction"))
// second-level dictionary
var attributes = JsonUtilities.DeserializeJson(JsonConvert.SerializeObject(dict["attributes"]), true);
var lifetimeActionsArray = JsonConvert.DeserializeObject<object[]>(JsonConvert.SerializeObject(dict["lifetimeActions"]));

// third-level dictionary
string expiresIn = attributes["expiryTime"].ToString();

var lifetimeActions = new List<PSKeyRotationLifetimeAction>();
lifetimeActionsArray?.ForEach((lifetimeAction) =>
{
KeyRotationPolicy.LifetimeActions = KeyRotationLifetimeAction;
}
var lifetimeActionDict = JsonUtilities.DeserializeJson(JsonConvert.SerializeObject(lifetimeAction), true);
var action = JsonUtilities.DeserializeJson(JsonConvert.SerializeObject(lifetimeActionDict["action"]), true);
var trigger = JsonUtilities.DeserializeJson(JsonConvert.SerializeObject(lifetimeActionDict["trigger"]), true);
// 4th-level dictionary
string actionType = action["type"].ToString();
// timeAfterCreate or timeBeforeExpiry may absent
string timeAfterCreate = trigger.ContainsKey("timeAfterCreate") ? trigger["timeAfterCreate"]?.ToString() : null;
string timeBeforeExpiry = trigger.ContainsKey("timeBeforeExpiry") ? trigger["timeBeforeExpiry"]?.ToString() : null;
lifetimeActions.Add(new PSKeyRotationLifetimeAction()
{
Action = actionType,
TimeAfterCreate = timeAfterCreate,
TimeBeforeExpiry = timeBeforeExpiry
});
});

return new PSKeyRotationPolicy()
{
VaultName = this.VaultName,
KeyName = this.Name,
ExpiresIn = expiresIn,
LifetimeActions = lifetimeActions
};
}
catch
{
throw new AzPSArgumentException(string.Format("Deserialize {0} failed", policyPath), nameof(PolicyPath));
}

}

public override void ExecuteCmdlet()
{
ValidateParameters();
NormalizeParameterSets();

ConfirmAction(KeyRotationPolicy.KeyName, Properties.Resources.SetKeyRotationPolicy, () =>
Expand Down
4 changes: 2 additions & 2 deletions src/KeyVault/KeyVault/Commands/Key/UpdateAzureKeyVaultKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,11 @@ public class UpdateAzureKeyVaultKey : KeyVaultCmdletBase
public SwitchParameter Immutable { get; set; }

/// <summary>
/// A path to a file containing JSON policy definition. The policy rules under which a key can be exported.
/// A path to the release policy file that contains JSON policy definition. The policy rules under which a key can be exported.
/// </summary>
[Parameter(Mandatory = false,
ParameterSetName = HsmInteractiveParameterSet,
HelpMessage = "A path to a file containing JSON policy definition. The policy rules under which a key can be exported.")]
HelpMessage = "A path to the release policy file that contains JSON policy definition. The policy rules under which a key can be exported.")]
public string ReleasePolicyPath { get; set; }


Expand Down
2 changes: 1 addition & 1 deletion src/KeyVault/KeyVault/KeyVault.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

<ItemGroup>
<PackageReference Include="Azure.Security.KeyVault.Administration" Version="4.0.0" />
<PackageReference Include="Azure.Security.KeyVault.Keys" Version="4.3.0-beta.7" />
<PackageReference Include="Azure.Security.KeyVault.Keys" Version="4.3.0" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.8" />
<PackageReference Include="Microsoft.Azure.KeyVault" Version="3.0.1" />
<PackageReference Include="Microsoft.Azure.KeyVault.WebKey" Version="3.0.1" />
Expand Down
32 changes: 20 additions & 12 deletions src/KeyVault/KeyVault/Models/Key/PSKeyRotationLifetimeAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,32 @@ public class PSKeyRotationLifetimeAction
// Gets or sets he Azure.Security.KeyVault.Keys.KeyRotationPolicyAction that will
// be executed.
public string Action { get; set; }
//
// Summary:
// Gets or sets the System.TimeSpan after creation to attempt to rotate. It only
// applies to Azure.Security.KeyVault.Keys.KeyRotationPolicyAction.Rotate.
public TimeSpan? TimeAfterCreate { get; set; }
//
// Summary:
// Gets or sets the System.TimeSpan before expiry to attempt to Azure.Security.KeyVault.Keys.KeyRotationPolicyAction.Rotate
// or Azure.Security.KeyVault.Keys.KeyRotationPolicyAction.Notify.
public TimeSpan? TimeBeforeExpiry { get; set; }

/// <summary>
/// Gets or sets the ISO 8601 duration after creation to attempt to rotate. It only
/// applies to Azure.Security.KeyVault.Keys.KeyRotationPolicyAction.Rotate.
/// </summary>
/// <example>
/// ISO 8601 duration examples:
/// • P90D – 90 days
/// • P3M – 3 months
/// • P1Y10D – 1 year and 10 days
/// </example>
public string TimeAfterCreate { get; set; }

/// <summary>
/// Gets or sets the ISO 8601 duration before expiry to attempt to Azure.Security.KeyVault.Keys.KeyRotationPolicyAction.Rotate
/// or Azure.Security.KeyVault.Keys.KeyRotationPolicyAction.Notify.
/// </summary>
public string TimeBeforeExpiry { get; set; }

public PSKeyRotationLifetimeAction() { }

public PSKeyRotationLifetimeAction(KeyRotationLifetimeAction keyRotationLifetimeAction)
{
Action = keyRotationLifetimeAction.Action.ToString();
TimeAfterCreate = string.IsNullOrEmpty(keyRotationLifetimeAction.TimeAfterCreate) ? null : XmlConvert.ToTimeSpan(keyRotationLifetimeAction.TimeAfterCreate) as TimeSpan?;
TimeBeforeExpiry = string.IsNullOrEmpty(keyRotationLifetimeAction.TimeBeforeExpiry) ? null : XmlConvert.ToTimeSpan(keyRotationLifetimeAction.TimeBeforeExpiry) as TimeSpan?;
TimeAfterCreate = keyRotationLifetimeAction.TimeAfterCreate;
TimeBeforeExpiry = keyRotationLifetimeAction.TimeBeforeExpiry;
}

public override string ToString()
Expand Down
13 changes: 7 additions & 6 deletions src/KeyVault/KeyVault/Models/Key/PSKeyRotationPolicy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@ public class PSKeyRotationPolicy
// Summary:
// Gets the actions that will be performed by Key Vault over the lifetime of a key.
public IList<PSKeyRotationLifetimeAction> LifetimeActions { get; set; }
//
// Summary:
// Gets or sets the System.TimeSpan when the Azure.Security.KeyVault.Keys.KeyRotationPolicy
// will expire. It should be at least 28 days.
public TimeSpan? ExpiresIn { get; set; }

/// <summary>
/// The expiryTime will be applied on the new key version. It should be at least 28 days.
/// It will be in ISO 8601 Format. Examples: 90 days: P90D, 3 months: P3M, 48 hours: PT48H, 1 year and 10 days: P1Y10D"
/// </summary>
public string ExpiresIn { get; set; }
//
// Summary:
// Gets a System.DateTimeOffset indicating when the Azure.Security.KeyVault.Keys.KeyRotationPolicy
Expand All @@ -65,7 +66,7 @@ public PSKeyRotationPolicy(KeyRotationPolicy keyRotationPolicy, string vaultName
LifetimeActions.Add(new PSKeyRotationLifetimeAction(action));
}

ExpiresIn = string.IsNullOrEmpty(keyRotationPolicy.ExpiresIn) ? null : XmlConvert.ToTimeSpan(keyRotationPolicy.ExpiresIn) as TimeSpan?;
ExpiresIn = keyRotationPolicy.ExpiresIn;
CreatedOn = keyRotationPolicy.CreatedOn;
UpdatedOn = keyRotationPolicy.UpdatedOn;
}
Expand Down
9 changes: 4 additions & 5 deletions src/KeyVault/KeyVault/Track2Models/Track2HsmClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -543,17 +543,16 @@ internal PSKeyRotationPolicy SetKeyRotationPolicy(PSKeyRotationPolicy psKeyRotat
var client = CreateKeyClient(psKeyRotationPolicy.VaultName);
var policy = new KeyRotationPolicy()
{
ExpiresIn = psKeyRotationPolicy.ExpiresIn.HasValue ? XmlConvert.ToString(psKeyRotationPolicy.ExpiresIn.Value) : null,
ExpiresIn = psKeyRotationPolicy.ExpiresIn,
LifetimeActions = { }
};

psKeyRotationPolicy.LifetimeActions?.ForEach(
psKeyRotationLifetimeAction => policy.LifetimeActions.Add(
new KeyRotationLifetimeAction()
new KeyRotationLifetimeAction(new KeyRotationPolicyAction(psKeyRotationLifetimeAction.Action))
{
Action = psKeyRotationLifetimeAction.Action,
TimeAfterCreate = psKeyRotationLifetimeAction.TimeAfterCreate.HasValue ? XmlConvert.ToString(psKeyRotationLifetimeAction.TimeAfterCreate.Value) : null,
TimeBeforeExpiry = psKeyRotationLifetimeAction.TimeBeforeExpiry.HasValue ? XmlConvert.ToString(psKeyRotationLifetimeAction.TimeBeforeExpiry.Value) : null
TimeAfterCreate = psKeyRotationLifetimeAction.TimeAfterCreate,
TimeBeforeExpiry = psKeyRotationLifetimeAction.TimeBeforeExpiry
}
));

Expand Down
Loading

0 comments on commit c834195

Please sign in to comment.