Ninja Nichols

The discipline of programming

Passing ADFS Claims to Shibboleth

According to the official Shibboleth documentation (https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPADFS#NativeSPADFS-AttributeHandling) ingesting claims from ADFS into Shibboleth requires adding the following mapping rules to /etc/shibboleth/attribute-map.xml:

<!-- WS-Fed attributes -->
<Attribute nameFormat="http://schemas.xmlsoap.org/claims" name="CommonName" id="cn"/>
<Attribute nameFormat="http://schemas.xmlsoap.org/claims" name="EmailAddress" id="email"/>
<Attribute nameFormat="http://schemas.xmlsoap.org/claims" name="UPN" id="userPrincipalName"/>
<Attribute nameFormat="http://schemas.xmlsoap.org/claims" name="Group" id="group"/>

This is NOT correct. Maybe it worked with older versions, but it doesn’t work at all with Shibboleth 2.4 and ADFS 2.x.

After much digging I stumbled across some interesting entries in the Shibboleth daemon log file /var/log/shibboleth/shibd.log. After each login attempt there were a number entries about unmapped SAML attributes:

2015-01-14 16:00:37 INFO Shibboleth.AttributeExtractor.XML [90]: skipping unmapped SAML 2.0 Attribute with Name: http://schemas.xmlsoap.org/claims/group, Format:urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified

Helpfully that log message tells us the Name and Format of each unmapped attribute. Plugging those values in results in an attribute mapping like this instead:

<Attribute nameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified" name="http://schemas.xmlsoap.org/claims/group" id="group"/>

Why do my custom datetime strings STILL depend on client locale?

C#’s built in culture support is great. Just call ToString() on any DateTime object and it will be converted into a localized string with the appropriate formatting for the client’s locale. Take a look:

Locale Culture DateTime.ToString()
English en 12/15/2014 10:48:51 PM
Arabic ar 23/02/36 10:48:51 م
Chinese zh 2014/12/15 22:48:51
Danish da 15-12-2014 22:48:51
German de 15.12.2014 22:48:51
Hungarian hu 2014.12.15. 22:48:51

Not only is the order different, but we’ve got slashes, dashes and periods. Fortunately it’s all taken care of for us! But while that’s great for the front-end, having the back-end pass around these localized date strings is just asking for trouble.

Of course, the Right Way to format DateTime strings for back-end use is with the Round-trip format specifier (“O”), which creates an unambiguous, Culture-invariant, timezone aware and ISO 8601-complaint string representation (which looks something like 2014-12-15T22:48:51.0000000-05:00).

But alas, not every application was written to emit/accept ISO 8601-formatted datetime strings. And so, dear reader, I now present one of the many pitfalls to doing custom format strings correctly. Here is the gist of some code I encountered recently:

WebService.ExternalCall(someData, timestamp.ToString("MM/dd/yyyy hh:mm:ss"));

Not too bad really, there might be some issues if the app happens to run in a different timezone than the web service, but at least that custom format string will make sure those timestamps are always in the same format. Except it doesn’t. Soon we start finding entries like this 12-15-2014 22:48:51. Dashes?!! WTF, we clearly specified *slashes*!

Turns out the “/” character in a DateTime format string is actually just a placeholder for the localized date separator. It will be replaced by whatever the client set it to. Keep in mind that users are not locked into the regional defaults and are free to customize their own date/time settings.

Windows 7 Region and Language Settings
Don’t assume that your English-speaking users won’t go and change the time format to something unexpected.

To make the “/” to always be a slash character, we have to either escape it or explicitly override the client’s culture (the same goes for the time separator, “:”, although none of the default Cultures/Locales use anything other than “:”):

timestamp.ToString("MM'/'dd'/'yyyy hh':'mm':'ss");

-or-

timestamp.ToString("MM/dd/yyyy hh:mm:ss", DateTimeFormatInfo.InvariantInfo);

Seriously though, just use “O” if at all possible. It’s quick, easy and solves this problem and many others.

timestamp.ToString("O");

Rare Database Entries and Partial Indexes

Today I discovered that PostgreSQL supports partial indexes and I’ll share my scenario for when a partial index has a number of space/speed advantages over a traditional index.

My application consists of many grid-like maps onto which users can place objects. Some objects don’t have to be placed right away; these appear in a staging area, which will become important later. The map creation process is collaborative and one user might make a change, save it and hand it off to another user to complete. Eventually when all the changes have been made, we attempt to finalize the modifications. Part of this process is to ensure that there are no objects left in the staging areas.

The staging area happens to have a special coordinate (-1, -1), so my query becomes:

SELECT object_pk FROM objects WHERE col=-1 AND row=-1;

It’s a simple query and there will typically only ever be a handful of results, but even so it has trouble scaling when there are hundreds of millions of objects in the objects table. We could try adding indexes to the “col” and “row” columns:

CREATE INDEX staging_col_idx ON objects USING btree(col);
CREATE INDEX staging_row_idx ON objects USING btree(row);

But since we only care about one specific lookup (-1, -1), nearly all of the index entries would be completely useless. Preliminary tests showed that it would result in several GBs of unnecessary index records and might slow down inserts/updates too.

This is where the partial index comes in. Partial indexes can be very useful in some specialized scenarios. They provide a way to avoid indexing common “uninteresting” values, thereby reducing the size of the index and speeding up queries that do use the index. Our partial index looks like this:

CREATE INDEX staging_idx ON objects USING btree(col, row)
WHERE col=-1 AND row=-1;

Only entries that satisfy the conditional will appear in the resulting index. I still get the same blazing fast lookups, but now my index will be a tiny fraction of the size and won’t need to be updated for the vast majority of insert or update operations on the table.

Unit Testable System.IO (C#)

It seems the hardest part of writing good unit tests is trying to mock out third-party dependencies.

A surprising number of the core Microsoft libraries like System.IO simply aren’t unit testable, so I was almost unreasonably happy when I stumbled upon System.IO.Abstractions, which describes itself as “Just like System.Web.Abstractions, but for System.IO. Yay for testable IO access!”.

From their documentation:

At the core of the library is IFileSystem and FileSystem. Instead of calling methods like File.ReadAllText directly, use IFileSystem.File.ReadAllText. We have exactly the same API, except that ours is injectable and testable.

Looks like it’s being actively maintained. Just use NuGet to add it your project:

PM> Install-Package System.IO.Abstractions

PowerShell Variable Followed by Colon

I’ve got a little PowerShell script that writes some configuration settings. In it there’s a line that computes a certain URL for a given subdomain:

$url = "http://$subdomain.ninjanichols.com:8080/"

Pretty standard stuff. But something interesting happens when you try to generalize it and make the domain itself variable:

$url = "http://$subdomain.$domain:8080/"

Instead of http://sub.ninjanichols.com:8080/, I get http://sub./. What’s going on?

Turns out the colon has a special meaning in PowerShell, it’s used both to specify items on a PSDrive, $Env:Foo, or to associate a variable with a scope or namespace, $global:var. So when PowerShell tries to interpret $domain:8080 it sees one variable, instead of a variable + a string.

How to fix it? Just make the intention clear using curly brackets:

$url = "http://${subdomain}.${domain}:8080/"

Change ‘fee’ value for ACM Latex Paper

Recently I submitted a paper to an ACM Conference. When I was reviewing the final draft I noticed that the fee value in the copyright block was incorrect. For example, in the block below the fee is automatically set to $5.00 but the Conference I’m submitting to has a fee of $10.00.

Permission to make digital or hard copies of all or part of this work for
personal or classroom use is granted without fee provided that copies are
not made or distributed for profit or commercial advantage and that copies
bear this notice and the full citation on the first page. To copy otherwise, to
republish, to post on servers or to redistribute to lists, requires prior specific
permission and/or a fee.

ICIMCS’11, August 5-7, 2011, Chengdu, Sichuan, China
Copyright 2011 ACM 978-1-4503-0918-9/11/08 ...$5.00.

It looks like this value is hard coded into the sig-alternate.cls file. Just go in and modify it like so:

globalcopyrightetc{Copyright thecopyrtyr ACM theacmcopyr ...$10.00}

Get VIM to use 256-bit color theme

All this time I’ve been stuck using vim in 8-bit color because I thought my terminal emulator couldn’t support any higher. Turns out all I needed to do was let VIM know that I wanted pretty colors.

Step 1: Make sure you have a VIM color scheme that supports 256 color. I like the wombat256 theme.

Step 2: Tell VIM that you want 256 color by adding this line to your ~/.vimrc file:

set t_Co = 256

Step 3 (optional): Depending on your terminal emulator, you may need to also add this line to your ~/.bashrc file:

export TERM="xterm-256color"

Controlling Banshee and RadioTray via DBus

I recently began using OpenBox as my window manager in Linux. One issue I ran into was that my media players were unable to automatically bind to the global media hotkeys. My solution was to write a little bash script to control the media players using DBus. Right now I have support for Banshee and RadioTray. VLC support should be fairly easy to add, but I haven’t had the motivation to search for documentation on their DBus interface.

#!/usr/bin/env bash
#
# Controls playback on any running media players.
#
# usage: media-controller <command>
#
# Command:
#   play
#   pause
#   stop
#   next
#   previous
#
# Supported commands by player:
#   banshee
#       play
#       pause
#       stop
#       next
#       previous
#
#   radiotray
#       play
#       pause
#       stop

if [ ! $# -eq 1 ]; then
    ech o "usage: media-controller <command>"
    exit
fi

COMMAND=$1

BANSHEE_PID=`pgrep -x "banshee"`
RADIO_TRAY_PID=`pgrep -x "radiotray"`

if [ $BANSHEE_PID ]; then
    case $COMMAND in
        play)
            dbus-send --session --type="method_call" 
               --dest='org.bansheeproject.Banshee' 
               '/org/bansheeproject/Banshee/PlayerEngine' 
               'org.bansheeproject.Banshee.PlayerEngine.TogglePlaying'
            ;;
        pause)
            dbus-send --session --type="method_call" 
               --dest='org.bansheeproject.Banshee' 
               '/org/bansheeproject/Banshee/PlayerEngine' 
               'org.bansheeproject.Banshee.PlayerEngine.Pause'
            ;;
        stop)
            dbus-send --session --type="method_call" 
               --dest='org.bansheeproject.Banshee' 
               '/org/bansheeproject/Banshee/PlayerEngine' 
               'org.bansheeproject.Banshee.PlayerEngine.Close'
            ;;
        next)
            dbus-send --session --type="method_call" 
                --dest='org.bansheeproject.Banshee' 
                '/org/bansheeproject/Banshee/PlaybackController' 
                'org.bansheeproject.Banshee.PlaybackController.Next' 
                'boolean:false'
            ;;
        previous)
            dbus-send --session --type="method_call" 
                --dest='org.bansheeproject.Banshee' 
                '/org/bansheeproject/Banshee/PlaybackController' 
                'org.bansheeproject.Banshee.PlaybackController.Previous' 
                'boolean:false'
            ;;
        *)
            ech o "Command not supported for this player"
            ;;
    esac
