Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Text;
using System.Collections;
using System.Globalization;
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
#if !CORECLR
Expand All @@ -19,6 +20,32 @@

namespace Microsoft.PowerShell.Commands
{
/// <summary>
/// The valid values for the -Authentication parameter for Invoke-RestMethod and Invoke-WebRequest
/// </summary>
public enum WebAuthenticationType
{
/// <summary>
/// No authentication. Default.
/// </summary>
None,

/// <summary>
/// RFC-7617 Basic Authentication. Requires -Credential
/// </summary>
Basic,

/// <summary>
/// RFC-6750 OAuth 2.0 Bearer Authentication. Requires -Token
/// </summary>
Bearer,

/// <summary>
/// RFC-6750 OAuth 2.0 Bearer Authentication. Requires -Token
/// </summary>
OAuth,
}

/// <summary>
/// Base class for Invoke-RestMethod and Invoke-WebRequest commands.
/// </summary>
Expand Down Expand Up @@ -61,6 +88,22 @@ public abstract partial class WebRequestPSCmdlet : PSCmdlet

#region Authorization and Credentials

/// <summary>
/// Gets or sets the AllowUnencryptedAuthentication property
/// </summary>
[Parameter]
public virtual SwitchParameter AllowUnencryptedAuthentication { get; set; }

/// <summary>
/// Gets or sets the Authentication property used to determin the Authentication method for the web session.
/// Authentication does not work with UseDefaultCredentials.
/// Authentication over unencrypted sessions requires AllowUnencryptedAuthentication.
/// Basic: Requires Credential
/// OAuth/Bearer: Requires Token
/// </summary>
[Parameter]
public virtual WebAuthenticationType Authentication { get; set; } = WebAuthenticationType.None;

/// <summary>
/// gets or sets the Credential property
/// </summary>
Expand Down Expand Up @@ -94,6 +137,12 @@ public abstract partial class WebRequestPSCmdlet : PSCmdlet
[Parameter]
public virtual SwitchParameter SkipCertificateCheck { get; set; }

/// <summary>
/// Gets or sets the Token property. Token is required by Authentication OAuth and Bearer.
/// </summary>
[Parameter]
public virtual SecureString Token { get; set; }

#endregion

#region Headers
Expand Down Expand Up @@ -274,6 +323,38 @@ internal virtual void ValidateParameters()
ThrowTerminatingError(error);
}

// Authentication
if (UseDefaultCredentials && (Authentication != WebAuthenticationType.None))
{
ErrorRecord error = GetValidationError(WebCmdletStrings.AuthenticationConflict,
"WebCmdletAuthenticationConflictException");
ThrowTerminatingError(error);
}
if ((Authentication != WebAuthenticationType.None) && (null != Token) && (null != Credential))
{
ErrorRecord error = GetValidationError(WebCmdletStrings.AuthenticationTokenConflict,
"WebCmdletAuthenticationTokenConflictException");
ThrowTerminatingError(error);
}
if ((Authentication == WebAuthenticationType.Basic) && (null == Credential))
{
ErrorRecord error = GetValidationError(WebCmdletStrings.AuthenticationCredentialNotSupplied,
"WebCmdletAuthenticationCredentialNotSuppliedException");
ThrowTerminatingError(error);
}
if ((Authentication == WebAuthenticationType.OAuth || Authentication == WebAuthenticationType.Bearer) && (null == Token))
{
ErrorRecord error = GetValidationError(WebCmdletStrings.AuthenticationTokenNotSupplied,
"WebCmdletAuthenticationTokenNotSuppliedException");
ThrowTerminatingError(error);
}
if (!AllowUnencryptedAuthentication && (Authentication != WebAuthenticationType.None) && (Uri.Scheme != "https"))
{
ErrorRecord error = GetValidationError(WebCmdletStrings.AllowUnencryptedAuthenticationRequired,
"WebCmdletAllowUnencryptedAuthenticationRequiredException");
ThrowTerminatingError(error);
}

