microsoft Archives - Tales of a Code Monkey https://cymbeline.ch/tag/microsoft/ ... the adventures of a guy making software. Sat, 22 Feb 2014 20:55:33 +0000 en-US hourly 1 https://wordpress.org/?v=5.9.3 Managed debugging with WinDbg https://cymbeline.ch/2010/03/29/managed-debugging-with-windbg/?utm_source=rss&utm_medium=rss&utm_campaign=managed-debugging-with-windbg Mon, 29 Mar 2010 18:43:00 +0000 /post/2010/03/29/Managed-debugging-with-WinDbg.aspx This is surely no surprise for many people: you can actually debug managed code with WinDbg (or CDB, if you prefer that one) by using the SOS extension (that ships with the .Net framework) in the debugger. What’s new is that today, Microsoft released Psscor2 which is pretty much an enhanced version of SOS. Now I’m … Continue reading "Managed debugging with WinDbg"

The post Managed debugging with WinDbg appeared first on Tales of a Code Monkey.

]]>
This is surely no surprise for many people: you can actually debug managed code with WinDbg (or CDB, if you prefer that one) by using the SOS extension (that ships with the .Net framework) in the debugger. What’s new is that today, Microsoft released Psscor2 which is pretty much an enhanced version of SOS. Now I’m sure that you’ll soon find much information on using Psscor2 on Tom’s blog. I’m much in favor of Psscor2 myself and therefore I’ll try to post useful scripts here as well (if I can come up with some ;-)).

The post Managed debugging with WinDbg appeared first on Tales of a Code Monkey.

]]>
You’ve got to restart that process … https://cymbeline.ch/2010/02/07/youve-got-to-restart-that-process/?utm_source=rss&utm_medium=rss&utm_campaign=youve-got-to-restart-that-process Sat, 06 Feb 2010 23:42:00 +0000 /post/2010/02/07/Youve-got-to-restart-that-process-.aspx … in order to pick up the changes you’ve recently made to the environment variables. Alright, to cut a long story short, I’m using the Microsoft Natural Ergonomic Keyboard at work. It has these (5) ‘My Favorits’ keys on top to which you can assign custom commands. I typically make heavy use of that to … Continue reading "You’ve got to restart that process …"

The post You’ve got to restart that process … appeared first on Tales of a Code Monkey.

]]>
… in order to pick up the changes you’ve recently made to the environment variables.

Alright, to cut a long story short, I’m using the Microsoft Natural Ergonomic Keyboard at work. It has these (5) ‘My Favorits’ keys on top to which you can assign custom commands. I typically make heavy use of that to

  1. Start a new console for the build environment
  2. Start PowerShell
  3. Start a lightweight editor with syntax hightlighting for many file types
  4. Start PaintDotNet
  5. Start a new (blank) console

I typically use #5 for checking out small things and starting remote user debugging sessions. From time to time, I tweak the environment a little bit by adding new environment variables or modifying existing ones (like e.g. adding new places to the %PATH% environment variable). To verify, I use the command line. Except that existing processes don’t automatically get the new variables. I was recently tricked by this, since I pressed #5 to get a new console and test the changes. Nothing there. Hmmm. When I start %COMSPEC% they are there. So why not with #5?

Well, the process which starts #5 was started when the changes to the environment variables where not yet made. That’s why also the processes started from this process don’t have the changes. So all I’ve got to do in this case is to kill itype.exe and then start it right away again. Now it works!

The post You’ve got to restart that process … appeared first on Tales of a Code Monkey.

]]>
PowerShell Script to trace OCS Components https://cymbeline.ch/2009/12/11/powershell-script-to-trace-ocs-components/?utm_source=rss&utm_medium=rss&utm_campaign=powershell-script-to-trace-ocs-components Fri, 11 Dec 2009 13:13:00 +0000 /post/2009/12/11/PowerShell-Script-to-trace-OCS-Components.aspx Tracing OCS components may be vital in troubleshooting various issues you may face in your deployment. On machines where you have OCS components installed, you’ll typically find a tool called OCSLogger.exe which allows you to start/stop/view traces of OCS components. However, sometimes this is not enough, for instance when you see problems at the startup … Continue reading "PowerShell Script to trace OCS Components"

The post PowerShell Script to trace OCS Components appeared first on Tales of a Code Monkey.

]]>
Tracing OCS components may be vital in troubleshooting various issues you may face in your deployment. On machines where you have OCS components installed, you’ll typically find a tool called OCSLogger.exe which allows you to start/stop/view traces of OCS components. However, sometimes this is not enough, for instance when you see problems at the startup of a machine. It’s kind of hard to run the GUI if you cannot logon yet. But you can typically run a scheduled task. Or maybe you are — just like me — more like the console guy and thus want to have a script/cmdline tool for everything.

Let’s start with the config file used by the script (TraceConfig.xml) which defines the components you want to trace, to what level the traces are supposed to be and some more things. The sample given here traces mostly the components which are useful in troubleshooting issues related to the Response Group Service of OCS.

