Authorization Problem in the Hello World PSI Extension sample

H

Heiner Eichmann

Hi!

I am trying to create a PSI extension, which uses the clients login
credentials to make ordinary PSI calls. The example PSIExtension2 from the
Microsoft Office Project 2007 SDK (see
http://msdn2.microsoft.com/en-us/library/bb428837.aspx ) does exactly this.
Just: If I run the example, I get a:

System.Web.Services.Protocols.SoapException was unhandled
Message="System.Web.Services.Protocols.SoapException: Server was unable to
process request. ---> System.Net.WebException: The request failed with HTTP
status 401: Unauthorized.\n
at
System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage
message, WebResponse response, Stream responseStream, Boolean asyncCall)\n
at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String
methodName, Object[] parameters)\n
at HelloWorldPSI.WSResource.Resource.ReadResource(Guid resourceUid)\n
at Service.HelloWorld()\n
--- End of inner exception stack trace ---"
Source="System.Web.Services"
Actor=""
Lang=""
Node=""
Role=""
StackTrace:
at
System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage
message, WebResponse response, Stream responseStream, Boolean asyncCall)
at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String
methodName, Object[] parameters)
at HelloWorldPSIClient.WSHelloWorldPSI.HelloWorldPSI.HelloWorld() in
C:\2007 Office System Developer Resources\Project 2007 SDK\Code
Samples\PSIExtensions\PSIExtension1\HelloWorldPSIClient\HelloWorldPSIClient\Web
References\WSHelloWorldPSI\Reference.cs:line 79
at HelloWorldPSIClient.HelloWorld.cmdHelloWorld_Click(Object sender,
EventArgs e) in C:\2007 Office System Developer Resources\Project 2007
SDK\Code
Samples\PSIExtensions\PSIExtension1\HelloWorldPSIClient\HelloWorldPSIClient\HelloWorld.cs:line
27
...

To get more details of the reason I replaced in Service.cs the lines

HelloWorldPSI.WSResource.ResourceDataSet resDS =
resWS.ReadResource(contextInfo.UserGuid);

message += "E-Mail Address: " +
resDS.Tables[resDS.Resources.TableName].Rows[0]
[resDS.Resources.WRES_EMAILColumn.ColumnName].ToString();

by

try
{
System.Security.Principal.WindowsIdentity identity =
System.Security.Principal.WindowsIdentity.GetCurrent();
message += "Current Account: " + identity.Name + "\r\n";
message += "Context identity name: " +
context.User.Identity.Name.ToString() + "\r\n";
message += "Context identity authentication type: " +
context.User.Identity.AuthenticationType + "\r\n";
message += "Context profile username: " +
context.Profile.UserName + "\r\n";
HelloWorldPSI.WSResource.ResourceDataSet resDS =
resWS.ReadResource(contextInfo.UserGuid);

message += "E-Mail Address: " +
resDS.Tables[resDS.Resources.TableName].Rows[0]
[resDS.Resources.WRES_EMAILColumn.ColumnName].ToString();
}
catch (Exception e)
{
message += "Error: " + e.ToString();
}

Now user account of the request and the account running the process are
printed out and the PSI call is surrounded by a try catch block. The
response is:


Hello World

The following user called the custom Project Server Web service:
UserName = A03TC065\heinriche, SiteGuid =
a3b3e226-5e36-40b2-ac74-649a08f65123, Lcid = 1033
Current Account: NT AUTHORITY\NETWORK SERVICE
Context identity name: NT AUTHORITY\NETWORK SERVICE
Context identity authentication type: NTLM
Context profile username:
Error: System.Net.WebException: The request failed with HTTP status 401:
Unauthorized.
at
System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage
message, WebResponse response, Stream responseStream, Boolean asyncCall)
at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String
methodName, Object[] parameters)
at HelloWorldPSI.WSResource.Resource.ReadResource(Guid resourceUid)
at Service.HelloWorld()

While the PSContextInfo knew the user making the call, the process itself
and the http context are the user running the Office Project Server service.
Therefore it is no surprise, that the PSI call failed. Just: Why did it work
on the machine of the developer, who wrote the SDK sample? Is there
somewhere a switch, to make this sample running?

I am using a basic project installation out of the box. Server and cliwnt
machine are in a domain. The domain user running the client is allowed to
make PSI calls.

Any pointer is appreciated. Regards,


Heiner
 
G

Guest

Hello Heiner,

It took me two man weeks last summer to get to where you are. All I can do is
confirm that it will not work as written. And, I will forgo a personal tirade
on my opinions about how this may have happened.

The bottom line is that you must pass a valid set of credentials when you call
any PSI method. If you call Microsoft support, they are going to tell you to
use impersonation. BUT, in order to use impersonation you have to set up a
proxy and you are still going to have this problem…

The following approach has worked well for me:

1. Create a service account. Grant this account access to the Project Server
with the appropriate privileges for the tasks you need to perform. If you only
have a single Project Server this can be a local account on the project server
system. If you have more than one project server in a farm, you will need to
use a domain account. NO domain or system privileges are required.

2. Put the credentials for this account in the PSI service web.config file.
This is in the same directory where you copied your HelloWorld.asmx file.
(Program Files\Microsoft Office Servers\12.0\WebServices\Shared\PSI) For
example:

<appSettings>
<add key="MyWebSvc_svcDomain" value="YourDomainName"/>
<add key=" MyWebSvc_svcUser" value="ServiceAccountUserName"/>
<add key=" MyWebSvc_svcPassword" value="LongHardToGuessPassword"/>
</appSettings>

