TF30063: You are not authorized ... programmatic access not working - c#

The code below provided in this answer did work well for a while but now its throwing Microsoft.TeamFoundation.TeamFoundationServerUnauthorizedException: 'TF30063: You are not authorized to access https://{mysite}.visualstudio.com/.' again.
var credentials = new VssClientCredentials();
credentials.PromptType = CredentialPromptType.PromptIfNeeded;
var teamProjects = new TfsTeamProjectCollection(tfsCollectionUri, credentials);
teamProjects.EnsureAuthenticated(); // exception thrown
Q How can I fix this problem?
Update Strange enough,
before executing teamProjects.EnsureAuthenticated(); the debugger reads PromptIfNeeded for credentials.PromptType.
after the exception has been thrown and the debugger has stopped the execution, it reads DoNotPrompt for credentials.PromptType.
Observation
The above code works perfectly well in a console application but fails to work in a windows forms application (i.e. it throws an exception).
Q1 How can I make the above code work in a windows forms application?

If you execute the code above within a Task (i.e. a separate thread) it just works. If the credentials are not present or stale at the location in the registry (see this answer) a window opens and you can authenticate yourself.
Can anyone explain why this works?

VS has added a registry entry to store the credential, try to delete the entry in the following path:
HKEY_CURRENT_USER\Software\Microsoft\VSCommon\14.0\ClientServices\TokenStorage\VisualStudio\VssApp
Update:
Also, try the code below to see whether it works:
var credentials = new VssClientCredentials();
credentials.PromptType = CredentialPromptType.PromptIfNeeded;
credentials.Storage = new VssClientCredentialStorage(storageKind: "VssApp2", storageNamespace: "VisualStudio");
var aTeamProjects = new TfsTeamProjectCollection(new Uri("https://xxxxx.visualstudio.com/"), credentials);
aTeamProjects.EnsureAuthenticated();

Related

Run C# script through scheduled task

I've developed a small script in c# that is querying SQL Server and add computer objects to some Active Directory groups based on certain criteria. The script is working fine when I run it using the account which has the necessary rights to add/remove objects from Active Directory Group.
When I try to schedule the job, so it runs automatically from server using the "SYSTEM" account it does not work, I get "Access denied" I've updated the bind account to use the credentials from an account that works but I still have the same issue.
> Error Message:
> *2020-01-13 18:32:30,984 [1] ERROR TestAD.clsActiveDirectory - Error occured when trying to add computerobject abcdefg-a10 to group. Error
> message: Access is denied.*
The only way to make it work is using the actual account as account to run the scheduled task, however, problem is that our company policy does not allow us to store passwords, so I need to have the account logged-on to run this script.
code snippet
de.Username = "testing#test.com";
de.Password = "xxxxxxxxx";
de.AuthenticationType = AuthenticationTypes.Secure;
de.AuthenticationType = AuthenticationTypes.Sealing;
de.AuthenticationType = AuthenticationTypes.Delegation;
de.Properties.Count.ToString();
ds.SearchRoot = de;
ds.Filter = "(&(objectClass=computer)(name=" + _myComputerName.ToString() + `"))";`
ds.PropertiesToLoad.Add("memberof");
ds.PropertiesToLoad.Add("distinguishedname");
ds.SizeLimit = 10;
ds.PageSize = 0;
ds.SearchScope = System.DirectoryServices.SearchScope.Subtree;
I've tried adding some "AuthenticationTypes" to see if that made difference but still same
any help would be appreciated
Thx.
Have you tried using SQL Server Agents? My company uses them as opposed to Scheduled Tasks. They may be less elegant, but it may be a good alternative to your situation.
Create a SQL Server Agent that calls the executable with or without parameters.
If you cannot call the executable from the hosting OS, you can call an SSIS package on the network to call the executable for you.
Please let me know if you need more details.
I found the issue, and in the end pretty straight forward.
The Active Directory flow is following
- Bind to active directory with my special account and search for the computer object and
validate if it needs to be added to Active Directory Group
- if it needs to be added do 2nd bind to the Active Directory group and add computer
object. ==> This piece failed when using scheduled task or run under "SYSTEM" context
Reason for failure: When I bind 2nd time I did not specify any credentials so it was
using default credentials (SYSTEM) if I run the script my account which has enough
rights to add computer objects to groups.
I updated code for 2nd bind to include binding credentials and now it's working as
expected.
I hope this will be helpful for somebody else who has to troubleshoot similar issues.
old code
try
{
DirectoryEntry ent = new DirectoryEntry(bindString);
ent.Properties["member"].Add(newMember);
ent.CommitChanges();
}
New code
try
{
DirectoryEntry ent = new DirectoryEntry(bindString);
ent.AuthenticationType = AuthenticationTypes.Secure;
ent.AuthenticationType = AuthenticationTypes.Sealing;
ent.AuthenticationType = AuthenticationTypes.Delegation;
ent.Username = "test123#test.com";
ent.Password = "test123";
ent.Properties["member"].Add(newMember);
ent.CommitChanges();
}