<?xml version="1.0" encoding="utf-8"?>
<Config>
    <!--
        Levels:
            TL_FATAL        1
            TL_ERROR        2
            TL_WARN         3
            TL_INFO         4
            TL_VERBOSE      5
            TL_NOISE        6

        Flags:
            TF_COMPONENT    0x00000001
            TF_PROTOCOL     0x00000002
            TF_CONNECTION   0x00000004
            TF_SECURITY     0x00000008
            TF_DIAG         0x00000010
            TF_AUTH         0x00000020
            TF_PARSE        0x00000040
            TF_NETWORK      0x00000080
            TF_STACKTRACE   0x00000100
    -->
    <Default Level="6" Flags="0xffff" />
    <Paths Tracer="C:\Program Files\Common Files\Microsoft Communications Server 2007 R2\Tracing"
           Etl="D:\Tracing"
           Log="D:\Tracing"
           TmfSearchPath="C:\Program Files\Common Files\Microsoft Communications Server 2007 R2\Tracing">
    </Paths>
    <Components>
        <Component Name="LcsWMI" Enabled="no" />
        <Component Name="LcsWMIUserServices" Enabled="no" />

        <Component Name="PowerShell" Enabled="yes" />

        <Component Name="ApplicationServer" Enabled="yes" />

        <Component Name="RgsClientsLib" Enabled="yes" />
        <Component Name="RgsCommonLibrary" Enabled="yes" />
        <Component Name="RgsDatastores" Enabled="yes" />
        <Component Name="RgsDeploymentApi" Enabled="yes" />
        <Component Name="RgsDeploymentLibrary" Enabled="yes" />
        <Component Name="RgsDiagnostics" Enabled="yes" />
        <Component Name="RgsHostingFramework" Enabled="yes" />
        <Component Name="RgsMatchMakingService" Enabled="yes" />
    </Components>
</Config>

I added the most importan trace levels and flags in the comment. Right now, the Default element defines the levels and flags for all components, but there’s no reason why you shouldn’t be able to do that per component you want to trace.

The PS1 script itself (Tracer.ps1) heavily relies on the OcsTracer.exe tool which also comes with OCS and is typically installed in the same place as OcsLogger.exe. It has four main actions:

  1. Start tracing components
  2. Stop tracing components and format the traces
  3. Format traces of ETL files (e.g. from a different machine)
  4. Show the configuration details from a particular config XML file
<#
.SYNOPSIS
        Starts or Stops tracing of Office Communications Server components.
.DESCRIPTION
        Starts or Stops tracing of Office Communications Server components.
.PARAMETER Action
        The action to perform. Must be one of 'Start', 'Stop', 'Config' or
        'Format'.
.PARAMETER ConfigPath
        The path to the configuration XML file. If not specified,
        "TraceConfig.xml" is used.
.LINK
        This script was originally posted to
        http://www.cymbeline.ch/post/2009/12/11/PowerShell-Script-to-trace-OCS-Components.aspx
.EXAMPLE
        .\Tracer.ps1 Start

        Starts tracing all the enabled components from the "TraceConfig.xml" file.
.EXAMPLE
        .\Tracer.ps1 Stop

        Stops tracing all the enabled components from the "TraceConfig.xml" file
        and formats the traces.
.EXAMPLE
        .\Tracer.ps1 Format "MyOtherConfig.xml"

        Formats the traces of the enabled components from the "MyOtherConfig.xml"
        file with all the settings from the "MyOtherConfig.xml" file.
.EXAMPLE
        .\Tracer.ps1 Config

        Shows the configuration of the "TraceConfig.xml" file.
#>
param(
    [Parameter(Mandatory=$true)]
    [ValidateSet("Start", "Stop", "Config", "Format", IgnoreCase=$true)]
    [String] $Action,
    [String] $ConfigPath = "TraceConfig.xml"
)

$configXml = ((Get-Content $ConfigPath))
$tracerPath = $configXml.Config.Paths.Tracer
$etlDir = $configXml.Config.Paths.Etl
$logDir = $configXml.Config.Paths.Log
$tmfSearchPath = $configXml.Config.Paths.TmfSearchPath

# Construct the parameters for the 'Start' command to OcsTracer.exe
function getStartParams()
{
    $ret = @()

    $configXml.Config.Components.Component |
        ? {$_.Enabled -eq "yes"} |
        foreach {
            $ret = $ret +
                ("/Component:" + $_.Name + "," + $configXml.Config.Default.Level +
                    "," + $configXml.Config.Default.Flags + " ")
        }

    return $ret
}

# Construct the parameters for the 'Stop' command to OcsTracer.exe
function getStopParams()
{
    $ret = @()

    $configXml.Config.Components.Component |
        ? {$_.Enabled -eq "yes"} |
        foreach { $ret = $ret + ("/Component:" + $_.Name) }

    return $ret
}

