Merge pull request #655 from whoisj/vsts/secret-storage
[series 2/2] Improve credential key generation for Azure hosted repositories.
This commit is contained in:
Коммит
f09ebdba40
|
@ -20,13 +20,9 @@ using System.Runtime.InteropServices;
|
|||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("ee663736-5bad-4ca6-a4f8-99978925ad8a")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version Minor Version Build Number Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers by using the '*'
|
||||
// as shown below: [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("2.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("2.0.0.0")]
|
||||
[assembly: AssemblyVersion("2.0.1.0")]
|
||||
[assembly: AssemblyFileVersion("2.0.1.0")]
|
||||
|
||||
[assembly: InternalsVisibleTo("Bitbucket.Authentication.Test")]
|
||||
|
||||
|
|
|
@ -153,9 +153,11 @@ namespace Microsoft.Alm.Authentication.Git
|
|||
// Gloves off...
|
||||
unsafe
|
||||
{
|
||||
var basicInfo = new Win32.ProcessBasicInformation { };
|
||||
int bytesRead = 0;
|
||||
long outResult = 0;
|
||||
|
||||
var basicInfo = new Win32.ProcessBasicInformation { };
|
||||
|
||||
// Ask the OS for information about the process, this will include the address of the PEB or
|
||||
// Process Environment Block, which contains useful information (like the offset of the process' parameters).
|
||||
var hresult = Win32.Ntdll.QueryInformationProcess(processHandle: processHandle,
|
||||
|
@ -173,7 +175,6 @@ namespace Microsoft.Alm.Authentication.Git
|
|||
return false;
|
||||
}
|
||||
|
||||
int bytesRead = 0;
|
||||
var peb = new Win32.ProcessEnvironmentBlock { };
|
||||
|
||||
// Now that we know the offsets of the process' parameters, read it because
|
||||
|
@ -187,7 +188,7 @@ namespace Microsoft.Alm.Authentication.Git
|
|||
{
|
||||
var error = Win32.Kernel32.GetLastError();
|
||||
|
||||
Trace.WriteLine($"failed to read process environment block [{error}].");
|
||||
Trace.WriteLine($"failed to read process environment block [{error}] ({bytesRead:n0} bytes read).");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -201,11 +202,11 @@ namespace Microsoft.Alm.Authentication.Git
|
|||
buffer: &processParameters,
|
||||
bufferSize: sizeof(Win32.PebProcessParameters),
|
||||
bytesRead: out bytesRead)
|
||||
|| bytesRead != sizeof(Win32.PebProcessParameters))
|
||||
|| bytesRead < sizeof(Win32.PebProcessParameters))
|
||||
{
|
||||
var error = Win32.Kernel32.GetLastError();
|
||||
|
||||
Trace.WriteLine($"failed to read process parameters [{error}].");
|
||||
Trace.WriteLine($"failed to read process parameters [{error}] ({bytesRead:n0} bytes read).");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -223,7 +224,7 @@ namespace Microsoft.Alm.Authentication.Git
|
|||
{
|
||||
var error = Win32.Kernel32.GetLastError();
|
||||
|
||||
Trace.WriteLine($"failed to read process image path [{error}].");
|
||||
Trace.WriteLine($"failed to read process image path [{error}] ({bytesRead:n0} bytes read).");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -241,7 +242,7 @@ namespace Microsoft.Alm.Authentication.Git
|
|||
{
|
||||
var error = Win32.Kernel32.GetLastError();
|
||||
|
||||
Trace.WriteLine($"failed to read process command line [{error}].");
|
||||
Trace.WriteLine($"failed to read process command line [{error}] ({bytesRead:n0} bytes read).");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -13,8 +13,8 @@ using System.Runtime.InteropServices;
|
|||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: Guid("c1daabc1-b493-459d-bb4f-04fbefb1ba13")]
|
||||
[assembly: AssemblyVersion("4.4.0.0")]
|
||||
[assembly: AssemblyFileVersion("4.4.0.0")]
|
||||
[assembly: AssemblyVersion("4.5.0.0")]
|
||||
[assembly: AssemblyFileVersion("4.5.0.0")]
|
||||
[assembly: NeutralResourcesLanguage("en-US")]
|
||||
|
||||
// Only expose internals when the binary isn't signed.
|
||||
|
|
|
@ -853,7 +853,7 @@ namespace Microsoft.Alm.Cli
|
|||
if (operationArguments is null)
|
||||
throw new ArgumentNullException(nameof(operationArguments));
|
||||
|
||||
if (program.Context.Utilities.TryReadGitRemoteHttpDetails(out string commandLine, out _))
|
||||
if (program.Utilities.TryReadGitRemoteHttpDetails(out string commandLine, out _))
|
||||
{
|
||||
operationArguments.GitRemoteHttpCommandLine = commandLine;
|
||||
}
|
||||
|
|
|
@ -416,6 +416,9 @@ namespace Microsoft.Alm.Cli
|
|||
internal Git.ITrace Trace
|
||||
=> _context.Trace;
|
||||
|
||||
internal Git.IUtilities Utilities
|
||||
=> _context.Utilities;
|
||||
|
||||
internal Git.IWhere Where
|
||||
=> _context.Where;
|
||||
|
||||
|
@ -471,22 +474,22 @@ namespace Microsoft.Alm.Cli
|
|||
}
|
||||
|
||||
internal void Die(Exception exception,
|
||||
[CallerFilePath] string path = "",
|
||||
[CallerLineNumber] int line = 0,
|
||||
[CallerMemberName] string name = "")
|
||||
[CallerFilePath] string path = "",
|
||||
[CallerLineNumber] int line = 0,
|
||||
[CallerMemberName] string name = "")
|
||||
=> _dieException(this, exception, path, line, name);
|
||||
|
||||
internal void Die(string message,
|
||||
[CallerFilePath] string path = "",
|
||||
[CallerLineNumber] int line = 0,
|
||||
[CallerMemberName] string name = "")
|
||||
[CallerFilePath] string path = "",
|
||||
[CallerLineNumber] int line = 0,
|
||||
[CallerMemberName] string name = "")
|
||||
=> _dieMessage(this, message, path, line, name);
|
||||
|
||||
internal void Exit(int exitcode = 0,
|
||||
string message = null,
|
||||
[CallerFilePath] string path = "",
|
||||
[CallerLineNumber] int line = 0,
|
||||
[CallerMemberName] string name = "")
|
||||
string message = null,
|
||||
[CallerFilePath] string path = "",
|
||||
[CallerLineNumber] int line = 0,
|
||||
[CallerMemberName] string name = "")
|
||||
=> _exit(this, exitcode, message, path, line, name);
|
||||
|
||||
internal Task LoadOperationArguments(OperationArguments operationArguments)
|
||||
|
|
|
@ -97,6 +97,32 @@ namespace Microsoft.Alm.Win32
|
|||
[return: MarshalAs(UnmanagedType.U4)]
|
||||
public static extern uint GetCurrentProcessId();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the process identifier of the specified process.
|
||||
/// </summary>
|
||||
/// <param name="processHandle">
|
||||
/// A handle to the process.
|
||||
/// <para/>
|
||||
/// The handle must have the `<seealso cref="DesiredAccess.QueryInformation"/>` or `<seealso cref="DesiredAccess.QueryLimitedInformation"/>` access right.
|
||||
/// </param>
|
||||
[DllImport(Name, BestFitMapping = false, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, EntryPoint = "GetProcessId", ExactSpelling = true, SetLastError = true, ThrowOnUnmappableChar = true)]
|
||||
[return: MarshalAs(UnmanagedType.U4)]
|
||||
public static extern uint GetProcessId(
|
||||
[In] SafeProcessHandle processHandle);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the process identifier of the specified process.
|
||||
/// </summary>
|
||||
/// <param name="processHandle">
|
||||
/// A handle to the process.
|
||||
/// <para/>
|
||||
/// The handle must have the `<seealso cref="DesiredAccess.QueryInformation"/>` or `<seealso cref="DesiredAccess.QueryLimitedInformation"/>` access right.
|
||||
/// </param>
|
||||
[DllImport(Name, BestFitMapping = false, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, EntryPoint = "GetProcessId", ExactSpelling = true, SetLastError = true, ThrowOnUnmappableChar = true)]
|
||||
[return: MarshalAs(UnmanagedType.U4)]
|
||||
public static extern uint GetProcessId(
|
||||
[In] IntPtr processHandle);
|
||||
|
||||
/// <summary>
|
||||
/// Opens an existing local process object.
|
||||
/// <para/>
|
||||
|
|
|
@ -299,16 +299,55 @@ namespace Microsoft.Alm.Win32
|
|||
internal enum Hresult : uint
|
||||
{
|
||||
Ok = 0x00000000,
|
||||
|
||||
/// <summary>
|
||||
/// Not implemented.
|
||||
/// </summary>
|
||||
NotImplemented = 0x80004001,
|
||||
|
||||
/// <summary>
|
||||
/// No such interface supported.
|
||||
/// </summary>
|
||||
NoInterface = 0x80004002,
|
||||
|
||||
/// <summary>
|
||||
/// Invalid pointer.
|
||||
/// </summary>
|
||||
InvalidPointer = 0x80004003,
|
||||
|
||||
/// <summary>
|
||||
/// Operation aborted.
|
||||
/// </summary>
|
||||
Abort = 0x80004004,
|
||||
|
||||
/// <summary>
|
||||
/// Unspecified error.
|
||||
/// </summary>
|
||||
Fail = 0x80004005,
|
||||
|
||||
/// <summary>
|
||||
/// Catastrophic failure.
|
||||
/// </summary>
|
||||
Unexpected = 0x8000FFFF,
|
||||
|
||||
/// <summary>
|
||||
/// General access denied error.
|
||||
/// </summary>
|
||||
AccessDenied = 0x80070005,
|
||||
|
||||
InvalidHandle = 0x80070006,
|
||||
|
||||
OutOfMemory = 0x8007000E,
|
||||
|
||||
/// <summary>
|
||||
/// One or more arguments are invalid.
|
||||
/// </summary>
|
||||
InvalidArgument = 0x80070057,
|
||||
|
||||
/// <summary>
|
||||
/// There is not enough space on the disk.
|
||||
/// </summary>
|
||||
DiskFull = 0x80070070,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -369,56 +408,113 @@ namespace Microsoft.Alm.Win32
|
|||
public string ExeFileName;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x30)]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe struct ProcessEnvironmentBlock
|
||||
{
|
||||
[FieldOffset(0x02)]
|
||||
private byte _offset_0x02;
|
||||
[FieldOffset(0x20)]
|
||||
private PebProcessParameters* _offset_0x20;
|
||||
fixed byte _[256];
|
||||
|
||||
/// <summary>
|
||||
/// Gets `<see langword="true"/> if the process is currently being debugged; otherwise `<see langword="false"/>`.
|
||||
/// <para/>
|
||||
/// It is best to use the `CheckRemoteDebuggerPresent` function instead.
|
||||
/// </summary>
|
||||
public bool IsBeingDebugged
|
||||
{
|
||||
get { return _offset_0x02 != 0; }
|
||||
get { fixed (byte* p = _) { return p[2] != 0; } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a pointer a `<seealso cref="PebProcessParameters"/>` structure.
|
||||
/// </summary>
|
||||
public PebProcessParameters* ProcessParameters
|
||||
{
|
||||
get { return _offset_0x20; }
|
||||
get
|
||||
{
|
||||
fixed (byte* p = _)
|
||||
{
|
||||
return IntPtr.Size == 4
|
||||
? *((PebProcessParameters**)(p + 0x10))
|
||||
: *((PebProcessParameters**)(p + 0x20));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 104)]
|
||||
[StructLayout(LayoutKind.Sequential, Size = 128)]
|
||||
internal unsafe struct PebProcessParameters
|
||||
{
|
||||
[FieldOffset(0x60)]
|
||||
private UnicodeString _offset_0x60;
|
||||
[FieldOffset(0x70)]
|
||||
private UnicodeString _offset_0x70;
|
||||
fixed byte _[128];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the command-line string passed to the process.
|
||||
/// </summary>
|
||||
public UnicodeString CommandLine
|
||||
{
|
||||
get { return _offset_0x70; }
|
||||
get
|
||||
{
|
||||
fixed (byte* p = _)
|
||||
{
|
||||
return IntPtr.Size == 4
|
||||
? *((UnicodeString*)(p + 0x40))
|
||||
: *((UnicodeString*)(p + 0x70));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path of the image file for the process.
|
||||
/// </summary>
|
||||
public UnicodeString ImagePathName
|
||||
{
|
||||
get { return _offset_0x60; }
|
||||
get
|
||||
{
|
||||
fixed (byte* p = _)
|
||||
{
|
||||
return IntPtr.Size == 4
|
||||
? *((UnicodeString*)(p + 0x38))
|
||||
: *((UnicodeString*)(p + 0x60));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe struct ProcessBasicInformation
|
||||
{
|
||||
private IntPtr _exitStatus;
|
||||
private IntPtr _pebBaseAddress;
|
||||
private IntPtr _affinityMask;
|
||||
private IntPtr _basePriority;
|
||||
private UIntPtr _uniqueProcessId;
|
||||
private IntPtr _inheritedFromUniqueProcessId;
|
||||
fixed byte _[32];
|
||||
|
||||
public byte* ProcessEnvironmentBlock
|
||||
/// <summary>
|
||||
/// Gets the address of the `<seealso cref="ProcessEnvironmentBlock"/>`.
|
||||
/// </summary>
|
||||
public void* ProcessEnvironmentBlock
|
||||
{
|
||||
get { return (byte*)_pebBaseAddress.ToPointer(); }
|
||||
get
|
||||
{
|
||||
fixed (byte* p = _)
|
||||
{
|
||||
return IntPtr.Size == 4
|
||||
? *((void**)(p + 0x04))
|
||||
: *((void**)(p + 0x08));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a pointer to the system's unique identifier for this process.
|
||||
/// <para/>
|
||||
/// Use the `<seealso cref="Kernel32.GetCurrentProcessId(IntPtr)"/>` function to retrieve this information.
|
||||
/// </summary>
|
||||
public uint* UniqueProcessId
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (byte* p = _)
|
||||
{
|
||||
return IntPtr.Size == 4
|
||||
? *((uint**)(p + 0x10))
|
||||
: *((uint**)(p + 0x20));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -504,22 +600,26 @@ namespace Microsoft.Alm.Win32
|
|||
/// <summary>
|
||||
/// Represents a Unicode encoded string.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit, Pack = 1)]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
[System.Diagnostics.DebuggerDisplay("{DebuggerDisplay, nq}")]
|
||||
internal unsafe struct UnicodeString
|
||||
{
|
||||
[FieldOffset(0x00)]
|
||||
private ushort _field1;
|
||||
[FieldOffset(0x02)]
|
||||
private ushort _field2;
|
||||
[FieldOffset(0x08)]
|
||||
private IntPtr _field3;
|
||||
fixed byte _[16];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer to the character data buffer.
|
||||
/// </summary>
|
||||
public char* Buffer
|
||||
{
|
||||
get { return (char*)_field3.ToPointer(); }
|
||||
get
|
||||
{
|
||||
fixed (byte* p = _)
|
||||
{
|
||||
return (IntPtr.Size == 4)
|
||||
? *((char**)(p + 0x04))
|
||||
: *((char**)(p + 0x08));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -527,7 +627,7 @@ namespace Microsoft.Alm.Win32
|
|||
/// </summary>
|
||||
public int Length
|
||||
{
|
||||
get { return _field1 / sizeof(char); }
|
||||
get { fixed (byte* p = _) { return *((ushort*)(p + 0x00)) / sizeof(char); } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -535,7 +635,23 @@ namespace Microsoft.Alm.Win32
|
|||
/// </summary>
|
||||
public int MaximumSize
|
||||
{
|
||||
get { return _field2; }
|
||||
get { fixed (byte* p = _) { return *((ushort*)(p + 0x02)); } }
|
||||
}
|
||||
|
||||
internal string DebuggerDisplay
|
||||
{
|
||||
get { return $"{nameof(UnicodeString)}: \"{ToString() ?? "<NULL>"}\""; }
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (Buffer == null)
|
||||
return null;
|
||||
|
||||
if (Length == 0)
|
||||
return string.Empty;
|
||||
|
||||
return new string(Buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ using System.Runtime.InteropServices;
|
|||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: Guid("c1daabc1-b493-459d-bb4f-04fbefb1ba13")]
|
||||
[assembly: AssemblyVersion("4.4.0.0")]
|
||||
[assembly: AssemblyFileVersion("4.4.0.0")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: NeutralResourcesLanguage("en-US")]
|
||||
|
||||
// Only expose internals when the binary isn't signed.
|
||||
|
|
|
@ -30,6 +30,14 @@ namespace VisualStudioTeamServices.Authentication.Test
|
|||
{
|
||||
public abstract class AuthenticationTests
|
||||
{
|
||||
protected AuthenticationTests()
|
||||
{
|
||||
if (Trace.Listeners.Count == 0)
|
||||
{
|
||||
Trace.Listeners.AddRange(Debug.Listeners);
|
||||
}
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
|
||||
protected static readonly Credential DefaultCredentials = new Credential("username", "password");
|
||||
|
||||
|
@ -50,13 +58,5 @@ namespace VisualStudioTeamServices.Authentication.Test
|
|||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
|
||||
protected static readonly TokenScope DefaultTokenScope = TokenScope.CodeWrite;
|
||||
|
||||
protected AuthenticationTests()
|
||||
{
|
||||
if (Trace.Listeners.Count == 0)
|
||||
{
|
||||
Trace.Listeners.AddRange(Debug.Listeners);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
/**** Git Credential Manager for Windows ****
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* 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."
|
||||
**/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Alm.Authentication;
|
||||
using Xunit;
|
||||
|
||||
namespace VisualStudioTeamServices.Authentication.Test
|
||||
{
|
||||
public class AuthorityTests
|
||||
{
|
||||
public static object[][] GetSecretKeyData
|
||||
{
|
||||
get
|
||||
{
|
||||
const string Org = "organization";
|
||||
const string Path = "project/_git/repository";
|
||||
const string Username = "user";
|
||||
const string VstsUrl = "visualstudio.com";
|
||||
const string CodexUrl = "codex.azure.com";
|
||||
|
||||
var boolean = new[] { false, true };
|
||||
var hosts = new[] { CodexUrl, VstsUrl, };
|
||||
|
||||
var data = new List<object[]>();
|
||||
|
||||
foreach (string host in hosts)
|
||||
{
|
||||
foreach (bool defaultPort in boolean)
|
||||
{
|
||||
foreach (bool hasUsername in boolean)
|
||||
{
|
||||
foreach (bool hasRemoteUrl in boolean)
|
||||
{
|
||||
foreach (bool hasFullPath in boolean)
|
||||
{
|
||||
var targetBuilder = new UriBuilder()
|
||||
{
|
||||
// VSTS uses {organization}.{host} where as Codex does not.
|
||||
Host = host.Equals(VstsUrl) ? Org + '.' + host : host,
|
||||
Scheme = Uri.UriSchemeHttps,
|
||||
};
|
||||
var expectedBuilder = new UriBuilder()
|
||||
{
|
||||
// VSTS uses {organization}.{host} where as Codex does not.
|
||||
Host = host.Equals(VstsUrl) ? Org + '.' + host : host,
|
||||
Scheme = Uri.UriSchemeHttps,
|
||||
};
|
||||
var remoteUrlBuilder = new UriBuilder()
|
||||
{
|
||||
// VSTS uses {organization}.{host} where as Codex does not.
|
||||
Host = host.Equals(VstsUrl) ? Org + '.' + host : host,
|
||||
// Codex places {organization} first on path, VSTS does not.
|
||||
Path = host.Equals(CodexUrl) ? Org + '/' : "/",
|
||||
Scheme = Uri.UriSchemeHttps,
|
||||
};
|
||||
|
||||
// If the URL has a non-default port, set it.
|
||||
if (!defaultPort)
|
||||
{
|
||||
targetBuilder.Port = 8080;
|
||||
expectedBuilder.Port = 8080;
|
||||
remoteUrlBuilder.Port = 8080;
|
||||
}
|
||||
|
||||
// If the URL includes a username, include it.
|
||||
if (hasUsername)
|
||||
{
|
||||
// Append username to target
|
||||
targetBuilder.UserName = Username;
|
||||
|
||||
// Append username to expected
|
||||
expectedBuilder.UserName = Username;
|
||||
|
||||
// Handle Codex oddities
|
||||
if (host.Equals(CodexUrl))
|
||||
{
|
||||
// If there's not a remote URL, then the username becomes the path of the expected and remote values.
|
||||
if (!hasRemoteUrl)
|
||||
{
|
||||
expectedBuilder.Path = Username + '/';
|
||||
remoteUrlBuilder.Path = Username + '/';
|
||||
}
|
||||
}
|
||||
|
||||
// The remote URL is the superset, so append the username
|
||||
remoteUrlBuilder.UserName = Username;
|
||||
}
|
||||
// Handle Codex oddities...
|
||||
else if (host.Equals(CodexUrl))
|
||||
{
|
||||
// If there's no remote URL, then Org needs to be the target's user-info;
|
||||
// and the expected's URL as well.
|
||||
if (!hasRemoteUrl)
|
||||
{
|
||||
targetBuilder.UserName = Org;
|
||||
expectedBuilder.UserName = Org;
|
||||
expectedBuilder.Path = Org + '/';
|
||||
}
|
||||
}
|
||||
|
||||
// Codex places the organization info in the path.
|
||||
if (hasRemoteUrl && host.Equals(CodexUrl))
|
||||
{
|
||||
expectedBuilder.Path = Org + '/';
|
||||
}
|
||||
|
||||
// Append the full path to both target and expected
|
||||
if (hasFullPath)
|
||||
{
|
||||
targetBuilder.Path = Org + '/' + Path;
|
||||
expectedBuilder.Path = Org + '/' + Path;
|
||||
}
|
||||
|
||||
// The remote URL is the superset, so append the path.
|
||||
remoteUrlBuilder.Path += Path;
|
||||
|
||||
// If the test doesn't contain a remote URL, set it to null.
|
||||
remoteUrlBuilder = hasRemoteUrl
|
||||
? remoteUrlBuilder
|
||||
: null;
|
||||
|
||||
data.Add(new object[]
|
||||
{
|
||||
targetBuilder.ToString(),
|
||||
remoteUrlBuilder?.ToString(),
|
||||
"git:" + expectedBuilder.ToString().Trim('/', '\\'),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
[Theory, MemberData(nameof(GetSecretKeyData))]
|
||||
public void GetSecretKey(string targetUrl, string actualUrl, string expectedKey)
|
||||
{
|
||||
var targetUri = new TargetUri(targetUrl, null, actualUrl);
|
||||
string actualKey = Authentication.GetSecretKey(targetUri, "git");
|
||||
|
||||
Assert.Equal(expectedKey, actualKey, StringComparer.Ordinal);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -52,11 +52,12 @@
|
|||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AuthenticationTests.cs" />
|
||||
<Compile Include="AuthorityFake.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="AadTests.cs" />
|
||||
<Compile Include="AuthorityFake.cs" />
|
||||
<Compile Include="AuthenticationTests.cs" />
|
||||
<Compile Include="AuthorityTests.cs" />
|
||||
<Compile Include="MsaTests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="TokenScopeTests.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
Загрузка…
Ссылка в новой задаче