Connect to TFS programmatically from vs 2017 - c#

I am using TFS 15.x. package.
Error:
Microsoft.TeamFoundation.TeamFoundationServerUnauthorizedException:
'TF30063: You are not authorized to access
"https://myproject.visualstudio.com/RpaCodeReview'
Uri Repurl = new Uri("https://myproject.visualstudio.com/RpaCodeReview");
NetworkCredential netCred = new NetworkCredential(username, password);
VssBasicCredential basicCred = new VssBasicCredential(netCred);
VssCredentials tfsCred = new VssCredentials(basicCred);
TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(Repurl, tfsCred);
tpc.EnsureAuthenticated();

It depends on the version of your TFS. However, if you're trying to connect to TFS2015, or TFS2017, this will do;
using Microsoft.TeamFoundation.Client;
using Microsoft.VisualStudio.Services.Common;
using System;
using System.Net;
namespace TFSConsoleApp
{
class Program
{
static void Main(string[] args)
{
NetworkCredential networkCredentials = new NetworkCredential(#"Domain\Account", #"Password");
Microsoft.VisualStudio.Services.Common.WindowsCredential windowsCredentials = new Microsoft.VisualStudio.Services.Common.WindowsCredential(networkCredentials);
VssCredentials basicCredentials = new VssCredentials(windowsCredentials);
TfsTeamProjectCollection tfsColl = new TfsTeamProjectCollection(
new Uri("http://XXX:8080/tfs/DefaultCollection"),
basicCredentials);
tfsColl.Authenticate(); // make sure it is authenticate
}
}
}
I cannot stress enough to ensure the credentials are a-okay! This error has occured to me a couple times too.
There is also another solution if the above doesn't work.
Close Visual Studio and go to Control Panel
User Accounts --> Manage your Credentials (on the left column)
Select "Windows Credentials"
Scroll down to the "Generic Credentials" section and look for
your TFS server connection
Expand the pull down and click "Edit"
Enter in your network password
Restart Visual Studio and retry the code

Along with all the comments on credentials I have found basic authentication blocked on some repositories.
I have found it best to create Personal Access Token (PAT) in the repository. Then use that in you connections to access the APIs.
Example to read what projects are in the default collection of a tfs/devops repo:
string PAT = "Put PAT String Here";
string RepoStore = "https://url of repo here";
string responseBody = "";
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(
System.Text.ASCIIEncoding.ASCII.GetBytes(
string.Format("{0}:{1}", "", PAT))));
using (HttpResponseMessage response = client.GetAsync(
RepoStore + "/_apis/projects").Result)
{
response.EnsureSuccessStatusCode();
responseBody = await response.Content.ReadAsStringAsync();
}
Console.WriteLine(responseBody);
}
Console.ReadKey();

Related

How to get specific commit from GitLab project

I am developing (so new at it) an application with Windows Forms C# in Visual Studio and I need to get a specific commit from a GitLab project.
I have seen many examples about for GitHub. I have tried this way (do not know if is the correct one):
Download repository at a particular commit using the 7-digit SHA1:
var Token = "xxxx";
var url = "http://{my_domain}/{user}/{project_name}/repository/archive/{shor_id}.zip";
var path = #"C:\GitLab\My_Projects";
try
{
using (var client = new System.Net.Http.HttpClient())
{
var credentials = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}:", Token);
credentials = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(credentials));
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", credentials);
var contents = client.GetByteArrayAsync(url).Result;
System.IO.File.WriteAllBytes(path, contents);
}
}
catch (System.UnauthorizedAccessException)
{
backgroundWorker1.CancelAsync();
Console.WriteLine("Problem downloading");
}
But I get this error:
'System.UnauthorizedAccessException' in mscorlib.dll. Access denied to 'C:\GitLab\My_Projects'.
As I said before, I am new at it and probably may have said something stupid, sorry in advance.
If someone knows about the subject, I would love to explain or help me with this concept.
Finally, I achived the solution. I took another direction using GitLab API.
If someone would like to look at the result I arrived, here you have the code:
var url_sha = $"http://{my_gitlab_domain}/api/v4/projects/{id_project}/repository/archive.zip?
private_token={my_token}&sha={short_id_commit}";
var client = new HttpClient();
var response = await client.GetAsync(url_sha);
var local_path = #"C:\GitLab\My_Projects";
using (var stream = await response.Content.ReadAsStreamAsync())
{
var fileInfo = new FileInfo(local_path + ".zip");
using (var fileStream = fileInfo.OpenWrite())
{
await stream.CopyToAsync(fileStream);
}
}

Resetting a user's password using Microsoft Graph