// credentials
if (UseDefaultCredentials && (null != Credential))
{
Expand Down Expand Up @@ -389,7 +470,7 @@ internal virtual void PrepareSession()
//
// handle credentials
//
if (null != Credential)
if (null != Credential && Authentication == WebAuthenticationType.None)
{
// get the relevant NetworkCredential
NetworkCredential netCred = Credential.GetNetworkCredential();
Expand All @@ -398,6 +479,10 @@ internal virtual void PrepareSession()
// supplying a credential overrides the UseDefaultCredentials setting
WebSession.UseDefaultCredentials = false;
}
else if ((null != Credential || null!= Token) && Authentication != WebAuthenticationType.None)
{
ProcessAuthentication();
}
else if (UseDefaultCredentials)
{
WebSession.UseDefaultCredentials = true;
Expand Down Expand Up @@ -666,6 +751,34 @@ private bool IsCustomMethodSet()
return (ParameterSetName == "CustomMethod");
}

private string GetBasicAuthorizationHeader()
{
string unencoded = String.Format("{0}:{1}", Credential.UserName, Credential.GetNetworkCredential().Password);
Byte[] bytes = Encoding.UTF8.GetBytes(unencoded);
return String.Format("Basic {0}", Convert.ToBase64String(bytes));
}

private string GetBearerAuthorizationHeader()
{
return String.Format("Bearer {0}", new NetworkCredential(String.Empty, Token).Password);
}

private void ProcessAuthentication()
{
if(Authentication == WebAuthenticationType.Basic)
{
WebSession.Headers["Authorization"] = GetBasicAuthorizationHeader();
}
else if (Authentication == WebAuthenticationType.Bearer || Authentication == WebAuthenticationType.OAuth)
{
WebSession.Headers["Authorization"] = GetBearerAuthorizationHeader();
}
else
{
Diagnostics.Assert(false, String.Format("Unrecognized Authentication value: {0}", Authentication));
}
}

#endregion Helper Methods
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,21 @@
<data name="AccessDenied" xml:space="preserve">
<value>Access to the path '{0}' is denied.</value>
</data>
<data name="AllowUnencryptedAuthenticationRequired" xml:space="preserve">
<value>The cmdlet cannot protect plain text secrets sent over unencrypted connections. To supress this warning and send plain text secrets over unencrypted networks, reissue the command specifying the AllowUnencryptedAuthentication parameter.</value>
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think it is more readable to put parameter names in single quotes AllowUnencryptedAuthentication ...
We use the format in other Resx files.
Please edit messages below too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is a mix, from what I have seen, of quoted, unquoted and Hyphen prepended in the project. Since there is no clear set standard, I went with the style used for the Web Cmdlets. Please look at the errors in this Resx. I would rather have a separate PR to address the errors styles as whole for these commands than have a few that do not match in this PR.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I agree. Could you please open a tracking issue (or PR)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I could.. but I think another Issue on standardizing how parameters are displayed in errors within the project is probably warranted first. I'd hate to go through the trouble of changing these error and then have the current way be later selected as the standard (I would hope it's not, because I agree it's not very readable, but its possible). thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

</data>
<data name="AuthenticationConflict" xml:space="preserve">
<value>The cmdlet cannot run because the following conflicting parameters are specified: Authentication and UseDefaultCredentials. Authentication does not support Default Credentials. Specify either Authentication or UseDefaultCredentials, then retry.</value>
</data>
<data name="AuthenticationCredentialNotSupplied" xml:space="preserve">
<value>The cmdlet cannot run because the following parameter is not specified: Credential. The supplied Authentication type requires a Credential. Specify Credential, then retry.</value>
</data>
<data name="AuthenticationTokenNotSupplied" xml:space="preserve">
<value>The cmdlet cannot run because the following parameter is not specified: Token. The supplied Authentication type requires a Token. Specify Token, then retry.</value>
</data>
<data name="AuthenticationTokenConflict" xml:space="preserve">
<value>The cmdlet cannot run because the following conflicting parameters are specified: Credential and Token. Specify either Credential or Token, then retry.</value>
</data>
<data name="BodyConflict" xml:space="preserve">
<value>The cmdlet cannot run because the following conflicting parameters are specified: Body and InFile. Specify either Body or Infile, then retry. </value>
</data>
Expand Down
Loading