seanmcne / Microsoft.Xrm.Data.PowerShell

This module uses the CRM connection from Microsoft.Xrm.Tooling.CrmConnector.Powershell and provides common functions to create, delete, query, and update data as well as functions for common tasks such as publishing, and manipulating System & CRM User Settings, etc. The module should function for both Dynamics CRM Online and On-Premise environment.
204 stars 65 forks source link

Connection fails if some parameters contain single quotes passed to the CrmServiceClient constructor. #518

Open break9111 opened 1 year ago

break9111 commented 1 year ago

I have identified the following error, which I think needs to be resolved.

How to reproduce the error When connecting to Dataverse using Connect-CrmOnline, an error occurs if the user's password contains single quotes.

Description of the error

PS C:\Users\aaaa> Connect-CrmOnline  -ServerUrl https://org27XXXXXX.crm.dynamics.com -Credential $cred
New-Object : Exception calling ".ctor" with "1" argument(s): "Object reference not set to an instance of an object."
At C:\Users\aaaa\Documents\WindowsPowerShell\Modules\Microsoft.Xrm.Data.Powershell\2.8.19\Microsoft.Xrm.Data.Powershel
l.psm1:255 char:19
+ ... obal:conn = New-Object Microsoft.Xrm.Tooling.Connector.CrmServiceClie ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [New-Object], MethodInvocationException
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

Cause of error When calling the CrmServiceClient constructor, the input parameters for Connect-CrmOnline are concatenated into a single string and passed to the constructor as follows. If the password contains single quotes, the process of parsing this concatenated string will fail, resulting in an error before any authentication process is performed.

$global:conn = New-Object Microsoft.Xrm.Tooling.Connector.CrmServiceClient -ArgumentList $cs

value

$cs = "RequireNewInstance=True;Url={your Dataverse environment URL};AuthType=Office365;Username=testuser@aaaaa.onmicrosoft.com;Password='Pass'word'";

Other ways to cause errors I have observed that the same error occurs with the -ConnectionString parameter as well as the -Credential parameter. As far as I have tried in my environment, I think the same error occurs with Secret, but I don't know if the client secret of the service principal could contain single quotes. Does anyone know what the rule is for this?

Solution I contacted MSFT with a support request, but they replied that they are not going to update the CrmServiceClient constructor. To avoid the error, I think we need to update the logic of Connect-CrmOnline, if the password or connection string contains single quotes, to double the single quotes (escape) by replace function.

Password='Pass'word' → Password='Pass''word'

seanmcne commented 1 year ago

As a workaround for this trying using oAuth (-forceOAuth) that will help ensure its connecting using modern auth but also you won't have to handle the password directly (if you auth via the browser window directly).

Since escaping works as a workaround, let me see about building that in (replacing any single quotes with '' inside of the parameter itself). Thx for identifying this and reporting it!

break9111 commented 1 year ago

Thank you for your reply. Even if -forceOAuth is used, an error will occur as long as -Credential is also used. (I assume you replied about using -forceOAuth only)

As you say, I agree that authentication via the browser (interactive) will allow a successful connection using a user with a password containing single quotes.

seanmcne commented 1 year ago

I was able to find a few minutes today to code up something I think will work here https://github.com/seanmcne/Microsoft.Xrm.Data.PowerShell/tree/AddSingleQuotePasswordEscaping

Still need to test/validate and look at publishing but if you have a min to test it out let me know. Thx!

break9111 commented 1 year ago

I checked the commit you made. The fixes were exactly what I expected. I don't think there is a problem because the results are the same as what I have tested in the past.

One thing I'm worried about is when we used -ConnectionString as a parameter. I want to cut out the password from the value that combines the parameters whose order is not fixed and replace it in the same way, but there is no appropriate delimiter.

Single quote is inappropriate because it is the cause of this issue, and other symbols (; and =) can also be included in passwords, so it is not possible to cut the password and value reliably. Am I correct in understanding that there is no workaround for this part?

seanmcne commented 1 year ago

I just stumbled up a comment in a stackoverflow that might offer a more comprehensive way to escape the string - I'll test this out later but wanted to be sure I captured it here.

# all chars. inside [...] are -escaped
'a*b\c~d;e(f%g?h.i:j@k/l' -replace '[*\\~;(%?.:@/]', '`$&'a`*b`\c`~d`;e`(f`%g`?h`.i`:j`@k`/l