# Format the ETL files for enabled components to a human readable format
function formatFiles(
    [Parameter(Mandatory=$true)]
    [String] $Timestamp
)
{
    md $logDir\$timestamp -ea silentlycontinue | Out-Null

    $configXml.Config.Components.Component |
        ? {$_.Enabled -eq "yes"} |
        foreach {
            $etlFile = $_.Name + ".etl";

            if (Test-Path $etlFile)
            {
                $logFile = $Timestamp + "\" + $Timestamp + "_" + $_.Name + ".log";

                & "$tracerPath\OcsTracer.exe" Format /LogFilePath:"$etlDir\$etlFile" /OutputFile:"$logDir\$logFile" /TmfSearchPath:"$tmfSearchPath" | Write-Verbose
            }
            else
            {
                Write-Warning "File $etlFile not found.";
            }
        }
}

Write-Host "Using Config File: $ConfigPath"
$timestamp = Get-Date -format "yyyy-MM-dd_HH.mm.ss"

if ($Action -eq "start")
{
    Write-Host "Removing all .etl files ..."
    ls $etlDir *.etl | ri

    Write-Host "Start tracing components ..."
    $params = getStartParams

    & "$tracerPath\OcsTracer.exe" Start $params /LogFileFolder:"$etlDir" | Write-Verbose
}
elseif ($Action -eq "stop")
{
    Write-Host "Stop tracing components ..."
    $params = getStopParams

    md $logDir\$timestamp | Out-Null

    & "$tracerPath\OcsTracer.exe" Stop $params /OutputFile:"$logDir\$timestamp\$($timestamp)_All.log" /TmfSearchPath:"$tmfSearchPath" | Write-Verbose

    if (!$?)
    {
        rd $logDir\$timestamp | Out-Null
    }
    else
    {
        Write-Host "Sessions stopped. Start formatting ..."
        formatFiles $timestamp
    }
}
elseif ($Action -eq "format")
{
    Write-Host "Formatting traces from ETL files ..."
    formatFiles $timestamp
}
elseif ($Action -eq "config")
{
    Write-Host "Default values"
    Write-Host "--------------"
    $configXml.Config.Default | ft Level,Flags

    Write-Host "Paths"
    Write-Host "-----"
    $configXml.Config.Paths | fl

    Write-Host "Components"
    Write-Host "----------"
    $configXml.Config.Components.Component | ft Name,Enabled
}
else
{
    Write-Error "Unknown action."
}

For samples on how to run the script, please run man .\Tracer.ps1 -Examples
Have fun 🙂

The post PowerShell Script to trace OCS Components appeared first on Tales of a Code Monkey.

]]>
Finding the Private Key File of Certificates https://cymbeline.ch/2009/11/30/finding-the-private-key-file-of-certificates/?utm_source=rss&utm_medium=rss&utm_campaign=finding-the-private-key-file-of-certificates Mon, 30 Nov 2009 18:54:00 +0000 /post/2009/11/30/Finding-the-Private-Key-File-of-Certificates.aspx At work, I have created multiple tools which we used to analyse and fix issues related to certificates that we use with Office Communications Server and their respective private key files. To summarize, a user/service who wants to use the certificate for authentication needs to have read access on the private key. If it doesn’t, … Continue reading "Finding the Private Key File of Certificates"

The post Finding the Private Key File of Certificates appeared first on Tales of a Code Monkey.

]]>
At work, I have created multiple tools which we used to analyse and fix issues related to certificates that we use with Office Communications Server and their respective private key files. To summarize, a user/service who wants to use the certificate for authentication needs to have read access on the private key. If it doesn’t, you’ll typically see a strange error which many people don’t relate to missing ACLs on the private key file.

Now these days, you don’t need to write such tools anymore. PowerShell allows you to pretty much do everything you need in this area. Let’s look at the following PS script (let’s call it FindPrivateKey.ps1) which accepts a parameter of type System.Security.Cryptography.X509Certificates.X509Certificate2, i.e. a reference to the certificate you want to analyze.

param(
    [Parameter(Mandatory=$true)]
    [System.Security.Cryptography.X509Certificates.X509Certificate2]
    $Certificate
)

echo "Looking for private key file of certificate"
echo $Certificate
echo ""
echo "The private key file is '$($Certificate.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName)'"
echo ""

$file = ls $env:userprofile -Filter $Certificate.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName -Recurse -Force -EA SilentlyContinue
echo "It is located at '$($file.FullName)'."

I guess I do not need to mention that you can easily find the certificate you’re interested in by running something like

pushd cert:\CurrentUser\My
ls
$cert = gci 0DAC31905AEB722D8561BFAF3F3BFD2F551AA197
popd
.\FindPrivateKey.ps1 $cert

where ‘0DAC31905AEB722D8561BFAF3F3BFD2F551AA197’ is simply the thumbprint of the certificate we’re interested in. From here on it should be easy to check what ACLs the file has (run $file.GetAccessControl()) and to modify them (run $file.SetAccessControl()).

The post Finding the Private Key File of Certificates appeared first on Tales of a Code Monkey.

]]>
Windows Authentication through Forms https://cymbeline.ch/2009/10/11/windows-authentication-through-forms/?utm_source=rss&utm_medium=rss&utm_campaign=windows-authentication-through-forms Sun, 11 Oct 2009 11:36:00 +0000 /post/2009/10/11/Windows-Authentication-through-Forms.aspx Let’s assume you have a web site which is exposed to the internet (i.e. to the public) but the site itself works with windows accounts internally. If you have problems visualizing this scenario, think about a web mail interface for a mail server system which offers mail services for user accounts in active directory. Users … Continue reading "Windows Authentication through Forms"

