Open ghost opened 11 years ago
Hi.
First off, the user running the app pool doesn't have anything to do with the TFS authorization: TfsProxy uses the Authorization and TfsUrl headers from the HTTP request to make the connection, so whatever username and password you provide via Basic auth are used to connect. So the IIS hosting is a red herring.
The exception you're getting from the Projects controller indicates that the Team Project Collection is returning null
for the workItemStore
on this line:
var workItemStore = collection.GetService<WorkItemStore>();
... which is strange, because if you successfully authenticate, you should be able to get an instance of the Work Item Store.
A coulpe of questions to help diagnose the issue:
[TfsBasicAuthentication]
attribute on the controller class, and getting the TfsConfiguratoinServer
instance that the TfsBasicAuthenticationAttribute
provides in HttpContext.Current.Items["TFS_CONFIG_SERVER"]
? Based on your stack trace, it looks like you're using TfsConnection
directly.I may also have a coding error in there - I'm assuming that the user will have permission to access the Work Item Store for each collection, but may not have the right to actually read work items. It's possible that
The purpose of this project is to help people connect to an arbitrary TFS server from clients that can't use the TFS API. That's the reason for taking credentials and TfsUrl in the request headers. Is your user-facing app in C#? If so you might just want to skip TfsProxy and talk to TFS using its native API. Or you might want to rip out my auth attribute in your fork and rely on the appdomain user, as it looks like you are in your Builds controller.
Also, it looks like this may be your issue: http://osnabrugge.wordpress.com/2011/11/04/tfs-api-workitemstore-is-null-and-throws-nullreferenceexception/
Try changing to using the WorkItemStore constructor on the line I mentioned above and see if you get the same exception mentioned in the blog post.
Did you add any references to the project? Specifically, did you add any from the ...\Common7\IDE\ReferenceAssemblies\v4.0
folder?
Thanks for the feedback. I'm not well versed in .NET authentication/authorization paradigm, so this is very helpful. My background is much more in Ruby than C#, but I need a good TFS api and this seems like a better start than what I was working on. The consumer will probably end up being either a JS or Ruby client of some kind. I am as of yet undecided.
Answers:
tfs.EnsureAuthenticated()
on the new connection. Seemed to be recommended by a number of MS resources. require 'httpi'
request = HTTPI::Request.new 'http://api-dev-01.../api/projects'
request.headers["TfsUrl"] = "http://tfs-dev-01:8080/tfs"
request.open_timeout = 20
request.auth.basic('domain\username', 'password')
response = HTTPI.get(request)
puts response.code
puts response.body
OK, sounds like this project could be useful to you for a Ruby or JavaScript client.
In your builds controller you were relying on the current App Pool user, but now you've switched to HttpContext.Current.User as UserDataPrincipal
, which is the credential provided by the TfsBasicAuthenticationAttribute
. You'll probably get better performance if you get the build service from the HttpContext.Current.Items["TFS_CONFIG_SERVER"] as TfsConfigurationServer
-- it's already authenticated, but you're probably re-authenticating if you're constructing a new TfsConnection
instance.
In any case, if your end-users aren't going to enter their own specific TFS credentials, you may be better off ripping out TfsBasicAuthenticationAttribute and just using the current App Pool user. In your non-working BuildController (before using the UserDataPrincipal), TfsBasicAuthenticationAttribute may have been interfering with authenticating as the app pool user when on full IIS, because it changes the HttpContext.Current.User
.
As for the exception in ProjectsController
, check out the link I posted above -- changing to the WorkItemStore
constructor may expose a more specific error.
Ah okay. I wasn't sure I could get the IBuildServer service from the HttpContext.Current.Items["TFS_CONFIG_SERVER"]
but I think I was doing it wrong. Appears to work now. Thanks for that. I will check the issue on WorkItemStore
momentarily and get back to you on that.
Out of curiosity, and mostly because I don't understand all the code is doing, does each request need to do basic auth, or are the creds cached in a cookie? And if so, is there a way to do an initial "auth" request, capture the authentication and then have all subsequent calls use that instead of requiring basic auth for every call?
An auth cookie is returned (named .ASPXAUTH I believe), which you can set on further requests (and omit the basic auth header). I have not actually tested this scenario, because my client lib automatically caches the Basic auth stuff. It ought to work.
Are you going to have users enter their individual TFS creds? If not, you can rip out the TfsBasicAuthenticationAttribute
all together, not set any basic auth on the request, and just rely on the app pool user's identity to auth against TFS.
By the way, someone at Microsoft has published a more fully-featured TFS proxy that exposes TFS resources over an OData API. The reason I created my proxy is that the Microsoft proxy gets configured server-side to talk to one specific TFS server, but I wanted clients to be able to specify an arbitrary server. The OData proxy also uses Basic auth, but doesn't take a header for the TFS URL. Here is is: http://www.microsoft.com/en-us/download/details.aspx?id=36230
There is a Ruby OData client lib: https://github.com/visoft/ruby_odata
... might save you some work
I had seem some buzz around odata but wasn't sure if it was ready for prime time yet. I'll investigate. Thanks for expanding.
I did add my build controller to my fork. Any feedback about how it's being done would be awesome.
As for the other issue regarding the project controller, I made the suggested changes, but in both cases, I am now only getting back an error with no backtrace. I'll dig further. And update.
Hey there. I'm working on adding stuff to this service for my own uses elsewhere and I'd be happy to contribute back to this.
Currently however, I'm having issues getting this service running with proper access to TFS instance of IIS outside of VS. When I login using valid creds running IIS express on my local machine, it can query TFS fine. As soon as I publish to an IIS server elsewhere, set the app pool to run as a TFS accepted user, login via the same creds, I get errors and/or unauthorized problems.
Your projects controller returns:
And my builds controller gets
Is there any magic configuration sauce when deploying to an IIS server outside of the local machine?