Config framework implementation (#17545)

* Modified code from Microsot.Extensions.Configuration

* implementation of config framework

* cmdlet related

* test related

* changelog; 3rd party license

* move PSConfig model to Accounts proj; feedback link

* Move preview attribute into child classes

* fix get/update by config key

* correct cmdlet regex
This commit is contained in:
Yeming Liu 2022-05-13 00:06:21 +08:00 коммит произвёл GitHub
Родитель 42085ee602
Коммит 7d4bee9535
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
70 изменённых файлов: 5828 добавлений и 8 удалений

Просмотреть файл

@ -273,5 +273,33 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-----------------------------------------------------------------------------
***************
The software includes Microsoft.Extensions.Configuration. The MIT License set out below is provided for informational purposes only. It is not the license that governs any part of the software.
The MIT License (MIT)
Copyright (c) .NET Foundation and Contributors
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-------------END OF THIRD PARTY NOTICE----------------------------------------

Просмотреть файл

@ -382,7 +382,8 @@ namespace Microsoft.WindowsAzure.Commands.Common.Test.Mocks
() =>
{
writeLocks[path] = false;
virtualStore[path] = Encoding.UTF8.GetString(buffer);
// trim \0 otherwise json fails to parse
virtualStore[path] = Encoding.UTF8.GetString(buffer).TrimEnd('\0');
}
);
}

Просмотреть файл

@ -276,6 +276,61 @@
</ListEntries>
</ListControl>
</View>
<View>
<Name>Microsoft.Azure.Commands.Profile.Models.PSConfig</Name>
<ViewSelectedBy>
<TypeName>Microsoft.Azure.Commands.Profile.Models.PSConfig</TypeName>
</ViewSelectedBy>
<TableControl>
<TableHeaders>
<TableColumnHeader>
<Alignment>Left</Alignment>
<Label>Key</Label>
</TableColumnHeader>
<TableColumnHeader>
<Alignment>Left</Alignment>
<Label>Value</Label>
</TableColumnHeader>
<TableColumnHeader>
<Alignment>Left</Alignment>
<Label>Applies To</Label>
</TableColumnHeader>
<TableColumnHeader>
<Alignment>Left</Alignment>
<Label>Scope</Label>
</TableColumnHeader>
<TableColumnHeader>
<Alignment>Left</Alignment>
<Label>Help Message</Label>
</TableColumnHeader>
</TableHeaders>
<TableRowEntries>
<TableRowEntry>
<TableColumnItems>
<TableColumnItem>
<Alignment>Left</Alignment>
<PropertyName>Key</PropertyName>
</TableColumnItem>
<TableColumnItem>
<Alignment>Left</Alignment>
<PropertyName>Value</PropertyName>
</TableColumnItem>
<TableColumnItem>
<Alignment>Left</Alignment>
<PropertyName>AppliesTo</PropertyName>
</TableColumnItem>
<TableColumnItem>
<Alignment>Left</Alignment>
<PropertyName>Scope</PropertyName>
</TableColumnItem>
<TableColumnItem>
<Alignment>Left</Alignment>
<PropertyName>HelpMessage</PropertyName>
</TableColumnItem>
</TableColumnItems>
</TableRowEntry>
</TableRowEntries>
</TableControl>
</View>
</ViewDefinitions>
</Configuration>

Просмотреть файл

@ -108,7 +108,7 @@ CmdletsToExport = 'Disable-AzDataCollection', 'Disable-AzContextAutosave',
'Set-AzDefault', 'Get-AzDefault', 'Clear-AzDefault',
'Register-AzModule', 'Enable-AzureRmAlias', 'Disable-AzureRmAlias',
'Uninstall-AzureRm', 'Invoke-AzRestMethod', 'Get-AzAccessToken',
'Open-AzSurveyLink'
'Open-AzSurveyLink', 'Get-AzConfig', 'Update-AzConfig', 'Clear-AzConfig'
# Variables to export from this module
# VariablesToExport = @()

Просмотреть файл

@ -19,6 +19,10 @@
-->
## Upcoming Release
* Added a preview feature allowing user to control the configurations of Azure PowerShell by using the following cmdlets:
- `Get-AzConfig`
- `Update-AzConfig`
- `Clear-AzConfig`
* Upgraded System.Reflection.DispatchProxy on Windows PowerShell [#17856]
* Upgraded Azure.Identity to 1.6.0 and Azure.Core to 1.24.0
@ -216,7 +220,7 @@
* Updated Add-AzEnvironment and Set-AzEnvironment to accept parameters AzureAttestationServiceEndpointResourceId and AzureAttestationServiceEndpointSuffix
## Version 1.6.6
* Add client-side telemetry info for Az 4.0 preview
* Add client-side telemetry info for Az 4.0 `preview`
## Version 1.6.5
* Update references in .psd1 to use relative path

Просмотреть файл

@ -0,0 +1,114 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.PowerShell.Common.Config;
using Microsoft.WindowsAzure.Commands.Common.CustomAttributes;
using Microsoft.WindowsAzure.Commands.Utilities.Common;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Management.Automation;
namespace Microsoft.Azure.Commands.Common.Authentication.Config
{
[Cmdlet("Clear", "AzConfig", SupportsShouldProcess = true)]
[OutputType(typeof(bool))]
[CmdletPreview(PreviewMessage)]
public class ClearConfigCommand : ConfigCommandBase, IDynamicParameters
{
private const string ClearByKey = "ClearByKey";
private const string ClearAll = "ClearAll";
private const string ProcessMessage = "Clear the configs that apply to \"{0}\" by the following keys: {1}.";
private string ContinueMessage => $"Clear all the configs that apply to \"{AppliesTo}\" in scope {Scope}?";
private string ProcessTarget => $"Configs in scope {Scope}";
[Parameter(ParameterSetName = ClearAll, Mandatory = true, HelpMessage = "Clear all configs.")]
public SwitchParameter All { get; set; }
[Parameter(ParameterSetName = ClearAll, HelpMessage = "Do not ask for confirmation when clearing all configs.")]
public SwitchParameter Force { get; set; }
[Parameter(HelpMessage = "Returns true if cmdlet executes correctly.")]
public SwitchParameter PassThru { get; set; }
public new object GetDynamicParameters()
{
return GetDynamicParameters((ConfigDefinition config) =>
new RuntimeDefinedParameter(
config.Key,
typeof(SwitchParameter),
new Collection<Attribute>() {
new ParameterAttribute {
ParameterSetName = ClearByKey,
HelpMessage = config.HelpMessage
}
}));
}
public override void ExecuteCmdlet()
{
switch (ParameterSetName)
{
case ClearByKey:
ClearConfigByKey();
break;
case ClearAll:
ClearAllConfigs();
break;
}
if (PassThru)
{
WriteObject(true);
}
}
private void ClearConfigByKey()
{
IEnumerable<string> configKeysFromInput = GetConfigsSpecifiedByUser()
.Where(x => (SwitchParameter)x.Value)
.Select(x => x.Key);
if (!configKeysFromInput.Any())
{
WriteWarning($"Please specify the key(s) of the configs to clear. Run `help {MyInvocation.MyCommand.Name}` for more information.");
return;
}
base.ConfirmAction(
string.Format(ProcessMessage, AppliesTo, string.Join(", ", configKeysFromInput)),
ProcessTarget,
() => configKeysFromInput.ForEach(ClearConfigByKey));
}
private void ClearConfigByKey(string key)
{
ConfigManager.ClearConfig(new ClearConfigOptions(key, Scope)
{
AppliesTo = AppliesTo
});
}
private void ClearAllConfigs()
{
ConfirmAction(Force, ContinueMessage, ContinueMessage, ProcessTarget, () =>
{
ConfigManager.ClearConfig(new ClearConfigOptions(null, Scope)
{
AppliesTo = AppliesTo
});
});
}
}
}

Просмотреть файл

@ -0,0 +1,98 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Exceptions;
using Microsoft.Azure.Commands.ResourceManager.Common;
using Microsoft.Azure.PowerShell.Common.Config;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
namespace Microsoft.Azure.Commands.Common.Authentication.Config
{
public abstract class ConfigCommandBase : AzureRMCmdlet
{
protected const string PreviewMessage = "The cmdlet group \"AzConfig\" is in preview. Feedback is welcome: https://aka.ms/azpsissue";
private readonly RuntimeDefinedParameterDictionary _dynamicParameters = new RuntimeDefinedParameterDictionary();
protected IConfigManager ConfigManager { get; }
protected IEnumerable<ConfigDefinition> ConfigDefinitions
{
get
{
if (_configDefinitions == null)
{
_configDefinitions = ConfigManager.ListConfigDefinitions();
}
return _configDefinitions;
}
}
private IEnumerable<ConfigDefinition> _configDefinitions;
public ConfigCommandBase() : base()
{
if (!AzureSession.Instance.TryGetComponent<IConfigManager>(nameof(IConfigManager), out var configManager))
{
throw new AzPSApplicationException($"Unexpected error: {nameof(IConfigManager)} has not been registered to the current session.");
}
ConfigManager = configManager;
}
[Parameter(HelpMessage = "Specifies what part of Azure PowerShell the config applies to. Possible values are:\n- \"" + ConfigFilter.GlobalAppliesTo + "\": the config applies to all modules and cmdlets of Azure PowerShell. \n- Module name: the config applies to a certain module of Azure PowerShell. For example, \"Az.Storage\".\n- Cmdlet name: the config applies to a certain cmdlet of Azure PowerShell. For example, \"Get-AzKeyVault\".\nIf not specified, when getting configs, output will be all of the above; when updating, it defaults to \"" + ConfigFilter.GlobalAppliesTo + "\"; when clearing, configs applying to any targets are cleared.")]
[ValidateNotNullOrEmpty]
public string AppliesTo { get; set; }
[Parameter(HelpMessage = "Determines the scope of config changes, for example, whether changes apply only to the current process, or to all sessions started by this user. By default it is CurrentUser.")]
public ConfigScope Scope { get; set; } = ConfigScope.CurrentUser;
protected override void BeginProcessing()
{
base.BeginProcessing();
ValidateParameters();
}
protected virtual void ValidateParameters()
{
if (!AppliesToHelper.TryParseAppliesTo(AppliesTo, out _))
{
throw new AzPSArgumentException($"{nameof(AppliesTo)} must be a valid module name, a cmdlet name, or \"{ConfigFilter.GlobalAppliesTo}\"", nameof(AppliesTo));
}
}
protected object GetDynamicParameters(Func<ConfigDefinition, RuntimeDefinedParameter> mapConfigToParameter)
{
_dynamicParameters.Clear();
foreach (var config in ConfigDefinitions)
{
_dynamicParameters.Add(config.Key, mapConfigToParameter(config));
}
return _dynamicParameters;
}
/// <summary>
/// Gets the dynamic parameters and their values if specified.
/// </summary>
/// <returns></returns>
protected IEnumerable<(string Key, object Value)> GetConfigsSpecifiedByUser()
{
var configs = new Dictionary<string, object>();
foreach (var param in _dynamicParameters.Values.Where(p => p.IsSet))
{
yield return (param.Name, param.Value);
}
}
}
}

Просмотреть файл

@ -0,0 +1,71 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Profile.Models;
using Microsoft.Azure.Commands.ResourceManager.Common;
using Microsoft.Azure.PowerShell.Common.Config;
using Microsoft.WindowsAzure.Commands.Common.CustomAttributes;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Management.Automation;
namespace Microsoft.Azure.Commands.Common.Authentication.Config
{
[Cmdlet(VerbsCommon.Get, AzureRMConstants.AzureRMPrefix + "Config")]
[OutputType(typeof(PSConfig))]
[CmdletPreview(PreviewMessage)]
public class GetConfigCommand : ConfigCommandBase, IDynamicParameters
{
public GetConfigCommand() : base()
{
}
public new object GetDynamicParameters()
{
return GetDynamicParameters((ConfigDefinition config) =>
new RuntimeDefinedParameter(
config.Key,
typeof(SwitchParameter),
new Collection<Attribute>() {
new ParameterAttribute {
HelpMessage = config.HelpMessage
}
}));
}
public override void ExecuteCmdlet()
{
ConfigFilter filter = CreateConfigFilter();
IEnumerable<ConfigData> configs = ConfigManager.ListConfigs(filter);
WriteObject(configs.Select(x => new PSConfig(x)), true);
}
private ConfigFilter CreateConfigFilter()
{
ConfigFilter filter = new ConfigFilter() { AppliesTo = AppliesTo };
IEnumerable<string> configKeysFromInput = GetConfigsSpecifiedByUser()
.Where(x => (SwitchParameter)x.Value)
.Select(x => x.Key);
if (configKeysFromInput.Any())
{
filter.Keys = configKeysFromInput;
}
return filter;
}
}
}

Просмотреть файл

@ -0,0 +1,78 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Profile.Models;
using Microsoft.Azure.PowerShell.Common.Config;
using Microsoft.WindowsAzure.Commands.Common.CustomAttributes;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Management.Automation;
namespace Microsoft.Azure.Commands.Common.Authentication.Config
{
[Cmdlet("Update", "AzConfig", SupportsShouldProcess = true)]
[OutputType(typeof(PSConfig))]
[CmdletPreview(PreviewMessage)]
public class UpdateConfigCommand : ConfigCommandBase, IDynamicParameters
{
private const string ProcessMessage = "Update the configs that apply to \"{0}\" by the following keys: {1}.";
private string ProcessTarget => $"Configs in scope {Scope}";
public new object GetDynamicParameters() => GetDynamicParameters(
(ConfigDefinition config) => new RuntimeDefinedParameter(
config.Key, config.ValueType,
new Collection<Attribute>() { new ParameterAttribute {
HelpMessage = config.HelpMessage,
ValueFromPipelineByPropertyName = true
} }
));
protected override void BeginProcessing()
{
base.BeginProcessing();
if (AppliesTo == null)
{
AppliesTo = ConfigFilter.GlobalAppliesTo;
}
}
public override void ExecuteCmdlet()
{
var configsFromInput = GetConfigsSpecifiedByUser();
if (!configsFromInput.Any())
{
WriteWarning($"Please specify the key(s) of the configs to update. Run `help {MyInvocation.MyCommand.Name}` for more information.");
return;
}
base.ConfirmAction(
string.Format(ProcessMessage, AppliesTo, string.Join(", ", configsFromInput.Select(x => x.Key))),
ProcessTarget,
() => UpdateConfigs(configsFromInput));
}
private void UpdateConfigs(IEnumerable<(string, object)> configsToUpdate)
{
foreach ((string key, object value) in configsToUpdate)
{
ConfigData updated = ConfigManager.UpdateConfig(new UpdateConfigOptions(key, value, Scope)
{
AppliesTo = AppliesTo
});
WriteObject(new PSConfig(updated));
}
}
}
}

Просмотреть файл

@ -1,9 +1,10 @@
<Project>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory).., Directory.Build.targets))\Directory.Build.targets" />
<Target Name ="BuildAssemblyLoadContextProject" AfterTargets="Build" Condition="'$(Configuration)' == 'Debug'">
<!-- Skip in live unit testing https://docs.microsoft.com/en-us/visualstudio/test/live-unit-testing-faq?view=vs-2022#can-i-customize-my-live-unit-testing-builds- -->
<Target Name ="BuildAssemblyLoadContextProject" AfterTargets="Build" Condition="'$(Configuration)' == 'Debug' And '$(BuildingForLiveUnitTesting)' != 'true'">
<Exec Command="dotnet build ../AuthenticationAssemblyLoadContext/AuthenticationAssemblyLoadContext.csproj"/>
</Target>
<Target Name="AddAccountsPsm1Dependency" AfterTargets="Build" Condition="'$(Configuration)' == 'Debug'">
<Target Name="AddAccountsPsm1Dependency" AfterTargets="Build" Condition="'$(Configuration)' == 'Debug' And '$(BuildingForLiveUnitTesting)' != 'true'">
<Exec Command="pwsh -NonInteractive -NoLogo -NoProfile -Command &quot;. '$(OutDir)../../../tools/AddModulePsm1Dependency.ps1' -ModuleFolder '$(OutDir)' -IgnorePwshVersion &quot;" />
</Target>
</Project>

Просмотреть файл

@ -0,0 +1,42 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.PowerShell.Common.Config;
namespace Microsoft.Azure.Commands.Profile.Models
{
/// <summary>
/// The output model of config-related cmdlets.
/// </summary>
public class PSConfig
{
public string Key { get; }
public object Value { get; }
public ConfigScope Scope { get; } = ConfigScope.CurrentUser;
public string AppliesTo { get; }
public string HelpMessage { get; }
public object DefaultValue { get; }
public PSConfig(ConfigData config)
{
Value = config.Value;
Scope = config.Scope;
AppliesTo = config.AppliesTo;
var def = config.Definition;
Key = def.Key;
HelpMessage = def.HelpMessage;
DefaultValue = def.DefaultValue;
}
}
}

Просмотреть файл

@ -14,6 +14,9 @@ Manages credentials and common configuration for all Azure modules.
### [Add-AzEnvironment](Add-AzEnvironment.md)
Adds endpoints and metadata for an instance of Azure Resource Manager.
### [Clear-AzConfig](Clear-AzConfig.md)
Clears the values of configs that are set by the user.
### [Clear-AzContext](Clear-AzContext.md)
Remove all Azure credentials, account, and subscription information.
@ -56,6 +59,9 @@ Enables AzureRm prefix aliases for Az modules.
### [Get-AzAccessToken](Get-AzAccessToken.md)
Get raw access token. When using -ResourceUrl, please make sure the value does match current Azure environment. You may refer to the value of `(Get-AzContext).Environment`.
### [Get-AzConfig](Get-AzConfig.md)
Gets the configs of Azure PowerShell.
### [Get-AzContext](Get-AzContext.md)
Gets the metadata used to authenticate Azure Resource Manager requests.
@ -120,3 +126,6 @@ Sets properties for an Azure environment.
### [Uninstall-AzureRm](Uninstall-AzureRm.md)
Removes all AzureRm modules from a machine.
### [Update-AzConfig](Update-AzConfig.md)
Updates the configs of Azure PowerShell.

Просмотреть файл