The post Windows Authentication through Forms appeared first on Tales of a Code Monkey.

]]>
Let’s assume you have a web site which is exposed to the internet (i.e. to the public) but the site itself works with windows accounts internally. If you have problems visualizing this scenario, think about a web mail interface for a mail server system which offers mail services for user accounts in active directory. Users inside your corporate network can use integrated Windows authentication to access this site, so they don’t really have a problem. But when they want to access the interface from outside the corporate network, or from a machine/device which doesn’t understand integrated Windows authentication, you’ll realize that it doesn’t just work like that.

So you’ll want to do authentication based on the credentials the user enters in a simple form on a web page. The challenge now is that the rest of the site (i.e. apart from the one form which does the initial authentication) will likely still work with the HttpContext.Current.User property to determine which user is currently authenticated and provide actions and data based on that identity, because you don’t want to re-implement the entire logic. The good news is, you can do that! The bad news is, you don’t just get it for free. But that’s why you came here, and that’s why I’ll try to help you with this.

Let’s first arrange a few things on the web site facing the public — you don’t necessarily need to change this on your internal site. First, the login page will need to be accessible for anyone, i.e. anonymous access must be turned on on the site. Second, from ASP.net’s point of view, all the pages which require the user to be authenticated should be in/under the same directory. If you don’t want to do this, you’ll simply have to add a element for all pages requiring authentication in your web.config or do the opposite and add all pages which don’t require authentication in the same way. With the directories however, the basic structure can be as simple as

WebSiteRoot/
+--Default.aspx        Could redirect to /ActualSite/Default.aspx
+--ActualSite/
   +--Default.aspx     The actual home page with all its logic
+--Auth/
   +--Login.aspx       The login page

So all the logic you had in the site’s root directly would be under ActualSite or whatever name you chose.

Updating Web.config

Now for the above scenario with different directories, the web.config in the site’s root could look as follows. Please note that for the pages under ActualSite we are simply disabling anonymous access, while for everything else, we allow it. Also, in the authentication element we’re setting the mode to None because we’re not going to use any predefined authentication mechanism as is.

<configuration>
    <location path="ActualSite">
        <system.web>
            <authorization>
                <deny users="?"/>
            </authorization>
        </system.web>
    </location>

    <system.web>
        <authentication mode="None">
            <forms loginUrl="~/Auth/Login.aspx" defaultUrl="~/ActualSite/Default.aspx" />
        </authentication>

        <authorization>
            <allow users="*" />
        </authorization>
    </system.web>
</configuration>

But of course that’s not all yet. You’ll see that if now you wanted to look at the actual site, you’ll get a 401 because you’re not authenticated. So let’s take a look at that.

Validating credentials

Next, let’s create the login page which the user will use to enter his credentials. Fortunately, ASP.net offers the Login control which does almost everything we need. So add that one to your login page plus add an event handler for the OnAuthenticate event of that control. Alternatively, you can also derive your own control from the Login control and override the OnAuthenticate method. This event handler is where we’ll do our custom logic to check that the credentials really map to an existing Windows user. Below’s the code which does that.

protected override void OnAuthenticate(AuthenticateEventArgs args)
{
    string[] parts = UserName.Split('\\');
    string password = Password;
    string username;
    string domain = null;

    args.Authenticated = false;

    if (parts.Length == 1)
    {
        username = parts[0];
    }
    else if (parts.Length == 2)
    {
        domain = parts[0];
        username = parts[1];
    }
    else
    {
        return;
    }

    if (WebAuthenticationModule.AuthenticateUser(username, domain, password))
    {
        string userData = String.Format("{0}\n{1}\n{2}",
            username, domain, password);

        FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
            2                           /* version */,
            username,
            DateTime.Now                /* issueDate */,
            DateTime.Now.AddMinutes(30) /* expiration */,
            true                        /* isPersistent */,
            userData,
            FormsAuthentication.FormsCookiePath);

        HttpCookie ticketCookie = new HttpCookie(
            FormsAuthentication.FormsCookieName,
            FormsAuthentication.Encrypt(ticket));

        Context.Response.Cookies.Add(ticketCookie);

        Context.Response.Redirect(
            FormsAuthentication.GetRedirectUrl(username, false), false);
    }
}

The AuthenticateUser method from WebAuthenticationModule is a wrapper on the LogonUser function from the Win32 API which will return true if the user could be logged on. So if the credentials are valid, we’re going to pass them in to the FormsAuthenticationTicket in the UserData property so later on, we’ll be able to use them again. At least, we don’t want the consumers of the site have to enter credentials for every request their making, right? Also, we’re encrypting the entire ticket because we’re going to send it over the wire. The Encrypt method from the FormsAuthentication class does this. However, you’ll have to make sure that the protection attribute of the forms element in the web.config is set to All which actually is the default (but it can be inherited, so watch out!).