Rotativa ActionAsPdf() Very Slow

Using Rotativa 1.6.4 from NuGet and have noticed the following issue using the code below.
ActionAsPdf hangs randomly for indeterminate amount of time.
Code below that is hanging:
var pdfResult = new ActionAsPdf("Report", new {id = Request.Params["id"]})
{
Cookies = cookieCollection,
FormsAuthenticationCookieName = FormsAuthentication.FormsCookieName,
CustomSwitches = "--load-error-handling ignore"
};
Background info that may help:
The customSwitches is in use to ignore a documented issue calling wkhtmltopdf.exe using the ActionAsPdf, but it does not suppress errors in the code only in the wkhtmltopdf call.
Observations, usage and testing:
It works but when running the application (whether or not stepping through code), it can be anywhere from 10 seconds up to about 4 minutes between hitting the pdfResult = new ActionAsPdf and finally entering into the "Report" action being called. Can't discern anything actually happening in the output window of Visual Studio, no errors are being thrown that I have found. Just random slow transition into the Reports() action.
I can run the Reports() action directly via URL and it never slows like this and is quite fast for PDF generation. I am running it using the ActionAsPdf to obtain the binary to save to file system and send via email, which is the prescribed method of doing so for this library.
The behavior exists on both a local Windows 10 dev box and a remote Server 2008R2 Test box. .Net 4.5.1 on both boxes, default IIS on each.
Questions I have:
Any idea on what might cause this slow down and how to remedy it?
I ended up using UrlAsPdf() instead of ActionAsPdf() and it works. Seems there may be some issues with the ActionAsPdf() and I have filed a bug with Rotative project on GitHub. The ActionAsPdf() is still marked as beta, so hopefully it get's fixed in future versions or by the community.
In my case, I had to do few more tweaks along with using UrlAsPdf(). I have narrowed down the issue to the cookie collection that I was adding. So I tried just adding the cookie that I needed, and the issue was resolved. Following is the sample code that I have used.
var report = new UrlAsPdf(url);
Dictionary<string, string> cookieCollection = new Dictionary<string, string>();
foreach (var key in Request.Cookies.AllKeys)
{
if (Crypto.Hash("_user").Equals(key))
{
cookieCollection.Add(key, Request.Cookies.Get(key).Value);
break;
}
}
report.Cookies = cookieCollection;
report.FormsAuthenticationCookieName = FormsAuthentication.FormsCookieName;

Changing how a service is configured

When installing a service, there is a helpful .NET class called ServiceProcessInstaller. This class has a property Account, which is a ServiceAccount enumeration with possible values LocalService, LocalSystem, NetworkService and User.
This is fine at install-time, but does anybody know how I can change this value for an existing service?
I assuming that I need to move away from the actual install-type classes, and have been researching hooking into the advapi32 ChangeServiceConfig method, WMI and ManagementObjects etc.
Indeed I have found code which will actually change the account under which the service runs,
ManagementObject mo = new ManagementObject("Win32_Service.Name='" + myService + "'");
object[] configParams = new object[11];
configParams[6] = userName;
configParams[7] = password;
object result = mo.InvokeMethod("Change", configParams);
(which on its own looks a bit like black magic but makes sense when viewed with the ChangeServiceConfig signature)
However when I apply this code to a service which happens to be installed as LocalSystem, it has no effect (although when I interpret result the call is reporting success). This doesn't really surprise me since I am only setting a username and password, I am not saying "rather than running as a local service, this service needs to run under a specific user account".
Now, my gut feel is that I am heading along the right lines here. The problem is that none of the parameters in ChangeServiceConfig appear to offer the opportunity to do this.
Any ideas? TIA, Pete
Error code 16 means "Service marked for deletion". Sometimes when you change service parameter, in particular when you delete / re-create a service you need to reboot your PC for operation to complete. While it's still pending, you can't manipulate service and you get error code 16.
Also, it might not be the case, that you problem has something to do with the fact that the call is inside a dll. If you put you code in a test rig dll and call it from a test rig exe (the same way you tested it in a test rig exe) and don't create / delete service in between I think it will work anyway.
The reason it does not working in your application on my opinion has to do with what you did with the service before (and this something most likely is not described in your question).
You need Impersonate an thread to run at context of user.
Try this class :
A small C# Class for impersonating a User
or this one :
Impersonate User
Return code is 21: "Invalid Parameter".
I ran into the same issue: Problem occurs when trying to apply a new user/password to a service which currently has "LocalSystem" with "Allow Service to interact with desktop" enabled.
To resolve, set the "DesktopInteract" flag in the "Change" query
var query = new ManagementPath(string.Format("Win32_Service.Name='{0}'", serviceName)); // string.Format("SELECT * FROM Win32_Service where Name='{0}'", serviceName);
using (ManagementObject service = new ManagementObject(query))
{
object[] wmiParams = new object[10];
//WMI update doesn't work if the service's user is currently set to LocalSystem
// with Interact with desktop on
wmiParams[5] = false;
wmiParams[6] = serviceUserName;
wmiParams[7] = password;
//update credentials for the service
var rtn = service.InvokeMethod("Change", wmiParams);
}

