1
0
Форкнуть 0
includes .gitattributes and .gitignore

core project work is feature complete
This commit is contained in:
J Wyman 2015-03-18 12:04:29 -07:00
Коммит b0b421ec4c
36 изменённых файлов: 2245 добавлений и 0 удалений

63
.gitattributes поставляемый Normal file
Просмотреть файл

@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

190
.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,190 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
x64/
build/
bld/
[Bb]in/
[Oo]bj/
# Roslyn cache directories
*.ide/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
#NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding addin-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
## TODO: Comment the next line if you want to checkin your
## web deploy settings but do note that will include unencrypted
## passwords
#*.pubxml
# NuGet Packages Directory
packages/*
## TODO: If the tool you use requires repositories.config
## uncomment the next line
#!packages/repositories.config
# Enable "build/" folder in the NuGet Packages folder since
# NuGet packages use it for MSBuild targets.
# This line needs to be after the ignore of the build folder
# (and the packages folder if the line above has been uncommented)
!packages/build/
# Windows Azure Build Output
csx/
*.build.csdef
# Windows Store app package directory
AppPackages/
# Others
sql/
*.Cache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# LightSwitch generated files
GeneratedArtifacts/
_Pvt_Extensions/
ModelManifest.xml

18
Cli/App.config Normal file
Просмотреть файл

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.IdentityModel.Clients.ActiveDirectory" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.14.0.0" newVersion="2.14.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

80
Cli/Cli.csproj Normal file
Просмотреть файл

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\LibGit2Sharp.0.21.0.176\build\net40\LibGit2Sharp.props" Condition="Exists('..\packages\LibGit2Sharp.0.21.0.176\build\net40\LibGit2Sharp.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{62F52119-63D4-40A8-A9DF-F1C4B473308A}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Microsoft.TeamFoundation.Git.Helpers.Authentication</RootNamespace>
<AssemblyName>git-credential-man</AssemblyName>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>a8aa1ff3</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="LibGit2Sharp, Version=0.21.0.176, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\LibGit2Sharp.0.21.0.176\lib\net40\LibGit2Sharp.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup>
<Compile Include="CredentialType.cs" />
<Compile Include="NativeMethods.cs" />
<Compile Include="OperationArguments.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Core\Core.csproj">
<Project>{c1daabc1-b493-459d-bb4f-04fbefb1ba13}</Project>
<Name>Core</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\LibGit2Sharp.0.21.0.176\build\net40\LibGit2Sharp.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\LibGit2Sharp.0.21.0.176\build\net40\LibGit2Sharp.props'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

18
Cli/CredentialType.cs Normal file
Просмотреть файл

@ -0,0 +1,18 @@
namespace Microsoft.TeamFoundation.Git.Helpers.Authentication
{
internal enum CredentialType
{
/// <summary>
/// Basic username and password scheme
/// </summary>
Basic,
/// <summary>
/// Username and password scheme using Microsoft's Live system
/// </summary>
MicrosoftAccount,
/// <summary>
/// Azure Directory Authentication based, including support for ADFS
/// </summary>
AzureDirectory,
}
}

142
Cli/NativeMethods.cs Normal file
Просмотреть файл

@ -0,0 +1,142 @@
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace Microsoft.TeamFoundation.Git.Helpers.Authentication
{
internal static class NativeMethods
{
internal enum CREDUIWIN : uint
{
/// <summary>
/// The caller is requesting that the credential provider return the user name and password in plain text.
/// This value cannot be combined with SECURE_PROMPT.
/// </summary>
GENERIC = 0x1,
/// <summary>
/// The Save check box is displayed in the dialog box.
/// </summary>
CHECKBOX = 0x2,
/// <summary>
/// Only credential providers that support the authentication package specified by the authPackage parameter should be enumerated.
/// This value cannot be combined with CREDUIWIN_IN_CRED_ONLY.
/// </summary>
AUTHPACKAGE_ONLY = 0x10,
/// <summary>
/// Only the credentials specified by the InAuthBuffer parameter for the authentication package specified by the authPackage parameter should be enumerated.
/// If this flag is set, and the InAuthBuffer parameter is NULL, the function fails.
/// This value cannot be combined with CREDUIWIN_AUTHPACKAGE_ONLY.
/// </summary>
IN_CRED_ONLY = 0x20,
/// <summary>
/// Credential providers should enumerate only administrators. This value is intended for User Account Control (UAC) purposes only. We recommend that external callers not set this flag.
/// </summary>
ENUMERATE_ADMINS = 0x100,
/// <summary>
/// Only the incoming credentials for the authentication package specified by the authPackage parameter should be enumerated.
/// </summary>
ENUMERATE_CURRENT_USER = 0x200,
/// <summary>
/// The credential dialog box should be displayed on the secure desktop. This value cannot be combined with CREDUIWIN_GENERIC.
/// Windows Vista: This value is not supported until Windows Vista with SP1.
/// </summary>
SECURE_PROMPT = 0x1000,
/// <summary>
/// The credential provider should align the credential BLOB pointed to by the refOutAuthBuffer parameter to a 32-bit boundary, even if the provider is running on a 64-bit system.
/// </summary>
PACK_32_WOW = 0x10000000,
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct CREDUI_INFO
{
public uint cbSize;
public IntPtr hwndParent;
public string pszMessageText;
public string pszCaptionText;
public IntPtr hbmBanner;
}
internal enum CREDUI_ERROR : int
{
NO_ERROR = 0,
ERROR_CANCELLED = 1223,
ERROR_NO_SUCH_LOGON_SESSION = 1312,
ERROR_NOT_FOUND = 1168,
ERROR_INVALID_ACCOUNT_NAME = 1315,
ERROR_INSUFFICIENT_BUFFER = 122,
ERROR_INVALID_PARAMETER = 87,
ERROR_INVALID_FLAGS = 1004
}
/// <summary>
/// The CredUIPromptForWindowsCredentials function creates and displays a configurable dialog box that allows users to supply credential information by using any credential provider installed on the local computer.
/// </summary>
/// <param name="uiInfo">
/// A pointer to a CREDUI_INFO structure that contains information for customizing the appearance of the dialog box that this function displays.
/// If the hwndParent member of the CREDUI_INFO structure is not NULL, this function displays a modal dialog box centered on the parent window.
/// If the hwndParent member of the CREDUI_INFO structure is NULL, the function displays a dialog box centered on the screen.
/// This function ignores the hbmBanner member of the CREDUI_INFO structure.
/// </param>
/// <param name="dwAuthError">A Windows error code, defined in Winerror.h, that is displayed in the dialog box. If credentials previously collected were not valid, the caller uses this parameter to pass the error message from the API that collected the credentials (for example, Winlogon) to this function. The corresponding error message is formatted and displayed in the dialog box. Set the value of this parameter to zero to display no error message.</param>
/// <param name="pulAuthPackage">
/// On input, the value of this parameter is used to specify the authentication package for which the credentials in the pvInAuthBuffer buffer are serialized. If the value of pvInAuthBuffer is NULL and the CREDUIWIN_AUTHPACKAGE_ONLY flag is set in the dwFlags parameter, only credential providers capable of serializing credentials for the specified authentication package are to be enumerated.
/// To get the appropriate value to use for this parameter on input, call the LsaLookupAuthenticationPackage function and use the value of the AuthenticationPackage parameter of that function.
/// On output, this parameter specifies the authentication package for which the credentials in the ppvOutAuthBuffer buffer are serialized.
/// </param>
/// <param name="pvInAuthBuffer">A pointer to a credential BLOB that is used to populate the credential fields in the dialog box. Set the value of this parameter to NULL to leave the credential fields empty.</param>
/// <param name="ulInAuthBufferSize">The size, in bytes, of the pvInAuthBuffer buffer.</param>
/// <param name="ppvOutAuthBuffer">
/// The address of a pointer that, on output, specifies the credential BLOB. For Kerberos, NTLM, or Negotiate credentials, call the CredUnPackAuthenticationBuffer function to convert this BLOB to string representations of the credentials.
/// When you have finished using the credential BLOB, clear it from memory by calling the SecureZeroMemory function, and free it by calling the CoTaskMemFree function.
/// </param>
/// <param name="pulOutAuthBufferSize">The size, in bytes, of the ppvOutAuthBuffer buffer.</param>
/// <param name="pfSave">
/// A pointer to a Boolean value that, on input, specifies whether the Save check box is selected in the dialog box that this function displays. On output, the value of this parameter specifies whether the Save check box was selected when the user clicks the Submit button in the dialog box. Set this parameter to NULL to ignore the Save check box.
/// This parameter is ignored if the CREDUIWIN_CHECKBOX flag is not set in the dwFlags parameter.
/// </param>
/// <param name="dwFlags">A value that specifies behavior for this function. </param>
/// <returns>If the function succeeds, the function returns ERROR_SUCCESS. If the function is canceled by the user, it returns ERROR_CANCELLED. Any other return value indicates that the function failed to load.</returns>
[DllImport("credui.dll", CharSet = CharSet.Unicode, EntryPoint = "CredUIPromptForWindowsCredentials")]
internal static extern CREDUI_ERROR CredUIPromptForWindowsCredentials(ref CREDUI_INFO uiInfo, uint dwAuthError, ref uint pulAuthPackage, IntPtr pvInAuthBuffer, uint ulInAuthBufferSize, out IntPtr ppvOutAuthBuffer, out uint pulOutAuthBufferSize, ref bool pfSave, CREDUIWIN dwFlags);
/// <summary>
/// The CredUnPackAuthenticationBuffer function converts an authentication buffer returned by a call to the CredUIPromptForWindowsCredentials function into a string user name and password.
/// </summary>
/// <param name="dwFlags">
/// Setting the value of this parameter to CRED_PACK_PROTECTED_CREDENTIALS specifies that the function attempts to decrypt the credentials in the authentication buffer. If the credential cannot be decrypted, the function returns FALSE, and a call to the GetLastError function will return the value ERROR_NOT_CAPABLE.
/// How the decryption is done depends on the format of the authentication buffer.
/// If the authentication buffer is a SEC_WINNT_AUTH_IDENTITY_EX2 structure, the function can decrypt the buffer if it is encrypted by using SspiEncryptAuthIdentityEx with the SEC_WINNT_AUTH_IDENTITY_ENCRYPT_SAME_LOGON option.
/// If the authentication buffer is one of the marshaled KERB_* _LOGON structures, the function decrypts the password before returning it in the pszPassword buffer.
/// </param>
/// <param name="pAuthBuffer">
/// A pointer to the authentication buffer to be converted.
/// This buffer is typically the output of the CredUIPromptForWindowsCredentials or CredPackAuthenticationBuffer function. This must be one of the following types:
/// A SEC_WINNT_AUTH_IDENTITY_EX2 structure for identity credentials. The function does not accept other SEC_WINNT_AUTH_IDENTITY structures.
/// A KERB_INTERACTIVE_LOGON or KERB_INTERACTIVE_UNLOCK_LOGON structure for password credentials.
/// A KERB_CERTIFICATE_LOGON or KERB_CERTIFICATE_UNLOCK_LOGON structure for smart card certificate credentials.
/// GENERIC_CRED for generic credentials.
/// </param>
/// <param name="cbAuthBuffer">The size, in bytes, of the pAuthBuffer buffer.</param>
/// <param name="pszUserName">A pointer to a null-terminated string that receives the user name.</param>
/// <param name="pcchMaxUserName">A pointer to a DWORD value that specifies the size, in characters, of the pszUserName buffer. On output, if the buffer is not of sufficient size, specifies the required size, in characters, of the pszUserName buffer. The size includes terminating null character.</param>
/// <param name="pszDomainName">A pointer to a null-terminated string that receives the name of the user's domain.</param>
/// <param name="pcchMaxDomainame">A pointer to a DWORD value that specifies the size, in characters, of the pszDomainName buffer. On output, if the buffer is not of sufficient size, specifies the required size, in characters, of the pszDomainName buffer. The size includes the terminating null character. The required size can be zero if there is no domain name.</param>
/// <param name="pszPassword">A pointer to a null-terminated string that receives the password.</param>
/// <param name="pcchMaxPassword">A pointer to a DWORD value that specifies the size, in characters, of the pszPassword buffer. On output, if the buffer is not of sufficient size, specifies the required size, in characters, of the pszPassword buffer. The size includes the terminating null character.</param>
/// <returns>TRUE if the function succeeds; otherwise, FALSE.</returns>
[DllImport("credui.dll", CharSet = CharSet.Auto, EntryPoint = "CredUnPackAuthenticationBuffer", SetLastError = true)]
internal static extern bool CredUnPackAuthenticationBuffer(uint dwFlags, IntPtr pAuthBuffer, uint cbAuthBuffer, StringBuilder pszUserName, ref uint pcchMaxUserName, StringBuilder pszDomainName, ref uint pcchMaxDomainame, StringBuilder pszPassword, ref uint pcchMaxPassword);
/// <summary>
/// The CredPackAuthenticationBuffer function converts a string user name and password into an authentication buffer.
/// Beginning with Windows 8 and Windows Server 2012, the CredPackAuthenticationBuffer function converts an identity credential into an authentication buffer, which is a SEC_WINNT_AUTH_IDENTITY_EX2 structure.This buffer can be passed to LsaLogonUser, AcquireCredentialsHandle, or other identity provider interfaces.
/// </summary>
/// <param name="dwFlags">Specifies how the credential should be packed.</param>
/// <param name="pszUserName">A pointer to a null-terminated string that specifies the user name to be converted.</param>
/// <param name="pszPassword">A pointer to a null-terminated string that specifies the password to be converted.</param>
/// <param name="pPackedCredentials">A pointer to an array of bytes that, on output, receives the packed authentication buffer. This parameter can be NULL to receive the required buffer size in the pcbPackedCredentials parameter.</param>
/// <param name="pcbPackedCredentials">A pointer to a DWORD value that specifies the size, in bytes, of the pPackedCredentials buffer. On output, if the buffer is not of sufficient size, specifies the required size, in bytes, of the pPackedCredentials buffer.</param>
/// <returns>TRUE if the function succeeds; otherwise, FALSE.</returns>
[DllImport("credui.dll", CharSet = CharSet.Unicode, EntryPoint = "CredPackAuthenticationBuffer", SetLastError = true)]
internal static extern bool CredPackAuthenticationBuffer(uint dwFlags, string pszUserName, string pszPassword, IntPtr pPackedCredentials, ref uint pcbPackedCredentials);
}
}

116
Cli/OperationArguments.cs Normal file
Просмотреть файл

@ -0,0 +1,116 @@
using System;
using System.IO;
using System.Text;
namespace Microsoft.TeamFoundation.Git.Helpers.Authentication
{
internal sealed class OperationArguments
{
internal OperationArguments(TextReader stdin)
{
this.Scheme = CredentialType.Basic;
string line;
while (!String.IsNullOrWhiteSpace((line = Console.In.ReadLine())))
{
string[] pair = line.Split(new[] { '=' }, 2);
if (pair.Length == 2)
{
switch (pair[0])
{
case "protocol":
this.Protocol = pair[1];
break;
case "host":
this.Host = pair[1];
break;
case "path":
this.Path = pair[1];
break;
}
}
}
if (this.Protocol != null && this.Host != null)
{
this.TargetUri = new Uri(String.Format("{0}://{1}", this.Protocol, this.Host), UriKind.Absolute);
}
}
public readonly string Protocol;
public readonly string Host;
public readonly string Path;
public readonly Uri TargetUri;
public string Username { get; private set; }
public string Password { get; private set; }
public CredentialType Scheme { get; set; }
public void SetCredentials(Credentials credentials)
{
this.Username = credentials.Username;
this.Password = credentials.Password;
}
public void SetScheme(string value)
{
if (String.Equals(value, "MSA", StringComparison.OrdinalIgnoreCase) ||
String.Equals(value, "Microsoft", StringComparison.OrdinalIgnoreCase) ||
String.Equals(value, "MicrosoftAccount", StringComparison.OrdinalIgnoreCase) ||
String.Equals(value, "Live", StringComparison.OrdinalIgnoreCase) ||
String.Equals(value, "LiveConnect", StringComparison.OrdinalIgnoreCase) ||
String.Equals(value, "LiveID", StringComparison.OrdinalIgnoreCase))
{
this.Scheme = CredentialType.MicrosoftAccount;
}
else if (String.Equals(value, "AAD", StringComparison.OrdinalIgnoreCase) ||
String.Equals(value, "Azure", StringComparison.OrdinalIgnoreCase) ||
String.Equals(value, "AzureDirectory", StringComparison.OrdinalIgnoreCase))
{
this.Scheme = CredentialType.AzureDirectory;
}
else
{
this.Scheme = CredentialType.Basic;
}
}
public override string ToString()
{
StringBuilder builder = new StringBuilder();
if (this.Protocol != null)
{
builder.Append("protocol=")
.Append(this.Protocol)
.Append("\n");
}
if (this.Host != null)
{
builder.Append("host=")
.Append(this.Host)
.Append("\n");
}
if (this.Path != null)
{
builder.Append("path=")
.Append(this.Path)
.Append("\n");
}
if (this.Username != null)
{
builder.Append("username=")
.Append(this.Username)
.Append("\n");
}
if (this.Password != null)
{
builder.Append("password=")
.Append(this.Password)
.Append("\n");
}
return builder.ToString();
}
}
}

153
Cli/Program.cs Normal file
Просмотреть файл

@ -0,0 +1,153 @@
using LibGit2Sharp;
using System;
using System.Diagnostics;
using System.Linq;
using GitConfigValue = LibGit2Sharp.ConfigurationEntry<string>;
namespace Microsoft.TeamFoundation.Git.Helpers.Authentication
{
class Program
{
private static readonly Guid TenantId = new Guid("72f988bf-86f1-41af-91ab-2d7cd011db47");
private static readonly string Resource = "499b84ac-1321-427f-aa17-267ca6975798";
private static readonly Guid ClientId = new Guid("872cd9fa-d31f-45e0-9eab-6e460a02d1f1");
static void Main(string[] args)
{
//Uri targetUri = new Uri("https://dev-x.visualstudio.com");
//IMicrosoftAccountAuthentication msa = new MicrosoftAccountAuthentication("https://dev-x.visualstudio.com", ClientId);
//Task.Run(async () =>
//{
// Credentials credentials = new Credentials("jeremy.wyman@outlook.com", "0thLight/");
// await msa.InteractiveLogon(targetUri, credentials);
//}).Wait();
OperationArguments operationArguments = new OperationArguments(Console.In);
Repository repo = new Repository(Environment.CurrentDirectory);
GitConfigValue schema = GetConfig(repo, operationArguments, "schema");
GitConfigValue clientId = GetConfig(repo, operationArguments, "clientid");
GitConfigValue resource = GetConfig(repo, operationArguments, "resource");
GitConfigValue tenantId = GetConfig(repo, operationArguments, "tenantid");
if (schema != null)
{
operationArguments.SetScheme(schema.Value);
}
if (clientId != null)
{
}
if (resource != null)
{
}
if (tenantId != null)
{
}
foreach (string arg in args)
{
switch (arg)
{
case "erase":
Erase(operationArguments);
break;
case "get":
Get(operationArguments);
break;
case "store":
Store(operationArguments);
break;
}
}
}
private static void Erase(OperationArguments operationArguments)
{
Debug.Assert(operationArguments != null, "The operationArguments is null");
Debug.Assert(operationArguments.TargetUri != null, "The operationArgument.TargetUri is null");
BaseAuthentication authentication = CreateAuthentication(operationArguments);
authentication.DeleteCredentials(operationArguments.TargetUri);
}
private static void Get(OperationArguments operationArguments)
{
Debug.Assert(operationArguments != null, "The operationArguments is null");
Debug.Assert(operationArguments.TargetUri != null, "The operationArgument.TargetUri is null");
BaseAuthentication authentication = CreateAuthentication(operationArguments);
Credentials credentials = null;
if (authentication != null && authentication.GetCredentials(operationArguments.TargetUri, out credentials))
{
operationArguments.SetCredentials(credentials);
}
Console.Out.Write(operationArguments);
}
private static void Store(OperationArguments operationArguments)
{
Debug.Assert(operationArguments != null, "The operationArguments is null");
Debug.Assert(operationArguments.Username != null, "The operaionArgument.Username is null");
Debug.Assert(operationArguments.Password != null, "The operaionArgument.Password is null");
Debug.Assert(operationArguments.TargetUri != null, "The operationArgument.TargetUri is null");
BaseAuthentication authentication = CreateAuthentication(operationArguments);
Credentials credentials = new Credentials(operationArguments.Username, operationArguments.Password);
authentication.SetCredentials(operationArguments.TargetUri, credentials);
}
private static BaseAuthentication CreateAuthentication(OperationArguments operationArguments)
{
Debug.Assert(operationArguments != null, "The operationArguments is null");
switch (operationArguments.Scheme)
{
case CredentialType.AzureDirectory:
return new VsoAadAuthentication();
case CredentialType.Basic:
default:
return new BasicAuthentication();
case CredentialType.MicrosoftAccount:
return new VsoMsaAuthentation();
}
}
private static GitConfigValue GetConfig(Repository repo, OperationArguments operationArguments, string key)
{
Debug.Assert(repo != null, "The repo parameter is null");
Debug.Assert(repo.Config != null, "The repo.Config parameter is null");
Debug.Assert(operationArguments != null, "The operationArguments parameter is null");
Debug.Assert(operationArguments.Protocol != null, "The operationArguments.Protocol parameter is null");
Debug.Assert(operationArguments.Host != null, "The operationArguments.Host parameter is null");
Debug.Assert(key != null, "The key parameter is null");
// return match seeksing from most specific (credenial.<schema>://<uri>.<key>) to least specific (credential.<key>)
return GetConfig(repo, String.Format("credential.{0}://{1}", operationArguments.Protocol, operationArguments.Host), key)
?? GetConfig(repo, String.Format("credential.{0}", operationArguments.Host), key)
?? GetConfig(repo, "credential", key);
}
private static GitConfigValue GetConfig(Repository repo, string prefix, string suffix)
{
Debug.Assert(repo != null, "The repo parameter is null");
Debug.Assert(repo.Config != null, "The repo.Config parameter is null");
Debug.Assert(prefix != null, "The prefix parameter is null");
Debug.Assert(suffix != null, "The suffic parameter is null");
return repo.Config.Where((GitConfigValue entry) =>
{
return entry.Key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)
&& entry.Key.EndsWith(suffix, StringComparison.OrdinalIgnoreCase);
})
.OrderByDescending((GitConfigValue entry) => { return entry.Level; })
.FirstOrDefault();
}
}
}

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

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("GitCredentialHelper")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("GitCredentialHelper")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("62f52119-63d4-40a8-a9df-f1c4b473308a")]
// 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("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

4
Cli/packages.config Normal file
Просмотреть файл

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="LibGit2Sharp" version="0.21.0.176" targetFramework="net451" userInstalled="true" />
</packages>

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

@ -0,0 +1,15 @@
using System;
namespace Microsoft.TeamFoundation.Git.Helpers.Authentication
{
public abstract class BaseAuthentication : IAuthentication
{
protected const string PrimaryCredentialPrefix = "git";
public abstract void DeleteCredentials(Uri targetUri);
public abstract bool GetCredentials(Uri targetUri, out Credentials credentials);
public abstract bool SetCredentials(Uri targetUri, Credentials credentials);
}
}

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

@ -0,0 +1,98 @@
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;
namespace Microsoft.TeamFoundation.Git.Helpers.Authentication
{
public abstract class BaseCredentialStore
{
protected void Delete(string targetName)
{
if (!NativeMethods.CredDelete(targetName, NativeMethods.CRED_TYPE.GENERIC, 0))
{
int errorCode = Marshal.GetLastWin32Error();
switch (errorCode)
{
case NativeMethods.CREDENTIAL_ERROR_NOT_FOUND:
throw new Exception("Credentials not found");
default:
throw new Exception("Failed to delete credentials", new Win32Exception(errorCode));
}
}
}
protected abstract string GetTargetName(Uri targetUri);
protected Credentials Read(string targetName)
{
Credentials credentials = null;
IntPtr credPtr = IntPtr.Zero;
try
{
if (NativeMethods.CredRead(targetName, NativeMethods.CRED_TYPE.GENERIC, 0, out credPtr))
{
NativeMethods.CREDENTIAL credStruct = (NativeMethods.CREDENTIAL)Marshal.PtrToStructure(credPtr, typeof(NativeMethods.CREDENTIAL));
int passwordLength = (int)credStruct.CredentialBlobSize;
string password = passwordLength > 0
? Marshal.PtrToStringUni(credStruct.CredentialBlob, passwordLength / sizeof(char))
: String.Empty;
string username = credStruct.UserName;
credentials = new Credentials(username, password);
}
}
finally
{
if (credPtr != IntPtr.Zero)
{
NativeMethods.CredFree(credPtr);
}
}
return credentials;
}
protected void Write(string targetName, Credentials credentials)
{
NativeMethods.CREDENTIAL credential = new NativeMethods.CREDENTIAL()
{
Type = NativeMethods.CRED_TYPE.GENERIC,
TargetName = targetName,
CredentialBlob = Marshal.StringToCoTaskMemUni(credentials.Password),
CredentialBlobSize = (uint)Encoding.Unicode.GetByteCount(credentials.Password),
Persist = NativeMethods.CRED_PERSIST.LOCAL_MACHINE,
AttributeCount = 0,
UserName = credentials.Username,
};
if (!NativeMethods.CredWrite(ref credential, 0))
{
int errorCode = Marshal.GetLastWin32Error();
throw new Exception("Failed to write credentials", new Win32Exception(errorCode));
}
Marshal.FreeCoTaskMem(credential.CredentialBlob);
}
internal static void ValidateCredentials(Credentials credentials)
{
if (credentials == null)
throw new ArgumentNullException("credentials");
if (credentials.Password == null)
throw new ArgumentException("The Password field of the Credentials object cannot be null", "credentials");
if (String.IsNullOrEmpty(credentials.Username))
throw new ArgumentException("The Username field of the Credentials object cannot be null or empty", "credentials");
if (credentials.Username.Length > NativeMethods.CREDENTIAL_USERNAME_MAXLEN)
throw new ArgumentOutOfRangeException("credentials", "The Username field of the Credentials object cannot be longer than 513 characters");
}
internal static void ValidateTargetUri(Uri targetUri)
{
if (targetUri == null)
throw new ArgumentNullException("targetUri");
if (!targetUri.IsAbsoluteUri)
throw new ArgumentException("The target URI must be an absolute URI", "targetUri");
}
}
}

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

@ -0,0 +1,127 @@
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Debug = System.Diagnostics.Debug;
namespace Microsoft.TeamFoundation.Git.Helpers.Authentication
{
public abstract class BaseVsoAuthentication: BaseAuthentication, IVsoAuthentication
{
public const string AuthorityHostUrlFormat = "https://login.windows.net/common";
public static readonly string DefaultResource = "499b84ac-1321-427f-aa17-267ca6975798";
public static readonly Guid DefaultClientId = new Guid("872cd9fa-d31f-45e0-9eab-6e460a02d1f1");
protected const string SecondaryCredentialPrefix = "alt-git";
protected const string TokenPrefix = "adal-refresh";
protected BaseVsoAuthentication()
{
this.AuthorityHostUrl = AuthorityHostUrlFormat;
this.ClientId = DefaultClientId;
this.Resource = DefaultResource;
this.PersonalAccessTokenStore = new CredentialStore(PrimaryCredentialPrefix);
this.UserCredentialStore = new CredentialStore(SecondaryCredentialPrefix);
this.AdaRefreshTokenStore = new TokenStore(TokenPrefix);
}
protected BaseVsoAuthentication(Guid tenantId, string resource, Guid clientId)
: this()
{
const string AuthorityHostUrlFormat = "https://login.windows.net/{0:D}";
this.AuthorityHostUrl = String.Format(CultureInfo.InvariantCulture, AuthorityHostUrlFormat, tenantId);
this.ClientId = clientId;
this.Resource = resource;
}
protected BaseVsoAuthentication(string resource, Guid clientId)
: this()
{
this.ClientId = clientId;
this.Resource = resource;
}
internal BaseVsoAuthentication(ICredentialStore personalAccessToken, ICredentialStore userCredential, ITokenStore adaRefresh)
:this()
{
this.PersonalAccessTokenStore = personalAccessToken;
this.UserCredentialStore = userCredential;
this.AdaRefreshTokenStore = adaRefresh;
}
public readonly string AuthorityHostUrl;
public readonly Guid ClientId;
public readonly string Resource;
protected ICredentialStore PersonalAccessTokenStore { get; set; }
protected ICredentialStore UserCredentialStore { get; set; }
protected ITokenStore AdaRefreshTokenStore { get; set; }
public override void DeleteCredentials(Uri targetUri)
{
BaseCredentialStore.ValidateTargetUri(targetUri);
this.PersonalAccessTokenStore.DeleteCredentials(targetUri);
this.UserCredentialStore.DeleteCredentials(targetUri);
this.AdaRefreshTokenStore.DeleteToken(targetUri);
}
public override bool GetCredentials(Uri targetUri, out Credentials credentials)
{
BaseCredentialStore.ValidateTargetUri(targetUri);
return this.PersonalAccessTokenStore.ReadCredentials(targetUri, out credentials);
}
public abstract Task<bool> InteractiveLogon(Uri targetUri, Credentials credentials);
protected async Task<bool> GeneratePersonalAccessToken(Uri targetUri, AuthenticationResult authResult)
{
const string VsspEndPointUrl = "https://app.vssps.visualstudio.com/_apis/token/sessiontokens?api-version=1.0&tokentype=compact";
Debug.Assert(targetUri != null, "The targetUri parameter is null");
Debug.Assert(authResult != null, "The authResult parameter is null");
try
{
using (HttpClient httpClient = new HttpClient())
{
StringContent content = new StringContent(String.Empty, Encoding.UTF8, "application/json");
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(authResult.AccessTokenType, authResult.AccessToken);
HttpResponseMessage response = await httpClient.PostAsync(VsspEndPointUrl, content);
if (response.StatusCode == HttpStatusCode.OK)
{
string responseText = await response.Content.ReadAsStringAsync();
Dictionary<string, string> values = JsonConvert.DeserializeObject<Dictionary<string, string>>(responseText);
if (values.ContainsKey("token"))
{
string token = values["token"];
Credentials personalAccessToken = new Credentials(token);
this.PersonalAccessTokenStore.WriteCredentials(targetUri, personalAccessToken);
return true;
}
}
}
}
catch (Exception exception)
{
Debug.WriteLine(exception);
}
return false;
}
protected void StoreRefreshToken(Uri targetUri, AuthenticationResult authResult)
{
Debug.Assert(targetUri != null, "The targetUri parameter is null");
Debug.Assert(authResult != null, "The authResult parameter is null");
Token refreshToken = new Token(authResult.RefreshToken);
this.AdaRefreshTokenStore.WriteToken(targetUri, refreshToken);
}
}
}

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

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.TeamFoundation.Git.Helpers.Authentication
{
public sealed class BasicAuthentication : BaseAuthentication, IAuthentication
{
public BasicAuthentication()
{
this.CredentialStore = new CredentialStore(PrimaryCredentialPrefix);
}
internal BasicAuthentication(ICredentialStore testStore)
:this()
{
this.CredentialStore = testStore;
}
private ICredentialStore CredentialStore;
public override void DeleteCredentials(Uri targetUri)
{
this.CredentialStore.DeleteCredentials(targetUri);
}
public override bool GetCredentials(Uri targetUri, out Credentials credentials)
{
return this.CredentialStore.ReadCredentials(targetUri, out credentials);
}
public override bool SetCredentials(Uri targetUri, Credentials credentials)
{
this.CredentialStore.WriteCredentials(targetUri, credentials);
return true;
}
}
}

84
Core/Core.csproj Normal file
Просмотреть файл

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{C1DAABC1-B493-459D-BB4F-04FBEFB1BA13}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Microsoft.TeamFoundation.Git.Helpers.Authentication</RootNamespace>
<AssemblyName>Core</AssemblyName>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>3487aeea</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.IdentityModel.Clients.ActiveDirectory, Version=2.14.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.2.14.201151115\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms, Version=2.14.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.2.14.201151115\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup>
<Compile Include="VsoAadAuthentication.cs" />
<Compile Include="BaseVsoAuthentication.cs" />
<Compile Include="BaseAuthentication.cs" />
<Compile Include="BaseCredentialStore.cs" />
<Compile Include="BasicAuthentication.cs" />
<Compile Include="Credentials.cs" />
<Compile Include="IAuthentication.cs" />
<Compile Include="IVsoAadAuthentication.cs" />
<Compile Include="ICredentialStore.cs" />
<Compile Include="IVsoAuthentication.cs" />
<Compile Include="ITokenStore.cs" />
<Compile Include="VsoMsaAuthentation.cs" />
<Compile Include="CredentialStore.cs" />
<Compile Include="TokenStore.cs" />
<Compile Include="Token.cs" />
<Compile Include="NativeMethods.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

76
Core/CredentialStore.cs Normal file
Просмотреть файл

@ -0,0 +1,76 @@
using System;
using System.Diagnostics;
namespace Microsoft.TeamFoundation.Git.Helpers.Authentication
{
public class CredentialStore : BaseCredentialStore, ICredentialStore
{
internal CredentialStore(string prefix) {
Debug.Assert(!String.IsNullOrWhiteSpace(prefix), "The prefix parameter value is invalid");
_prefix = prefix;
}
private readonly string _prefix;
/// <summary>
/// Deleted credentials for target URI from the credential store
/// </summary>
/// <param name="targetUri">The URI of the target for which credentials are being deleted</param>
public void DeleteCredentials(Uri targetUri)
{
BaseCredentialStore.ValidateTargetUri(targetUri);
string targetName = this.GetTargetName(targetUri);
this.Delete(targetName);
}
/// <summary>
/// Reads credentials for a target URI from the credential store
/// </summary>
/// <param name="targetUri">The URI of the target for which credentials are being read</param>
/// <param name="credentials">The credentials from the store; null if failure</param>
/// <returns>True if success; false if failure</returns>
public bool ReadCredentials(Uri targetUri, out Credentials credentials)
{
BaseCredentialStore.ValidateTargetUri(targetUri);
string targetName = this.GetTargetName(targetUri);
credentials = this.Read(targetName);
return credentials != null;
}
/// <summary>
/// Writes credentials for a target URI to the credential store
/// </summary>
/// <param name="targetUri">The URI of the target for which credentials are being stored</param>
/// <param name="credentials">The credentials to be stored</param>
public void WriteCredentials(Uri targetUri, Credentials credentials)
{
BaseCredentialStore.ValidateTargetUri(targetUri);
BaseCredentialStore.ValidateCredentials(credentials);
string targetName = this.GetTargetName(targetUri);
this.Write(targetName, credentials);
}
/// <summary>
/// Formats a TargetName string based on the TargetUri base on the format started by git-credential-winstore
/// </summary>
/// <param name="targetUri">Uri of the target</param>
/// <returns>Properly formatted TargetName string</returns>
protected override string GetTargetName(Uri targetUri)
{
// use the format started by git-credential-winstore for maximum compatibility
// see https://gitcredentialstore.codeplex.com/
const string PrimaryNameFormat = "{0}:{1}://{2}";
System.Diagnostics.Debug.Assert(targetUri != null, "The targetUri parameter is null");
// trim any trailing slashes and/or whitespace for compat with git-credential-winstore
string trimmedHostUrl = targetUri.Host
.TrimEnd('/', '\\')
.TrimEnd();
string targetName = String.Format(PrimaryNameFormat, _prefix, targetUri.Scheme, trimmedHostUrl);
return targetName;
}
}
}

28
Core/Credentials.cs Normal file
Просмотреть файл

@ -0,0 +1,28 @@
using System;
namespace Microsoft.TeamFoundation.Git.Helpers.Authentication
{
public sealed class Credentials
{
public Credentials()
{
this.Password = String.Empty;
this.Username = String.Empty;
}
public Credentials(string username)
: this()
{
this.Username = username;
}
public Credentials(string username, string password)
: this()
{
this.Username = username;
this.Password = password;
}
public readonly string Password;
public readonly string Username;
}
}

11
Core/IAuthentication.cs Normal file
Просмотреть файл

@ -0,0 +1,11 @@
using System;
namespace Microsoft.TeamFoundation.Git.Helpers.Authentication
{
public interface IAuthentication
{
void DeleteCredentials(Uri targetUri);
bool GetCredentials(Uri targetUri, out Credentials credentials);
bool SetCredentials(Uri targetUri, Credentials credentials);
}
}

11
Core/ICredentialStore.cs Normal file
Просмотреть файл

@ -0,0 +1,11 @@
using System;
namespace Microsoft.TeamFoundation.Git.Helpers.Authentication
{
public interface ICredentialStore
{
void DeleteCredentials(Uri targetUri);
bool ReadCredentials(Uri targetUri, out Credentials credentials);
void WriteCredentials(Uri targetUri, Credentials credentials);
}
}

11
Core/ITokenStore.cs Normal file
Просмотреть файл

@ -0,0 +1,11 @@
using System;
namespace Microsoft.TeamFoundation.Git.Helpers.Authentication
{
public interface ITokenStore
{
void DeleteToken(Uri targetUri);
bool ReadToken(Uri targetUri, out Token token);
void WriteToken(Uri targetUri, Token token);
}
}

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

@ -0,0 +1,11 @@
using System;
using System.Threading.Tasks;
namespace Microsoft.TeamFoundation.Git.Helpers.Authentication
{
public interface IVsoAadAuthentication : IVsoAuthentication
{
Task<bool> NoninteractiveLogon(Uri targetUri);
Task<bool> RefreshCredentials(Uri targetUri);
}
}

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

@ -0,0 +1,10 @@
using System;
using System.Threading.Tasks;
namespace Microsoft.TeamFoundation.Git.Helpers.Authentication
{
public interface IVsoAuthentication:IAuthentication
{
Task<bool> InteractiveLogon(Uri targetUri, Credentials credentials);
}
}

252
Core/NativeMethods.cs Normal file
Просмотреть файл

@ -0,0 +1,252 @@
using System;
using System.Runtime.InteropServices;
namespace Microsoft.TeamFoundation.Git.Helpers.Authentication
{
internal static class NativeMethods
{
internal const int CREDENTIAL_ERROR_NOT_FOUND = 1168;
internal const int CREDENTIAL_USERNAME_MAXLEN = 513;
[Flags]
internal enum CRED_FLAGS : uint
{
/// <summary>
/// Bit set if the credential does not persist the CredentialBlob and the credential has not been written during this logon session.
/// This bit is ignored on input and is set automatically when queried.
/// </summary>
/// <remarks>
/// If Type is CRED_TYPE_DOMAIN_CERTIFICATE, the CredentialBlob is not persisted across logon sessions because the PIN of a certificate is very sensitive information.
/// Indeed, when the credential is written to credential manager, the PIN is passed to the CSP associated with the certificate.
/// The CSP will enforce a PIN retention policy appropriate to the certificate.
///
/// If Type is CRED_TYPE_DOMAIN_PASSWORD or CRED_TYPE_DOMAIN_CERTIFICATE, an authentication package always fails an authentication attempt when using credentials marked as CRED_FLAGS_PROMPT_NOW.
/// The application (typically through the key ring UI) prompts the user for the password. The application saves the credential and retries the authentication.
/// Because the credential has been recently written, the authentication package now gets a credential that is not marked as CRED_FLAGS_PROMPT_NOW.
/// </remarks>
PROMPT_NOW = 0x02,
/// <summary>
/// Bit is set if this credential has a TargetName member set to the same value as the UserName member.
/// Such a credential is one designed to store the CredentialBlob for a specific user.
/// </summary>
/// <remarks>
/// This bit can only be specified if Type is CRED_TYPE_DOMAIN_PASSWORD or CRED_TYPE_DOMAIN_CERTIFICATE.
/// </remarks>
/// <seealso cref="https://msdn.microsoft.com/en-us/library/windows/desktop/aa374801(v=vs.85).aspx"/>
USERNAME_TARGET = 0x04,
}
internal enum CRED_PERSIST : uint
{
/// <summary>
/// The credential persists for the life of the logon session.
/// It will not be visible to other logon sessions of this same user.
/// It will not exist after this user logs off and back on.
/// </summary>
SESSION = 0x01,
/// <summary>
/// The credential persists for all subsequent logon sessions on this same computer.
/// It is visible to other logon sessions of this same user on this same computer and not visible to logon sessions for this user on other computers.
/// </summary>
/// <remarks>
/// Windows Vista Home Basic, Windows Vista Home Premium, Windows Vista Starter, and Windows XP Home Edition: This value is not supported.
/// </remarks>
LOCAL_MACHINE = 0x02,
/// <summary>
/// The credential persists for all subsequent logon sessions on this same computer.
/// It is visible to other logon sessions of this same user on this same computer and to logon sessions for this user on other computers.
/// </summary>
/// <remarks>
/// Windows Vista Home Basic, Windows Vista Home Premium, Windows Vista Starter, and Windows XP Home Edition: This value is not supported.
/// </remarks>
ENTERPRISE = 0x03
}
internal enum CRED_TYPE : uint
{
/// <summary>
/// The credential is a generic credential. The credential will not be used by any particular authentication package.
/// The credential will be stored securely but has no other significant characteristics.
/// </summary>
GENERIC = 0x01,
/// <summary>
/// The credential is a password credential and is specific to Microsoft's authentication packages.
/// The NTLM, Kerberos, and Negotiate authentication packages will automatically use this credential when connecting to the named target.
/// </summary>
DOMAIN_PASSWORD = 0x02,
/// <summary>
/// The credential is a certificate credential and is specific to Microsoft's authentication packages.
/// The Kerberos, Negotiate, and Schannel authentication packages automatically use this credential when connecting to the named target.
/// </summary>
DOMAIN_CERTIFICATE = 0x03,
/// <summary>
/// The credential is a password credential and is specific to authentication packages from Microsoft.
/// The Passport authentication package will automatically use this credential when connecting to the named target.
/// </summary>
[Obsolete("This value is no longer supported", true)]
DOMAIN_VISIBLE_PASSWORD = 0x04,
/// <summary>
/// The credential is a certificate credential that is a generic authentication package.
/// </summary>
CRED_TYPE_GENERIC_CERTIFICATE = 0x05,
/// <summary>
/// The credential is supported by extended Negotiate packages.
/// </summary>
/// <remarks>
/// Windows Server 2008, Windows Vista, Windows Server 2003, and Windows XP: This value is not supported.
/// </remarks>
CRED_TYPE_DOMAIN_EXTENDED = 0x06,
/// <summary>
/// The maximum number of supported credential types.
/// </summary>
/// <remarks>
/// Windows Server 2008, Windows Vista, Windows Server 2003, and Windows XP: This value is not supported.
/// </remarks>
CRED_TYPE_MAXIMUM = 0x07,
/// <summary>
/// The extended maximum number of supported credential types that now allow new applications to run on older operating systems.
/// </summary>
/// <remarks>
/// Windows Server 2008, Windows Vista, Windows Server 2003, and Windows XP: This value is not supported.
/// </remarks>
CRED_TYPE_MAXIMUM_EX = CRED_TYPE_MAXIMUM + 1000
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct CREDENTIAL
{
/// <summary>
/// A bit member that identifies characteristics of the credential.
/// Undefined bits should be initialized as zero and not otherwise altered to permit future enhancement.
/// </summary>
public CRED_FLAGS Flags;
/// <summary>
/// The type of the credential. This member cannot be changed after the credential is created.
/// </summary>
public CRED_TYPE Type;
[MarshalAs(UnmanagedType.LPWStr)]
public string TargetName;
/// <summary>
/// A string comment from the user that describes this credential.
/// This member cannot be longer than CRED_MAX_STRING_LENGTH (256) characters.
/// </summary>
[MarshalAs(UnmanagedType.LPWStr)]
public string Comment;
/// <summary>
/// The time, in Coordinated Universal Time (Greenwich Mean Time), of the last modification of the credential.
/// For write operations, the value of this member is ignored.
/// </summary>
public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
/// <summary>
/// The size, in bytes, of the CredentialBlob member.
/// This member cannot be larger than CRED_MAX_CREDENTIAL_BLOB_SIZE (512) bytes.
/// </summary>
public uint CredentialBlobSize;
/// <summary>
/// Secret data for the credential. The CredentialBlob member can be both read and written.
/// </summary>
/// <remarks>
/// If the Type member is CRED_TYPE_DOMAIN_PASSWORD, this member contains the plaintext Unicode password for UserName.
/// The CredentialBlob and CredentialBlobSize members do not include a trailing zero character.
/// Also, for CRED_TYPE_DOMAIN_PASSWORD, this member can only be read by the authentication packages.
///
/// If the Type member is CRED_TYPE_DOMAIN_CERTIFICATE, this member contains the clear test Unicode PIN for UserName.
/// The CredentialBlob and CredentialBlobSize members do not include a trailing zero character.
/// Also, this member can only be read by the authentication packages.
///
/// If the Type member is CRED_TYPE_GENERIC, this member is defined by the application.
///
/// Credentials are expected to be portable. Applications should ensure that the data in CredentialBlob is portable.
/// </remarks>
public IntPtr CredentialBlob;
/// <summary>
/// Defines the persistence of this credential. This member can be read and written.
/// </summary>
public CRED_PERSIST Persist;
/// <summary>
/// The number of application-defined attributes to be associated with the credential.
/// This member can be read and written. Its value cannot be greater than CRED_MAX_ATTRIBUTES (64).
/// </summary>
public uint AttributeCount;
/// <summary>
/// Application-defined attributes that are associated with the credential. This member can be read and written.
/// </summary>
public IntPtr Attributes;
/// <summary>
/// Alias for the TargetName member. This member can be read and written. It cannot be longer than CRED_MAX_STRING_LENGTH (256) characters.
/// If the credential Type is CRED_TYPE_GENERIC, this member can be non-NULL, but the credential manager ignores the member.
/// </summary>
[MarshalAs(UnmanagedType.LPWStr)]
public string TargetAlias;
/// <summary>
/// The user name of the account used to connect to TargetName.
/// </summary>
/// <remarks>
/// If the credential Type is CRED_TYPE_DOMAIN_PASSWORD, this member can be either a DomainName\UserName or a UPN.
/// If the credential Type is CRED_TYPE_DOMAIN_CERTIFICATE, this member must be a marshaled certificate reference created by calling CredMarshalCredential with a CertCredential.
/// If the credential Type is CRED_TYPE_GENERIC, this member can be non-NULL, but the credential manager ignores the member.
/// This member cannot be longer than CRED_MAX_USERNAME_LENGTH(513) characters.
/// </remarks>
[MarshalAs(UnmanagedType.LPWStr)]
public string UserName;
}
/// <summary>
/// The CredWrite function creates a new credential or modifies an existing credential in the user's credential set.
/// The new credential is associated with the logon session of the current token.
/// The token must not have the user's security identifier (SID) disabled.
/// </summary>
/// <param name="credential">A pointer to the CREDENTIAL structure to be written.</param>
/// <param name="flags">
/// Flags that control the function's operation.
/// Must be set to 0.</param>
/// <returns>
/// The function returns TRUE on success and FALSE on failure.
/// The GetLastError function can be called to get a more specific status code.
/// </returns>
[DllImport("Advapi32.dll", SetLastError = true, EntryPoint = "CredWriteW", CharSet = CharSet.Unicode)]
internal static extern bool CredWrite(ref CREDENTIAL credential, UInt32 flags);
/// <summary>
/// The CredRead function reads a credential from the user's credential set.
/// The credential set used is the one associated with the logon session of the current token.
/// The token must not have the user's SID disabled.
/// </summary>
/// <param name="targetName">Pointer to a null-terminated string that contains the name of the credential to read.</param>
/// <param name="type">Type of the credential to read. Type must be one of the CRED_TYPE_* defined types.</param>
/// <param name="flags">Currently reserved and must be zero.</param>
/// <param name="credential">
/// Pointer to a single allocated block buffer to return the credential.
/// Any pointers contained within the buffer are pointers to locations within this single allocated block.
/// The single returned buffer must be freed by calling CredFree.
/// </param>
/// <returns>
/// The function returns TRUE on success and FALSE on failure.
/// The GetLastError function can be called to get a more specific status code.
/// </returns>
[DllImport("advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool CredRead(string targetName, CRED_TYPE type, uint flags, out IntPtr credential);
/// <summary>
/// The CredDelete function deletes a credential from the user's credential set.
/// The credential set used is the one associated with the logon session of the current token.
/// The token must not have the user's SID disabled.
/// </summary>
/// <param name="targetName">Pointer to a null-terminated string that contains the name of the credential to delete.</param>
/// <param name="type">
/// Type of the credential to delete. Must be one of the CRED_TYPE_* defined types. For a list of the defined types, see the Type member of the CREDENTIAL structure.
/// If the value of this parameter is CRED_TYPE_DOMAIN_EXTENDED, this function can delete a credential that specifies a user name when there are multiple credentials for the same target.The value of the TargetName parameter must specify the user name as Target|UserName.
/// </param>
/// <param name="flags">Reserved and must be zero.</param>
/// <returns>
/// The function returns TRUE on success and FALSE on failure.
/// The GetLastError function can be called to get a more specific status code.
/// </returns>
[DllImport("advapi32.dll", EntryPoint = "CredDeleteW", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool CredDelete(string targetName, CRED_TYPE type, uint flags);
/// <summary>
/// The CredFree function frees a buffer returned by any of the credentials management functions.
/// </summary>
/// <param name="buffer"Pointer to the buffer to be freed.></param>
[DllImport("advapi32.dll", EntryPoint = "CredFree")]
internal static extern void CredFree(IntPtr buffer);
}
}

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

@ -0,0 +1,37 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Core")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Core")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("c1daabc1-b493-459d-bb4f-04fbefb1ba13")]
// 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("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly:InternalsVisibleTo("Microsoft.TeamFoundation.Git.Helpers.Authentication.CoreTest")]

12
Core/Token.cs Normal file
Просмотреть файл

@ -0,0 +1,12 @@
namespace Microsoft.TeamFoundation.Git.Helpers.Authentication
{
public sealed class Token
{
public Token(string value)
{
this.Value = value;
}
public readonly string Value;
}
}

87
Core/TokenStore.cs Normal file
Просмотреть файл

@ -0,0 +1,87 @@
using System;
using System.Diagnostics;
namespace Microsoft.TeamFoundation.Git.Helpers.Authentication
{
public class TokenStore : BaseCredentialStore, ITokenStore
{
internal TokenStore(string prefix)
{
Debug.Assert(!String.IsNullOrWhiteSpace(prefix), "The prefix parameter value is invalid");
_prefix = prefix;
}
private readonly string _prefix;
/// <summary>
/// Deleted credentials for target URI from the credential store
/// </summary>
/// <param name="targetUri">The URI of the target for which credentials are being deleted</param>
public void DeleteToken(Uri targetUri)
{
BaseCredentialStore.ValidateTargetUri(targetUri);
string targetName = this.GetTargetName(targetUri);
this.Delete(targetName);
}
/// <summary>
/// Reads credentials for a target URI from the credential store
/// </summary>
/// <param name="targetUri">The URI of the target for which credentials are being read</param>
/// <param name="token">The token from the store; null if failure</param>
/// <returns>True if success; false if failure</returns>
public bool ReadToken(Uri targetUri, out Token token)
{
BaseCredentialStore.ValidateTargetUri(targetUri);
token = null;
string targetName = this.GetTargetName(targetUri);
Credentials credentials = this.Read(targetName);
if (credentials != null)
{
token = new Token(credentials.Password);
}
return token != null;
}
/// <summary>
/// Writes credentials for a target URI to the credential store
/// </summary>
/// <param name="targetUri">The URI of the target for which credentials are being stored</param>
/// <param name="token">The token to be stored</param>
public void WriteToken(Uri targetUri, Token token)
{
BaseCredentialStore.ValidateTargetUri(targetUri);
if (token == null)
throw new ArgumentNullException("token", "The token parameter is null");
if (String.IsNullOrWhiteSpace(token.Value))
throw new ArgumentException("The token parameter is invlaid", "token");
string targetName = this.GetTargetName(targetUri);
Credentials credentials = new Credentials("Azure Directory Refresh Token", token.Value);
this.Write(targetName, credentials);
}
/// <summary>
/// Formats a TargetName string based on the TargetUri base on the format started by git-credential-winstore
/// </summary>
/// <param name="targetUri">Uri of the target</param>
/// <returns>Properly formatted TargetName string</returns>
protected override string GetTargetName(Uri targetUri)
{
const string TokenNameFormat = "{0}:{1}://{2}";
System.Diagnostics.Debug.Assert(targetUri != null, "The targetUri parameter is null");
// trim any trailing slashes and/or whitespace for compat with git-credential-winstore
string trimmedHostUrl = targetUri.Host
.TrimEnd('/', '\\')
.TrimEnd();
string targetName = String.Format(TokenNameFormat, _prefix, targetUri.Scheme, trimmedHostUrl);
return targetName;
}
}
}

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

@ -0,0 +1,123 @@
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Threading.Tasks;
using Debug = System.Diagnostics.Debug;
namespace Microsoft.TeamFoundation.Git.Helpers.Authentication
{
public class VsoAadAuthentication : BaseVsoAuthentication, IVsoAadAuthentication
{
public VsoAadAuthentication()
: base()
{ }
public VsoAadAuthentication(Guid tenantId, string resource, Guid clientId)
: base(tenantId, resource, clientId)
{ }
public VsoAadAuthentication(string resource, Guid clientId)
: base(resource, clientId)
{ }
internal VsoAadAuthentication(ICredentialStore personalAccessToken, ICredentialStore userCredential, ITokenStore adaRefresh)
: base(personalAccessToken, userCredential, adaRefresh)
{ }
public override async Task<bool> InteractiveLogon(Uri targetUri, Credentials credentials)
{
BaseCredentialStore.ValidateTargetUri(targetUri);
BaseCredentialStore.ValidateCredentials(credentials);
try
{
string clientId = this.ClientId.ToString("D");
string resource = this.Resource;
UserCredential userCredential = new UserCredential(credentials.Username, credentials.Password);
AuthenticationContext authCtx = new AuthenticationContext(this.AuthorityHostUrl, TokenCache.DefaultShared);
AuthenticationResult authResult = await authCtx.AcquireTokenAsync(resource, clientId, userCredential);
this.StoreRefreshToken(targetUri, authResult);
this.UserCredentialStore.WriteCredentials(targetUri, credentials);
return await this.GeneratePersonalAccessToken(targetUri, authResult);
}
catch (AdalException exception)
{
Debug.Write(exception);
}
return false;
}
public async Task<bool> NoninteractiveLogon(Uri targetUri)
{
BaseCredentialStore.ValidateTargetUri(targetUri);
try
{
string clientId = this.ClientId.ToString("D");
string resource = this.Resource;
UserCredential userCredential = new UserCredential();
AuthenticationContext authCtx = new AuthenticationContext(this.AuthorityHostUrl, TokenCache.DefaultShared);
AuthenticationResult authResult = await authCtx.AcquireTokenAsync(resource, clientId, userCredential);
this.StoreRefreshToken(targetUri, authResult);
return await this.GeneratePersonalAccessToken(targetUri, authResult);
}
catch (AdalServiceException exception)
{
Debug.WriteLine(exception);
}
return false;
}
public async Task<bool> RefreshCredentials(Uri targetUri)
{
BaseCredentialStore.ValidateTargetUri(targetUri);
try
{
string clientId = this.ClientId.ToString("D");
string resource = this.Resource;
Token refreshToken = null;
if (this.AdaRefreshTokenStore.ReadToken(targetUri, out refreshToken))
{
AuthenticationContext authCtx = new AuthenticationContext(this.AuthorityHostUrl, TokenCache.DefaultShared);
AuthenticationResult authResult = await authCtx.AcquireTokenByRefreshTokenAsync(refreshToken.Value, clientId, resource);
return await this.GeneratePersonalAccessToken(targetUri, authResult);
}
else
{
Credentials credentials = null;
if (this.UserCredentialStore.ReadCredentials(targetUri, out credentials))
{
return await this.InteractiveLogon(targetUri, credentials);
}
}
}
catch (AdalException exception)
{
Debug.WriteLine(exception);
}
return true;
}
public override bool SetCredentials(Uri targetUri, Credentials credentials)
{
BaseCredentialStore.ValidateTargetUri(targetUri);
BaseCredentialStore.ValidateCredentials(credentials);
var task = Task.Run<bool>(async () => { return await this.InteractiveLogon(targetUri, credentials); });
task.Wait();
if (task.IsFaulted)
throw task.Exception;
return task.Result;
}
}
}

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

@ -0,0 +1,67 @@
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Threading.Tasks;
using Debug = System.Diagnostics.Debug;
namespace Microsoft.TeamFoundation.Git.Helpers.Authentication
{
public class VsoMsaAuthentation : BaseVsoAuthentication, IVsoAuthentication
{
public VsoMsaAuthentation()
: base()
{ }
public VsoMsaAuthentation(string resource, Guid clientId)
: base(resource, clientId)
{ }
/// <summary>
/// Test constructor which allows for
/// </summary>
/// <param name="personalAccessToken"></param>
/// <param name="userCredential"></param>
/// <param name="adaRefresh"></param>
internal VsoMsaAuthentation(ICredentialStore personalAccessToken, ICredentialStore userCredential, ITokenStore adaRefresh)
: base(personalAccessToken, userCredential, adaRefresh)
{ }
public override async Task<bool> InteractiveLogon(Uri targetUri, Credentials credentials)
{
BaseCredentialStore.ValidateTargetUri(targetUri);
BaseCredentialStore.ValidateCredentials(credentials);
try
{
string clientId = this.ClientId.ToString("D");
string resource = this.Resource;
UserCredential userCredential = new UserCredential(credentials.Username, credentials.Password);
AuthenticationContext authCtx = new AuthenticationContext(this.AuthorityHostUrl, TokenCache.DefaultShared);
AuthenticationResult authResult = await authCtx.AcquireTokenAsync(resource, clientId, userCredential);
this.StoreRefreshToken(targetUri, authResult);
this.UserCredentialStore.WriteCredentials(targetUri, credentials);
return await this.GeneratePersonalAccessToken(targetUri, authResult);
}
catch (AdalException exception)
{
Debug.Write(exception);
}
return false;
}
public override bool SetCredentials(Uri targetUri, Credentials credentials)
{
BaseCredentialStore.ValidateTargetUri(targetUri);
BaseCredentialStore.ValidateCredentials(credentials);
var task = Task.Run<bool>(async () => { return await this.InteractiveLogon(targetUri, credentials); });
task.Wait();
if (task.IsFaulted)
throw task.Exception;
return task.Result;
}
}
}

11
Core/app.config Normal file
Просмотреть файл

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

5
Core/packages.config Normal file
Просмотреть файл

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.IdentityModel.Clients.ActiveDirectory" version="2.14.201151115" targetFramework="net451" userInstalled="true" />
<package id="Newtonsoft.Json" version="6.0.8" targetFramework="net451" userInstalled="true" />
</packages>

101
CoreTest/CoreTest.csproj Normal file
Просмотреть файл

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\LibGit2Sharp.0.22.0-pre20150223185624\build\net40\LibGit2Sharp.props" Condition="Exists('..\packages\LibGit2Sharp.0.22.0-pre20150223185624\build\net40\LibGit2Sharp.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{0AEB1CFC-371F-415C-93C5-44CEAA0E9A34}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Microsoft.TeamFoundation.Git.Helpers.Authentication</RootNamespace>
<AssemblyName>Microsoft.TeamFoundation.Git.Helpers.Authentication.CoreTest</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
<NuGetPackageImportStamp>04eed5d2</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
</ItemGroup>
<Choose>
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework" />
</ItemGroup>
</Otherwise>
</Choose>
<ItemGroup>
<Compile Include="CredentialTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Core\Core.csproj">
<Project>{c1daabc1-b493-459d-bb4f-04fbefb1ba13}</Project>
<Name>Core</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.CodedUITestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Extension, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITesting, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
</ItemGroup>
</When>
</Choose>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\LibGit2Sharp.0.22.0-pre20150223185624\build\net40\LibGit2Sharp.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\LibGit2Sharp.0.22.0-pre20150223185624\build\net40\LibGit2Sharp.props'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

124
CoreTest/CredentialTests.cs Normal file
Просмотреть файл

@ -0,0 +1,124 @@
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.TeamFoundation.Git.Helpers.Authentication.Test
{
[TestClass]
public class CredentialTests
{
[TestMethod]
public void CredentialStoreTests()
{
CredentialStoreTest("http://dummy.url/for/testing", "username", "password");
CredentialStoreTest(@"\\unc\share\test", "username", "password");
CredentialStoreTest("https://dummy.url/for/testing", "username", "password");
CredentialStoreTest("http://dummy.url/for/testing?with=params", "username", "password");
CredentialStoreTest("http://dummy.url/for/testing", "u", "password_that_is_kind_of_long");
CredentialStoreTest("http://dummy.url/for/testing", "username", "");
try
{
CredentialStoreTest("http://dummy.url/for/testing", null, "null_usernames_are_illegal");
Assert.Fail("Null username was accepted");
}
catch { }
try
{
CredentialStoreTest("http://dummy.url/for/testing", "", "blank_usernames_are_illegal");
Assert.Fail("Empty username was accepted");
}
catch { }
try
{
CredentialStoreTest("http://dummy.url/for/testing", "null_passwords_are_illegal", null);
Assert.Fail("Null password was accepted");
}
catch { }
}
[TestMethod]
public void TokenStoreTests()
{
const string tokenString = "The Azure AD Authentication Library (ADAL) for .NET enables client application developers to easily authenticate users to cloud or on-premises Active Directory (AD), and then obtain access tokens for securing API calls. ADAL for .NET has many features that make authentication easier for developers, such as asynchronous support, a configurable token cache that stores access tokens and refresh tokens, automatic token refresh when an access token expires and a refresh token is available, and more. By handling most of the complexity, ADAL can help a developer focus on business logic in their application and easily secure resources without being an expert on security.";
TokenStoreTest("http://dummy.url/for/testing", tokenString);
TokenStoreTest(@"\\unc\share\test", tokenString);
TokenStoreTest("https://dummy.url/for/testing", tokenString);
TokenStoreTest("http://dummy.url/for/testing?with=params", tokenString);
try
{
TokenStoreTest("http://dummy.url/for/testing", null);
Assert.Fail("Null token was accepted");
}
catch { }
try
{
TokenStoreTest("http://dummy.url/for/testing", "");
Assert.Fail("Empty token was accepted");
}
catch { }
}
private void CredentialStoreTest(string url, string username, string password)
{
try
{
Uri uri = new Uri(url, UriKind.Absolute);
Credentials writeCreds = new Credentials(username, password);
Credentials readCreds = null;
ICredentialStore priamryStore = new CredentialStore("prime-test");
priamryStore.WriteCredentials(uri, writeCreds);
if (priamryStore.ReadCredentials(uri, out readCreds))
{
Assert.AreEqual(writeCreds.Password, readCreds.Password, "Passwords did not match between written and read credentials");
Assert.AreEqual(writeCreds.Username, readCreds.Username, "Usernames did not match between written and read credentials");
}
else
{
Assert.Fail("Failed to read credentials");
}
priamryStore.DeleteCredentials(uri);
Assert.IsFalse(priamryStore.ReadCredentials(uri, out readCreds), "Deleted credentials were read back");
}
catch (Exception exception)
{
Assert.Fail(exception.Message);
}
}
private void TokenStoreTest(string url, string token)
{
try
{
Uri uri = new Uri(url, UriKind.Absolute);
ITokenStore tokenStore = new TokenStore("token-test");
Token writeToken = new Token(token);
Token readToken = null;
tokenStore.WriteToken(uri, writeToken);
if (tokenStore.ReadToken(uri, out readToken))
{
Assert.AreEqual(writeToken.Value, readToken.Value, "Tokens did not match between written and read");
}
else
{
Assert.Fail("Failed to read credentials");
}
tokenStore.DeleteToken(uri);
Assert.IsFalse(tokenStore.ReadToken(uri, out readToken), "Deleted token was read back");
}
catch (Exception exception)
{
Assert.Fail(exception.Message);
}
}
}
}

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

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("CoreTest")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CoreTest")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("0aeb1cfc-371f-415c-93c5-44ceaa0e9a34")]
// 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("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

11
CoreTest/app.config Normal file
Просмотреть файл

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

4
CoreTest/packages.config Normal file
Просмотреть файл

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="LibGit2Sharp" version="0.22.0-pre20150223185624" targetFramework="net452" userInstalled="true" />
</packages>

34
GitCredentialHelper.sln Normal file
Просмотреть файл

@ -0,0 +1,34 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.22710.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cli", "Cli\Cli.csproj", "{62F52119-63D4-40A8-A9DF-F1C4B473308A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "Core\Core.csproj", "{C1DAABC1-B493-459D-BB4F-04FBEFB1BA13}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoreTest", "CoreTest\CoreTest.csproj", "{0AEB1CFC-371F-415C-93C5-44CEAA0E9A34}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{62F52119-63D4-40A8-A9DF-F1C4B473308A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{62F52119-63D4-40A8-A9DF-F1C4B473308A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{62F52119-63D4-40A8-A9DF-F1C4B473308A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{62F52119-63D4-40A8-A9DF-F1C4B473308A}.Release|Any CPU.Build.0 = Release|Any CPU
{C1DAABC1-B493-459D-BB4F-04FBEFB1BA13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C1DAABC1-B493-459D-BB4F-04FBEFB1BA13}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C1DAABC1-B493-459D-BB4F-04FBEFB1BA13}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C1DAABC1-B493-459D-BB4F-04FBEFB1BA13}.Release|Any CPU.Build.0 = Release|Any CPU
{0AEB1CFC-371F-415C-93C5-44CEAA0E9A34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0AEB1CFC-371F-415C-93C5-44CEAA0E9A34}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0AEB1CFC-371F-415C-93C5-44CEAA0E9A34}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0AEB1CFC-371F-415C-93C5-44CEAA0E9A34}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal