Skip to content

Commit

Permalink
Merge branch 'dev' into UndoCheckOut
Browse files Browse the repository at this point in the history
  • Loading branch information
KoenZomers authored Feb 22, 2023
2 parents 562304b + 53d8c4f commit 7281ad7
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 29 deletions.
9 changes: 8 additions & 1 deletion documentation/Invoke-PnPGraphMethod.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Invoke-PnPGraphMethod -Url <String>
```

## DESCRIPTION
Invokes a REST request towards the Microsoft Graph API
Invokes a REST request towards the Microsoft Graph API. It will take care of potential throttling retries that are needed to retrieve the data.

## EXAMPLES

Expand Down Expand Up @@ -61,6 +61,13 @@ Invoke-PnPGraphMethod -Url "v1.0/users?$filter=accountEnabled ne true&$count=tru

Get users with advanced query capabilities. Use of -ConsistencyLevelEventual.

### Example 5
```powershell
Invoke-PnPGraphMethod "https://graph.microsoft.com/v1.0/users"
```

Performs a GET request to retrieve users from the Microsoft Graph API using the full URL.

## PARAMETERS

### -AdditionalHeaders
Expand Down
18 changes: 9 additions & 9 deletions pages/articles/connecting.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,21 @@ This will launch a device login flow that will ask you to consent to the applica
After that you can authenticate using

```powershell
Connect-PnPOnline -Url https://[yourtenant].sharepoint.com -Credentials (Get-Credential)
Connect-PnPOnline -Url https://[tenant].sharepoint.com -Credentials (Get-Credential)
```

or in case the account you would like to use has MFA or any other authentication provider configured for it, instead use:

```powershell
Connect-PnPOnline -Url https://[yourtenant].sharepoint.com -Interactive
Connect-PnPOnline -Url https://[tenant].sharepoint.com -Interactive
```

### Connect by using your own Azure AD Application

You will have to create your own Azure AD Application registration, or you can create one:

```powershell
Register-PnPAzureADApp -ApplicationName "YourApplicationName" -Tenant [yourtenant.onmicrosoft.com] -Interactive
Register-PnPAzureADApp -ApplicationName "YourApplicationName" -Tenant [tenant.onmicrosoft.com] -Interactive
```

This will launch a authentication dialog where you need to authenticate. After closing this window the cmdlet will continue to register a new application with a set of default permissions. By default a certificate will be generated and stored in the current folder, named after the application you want to create. You can specify your own certificate by using the `-CertificatePath` parameter and optional `-CertificatePassword` parameter.
Expand All @@ -43,7 +43,7 @@ You can add permissions by using the `-GraphApplicationPermissions`, `-GraphDele
Note if you are using Credential Based Authentication, you will need to make a change to the app registration manifest file. Go to the app registration, select Manifest under the Manage section, then change the "allowPublicClient" property to true and click save.

```powershell
Connect-PnPOnline -Url "https://[yourtenant.sharepoint.com] -Credentials (Get-Credential) -ClientId [clientid]
Connect-PnPOnline -Url "https://[tenant.sharepoint.com] -Credentials (Get-Credential) -ClientId [clientid]
```

## Connect interactively using WebLogin supporting MFA
Expand All @@ -59,15 +59,15 @@ Connect-PnPOnline -Url https://tenant.sharepoint.com -UseWebLogin
Allows using an Azure Active Directory app registration from your own Azure Active Directory with a certificate to connect. The private key certificate, typically the .pfx file, should be accessible on your local machine.

The following will generate an Azure AD Application registration and create a certificate containing a public and private key.

```powershell
$password = ConvertTo-SecureString -String "password" -AsPlainText -Force
Register-PnPAzureADApp -ApplicationName "PnPPowerShell" -Tenant yourtenant.onmicrosoft.com
Register-PnPAzureADApp -ApplicationName "PnPPowerShell" -Tenant tenant.onmicrosoft.com -Password (ConvertTo-SecureString -String "password" -AsPlainText -Force)
```

You will be asked to authenticate. After that the cmdlet will generate two files, PnPPowerShell.pfx and PnPPowerShell.cer and a new Azure AD Application will be registered with the specified name. The public key/CER file will be uploaded and registered with the newly create application registration. You will have to use the .pfx file to connect. Notice that the `Register-PnPAzureADApp` cmdlet only have to be executed once per tenant/application.