Unable to access ArcGIS ServerObjectManager

I have a C# plugin for ArcGIS, and I'm trying to access ServerObjectManager off of an AGSServerConnection but it's coming out as null.
The block of code is:
string serverMachineName = adfMap.PrimaryMapResourceInstance.DataSource.DataSourceDefinition;
Identity adfIdentity = new Identity("Administrator", "password", "computername");
AGSServerConnection agsServerConnection = new AGSServerConnection(serverMachineName, adfIdentity, true);
IServerObjectManager serverObjectManager = agsServerConnection.ServerObjectManager;
IServerContext serverContext = serverObjectManager.CreateServerContext("TemporaryContext", "MapServer");
serverMachineName is something along the lines of http://computername/arcgis/services.
The Administrator account is an administrator on the system, as well as a member of agsadmin and agsusers (just in case).
The connection line succeeds.
serverObjectManager is null at this point, so the subsequent call to create a server context fails.
Any advice?
Looks like I shouldn't have taken a non-error on the connection for granted. Throwing in a check to see if it was actually connected showed that it really wasn't.

Setting Server Bindings of IIS 6.0 Programmatically

I'm trying to set up an installer to register a web site. Currently, I've got it creating an Application Pool and Web Site under Windows Server 2003. Unfortunately, whenever I try to modify the ServerBindings property to set the IP Address, it throws an exception at me. I first tried this because the documentation here told me to http://msdn.microsoft.com/en-us/library/ms525712%28VS.90%29.aspx. I'm currently using VB.NET, but C# answers are okay too as I need to switch it over to using C# anyway.
siteRootDE.Properties.Item("ServerBindings").Item(0) = "<address>"
This throws an ArgumentOutOfRangeException. I checked it, and server bindings is of size 0. When I tried to create a new entry in the list like this:
siteRootDE.Properties.Item("ServerBindings").Add("<address>")
I get a COMException when I try that.
I looked at the registered property keys, and ServerBindings is nowhere to be found. However, when I create the Web Site through IIS, it generates ServerBindings correctly and I can see it.
What do I need to do to get ServerBindings to appear?
EDIT: I moved the code over to C# and tried it. It seems for some reason, VB.NET crashes when given either the above, but C# doesn't. But that code still doesn't seem to do anything. It just silently fails. I'm trying it like this:
// WebPage is the folder where I created the website
DirectoryEntry siteRootDE = new DirectoryRoot("IIS://LocalHost/W3SVC/WebPage");
// www.mydomain.com is one of the IP addresses that shows up
// when I used the IIS administrative program
siteRootDE.Properties["ServerBindings"].Value = ":80:www.mydomain.com";
siteRootDE.CommitChanges();
In C# you should be able to do this:
webSite.Invoke("Put", "ServerBindings", ":80:www.mydomain.com");
or
webSite.Properties["ServerBindings"].Value = ":80:www.mydomain.com";
EDIT:
Here is the sample code I used.
public static void CreateNewWebSite(string siteID, string hostname)
{
DirectoryEntry webService = new DirectoryEntry("IIS://LOCALHOST/W3SVC");
DirectoryEntry website = new DirectoryEntry();
website = webService.Children.Add(siteID, "IIsWebServer");
website.CommitChanges();
website.Invoke("Put", "ServerBindings", ":80:" + hostname);
// Or website.Properties["ServerBindings"].Value = ":80:" + hostname;
website.Properties["ServerState"].Value = 2;
website.Properties["ServerComment"].Value = hostname;
website.CommitChanges();
DirectoryEntry rootDir = website.Children.Add("ROOT", "IIsWebVirtualDir");
rootDir.CommitChanges();
rootDir.Properties["AppIsolated"].Value = 2;
rootDir.Properties["Path"].Value = #"C:\Inetpub\wwwroot\MyRootDir";
rootDir.Properties["AuthFlags"].Value = 5;
rootDir.Properties["AccessFlags"].Value = 513;
rootDir.CommitChanges();
website.CommitChanges();
webService.CommitChanges();
}
Also, here is a good article for reference.

Categories