I'm trying to write a web portal that users can use to reset their own Azure AD password. Because of the requirements of my client, the Azure AD SSPR is not an option.
To achieve this I'm using Microsoft Graph. According to the documentation, it is possible to reset a users password using Microsoft Graph if you have User.ReadWrite.All or Directory.AccessAsUser.All permissions.
Then the permissions documentation, the remarks it states that even if you have the Directory.ReadWrite.All permissions you won't be able to reset a users password.
I've done a test to see if this will work but I get an HTTP 403 Forbidden response.
The code I'm using is:
string ResourceUrl = "https://graph.windows.net/";
string AuthorityUrl = "https://login.microsoftonline.com/companyxxx.onmicrosoft.com/oauth2/authorize/";
//Create a user password cradentials.
var credential = new Microsoft.IdentityModel
.Clients
.ActiveDirectory
.UserPasswordCredential("username#xxxx.com", "passwordxxx");
// Authenticate using created credentials
var authenticationContext = new AuthenticationContext(AuthorityUrl);
var authenticationResult = authenticationContext
.AcquireTokenAsync(ResourceUrl, "xxxxxxxx-3017-4833-9923-30d05726b32f", credential)
.Result;
string jwtToken = authenticationResult.AccessToken;
var cred = new Microsoft.Rest
.TokenCredentials(authenticationResult.AccessToken, "Bearer");
HttpClient client = new HttpClient();
var queryString = HttpUtility.ParseQueryString(string.Empty);
queryString["api-version"] = "1.6";
client.DefaultRequestHeaders
.Accept
.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwtToken);
var uri = "https://graph.windows.net/xxxxxxxx-18fe-xxxx-bb90-d62195600495/users/xxxxxxxx-aa58-4329-xxxx-b39af07325ee?" + queryString;
//var content = new StringContent("{\"passwordProfile\": {\"password\": \"Test123456\", \"forceChangePasswordNextLogin\": true }}");
var response = client.PatchAsync(new Uri(uri), content, jwtToken);
The PatchAsync method is an extension method as below:
public static class HttpClientExtensions
{
public static async Task<HttpResponseMessage> PatchAsync(this HttpClient client,
Uri requestUri, HttpContent iContent, string jwtToken)
{
var method = new HttpMethod("PATCH");
var request = new HttpRequestMessage(method, requestUri)
{
Content = iContent,
};
request.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/json");
request.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", jwtToken);
HttpResponseMessage response = new HttpResponseMessage();
try
{
response = await client.SendAsync(request);
}
catch (TaskCanceledException e)
{
Console.WriteLine("ERROR: " + e.ToString());
}
return response;
}
}
Could someone please clarify if this is possible using the credentials grant flow with a username and password for authentication. If so how do I achieve this?
You're mixing up Microsoft Graph and Azure AD Graph API. These are two different APIs and calls to one are not interchangeable with the other.
You are correct in that you need to use the Directory.AccessAsUser.All scope for this activity. This scope allows the API to do anything to the AAD that the signed in user would be able to do themselves (i.e. change their own password).
Once you have a valid access_token for the user with Directory.AccessAsUser.All permission, you can update the user's passwordProfile:
PATCH https://graph.microsoft.com/v1.0/me
Content-type: application/json
{
"passwordProfile" : {
"forceChangePasswordNextSignIn": true,
"password": "password-value"
}
}

Passing current authentication credentials from UWP app to web service to access resources on server with these credentials

