This is the code from the ImpersonationConsoleApp from the Project SDK that
I've been trying to use as a baseline (I only edited the PSI_RESOURCE_SSP,
PROJECT_SERVER_URI, and PWA_SITE_GUID). The output when running this is below
it.
--------------------------------------------------------------------------------------------------
// During impersonation, use URLs for the PSI Web services in the Shared
Service Provider.
// The Shared Service Provider URLs go directly to the PSI, not
through Project Web Access.
private const string PSI_RESOURCE_SSP =
"
http://w2k8-01:56737/SharedServices1/PSI/Resource.asmx";
// Before impersonation, use the PSI through Project Web Access.
private const string PROJECT_SERVER_URI = "
http://w2k8-01/pwa";
private const string RESOURCE_SERVICE_PATH =
"/_vti_bin/psi/resource.asmx";
// GUID of the Project Web Access site in SharePoint. **Change to
your value**
private const string PWA_SITE_GUID =
"12d3f676-aeb2-4a39-9a1e-ba85e66dcc6c";
static ResourceDerived resProxyBySSP;
static void Main(string[] args)
{
Console.WriteLine("Enter a valid Project Server user account,
such as");
Console.WriteLine(@"domain\username or
aspnetsqlmembershipprovider:username:");
string userAccount = Console.ReadLine();
try
{
resProxyBySSP = new ResourceDerived();
resProxyBySSP.Url = PSI_RESOURCE_SSP;
resProxyBySSP.Credentials =
System.Net.CredentialCache.DefaultCredentials;
// Get the GUID of the user to impersonate.
Guid userGuid = GetResourceUid(userAccount);
Console.WriteLine("User GUID to impersonate: " +
userGuid.ToString());
// You can also hard-code the user's Guid. Get the user's
Guid from the Published
// database or from the System Identification Data section
on the Manage User page
// in Project Web Access. For example:
// Guid userGuid = new
Guid("0154dd59-13fc-4f50-908e-5f24fa6ef785");
// To use the following code, the application must run on
the Project Server computer
// where Windows SharePoint Services is installed.
//
// Microsoft.SharePoint.SPSite site = new
SPSite(PROJECT_SERVER_URI);
// Guid siteId = site.ID;
// You can hard-code the siteId. Get the siteID in one of
the following two ways:
// a. Open the SharePoint ContentDB for the SSP. Open the
Webs table and
// get the SiteId where the FullUrl value is the name
of the
// Project Web Access instance, for example,
ProjectServerName.
// b. Open the Manage Project Web Access Sites page in
the SSP Admin site,
// click the drop-down list for the site and then
click Edit. The siteID
// is the GUID of the id option in the URL, for example:
//
http://ServerName:19466/ssp/admin/_...=Edit&id=44c4ae03-16c2-4618-bf9a-12643006c5be
//
// To use the following code, the application must run on
the Project Web
// Access computer. Set references to the
Microsoft.SharePoint and
// Microsoft.SharePoint.Search assemblies on the same
computer.
// Microsoft.SharePoint.SPSite site = new
SPSite(PROJECT_SERVER_URI);
// Guid siteId = site.ID;
// Otherwise, hard-code the site ID.
Guid siteId = new Guid(PWA_SITE_GUID);
resProxyBySSP.Credentials =
System.Net.CredentialCache.DefaultCredentials;
// Get the GUID of the user to impersonate.
bool isWindowsUser = true;
if (userAccount.Contains("aspnetsqlmembershipprovider"))
isWindowsUser = false;
ResourceDerived.SetImpersonationContext(isWindowsUser,
userAccount,
userGuid, Guid.Empty, siteId, "1033");
// To get the GUID of the user we are impersonating,
// call GetCurrentUserUid in the Resource Web service.
Guid impersonatedUserGuid = resProxyBySSP.GetCurrentUserUid();
Console.WriteLine(string.Format("\nImpersonating
'{0}':\n\tResourceProxy.Url:\n\t{1}",
userAccount, resProxyBySSP.Url));
Console.Write("\nImpersonated user GUID: ");
Console.WriteLine(impersonatedUserGuid.ToString());
}
catch (SoapException ex)
{
ExceptionHandlers.HandleSoapException(ex);
}
catch (WebException ex)
{
ExceptionHandlers.HandleWebException(ex);
}
catch (Exception ex)
{
ExceptionHandlers.HandleException(ex);
}
finally
{
ExceptionHandlers.ResetConsole();
}
Console.Write("Press any key...");
Console.ReadKey();
}
/// <summary>
/// Returns the GUID for a Project Server account name.
/// </summary>
/// <param name="ntAccount"></param>
/// <returns></returns>
private static Guid GetResourceUid(String accountName)
{
Resource resProxyByPWA = new Resource();
resProxyByPWA.Credentials = CredentialCache.DefaultCredentials;
// Before impersonation, use the Project Web Access URL.
resProxyByPWA.Url = PROJECT_SERVER_URI + RESOURCE_SERVICE_PATH;
ResourceDataSet rds = new ResourceDataSet();
// Filter for the account name, which can be a Windows or
Project Server account.
PSLibrary.Filter filter = new PSLibrary.Filter();
filter.FilterTableName = rds.Resources.TableName;
PSLibrary.Filter.Field accountField =
new PSLibrary.Filter.Field(rds.Resources.TableName,
rds.Resources.WRES_ACCOUNTColumn.ColumnName);
filter.Fields.Add(accountField);
PSLibrary.Filter.FieldOperator op =
new PSLibrary.Filter.FieldOperator(
PSLibrary.Filter.FieldOperationType.Equal,
rds.Resources.WRES_ACCOUNTColumn.ColumnName, accountName);
filter.Criteria = op;
Console.WriteLine("\nNot
impersonating:\n\tResourceProxy.Url:\n\t" + resProxyByPWA.Url);
rds =
(ResourceDataSet)(resProxyByPWA.ReadResources(filter.GetXml(), false));
// Return the account GUID
return (Guid)rds.Resources.Rows[0]["RES_UID"];
}
}
--------------------------------------------------------------------------------------------------
And here is the code from resourceDerived to set the impersonation headers:
--------------------------------------------------------------------------------------------------
protected override WebRequest GetWebRequest(Uri uri)
{
WebRequest webRequest = base.GetWebRequest(uri);
if (contextString != String.Empty)
{
webRequest.UseDefaultCredentials = true;
bool isImpersonating =
(System.Security.Principal.WindowsIdentity.GetCurrent(true) != null);
webRequest.Credentials = CredentialCache.DefaultCredentials;
webRequest.Headers.Add("PjAuth", contextString);
webRequest.Headers.Add("ForwardedFrom",
"/_vti_bin/psi/resource.asmx");
webRequest.PreAuthenticate = true;
}
return webRequest;
}
--------------------------------------------------------------------------------------------------
Because I can't use my development tools under a different
user name, I'm logged in as myself on the domain (Artsoft\Martijn).
I've given this account administrator priviliges on the machine where
project is installed (w2k8-01), in Project Web Access and for the Shared
Service Provider.
Then I've tried impersonation several users that were entered as resources
in Project:
W2k8-01\Piet
W2k8-01\Administrator
AspNetSqlMembershipProvider
iet
The result, for all of these is as follows:
Enter a valid Project Server user account, such as
domain\username or aspnetsqlmembershipprovider:username:
W2k8-01\Piet
Not impersonating:
ResourceProxy.Url:
http://w2k8-01/pwa/_vti_bin/psi/resource.asmx
User GUID to impersonate: 4c0c3396-5506-4bec-977a-c56e4822076b
Web Exception: The request failed with HTTP status 401: Unauthorized.
Log on, or check that Project Server is running.
Press any key...
:
Can you please post your code?
--
Stephen Sanderlin
VP of Technology
MSProjectExperts
For Project Server Consulting:
http://www.msprojectexperts.com
For Project Server Training:
http://www.projectservertraining.com
Read my blog at:
http://www.projectserverhelp.com
Join the community at:
http://forums.epmfaq.com
I'm trying to write an application that updates the actuals of assignments of
various resources in MS Project 2007 through the SOAP/PSI interface.
The statusing changexml function seems to do what I require, but only for
the currently logged in user.
After some investigation it appears that I'll to use impersonation to get
this to function properly.
I've been trying to get impersonation to work, but so far, no luck.
Neither my own code, nor the impersonationConsoleApp from the SDK appear to
be able to impersonate anybody.
I've added myself as administrator to the machine, and granted myself direct
access to the shared service provider using the sharepoint central admin site
and the stsadm command-line tool.
That not helping, I added <identity impersonatie="true"> to the system.web
section of the sharedservices site.
I have a Windows 2008 Server with IIS 7 and Project 2007.
My Project Web Access site is running at
http://w2k8-01/pwa
My Shared Services site is running at
http://w2k8-01:56737/sharedservices1
Now, if I understand this correctly, to make a call using impersonation, one
sends a SOAP request to
http://w2k8-01:56737/SharedServices1/PSI/Resource.asmx (in the example, or
http://w2k8-01:56737/SharedServices1/PSI/Statusing.asmx for my own code)
This SOAP request has an pjAuth header from the PSContextInfo containing:
- the GUID of the account to impersonate
- the GUID of the Project Web Access site
and a ForwardedFrom header containing:
- "/_vti_bin/PSI/Resource.asmx"
The sharedservices site validates my credentials, then calls the Project Web
Access site (
http://w2k8-01/pwa/_vti_bin/PSI/Resource.asmx) with the