Super User API Access
tl;dr I'm getting 401:Unauthorized errors when making HTTP requests to REST API endpoints using a blank username and superuser token taken from teamcity-rest.log as credentials.
I've searched the forums/tracker/etc. extensively and not found a definitive answer, so I figured I'd ask for help. First, the context in case someone sees something I'm not and has an alternative solution:
We're upgrading from 9.1.7 to 10.0.4 and implementing a test environment to (immediate need) test TeamCity 10 for compatibility and (long term need) have a test environment for all our developer tools (youtrack, upsource, etc.). The long term concerns have made automating a regular backup/restore from the live environment to the test one a necessity. So right now I'm scripting a backup/restore from our production 9.1.7 TeamCity server to a test 9.1.7 one (upgrading once I ensure the test environment is correctly configured).
A problem has arisen in that our projects/configurations are too tightly coupled to the production environment, so I figured I'd lean on the REST API to automatically reconfigure builds when the live backup is restored to test to re-parameterize them for our test build environment. This is no obstacle in itself, but accessing the API has become one. I want to run the script using the credentials of the (unmanaged) service account we use for TeamCity. This account has the project viewer role in the application itself.
Here's the problem I've run into, and what I've tried:
My way around this was initially to enable superuser login for the API (added "rest.use.authToken=true" to the internal.properties file) after restoring the backup on the test server, restart the service, grab the superuser token from the teamcity-rest.log file and use that to authenticate with the API and make necessary changes. The following (PowerShell) returned a 401 unauthorized. I should also note that switching from .NET's WebClient to Invoke-Webrequest had no effect. When I run it using my account credentials (system admin role in the application), I get a valid response.
$wc = New-Object Net.WebClient
$creds = New-Object Net.NetworkCredential("", $superUserToken)
$wc.Credentials = $creds
$wc.DownloadString("http://testserverhostname.domain/httpAuth/app/rest/projects)
With that failing, I noticed the last entry in the API docs and thought I could use the token to elevate the service account to the system admin role, storing the service account's credentials (encrypted) on the test server where the script's running as a last resort. The following also returned a 401, using both the service account's and my credentials (as $user:$pass below). It too failed with Invoke-Webrequest.
$wc = New-Object Net.WebClient
$wc.UploadString("http://$user:$pass@testserverhostname.domain/httpAuth/app/rest/users/username:$serviceAccountName/roles/SYSTEM_ADMIN/g/?authToken=$superUserToken", "Put", "")
Finally, thinking I might be grabbing the wrong token from the log, I've tried all the ones that make sense (in bold below).
[2017-01-30 11:27:19,616] INFO [17-01-30 11:27)] - rver.server.rest.APIController - Authentication token for superuser generated: '[TOKEN]'.
[2017-01-30 11:27:29,212] INFO [17-01-30 11:27)] - est.APIController/rest-api-9.0 - Authentication token for Super user generated: '[TOKEN]' (plugin 'rest-api-9.0')
[2017-01-30 11:27:29,821] INFO [17-01-30 11:27)] - rver.server.rest.APIController - Authentication token for superuser generated: '[TOKEN]'.
[2017-01-30 11:27:31,758] INFO [17-01-30 11:27)] - er.rest.APIController/rest-api - Authentication token for Super user generated: '[TOKEN]' (plugin 'rest-api')
[2017-01-30 11:27:32,633] INFO [17-01-30 11:27)] - rver.server.rest.APIController - Authentication token for Super user generated: '[TOKEN]' (plugin 'rest-api-8.1')
Thoughts or help would be greatly appreciated.
Please sign in to leave a comment.
Hi there,
I've been trying the command line mentioned in the docs for version 10 and found that depending on the client implementation, passing the user-pass (empty:superusertoken) as part of the URL might not always work. The rest API expects it as basic authentication, so it might make sense to extract them from the URL and pass them as an actual header on the request. That has consistently worked on my end, and I have updated the documentation to reflect that. I have tested the same approach for both version 10 and version 9.1 and both seem to work with the same procedure mentioned for version 10, so I would suggest sticking to that one for easier maintenance.
On the other hand, the super user token should be on the teamcity-server.log file. Could you please test with this one?
That worked great, thanks (encoding in header + using the token in teamcity-server.log)! Why have a superuser token in teamcity-rest.log then?
For anyone that stumbles across this, here's how I'm authenticating requests to the server as the superuser (manually encoding the credentials in the header per this stackoverflow post; though oddly teamcity seems to stick to the basic auth RFC and return a 401...dunno the internals on Net.WebClient well enough to say why it doesn't respond with the credentials stored in the credentials property):
Glad it worked. That token was used in some older versions to grant permissions. Eventually, it got moved into the core functionality (where you could find the current super user token) and it got redundant, but hasn't been removed from the logs yet.
Hey Denis,
Clarification question on that last point:
Does the rest.use.authToken internal property need to be set to true to be able to authenticate against the REST API with the super user token found in the server.log or is that also just part of that redundant functionality?
It's also part of the redundant functionality. Did a test just in case, and without the internal property set, it works as expected.