Impersonating Current User

Jun 16, 2010 at 4:37 PM

I've a scenerio where I'm creating FIM extension website, where users can search for FIM groups and add themselves to that group's membership. I've already written the code (using latest RmClient) to add and remove user from given group.

But, I'm facing issues when I try to impersonate current user, so that FIM web service call to add a user is make from user using the website.

 

My set up is like this,

App Pool : running on a service account
Web Site  in IIS : Impersonation is on

and, I want somethign like this,

  • User X browse to this website
  • Searches a group
  • Clicks JOIN
  • Join calls the code given below
  • Request should be sent as user X

I'm using following code to impersonate user, then call RmClient PUT method and then undo the impersonation,

private static void ProcessGroupTransaction(OperationType operation, RmPerson person, RmGroup group)
        {
            System.Security.Principal.WindowsImpersonationContext ctx = null;
            Logger.Write("Before impersonation :" + System.Security.Principal.WindowsIdentity.GetCurrent().Name);
            
            ctx = ((System.Security.Principal.WindowsIdentity)HttpContext.Current.User.Identity).Impersonate();

            try
            {
                Logger.Write("After impersonation :" + System.Security.Principal.WindowsIdentity.GetCurrent().Name);
                using (RmResourceChanges transaction = new RmResourceChanges(group))
                {
                    transaction.BeginChanges();
                    if (operation == OperationType.AddUserToGroup)
                        group.ExplicitMember.Add(person.ObjectID);
                    else if (operation == OperationType.RemoveUserFromGroup)
                        group.ExplicitMember.Remove(person.ObjectID);

                    Client.Put(transaction);
                    transaction.AcceptChanges();
                    Logger.Write("Before undo :" + System.Security.Principal.WindowsIdentity.GetCurrent().Name);
                    ctx.Undo();
                    Logger.Write("After undo :" + System.Security.Principal.WindowsIdentity.GetCurrent().Name);
                }
            }
            catch (Exception ex)
            {
                Logger.Write("Before undo ERROR:" + System.Security.Principal.WindowsIdentity.GetCurrent().Name);
                ctx.Undo();
                Logger.Write("After undo ERROR:" + System.Security.Principal.WindowsIdentity.GetCurrent().Name);
                Logger.Write(ex.ToString(), "Fatal");
                throw;
            }
        } 

I get this error:

Timestamp: 6/16/2010 4:18:43 PM

Message: System.ServiceModel.Security.SecurityNegotiationException: The caller was not authenticated by the service. ---> System.ServiceModel.FaultException: The request for security token could not be satisfied because authentication failed.

at System.ServiceModel.Security.SecurityUtils.ThrowIfNegotiationFault(Message message, EndpointAddress target)

at System.ServiceModel.Security.SspiNegotiationTokenProvider.GetNextOutgoingMessageBody(Message incomingMessage, SspiNegotiationTokenProviderState sspiState)

--- End of inner exception stack trace ---

This is very urgent. It would be great if anyone can assist me on this.

Thanks,
Gaurav

 

Developer
Jun 16, 2010 at 4:45 PM

Look in the samples for the sections where the user credentials are sent to the default client.

You need your client to impersonate the user, not your web app.

-Jeremy

From: getsetgo_007 [mailto:notifications@codeplex.com]
Sent: Wednesday, June 16, 2010 9:37 AM
To: jeremy@palenchar.net
Subject: Impersonating Current User [fim2010client:216253]

From: getsetgo_007

I've a scenerio where I'm creating FIM extension website, where users can search for FIM groups and add themselves to that group's membership. I've already written the code (using latest RmClient) to add and remove user from given group.

But, I'm facing issues when I try to impersonate current user, so that FIM web service call to add a user is make from user using the website.

My set up is like this,

App Pool : running on a service account
Web Site in IIS : Impersonation is on

and, I want somethign like this,

  • User X browse to this website
  • Searches a group
  • Clicks JOIN
  • Join calls the code given below
  • Request should be sent as user X

I'm using following code to impersonate user, then call RmClient PUT method and then undo the impersonation,