My Windows 10 UWP app is calling a WebAPI web service that I have created. I need to pass the current credentials on the client side when calling the web service so that it can access other resources using these credentials.
I also need to do this without prompting the user for credentials so that the experience is seamless.
I am able to do this with using System.Net.Http and successfully pass the current credentials to the server to use for accessing resources. This sends the request and brings back the response without any prompt. I have enabled Enterprise Authentication and Private Networks capabilities on the UWP app to make this work.
Problem: This works fine for GET requests but not for POST requests to the same server. POST requests result in the following error:
This IRandomAccessStream does not support the GetInputStreamAt method
because it requires cloning and this stream does not support cloning.
I read that this was a bug on this link: PostAsync throwing IRandomAccessStream error when targeting windows 10 UWP. The workaround proposed in multiple locations for this bug is to use Windows.Web.Http instead. However, if I do this, how can I pass the default/current credentials to the server?
Here is the code that I am using to do a GET request using the current Windows credentials without prompting for it. It works flawlessly:
System.Net.Http.HttpClientHandler handler = new System.Net.Http.HttpClientHandler
{
UseDefaultCredentials = true
// Credentials = (NetworkCredential)System.Net.CredentialCache.DefaultNetworkCredentials
//using either one of the above enables me to have the web service use the current credentials without prompting
};
string responseContent = string.Empty;
using (System.Net.Http.HttpClient client = new System.Net.Http.HttpClient(handler))
{
System.Net.Http.HttpRequestMessage requestMessage = new System.Net.Http.HttpRequestMessage();
requestMessage = new System.Net.Http.HttpRequestMessage
{
Method = System.Net.Http.HttpMethod.Get,
RequestUri = new Uri(strWebServiceURL)
};
using (System.Net.Http.HttpResponseMessage response = await client.SendAsync(requestMessage))
{
responseContent = await response.Content.ReadAsStringAsync();
}
//This also works fine
using (System.Net.Http.HttpResponseMessage response = await client.GetAsync(strWebServiceURL))
{
responseContent = await response.Content.ReadAsStringAsync();
}
Below is the code I use to do a POST request which results in the IRandomAccessStream error:
System.Net.Http.HttpClientHandler handler = new System.Net.Http.HttpClientHandler
{
UseDefaultCredentials = true
// Credentials = (NetworkCredential)System.Net.CredentialCache.DefaultNetworkCredentials
//using either one of the above enables me to have the web service use the current credentials without prompting
};
string responseContent = string.Empty;
using (System.Net.Http.HttpClient client = new System.Net.Http.HttpClient(handler))
{
System.Net.Http.HttpRequestMessage requestMessage = new System.Net.Http.HttpRequestMessage();
requestMessage = new System.Net.Http.HttpRequestMessage
{
Content = myMultipartFormDataContent,
Method = System.Net.Http.HttpMethod.Post,
RequestUri = new Uri(strWebServiceURL)
};
using (System.Net.Http.HttpResponseMessage response = await client.SendAsync(requestMessage))
{
responseContent = await response.Content.ReadAsStringAsync();
}
//No difference when using it this way as well
using (System.Net.Http.HttpResponseMessage response = await client.PostAsync(strWebServiceURL, myMultipartFormDataContent))
{
responseContent = await response.Content.ReadAsStringAsync();
}
I tried using Windows.Web.Http but I don't know how I can get it to pass the current/default credentials to the server without prompting.
I have also added the WebService URL to a IE Local Intranet zone and have that zone set to automatically log in with current user name and password:
Please help!
With the new Windows.Web.Http namespace in UWP app, if you want to use the DefaultCredentials, all you have to do is turn on enterprise credentials in the manifest and the uwp app will send them out as appropriate. You don't need to configure anything on the HttpClientto make it work. Details please reference this thread.
Since you already enable the enterprise credentials capability, you could just create HttpClient without configure. But to avoid the username and password prompt, you may need to disable the UI, for example:
var myFilter = new HttpBaseProtocolFilter();
myFilter.AllowUI = false;
Windows.Web.Http.HttpClient client = new Windows.Web.Http.HttpClient(myFilter);
Windows.Web.Http.HttpResponseMessage result = await client.GetAsync(new Uri("http://localhost:5132/api/values"));

Download "Visual Studio Team Services" Charts via WebRequest

I need to upgrade an old TFS 2013 class for Visual Studio Team Services.
To get the Burndown-Chart I used to download the image via HttpWebRequest direcly from the url.
Somehow Iam not able to do this in VSTS. I always get the error message "invalid parameters". Everything else works fine. (I had to setup the Alternate authentication credentials in my profile to get it working for my application)
Here my code:
public Image GetChart(string uri)
{
HttpWebRequest httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(uri);
httpWebRequest.Credentials = new NetworkCredential("MyUserNameForApplication", "MyPWForApplication");
using (HttpWebResponse httpWebReponse = (HttpWebResponse)httpWebRequest.GetResponse())
{
using (Stream stream = httpWebReponse.GetResponseStream())
{
return Image.FromStream(stream); //Error occourse
}
}
}
The url which gets passed as a parameter usally looks like this:
https://YourVSName.visualstudio.com/DefaultCollection/a5d2310b-d3f8-4365-b693-3826ab60e939/_api/_teamChart/Burndown?chartOptions={%22Width%22%3A1248%2C%22Height%22%3A161%2C%22ShowDetails%22%3Atrue%2C%22Title%22%3A%22%22}&counter=1&iterationPath=Developing\Sprint+1&__v=5
What I think the problem is:
First I thought this might be a security issue, because this code is able to download normal google images. And when I try to get the content of the url It returns a lot of code with a message in it:
Microsoft Internet Explorer's Enhanced Security Configuration is currently enabled on your environment. This enhanced level of security prevents our web integration experiences from displaying or performing correctly. To continue with your operation please disable this configuration or contact your administrator
I set my Internet security settings to the lowest level and still the same result.
Another reason why this might not working is, because the url linking to the burndown-chart doesnt contain an Image extension. Iam not quite shure here this effects the result.
Or that the parameters which are getting past in the url are incorrect...
What I have tried so far:
I have used bunch of other code to get the image from that link. For example using WebClient or tried to upload cookies (credentials) to the tfs and than tried to connect.
My Question
Is it possible to get that image from the chart via url, and if so, how?
Thanks for any kind of help :).
EDIT
Currently Iam using this code (Thanks to #Eddie - MSFT):
public static async void GetChart(string uri,string username, string password)
{
try
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(
System.Text.ASCIIEncoding.ASCII.GetBytes(
string.Format("{0}:{1}", username, password))));
using (HttpResponseMessage response = client.GetAsync(uri).Result)
{
response.EnsureSuccessStatusCode();
var responseStream = await response.Content.ReadAsStreamAsync();
var img = Image.FromStream(responseStream);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
static void Main(string[] args)
{
string uri = "https://Name.visualstudio.com/DefaultCollection/a5d2310b-d3f8-4365-b693-3826ab60e939/_api/_teamChart/Burndown?chartOptions=%7B%22Width%22%3A1248%2C%22Height%22%3A636%2C%22ShowDetails%22%3Atrue%2C%22Title%22%3A%22%22%7D&counter=1&iterationPath=Developing%5CSprint+1&__v=5";
TFSHelper.TFSHelper.GetChart(uri, username,pw)
}
I use "httpclient" with alternative credential to do this:
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Net.Http;
using System.Net.Http.Headers;
using System.IO;
namespace GetImageA
{
class Program
{
static void Main(string[] args)
{
Uri uri = new Uri("your image uri");
GetImage(uri);
}
public static void GetImage(Uri uri)
{
var username = "username";
var password = "password";
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(
ASCIIEncoding.ASCII.GetBytes(
string.Format("{0}:{1}", username, password))));
Stream str = client.GetStreamAsync(uri).Result;
Image im = Image.FromStream(str);
im.Save("E:\\image.png");
}
}
}
}
Did you try the authentication with your personal access token instead of username and password, something like this?
I am using the below code to download the attachments, inline images of the Work items from VSTS.
try
{
var personalaccesstoken = "Your_VSTS_Personal_Access_Token";
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(
System.Text.ASCIIEncoding.ASCII.GetBytes(
string.Format("{0}:{1}", "", personalaccesstoken))));
using (HttpResponseMessage response = client.GetAsync(uri).Result)
{
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseBody);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}