@ -0,0 +1,251 @@
---
external help file: Microsoft.Azure.PowerShell.Cmdlets.Accounts.dll-Help.xml
Module Name: Az.Accounts
online version: https://docs.microsoft.com/powershell/module/az.accounts/clear-azconfig
schema: 2.0.0
---
# Clear-AzConfig
## SYNOPSIS
Clears the values of configs that are set by the user.
## SYNTAX
### ClearAll
```
Clear-AzConfig [-All] [-Force] [-PassThru] [-AppliesTo <String>] [-Scope <ConfigScope>]
[-DefaultProfile <IAzureContextContainer>] [-WhatIf] [-Confirm] [<CommonParameters>]
```
### ClearByKey
```
Clear-AzConfig [-PassThru] [-AppliesTo <String>] [-Scope <ConfigScope>]
[-DefaultProfile <IAzureContextContainer>] [-WhatIf] [-Confirm] [-DefaultSubscriptionForLogin]
[-EnableDataCollection] [-EnableInterceptSurvey] [-SuppressWarningMessage] [<CommonParameters>]
```
## DESCRIPTION
{{ Fill in the Description }}
## EXAMPLES
### Example 1
```powershell
Clear-AzConfig -Todo
```
```output
Todo
```
Todo
## PARAMETERS
### -All
Clear all configs.
```yaml
Type: System.Management.Automation.SwitchParameter
Parameter Sets: ClearAll
Aliases:
Required: True
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -AppliesTo
Specifies what part of Azure PowerShell the config applies to.
Possible values are:
- "Az": the config applies to all modules and cmdlets of Azure PowerShell.
- Module name: the config applies to a certain module of Azure PowerShell.
For example, "Az.Storage".
- Cmdlet name: the config applies to a certain cmdlet of Azure PowerShell.
For example, "Get-AzKeyVault".
If not specified, when getting configs, output will be all of the above; when updating or clearing configs, it defaults to "Az"
```yaml
Type: System.String
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -DefaultProfile
The credentials, account, tenant, and subscription used for communication with Azure.
```yaml
Type: Microsoft.Azure.Commands.Common.Authentication.Abstractions.Core.IAzureContextContainer
Parameter Sets: (All)
Aliases: AzContext, AzureRmContext, AzureCredential
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -DefaultSubscriptionForLogin
Subscription name or GUID.
If defined, when logging in Azure PowerShell without specifying the subscription, this one will be used to select the default context.
```yaml
Type: System.Management.Automation.SwitchParameter
Parameter Sets: ClearByKey
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -EnableDataCollection
todo
```yaml
Type: System.Management.Automation.SwitchParameter
Parameter Sets: ClearByKey
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -EnableInterceptSurvey
When enabled, a message of taking part in the survey about the user experience of Azure PowerShell will prompt at low frequency.
```yaml
Type: System.Management.Automation.SwitchParameter
Parameter Sets: ClearByKey
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -Force
Do not ask for confirmation when clearing all configs.
```yaml
Type: System.Management.Automation.SwitchParameter
Parameter Sets: ClearAll
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -PassThru
Returns true if cmdlet executes correctly.
```yaml
Type: System.Management.Automation.SwitchParameter
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -Scope
Determines the scope of config changes, for example, whether changes apply only to the current process, or to all sessions started by this user.
By default it is CurrentUser.
```yaml
Type: Microsoft.Azure.PowerShell.Common.Config.ConfigScope
Parameter Sets: (All)
Aliases:
Accepted values: CurrentUser, Process, Default
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -SuppressWarningMessage
Controls if the warning messages of upcoming breaking changes are enabled or suppressed.
The messages are typically displayed when a cmdlet that will have breaking change in the future is executed.
```yaml
Type: System.Management.Automation.SwitchParameter
Parameter Sets: ClearByKey
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -Confirm
Prompts you for confirmation before running the cmdlet.
```yaml
Type: System.Management.Automation.SwitchParameter
Parameter Sets: (All)
Aliases: cf
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -WhatIf
Shows what would happen if the cmdlet runs.
The cmdlet is not run.
```yaml
Type: System.Management.Automation.SwitchParameter
Parameter Sets: (All)
Aliases: wi
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### CommonParameters
This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
## INPUTS
### None
## OUTPUTS
### System.Boolean
## NOTES
## RELATED LINKS

Просмотреть файл

@ -0,0 +1,168 @@
---
external help file: Microsoft.Azure.PowerShell.Cmdlets.Accounts.dll-Help.xml
Module Name: Az.Accounts
online version: https://docs.microsoft.com/powershell/module/az.accounts/get-azconfig
schema: 2.0.0
---
# Get-AzConfig
## SYNOPSIS
Gets the configs of Azure PowerShell.
## SYNTAX
```
Get-AzConfig [-AppliesTo <String>] [-Scope <ConfigScope>] [-DefaultProfile <IAzureContextContainer>]
[-DefaultSubscriptionForLogin] [-EnableDataCollection] [-EnableInterceptSurvey] [-SuppressWarningMessage]
[<CommonParameters>]
```
## DESCRIPTION
{{ Fill in the Description }}
## EXAMPLES
### Example 1
```powershell
Get-AzConfig
```
```output
Todo
```
Todo
## PARAMETERS
### -AppliesTo
Specifies what part of Azure PowerShell the config applies to.
Possible values are:
- "Az": the config applies to all modules and cmdlets of Azure PowerShell.
- Module name: the config applies to a certain module of Azure PowerShell.
For example, "Az.Storage".
- Cmdlet name: the config applies to a certain cmdlet of Azure PowerShell.
For example, "Get-AzKeyVault".
If not specified, when getting configs, output will be all of the above; when updating or clearing configs, it defaults to "Az"
```yaml
Type: System.String
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -DefaultProfile
The credentials, account, tenant, and subscription used for communication with Azure.
```yaml
Type: Microsoft.Azure.Commands.Common.Authentication.Abstractions.Core.IAzureContextContainer
Parameter Sets: (All)
Aliases: AzContext, AzureRmContext, AzureCredential
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -DefaultSubscriptionForLogin
Subscription name or GUID.
If defined, when logging in Azure PowerShell without specifying the subscription, this one will be used to select the default context.
```yaml
Type: System.Management.Automation.SwitchParameter
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -EnableDataCollection
todo
```yaml
Type: System.Management.Automation.SwitchParameter
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -EnableInterceptSurvey
When enabled, a message of taking part in the survey about the user experience of Azure PowerShell will prompt at low frequency.
```yaml
Type: System.Management.Automation.SwitchParameter
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -Scope
Determines the scope of config changes, for example, whether changes apply only to the current process, or to all sessions started by this user.
By default it is CurrentUser.
```yaml
Type: Microsoft.Azure.PowerShell.Common.Config.ConfigScope
Parameter Sets: (All)
Aliases:
Accepted values: CurrentUser, Process, Default
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -SuppressWarningMessage
Controls if the warning messages of upcoming breaking changes are enabled or suppressed.
The messages are typically displayed when a cmdlet that will have breaking change in the future is executed.
```yaml
Type: System.Management.Automation.SwitchParameter
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### CommonParameters
This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
## INPUTS
### None
## OUTPUTS
### Microsoft.Azure.Commands.Common.Authentication.Config.PSConfig
## NOTES
## RELATED LINKS

Просмотреть файл

@ -0,0 +1,199 @@
---
external help file: Microsoft.Azure.PowerShell.Cmdlets.Accounts.dll-Help.xml
Module Name: Az.Accounts
online version: https://docs.microsoft.com/powershell/module/az.accounts/update-azconfig
schema: 2.0.0
---
# Update-AzConfig
## SYNOPSIS
Updates the configs of Azure PowerShell.
## SYNTAX
```
Update-AzConfig [-AppliesTo <String>] [-Scope <ConfigScope>] [-DefaultProfile <IAzureContextContainer>]
[-WhatIf] [-Confirm] [-DefaultSubscriptionForLogin <String>] [-EnableDataCollection <Boolean>]
[-EnableInterceptSurvey <Boolean>] [-SuppressWarningMessage <Boolean>] [<CommonParameters>]
```
## DESCRIPTION
{{ Fill in the Description }}
## EXAMPLES
### Example 1
```powershell
Update-AzConfig -Todo $true
```
```output
Todo
```
Todo
## PARAMETERS
### -AppliesTo
Specifies what part of Azure PowerShell the config applies to.
Possible values are:
- "Az": the config applies to all modules and cmdlets of Azure PowerShell.
- Module name: the config applies to a certain module of Azure PowerShell.
For example, "Az.Storage".
- Cmdlet name: the config applies to a certain cmdlet of Azure PowerShell.
For example, "Get-AzKeyVault".
If not specified, when getting configs, output will be all of the above; when updating or clearing configs, it defaults to "Az"
```yaml
Type: System.String
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -DefaultProfile
The credentials, account, tenant, and subscription used for communication with Azure.
```yaml
Type: Microsoft.Azure.Commands.Common.Authentication.Abstractions.Core.IAzureContextContainer
Parameter Sets: (All)
Aliases: AzContext, AzureRmContext, AzureCredential
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -DefaultSubscriptionForLogin
Subscription name or GUID.
If defined, when logging in Azure PowerShell without specifying the subscription, this one will be used to select the default context.
```yaml
Type: System.String
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: True (ByPropertyName)
Accept wildcard characters: False
```
### -EnableDataCollection
todo
```yaml
Type: System.Boolean
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: True (ByPropertyName)
Accept wildcard characters: False
```
### -EnableInterceptSurvey
When enabled, a message of taking part in the survey about the user experience of Azure PowerShell will prompt at low frequency.
```yaml
Type: System.Boolean
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: True (ByPropertyName)
Accept wildcard characters: False
```
### -Scope
Determines the scope of config changes, for example, whether changes apply only to the current process, or to all sessions started by this user.
By default it is CurrentUser.
```yaml
Type: Microsoft.Azure.PowerShell.Common.Config.ConfigScope
Parameter Sets: (All)
Aliases:
Accepted values: CurrentUser, Process, Default
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -SuppressWarningMessage
Controls if the warning messages of upcoming breaking changes are enabled or suppressed.
The messages are typically displayed when a cmdlet that will have breaking change in the future is executed.
```yaml
Type: System.Boolean
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: True (ByPropertyName)
Accept wildcard characters: False
```
### -Confirm
Prompts you for confirmation before running the cmdlet.
```yaml
Type: System.Management.Automation.SwitchParameter
Parameter Sets: (All)
Aliases: cf
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -WhatIf
Shows what would happen if the cmdlet runs.
The cmdlet is not run.
```yaml
Type: System.Management.Automation.SwitchParameter
Parameter Sets: (All)
Aliases: wi
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### CommonParameters
This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
## INPUTS
### None
## OUTPUTS
### Microsoft.Azure.Commands.Common.Authentication.Config.PSConfig
## NOTES
## RELATED LINKS

Просмотреть файл