private static void ProcessGroupTransaction(OperationType operation, RmPerson person, RmGroup group)
        {
            System.Security.Principal.WindowsImpersonationContext ctx = null;
            Logger.Write("Before impersonation :" + System.Security.Principal.WindowsIdentity.GetCurrent().Name);
            
            ctx = ((System.Security.Principal.WindowsIdentity)HttpContext.Current.User.Identity).Impersonate();
 
            try
            {
                Logger.Write("After impersonation :" + System.Security.Principal.WindowsIdentity.GetCurrent().Name);
                using (RmResourceChanges transaction = new RmResourceChanges(group))
                {
                    transaction.BeginChanges();
                    if (operation == OperationType.AddUserToGroup)
                        group.ExplicitMember.Add(person.ObjectID);
                    else if (operation == OperationType.RemoveUserFromGroup)
                        group.ExplicitMember.Remove(person.ObjectID);
 
                    Client.Put(transaction);
                    transaction.AcceptChanges();
                    Logger.Write("Before undo :" + System.Security.Principal.WindowsIdentity.GetCurrent().Name);
                    ctx.Undo();
                    Logger.Write("After undo :" + System.Security.Principal.WindowsIdentity.GetCurrent().Name);
                }
            }
            catch (Exception ex)
            {
                Logger.Write("Before undo ERROR:" + System.Security.Principal.WindowsIdentity.GetCurrent().Name);
                ctx.Undo();
                Logger.Write("After undo ERROR:" + System.Security.Principal.WindowsIdentity.GetCurrent().Name);
                Logger.Write(ex.ToString(), "Fatal");
                throw;
            }
        } 

I get this error:

Timestamp: 6/16/2010 4:18:43 PM

Message: System.ServiceModel.Security.SecurityNegotiationException: The caller was not authenticated by the service. ---> System.ServiceModel.FaultException: The request for security token could not be satisfied because authentication failed.

at System.ServiceModel.Security.SecurityUtils.ThrowIfNegotiationFault(Message message, EndpointAddress target)

at System.ServiceModel.Security.SspiNegotiationTokenProvider.GetNextOutgoingMessageBody(Message incomingMessage, SspiNegotiationTokenProviderState sspiState)

--- End of inner exception stack trace ---

This is very urgent. It would be great if anyone can assist me on this.

Thanks,
Gaurav

Read the full discussion online.

To add a post to this discussion, reply to this email (fim2010client@discussions.codeplex.com)

To start a new discussion for this project, email fim2010client@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe or change your settings on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com

Jun 16, 2010 at 7:15 PM

I've seen sample where credentials are set as Network Credentials using Username and Password. But, if I need to impersonate my page identify to hit FIM endpoint, then what should I do? How can I impersonate ASP.NET page identity to hit FIm service endpoint. This is a intranet website and anyone who is a valid user on domain can hit the service.

Any pointers?

Regards,
Gaurav

Developer
Jun 16, 2010 at 10:16 PM

I am not a web developer.

But you should be able convert the user credentials as known and understood by ASP into a valid Network Credential that can be used by the Client.

-Jeremy

From: getsetgo_007 [mailto:notifications@codeplex.com]
Sent: Wednesday, June 16, 2010 12:16 PM
To: jeremy@palenchar.net
Subject: Re: Impersonating Current User [fim2010client:216253]

From: getsetgo_007

I've seen sample where credentials are set as Network Credentials using Username and Password. But, if I need to impersonate my page identify to hit FIM endpoint, then what should I do? How can I impersonate ASP.NET page identity to hit FIm service endpoint. This is a intranet website and anyone who is a valid user on domain can hit the service.

Any pointers?

Regards,
Gaurav

Read the full discussion online.

To add a post to this discussion, reply to this email (fim2010client@discussions.codeplex.com)

To start a new discussion for this project, email fim2010client@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe or change your settings on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com

Coordinator
Jun 29, 2010 at 8:44 AM

Hi Gaurav,
I don't know what you have done so far, but basically you must:

Then you can impersonate a user like this:

WindowsImpersonationContext wicontext;
WindowsIdentity windowsIdentity = HttpContext.Current.User.Identity as WindowsIdentity;
wicontext = windowsIdentity.Impersonate();
// do something while impersonating...
_wicontext.Undo();

Hope this helps,
Paolo  

Jan 26, 2012 at 9:39 AM