What you see is that we’re heavily using the functionality offered by the FormsAuthentication class and related classes to handle the tickets, encryption, settings, etc. This is not mandatory but it helps a lot. Plus it’s better anyway than coming with your own ticketing and encryption mechanisms; unless you have a degree in maths and/or cryptography, chances are that that’s not so secure as you think it is.

Authenticating users

Then, we need to authenticate the user for all the requests he makes after providing the credentials. Thus, we need to add some custom logic to the AuthenticateRequest event of the HttpApplication. There’s multiple ways to do that:

  • Add a file called global.asax to your site’s root
  • Create and register a new HttpModule by implementing the System.Web.IHttpModule interface and adding an entry in the httpModules section in your root’s web.config

Personally, I like the approach with the custom module more, but adding this stuff to the global.asax can be done a little bit faster. In either case, make sure you can handle the AuthenticateRequest event. My code proposal for the handler is given below. I’m intentionally omitting most error handling code here.

private static void OnAuthenticateRequest(object sender, EventArgs args)
{
    HttpApplication application = sender as HttpApplication;

    HttpContext context = application.Context;
    HttpRequest request = context.Request;
    HttpResponse response = context.Response;

    if (!request.IsAuthenticated &amp;&amp;
        !context.SkipAuthorization)
    {
        if (request.CurrentExecutionFilePath.Equals(FormsAuthentication.LoginUrl,
                                                    StringComparison.OrdinalIgnoreCase) ||
            request.CurrentExecutionFilePath.EndsWith(".axd"))
        {
            context.SkipAuthorization = true;
        }
        else
        {
            HttpCookie cookie = request.Cookies.Get(FormsAuthentication.FormsCookieName);
            FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);

            if (!ticket.Expired)
            {
                IntPtr hToken = LogonUserFromTicket(ticket);

                WindowsIdentity identity = new WindowsIdentity(
                    hToken, "Win/Forms", WindowsAccountType.Normal, true);

                context.User = new WindowsPrincipal(identity);

                if (FormsAuthentication.SlidingExpiration)
                {
                    ticket = FormsAuthentication.RenewTicketIfOld(ticket);
                    cookie.Value = FormsAuthentication.Encrypt(ticket);
                    response.Cookies.Set(cookie);
                }

                return;
            }

            FormsAuthentication.RedirectToLoginPage();
        }
    }
}

So let’s go through the important things here. First we check (lines 9/10) if the request is already authenticated or authorization is to be skipped completely. If either of those is true, we’re out of the picture already. Else it’s our job to do the authentication. Lines 12-17 are used to prevent authentication on the login page as well as the *.axd handlers, which are typically used to return resources like scripts for ASP.net components. Lines 20-42 do the actual authentication: we first retrieve the cookie from the previous credential validation and decrypt it to get the ticket. If the ticket has not expired, we log the user on (the call to LogonUserFromTicket is again only a wrapper for the LogonUser function from the Win32 API; it uses the data from the UserData property of the ticket) to get the logon token which we’ll pass in to the WindowsIdentity constructor to get the WindowsIdentity object all the code in ActualSite will use to determine which user is making the request. Then of course we need to update the context with the new identity. If sliding expiration is turned on in the web.config, we renew the ticket. And if the ticket has expired, of course we don’t authenticate the user but instead we redirect him to the login page.

Cleanup

Finally, we still have a tiny problem here. We have used the LogonUser function, but according to its documentation, we should call the CloseHandle function from the Win32 API. For the method AuthenticateUser I have already done that; there we don’t need the token/handle anymore when we have verified the credentials. But what about the authentication we’re doing in AuthenticateRequest? We’re setting the newly created WindowsIdentity (which is here used to represent the user token) to the current HttpContext because we’ll need it there to ultimately handle the request. But once the request handler is done, we don’t need it anymore. Luckily, there’s also an event for this purpose. It’s the last event that there is and it’s called EndRequest. So let’s add the following code in the handler for that event.

private static void OnEndRequest(object sender, EventArgs args)
{
    HttpApplication application = sender as HttpApplication;

    HttpContext context = application.Context;

    if (null != context.User)
    {
        WindowsIdentity identity = context.User.Identity as WindowsIdentity;

        if (null != identity)
        {
            NativeAuth.CloseHandle(identity.Token);
        }
    }
}

Basically all it does, if the request was given a WindowsIdentity, it’ll call the CloseHandle function from the Win32 API on that identity’s token handle. That should do the trick and we shouldn’t leak handles anymore.

Summary

I have shown here how you can make use of the forms authentication mechanisms which come with ASP.net to do Windows authentication behind the scenes. This can be very useful in cases when not all users have access to systems which know how to do Windows authentication.

Please note that I do not claim that this solution is going to work for every challenge you may be facing. The solution shown here is neither claimed to be complete nor suitable for every web application.