3. Use these settings to access the PSI services. For example:

// Define the service credentials used for accessing the PSI web services
NetworkCredential serviceCredentials = new NetworkCredential();
serviceCredentials.Domain = reader.GetValue("MyWebSvc_svcDomain",
typeof(System.String)).ToString();
serviceCredentials.UserName = reader.GetValue("MyWebSvc_svcUser",
typeof(System.String)).ToString();
serviceCredentials.Password = reader.GetValue("MyWebSvc_svcPassword",
typeof(System.String)).ToString();

// Configure Resource web service
ResourceWebSvc.Resource resourceSvc = new ResourceWebSvc.Resource();
resourceSvc.Credentials = serviceCredentials;
resourceSvc.Url = "http://" + PSHost + "/" + PSInstance +
"/_vti_bin/psi/Resource.asmx";

ResourceWebSvc.ResourceDataSet dsResource =
resourceSvc.ReadResources(string.Empty, false);

However, you must realize that ANYONE can call your web service. As such the
very first thing you should add to your web service is a subroutine to validate
the credentials of the calling user. In my case I created an enterprise
resource field that holds a short list of application specific user roles. My
validation routine simply tries to find the appropriate value for this field in
the dsResource dataset. If the user isn’t in the dataset or doesn’t have the
correct value in this field, I log an application specific error in the
security log and return a status of ‘Failure’ to the caller.

I hope this helps you through the knot hole…

Bob Segrest
 
B

Bryant Likes

The user you're calling the service as must be added to Project Server under
Server Settings -> Manage Users.

I just ran the project as a non-user and got the 401 error. After I added
the user as a project user then I could make the call.
 
B

Bryant Likes

It took me two man weeks last summer to get to where you are. All I can do
is
confirm that it will not work as written. And, I will forgo a personal tirade
on my opinions about how this may have happened.

The bottom line is that you must pass a valid set of credentials when you call
any PSI method. If you call Microsoft support, they are going to tell you to
use impersonation. BUT, in order to use impersonation you have to set up a
proxy and you are still going to have this problem…

Hmmm.. I'm not sure I'm following. It seems to work for me as long as the
calling user is setup in Project Server. Impersonation is on by default (I
believe).

Are you trying to expose the service outside the domain?
 
H

Heiner Eichmann

The user you're calling the service as must be added to Project Server under
Server Settings -> Manage Users.

Hi Bryant,

thnks for you response. The user who makes the calls is already added to
Project server. As I wrote: the client is allowed to make PSI calls. In other
words: If I call from the same client logged in with the same user any PSI
web service from Project Server, the call succeeds. Just the call from the
PSI extension to Project Server fails.

Obviously this sample works on some installations but doen't on others.
Questions: Did you use a basic or a farm installation? Under which user
account is your Microsoft Office service running?

Thanks again and best regards,


Heiner
 
H

Heiner Eichmann

Hi Bob,

thanks for your reply. This is what I've don so far but my hope was, that I
could pass the credentails behind the curtain. The hello world example
states, that it is possible and the fact, that the sample seems to work on
some installation implies, that there must be a knob to make it work.

Furthermore: If you deploy a similar dll as an ordinary web service (NOT a
PSI extension) and if you set

<identity impersonate="true"/>

in the web.config of this web service, the "behind the curtain" passing of
the credentials from the client through the dll to the Project Server works!
Unfortunately just setting this in the web.config of the PSI does not help.
There must be something elseI've missed so far...

Thanks again and best regards,


Heiner
 
G

Guest

Hello Heiner,

If you find the magic switch be sure to post it here. I would be very
interested in hearing the details.

Bob Segrest
 
B

Bryant Likes

Obviously this sample works on some installations but doen't on others.
Questions: Did you use a basic or a farm installation? Under which user
account is your Microsoft Office service running?

I tested it on a basic installation. Maybe that is the issue?

If you're using a farm installation let me know and I'll deploy my extention
to one of our test farms. I have a real extention running in both QA and
Production in our environment (which are both farms), but they might not pull
the user info out of the header as the example does.

Let me know.

Thanks!
 
B

Bryant Likes

Obviously this sample works on some installations but doen't on others.
Questions: Did you use a basic or a farm installation? Under which user
account is your Microsoft Office service running?

Sorry, forgot to post the account information. In the simple farm the
website is running using a local user account and in the farms it is a domain
account.

Also, all installations are still pre-SP1 right now.
 
H

Heiner Eichmann

Hi Bryant,

I am using a basic installation out of the box. That means, that the two
services

Microsoft Office Project Server Event/Queue Service

are running under the user account "NT Authority\Network Service". This was
done by the installer - I havn't touched it.

I have added 2 users to "Server Settings" -> "Manage Users" of the pwa: the
local Administrator and a domain user (which is an Administrator on the
server machine as well). Both users have all available rights in "Manage
Users" of the pwa.

Furthermore I have modified the HelloWorldPSIClient code: Now it first calls
ReadResource of the Resource web service before calling HelloWorld.

I have run the HelloWorldPSIClient from
- a client machine in the same domain logged in as the domain user above
- the project server logged in as the domain user above
- the project server logged in as local Administrator

The result is always the same:
- I can call ReadResource of the Resource web service
- I can call HelloWorld
- The ReadResource call from the HelloWorld PSI extension gets a 401
unauthorized.

As I said: my project installation is a basic installation right from the
installer (with SP1 applied). I haven't changed anything (except from adding
the 2 users).

Best regards,


Heiner
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top