fi
if [ $RADIO_TRAY_PID ]; then
    case $COMMAND in
        play)
            STATUS=`dbus-send --print-reply --session --type="method_call" 
                --dest='net.sourceforge.radiotray' 
                '/net/sourceforge/radiotray' 
                'net.sourceforge.radiotray.getCurrentRadio'`

            CURRENT_SONG=`ech o '$STATUS' | sed 's/.*"(.*)"[^"]*$/1/'`

            dbus-send --session --type="method_call" 
                --dest='net.sourceforge.radiotray' 
                '/net/sourceforge/radiotray' 
                'net.sourceforge.radiotray.playRadio' 
                'string:'$CURRENT_SONG
            ;;
        pause)
            dbus-send --session --type="method_call" 
                --dest='net.sourceforge.radiotray' 
                '/net/sourceforge/radiotray' 
                'net.sourceforge.radiotray.turnOff'
            ;;
        stop)
            dbus-send --session --type="method_call" 
                --dest='net.sourceforge.radiotray' 
                '/net/sourceforge/radiotray' 
                'net.sourceforge.radiotray.turnOff'
            ;;
        *)
            ech o "Command not supported for this player"
            ;;
    esac
fi

Actually getting the desired effect from the media keys required adding the following XML to my ~/.config/openbox/rc.xml file:

    <keybind key="XF86AudioNext">
      <action name="Execute">
        <command>media-controller next</command>
      </action>
    </keybind>
    <keybind key="XF86AudioPlay">
      <action name="Execute">
        <command>media-controller play</command>
      </action>
    </keybind>
    <keybind key="XF86AudioPrev">
      <action name="Execute">
        <command>media-controller previous</command>
      </action>
    </keybind>
    <keybind key="XF86AudioStop">
      <action name="Execute">
        <command>media-controller stop</command>
      </action>
    </keybind>
    <keybind key="XF86AudioPause">
      <action name="Execute">
        <command>media-controller pause</command>
      </action>
    </keybind>