The post Windows Authentication through Forms appeared first on Tales of a Code Monkey.

]]>
Troubleshooting Authentication Issues with RGS Agent Tab https://cymbeline.ch/2009/07/29/troubleshooting-authentication-issues-with-rgs-agent-tab/?utm_source=rss&utm_medium=rss&utm_campaign=troubleshooting-authentication-issues-with-rgs-agent-tab Wed, 29 Jul 2009 19:08:00 +0000 /post/2009/07/29/Troubleshooting-Authentication-Issues-with-RGS-Agent-Tab.aspx Sometimes – especially in lab environments – you’ll see issues around user authentication with the RGS Agent Tab of Office Communications Server 2007 R2. This post should help you in determining what could be the issue and how to work around it. First of all, when the OCS 2007 R2 WebComponents get installed on a … Continue reading "Troubleshooting Authentication Issues with RGS Agent Tab"

The post Troubleshooting Authentication Issues with RGS Agent Tab appeared first on Tales of a Code Monkey.

]]>
Sometimes – especially in lab environments – you’ll see issues around user authentication with the RGS Agent Tab of Office Communications Server 2007 R2. This post should help you in determining what could be the issue and how to work around it.

First of all, when the OCS 2007 R2 WebComponents get installed on a machine, by default Integrated Windows Authenticated (IWA) for the RGS parts of WebComponents are enabled. We don’t require IWA, but this is the recommended setting; anything but Anonymous Authentication should work. If Anonymous Authentication is set for the RGS virtual directory in IIS, you’ll find a warning in NT event log about that. In that case, you should turn back on authentication for the virtual directory.

Another problem I’ve seen a couple of times was as follows: Agent A’s credentials are used to sign in with Office Communicator, but the Agent Tab in OC shows the RGS Agent Group memberships of Agent B, or it shows that the “Current User is not an Agent”. In this case, you should start tracing the RgsClientsLib component and either wait until the Agent Tab in OC refreshes automatically (this should happen within 30 – 60 seconds) or you can open the tab URL in IE; it’s typically something like https://pool-1.contoso.com/Rgs/Clients/Tab.aspx. Then, stop tracing and check out the captured traces for RgsClientsLib. You should now find something along the lines of

Authentication type: [Negotiate]
Authenticated user: [CONTOSO\AgentB]
Authenticated user's SID: [S-1-5-21-2278291046-1170081271-1450921830-1285]
Authenticated user's SID maps to: [efa2cabd-462c-49e4-a021-4dd71bd97ce4]

Please note that I left out the less important information like timestamps etc. here. What you see is that instead of AgentA, AgentB is being authenticated. Usually, this happens when the credentials you pass in to OC are different from the credentials you used to log in to Windows. OC uses the IE engine to render the tabs and thus also leaves the authentication for IE. Then, IE performs the authentication based on the “User Authentication” / “Logon” settings for the zone the Agent Tab is in. The default setting for the “Local Intranet Zone” in IE is to automatically try loggin on with the current user’s credentials – i.e. AgentB’s credentials in this case, because AgentB is the currently logged on (Windows) user. Only if authentication for this user fails, IE is going to prompt you for a different set of credentials. To change this behavior, you can set the security settings in IE accordingly:

IeSecurityZoneUserAuth

Setting it to “Prompt for user name and password” will always prompt you for sites in the intranet zone. Once you’ve done that, exit OC and start it again. Now you should be prompted for the credentials to the Agent Tab and you can provide AgentA’s credentials. You then should see the correct list of groups AgentA is a member of.

The post Troubleshooting Authentication Issues with RGS Agent Tab appeared first on Tales of a Code Monkey.

]]>
Windows 7 to RTM https://cymbeline.ch/2009/07/23/windows-7-to-rtm/?utm_source=rss&utm_medium=rss&utm_campaign=windows-7-to-rtm Thu, 23 Jul 2009 07:24:00 +0000 /post/2009/07/23/Windows-7-to-RTM.aspx Microsoft has announced yesterday the release of Windows 7 (and Windows Server 2008 R2) to manufacturing, Windows 7 will be available on October 22nd. And just to show you how much fun we have when we RTM a product, check out the Windows 7 RTM Sign-off video on YouTube …

The post Windows 7 to RTM appeared first on Tales of a Code Monkey.

]]>
Microsoft has announced yesterday the release of Windows 7 (and Windows Server 2008 R2) to manufacturing, Windows 7 will be available on October 22nd. And just to show you how much fun we have when we RTM a product, check out the Windows 7 RTM Sign-off video on YouTube …

The post Windows 7 to RTM appeared first on Tales of a Code Monkey.

]]>
Agent Communications Panel for Microsoft Dynamics CRM 4.0 Released https://cymbeline.ch/2009/04/13/agent-communications-panel-for-microsoft-dynamics-crm-4-0-released/?utm_source=rss&utm_medium=rss&utm_campaign=agent-communications-panel-for-microsoft-dynamics-crm-4-0-released Mon, 13 Apr 2009 08:29:00 +0000 /post/2009/04/13/Agent-Communications-Panel-for-Microsoft-Dynamics-CRM-40-Released.aspx Last week the Agent Communications Panel for Microsoft Dynamics CRM 4.0 has been released and is now available from the Microsoft Download Center. It allows a certain amount of customization such as integration with the Response Group Service of Office Communications Server 2007 R2. That said, it uses the Agent WebService of RGS to determine … Continue reading "Agent Communications Panel for Microsoft Dynamics CRM 4.0 Released"