```PowerShell
Connect-PnPOnline -ClientId fa1a81f1-e729-44d8-bb71-0a0c339c0f62 -Url "https://tenant.sharepoint.com" -Tenant tenant.onmicrosoft.com -CertificatePath '.\PnPPowerShell.pfx'
```powershell
Connect-PnPOnline -ClientId fa1a81f1-e729-44d8-bb71-0a0c339c0f62 -Url "https://tenant.sharepoint.com" -Tenant tenant.onmicrosoft.com -CertificatePath '.\PnPPowerShell.pfx' -CertificatePassword (ConvertTo-SecureString -AsPlainText -Force "password")
```

## Connect using a ClientId and PFX certificate stored in the Windows Certificate Management Store
Expand All @@ -77,7 +77,7 @@ Allows using an Azure Active Directory app registration from your own Azure Acti
The following will generate an Azure AD Application registration and create a certificate containing a public and private key which will be stored for the current user in the Windows Certificate Management Store.
```powershell
$password = ConvertTo-SecureString -String "password" -AsPlainText -Force
Register-PnPAzureADApp -ApplicationName "PnPPowerShell" -Tenant yourtenant.onmicrosoft.com -Store CurrentUser
Register-PnPAzureADApp -ApplicationName "PnPPowerShell" -Tenant tenant.onmicrosoft.com -Store CurrentUser
```

You will be asked to authenticate. After that the cmdlet will generate a certificate and will store it in the Windows Certificate Management Store and a new Azure AD Application will be registered with the specified name. The public key of the certificate file will be uploaded and registered with the newly create application registration. Notice that the `Register-PnPAzureADApp` cmdlet only have to be executed once per tenant/application. The output of the cmdlet contains the thumbprint to use.
Expand Down
17 changes: 9 additions & 8 deletions src/Commands/Base/ConnectOnline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -480,10 +480,11 @@ private PnPConnection ConnectAppOnlyWithCertificate()
{
throw new FileNotFoundException("Certificate not found");
}
X509Certificate2 certificate = CertificateHelper.GetCertificateFromPath(CertificatePath, CertificatePassword);
if (Connection?.ClientId == ClientId &&
Connection?.Tenant == Tenant &&
Connection?.Certificate?.Thumbprint == certificate.Thumbprint)

X509Certificate2 certificate = CertificateHelper.GetCertificateFromPath(this, CertificatePath, CertificatePassword);
if (PnPConnection.Current?.ClientId == ClientId &&
PnPConnection.Current?.Tenant == Tenant &&
PnPConnection.Current?.Certificate?.Thumbprint == certificate.Thumbprint)
{
ReuseAuthenticationManager();
}
Expand Down Expand Up @@ -651,10 +652,10 @@ private PnPConnection ConnectEnvironmentVariable(InitializationType initializati

SecureString secPassword = StringToSecureString(azureCertPassword);

X509Certificate2 certificate = CertificateHelper.GetCertificateFromPath(azureCertificatePath, secPassword);
if (Connection?.ClientId == azureClientId &&
Connection?.Tenant == Tenant &&
Connection?.Certificate?.Thumbprint == certificate.Thumbprint)
X509Certificate2 certificate = CertificateHelper.GetCertificateFromPath(this, azureCertificatePath, secPassword);
if (PnPConnection.Current?.ClientId == azureClientId &&
PnPConnection.Current?.Tenant == Tenant &&
PnPConnection.Current?.Certificate?.Thumbprint == certificate.Thumbprint)
{
ReuseAuthenticationManager();
}
Expand Down
2 changes: 1 addition & 1 deletion src/Commands/Graph/InvokeGraphMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace PnP.PowerShell.Commands.Base
[Cmdlet(VerbsLifecycle.Invoke, "PnPGraphMethod")]
public class InvokeGraphMethod : PnPGraphCmdlet
{
[Parameter(Mandatory = false, Position = 0)]
[Parameter(Mandatory = false)]
public HttpRequestMethod Method = HttpRequestMethod.Get;

private string _url;
Expand Down
40 changes: 30 additions & 10 deletions src/Commands/Utilities/CertificateHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,23 +133,45 @@ internal static X509Certificate2 GetCertificateFromStore(string thumbprint)
return null;
}

internal static X509Certificate2 GetCertificateFromPath(string certificatePath, SecureString certificatePassword)
/// <summary>
/// Opens the X509Certificate2 at the provided path using the provided certificate password
/// </summary>
/// <param name="cmdlet">Cmdlet executing this function</param>
/// <param name="certificatePath">Path to the private key certificate file</param>
/// <param name="certificatePassword">Password to open the certificate or NULL if no password set on the certificate</param>
/// <returns>X509Certificate2 instance</returns>
/// <exception cref="PSArgumentException">Thrown if the certificate cannot be read</exception>
/// <exception cref="FileNotFoundException">Thrown if the certificate cannot be found at the provided path</exception>
internal static X509Certificate2 GetCertificateFromPath(Cmdlet cmdlet, string certificatePath, SecureString certificatePassword)
{
if (System.IO.File.Exists(certificatePath))
{
cmdlet.WriteVerbose($"Reading certificate from file '{certificatePath}'");

var certFile = System.IO.File.OpenRead(certificatePath);
if (certFile.Length == 0)
{
throw new PSArgumentException($"The specified certificate path '{certificatePath}' points to an empty file");
}

var certificateBytes = new byte[certFile.Length];
certFile.Read(certificateBytes, 0, (int)certFile.Length);
var certificate = new X509Certificate2(
certificateBytes,
certificatePassword,
X509KeyStorageFlags.Exportable |
X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.PersistKeySet);
return certificate;

cmdlet.WriteVerbose($"Opening certificate in file '{certificatePath}' {(certificatePassword == null ? "without" : "using")} a certificate password");
try
{
var certificate = new X509Certificate2(
certificateBytes,
certificatePassword,
X509KeyStorageFlags.Exportable |
X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.PersistKeySet);
return certificate;
}
catch (CryptographicException e)
{
throw new PSArgumentException($"The specified certificate at '{certificatePath}' could not be read. The certificate could be corrupt or it may require a password which has not been provided or is incorrect.", e);
}
}
else if (System.IO.Directory.Exists(certificatePath))
{
Expand All @@ -161,8 +183,6 @@ internal static X509Certificate2 GetCertificateFromPath(string certificatePath,
}
}



#region certificate manipulation
private static void EncodeLength(BinaryWriter stream, int length)
{
Expand Down

0 comments on commit 7281ad7

Please sign in to comment.