How much power is your laptop using?

Products like Kill-A-Watt show how much power appliances are using. It’s good way to measure your computer’s power consumption — when it’s plugged in. But what if I want to know how much power my laptop is using when I’m not plugged in?

Turns out it’s fairly easy to get battery info. Running cat /proc/acpi/battery/BAT0/state should return the current discharge rate and total battery capacity.

The trouble is that the “current rate” field sometimes switches between mW and mA. I have no idea why this is the case. Oh well, we can easily convert mA to mW with the formula: $latex watts = amps times volts $.

The end result is this conky friendly bash script:

[bash]
#!/usr/bin/env bash
# Print the current power consumption in watts.
# The script pulls the power consumption info
# from /proc/acpi/battery/BAT0/state
#
# Usage: power.sh [battery_num]
#
# Add to conky:
# ${execi 10 ~/conkyscripts/power.sh 0}
#

# Default is BAT0
BATTERY=0
if [ $1 ]; then
BATTERY=$1
fi

# Sometimes the "present rate" is returned in milliwatts,
# but sometimes it is in milliamps. If it’s in milliwatts,
# we just convert to watts and return. Otherwise we
# convert to watts with the formula:
# Watts = Amps * Volts

UNIT=`cat /proc/acpi/battery/BAT$BATTERY/state |
grep "present rate:" | awk ‘{ print $(NF) }’`

if [ $UNIT == "mW" ]; then
MILLI_WATTS=`cat /proc/acpi/battery/BAT$BATTERY/state |
grep "present rate:" | awk ‘{ print $(NF-1) }’`
e cho $[ $MILLI_WATTS / 1000 ].$[ ($MILLI_WATTS % 1000) / 100 ]
else
MILLI_AMPS=`cat /proc/acpi/battery/BAT$BATTERY/state |
grep "present rate:" | awk ‘{ print $(NF-1) }’`
MILLI_VOLTS=`cat /proc/acpi/battery/BAT$BATTERY/state |
grep "present voltage:" | awk ‘{ print $(NF-1) }’`
POWER=$[$MILLI_AMPS * $MILLI_VOLTS]
echo $[ $POWER / 1000000 ].$[ ($POWER % 1000000) / 100000 ]
fi
[/bash]