The post Agent Communications Panel for Microsoft Dynamics CRM 4.0 Released appeared first on Tales of a Code Monkey.

]]>
Last week the Agent Communications Panel for Microsoft Dynamics CRM 4.0 has been released and is now available from the Microsoft Download Center. It allows a certain amount of customization such as integration with the Response Group Service of Office Communications Server 2007 R2. That said, it uses the Agent WebService of RGS to determine the agent group memberships. And given the title, it of course also integrates with Dynamics CRM 4.0.

The post Agent Communications Panel for Microsoft Dynamics CRM 4.0 Released appeared first on Tales of a Code Monkey.

]]>
Using the Agent WebService of the Response Group Service https://cymbeline.ch/2009/03/19/using-the-agent-webservice-of-the-response-group-service/?utm_source=rss&utm_medium=rss&utm_campaign=using-the-agent-webservice-of-the-response-group-service Thu, 19 Mar 2009 22:25:00 +0000 /post/2009/03/19/Using-the-Agent-WebService-of-the-Response-Group-Service.aspx Your Office Communications Server 2007 R2, Response Group Service deployment comes with a tiny but nice little addition: the Agent WebService. It basically offers exactly the same data and functionality as the Agent OC tab but does this through a SOAP interface. If you have RGS deployed on a pool with the FQDN ‘ocs-pool-01.contoso.com’, you’ll … Continue reading "Using the Agent WebService of the Response Group Service"

The post Using the Agent WebService of the Response Group Service appeared first on Tales of a Code Monkey.

]]>
Your Office Communications Server 2007 R2, Response Group Service deployment comes with a tiny but nice little addition: the Agent WebService. It basically offers exactly the same data and functionality as the Agent OC tab but does this through a SOAP interface. If you have RGS deployed on a pool with the FQDN ‘ocs-pool-01.contoso.com’, you’ll find the Agent OC tab at https://ocs-pool-01.contoso.com/Rgs/Clients/Tab.aspx. The Agent WebService is then located at https://ocs-pool-01.contoso.com/Rgs/Clients/ProxyService.asmx. If you are running the OCS WebComponents on other machines than the front ends, then the host name is the FQDN of the WebComponents machine/farm. So here’s how you can write your own client to sign in/out with RGS Agent Groups. The TechNet article Deploying Response Group Clients gives more information about deploying RGS Clients, with focus on the Agent OC Tab.

Generating the Client Proxy

The first thing you typically want to do is to actually generate the proxy code which you will compile into your own client. You do so by calling

wsdl.exe /namespace:RgsAgentService /language:cs /out:RgsAgentService.cs https://ocs-pool-01.contoso.com/Rgs/Clients/ProxyService.asmx?wsdl

This will generate the RgsAgentService.cs file which you can include into your project and use right away. Web Services Description Language Tool (Wsdl.exe) on MSDN has more info on wsdl.exe if needed.

Using the Web Service

Now you’re already good to go and use the web service. The code below shows a sample console application which does the following.

  • Create a new instance of ProxyService (the generated class from the step above) which points to the service on the pool you’re interested in.
  • Query the web service if the current user is an agent or not. This requires you to authenticate with the user’s credentials.
  • If the current user is an agent
    • Determine some basic information (e.g. the name and SIP address of the agent).
    • Retrieve the list of agent groups the agent is a member of in the connected pool.
    • If there are formal agent groups to which the agent is not signed in, the user is asked if he wants to sign in.
      • If he choses to sign in, tries to sign the agent in to those formal groups.
using System;
using System.Collections.Generic;

using RgsAgentService;

namespace RgsClient
{
    class Program
    {
        static void Main(string[] args)
        {
            string poolFqdn = "ocs-pool-01.contoso.com";

            if (args.Length > 0)
            {
                poolFqdn = args[0];
            }

            ProxyService service = ConnectToPool(poolFqdn);

            // First, figure out if the current user is an Agent.
            if (!service.IsAgent())
            {
                Console.WriteLine("You are not an agent in Pool '{0}'.", poolFqdn);
                return;
            }

            // Now get some information about the Agent (i.e. the current User).
            AcdAgent self = service.GetAgent();

            Console.WriteLine("You were authenticated as agent '{0}' ('{1}') in pool '{2}'.",
                self.DisplayName, self.SipAddress, poolFqdn);
            Console.WriteLine();

            // Finally, determine which Agent Groups this Agent belongs to.
            AcdGroup[] agentGroups = service.GetGroups();

            Console.WriteLine("Agent Group Name                  Formal?   Signed In?  # Agents");
            Console.WriteLine("----------------------------------------------------------------");

            Dictionary<string, Guid> agentGroupIdsForSignIn = new Dictionary<string, Guid>();

            for (int i = 0; i < agentGroups.Length; i++)
            {
                Console.WriteLine("{0,-32}  {1,-8}  {2,-10}  {3,8}",
                    agentGroups[i].Name, agentGroups[i].CanSignIn,
                    agentGroups[i].IsSignedIn, agentGroups[i].NumberOfAgents);

                if (agentGroups[i].CanSignIn &&
                    !agentGroups[i].IsSignedIn)
                {
                    agentGroupIdsForSignIn.Add(agentGroups[i].Name, agentGroups[i].Id);
                }
            }

            // If the Agent is not signed in to all his formal groups, then offer
            // him to do so now.
            if (agentGroupIdsForSignIn.Count > 0)
            {
                Console.WriteLine();
                Console.WriteLine("You are not currently signed in to {0} agent group(s):",
                    agentGroupIdsForSignIn.Count);

                foreach (string agentGroupName in agentGroupIdsForSignIn.Keys)
                {
                    Console.WriteLine("    {0}", agentGroupName);
                }

                Console.WriteLine();
                Console.Write("Do you want to sign in to these groups now? [y/n] ");

                ConsoleKeyInfo key = Console.ReadKey();
                while (key.KeyChar != 'y' && key.KeyChar != 'n')
                {
                    key = Console.ReadKey();
                }

                if (key.KeyChar == 'n')
                {
                    return;
                }

                Console.WriteLine();

                if (service.SignInMultiple(agentGroupIdsForSignIn.Values.ToArray()))
                {
                    Console.WriteLine("You have successfully signed in.");
                }
                else
                {
                    Console.WriteLine("Sign-in to at leat one agent group has failed.");
                }
            }
        }

