I have the following code to retrieve all projects in TFS using the .net SDK for VSTS and using TFS2018. But I only get the top most project folders. How can I retrieve the subfolders of a certain project?
var uri = new Uri("https://devserver/tfs/DefaultCollection");
using (var projectHttpClient = new ProjectHttpClient(uri, cred)) {
var projects = projectHttpClient.GetProjects().Result;
}
I also tried changing the uri to
var uri = new Uri("https://devserver/tfs/DefaultCollection/MyProject");
But I get a Page not found error.
Here is a snapshot of the TFS structure. I would like to retrieve the projects on the sublevel. However I am only receiving the second level projects. The level with the user icons.
Try the code below to get folders:
using Microsoft.TeamFoundation.Client;
using System;
using Microsoft.TeamFoundation.VersionControl.Client;
namespace TestCaseProject
{
class Program
{
static void Main(string[] args)
{
TfsTeamProjectCollection tfs = new TfsTeamProjectCollection(new Uri("http://tfsserver:8080/tfs/DefaultCollection"));
var versioncontrols = tfs.GetService<VersionControlServer>();
var workspace = versioncontrols.CreateWorkspace("workspaceName","workspaceOwner");
String ServerFolder = #"$/TeamProject/Folder";
String LocalFolder = #"C:\Folder";
WorkingFolder workfolder = new WorkingFolder(ServerFolder, LocalFolder);
workspace.CreateMapping(workfolder);
workspace.Get();
}
}
}
I have a ASP.NET MVC website that uses Windows Authentication to control access. I would like to have a specflow selenium test that checks the configuration is correct by attempting to visit the site as a non-authorised user.
As we're using domain accounts to control access there isn't a username/password login screen. The credentials of the current user are automatically passed to the site by the browser.
So for my Selenium test I need to be able to run Internet Explorer as a specific user.
I have found a number of articles about windows impersonation and I can switch to my test user during the running of the test (using the code from http://support.microsoft.com/kb/306158). However if I then create an InternetExplorerDriver it starts internet explorer with my credentials rather than the test user's (although this question and answer suggests that it should work https://sqa.stackexchange.com/questions/2277/using-selenium-webdriver-with-windows-authentication).
I can also explicitly start an Internet Explorer process as my test user, but I can't see a way of binding an InternetExplorerDriver to an already running Internet Explorer process, so this may be a dead end.
My code, basically taken from the MSDN page above is below. In the debugger I can see that WindowsIdentity.GetCurrent().Name is "testUser" in all the steps of the test.
namespace MyProject.Specs
{
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.IE;
using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using TechTalk.SpecFlow;
[Binding]
public class AuthorisationSteps
{
public const int LOGON32_LOGON_INTERACTIVE = 2;
public const int LOGON32_PROVIDER_DEFAULT = 0;
private static WindowsImpersonationContext impersonationContext;
private static IWebDriver driver;
[BeforeScenario]
public static void impersonateUser()
{
if (!impersonateValidUser("testUser", "testDomain", "password"))
{
throw new Exception();
}
driver = new InternetExplorerDriver();
}
[AfterScenario]
public static void cleanupUser()
{
undoImpersonation();
driver.Quit();
}
[Given(#"I am an unauthorised user")]
public void GivenIAmAnUnauthorisedUser()
{
var temp = WindowsIdentity.GetCurrent().Name;
}
[When(#"I go to the home page")]
public void WhenIGoToTheHomePage()
{
var temp = WindowsIdentity.GetCurrent().Name;
driver.Navigate().GoToUrl(BaseUrl);
}
[Then(#"I should see an error page")]
public void ThenIShouldSeeAnErrorPage()
{
var temp = WindowsIdentity.GetCurrent().Name;
Assert.That(driver.Title.Contains("Error"));
}
[DllImport("advapi32.dll")]
public static extern int LogonUserA(String lpszUserName,
String lpszDomain,
String lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int DuplicateToken(IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool RevertToSelf();
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);
private static bool impersonateValidUser(String userName, String domain, String password)
{
WindowsIdentity tempWindowsIdentity;
var token = IntPtr.Zero;
var tokenDuplicate = IntPtr.Zero;
if (RevertToSelf())
{
if (LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, ref token) != 0)
{
if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
{
tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
impersonationContext = tempWindowsIdentity.Impersonate();
if (impersonationContext != null)
{
CloseHandle(token);
CloseHandle(tokenDuplicate);
return true;
}
}
}
}
if (token != IntPtr.Zero)
{
CloseHandle(token);
}
if (tokenDuplicate != IntPtr.Zero)
{
CloseHandle(tokenDuplicate);
}
return false;
}
private static void undoImpersonation()
{
impersonationContext.Undo();
}
}
}
We have many enterprise clients that use Windows Authentication for intranet facing applications and we are starting to run many Selenium tests for confirmation, regression, etc.
We've taken the helpful code from Steven's answer and refactored it into a re-usable class similar to other Impersonate posts that just weren't working for us because we wanted the tests to work both locally in development and deployed as part of the Visual Studio Team System release process.
The uri method was not working locally and neither were impersonating methods using Win32 native methods.
This one worked so here it is.
Example of a test using Steven's code refactored into a helper
[TestMethod]
public void ThisApp_WhenAccessedByUnathorizedUser_ShouldDisallowAccess()
{
string userName = "ThisAppNoAccess";
string password = "123456";
string domainName = Environment.MachineName;
using (new Perkins.Impersonator(userName, domainName, password))
{
// - Use Remote Web Driver to hook up the browser driver instance launched manually.
using (var driver = new RemoteWebDriver(new Uri("http://localhost:9515"), DesiredCapabilities.Chrome()))
{
var desiredUri = Helper.Combine(Helper.BaseURL, "/ThisApp/#/appGrid");
TestContext.WriteLine("desiredUri: {0}", desiredUri);
driver.Navigate().GoToUrl(desiredUri);
Helper.WaitForAngular(driver);
var noPermissionNotificationElement = driver.FindElementByXPath("//div[#ng-show='!vm.authorized']/div/div/div/p");
var showsNoPermissionNotification = noPermissionNotificationElement.Text.Contains("You do not have permissions to view ThisApp.");
Assert.AreEqual(true, showsNoPermissionNotification, "The text `You do not have permissions to view ThisApp.` is not being displayed!");
}
}
}
The helper class
// Idea from http://stackoverflow.com/a/34406336/16008
// - Launch the browser driver manually with other user's credentials in background
public class Perkins
{
public class Impersonator : IDisposable
{
Process _driverProcess = null;
string _driverPath = #"chromedriver.exe";
/// <summary>
/// Impersonates the specified user account by launching the selenium server under that account. Connect to it via RemoteWebDriver and localhost on port 9515.
/// </summary>
/// <remarks>
/// We may later want to enhance this by allowing for different ports, etc.
/// </remarks>
/// <param name="userName">Name of the user</param>
/// <param name="domainName">Name of the domain or computer if using a local account.</param>
/// <param name="password">The password</param>
public Impersonator(string userName, string domainName, string password)
{
ProcessStartInfo processStartInfo = new ProcessStartInfo(_driverPath);
processStartInfo.UserName = userName;
System.Security.SecureString securePassword = new System.Security.SecureString();
foreach (char c in password)
{
securePassword.AppendChar(c);
}
processStartInfo.Password = securePassword;
processStartInfo.Domain = domainName; // this is important, mcollins was getting a 'stub received bad data' without it, even though rglos was not
processStartInfo.UseShellExecute = false;
processStartInfo.LoadUserProfile = true; // this seemed to be key, without this, I get Internal Server Error 500
Thread startThread = new Thread(() =>
{
_driverProcess = Process.Start(processStartInfo);
_driverProcess.WaitForExit();
})
{ IsBackground = true };
startThread.Start();
}
public void Dispose()
{
// - Remember to close/exit/terminate the driver process and browser instance when you are done.
if (_driverProcess != null)
{
// Free managed resources
if (!_driverProcess.HasExited)
{
_driverProcess.CloseMainWindow();
_driverProcess.WaitForExit(5000);
// Kill the process if the process still alive after the wait
if (!_driverProcess.HasExited)
{
_driverProcess.Kill();
}
_driverProcess.Close();
}
_driverProcess.Dispose();
_driverProcess = null;
}
}
}
}
Perhaps this will help someone else with the same issue.
This is in fact possible. I ran into the exact problem you had. Basically, here are the steps you need to do.
Launch the browser driver manually with other user's credentials in background
Process driverProcess;
string driverPath; // The path to Selenium's IE driver.
ProcessStartInfo info = new ProcessStartInfo(driverPath)
{
UserName = "UserName", // The user name.
Password = new SecureString(), // The password for the user.
UseShellExecute = false,
LoadUserProfile = true,
Arguments = "about:blank"
};
// Start the driver in background thread
Thread startThread = new Thread(
() => {
try
{
driverProcess = Process.Start(info);
driverProcess.WaitForExit();
}
catch
{
// Close the process.
}
})
{
IsBackground = true
};
startThread.Start();
Use Remote Web Driver to hook up the browser driver instance launched manually.
var remoteDriver = new RemoteWebDriver(Uri("http://localhost:5555"), DesiredCapabilities.InternetExplorer());
Remember to close/exit/terminate the driver process and browser instance when you are done.
// Close the process when done.
if (driverProcess != null)
{
// Free managed resources
if (!driverProcess.HasExited)
{
driverProcess.CloseMainWindow();
driverProcess.WaitForExit(5000);
// Kill the process if the process still alive after the wait
if (!driverProcess.HasExited)
{
driverProcess.Kill();
}
driverProcess.Close();
}
driverProcess.Dispose();
driverProcess = null;
}
This similar question links to this Microsoft support article. Essentially you need
System.Security.Principal.WindowsImpersonationContext impersonationContext;
impersonationContext =
((System.Security.Principal.WindowsIdentity)User.Identity).Impersonate();
IWebDriver webDriver = new InternetExplorerDriver();
// do your stuff here.
impersonationContext.Undo();
There's additional code in the support article about impersonating a specific user.
Do you have a couple of old PCs? Or the capacity for some virtual machines?
If so, build a Selenium Grid set-up, and configure one to automatically login as the desired domain user and one as a non-domain user.
http://code.google.com/p/selenium/wiki/Grid2
I was having same problem when I was doing automation project for web based application which required window authentication. However, I have achieved this with using firefox, following are the steps to achieve it.
FIREFOX SETUP
OPEN RUN DIALOG OF YOUR SYSTEM AND TYPE 'firefox.exe -p' (CLOSE YOUR FIREFOX BROWSER BEFORE RUNNING THIS COMMAND) http://www.wikihow.com/Create-a-Firefox-Profile
CLICK ON CREATE PROFILE AND GIVE A NAME AS REQURIED
SELECT CREATED PROFILE AND START BROWSER AND OPEN ADD-ONS MANAGER (TOOLS - ADD-ONS)
SEARCH FOR 'AutoAuth' AND INSTALL IT. IT WILL ASK FOR RESTART, DO IT
ONCE THE FIREFOX IS RESTARTED, THAN OPEN URL IT WILL ASK YOU FOR AUTHENTICATION
ENTER USERNAME AND PASSWORD - SUBMIT IT, FIREFOX WILL ASK YOU TO REMEMBER THE PASSWORD
CLICK ON REMEMBER AND IT WILL SAVE THE PASSWORD IN FIREFOX PROFILE
COPY CREATED FIREFOX PROFILE AND SAVE IT TO REQUIRED FOLDER
IN YOUR SELENIUM SCRIPT CALL ABOVE CREATED PROFILE WITH FIREFOX DRIVER AND PASS THE SAME URL, IT WILL NOT ASK FOR AUTHENTICATION DIALOG
This is working very successfully in my project.
We use https://stackoverflow.com/a/31540010/3489693 approach for IE and Chrome over 2 years. It works fine
So it seems the problem that the question is trying to circumvent has to do with NTLM Auto Login. See Google Chrome and NTLM Auto Login Using Windows Authentication
The solutions above did not work for me since the auto-login would successfully authenticate with any user on my system, so it didn't matter which user I used for impersonation.
However, I noticed that you can outsmart auto-login by replacing localhost with any other domain name, such as the local IP address. No impersonation required :)
This may / may not work.
Try to launch your site in "CHROME".
Hit F-12, go to Application Tab -> Cookies -> Click on your site link. on left hand side look for something that represent your session id, may be JSESSIONID or similar that represents user's session, copy that.
Now open your Internet Explorer,
hit F-12 and manually create that JSESSIONID ( or similar key ) by running this command in console window
document.cookie = "JSESSIONID=your-session-id-from-chrome"
hit play button to execute script
Refresh your browser
We have many projects with several files inside each. Files can be checked in from the main solution root, from the project level and from the individual level.
Is there a way to find all files checked in by a particular user during the past few days, for all the levels?
If you have the TFS power tools installed you can use the command "tfpt searchcs" from a visual studio command prompt. This will allow you to search for all change sets checked in by a particular user and also set a start and end date along side some other filters. This might meet your needs
I think it's not possible to drill down to the files of each changeset of a user within a given timeframe by using the standard reporting facilities of TFS.The following uses the TFS-SDK & should accomplish the task:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
namespace GetCheckedInFiles
{
class Program
{
static void Main()
{
TfsTeamProjectCollection teamProjectCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri("http://tfsURI"));
var versionControl = teamProjectCollection.GetService<VersionControlServer>();
//enforcing 3 days as "past few days":
var deltaInDays = new TimeSpan(3, 0, 0, 0);
DateTime date = DateTime.Now - deltaInDays;
VersionSpec versionFrom = GetDateVSpec(date);
VersionSpec versionTo = GetDateVSpec(DateTime.Now);
IEnumerable results = versionControl.QueryHistory("$/", VersionSpec.Latest, 0, RecursionType.Full, "User" , versionFrom, versionTo, int.MaxValue, true, true);
List<Changeset> changesets = results.Cast<Changeset>().ToList();
if (0 < changesets.Count)
{
foreach (Changeset changeset in changesets)
{
Change[] changes = changeset.Changes;
Console.WriteLine("Files contained in "+changeset.ChangesetId+" at "+changeset.CreationDate+" with comment "+changeset.Comment);
foreach (Change change in changes)
{
string serverItem = change.Item.ServerItem;
Console.WriteLine(serverItem + " "+change.ChangeType);
}
Console.WriteLine();
}
}
}
private static VersionSpec GetDateVSpec(DateTime date)
{
string dateSpec = string.Format("D{0:yyy}-{0:MM}-{0:dd}T{0:HH}:{0:mm}", date);
return VersionSpec.ParseSingleSpec(dateSpec, "");
}
}
}
The GetDateVSpec was copied from this post by Robaticus
Is there a way to get tfs 2010 teambuild's controller (and agent) status from the command line? my controllers (have got about 20) keep on having to be restarted (we know why this is) and I'd like a way to run a script (psexec?) to check what's stayed up.
It's possible to have a small console app that does this for you as follows:
using System;
using Microsoft.TeamFoundation.Build.Client;
using Microsoft.TeamFoundation.Client;
namespace GetAgentsStatus
{
class Program
{
static void Main()
{
TfsTeamProjectCollection teamProjectCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri("CollectionUri"));
IBuildServer buildService = (IBuildServer)teamProjectCollection.GetService(typeof(IBuildServer));
IBuildController buildController = buildService.GetBuildController("BuildControllerName");
foreach (var agent in buildController.Agents)
{
if(agent.Status == AgentStatus.Offline || agent.Status == AgentStatus.Unavailable)
{
Console.WriteLine(string.Format("{0} needs restarting",agent.Name));
}
}
}
}
}
If you open any build definition for editing, navigate to "Build Defaults" to retrieve the value of BuildControllerName
I am working on TFS API. I am trying to get the entire list of projects, subprojects, files from TFS.
Could someone guide me regarding it.
TfsTeamProjectCollection teamProjectCollection = teamFoundationserver.TfsTeamProjectCollection;
ProjectCollection projCollect = (ProjectCollection) teamProjectCollection.GetService(typeof(ProjectCollection));
The above code just shows the first level from TFS. How Can I go further deep into TFS tree.
I want the entire list of projects, and solutions under each project.
Thanks,
SV
There's no such thing as a "subproject." What it sounds like you want to do is get a listing of all subfolders / files under each project.
To do that, iterate through each of your projects, and do a GetItems on each. Here's some code:
TfsTeamProjectCollection teamProjectCollection = new TfsTeamProjectCollection(new Uri("http://sw100429:8080"));
ProjectCollection projCollect = (ProjectCollection)teamProjectCollection.GetService(typeof(ProjectCollection));
VersionControlServer vcs = teamProjectCollection.GetService<VersionControlServer>();
// This approach lets you get the list of files for each team project individually.
foreach (TeamProject tp in projCollect)
{
string path = string.Format("$/{0}", tp.Name);
var filesAndFolders = vcs.GetItems(path, RecursionType.Full);
}
// However, this approach is a bit more succinct - instead
// of getting them for each team project, just start at "$/" and work your way down
var allFilesAndFolders = vcs.GetItems("$/", RecursionType.Full);
Using your q&a (thanks) I was able to put this sample together after a lot of trial and error. It goes a step further to show how to map the local paths too. I hope this saves some readers some head aches.
This example was put together in a form in VS 2015 and uses the following assembly references (that were also tricky to track down)
All located in C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\Extensions\vl45o2it.tph on my machine.
Microsoft.TeamFoundation.Client.dll
Microsoft.TeamFoundation.Common.dll
Microsoft.TeamFoundation.VersionControl.Client.dll
Microsoft.VisualStudio.TeamFoundation.dll
Apologies if my terminology is out in places. I don't mind if you edit any of this.
using System;
using System.Linq;
using System.Windows.Forms;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Framework.Common;
using Microsoft.TeamFoundation.Framework.Client;
using System.Diagnostics;
using Microsoft.TeamFoundation.VersionControl.Client;
namespace Tfs
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Uri tfsUri = new Uri("http://server:8080/tfs");
string repositoryName = "yourrepository";
string projectPath = "$/project/path/path/path";
Uri repositoryUri = new Uri(string.Format("{0}/{1}", tfsUri.AbsoluteUri, repositoryName));
TfsConfigurationServer tfscs = TfsConfigurationServerFactory.GetConfigurationServer(tfsUri);
//get the repository
CatalogNode repository = tfscs.CatalogNode.QueryChildren(new Guid[] { CatalogResourceTypes.ProjectCollection }, false, CatalogQueryOptions.None)
.FirstOrDefault(a => string.Compare(a.Resource.DisplayName, repositoryName, true) == 0);
//open in the project collection
TfsTeamProjectCollection pc = tfscs.GetTeamProjectCollection(new Guid(repository.Resource.Properties["InstanceId"]));
//tfs project file structure access
VersionControlServer vcs = pc.GetService<VersionControlServer>();
WorkspaceInfo wsi = Workstation.Current.GetAllLocalWorkspaceInfo().FirstOrDefault(a => a.ServerUri == repositoryUri);
//user functionality (checkin, localpaths etc)
Workspace ws = wsi.GetWorkspace(pc);
//get the file structure
ItemSet items = vcs.GetItems(projectPath, RecursionType.Full);
foreach (Item i in items.Items)
{
Debug.WriteLine(string.Format("{0} ({1}) - {2} - {3}", i.ServerItem,
i.ContentLength.ToString(),
i.ItemType.ToString(),
ws.GetLocalItemForServerItem(i.ServerItem)));
}
}
}
}