Cannot connect to url in C# with 401 unauthorized error

I'm looking to get the content of a page from
http://12.18.60.199:81
I'm using my corporate network and if I use internet explorer, it prompts for username and password, I type that in and I get the content of the page. I need to do this in C#, but have no luck for the past few hours:
Uri requestUri = null;
Uri.TryCreate("http://12.18.60.199:81", UriKind.Absolute, out requestUri);
NetworkCredential nc = new NetworkCredential(#"username", #"password", "domain");
CredentialCache cache = new CredentialCache();
cache.Add(requestUri, "Basic", nc); //also tried "Anonymous", "Basic", "Digest", "Dpa",
//"External", "Kerberos", "Msn", "Negotiate", "Ntlm", "Sicily"
using (WebClient client = new WebClient())
{
client.Credentials = cache;
using (Stream stream = client.OpenRead("http://12.18.60.199:81"))
using (StreamReader reader = new StreamReader(stream))
{
//stuff
}
}
Keep getting 401 unauthorized, invalid credentials, help!
If I substitute the above address with http://google.com, it'll work, so the code works... username and password have been tested to work in broswer
If you are connecting through a proxy server try adding in your proxy and pass the credentials. For example:
// Prepare web request...
HttpWebRequest myRequest =
(HttpWebRequest)WebRequest.Create("http://www.test.com");
// proxy details
myRequest.Proxy = new WebProxy("http://10.0.0.1", true);
myRequest.Proxy.Credentials = new NetworkCredential("test", "password", "domain");
I had same issue, and found workaround to add Authorization request header. I've used fiddler to get that base64. Never found proper solution, as you all know temp solutions are long-lasting in out world :)
service.Url = "http://127.0.0.1/";
service.SetRequestHeader("Authorization", "Basic dW8sX3NvYXB6SjRma2pzdXZqNDg5ZA==");
service.PreAuthenticate = true;

Categories

Resources