@ -0,0 +1,214 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Exceptions;
using Microsoft.Azure.Commands.Common.Authentication.Config;
using Microsoft.Azure.PowerShell.Common.Config;
using Microsoft.Rest.ClientRuntime.Azure.TestFramework;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Xunit;
namespace Microsoft.Azure.Authentication.Test.Config
{
public class ClearConfigTests : ConfigTestsBase
{
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanClearSingleConfig()
{
string key = "FalseByDefault";
IConfigManager icm = GetConfigManager(new SimpleTypedConfig<bool>(key, "{help message}", false));
Assert.False(icm.GetConfigValue<bool>(key));
icm.UpdateConfig(new UpdateConfigOptions(key, true, ConfigScope.Process));
Assert.True(icm.GetConfigValue<bool>(key));
icm.ClearConfig(new ClearConfigOptions(key, ConfigScope.Process));
Assert.False(icm.GetConfigValue<bool>(key));
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CannotClearUnknownConfig()
{
IConfigManager configurationManager = GetConfigManager();
Assert.Throws<AzPSArgumentException>(() =>
{
configurationManager.ClearConfig(new ClearConfigOptions("NeverRegistered", ConfigScope.CurrentUser));
});
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void ShouldNotThrowToClearConfigNeverSet()
{
string key1 = "key1";
var config1 = new SimpleTypedConfig<bool>(key1, "{help message}", false);
string key2 = "key2";
var config2 = new SimpleTypedConfig<bool>(key2, "{help message}", false);
IConfigManager icm = GetConfigManager(config1, config2);
icm.ClearConfig(key1, ConfigScope.CurrentUser);
icm.ClearConfig(key2, ConfigScope.Process);
icm.ClearConfig(null, ConfigScope.CurrentUser);
icm.ClearConfig(null, ConfigScope.Process);
icm.ClearConfig(new ClearConfigOptions(null, ConfigScope.CurrentUser)
{
AppliesTo = null
});
icm.ClearConfig(new ClearConfigOptions(null, ConfigScope.Process)
{
AppliesTo = null
});
icm.ClearConfig(new ClearConfigOptions(null, ConfigScope.CurrentUser)
{
AppliesTo = "Az.Accounts"
});
icm.ClearConfig(new ClearConfigOptions(null, ConfigScope.Process)
{
AppliesTo = "Az.Accounts"
});
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanClearSingleConfigInJson()
{
IConfigManager icm = GetConfigManager();
string key = "DisableSomething";
icm.RegisterConfig(new SimpleTypedConfig<bool>(key, "{help message}", false));
icm.BuildConfig();
Assert.False(icm.GetConfigValue<bool>(key));
icm.UpdateConfig(new UpdateConfigOptions(key, true, ConfigScope.CurrentUser));
Assert.True(icm.GetConfigValue<bool>(key));
icm.ClearConfig(new ClearConfigOptions(key, ConfigScope.CurrentUser));
Assert.False(icm.GetConfigValue<bool>(key));
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanClearAllConfigsInJson()
{
string key1 = "key1";
var config1 = new SimpleTypedConfig<bool>(key1, "{help message}", false);
string key2 = "key2";
var config2 = new SimpleTypedConfig<bool>(key2, "{help message}", false);
ConfigManager cm = GetConfigManager(config1, config2) as ConfigManager;
Assert.False(cm.GetConfigValue<bool>(key1));
Assert.False(cm.GetConfigValue<bool>(key2));
// Scenario 1: update the configs, applying to Az
cm.UpdateConfig(new UpdateConfigOptions(key1, true, ConfigScope.CurrentUser));
Assert.True(cm.GetConfigValue<bool>(key1));
cm.UpdateConfig(new UpdateConfigOptions(key2, true, ConfigScope.CurrentUser));
Assert.True(cm.GetConfigValue<bool>(key2));
// clear all configs by specifying `null` as the key, applying to Az
cm.ClearConfig(null, ConfigScope.CurrentUser);
Assert.False(cm.GetConfigValue<bool>(key1));
Assert.False(cm.GetConfigValue<bool>(key2));
// Scenario 2: update the configs, applying to Az.Accounts
cm.UpdateConfig(new UpdateConfigOptions(key1, true, ConfigScope.CurrentUser) { AppliesTo = "Az.Accounts" });
Assert.True(cm.GetConfigValueInternal<bool>(key1, new InternalInvocationInfo() { ModuleName = "Az.Accounts" }));
cm.UpdateConfig(new UpdateConfigOptions(key2, true, ConfigScope.CurrentUser) { AppliesTo = "Az.Accounts" });
Assert.True(cm.GetConfigValueInternal<bool>(key2, new InternalInvocationInfo() { ModuleName = "Az.Accounts" }));
// clear all configs, applying to Az.Accounts
cm.ClearConfig(new ClearConfigOptions(null, ConfigScope.CurrentUser) { AppliesTo = "Az.Accounts" });
Assert.False(cm.GetConfigValueInternal<bool>(key1, new InternalInvocationInfo() { ModuleName = "Az.Accounts" }));
Assert.False(cm.GetConfigValueInternal<bool>(key2, new InternalInvocationInfo() { ModuleName = "Az.Accounts" }));
// Scenario 3: update the configs, applying differently
cm.UpdateConfig(new UpdateConfigOptions(key1, true, ConfigScope.CurrentUser) { AppliesTo = "Az.Accounts" });
Assert.True(cm.GetConfigValueInternal<bool>(key1, new InternalInvocationInfo() { ModuleName = "Az.Accounts" }));
cm.UpdateConfig(new UpdateConfigOptions(key2, true, ConfigScope.CurrentUser) { AppliesTo = "Az.KeyVault" });
Assert.True(cm.GetConfigValueInternal<bool>(key2, new InternalInvocationInfo() { ModuleName = "Az.KeyVault" }));
// clear all configs, applying anything
cm.ClearConfig(null, ConfigScope.CurrentUser);
Assert.False(cm.GetConfigValueInternal<bool>(key1, new InternalInvocationInfo() { ModuleName = "Az.Accounts" }));
Assert.False(cm.GetConfigValueInternal<bool>(key2, new InternalInvocationInfo() { ModuleName = "Az.KeyVault" }));
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void ShouldNotThrowWhenClearConfigNeverSet()
{
string key = "DisableSomething";
var config = new SimpleTypedConfig<bool>(key, "{help message}", false);
IConfigManager icm = GetConfigManager(config);
icm.ClearConfig(key, ConfigScope.CurrentUser);
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanClearByScope()
{
const string boolKey = "BoolKey";
var boolConfig = new SimpleTypedConfig<bool>(boolKey, "", false);
const string intKey = "intKey";
var intConfig = new SimpleTypedConfig<int>(intKey, "", 0);
var icm = GetConfigManager(boolConfig, intConfig);
icm.UpdateConfig(new UpdateConfigOptions(boolKey, true, ConfigScope.CurrentUser));
icm.UpdateConfig(new UpdateConfigOptions(intKey, 10, ConfigScope.CurrentUser));
icm.UpdateConfig(new UpdateConfigOptions(boolKey, true, ConfigScope.Process));
icm.UpdateConfig(new UpdateConfigOptions(intKey, 10, ConfigScope.Process));
icm.ClearConfig(new ClearConfigOptions(boolKey, ConfigScope.Process));
icm.ClearConfig(new ClearConfigOptions(intKey, ConfigScope.Process));
foreach (var configData in icm.ListConfigs())
{
Assert.NotEqual(ConfigScope.Process, configData.Scope);
}
icm.ClearConfig(boolKey, ConfigScope.CurrentUser);
icm.ClearConfig(intKey, ConfigScope.CurrentUser);
foreach (var configData in icm.ListConfigs())
{
Assert.NotEqual(ConfigScope.CurrentUser, configData.Scope);
}
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void AppliesToShouldDefaultToAz()
{
const string boolKey = "BoolKey";
var boolConfig = new SimpleTypedConfig<bool>(boolKey, "", false);
var icm = GetConfigManager(boolConfig);
const string appliesTo = "Az.A";
icm.UpdateConfig(new UpdateConfigOptions(boolKey, true, ConfigScope.CurrentUser)
{
AppliesTo = appliesTo
});
icm.ClearConfig(boolKey, ConfigScope.CurrentUser);
Assert.Single(icm.ListConfigs(new ConfigFilter() { Keys = new string[] { boolKey }, AppliesTo = appliesTo }));
icm.ClearConfig(new ClearConfigOptions(boolKey, ConfigScope.CurrentUser) { AppliesTo = appliesTo });
Assert.Empty(icm.ListConfigs(new ConfigFilter() { Keys = new string[] { boolKey }, AppliesTo = appliesTo }));
}
}
}

Просмотреть файл

@ -0,0 +1,60 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Exceptions;
using Microsoft.Azure.Commands.Common.Authentication.Config;
using Microsoft.Rest.ClientRuntime.Azure.TestFramework;
using Microsoft.WindowsAzure.Commands.Common;
using System;
using System.Linq;
using Xunit;
using Microsoft.Azure.PowerShell.Common.Config;
namespace Microsoft.Azure.Authentication.Test.Config
{
public class ConfigDefinitionTests : ConfigTestsBase
{
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanValidateInput() {
const string boolKey = "BoolKey";
var boolConfig = new SimpleTypedConfig<bool>(boolKey, "", false);
var rangedIntConfig = new RangedConfig();
var icm = GetConfigManagerWithInitState(null, null, boolConfig, rangedIntConfig);
Assert.Throws<AzPSArgumentException>(() => { icm.UpdateConfig(boolKey, 0, ConfigScope.CurrentUser); });
Assert.Throws<AzPSArgumentException>(() => { icm.UpdateConfig(rangedIntConfig.Key, true, ConfigScope.CurrentUser); });
Assert.Throws<AzPSArgumentException>(() => { icm.UpdateConfig(rangedIntConfig.Key, -1, ConfigScope.CurrentUser); });
}
private class RangedConfig : TypedConfig<int>
{
public override object DefaultValue => 0;
public override string Key => "RangedKey";
public override string HelpMessage => "";
public override void Validate(object value)
{
base.Validate(value);
int valueAsInt = (int)value;
if (valueAsInt < 0 || valueAsInt > 100)
{
throw new ArgumentOutOfRangeException($"The value of config {Key} must be in between 0 and 100.");
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,75 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Config;
using Microsoft.Azure.PowerShell.Authentication.Test.Mocks;
using Microsoft.Azure.PowerShell.Common.Config;
using Microsoft.WindowsAzure.Commands.Common.Test.Mocks;
using System;
using System.Collections.Generic;
using System.IO;
namespace Microsoft.Azure.Authentication.Test.Config
{
public class ConfigTestsBase
{
private readonly Action<MockDataStore, string> _noopFileWriter = (x, y) => { };
private readonly Action<MockEnvironmentVariableProvider> _noopEnvVarWriter = (x) => { };
/// <summary>
/// Initializes and returns an <see cref="IConfigManager"/> with the specified configs registered.
/// </summary>
/// <param name="config">Definitions of configs to be registered to the config manager.</param>
/// <returns>A config manager ready to use.</returns>
protected IConfigManager GetConfigManager(params ConfigDefinition[] config) => GetConfigManagerWithInitState(null, null, config);
/// <summary>
/// Initializes and returns an <see cref="IConfigManager"/> with the specified configs registered with initial state.
/// </summary>
/// <param name="configFileWriter">An action to set up the config file before config manager initializes.</param>
/// <param name="envVarWriter">An action to set up the environments before config manager initializes.</param>
/// <param name="config">Definitions of configs to be registered to the config manager.</param>
/// <returns>A config manager with initial state, ready to use.</returns>
protected IConfigManager GetConfigManagerWithInitState(Action<MockDataStore, string> configFileWriter, Action<MockEnvironmentVariableProvider> envVarWriter, params ConfigDefinition[] config)
{
if (configFileWriter == null)
{
configFileWriter = _noopFileWriter;
}
if (envVarWriter == null)
{
envVarWriter = _noopEnvVarWriter;
}
string configPath = Path.GetRandomFileName();
var mockDataStore = new MockDataStore();
configFileWriter(mockDataStore, configPath);
var environmentVariables = new MockEnvironmentVariableProvider();
envVarWriter(environmentVariables);
ConfigInitializer ci = new ConfigInitializer(new List<string>() { configPath })
{
DataStore = mockDataStore,
EnvironmentVariableProvider = environmentVariables
};
IConfigManager icm = ci.GetConfigManager();
foreach (var configDefinition in config)
{
icm.RegisterConfig(configDefinition);
}
icm.BuildConfig();
return icm;
}
}
}

Просмотреть файл

@ -0,0 +1,339 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Exceptions;
using Microsoft.Azure.Commands.Common.Authentication.Config;
using Microsoft.Azure.PowerShell.Common.Config;
using Microsoft.Rest.ClientRuntime.Azure.TestFramework;
using System.Linq;
using Xunit;
namespace Microsoft.Azure.Authentication.Test.Config
{
public class GetConfigTests : ConfigTestsBase
{
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanGetAppliesTo()
{
const string key = "EnableTelemetry";
var def = new SimpleTypedConfig<bool>(
key,
"Enable telemetry",
true);
IConfigManager icm = GetConfigManager(def);
var config = icm.ListConfigs().Single();
Assert.NotNull(config);
Assert.Equal(key, config.Definition.Key);
icm.UpdateConfig(new UpdateConfigOptions(key, false, ConfigScope.CurrentUser));
config = icm.ListConfigs().Single();
Assert.Equal(ConfigFilter.GlobalAppliesTo, config.AppliesTo);
icm.UpdateConfig(new UpdateConfigOptions(key, false, ConfigScope.CurrentUser) { AppliesTo = "Az.KeyVault" });
config = icm.ListConfigs(new ConfigFilter() { AppliesTo = "Az.KeyVault" }).Single();
Assert.Equal("Az.KeyVault", config.AppliesTo);
icm.UpdateConfig(new UpdateConfigOptions(key, false, ConfigScope.CurrentUser) { AppliesTo = "Get-AzKeyVault" });
config = icm.ListConfigs(new ConfigFilter { AppliesTo = "Get-AzKeyVault" }).Single();
Assert.Equal("Get-AzKeyVault", config.AppliesTo);
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void ShouldReturnEmptyWhenFilterIsWrong()
{
const string key = "EnableTelemetry";
var config = new SimpleTypedConfig<bool>(
key,
"Enable telemetry",
true);
var icm = GetConfigManager(config);
Assert.NotEmpty(icm.ListConfigs());
Assert.NotEmpty(icm.ListConfigs(null));
Assert.Empty(icm.ListConfigs(new ConfigFilter() { Keys = new string[] { "Never Exist" } }));
Assert.Empty(icm.ListConfigs(new ConfigFilter() { AppliesTo = "xxx" }));
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanGetAndListRegisteredConfigs()
{
const string key1 = "EnableTelemetry";
var config1 = new SimpleTypedConfig<bool>(
key1,
"Enable telemetry",
true);
TestConfig config2 = new TestConfig();
IConfigManager configurationManager = GetConfigManager(config1, config2);
var listResult = configurationManager.ListConfigs();
Assert.Equal(2, listResult.Count());
ConfigData configData = listResult.Where(x => x.Definition.Key == key1).Single();
Assert.Equal(true, configData.Value);
Assert.True(configurationManager.GetConfigValue<bool>(key1));
ConfigData tempConfigResult = listResult.Where(x => x.Definition.Key == config2.Key).Single();
Assert.Equal(config2.DefaultValue, tempConfigResult.Value);
Assert.Equal(config2.HelpMessage, tempConfigResult.Definition.HelpMessage);
Assert.Equal(config2.DefaultValue, configurationManager.GetConfigValue<int>(config2.Key));
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanUpdateAndList()
{
IConfigManager configurationManager = GetConfigManager();
const string key = "EnableTelemetry";
configurationManager.RegisterConfig(
new SimpleTypedConfig<bool>(
key,
"Enable telemetry",
true));
configurationManager.BuildConfig();
var updatedConfig = configurationManager.UpdateConfig(new UpdateConfigOptions(key, false, ConfigScope.Process));
Assert.Equal(key, updatedConfig.Definition.Key);
Assert.False((bool)updatedConfig.Value);
Assert.False(configurationManager.GetConfigValue<bool>(key));
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanGetFromEnvironmentVar()
{
const string key = "FromEnv";
const string envKey = "ENV_VAR_FOR_CONFIG";
var config = new SimpleTypedConfig<int>(key, "", -1, envKey);
const int value = 20;
var configurationManager = GetConfigManagerWithInitState(null, (envVar) => { envVar.Set(envKey, value.ToString()); }, config);
Assert.Equal(value, configurationManager.GetConfigValue<int>(key));
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void ShouldNotThrowWhenEnvVarIsWrong()
{
const string key = "FromEnv";
const string envKey = "ENV_VAR_FOR_CONFIG";
const int defaultValue = -1;
var config = new SimpleTypedConfig<int>(key, "", defaultValue, envKey);
const bool valueWithWrongType = true;
var configurationManager = GetConfigManagerWithInitState(null, envVar =>
{
envVar.Set(envKey, valueWithWrongType.ToString());
}, config);
Assert.Equal(defaultValue, configurationManager.GetConfigValue<int>(key));
}
private class TestConfig : TypedConfig<int>
{
public override object DefaultValue => -1;
public override string Key => "TempConfig";
public override string HelpMessage => "temp config";
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanGetFromJson()
{
var config1 = new SimpleTypedConfig<int>("Retry", "", -1);
var config2 = new SimpleTypedConfig<string[]>("Array", "", null);
IConfigManager icm = GetConfigManagerWithInitState((dataStore, path) =>
{
dataStore.WriteFile(path,
@"{
""Az"": {
""Retry"": 100
},
""Az.KeyVault"": {
""Array"": [""a"",""b""]
},
""Get-AzKeyVault"": {
""Array"": [""k"",""v""]
}
}");
}, null, config1, config2);
ConfigManager cm = icm as ConfigManager;
Assert.Equal(100, cm.GetConfigValue<int>("Retry"));
Assert.Equal(new string[] { "a", "b" }, cm.GetConfigValueInternal<string[]>("Array", new InternalInvocationInfo() { ModuleName = "Az.KeyVault" }));
Assert.Equal(new string[] { "k", "v" }, cm.GetConfigValueInternal<string[]>("Array", new InternalInvocationInfo() { ModuleName = "Az.KeyVault", CmdletName = "Get-AzKeyVault" }));
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void ThrowWhenGetUnknownConfig()
{
IConfigManager entry = GetConfigManager();
entry.BuildConfig();
Assert.Throws<AzPSArgumentNullException>(() => { entry.GetConfigValue<int>(null); });
Assert.Throws<AzPSArgumentException>(() => { entry.GetConfigValue<int>(""); });
const string key = "KeyThatIsNotRegistered";
Assert.Throws<AzPSArgumentException>(() => { entry.GetConfigValue<int>(key); });
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanFilterByKeyAndAppliesTo()
{
const string key = "key";
var config = new SimpleTypedConfig<bool>(key, "", true);
var icm = GetConfigManager(config);
const string module = "Az.KeyVault";
icm.UpdateConfig(new UpdateConfigOptions(key, false, ConfigScope.CurrentUser) { AppliesTo = module });
Assert.Single(icm.ListConfigs(new ConfigFilter() { Keys = new[] { key }, AppliesTo = module }));
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanFilterByKey()
{
const string key = "key";
var config = new SimpleTypedConfig<bool>(key, "", true);
var icm = GetConfigManager(config);
const string module = "Az.KeyVault";
icm.UpdateConfig(new UpdateConfigOptions(key, false, ConfigScope.CurrentUser) { AppliesTo = module });
var listResults = icm.ListConfigs(new ConfigFilter() { Keys = new[] { key } });
Assert.Equal(2, listResults.Count());
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanFilterByAppliesTo()
{
const string key1 = "key";
var config1 = new SimpleTypedConfig<bool>(key1, "", true);
const string key2 = "key2";
var config2 = new SimpleTypedConfig<bool>(key2, "", true);
var icm = GetConfigManager(config1, config2);
const string module = "Az.KeyVault";
icm.UpdateConfig(new UpdateConfigOptions(key1, false, ConfigScope.CurrentUser) { AppliesTo = module });
icm.UpdateConfig(new UpdateConfigOptions(key2, false, ConfigScope.CurrentUser) { AppliesTo = module });
var listResults = icm.ListConfigs(new ConfigFilter() { AppliesTo = module });
Assert.Equal(2, listResults.Count());
listResults = icm.ListConfigs(new ConfigFilter() { AppliesTo = ConfigFilter.GlobalAppliesTo });
Assert.Equal(2, listResults.Count());
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanFilterByNoFilter()
{
const string key1 = "key";
var config1 = new SimpleTypedConfig<bool>(key1, "", true);
const string key2 = "key2";
var config2 = new SimpleTypedConfig<bool>(key2, "", true);
var icm = GetConfigManager(config1, config2);
const string module = "Az.KeyVault";
icm.UpdateConfig(new UpdateConfigOptions(key1, false, ConfigScope.CurrentUser) { AppliesTo = module });
icm.UpdateConfig(new UpdateConfigOptions(key2, false, ConfigScope.CurrentUser) { AppliesTo = module });
var listResults = icm.ListConfigs();
Assert.Equal(4, listResults.Count()); // default*2, module*2
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanListDefinitions()
{
const string key1 = "key";
var config1 = new SimpleTypedConfig<bool>(key1, "", true);
const string key2 = "key2";
var config2 = new SimpleTypedConfig<bool>(key2, "", true);
var config3 = new TestConfig();
var icm = GetConfigManager(config1, config2, config3);
Assert.Equal(3, icm.ListConfigDefinitions().Count());
const string module = "Az.KeyVault";
icm.UpdateConfig(new UpdateConfigOptions(key1, false, ConfigScope.CurrentUser) { AppliesTo = module });
Assert.Equal(3, icm.ListConfigDefinitions().Count());
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanGetScope()
{
const string key1 = "key";
var config1 = new SimpleTypedConfig<bool>(key1, "", true);
var config2 = new TestConfig();
var icm = GetConfigManager(config1, config2);
var listResults = icm.ListConfigs();
foreach (var config in listResults)
{
Assert.Equal(ConfigScope.Default, config.Scope);
}
var updated = icm.UpdateConfig(new UpdateConfigOptions(key1, false, ConfigScope.CurrentUser));
Assert.Equal(ConfigScope.CurrentUser, updated.Scope);
updated = icm.UpdateConfig(new UpdateConfigOptions(key1, true, ConfigScope.Process));
Assert.Equal(ConfigScope.Process, updated.Scope);
icm.ClearConfig(new ClearConfigOptions(key1, ConfigScope.Process));
updated = icm.ListConfigs(new ConfigFilter() { Keys = new string[] { key1 } }).Single();
Assert.Equal(ConfigScope.CurrentUser, updated.Scope);
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void AppliesToShouldBeCaseInsensitive()
{
const string key = "key";
var config = new SimpleTypedConfig<int>(key, "", 0);
var icm = GetConfigManager(config);
icm.UpdateConfig(new UpdateConfigOptions(key, 1, ConfigScope.CurrentUser) { AppliesTo = "az.abc" });
Assert.Equal(1, icm.ListConfigs(new ConfigFilter() { Keys = new[] { key }, AppliesTo = "az.abc" }).Single().Value);
Assert.Equal(1, icm.ListConfigs(new ConfigFilter() { Keys = new[] { key }, AppliesTo = "Az.Abc" }).Single().Value);
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void ListDefinitionsShouldBeDictOrder()
{
const string key1 = "key1";
var config1 = new SimpleTypedConfig<int>(key1, "", 0);
const string key2 = "key2";
var config2 = new SimpleTypedConfig<int>(key2, "", 0);
const string key3 = "key3";
var config3 = new SimpleTypedConfig<int>(key3, "", 0);
// register using wrong order
var icm = GetConfigManager(config2, config1, config3);
for (int i = 0; i != 10; ++i)
{
var definitions = icm.ListConfigDefinitions();
// expect return with dict order
Assert.Equal(key1, definitions.ElementAt(0).Key);
Assert.Equal(key2, definitions.ElementAt(1).Key);
Assert.Equal(key3, definitions.ElementAt(2).Key);
}
}
}
}

Просмотреть файл

@ -0,0 +1,85 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Config;
using Microsoft.Azure.PowerShell.Common.Config;
using Microsoft.Rest.ClientRuntime.Azure.TestFramework;
using Xunit;
namespace Microsoft.Azure.Authentication.Test.Config
{
public class PriorityTests : ConfigTestsBase
{
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void UserConfigHigherThanSystemUserEnv()
{
const string retryKey = "Retry";
const string envName = "ENV_FOR_RETRY";
var config = new SimpleTypedConfig<int>(retryKey, "", -1, envName);
IConfigManager icm = GetConfigManagerWithInitState((dataStore, path) =>
{
dataStore.WriteFile(path,
@"{
""Az"": {
""Retry"": 100
}
}");
}, envVar =>
{
envVar.Set(envName, "10", System.EnvironmentVariableTarget.User);
}, config);
Assert.Equal(100, icm.GetConfigValue<int>(retryKey));
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void ProcessEnvHigherThanUserConfig()
{
const string retryKey = "Retry";
const string envName = "ENV_FOR_RETRY";
var config = new SimpleTypedConfig<int>(retryKey, "", -1, envName);
IConfigManager icm = GetConfigManagerWithInitState((dataStore, path) =>
{
dataStore.WriteFile(path,
@"{
""Az"": {
""Retry"": 100
}
}");
}, envVar =>
{
envVar.Set(envName, "10", System.EnvironmentVariableTarget.Process);
}, config);
Assert.Equal(10, icm.GetConfigValue<int>(retryKey));
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void ProcessConfigHigherThanProcessEnv()
{
const string retryKey = "Retry";
const string envName = "ENV_FOR_RETRY";
var config = new SimpleTypedConfig<int>(retryKey, "", -1, envName);
IConfigManager icm = GetConfigManagerWithInitState(null, envVar =>
{
envVar.Set(envName, "10", System.EnvironmentVariableTarget.Process);
}, config);
icm.UpdateConfig(new UpdateConfigOptions(retryKey, 100, ConfigScope.Process));
Assert.Equal(100, icm.GetConfigValue<int>(retryKey));
}
}
}

Просмотреть файл

@ -0,0 +1,106 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Exceptions;
using Microsoft.Azure.Commands.Common.Authentication.Config;
using Microsoft.Azure.PowerShell.Common.Config;
using Microsoft.Rest.ClientRuntime.Azure.TestFramework;
using System;
using System.Collections.Generic;
using Xunit;
namespace Microsoft.Azure.Authentication.Test.Config
{
public class RegisterConfigTests : ConfigTestsBase
{
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CannotRegisterSameKeyTwice()
{
IConfigManager entry = GetConfigManager();
const string key = "CannotRegisterTwice";
entry.RegisterConfig(new SimpleTypedConfig<int>(key, "", -1));
Assert.Throws<AzPSArgumentException>(() =>
{
entry.RegisterConfig(new SimpleTypedConfig<object>(key, "", null));
});
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanRegisterSameConfigTwice()
{
IConfigManager entry = GetConfigManager();
const string key = "CanRegisterTwice";
SimpleTypedConfig<int> config = new SimpleTypedConfig<int>(key, "", -1);
entry.RegisterConfig(config);
entry.RegisterConfig(config);
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanGetDefaultValue()
{
IConfigManager entry = GetConfigManager();
const string key = "CanGetConfigValue";
SimpleTypedConfig<int> config = new SimpleTypedConfig<int>(key, "", -1);
entry.RegisterConfig(config);
entry.BuildConfig();
Assert.Equal(-1, entry.GetConfigValue<int>(key));
entry.UpdateConfig(new UpdateConfigOptions(key, 10, ConfigScope.Process));
Assert.Equal(10, entry.GetConfigValue<int>(key));
}
[Theory]
[MemberData(nameof(TestData))]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanRegisterConfigs(ConfigDefinition config)
{
ConfigManager manager = GetConfigManager() as ConfigManager;
manager.RegisterConfig(config);
manager.BuildConfig();
Assert.Equal(config.DefaultValue, manager.GetConfigValueInternal(config.Key, null));
}
public static IEnumerable<object[]> TestData => new List<object[]>
{
new object[] { new SimpleTypedConfig<int>("Config", "", -1) },
new object[] { new SimpleTypedConfig<int>("Config", "", -1, "ENV_VAR_FOR_CONFIG") },
new object[] { new SimpleTypedConfig<int?>("Config", "", null) },
new object[] { new SimpleTypedConfig<int?>("Config", "", 1) },
new object[] { new SimpleTypedConfig<bool>("Config", "", true) },
new object[] { new SimpleTypedConfig<string>("Config", "", "default") },
new object[] { new SimpleTypedConfig<double>("Config", "", 3.1415926) },
new object[] { new SimpleTypedConfig<int[]>("Config", "", new int[] { 1,2,3 }) },
new object[] { new SimpleTypedConfig<string[]>("Config", "", new string[] { "Az.Accounts", "Az.Compute" })},
new object[] { new SimpleTypedConfig<DateTime>("Config", "", DateTime.MinValue) },
new object[] { new SimpleTypedConfig<bool>("Config", "", true, "env_var", new [] { AppliesTo.Cmdlet }) },
new object[] { new TestConfigForDefaultValue() }
};
private class TestConfigForDefaultValue : ConfigDefinition
{
public override object DefaultValue => (decimal)10;
public override string Key => nameof(TestConfigForDefaultValue);
public override string HelpMessage => "";
public override Type ValueType => typeof(decimal);
public override void Validate(object value) { base.Validate(value); }
}
}
}

Просмотреть файл

@ -0,0 +1,186 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Exceptions;
using Microsoft.Azure.Commands.Common.Authentication.Config;
using Microsoft.Azure.PowerShell.Common.Config;
using Microsoft.Rest.ClientRuntime.Azure.TestFramework;
using Moq;
using System;
using System.Linq;
using Xunit;
namespace Microsoft.Azure.Authentication.Test.Config
{
public class UpdateConfigTests : ConfigTestsBase
{
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanUpdateJsonFile()
{
const string retryKey = "Retry";
var intConfig = new SimpleTypedConfig<int>(retryKey, "", -1);
const string arrayKey = "Array";
var arrayConfig = new SimpleTypedConfig<string[]>(arrayKey, "", null);
IConfigManager icm = GetConfigManagerWithInitState((dataStore, path) =>
{
dataStore.WriteFile(path,
@"{
""Az"": {
""Retry"": 100
},
""Az.KeyVault"": {
""Array"": [""a"",""b""]
}
}");
}, null, intConfig, arrayConfig);
ConfigManager cm = icm as ConfigManager;
Assert.Equal(100, cm.GetConfigValue<int>(retryKey));
Assert.Equal(new string[] { "a", "b" }, cm.GetConfigValueInternal<string[]>(arrayKey, new InternalInvocationInfo() { ModuleName = "Az.KeyVault" }));
ConfigData updated = icm.UpdateConfig(new UpdateConfigOptions(retryKey, 10, ConfigScope.CurrentUser));
Assert.Equal(10, updated.Value);
Assert.Equal(10, icm.GetConfigValue<int>(retryKey));
string[] updatedArray = new string[] { "c", "d" };
ConfigData updated2 = icm.UpdateConfig(new UpdateConfigOptions(arrayKey, updatedArray, ConfigScope.CurrentUser)
{
AppliesTo = "Az.KeyVault"
});
Assert.Equal(updatedArray, updated2.Value);
Assert.Equal(updatedArray, cm.GetConfigValueInternal<string[]>(arrayKey, new InternalInvocationInfo() { ModuleName = "Az.KeyVault" }));
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanUpdateConfigForCmdlet()
{
const string warningKey = "DisalbeWarning";
var warningConfig = new SimpleTypedConfig<bool>(warningKey, "", false);
IConfigManager icm = GetConfigManager(warningConfig);
Assert.False(icm.GetConfigValue<bool>(warningKey));
ConfigData updated = icm.UpdateConfig(new UpdateConfigOptions(warningKey, true, ConfigScope.CurrentUser)
{
AppliesTo = "Get-AzKeyVault"
});
Assert.Equal(true, updated.Value);
Assert.False(icm.GetConfigValue<bool>(warningKey));
var cm = (ConfigManager)icm;
Assert.True(cm.GetConfigValueInternal<bool>(warningKey, new InternalInvocationInfo("Az.KeyVault", "Get-AzKeyVault")));
Assert.False(cm.GetConfigValueInternal<bool>(warningKey, new InternalInvocationInfo("Az.Storage", "Get-AzStorageAccount")));
Assert.False(cm.GetConfigValueInternal<bool>(warningKey, new InternalInvocationInfo("Az.KeyVault", "Remove-AzKeyVault")));
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void ThrowWhenOptionIsInvalid()
{
const string key1 = "key";
var config1 = new SimpleTypedConfig<bool>(key1, "", true);
const string key2 = "key2";
var config2 = new SimpleTypedConfig<bool>(key2, "", true);
var icm = GetConfigManager(config1, config2);
Assert.Throws<AzPSArgumentNullException>(() => icm.UpdateConfig(null));
Assert.Throws<ArgumentNullException>(() => icm.UpdateConfig(new UpdateConfigOptions(null, null, ConfigScope.CurrentUser)));
Assert.Throws<AzPSArgumentException>(() => icm.UpdateConfig(new UpdateConfigOptions(key1, "ThisShouldNotBeAString", ConfigScope.CurrentUser)));
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void AppliesToShouldBeCaseInsensitive()
{
const string key = "key";
var config = new SimpleTypedConfig<int>(key, "", 0);
var icm = GetConfigManager(config);
icm.UpdateConfig(new UpdateConfigOptions(key, 1, ConfigScope.CurrentUser) { AppliesTo = "az.abc" });
icm.UpdateConfig(new UpdateConfigOptions(key, 2, ConfigScope.CurrentUser) { AppliesTo = "Az.Abc" });
Assert.Equal(2, icm.ListConfigs(new ConfigFilter() { Keys = new[] { key }, AppliesTo = "az.abc" }).Single().Value);
Assert.Equal(2, icm.ListConfigs(new ConfigFilter() { Keys = new[] { key }, AppliesTo = "Az.Abc" }).Single().Value);
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanUpdateConfigHasSideEffect()
{
int calls = 0;
var mock = new Mock<ConfigDefinition>();
mock.Setup(c => c.Key).Returns("key");
mock.Setup(c => c.CanApplyTo).Returns(new[] { AppliesTo.Az });
mock.Setup(c => c.Apply(It.IsAny<bool>())).Callback((object v) =>
{
switch (++calls)
{
case 1:
Assert.True((bool)v);
break;
case 2:
Assert.False((bool)v);
break;
default:
break;
}
});
var config = mock.Object;
var icm = GetConfigManager(config);
icm.UpdateConfig(config.Key, true, ConfigScope.CurrentUser);
icm.UpdateConfig(config.Key, false, ConfigScope.CurrentUser);
Assert.Equal(2, calls);
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void ShouldNotUpdateConfigIfSideEffectThrows()
{
var config = new ConfigWithSideEffect((bool v) => throw new Exception("oops"));
var icm = GetConfigManager(config);
Assert.Throws<Exception>(() => icm.UpdateConfig(config.Key, !config.TypedDefaultValue, ConfigScope.CurrentUser));
Assert.Equal(config.TypedDefaultValue, icm.GetConfigValue<bool>(config.Key));
}
[Fact]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void ShouldThrowIfAppliesToIsWrong()
{
var key = "OnlyAppliesToAz";
var config = new SimpleTypedConfig<bool>(key, "", true, null, new AppliesTo[] {AppliesTo.Az});
var icm = GetConfigManager(config);
Assert.Throws<AzPSArgumentException>(() => icm.UpdateConfig(new UpdateConfigOptions(key, true, ConfigScope.CurrentUser) { AppliesTo = "Az.Accounts" }));
}
internal class ConfigWithSideEffect : TypedConfig<bool>
{
private readonly Action<bool> _sideEffect;
public ConfigWithSideEffect(Action<bool> sideEffect)
{
_sideEffect = sideEffect;
}
public override object DefaultValue => true;
public override string Key => "ConfigWithSideEffect";
public override string HelpMessage => "{HelpMessage}";
protected override void ApplyTyped(bool value)
{
base.ApplyTyped(value);
_sideEffect(value);
}
}
}
}

Просмотреть файл

@ -376,7 +376,8 @@ namespace Microsoft.WindowsAzure.Commands.Common.Test.Mocks
() =>
{
writeLocks[path] = false;
virtualStore[path] = Encoding.Default.GetString(buffer);
// trim \0 otherwise json fails to parse
virtualStore[path] = Encoding.UTF8.GetString(buffer).TrimEnd('\0');
}
);
}

Просмотреть файл

@ -0,0 +1,53 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces;
namespace Microsoft.Azure.PowerShell.Authentication.Test.Mocks
{
public class MockEnvironmentVariableProvider : IEnvironmentVariableProvider
{
private readonly IDictionary<string, string> _processVariables = new Dictionary<string, string>();
private readonly IDictionary<string, string> _userVariables = new Dictionary<string, string>();
private readonly IDictionary<string, string> _systemVariables = new Dictionary<string, string>();
public string Get(string variableName, EnvironmentVariableTarget target = EnvironmentVariableTarget.Process)
{
GetVariablesByTarget(target).TryGetValue(variableName, out var result);
return result;
}
private IDictionary<string, string> GetVariablesByTarget(EnvironmentVariableTarget target)
{
switch (target)
{
case EnvironmentVariableTarget.Process:
return _processVariables;
case EnvironmentVariableTarget.User:
return _userVariables;
case EnvironmentVariableTarget.Machine:
return _systemVariables;
default:
throw new ArgumentException(nameof(target));
}
}
public void Set(string variableName, string value, EnvironmentVariableTarget target = EnvironmentVariableTarget.Process)
{
GetVariablesByTarget(target)[variableName] = value;
}
}
}

Просмотреть файл

@ -0,0 +1,69 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Utilities;
using Microsoft.Rest.ClientRuntime.Azure.TestFramework;
using Xunit;
namespace Microsoft.Azure.Commands.Common.Authentication.Test
{
public class PSNamingUtilitiesTests
{
[Theory]
[InlineData("Az.Accounts", true)]
[InlineData("aZ.cOMPUTE", true)]
[InlineData("az.stackhci", true)]
[InlineData("", false)]
[InlineData("AzureRM.Profile", false)]
[InlineData("Az", false)]
[InlineData("AzAccounts", false)]
[InlineData("Get-AzContext", false)]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanRecognizeModuleName(string name, bool expected)
{
Assert.Equal(expected, PSNamingUtilities.IsModuleName(name));
}
[Theory]
[InlineData("Get-AzContext", true)]
[InlineData("update-azstorageaccount", true)]
[InlineData("Remove-AzEverything", true)]
[InlineData("Get-AzDataFactoryV2", true)]
[InlineData("", false)]
[InlineData("Az.Accounts", false)]
[InlineData("Az", false)]
[InlineData("NewAzVM", false)]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanRecognizeCmdletName(string name, bool expected)
{
Assert.Equal(expected, PSNamingUtilities.IsCmdletName(name));
}
[Theory]
[InlineData("Az.Accounts", true)]
[InlineData("aZ.cOMPUTE", true)]
[InlineData("az.stackhci", true)]
[InlineData("Get-AzContext", true)]
[InlineData("Remove-AzEverything", true)]
[InlineData("Get-AzDataFactoryV2", true)]
[InlineData("", false)]
[InlineData("Az", false)]
[InlineData("NewAzVM", false)]
[Trait(TestTraits.AcceptanceType, TestTraits.CheckIn)]
public void CanRecognizeModuleOrCmdletName(string name, bool expected)
{
Assert.Equal(expected, PSNamingUtilities.IsModuleOrCmdletName(name));
}
}
}

Просмотреть файл

@ -24,10 +24,11 @@ using Microsoft.Azure.Commands.Common.Authentication.Abstractions.Core;
using Microsoft.Azure.Commands.Common.Authentication.Authentication.TokenCache;
using Microsoft.Azure.Commands.Common.Authentication.Factories;
using Microsoft.Azure.Commands.Common.Authentication.Properties;
using Microsoft.Azure.Commands.Common.Authentication.Config;
using Newtonsoft.Json;
using TraceLevel = System.Diagnostics.TraceLevel;
using System.Collections.Generic;
namespace Microsoft.Azure.Commands.Common.Authentication
{
@ -244,12 +245,23 @@ namespace Microsoft.Azure.Commands.Common.Authentication
session.TokenCacheDirectory = autoSave.CacheDirectory;
session.TokenCacheFile = autoSave.CacheFile;
InitializeConfigs(session);
InitializeDataCollection(session);
session.RegisterComponent(HttpClientOperationsFactory.Name, () => HttpClientOperationsFactory.Create());
session.TokenCache = session.TokenCache ?? new AzureTokenCache();
return session;
}
private static void InitializeConfigs(AzureSession session)
{
var fallbackList = new List<string>()
{
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".Azure", "PSConfig.json"),
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), ".Azure", "PSConfig.json")
};
new ConfigInitializer(fallbackList).InitializeForAzureSession(session);
}
public class AdalSession : AzureSession
{
#if !NETSTANDARD

Просмотреть файл

@ -0,0 +1,146 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Abstractions;
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces;
using Microsoft.Azure.PowerShell.Common.Config;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
namespace Microsoft.Azure.Commands.Common.Authentication.Config
{
/// <summary>
/// Initializes the config file and config manager.
/// </summary>
internal class ConfigInitializer
{
internal IDataStore DataStore { get; set; } = new DiskDataStore();
private static readonly object _fsLock = new object();
internal IEnvironmentVariableProvider EnvironmentVariableProvider { get; set; } = new DefaultEnvironmentVariableProvider();
private readonly string _pathToConfigFile;
public ConfigInitializer(IEnumerable<string> paths)
{
_ = paths ?? throw new ArgumentNullException(nameof(paths));
_pathToConfigFile = GetPathToConfigFile(paths);
}
/// <summary>
/// Loop through the fallback list of paths of the config file. Returns the first usable one.
/// </summary>
/// <param name="paths">A list of paths to the config file. When one is not usable, it will fallback to the next.</param>
/// <returns></returns>
/// <exception cref="ApplicationException">When no one in the list is usable.</exception>
private string GetPathToConfigFile(IEnumerable<string> paths)
{
// find first exist path and use it
foreach (string path in paths)
{
if (DataStore.FileExists(path))
{
return path;
}
}
// if not found, use the first writable path
foreach (string path in paths)
{
try
{
DirectoryInfo dir = new FileInfo(path).Directory;
DataStore.CreateDirectory(dir.FullName); // create directory if not exists
using (var _ = DataStore.OpenForExclusiveWrite(path)) { }
return path;
}
catch (Exception)
{
continue;
}
}
throw new ApplicationException($"Failed to store the config file. Please make sure any one of the following paths is accessible: {string.Join(", ", paths)}");
}
internal IConfigManager GetConfigManager()
{
lock (_fsLock)
{
ValidateConfigFile();
}
return new ConfigManager(_pathToConfigFile, DataStore, EnvironmentVariableProvider);
}
private void ValidateConfigFileContent()
{
string json = DataStore.ReadFileAsText(_pathToConfigFile);
bool isValidJson = true;
try
{
JObject.Parse(json);
}
catch (Exception)
{
isValidJson = false;
}
if (string.IsNullOrEmpty(json) || !isValidJson)
{
Debug.Write($"[ConfigInitializer] Failed to parse the config file at {_pathToConfigFile}. Clearing the file.");
ResetConfigFileToDefault();
}
}
private void ValidateConfigFile()
{
if (!DataStore.FileExists(_pathToConfigFile))
{
ResetConfigFileToDefault();
}
else
{
ValidateConfigFileContent();
}
}
private void ResetConfigFileToDefault()
{
try
{
DataStore.WriteFile(_pathToConfigFile, @"{}");
}
catch (Exception ex)
{
// do not halt for IO exception
Debug.WriteLine(ex.Message);
}
}
// todo: tests initializes configs in a different way. Maybe there should be an abstraction IConfigInitializer and two concrete classes ConfigInitializer + TestConfigInitializer
internal void InitializeForAzureSession(AzureSession session)
{
IConfigManager configManager = GetConfigManager();
session.RegisterComponent(nameof(IConfigManager), () => configManager);
RegisterConfigs(configManager);
configManager.BuildConfig();
}
private void RegisterConfigs(IConfigManager configManager)
{
}
}
}

Просмотреть файл

@ -0,0 +1,468 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Abstractions;
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal;
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces;
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Providers;
using Microsoft.Azure.Commands.Common.Exceptions;
using Microsoft.Azure.Commands.ResourceManager.Common;
using Microsoft.Azure.PowerShell.Common.Config;
using Microsoft.WindowsAzure.Commands.Utilities.Common;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Management.Automation;
using System.Threading;
namespace Microsoft.Azure.Commands.Common.Authentication.Config
{
/// <summary>
/// Default implementation of <see cref="IConfigManager"/>, providing CRUD abilities to the configs.
/// </summary>
internal class ConfigManager : IConfigManager
{
/// <inheritdoc/>
public string ConfigFilePath { get; private set; }
private IConfigurationRoot _root;
private readonly ConcurrentDictionary<string, ConfigDefinition> _configDefinitionMap = new ConcurrentDictionary<string, ConfigDefinition>(StringComparer.OrdinalIgnoreCase);
private IOrderedEnumerable<KeyValuePair<string, ConfigDefinition>> OrderedConfigDefinitionMap => _configDefinitionMap.OrderBy(x => x.Key);
private readonly ConcurrentDictionary<string, string> EnvironmentVariableToKeyMap = new ConcurrentDictionary<string, string>();
private readonly IEnvironmentVariableProvider _environmentVariableProvider;
private readonly IDataStore _dataStore;
private readonly JsonConfigWriter _jsonConfigWriter;
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
/// <summary>
/// Creates an instance of <see cref="ConfigManager"/>.
/// </summary>
/// <param name="configFilePath">Path to the config file.</param>
/// <param name="dataStore">Provider of file system APIs.</param>
/// <param name="environmentVariableProvider">Provider of environment variable APIs.</param>
internal ConfigManager(string configFilePath, IDataStore dataStore, IEnvironmentVariableProvider environmentVariableProvider)
{
_ = dataStore ?? throw new AzPSArgumentNullException($"{nameof(dataStore)} cannot be null.", nameof(dataStore));
_ = configFilePath ?? throw new AzPSArgumentNullException($"{nameof(configFilePath)} cannot be null.", nameof(configFilePath));
_ = environmentVariableProvider ?? throw new AzPSArgumentNullException($"{nameof(environmentVariableProvider)} cannot be null.", nameof(environmentVariableProvider));
ConfigFilePath = configFilePath;
_environmentVariableProvider = environmentVariableProvider;
_dataStore = dataStore;
_jsonConfigWriter = new JsonConfigWriter(ConfigFilePath, _dataStore);
}
/// <summary>
/// Rebuild config hierarchy and load from the providers.
/// </summary>
public void BuildConfig()
{
var builder = new ConfigurationBuilder();
if (SharedUtilities.IsWindowsPlatform())
{
// User and machine level environment variables are only on Windows
builder.AddEnvironmentVariables(Constants.ConfigProviderIds.MachineEnvironment, new EnvironmentVariablesConfigurationOptions()
{
EnvironmentVariableProvider = _environmentVariableProvider,
EnvironmentVariableTarget = EnvironmentVariableTarget.Machine,
EnvironmentVariableToKeyMap = EnvironmentVariableToKeyMap
})
.AddEnvironmentVariables(Constants.ConfigProviderIds.UserEnvironment, new EnvironmentVariablesConfigurationOptions()
{
EnvironmentVariableProvider = _environmentVariableProvider,
EnvironmentVariableTarget = EnvironmentVariableTarget.User,
EnvironmentVariableToKeyMap = EnvironmentVariableToKeyMap
});
}
builder.AddJsonStream(Constants.ConfigProviderIds.UserConfig, _dataStore.ReadFileAsStream(ConfigFilePath))
.AddEnvironmentVariables(Constants.ConfigProviderIds.ProcessEnvironment, new EnvironmentVariablesConfigurationOptions()
{
EnvironmentVariableProvider = _environmentVariableProvider,
EnvironmentVariableTarget = EnvironmentVariableTarget.Process,
EnvironmentVariableToKeyMap = EnvironmentVariableToKeyMap
})
.AddUnsettableInMemoryCollection(Constants.ConfigProviderIds.ProcessConfig);
_lock.EnterReadLock();
try
{
_root = builder.Build();
}
finally
{
_lock.ExitReadLock();
}
}
/// <inheritdoc/>
public void RegisterConfig(ConfigDefinition config)
{
// check if key already taken
if (_configDefinitionMap.ContainsKey(config.Key))
{
if (_configDefinitionMap[config.Key] == config)
{
Debug.WriteLine($"Config with key [{config.Key}] was registered twice");
}
else
{
throw new AzPSArgumentException($"Duplicated config key. [{config.Key}] was already taken.", nameof(config.Key));
}
return;
}
// configure environment variable providers
if (!string.IsNullOrEmpty(config.EnvironmentVariableName))
{
EnvironmentVariableToKeyMap[config.EnvironmentVariableName] = ConfigPathHelper.GetPathOfConfig(config.Key);
}
_configDefinitionMap[config.Key] = config;
}
/// <inheritdoc/>
public T GetConfigValue<T>(string key, object invocation = null)
{
if (invocation != null && !(invocation is InvocationInfo))
{
throw new AzPSArgumentException($"Type error: type of {nameof(invocation)} must be {nameof(InvocationInfo)}", nameof(invocation));
}
return GetConfigValueInternal<T>(key, new InvocationInfoAdapter((InvocationInfo)invocation));
}
internal T GetConfigValueInternal<T>(string key, InternalInvocationInfo invocation) => (T)GetConfigValueInternal(key, invocation);
internal object GetConfigValueInternal(string key, InternalInvocationInfo invocation)
{
_ = key ?? throw new AzPSArgumentNullException($"{nameof(key)} cannot be null.", nameof(key));
if (!_configDefinitionMap.TryGetValue(key, out ConfigDefinition definition) || definition == null)
{
throw new AzPSArgumentException($"Config with key [{key}] was not registered.", nameof(key));
}
foreach (var path in ConfigPathHelper.EnumerateConfigPaths(key, invocation))
{
IConfigurationSection section = _root.GetSection(path);
if (section.Exists())
{
(object value, _) = GetConfigValueOrDefault(section, definition);
WriteDebug($"[ConfigManager] Got [{value}] from [{key}], Module = [{invocation?.ModuleName}], Cmdlet = [{invocation?.CmdletName}].");
return value;
}
}
WriteDebug($"[ConfigManager] Got nothing from [{key}], Module = [{invocation?.ModuleName}], Cmdlet = [{invocation?.CmdletName}]. Returning default value [{definition.DefaultValue}].");
return definition.DefaultValue;
}
private void WriteDebug(string message)
{
WriteMessage(message, AzureRMCmdlet.WriteDebugKey);
}
private void WriteMessage(string message, string eventHandlerKey)
{
try
{
if (AzureSession.Instance.TryGetComponent(eventHandlerKey, out EventHandler<StreamEventArgs> writeDebug))
{
writeDebug.Invoke(this, new StreamEventArgs() { Message = message });
}
}
catch (Exception)
{
// do not throw when session is not initialized
}
}
private void WriteWarning(string message)
{
WriteMessage(message, AzureRMCmdlet.WriteWarningKey);
}
/// <inheritdoc/>
public IEnumerable<ConfigDefinition> ListConfigDefinitions()
{
return OrderedConfigDefinitionMap.Select(x => x.Value);
}
/// <inheritdoc/>
public IEnumerable<ConfigData> ListConfigs(ConfigFilter filter = null)
{
IList<ConfigData> results = new List<ConfigData>();
// include all values
ISet<string> noNeedForDefault = new HashSet<string>();
foreach (var appliesToSection in _root.GetChildren())
{
foreach (var configSection in appliesToSection.GetChildren())
{
string key = configSection.Key;
if (_configDefinitionMap.TryGetValue(key, out var configDefinition))
{
(object value, string providerId) = GetConfigValueOrDefault(configSection, configDefinition);
ConfigScope scope = ConfigScopeHelper.GetScopeByProviderId(providerId);
results.Add(new ConfigData(configDefinition, value, scope, appliesToSection.Key));
// if a config is already set at global level, there's no need to return its default value
if (string.Equals(ConfigFilter.GlobalAppliesTo, appliesToSection.Key, StringComparison.OrdinalIgnoreCase))
{
noNeedForDefault.Add(configDefinition.Key);
}
}
}
}
// include default values
IEnumerable<string> keys = filter?.Keys ?? Enumerable.Empty<string>();
bool isRegisteredKey(string key) => _configDefinitionMap.Keys.Contains(key, StringComparer.OrdinalIgnoreCase);
IEnumerable<ConfigDefinition> configDefinitions = keys.Any() ? keys.Where(isRegisteredKey).Select(key => _configDefinitionMap[key]) : OrderedConfigDefinitionMap.Select(x => x.Value);
configDefinitions.Where(x => !noNeedForDefault.Contains(x.Key)).Select(x => GetDefaultConfigData(x)).ForEach(x => results.Add(x));
if (keys.Any())
{
results = results.Where(x => keys.Contains(x.Definition.Key, StringComparer.OrdinalIgnoreCase)).ToList();
}
string appliesTo = filter?.AppliesTo;
if (!string.IsNullOrEmpty(appliesTo))
{
results = results.Where(x => string.Equals(appliesTo, x.AppliesTo, StringComparison.OrdinalIgnoreCase)).ToList();
}
return results;
}
/// <summary>
/// Get the value and the ID of the corresponding provider of the config.
/// </summary>
/// <param name="section">The section that stores the config.</param>
/// <param name="definition">The definition of the config.</param>
/// <returns>A tuple containing the value of the config and the ID of the provider from which the value is got.</returns>
/// <remarks>Exceptions are handled gracefully in this method.</remarks>
private (object value, string providerId) GetConfigValueOrDefault(IConfigurationSection section, ConfigDefinition definition)
{
try
{
return section.Get(definition.ValueType);
}
catch (InvalidOperationException ex)
{
WriteWarning($"[ConfigManager] Failed to get value for [{definition.Key}]. Using the default value [{definition.DefaultValue}] instead. Error: {ex.Message}. {ex.InnerException?.Message}");
WriteDebug($"[ConfigManager] Exception: {ex.Message}, stack trace: \n{ex.StackTrace}");
return (definition.DefaultValue, Constants.ConfigProviderIds.None);
}
}
private ConfigData GetDefaultConfigData(ConfigDefinition configDefinition)
{
return new ConfigData(configDefinition,
configDefinition.DefaultValue,
ConfigScope.Default,
ConfigFilter.GlobalAppliesTo);
}
// A bulk update API is currently unnecessary as we don't expect users to do that.
// But if telemetry data proves it's a demanded feature, we might add it in the future.
// public IEnumerable<Config> UpdateConfigs(IEnumerable<UpdateConfigOptions> updateConfigOptions) => updateConfigOptions.Select(UpdateConfig);
/// <inheritdoc/>
public ConfigData UpdateConfig(string key, object value, ConfigScope scope)
{
return UpdateConfig(new UpdateConfigOptions(key, value, scope));
}
/// <inheritdoc/>
public ConfigData UpdateConfig(UpdateConfigOptions options)
{
if (options == null)
{
throw new AzPSArgumentNullException($"{nameof(options)} cannot be null when updating config.", nameof(options));
}
if (!_configDefinitionMap.TryGetValue(options.Key, out ConfigDefinition definition) || definition == null)
{
throw new AzPSArgumentException($"Config with key [{options.Key}] was not registered.", nameof(options.Key));
}
try
{
definition.Validate(options.Value);
}
catch (Exception e)
{
throw new AzPSArgumentException(e.Message, e);
}
if (AppliesToHelper.TryParseAppliesTo(options.AppliesTo, out var appliesTo) && !definition.CanApplyTo.Contains(appliesTo))
{
throw new AzPSArgumentException($"[{options.AppliesTo}] is not a valid value for AppliesTo - it doesn't match any of ({AppliesToHelper.FormatOptions(definition.CanApplyTo)}).", nameof(options.AppliesTo));
}
definition.Apply(options.Value);
string path = ConfigPathHelper.GetPathOfConfig(options.Key, options.AppliesTo);
switch (options.Scope)
{
case ConfigScope.Process:
SetProcessLevelConfig(path, options.Value);
break;
case ConfigScope.CurrentUser:
SetUserLevelConfig(path, options.Value);
break;
}
WriteDebug($"[ConfigManager] Updated [{options.Key}] to [{options.Value}]. Scope = [{options.Scope}], AppliesTo = [{options.AppliesTo}]");
return new ConfigData(definition, options.Value, options.Scope, options.AppliesTo);
}
private void SetProcessLevelConfig(string path, object value)
{
GetProcessLevelConfigProvider().Set(path, value.ToString());
}
private UnsettableMemoryConfigurationProvider GetProcessLevelConfigProvider()
{
return _root.GetConfigurationProvider(Constants.ConfigProviderIds.ProcessConfig) as UnsettableMemoryConfigurationProvider;
}
private void SetUserLevelConfig(string path, object value)
{
_lock.EnterWriteLock();
try
{
_jsonConfigWriter.Update(path, value);
}
finally
{
_lock.ExitWriteLock();
}
BuildConfig(); // reload the config values
}
/// <inheritdoc/>
public void ClearConfig(string key, ConfigScope scope) => ClearConfig(new ClearConfigOptions(key, scope));
/// <inheritdoc/>
public void ClearConfig(ClearConfigOptions options)
{
_ = options ?? throw new AzPSArgumentNullException($"{nameof(options)} cannot be null.", nameof(options));
bool clearAll = string.IsNullOrEmpty(options.Key);
if (clearAll)
{
ClearAllConfigs(options);
}
else
{
ClearConfigByKey(options);
}
}
private void ClearAllConfigs(ClearConfigOptions options)
{
switch (options.Scope)
{
case ConfigScope.Process:
ClearProcessLevelAllConfigs(options);
break;
case ConfigScope.CurrentUser:
ClearUserLevelAllConfigs(options);
break;
default:
throw new AzPSArgumentException($"[{options.Scope}] is not a valid scope when clearing configs.", nameof(options.Scope));
}
WriteDebug($"[ConfigManager] Cleared all the configs. Scope = [{options.Scope}].");
}
private void ClearProcessLevelAllConfigs(ClearConfigOptions options)
{
var configProvider = GetProcessLevelConfigProvider();
if (string.IsNullOrEmpty(options.AppliesTo))
{
configProvider.UnsetAll();
}
else
{
foreach (var key in _configDefinitionMap.Keys)
{
configProvider.Unset(ConfigPathHelper.GetPathOfConfig(key, options.AppliesTo));
}
}
}
private void ClearUserLevelAllConfigs(ClearConfigOptions options)
{
_lock.EnterWriteLock();
try
{
if (string.IsNullOrEmpty(options.AppliesTo))
{
_jsonConfigWriter.ClearAll();
}
else
{
foreach (var key in _configDefinitionMap.Keys)
{
_jsonConfigWriter.Clear(ConfigPathHelper.GetPathOfConfig(key, options.AppliesTo));
}
}
}
finally
{
_lock.ExitWriteLock();
}
BuildConfig();
}
private void ClearConfigByKey(ClearConfigOptions options)
{
if (!_configDefinitionMap.TryGetValue(options.Key, out ConfigDefinition definition))
{
throw new AzPSArgumentException($"Config with key [{options.Key}] was not registered.", nameof(options.Key));
}
string path = ConfigPathHelper.GetPathOfConfig(definition.Key, options.AppliesTo);
switch (options.Scope)
{
case ConfigScope.Process:
GetProcessLevelConfigProvider().Unset(path);
break;
case ConfigScope.CurrentUser:
ClearUserLevelConfigByKey(path);
break;
}
WriteDebug($"[ConfigManager] Cleared [{options.Key}]. Scope = [{options.Scope}], AppliesTo = [{options.AppliesTo}]");
}
private void ClearUserLevelConfigByKey(string key)
{
_lock.EnterWriteLock();
try
{
_jsonConfigWriter.Clear(key);
}
finally
{
_lock.ExitWriteLock();
}
BuildConfig();
}
}
}

Просмотреть файл

@ -0,0 +1,95 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Utilities;
using Microsoft.Azure.PowerShell.Common.Config;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Microsoft.Azure.Commands.Common.Authentication.Config
{
/// <summary>
/// Helper class to deal with AppliesTo (how large is the scope that the config affects Azure PowerShell).
/// </summary>
public static class AppliesToHelper
{
/// <summary>
/// Tries to parse a user-input text to an <see cref="AppliesTo"/> enum.
/// </summary>
/// <param name="text">Input from user.</param>
/// <param name="appliesTo">Result if successful.</param>
/// <returns>True if parsed successfully.</returns>
public static bool TryParseAppliesTo(string text, out AppliesTo appliesTo)
{
if (string.IsNullOrEmpty(text) || string.Equals(ConfigFilter.GlobalAppliesTo, text, StringComparison.OrdinalIgnoreCase))
{
appliesTo = AppliesTo.Az;
return true;
}
if (PSNamingUtilities.IsModuleName(text))
{
appliesTo = AppliesTo.Module;
return true;
}
if (PSNamingUtilities.IsCmdletName(text))
{
appliesTo = AppliesTo.Cmdlet;
return true;
}
appliesTo = AppliesTo.Az;
return false;
}
/// <summary>
/// Gets a comma-divided string for human-readable description of the AppliesTo options.
/// </summary>
/// <param name="options">Options of AppliesTo.</param>
/// <returns>The formated string.</returns>
internal static string FormatOptions(IReadOnlyCollection<AppliesTo> options)
{
if (options == null || !options.Any())
{
throw new ArgumentException($"Make sure the config definition has a non-empty {nameof(ConfigDefinition.CanApplyTo)}.", nameof(options));
}
var sb = new StringBuilder();
bool isFirst = true;
foreach (var option in options)
{
if (!isFirst)
{
sb.Append(", ");
isFirst = false;
}
switch (option)
{
case AppliesTo.Az:
sb.Append(ConfigFilter.GlobalAppliesTo);
break;
case AppliesTo.Cmdlet:
sb.Append("name of a cmdlet");
break;
case AppliesTo.Module:
sb.Append("name of a module");
break;
}
}
return sb.ToString();
}
}
}

Просмотреть файл

@ -0,0 +1,59 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal;
using Microsoft.Azure.PowerShell.Common.Config;
using System.Collections.Generic;
namespace Microsoft.Azure.Commands.Common.Authentication.Config
{
/// <summary>
/// Helper class to deal with the full path where configs are stored.
/// </summary>
internal static class ConfigPathHelper
{
/// <summary>
/// Gets a list of paths to check when getting a config value by key and invocation info.
/// </summary>
/// <param name="key">The key in the config definition.</param>
/// <param name="invocation">Command invocation info, containing command name and module name.</param>
public static IEnumerable<string> EnumerateConfigPaths(string key, InternalInvocationInfo invocation = null)
{
if (!string.IsNullOrEmpty(invocation?.CmdletName))
{
yield return GetPathOfConfig(key, invocation.CmdletName);
}
if (!string.IsNullOrEmpty(invocation?.ModuleName))
{
yield return GetPathOfConfig(key, invocation.ModuleName);
}
yield return GetPathOfConfig(key);
}
/// <summary>
/// Get the path (full key) of a config by its key and what it applies to.
/// </summary>
/// <param name="key"></param>
/// <param name="appliesTo">Global appliesTo by default.</param>
/// <returns></returns>
internal static string GetPathOfConfig(string key, string appliesTo = null)
{
if (string.IsNullOrEmpty(appliesTo))
{
appliesTo = ConfigFilter.GlobalAppliesTo;
}
return appliesTo + ConfigurationPath.KeyDelimiter + key;
}
}
}

Просмотреть файл

@ -0,0 +1,40 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Exceptions;
using Microsoft.Azure.PowerShell.Common.Config;
namespace Microsoft.Azure.Commands.Common.Authentication.Config
{
internal static class ConfigScopeHelper
{
public static ConfigScope GetScopeByProviderId(string id)
{
switch (id)
{
case Constants.ConfigProviderIds.MachineEnvironment:
case Constants.ConfigProviderIds.UserEnvironment:
case Constants.ConfigProviderIds.UserConfig:
return ConfigScope.CurrentUser;
case Constants.ConfigProviderIds.ProcessEnvironment:
case Constants.ConfigProviderIds.ProcessConfig:
return ConfigScope.Process;
case Constants.ConfigProviderIds.None:
return ConfigScope.Default;
default:
throw new AzPSArgumentOutOfRangeException($"Unexpected provider ID [{id}]. See {nameof(Constants.ConfigProviderIds)} class for all valid IDs.", nameof(id));
}
}
}
}

Просмотреть файл

@ -0,0 +1,35 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces;
using System;
namespace Microsoft.Azure.Commands.Common.Authentication.Config
{
/// <summary>
/// Default implementation of <see cref="IEnvironmentVariableProvider"/> that utilizes the <see cref="System.Environment"/> API.
/// </summary>
internal class DefaultEnvironmentVariableProvider : IEnvironmentVariableProvider
{
public string Get(string variableName, EnvironmentVariableTarget target = EnvironmentVariableTarget.Process)
{
return Environment.GetEnvironmentVariable(variableName, target);
}
public void Set(string variableName, string value, EnvironmentVariableTarget target = EnvironmentVariableTarget.Process)
{
Environment.SetEnvironmentVariable(variableName, value, target);
}
}
}

Просмотреть файл

@ -0,0 +1,153 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.IO;
namespace Microsoft.Azure.Commands.Common.Authentication.Config
{
/// <summary>
/// Helper for updating the config JSON file.
/// </summary>
internal class JsonConfigWriter
{
private readonly string _jsonConfigPath;
private readonly IDataStore _dataStore;
public JsonConfigWriter(string jsonConfigPath, IDataStore dataStore)
{
_jsonConfigPath = jsonConfigPath;
_dataStore = dataStore;
}
/// <summary>
/// Update a config value.
/// </summary>
/// <param name="key">The full path of the config.</param>
/// <param name="value">The value to update.</param>
internal void Update(string key, object value) => TryUpdate(key, true, (JObject parent, string propertyName) =>
{
var prop = parent.Property(propertyName);
if (prop == null)
{
prop = new JProperty(propertyName, value);
parent.Add(prop);
}
else
{
prop.Value = IsMultiContent(value) ? new JArray(value) : JToken.FromObject(value);
}
});
private bool IsMultiContent(object value)
{
return value is Array;
}
/// <summary>
/// Locates the node by key in the JSON object, and performs a general update (add, modify or remove a property).
/// </summary>
/// <param name="key">The full path to the config.</param>
/// <param name="createWhenNotExist">Whether to create the JSON node when part of the path is missing.</param>
/// <param name="updateAction">The concrete action to perform. First argument is the parent node in the JSON object, second is the name of the property to update.</param>
/// <returns>Whether the update is successful.</returns>
private bool TryUpdate(string key, bool createWhenNotExist, Action<JObject, string> updateAction)
{
string json = _dataStore.ReadFileAsText(_jsonConfigPath);
JObject root = JObject.Parse(json);
string[] segments = key.Split(ConfigurationPath.KeyDelimiter.ToCharArray());
JObject parent = LocateParentNode(root, segments, createWhenNotExist);
if (parent == null)
{
return false;
}
string propertyName = segments[segments.Length - 1];
updateAction(parent, propertyName);
// hack: to avoid last version of the config remaining in the file, empty it first
_dataStore.WriteFile(_jsonConfigPath, string.Empty);
JsonSerializer serializer = new JsonSerializer
{
Formatting = Formatting.Indented
};
using (Stream fs = _dataStore.OpenForExclusiveWrite(_jsonConfigPath))
using (StreamWriter sw = new StreamWriter(fs))
using (var writer = new JsonTextWriter(sw) { Indentation = 4 })
{
serializer.Serialize(writer, root);
}
return true;
}
private static JObject LocateParentNode(JObject root, string[] segments, bool createWhenNotExist)
{
JObject node = root;
for (int i = 0; i < segments.Length - 1; ++i)
{
string segment = segments[i];
// JObject.TryGetValue() supports case insensitivity
// otherwise we might get duplicated keys with different casing in the config file
if (node.TryGetValue(segment, StringComparison.OrdinalIgnoreCase, out JToken match))
{
node = (JObject)match;
}
else
{
if (createWhenNotExist)
{
node[segment] = new JObject();
node = (JObject)node[segment];
}
else
{
return null;
}
}
}
return node;
}
/// <summary>
/// Clear a config by key.
/// </summary>
/// <param name="key">The full path to the config.</param>
internal void Clear(string key) => TryUpdate(key, false, (parent, propertyName) =>
{
if (parent.Property(propertyName) != null)
{
parent.Remove(propertyName);
}
// if the config is never set, there's no need to clear.
});
/// <summary>
/// Clear all the configs.
/// </summary>
internal void ClearAll()
{
_dataStore.WriteFile(_jsonConfigPath, @"{}");
}
}
}

Просмотреть файл

@ -0,0 +1,28 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal
{
/// <summary>
/// Options class used by the <see cref="ConfigurationBinder"/>.
/// </summary>
internal class BinderOptions
{
/// <summary>
/// When false (the default), the binder will only attempt to set public properties.
/// If true, the binder will attempt to set all non read-only properties.
/// </summary>
public bool BindNonPublicProperties { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,587 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal
{
/// <summary>
/// Static helper class that allows binding strongly typed objects to configuration values.
/// </summary>
internal static class ConfigurationBinder
{
/// <summary>
/// Attempts to bind the configuration instance to a new instance of type T.
/// If this configuration section has a value, that will be used.
/// Otherwise binding by matching property names against configuration keys recursively.
/// </summary>
/// <typeparam name="T">The type of the new instance to bind.</typeparam>
/// <param name="configuration">The configuration instance to bind.</param>
/// <returns>The new instance of T if successful, default(T) otherwise.</returns>
public static (T, string) Get<T>(this IConfiguration configuration)
=> configuration.Get<T>(_ => { });
/// <summary>
/// Attempts to bind the configuration instance to a new instance of type T.
/// If this configuration section has a value, that will be used.
/// Otherwise binding by matching property names against configuration keys recursively.
/// </summary>
/// <typeparam name="T">The type of the new instance to bind.</typeparam>
/// <param name="configuration">The configuration instance to bind.</param>
/// <param name="configureOptions">Configures the binder options.</param>
/// <returns>The new instance of T if successful, default(T) otherwise.</returns>
public static (T, string) Get<T>(this IConfiguration configuration, Action<BinderOptions> configureOptions)
{
if (configuration == null)
{
throw new ArgumentNullException(nameof(configuration));
}
(object result, string providerId) = configuration.Get(typeof(T), configureOptions);
if (result == null)
{
return (default(T), providerId);
}
return ((T)result, providerId);
}
/// <summary>
/// Attempts to bind the configuration instance to a new instance of type T.
/// If this configuration section has a value, that will be used.
/// Otherwise binding by matching property names against configuration keys recursively.
/// </summary>
/// <param name="configuration">The configuration instance to bind.</param>
/// <param name="type">The type of the new instance to bind.</param>
/// <returns>The new instance if successful, null otherwise.</returns>
public static (object, string) Get(this IConfiguration configuration, Type type)
=> configuration.Get(type, _ => { });
/// <summary>
/// Attempts to bind the configuration instance to a new instance of type T.
/// If this configuration section has a value, that will be used.
/// Otherwise binding by matching property names against configuration keys recursively.
/// </summary>
/// <param name="configuration">The configuration instance to bind.</param>
/// <param name="type">The type of the new instance to bind.</param>
/// <param name="configureOptions">Configures the binder options.</param>
/// <returns>The new instance if successful, null otherwise.</returns>
public static (object, string) Get(this IConfiguration configuration, Type type, Action<BinderOptions> configureOptions)
{
if (configuration == null)
{
throw new ArgumentNullException(nameof(configuration));
}
var options = new BinderOptions();
configureOptions?.Invoke(options);
object bound = BindInstance(type, instance: null, config: configuration, options: options);
string providerId = (configuration as IConfigurationSection).GetValueWithProviderId().Item2;
return (bound, providerId);
}
/// <summary>
/// Attempts to bind the given object instance to the configuration section specified by the key by matching property names against configuration keys recursively.
/// </summary>
/// <param name="configuration">The configuration instance to bind.</param>
/// <param name="key">The key of the configuration section to bind.</param>
/// <param name="instance">The object to bind.</param>
public static void Bind(this IConfiguration configuration, string key, object instance)
=> configuration.GetSection(key).Bind(instance);
/// <summary>
/// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.
/// </summary>
/// <param name="configuration">The configuration instance to bind.</param>
/// <param name="instance">The object to bind.</param>
public static void Bind(this IConfiguration configuration, object instance)
=> configuration.Bind(instance, o => { });
/// <summary>
/// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.
/// </summary>
/// <param name="configuration">The configuration instance to bind.</param>
/// <param name="instance">The object to bind.</param>
/// <param name="configureOptions">Configures the binder options.</param>
public static void Bind(this IConfiguration configuration, object instance, Action<BinderOptions> configureOptions)
{
if (configuration == null)
{
throw new ArgumentNullException(nameof(configuration));
}
if (instance != null)
{
var options = new BinderOptions();
configureOptions?.Invoke(options);
BindInstance(instance.GetType(), instance, configuration, options);
}
}
/// <summary>
/// Extracts the value with the specified key and converts it to type T.
/// </summary>
/// <typeparam name="T">The type to convert the value to.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="key">The key of the configuration section's value to convert.</param>
/// <returns>The converted value.</returns>
public static T GetValue<T>(this IConfiguration configuration, string key)
{
return GetValue(configuration, key, default(T));
}
/// <summary>
/// Extracts the value with the specified key and converts it to type T.
/// </summary>
/// <typeparam name="T">The type to convert the value to.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="key">The key of the configuration section's value to convert.</param>
/// <param name="defaultValue">The default value to use if no value is found.</param>
/// <returns>The converted value.</returns>
public static T GetValue<T>(this IConfiguration configuration, string key, T defaultValue)
{
return (T)GetValue(configuration, typeof(T), key, defaultValue);
}
/// <summary>
/// Extracts the value with the specified key and converts it to the specified type.
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="type">The type to convert the value to.</param>
/// <param name="key">The key of the configuration section's value to convert.</param>
/// <returns>The converted value.</returns>
public static object GetValue(this IConfiguration configuration, Type type, string key)
{
return GetValue(configuration, type, key, defaultValue: null);
}
/// <summary>
/// Extracts the value with the specified key and converts it to the specified type.
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="type">The type to convert the value to.</param>
/// <param name="key">The key of the configuration section's value to convert.</param>
/// <param name="defaultValue">The default value to use if no value is found.</param>
/// <returns>The converted value.</returns>
public static object GetValue(this IConfiguration configuration, Type type, string key, object defaultValue)
{
IConfigurationSection section = configuration.GetSection(key);
string value = section.Value;
if (value != null)
{
return ConvertValue(type, value, section.Path);
}
return defaultValue;
}
private static void BindNonScalar(this IConfiguration configuration, object instance, BinderOptions options)
{
if (instance != null)
{
foreach (PropertyInfo property in GetAllProperties(instance.GetType().GetTypeInfo()))
{
BindProperty(property, instance, configuration, options);
}
}
}
private static void BindProperty(PropertyInfo property, object instance, IConfiguration config, BinderOptions options)
{
// We don't support set only, non public, or indexer properties
if (property.GetMethod == null ||
(!options.BindNonPublicProperties && !property.GetMethod.IsPublic) ||
property.GetMethod.GetParameters().Length > 0)
{
return;
}
object propertyValue = property.GetValue(instance);
bool hasSetter = property.SetMethod != null && (property.SetMethod.IsPublic || options.BindNonPublicProperties);
if (propertyValue == null && !hasSetter)
{
// Property doesn't have a value and we cannot set it so there is no
// point in going further down the graph
return;
}
propertyValue = BindInstance(property.PropertyType, propertyValue, config.GetSection(property.Name), options);
if (propertyValue != null && hasSetter)
{
property.SetValue(instance, propertyValue);
}
}
private static object BindToCollection(TypeInfo typeInfo, IConfiguration config, BinderOptions options)
{
Type type = typeof(List<>).MakeGenericType(typeInfo.GenericTypeArguments[0]);
object instance = Activator.CreateInstance(type);
BindCollection(instance, type, config, options);
return instance;
}
// Try to create an array/dictionary instance to back various collection interfaces
private static object AttemptBindToCollectionInterfaces(Type type, IConfiguration config, BinderOptions options)
{
TypeInfo typeInfo = type.GetTypeInfo();
if (!typeInfo.IsInterface)
{
return null;
}
Type collectionInterface = FindOpenGenericInterface(typeof(IReadOnlyList<>), type);
if (collectionInterface != null)
{
// IEnumerable<T> is guaranteed to have exactly one parameter
return BindToCollection(typeInfo, config, options);
}
collectionInterface = FindOpenGenericInterface(typeof(IReadOnlyDictionary<,>), type);
if (collectionInterface != null)
{
Type dictionaryType = typeof(Dictionary<,>).MakeGenericType(typeInfo.GenericTypeArguments[0], typeInfo.GenericTypeArguments[1]);
object instance = Activator.CreateInstance(dictionaryType);
BindDictionary(instance, dictionaryType, config, options);
return instance;
}
collectionInterface = FindOpenGenericInterface(typeof(IDictionary<,>), type);
if (collectionInterface != null)
{
object instance = Activator.CreateInstance(typeof(Dictionary<,>).MakeGenericType(typeInfo.GenericTypeArguments[0], typeInfo.GenericTypeArguments[1]));
BindDictionary(instance, collectionInterface, config, options);
return instance;
}
collectionInterface = FindOpenGenericInterface(typeof(IReadOnlyCollection<>), type);
if (collectionInterface != null)
{
// IReadOnlyCollection<T> is guaranteed to have exactly one parameter
return BindToCollection(typeInfo, config, options);
}
collectionInterface = FindOpenGenericInterface(typeof(ICollection<>), type);
if (collectionInterface != null)
{
// ICollection<T> is guaranteed to have exactly one parameter
return BindToCollection(typeInfo, config, options);
}
collectionInterface = FindOpenGenericInterface(typeof(IEnumerable<>), type);
if (collectionInterface != null)
{
// IEnumerable<T> is guaranteed to have exactly one parameter
return BindToCollection(typeInfo, config, options);
}
return null;
}
private static object BindInstance(Type type, object instance, IConfiguration config, BinderOptions options)
{
// if binding IConfigurationSection, break early
if (type == typeof(IConfigurationSection))
{
return config;
}
var section = config as IConfigurationSection;
string configValue = section?.Value;
object convertedValue;
Exception error;
if (configValue != null && TryConvertValue(type, configValue, section.Path, out convertedValue, out error))
{
if (error != null)
{
throw error;
}
// Leaf nodes are always reinitialized
return convertedValue;
}
if (config != null && config.GetChildren().Any())
{
// If we don't have an instance, try to create one
if (instance == null)
{
// We are already done if binding to a new collection instance worked
instance = AttemptBindToCollectionInterfaces(type, config, options);
if (instance != null)
{
return instance;
}
instance = CreateInstance(type);
}
// See if its a Dictionary
Type collectionInterface = FindOpenGenericInterface(typeof(IDictionary<,>), type);
if (collectionInterface != null)
{
BindDictionary(instance, collectionInterface, config, options);
}
else if (type.IsArray)
{
instance = BindArray((Array)instance, config, options);
}
else
{
// See if its an ICollection
collectionInterface = FindOpenGenericInterface(typeof(ICollection<>), type);
if (collectionInterface != null)
{
BindCollection(instance, collectionInterface, config, options);
}
// Something else
else
{
BindNonScalar(config, instance, options);
}
}
}
return instance;
}
private static object CreateInstance(Type type)
{
TypeInfo typeInfo = type.GetTypeInfo();
if (typeInfo.IsInterface || typeInfo.IsAbstract)
{
throw new InvalidOperationException($"Error: cannot activate abstract class or interface, type: {type}");
}
if (type.IsArray)
{
if (typeInfo.GetArrayRank() > 1)
{
throw new InvalidOperationException($"Error: multi-dimensional array is not supported, type: {type})");
}
return Array.CreateInstance(typeInfo.GetElementType(), 0);
}
if (!typeInfo.IsValueType)
{
bool hasDefaultConstructor = typeInfo.DeclaredConstructors.Any(ctor => ctor.IsPublic && ctor.GetParameters().Length == 0);
if (!hasDefaultConstructor)
{
throw new InvalidOperationException($"Error: missing parameterless constructor in type {type}");
}
}
try
{
return Activator.CreateInstance(type);
}
catch (Exception ex)
{
throw new InvalidOperationException($"Error: failed to activate type [{type}]. {ex.Message}", ex);
}
}
private static void BindDictionary(object dictionary, Type dictionaryType, IConfiguration config, BinderOptions options)
{
TypeInfo typeInfo = dictionaryType.GetTypeInfo();
// IDictionary<K,V> is guaranteed to have exactly two parameters
Type keyType = typeInfo.GenericTypeArguments[0];
Type valueType = typeInfo.GenericTypeArguments[1];
bool keyTypeIsEnum = keyType.GetTypeInfo().IsEnum;
if (keyType != typeof(string) && !keyTypeIsEnum)
{
// We only support string and enum keys
return;
}
PropertyInfo setter = typeInfo.GetDeclaredProperty("Item");
foreach (IConfigurationSection child in config.GetChildren())
{
object item = BindInstance(
type: valueType,
instance: null,
config: child,
options: options);
if (item != null)
{
if (keyType == typeof(string))
{
string key = child.Key;
setter.SetValue(dictionary, item, new object[] { key });
}
else if (keyTypeIsEnum)
{
object key = Enum.Parse(keyType, child.Key);
setter.SetValue(dictionary, item, new object[] { key });
}
}
}
}
private static void BindCollection(object collection, Type collectionType, IConfiguration config, BinderOptions options)
{
TypeInfo typeInfo = collectionType.GetTypeInfo();
// ICollection<T> is guaranteed to have exactly one parameter
Type itemType = typeInfo.GenericTypeArguments[0];
MethodInfo addMethod = typeInfo.GetDeclaredMethod("Add");
foreach (IConfigurationSection section in config.GetChildren())
{
try
{
object item = BindInstance(
type: itemType,
instance: null,
config: section,
options: options);
if (item != null)
{
addMethod.Invoke(collection, new[] { item });
}
}
catch
{
}
}
}
private static Array BindArray(Array source, IConfiguration config, BinderOptions options)
{
IConfigurationSection[] children = config.GetChildren().ToArray();
int arrayLength = source.Length;
Type elementType = source.GetType().GetElementType();
var newArray = Array.CreateInstance(elementType, arrayLength + children.Length);
// binding to array has to preserve already initialized arrays with values
if (arrayLength > 0)
{
Array.Copy(source, newArray, arrayLength);
}
for (int i = 0; i < children.Length; i++)
{
try
{
object item = BindInstance(
type: elementType,
instance: null,
config: children[i],
options: options);
if (item != null)
{
newArray.SetValue(item, arrayLength + i);
}
}
catch
{
}
}
return newArray;
}
private static bool TryConvertValue(Type type, string value, string path, out object result, out Exception error)
{
error = null;
result = null;
if (type == typeof(object))
{
result = value;
return true;
}
if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
if (string.IsNullOrEmpty(value))
{
return true;
}
return TryConvertValue(Nullable.GetUnderlyingType(type), value, path, out result, out error);
}
TypeConverter converter = TypeDescriptor.GetConverter(type);
if (converter.CanConvertFrom(typeof(string)))
{
try
{
result = converter.ConvertFromInvariantString(value);
}
catch (Exception ex)
{
error = new InvalidOperationException($"Failed to convert value [{value}] to type [{type}].", ex);
}
return true;
}
return false;
}
private static object ConvertValue(Type type, string value, string path)
{
object result;
Exception error;
TryConvertValue(type, value, path, out result, out error);
if (error != null)
{
throw error;
}
return result;
}
private static Type FindOpenGenericInterface(Type expected, Type actual)
{
TypeInfo actualTypeInfo = actual.GetTypeInfo();
if (actualTypeInfo.IsGenericType &&
actual.GetGenericTypeDefinition() == expected)
{
return actual;
}
IEnumerable<Type> interfaces = actualTypeInfo.ImplementedInterfaces;
foreach (Type interfaceType in interfaces)
{
if (interfaceType.GetTypeInfo().IsGenericType &&
interfaceType.GetGenericTypeDefinition() == expected)
{
return interfaceType;
}
}
return null;
}
private static IEnumerable<PropertyInfo> GetAllProperties(TypeInfo type)
{
var allProperties = new List<PropertyInfo>();
do
{
allProperties.AddRange(type.DeclaredProperties);
type = type.BaseType.GetTypeInfo();
}
while (type != typeof(object).GetTypeInfo());
return allProperties;
}
}
}

Просмотреть файл

@ -0,0 +1,72 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces;
using System;
using System.Collections.Generic;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal
{
/// <summary>
/// Used to build key/value based configuration settings for use in an application.
/// </summary>
internal class ConfigurationBuilder : IConfigurationBuilder
{
/// <summary>
/// Returns the sources used to obtain configuration values.
/// </summary>
public IList<IConfigurationSource> Sources { get; } = new List<IConfigurationSource>();
private IDictionary<IConfigurationSource, string> _ids = new Dictionary<IConfigurationSource, string>();
/// <summary>
/// Gets a key/value collection that can be used to share data between the <see cref="IConfigurationBuilder"/>
/// and the registered <see cref="IConfigurationProvider"/>s.
/// </summary>
public IDictionary<string, object> Properties { get; } = new Dictionary<string, object>();
/// <summary>
/// Adds a new configuration source.
/// </summary>
/// <param name="source">The configuration source to add.</param>
/// <returns>The same <see cref="IConfigurationBuilder"/>.</returns>
public IConfigurationBuilder Add(string id, IConfigurationSource source)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
Sources.Add(source);
_ids[source] = id;
return this;
}
/// <summary>
/// Builds an <see cref="IConfiguration"/> with keys and values from the set of providers registered in
/// <see cref="Sources"/>.
/// </summary>
/// <returns>An <see cref="IConfigurationRoot"/> with keys and values from the registered providers.</returns>
public IConfigurationRoot Build()
{
var providers = new List<IConfigurationProvider>();
foreach (IConfigurationSource source in Sources)
{
IConfigurationProvider provider = source.Build(this, _ids[source]);
providers.Add(provider);
}
return new ConfigurationRoot(providers);
}
}
}

Просмотреть файл

@ -0,0 +1,97 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal
{
/// <summary>
/// Extension methods for configuration classes./>.
/// </summary>
internal static class ConfigurationExtensions
{
/// <summary>
/// Adds a new configuration source.
/// </summary>
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
/// <param name="configureSource">Configures the source secrets.</param>
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
public static IConfigurationBuilder Add<TSource>(this IConfigurationBuilder builder, string id, Action<TSource> configureSource) where TSource : IConfigurationSource, new()
{
var source = new TSource();
configureSource?.Invoke(source);
return builder.Add(id, source);
}
/// <summary>
/// Shorthand for GetSection("ConnectionStrings")[name].
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="name">The connection string key.</param>
/// <returns>The connection string.</returns>
public static string GetConnectionString(this IConfiguration configuration, string name)
{
return configuration?.GetSection("ConnectionStrings")?[name];
}
/// <summary>
/// Get the enumeration of key value pairs within the <see cref="IConfiguration" />
/// </summary>
/// <param name="configuration">The <see cref="IConfiguration"/> to enumerate.</param>
/// <returns>An enumeration of key value pairs.</returns>
public static IEnumerable<KeyValuePair<string, string>> AsEnumerable(this IConfiguration configuration) => configuration.AsEnumerable(makePathsRelative: false);
/// <summary>
/// Get the enumeration of key value pairs within the <see cref="IConfiguration" />
/// </summary>
/// <param name="configuration">The <see cref="IConfiguration"/> to enumerate.</param>
/// <param name="makePathsRelative">If true, the child keys returned will have the current configuration's Path trimmed from the front.</param>
/// <returns>An enumeration of key value pairs.</returns>
public static IEnumerable<KeyValuePair<string, string>> AsEnumerable(this IConfiguration configuration, bool makePathsRelative)
{
var stack = new Stack<IConfiguration>();
stack.Push(configuration);
var rootSection = configuration as IConfigurationSection;
int prefixLength = (makePathsRelative && rootSection != null) ? rootSection.Path.Length + 1 : 0;
while (stack.Count > 0)
{
IConfiguration config = stack.Pop();
// Don't include the sections value if we are removing paths, since it will be an empty key
if (config is IConfigurationSection section && (!makePathsRelative || config != configuration))
{
yield return new KeyValuePair<string, string>(section.Path.Substring(prefixLength), section.Value);
}
foreach (IConfigurationSection child in config.GetChildren())
{
stack.Push(child);
}
}
}
/// <summary>
/// Determines whether the section has a <see cref="IConfigurationSection.Value"/> or has children
/// </summary>
public static bool Exists(this IConfigurationSection section)
{
if (section == null)
{
return false;
}
return section.Value != null || section.GetChildren().Any();
}
}
}

Просмотреть файл

@ -0,0 +1,82 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal
{
internal class ConfigurationKeyComparer : IComparer<string>
{
private static readonly string[] _keyDelimiterArray = new[] { ConfigurationPath.KeyDelimiter };
/// <summary>
/// The default instance.
/// </summary>
public static ConfigurationKeyComparer Instance { get; } = new ConfigurationKeyComparer();
/// <summary>
/// Compares two strings.
/// </summary>
/// <param name="x">First string.</param>
/// <param name="y">Second string.</param>
/// <returns>Less than 0 if x is less than y, 0 if x is equal to y and greater than 0 if x is greater than y.</returns>
public int Compare(string x, string y)
{
string[] xParts = x?.Split(_keyDelimiterArray, StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();
string[] yParts = y?.Split(_keyDelimiterArray, StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();
// Compare each part until we get two parts that are not equal
for (int i = 0; i < Math.Min(xParts.Length, yParts.Length); i++)
{
x = xParts[i];
y = yParts[i];
int value1 = 0;
int value2 = 0;
bool xIsInt = x != null && int.TryParse(x, out value1);
bool yIsInt = y != null && int.TryParse(y, out value2);
int result;
if (!xIsInt && !yIsInt)
{
// Both are strings
result = string.Compare(x, y, StringComparison.OrdinalIgnoreCase);
}
else if (xIsInt && yIsInt)
{
// Both are int
result = value1 - value2;
}
else
{
// Only one of them is int
result = xIsInt ? -1 : 1;
}
if (result != 0)
{
// One of them is different
return result;
}
}
// If we get here, the common parts are equal.
// If they are of the same length, then they are totally identical
return xParts.Length - yParts.Length;
}
}
}

Просмотреть файл

@ -0,0 +1,90 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal
{
/// <summary>
/// Utility methods and constants for manipulating Configuration paths
/// </summary>
internal static class ConfigurationPath
{
/// <summary>
/// The delimiter ":" used to separate individual keys in a path.
/// </summary>
public static readonly string KeyDelimiter = ":";
/// <summary>
/// Combines path segments into one path.
/// </summary>
/// <param name="pathSegments">The path segments to combine.</param>
/// <returns>The combined path.</returns>
public static string Combine(params string[] pathSegments)
{
if (pathSegments == null)
{
throw new ArgumentNullException(nameof(pathSegments));
}
return string.Join(KeyDelimiter, pathSegments);
}
/// <summary>
/// Combines path segments into one path.
/// </summary>
/// <param name="pathSegments">The path segments to combine.</param>
/// <returns>The combined path.</returns>
public static string Combine(IEnumerable<string> pathSegments)
{
if (pathSegments == null)
{
throw new ArgumentNullException(nameof(pathSegments));
}
return string.Join(KeyDelimiter, pathSegments);
}
/// <summary>
/// Extracts the last path segment from the path.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>The last path segment of the path.</returns>
public static string GetSectionKey(string path)
{
if (string.IsNullOrEmpty(path))
{
return path;
}
int lastDelimiterIndex = path.LastIndexOf(KeyDelimiter, StringComparison.OrdinalIgnoreCase);
return lastDelimiterIndex == -1 ? path : path.Substring(lastDelimiterIndex + 1);
}
/// <summary>
/// Extracts the path corresponding to the parent node for a given path.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>The original path minus the last individual segment found in it. Null if the original path corresponds to a top level node.</returns>
public static string GetParentPath(string path)
{
if (string.IsNullOrEmpty(path))
{
return null;
}
int lastDelimiterIndex = path.LastIndexOf(KeyDelimiter, StringComparison.OrdinalIgnoreCase);
return lastDelimiterIndex == -1 ? null : path.Substring(0, lastDelimiterIndex);
}
}
}

Просмотреть файл

@ -0,0 +1,97 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal
{
/// <summary>
/// Base helper class for implementing an <see cref="IConfigurationProvider"/>
/// </summary>
internal abstract class ConfigurationProvider : IConfigurationProvider
{
public string Id { get; }
/// <summary>
/// Initializes a new <see cref="IConfigurationProvider"/>
/// </summary>
protected ConfigurationProvider(string id)
{
Id = id;
Data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
/// <summary>
/// The configuration key value pairs for this provider.
/// </summary>
protected IDictionary<string, string> Data { get; set; }
/// <summary>
/// Attempts to find a value with the given key, returns true if one is found, false otherwise.
/// </summary>
/// <param name="key">The key to lookup.</param>
/// <param name="value">The value found at key if one is found.</param>
/// <returns>True if key has a value, false otherwise.</returns>
public virtual bool TryGet(string key, out string value)
=> Data.TryGetValue(key, out value);
/// <summary>
/// Sets a value for a given key.
/// </summary>
/// <param name="key">The configuration key to set.</param>
/// <param name="value">The value to set.</param>
public virtual void Set(string key, string value)
=> Data[key] = value;
/// <summary>
/// Loads (or reloads) the data for this provider.
/// </summary>
public virtual void Load()
{ }
/// <summary>
/// Returns the list of keys that this provider has.
/// </summary>
/// <param name="earlierKeys">The earlier keys that other providers contain.</param>
/// <param name="parentPath">The path for the parent IConfiguration.</param>
/// <returns>The list of keys for this provider.</returns>
public virtual IEnumerable<string> GetChildKeys(
IEnumerable<string> earlierKeys,
string parentPath)
{
string prefix = parentPath == null ? string.Empty : parentPath + ConfigurationPath.KeyDelimiter;
return Data
.Where(kv => kv.Key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
.Select(kv => Segment(kv.Key, prefix.Length))
.Concat(earlierKeys)
.OrderBy(k => k, ConfigurationKeyComparer.Instance);
}
private static string Segment(string key, int prefixLength)
{
int indexOf = key.IndexOf(ConfigurationPath.KeyDelimiter, prefixLength, StringComparison.OrdinalIgnoreCase);
return indexOf < 0 ? key.Substring(prefixLength) : key.Substring(prefixLength, indexOf - prefixLength);
}
/// <summary>
/// Generates a string representing this provider name and relevant details.
/// </summary>
/// <returns> The configuration name. </returns>
public override string ToString() => $"{GetType().Name}";
}
}

Просмотреть файл

@ -0,0 +1,137 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal
{
/// <summary>
/// The root node for a configuration.
/// </summary>
internal class ConfigurationRoot : IConfigurationRoot, IDisposable
{
private readonly IList<IConfigurationProvider> _providers;
/// <summary>
/// Initializes a Configuration root with a list of providers.
/// </summary>
/// <param name="providers">The <see cref="IConfigurationProvider"/>s for this configuration.</param>
public ConfigurationRoot(IList<IConfigurationProvider> providers)
{
if (providers == null)
{
throw new ArgumentNullException(nameof(providers));
}
_providers = providers;
foreach (IConfigurationProvider p in providers)
{
p.Load();
}
}
/// <summary>
/// The <see cref="IConfigurationProvider"/>s for this configuration.
/// </summary>
public IEnumerable<IConfigurationProvider> Providers => _providers;
/// <summary>
/// Gets or sets the value corresponding to a configuration key.
/// </summary>
/// <param name="key">The configuration key.</param>
/// <returns>The configuration value.</returns>
public string this[string key]
{
get
{
return GetValueWithProviderId(key).Item1;
}
set
{
if (!_providers.Any())
{
throw new InvalidOperationException($"Error: none config source is registered.");
}
foreach (IConfigurationProvider provider in _providers)
{
provider.Set(key, value);
}
}
}
public (string, string) GetValueWithProviderId(string key)
{
for (int i = _providers.Count - 1; i >= 0; i--)
{
IConfigurationProvider provider = _providers[i];
if (provider.TryGet(key, out string value))
{
return (value, provider.Id);
}
}
return (null, null);
}
/// <summary>
/// Gets the immediate children sub-sections.
/// </summary>
/// <returns>The children.</returns>
public IEnumerable<IConfigurationSection> GetChildren() => this.GetChildrenImplementation(null);
/// <summary>
/// Gets a configuration sub-section with the specified key.
/// </summary>
/// <param name="key">The key of the configuration section.</param>
/// <returns>The <see cref="IConfigurationSection"/>.</returns>
/// <remarks>
/// This method will never return <c>null</c>. If no matching sub-section is found with the specified key,
/// an empty <see cref="IConfigurationSection"/> will be returned.
/// </remarks>
public IConfigurationSection GetSection(string key)
=> new ConfigurationSection(this, key);
/// <summary>
/// Force the configuration values to be reloaded from the underlying sources.
/// </summary>
public void Reload()
{
foreach (IConfigurationProvider provider in _providers)
{
provider.Load();
}
}
/// <inheritdoc />
public void Dispose()
{
// dispose providers
foreach (IConfigurationProvider provider in _providers)
{
(provider as IDisposable)?.Dispose();
}
}
public IConfigurationProvider GetConfigurationProvider(string id)
{
return _providers.FirstOrDefault(x => x.Id.Equals(id));
}
}
}

Просмотреть файл

@ -0,0 +1,127 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces;
using System;
using System.Collections.Generic;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal
{
/// <summary>
/// Represents a section of application configuration values.
/// </summary>
internal class ConfigurationSection : IConfigurationSection
{
private readonly IConfigurationRoot _root;
private readonly string _path;
private string _key;
/// <summary>
/// Initializes a new instance.
/// </summary>
/// <param name="root">The configuration root.</param>
/// <param name="path">The path to this section.</param>
public ConfigurationSection(IConfigurationRoot root, string path)
{
if (root == null)
{
throw new ArgumentNullException(nameof(root));
}
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
_root = root;
_path = path;
}
/// <summary>
/// Gets the full path to this section from the <see cref="IConfigurationRoot"/>.
/// </summary>
public string Path => _path;
/// <summary>
/// Gets the key this section occupies in its parent.
/// </summary>
public string Key
{
get
{
if (_key == null)
{
// Key is calculated lazily as last portion of Path
_key = ConfigurationPath.GetSectionKey(_path);
}
return _key;
}
}
/// <summary>
/// Gets or sets the section value.
/// </summary>
public string Value
{
get
{
return _root[Path];
}
set
{
_root[Path] = value;
}
}
public (string, string) GetValueWithProviderId()
{
return _root.GetValueWithProviderId(Path);
}
/// <summary>
/// Gets or sets the value corresponding to a configuration key.
/// </summary>
/// <param name="key">The configuration key.</param>
/// <returns>The configuration value.</returns>
public string this[string key]
{
get
{
return _root[ConfigurationPath.Combine(Path, key)];
}
set
{
_root[ConfigurationPath.Combine(Path, key)] = value;
}
}
/// <summary>
/// Gets a configuration sub-section with the specified key.
/// </summary>
/// <param name="key">The key of the configuration section.</param>
/// <returns>The <see cref="IConfigurationSection"/>.</returns>
/// <remarks>
/// This method will never return <c>null</c>. If no matching sub-section is found with the specified key,
/// an empty <see cref="IConfigurationSection"/> will be returned.
/// </remarks>
public IConfigurationSection GetSection(string key) => _root.GetSection(ConfigurationPath.Combine(Path, key));
/// <summary>
/// Gets the immediate descendant configuration sub-sections.
/// </summary>
/// <returns>The configuration sub-sections.</returns>
public IEnumerable<IConfigurationSection> GetChildren() => _root.GetChildrenImplementation(Path);
}
}

Просмотреть файл

@ -0,0 +1,45 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using System.Collections.Generic;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces
{
internal interface IConfiguration
{
/// <summary>
/// Gets or sets a configuration value.
/// </summary>
/// <param name="key">The configuration key.</param>
/// <returns>The configuration value.</returns>
string this[string key] { get; set; }
/// <summary>
/// Gets a configuration sub-section with the specified key.
/// </summary>
/// <param name="key">The key of the configuration section.</param>
/// <returns>The <see cref="IConfigurationSection"/>.</returns>
/// <remarks>
/// This method will never return <c>null</c>. If no matching sub-section is found with the specified key,
/// an empty <see cref="IConfigurationSection"/> will be returned.
/// </remarks>
IConfigurationSection GetSection(string key);
/// <summary>
/// Gets the immediate descendant configuration sub-sections.
/// </summary>
/// <returns>The configuration sub-sections.</returns>
IEnumerable<IConfigurationSection> GetChildren();
}
}

Просмотреть файл

@ -0,0 +1,49 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using System.Collections.Generic;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces
{
/// <summary>
/// Represents a type used to build application configuration.
/// </summary>
internal interface IConfigurationBuilder
{
/// <summary>
/// Gets a key/value collection that can be used to share data between the <see cref="IConfigurationBuilder"/>
/// and the registered <see cref="IConfigurationSource"/>s.
/// </summary>
IDictionary<string, object> Properties { get; }
/// <summary>
/// Gets the sources used to obtain configuration values
/// </summary>
IList<IConfigurationSource> Sources { get; }
/// <summary>
/// Adds a new configuration source.
/// </summary>
/// <param name="source">The configuration source to add.</param>
/// <returns>The same <see cref="IConfigurationBuilder"/>.</returns>
IConfigurationBuilder Add(string id, IConfigurationSource source);
/// <summary>
/// Builds an <see cref="IConfiguration"/> with keys and values from the set of sources registered in
/// <see cref="Sources"/>.
/// </summary>
/// <returns>An <see cref="IConfigurationRoot"/> with keys and values from the registered sources.</returns>
IConfigurationRoot Build();
}
}

Просмотреть файл

@ -0,0 +1,56 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using System.Collections.Generic;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces
{
/// <summary>
/// Provides configuration key/values for an application.
/// </summary>
internal interface IConfigurationProvider
{
string Id { get; }
/// <summary>
/// Tries to get a configuration value for the specified key.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="value">The value.</param>
/// <returns><c>True</c> if a value for the specified key was found, otherwise <c>false</c>.</returns>
bool TryGet(string key, out string value);
/// <summary>
/// Sets a configuration value for the specified key.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="value">The value.</param>
void Set(string key, string value);
/// <summary>
/// Loads configuration values from the source represented by this <see cref="IConfigurationProvider"/>.
/// </summary>
void Load();
/// <summary>
/// Returns the immediate descendant configuration keys for a given parent path based on this
/// <see cref="IConfigurationProvider"/>s data and the set of keys returned by all the preceding
/// <see cref="IConfigurationProvider"/>s.
/// </summary>
/// <param name="earlierKeys">The child keys returned by the preceding providers for the same parent path.</param>
/// <param name="parentPath">The parent path.</param>
/// <returns>The child keys.</returns>
IEnumerable<string> GetChildKeys(IEnumerable<string> earlierKeys, string parentPath);
}
}

Просмотреть файл

@ -0,0 +1,38 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using System.Collections.Generic;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces
{
/// <summary>
/// Represents the root of an <see cref="IConfiguration"/> hierarchy.
/// </summary>
internal interface IConfigurationRoot : IConfiguration
{
/// <summary>
/// Force the configuration values to be reloaded from the underlying <see cref="IConfigurationProvider"/>s.
/// </summary>
void Reload();
/// <summary>
/// The <see cref="IConfigurationProvider"/>s for this configuration.
/// </summary>
IEnumerable<IConfigurationProvider> Providers { get; }
IConfigurationProvider GetConfigurationProvider(string id);
(string, string) GetValueWithProviderId(string key);
}
}

Просмотреть файл

@ -0,0 +1,40 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces
{
internal interface IConfigurationSection : IConfiguration
{
/// <summary>
/// Gets the key this section occupies in its parent.
/// </summary>
string Key { get; }
/// <summary>
/// Gets the full path to this section within the <see cref="IConfiguration"/>.
/// </summary>
string Path { get; }
/// <summary>
/// Gets or sets the section value.
/// </summary>
string Value { get; set; }
/// <summary>
/// Gets the section value and the ID of the provider which provides this value.
/// </summary>
/// <returns></returns>
(string, string) GetValueWithProviderId();
}
}

Просмотреть файл

@ -0,0 +1,29 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces
{
/// <summary>
/// Represents a source of configuration key/values for an application.
/// </summary>
internal interface IConfigurationSource
{
/// <summary>
/// Builds the <see cref="IConfigurationProvider"/> for this source.
/// </summary>
/// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
/// <returns>An <see cref="IConfigurationProvider"/></returns>
IConfigurationProvider Build(IConfigurationBuilder builder, string id);
}
}

Просмотреть файл

@ -0,0 +1,28 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using System;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces
{
/// <summary>
/// An abstraction of the ability to get and set environment variable on various targets.
/// </summary>
internal interface IEnvironmentVariableProvider
{
string Get(string variableName, EnvironmentVariableTarget target = EnvironmentVariableTarget.Process);
void Set(string variableName, string value, EnvironmentVariableTarget target = EnvironmentVariableTarget.Process);
}
}

Просмотреть файл

@ -0,0 +1,42 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal
{
/// <summary>
/// Extensions method for <see cref="IConfigurationRoot"/>
/// </summary>
internal static class InternalConfigurationRootExtensions
{
/// <summary>
/// Gets the immediate children sub-sections of configuration root based on key.
/// </summary>
/// <param name="root">Configuration from which to retrieve sub-sections.</param>
/// <param name="path">Key of a section of which children to retrieve.</param>
/// <returns>Immediate children sub-sections of section specified by key.</returns>
internal static IEnumerable<IConfigurationSection> GetChildrenImplementation(this IConfigurationRoot root, string path)
{
return root.Providers
.Aggregate(Enumerable.Empty<string>(),
(seed, source) => source.GetChildKeys(seed, path))
.Distinct(StringComparer.OrdinalIgnoreCase)
.Select(key => root.GetSection(path == null ? key : ConfigurationPath.Combine(path, key)));
}
}
}

Просмотреть файл

@ -0,0 +1,29 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Providers
{
internal static class EnvironmentVariablesConfigurationBuilderExtensions
{
public static IConfigurationBuilder AddEnvironmentVariables(this IConfigurationBuilder builder,
string providerId,
EnvironmentVariablesConfigurationOptions options)
{
builder.Add(providerId, new EnvironmentVariablesConfigurationSource(options));
return builder;
}
}
}

Просмотреть файл

@ -0,0 +1,27 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces;
using System;
using System.Collections.Generic;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Providers
{
internal class EnvironmentVariablesConfigurationOptions
{
public IEnvironmentVariableProvider EnvironmentVariableProvider { get; set; }
public EnvironmentVariableTarget EnvironmentVariableTarget { get; set; }
public IDictionary<string, string> EnvironmentVariableToKeyMap { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,49 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces;
using System;
using System.Collections.Generic;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Providers
{
internal class EnvironmentVariablesConfigurationProvider : ConfigurationProvider
{
private EnvironmentVariableTarget _environmentVariableTarget;
private IDictionary<string, string> _environmentVariableNameToKeyMapping;
private IEnvironmentVariableProvider _environmentVariableProvider;
public EnvironmentVariablesConfigurationProvider(string id, EnvironmentVariablesConfigurationOptions options) : base(id)
{
_environmentVariableTarget = options.EnvironmentVariableTarget;
_environmentVariableNameToKeyMapping = options.EnvironmentVariableToKeyMap ?? new Dictionary<string, string>();
_environmentVariableProvider = options.EnvironmentVariableProvider;
}
public override void Load()
{
var data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var i in _environmentVariableNameToKeyMapping)
{
string value = _environmentVariableProvider.Get(i.Key, _environmentVariableTarget);
if (!string.IsNullOrEmpty(value))
{
data[i.Value] = value;
}
}
Data = data;
}
}
}

Просмотреть файл

@ -0,0 +1,33 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Providers
{
internal class EnvironmentVariablesConfigurationSource : IConfigurationSource
{
private EnvironmentVariablesConfigurationOptions _options;
public EnvironmentVariablesConfigurationSource(EnvironmentVariablesConfigurationOptions options)
{
_options = options;
}
public IConfigurationProvider Build(IConfigurationBuilder builder, string id)
{
return new EnvironmentVariablesConfigurationProvider(id, _options);
}
}
}

Просмотреть файл

@ -0,0 +1,42 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces;
using System;
using System.IO;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Providers
{
/// <summary>
/// Extension methods for adding the <see cref="JsonStreamConfigurationProvider"/>.
/// </summary>
internal static class JsonConfigurationExtensions
{
/// <summary>
/// Adds a JSON configuration source to <paramref name="builder"/>.
/// </summary>
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
/// <param name="stream">The <see cref="Stream"/> to read the json configuration data from.</param>
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
public static IConfigurationBuilder AddJsonStream(this IConfigurationBuilder builder, string id, Stream stream)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
return builder.Add<JsonStreamConfigurationSource>(id, s => s.Stream = stream);
}
}
}

Просмотреть файл

@ -0,0 +1,116 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Providers
{
internal class JsonConfigurationFileParser
{
private JsonConfigurationFileParser() { }
private readonly IDictionary<string, string> _data = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
private readonly Stack<string> _context = new Stack<string>();
private string _currentPath;
public static IDictionary<string, string> Parse(Stream input)
=> new JsonConfigurationFileParser().ParseStream(input);
private IDictionary<string, string> ParseStream(Stream input)
{
_data.Clear();
var jsonDocumentOptions = new JsonDocumentOptions
{
CommentHandling = JsonCommentHandling.Skip,
AllowTrailingCommas = true,
};
using (var reader = new StreamReader(input))
using (JsonDocument doc = JsonDocument.Parse(reader.ReadToEnd(), jsonDocumentOptions))
{
if (doc.RootElement.ValueKind != JsonValueKind.Object)
{
throw new FormatException($"Error: unsupported JSON token [{doc.RootElement.ValueKind}]. Object kind is expected.");
}
VisitElement(doc.RootElement);
}
return _data;
}
private void VisitElement(JsonElement element)
{
foreach (JsonProperty property in element.EnumerateObject())
{
EnterContext(property.Name);
VisitValue(property.Value);
ExitContext();
}
}
private void VisitValue(JsonElement value)
{
switch (value.ValueKind)
{
case JsonValueKind.Object:
VisitElement(value);
break;
case JsonValueKind.Array:
int index = 0;
foreach (JsonElement arrayElement in value.EnumerateArray())
{
EnterContext(index.ToString());
VisitValue(arrayElement);
ExitContext();
index++;
}
break;
case JsonValueKind.Number:
case JsonValueKind.String:
case JsonValueKind.True:
case JsonValueKind.False:
case JsonValueKind.Null:
string key = _currentPath;
if (_data.ContainsKey(key))
{
throw new FormatException($"Error: key [{key}] is duplicated.");
}
_data[key] = value.ToString();
break;
default:
throw new FormatException($"Error: unsupported JSON token [{value.ValueKind}]");
}
}
private void EnterContext(string context)
{
_context.Push(context);
_currentPath = ConfigurationPath.Combine(_context.Reverse());
}
private void ExitContext()
{
_context.Pop();
_currentPath = ConfigurationPath.Combine(_context.Reverse());
}
}
}

Просмотреть файл

@ -0,0 +1,37 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using System.IO;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Providers
{
/// <summary>
/// Loads configuration key/values from a json stream into a provider.
/// </summary>
internal class JsonStreamConfigurationProvider : StreamConfigurationProvider
{
public JsonStreamConfigurationProvider(JsonStreamConfigurationSource source, string id) : base(source, id)
{
}
/// <summary>
/// Loads json configuration key/values from a stream into a provider.
/// </summary>
/// <param name="stream">The json <see cref="Stream"/> to load configuration data from.</param>
public override void Load(Stream stream)
{
Data = JsonConfigurationFileParser.Parse(stream);
}
}
}

Просмотреть файл

@ -0,0 +1,32 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Providers
{
/// <summary>
/// Represents a JSON file as an <see cref="IConfigurationSource"/>.
/// </summary>
internal class JsonStreamConfigurationSource : StreamConfigurationSource
{
/// <summary>
/// Builds the <see cref="JsonStreamConfigurationProvider"/> for this source.
/// </summary>
/// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
/// <returns>An <see cref="JsonStreamConfigurationProvider"/></returns>
public override IConfigurationProvider Build(IConfigurationBuilder builder, string id)
=> new JsonStreamConfigurationProvider(this, id);
}
}

Просмотреть файл

@ -0,0 +1,60 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using System;
using System.IO;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Providers
{
/// <summary>
/// Stream based configuration provider
/// </summary>
internal abstract class StreamConfigurationProvider : ConfigurationProvider
{
/// <summary>
/// The source settings for this provider.
/// </summary>
public StreamConfigurationSource Source { get; }
private bool _loaded;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="source">The source.</param>
public StreamConfigurationProvider(StreamConfigurationSource source, string id) : base(id)
{
Source = source ?? throw new ArgumentNullException(nameof(source));
}
/// <summary>
/// Load the configuration data from the stream.
/// </summary>
/// <param name="stream">The data stream.</param>
public abstract void Load(Stream stream);
/// <summary>
/// Load the configuration data from the stream. Will throw after the first call.
/// </summary>
public override void Load()
{
if (_loaded)
{
throw new InvalidOperationException("StreamConfigurationProviders cannot be loaded more than once.");
}
Load(Source.Stream);
_loaded = true;
}
}
}

Просмотреть файл

@ -0,0 +1,37 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces;
using System.IO;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Providers
{
/// <summary>
/// Stream based <see cref="IConfigurationSource" />.
/// </summary>
internal abstract class StreamConfigurationSource : IConfigurationSource
{
/// <summary>
/// The stream containing the configuration data.
/// </summary>
public Stream Stream { get; set; }
/// <summary>
/// Builds the <see cref="StreamConfigurationProvider"/> for this source.
/// </summary>
/// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
/// <returns>An <see cref="IConfigurationProvider"/></returns>
public abstract IConfigurationProvider Build(IConfigurationBuilder builder, string id);
}
}

Просмотреть файл

@ -0,0 +1,33 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces;
using System;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Providers
{
internal static class UnsettableMemoryConfigurationBuilderExtensions
{
public static IConfigurationBuilder AddUnsettableInMemoryCollection(this IConfigurationBuilder builder, string id)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.Add(id, new UnsettableMemoryConfigurationSource());
return builder;
}
}
}

Просмотреть файл

@ -0,0 +1,81 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Providers
{
/// <summary>
/// In-memory implementation of <see cref="IConfigurationProvider"/>
/// </summary>
internal class UnsettableMemoryConfigurationProvider : ConfigurationProvider, IEnumerable<KeyValuePair<string, string>>
{
private readonly UnsettableMemoryConfigurationSource _source;
/// <summary>
/// Initialize a new instance from the source.
/// </summary>
/// <param name="source">The source settings.</param>
public UnsettableMemoryConfigurationProvider(UnsettableMemoryConfigurationSource source, string id): base(id)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
_source = source;
}
/// <summary>
/// Add a new key and value pair.
/// </summary>
/// <param name="key">The configuration key.</param>
/// <param name="value">The configuration value.</param>
public void Add(string key, string value)
{
Data.Add(key, value);
}
public void Unset(string key)
{
Data.Remove(key);
}
/// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>An enumerator that can be used to iterate through the collection.</returns>
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return Data.GetEnumerator();
}
/// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>An enumerator that can be used to iterate through the collection.</returns>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void UnsetAll()
{
Data.Clear();
}
}
}

Просмотреть файл

@ -0,0 +1,26 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Interfaces;
namespace Microsoft.Azure.Commands.Common.Authentication.Config.Internal.Providers
{
internal class UnsettableMemoryConfigurationSource : IConfigurationSource
{
public IConfigurationProvider Build(IConfigurationBuilder builder, string id)
{
return new UnsettableMemoryConfigurationProvider(this, id);
}
}
}

Просмотреть файл

@ -0,0 +1,35 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
namespace Microsoft.Azure.Commands.Common.Authentication.Config
{
/// <summary>
/// Abstraction for the PS InvocationInfo.
/// </summary>
internal class InternalInvocationInfo
{
public InternalInvocationInfo() : this(null, null)
{
}
public InternalInvocationInfo(string moduleName, string cmdletName)
{
CmdletName = cmdletName;
ModuleName = moduleName;
}
public string ModuleName { get; set; } = null;
public string CmdletName { get; set; } = null;
}
}

Просмотреть файл

@ -0,0 +1,25 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using System.Management.Automation;
namespace Microsoft.Azure.Commands.Common.Authentication.Config
{
internal class InvocationInfoAdapter : InternalInvocationInfo
{
public InvocationInfoAdapter(InvocationInfo invocationInfo) : base(
invocationInfo?.MyCommand?.ModuleName, invocationInfo?.MyCommand?.Name)
{ }
}
}

Просмотреть файл

@ -0,0 +1,50 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.PowerShell.Common.Config;
using System.Collections.Generic;
namespace Microsoft.Azure.Commands.Common.Authentication.Config
{
/// <summary>
/// Represents a simple typed config. For complex configs please define your own type inheriting <see cref="TypedConfig{TValue}"/> or <see cref="ConfigDefinition"/>.
/// </summary>
/// <typeparam name="TValue">Type of the config value.</typeparam>
internal class SimpleTypedConfig<TValue> : TypedConfig<TValue>
{
private readonly string _key;
private readonly string _helpMessage;
private readonly TValue _defaultValue;
private readonly string _environmentVariable;
private readonly IReadOnlyCollection<AppliesTo> _canApplyTo = null;
public SimpleTypedConfig(string key, string helpMessage, TValue defaultValue, string environmentVariable = null, IReadOnlyCollection<AppliesTo> canApplyTo = null)
{
_key = key;
_helpMessage = helpMessage;
_defaultValue = defaultValue;
_environmentVariable = environmentVariable;
_canApplyTo = canApplyTo;
}
public override string Key => _key;
public override string HelpMessage => _helpMessage;
public override object DefaultValue => _defaultValue;
public override string EnvironmentVariableName => _environmentVariable;
public override IReadOnlyCollection<AppliesTo> CanApplyTo
{
get { return _canApplyTo ?? base.CanApplyTo; }
}
}
}

Просмотреть файл

@ -0,0 +1,69 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.PowerShell.Common.Config;
using System;
namespace Microsoft.Azure.Commands.Common.Authentication.Config
{
/// <summary>
/// Base class for configs that have a typed value.
/// </summary>
/// <typeparam name="TValue">The type of the config value.</typeparam>
internal abstract class TypedConfig<TValue> : ConfigDefinition
{
protected TypedConfig()
{
}
public TValue TypedDefaultValue => (TValue)DefaultValue;
/// <summary>
/// Validates if the input value is type <see cref="ValueType"/>.
/// </summary>
/// <param name="value">The value to check.</param>
/// <exception cref="ArgumentException">Throws when the value in another type.</exception>
public override void Validate(object value)
{
base.Validate(value);
if (!(value is TValue))
{
throw new ArgumentException($"Unexpected value type [{value.GetType()}]. The value of config [{Key}] should be of type [{ValueType}]", nameof(value));
}
}
/// <summary>
/// Performs side effects before applying the config.
/// </summary>
/// <param name="value">The value to be applied to this typed config.</param>
/// <remarks>
/// This method is sealed.
/// Derived types should override <see cref="ApplyTyped(TValue)"/>.
/// </remarks>
public override sealed void Apply(object value)
{
base.Apply(value);
ApplyTyped((TValue)value);
}
/// <summary>
/// Generic version of <see cref="Apply(object)"/>.
/// Override in child classes to perform side effects before applying the config value.
/// </summary>
/// <param name="value">The value to be applied to this typed config, cast to the correct type.</param>
protected virtual void ApplyTyped(TValue value) { }
public override Type ValueType => typeof(TValue);
}
}

Просмотреть файл

@ -24,5 +24,18 @@ namespace Microsoft.Azure.Commands.Common.Authentication
public const string MicrosoftGraphAccessToken = "MicrosoftGraphAccessToken";
public const string DefaultValue = "Default";
public class ConfigProviderIds
{
public const string MachineEnvironment = "Environment (Machine)";
public const string UserEnvironment = "Environment (User)";
public const string ProcessEnvironment = "Environment (Process)";
public const string UserConfig = "Config (User)";
public const string ProcessConfig = "Config (Process)";
/// <summary>
/// Represents that the value is not in any providers.
/// </summary>
public const string None = "None";
}
}
}

Просмотреть файл

@ -45,3 +45,6 @@ using System.Runtime.InteropServices;
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.7.6")]
[assembly: AssemblyFileVersion("2.7.6")]
#if !SIGN
[assembly: InternalsVisibleTo("Microsoft.Azure.PowerShell.Authentication.Test")]
#endif

Просмотреть файл

@ -0,0 +1,64 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using System.Text.RegularExpressions;
namespace Microsoft.Azure.Commands.Common.Authentication.Utilities
{
/// <summary>
/// Utility class about PowerShell naming (cmdlet name, module name).
/// </summary>
/// <remarks>
/// All the mothods are within Azure PowerShell context, for example, module name should start with "Az.".
/// </remarks>
public static class PSNamingUtilities
{
private static readonly Regex ModulePattern = new Regex(@"^az\.[a-z]+$", RegexOptions.IgnoreCase);
private static readonly Regex CmdletPattern = new Regex(@"^[a-z]+-[a-z\d]+$", RegexOptions.IgnoreCase);
private static readonly Regex ModuleOrCmdletPattern = new Regex(@"^az\.[a-z]+$|^[a-z]+-[a-z\d]+$", RegexOptions.IgnoreCase);
/// <summary>
/// Returns if the given <paramref name="moduleName"/> is a valid module name.
/// </summary>
/// <remarks>
/// This method only does pattern-matching. It does not check if the name is real.
/// </remarks>
public static bool IsModuleName(string moduleName)
{
return ModulePattern.IsMatch(moduleName);
}
/// <summary>
/// Returns if the given <paramref name="cmdletName"/> is a valid cmdlet name.
/// </summary>
/// <remarks>
/// This method only does pattern-matching. It does not check if the name is real.
/// </remarks>
public static bool IsCmdletName(string cmdletName)
{
return CmdletPattern.IsMatch(cmdletName);
}
/// <summary>
/// Returns if the given <paramref name="moduleOrCmdletName"/> is a valid module name or cmdlet name.
/// </summary>
/// <remarks>
/// This method only does pattern-matching. It does not check if the name is real.
/// </remarks>
public static bool IsModuleOrCmdletName(string moduleOrCmdletName)
{
return ModuleOrCmdletPattern.IsMatch(moduleOrCmdletName);
}
}
}