        private static ProxyService ConnectToPool(string poolFqdn)
        {
            ProxyService service = new ProxyService();

            service.Url = String.Format("https://{0}/Rgs/Clients/ProxyService.asmx", poolFqdn);
            service.UseDefaultCredentials = true;

            return service;
        }
    }
}

Running this program will yield something similar to the following.

You were authenticated as agent 'Bob' ('bob@contoso.com') in pool 'rgs-pool-01.contoso.com'.

Agent Group Name                  Formal?   Signed In?  # Agents
----------------------------------------------------------------
Payroll Questions                 False     True               5
General HR Questions              True      False              8

You are not currently signed in to 1 agent group(s):
    General HR Questions

Do you want to sign in to these groups now? [y/n] y
You have successfully signed in.

More Methods

The WebService has a few more methods. These are in particular

  • SignIn(Guid groupId) – Tries to sign the current agent in to the agent group with the given ID
  • SignOut(Guid groupId) – Tries to sign the current agent out of the agent group with the given ID
  • SignOutMultiple(Guid[] groupIds) – Tries to sign the current agent out of all the groups identified with their respective IDs

These methods all return a boolean which indicates success (true) or failure (false).

Summary

You can integrate the RGS Agent Services into your own application by using the corresponding web service which is installed with the Response Group Service. This allows you to offer the same information and functionality as the RGS Agent OC Tab in a customizable manner.

The post Using the Agent WebService of the Response Group Service appeared first on Tales of a Code Monkey.

]]>
User Search in AD https://cymbeline.ch/2009/02/10/user-search-in-ad/?utm_source=rss&utm_medium=rss&utm_campaign=user-search-in-ad Tue, 10 Feb 2009 22:16:00 +0000 /post/2009/02/10/User-Search-in-AD.aspx I stumbled upon the System.DirectoryServices.AccountManagement namespace this week. It was introduced with .Net 3.5 and offers functionality to perform queries on AD objects like users, groups and computers in a more comfortable way than through the DirectorySearcher class from the System.DirectoryServices namespace. To illustrate the ease of using these classes, I came up with a … Continue reading "User Search in AD"

The post User Search in AD appeared first on Tales of a Code Monkey.

]]>
I stumbled upon the System.DirectoryServices.AccountManagement namespace this week. It was introduced with .Net 3.5 and offers functionality to perform queries on AD objects like users, groups and computers in a more comfortable way than through the DirectorySearcher class from the System.DirectoryServices namespace. To illustrate the ease of using these classes, I came up with a tiny example which lists all users whose account name (the samAccountName attribute in AD) starts with an 'a'. On top of this, using LINQ it is quite simple to convert the resulting PrincipalSearchResult<Principal> collection into an IEnumerable<UserPrincipal>.

using System;
using System.Collections.Generic;
using System.DirectoryServices.AccountManagement;
using System.Linq;

namespace UserSearch
{
    class Program
    {
        static void Main(string[] args)
        {
            PrincipalContext context = new PrincipalContext(ContextType.Domain, "contoso.com");

            UserPrincipal searchFilter = new UserPrincipal(context);
            searchFilter.SamAccountName = "a*";

            PrincipalSearcher ps = new PrincipalSearcher(searchFilter);

            IEnumerable<UserPrincipal> results = from principal in ps.FindAll()
                                                 where principal is UserPrincipal
                                                 select principal as UserPrincipal;

            foreach (UserPrincipal user in results)
            {
                Console.WriteLine("User '{0}' ({1}) Info:", user.SamAccountName, user.Name);
                Console.WriteLine("    Password Set On  {0}", user.LastPasswordSet);
                Console.WriteLine("    Last Log On      {0}", user.LastLogon);
                Console.WriteLine();
            }
        }
    }
}

The post User Search in AD appeared first on Tales of a Code Monkey.

]]>