I had same problem :(

can enyone help me?

how to join fim group using IIS?

My code:

 

public DefaultClient RmClient
        {
            get
            {
                if (rmClient == null)
                {
                    WindowsImpersonationContext wicontext;
                    WindowsIdentity windowsIdentity = HttpContext.Current.User.Identity as WindowsIdentity;
                    wicontext = windowsIdentity.Impersonate();
                    rmClient = new DefaultClient();
                    rmClient.ClientCredential = CredentialCache.DefaultNetworkCredentials;
                    rmClient.RefreshSchema();
                }
                return rmClient;
            }
        }

 

protected void Button1_Click(object sender, EventArgs e)
        {
            try
            {
                var groupId = "bbc6cca4-1bd1-45ed-92e6-bec80801f2ba";
                var fullQuery = "/Group[ObjectID='" + groupId + "']";
 
                foreach (RmGroup group in RmClient.Enumerate(fullQuery))
                {
                    var transaction = new RmResourceChanges(group);
                    try
                    {
                        transaction.BeginChanges();
 
                        group.ExplicitMember.Add(GetUserIdByLogin(Label1.Text).ObjectID);
 
                        RmClient.Put(transaction);
                        transaction.AcceptChanges();
                    }
                    catch (Exception ex)
                    {
                        transaction.DiscardChanges();
                    }
 
                }
            }
            catch (Exception ex)
            {
                //Result.Text = "ERROR: " + ex.Message;
            }
            
        }

 

Coordinator
Jan 26, 2012 at 11:11 AM

Your code could maybe be improved (you don't need to run a query if you know the ObjectID, a Get is enough), but if it works when you provide user name and password, then it's not a code but a configuration issue.

Under which credentials is the asp.net application pool running? How did you configure that account?

Jan 26, 2012 at 11:18 AM
Edited Jan 26, 2012 at 11:21 AM

App iis pool run under domain\user1

impersonate=true and windows authentication configured with IIS

My test user is: domain\user2

when i comment code

//rmClient.ClientCredential = CredentialCache.DefaultNetworkCredentials;
user successfull join to group, but under domain\user1

I want to join FIM group with domain\user2 credentials and without login and password
My FIM service run under special accounts, like domain\fimservice

Please more detailed with this
"configure the account under which your ASP.NET application is running to be able 
to delegate credentials to the FIM service"
Jan 26, 2012 at 11:24 AM

It was not related to FIM, the problem is with your delegation set up.

  • Create SPNs for the AppPool/ service account and enable constrained delegation for the accounts.
  • Modified the FIM Service endpoints in ASP.NET Web site config file to use the above created servicePrincipalName as the identity instead of userPrincipalName
  • As mentioned above the Enumeration and Resource endpoints for FIM need to use the servicePrincipalName instead of userPrincipalName. FIMService/SPN is the SPN for DomainName\ServiceAccountName

<identity>

     <servicePrincipalName value="FIMService/SPN" />

 </identity>

Instead of

<identity>

     <userPrincipalName value="DomainName\ServiceAccount" />

    </identity>

  • Set the EnableClientImpersonation to true
  • Use simple ASP.NET impersonation code to call the FIM service.
Jan 26, 2012 at 12:37 PM
Edited Jan 26, 2012 at 12:38 PM

what I do wrong?

my FIM service use domain\fimcorenotify account, spn configured:

 setspn -S HTTP/APP21-COF DOMAIN\fimcorenotify

 setspn -S FIMService/APP21-COF DOMAIN\fimcorenotify

 

for test I run IIS pool with account domain\fimcorenotify (Trust this user for delegation to any service (Kerberos only))

using http://msdn.microsoft.com/en-us/library/ms998351.aspx create test page

 

protected void Button1_Click(object sender, EventArgs e)
        {
            WindowsIdentity id = WindowsIdentity.GetCurrent();
            Response.Write("<b>Windows Identity Check</b><br>");   // <-AAAAAAAAAAAAAAA
            Response.Write("Name: " + id.Name + "<br>");
 
            // Obtain the authenticated user's Identity
            WindowsIdentity winId = (WindowsIdentity)HttpContext.Current.User.Identity;
            WindowsImpersonationContext ctx = null;
            try
            {
                // Start impersonating
                ctx = winId.Impersonate();
                // Now impersonating
                // Access resources using the identity of the authenticated user
 
                WindowsIdentity id2 = WindowsIdentity.GetCurrent();
                Response.Write("<b>Windows Identity Check impersonating</b><br>");
                Response.Write("Name: " + id2.Name + "<br>");   // <-BBBBBBBBBBBBBB
            }
            // Prevent exceptions from propagating
            catch
            {
            }
            finally
            {
                // Revert impersonation
                if (ctx != null)
                    ctx.Undo();
            }

in point AAAAAAAA:
Windows Identity Check
Name: DOMAIN\fimcorenotify
in point BBBBBBBB:
Windows Identity Check impersonating
Name: DOMAIN\user2

after point BBBBBBB I placed my fim code and got error 
"The caller was not authenticated by the service." :(

impersonated worked fine ..... possibly...

 

PS: my client config contains

<endpoint 
address="http://app21-cof.domain.com:5726/ResourceManagementService/SecurityTokenService/Registration"
        binding="wsHttpContextBinding" bindingConfiguration="ServiceMultipleTokenBinding_SecurityTokenService"
        contract="ISecurityTokenService"
        name="ServiceMultipleTokenBinding_SecurityTokenService">
        <identity>
          <userPrincipalName value="DOMAIN\FIMService"/>
        </identity>
      </endpoint>