Sametime photos served up by IHS

Between customer work I have been working on replacing our internal Sametime servers with shiny new 9.0.1 servers using AD instead of Domino LDAP.

The final piece of the puzzle is photos. Anyone who knows Sametime knows that something as simple as a photo is not made simple by the applications. The Sametime Proxy requires an LDAP attribute (PhotoURL) to be used which points STProxy to the image retrieving it for the client. Meetings doesn’t use the same approach, grr. It can use a binary object saved in LDAP or offload the retrieval to a web server like PhotoURL for STProxy but uses a “string” where all photos must be named joe.bloggs@acme.com.jpg. Confusing? Yep.

I was about to roll over and say it’s not possible but it seems that it is possible to cover all use cases.

  1. Notes/Sametime clients using ImagePath URL
  2. STProxy web client using PhotoURL
  3. Meetings off loading to a web server
  4. Stop external access to photos

The nice thing STProxy does is that it will “proxy” the photos so the web browser doesn’t need direct connectivity to the jpgs. That is great because I can put the photos on an internal facing web server. The STProxy then calls the URL specified in the user’s LDAP entry (PhotoURL), caches it locally and then serves it up. Brilliant, I can lock the photos away so that no one can browse them from the internet if they know our email addresses.

You’ll need to update stproxyconfig.xml adding proxyServerURL otherwise it will not work. Don’t forget to sync and restart STProxy.

    <photoCache>
<enabled>true</enabled>
<cacheExpiry>60</cacheExpiry>
<storageLocation>/opt/IBM/phototemp</storageLocation>
<proxyServerURL>https://chat.acme.com</proxyServerURL&gt;
</photoCache>

Ah, the Meeting server doesn’t follow the same logic. Clients (thick or web browser or mobile) need direct access to the photo to render it in the client. This means I’m back to square one….

Let’s jump back a step. How do we get the photos up to a web server?

Photos from Connections

At present our Sametime and Connections servers are using different LDAPs so SSO is not possible and even if it was retrieving photos from Connections via photo.do is not possible for guests because the photos require authentication so using the Connections business card for STProxy and Meetings is a show stopper.

Luckily in the Connections TDISOL there is an AL we can use called dump_photos_to_files. I won’t go into too much details about this but you can copy and paste the AL and then alter it. I altered it to return all user’s email addresses as well as UID and then dump the photos in the format of emailaddress.jpg which is the format needed by the Meeting server.

You may find the email addresses are capitalised. If so you will need to add some JavaScript to the lookup_user process to get it all in lower case

ret.value=conn.getstring(“email”).toLowerCase();

Once you have the photos in the correct format you need to get them from the server running TDI to a web server.

Web server

The logical way to serve the photos is using IHS in front of Connections. To get the files there I needed to scp them from the TDI server to IHS. I had to create ssh-keygens detailed in http://www.linuxproblem.org/art_9.html so I could scp the files wrapped in a shell script. Incidentally , the shell script called the AL and then scp’d the photos to the IHS server. Then add the shell script to cron so it is called on a schedule.

I wanted to lock down access to the photos so that people couldn’t browse to them. This is a little difficult to do but you can use IP ranges for all your internal offices and/or VPNs so that they are allowed to access the photos. The problem is guests who are truly external.

I created a new virtual host in httpd.conf with the following details.

# Sametime photos
<VirtualHost *:80>
ServerName icphotos.acme.com:80
DocumentRoot “/opt/IBM/HTTPServer/photos”
RewriteEngine On
RewriteCond %{HTTP_COOKIE} !LtpaToken2=.*$ [NC]
RewriteCond %{HTTP_COOKIE} !LtpaToken=.*$ [NC]
RewriteCond %{HTTP_COOKIE} !STPluginActivePage=stMeetingroom [NC]
# Old subnets and staff VPN
RewriteCond %{REMOTE_ADDR} !^xxx\.xx\.(x[x-x]|x[x-x])\.([x-x]|[x-x][x-x]|x([x-x][x-x])|x([x-x][x-x]|x[x-x]))$
# UK
RewriteCond %{REMOTE_ADDR} !^xxx\.xx\.(x[x-x]|x[x-x])\.([x-x]|[x-x][x-x]|x([x-x][x-x])|x([x-x][x-x]|x[x-x]))$
# India
RewriteCond %{REMOTE_ADDR} !^xxx\.xx\.(x[x-x]|x[x-x])\.([x-x]|[x-x][x-x]|x([x-x][x-x])|x([x-x][x-x]|x[x-x]))$
# Sametime Proxy
RewriteCond %{REMOTE_ADDR} !^xxx\.xx\.xx\.xxx$ [NC]
RewriteRule ^(.*)$ http://www.acme.com [R,L]
</VirtualHost>

In a nutshell this allows all clients on certain IP range s to access photos. It also allows any web browser whether it is internal or on the internet to access photos IF it has either one of three cookies, LtpaToken/LtpaToken2 which is provided to the browser when someone authenticates or the cookie STPluginActivePage which the browser stores when you enter a meeting room. STPluginActivePage is in the browser whether you are a guest or an authenticated user, you just need to enter a meeting room.

I included both LtpaToken and LtpaToken2. I found the Sametime client was sending only LtpaToken with the HTTP GET for the photos. This may be due to the fact that I allow both LtpaToken and LtpaToken2 in the Domino web SSO configuration document. If you only allow LtpaToken2 then you may find that the client sends LtpaToken2 with the GET.

If you are a web browser outside of the IP ranges and you do not have any of the three cookies then you will be redirected to http://www.acme.com. You could change this to a static html page of your choice.

I’m no whiz when it comes to Apache but I have tested this quite a bit and it seems pretty secure and should cover most bases. Of course it doesn’t stop a meeting guest from guessing email addresses and browsing other people’s photos but since you have invited them to a meeting, provided them with the meeting room password there is an element of familiarity that should stop them from being malicious in this way. If you back this up with changing the meeting room passwords often you should be in a strong position to keep these photos relatively secure.

If anyone has any thoughts on the httpd.conf I am all ears as I would like to tie it down further if it needs it.

UPDATE

I found that my original RewriteCond  for the IP addresses were not working. I was originally using the following method because it seemed nice and easy to just enter the CIDR but reading further the following approach only works with Apache 2.4 and IHS is using 2.2.8. You can find out by running apachectl -V.

RewriteCond expr “-R ‘xxx.xx.xx.0/xx'”

So regex was the only way to go and trying to work it out was going to be a headache. To my rescue came http://jodies.de/ipcalc? to convert the CIDR to all the IP addresses (well the first and last) and then I put these values into http://www.analyticsmarket.com/freetools/ipregex to give me the regex.

Populating Profiles – long search filter error

A customer wanted to use a series of nested groups to populate Profiles. The theory is that the parent group has a number of child groups which are controlled by various location specific administrators.

Initially I hoped to be able to achieve this by using a special query (LDAP_MATCHING_RULE_IN_CHAIN) which would walk to the root and thus include all members of the nested groups.

“(&(objectClass=user)(member:1.2.840.113556.1.4.1941:(=CN=IBM Connections Users,DC=acme,DC=com)))”

I couldn’t get this to work using ldapsearch so I had their AD admins investigate. They too could not get it to work so the work around was to add all the groups to the source_ldap_search_filter value in profiles_tdi.properties. The search filter consisted of over 26000 characters!!

On running sync_all_dns I saw failures (after enabling debug from Profiles MustGather) in the ibmdi.log. The errors matched Population or Synchronization fails trying to update the Peopledb with the error SQLCODE: -302, SQLSTATE: 22001

I was hesitant to go ahead and make the changes details and read else where that LONG VARCHAR is deprecated in 9.7 potentially meaning CLOB datatype was required. I raised a PMR with IBM to check and they came back and said that there is no change made to PROF_SOURCE_URL in the later versions of Connections. The one pain could be if a side by side migration to 4.5 (or later) is performed as the new database will have VARCHAR as the datatype. With some forward planning this migration failure can be removed.

My DB2 colleague and I decided to back up PEOPLEDB and then run the following command to dump the definitions of the database before altering the datatype.

db2look -d PEOPLEDB -f -l -e -x > D:\db2look_peopledb_prechange.txt

To alter the datatype we used the Control Center which gave us the below SQL.

CONNECT TO PEOPLEDB;
CALL SYSPROC.ALTOBJ ( ‘APPLY_CONTINUE_ON_ERROR’, ‘CREATE TABLE EMPINST.EMPLOYEE ( PROF_KEY VARCHAR (36)  NOT NULL , PROF_UID VARCHAR (256)  NOT NULL , PROF_UID_LOWER VARCHAR (256)  NOT NULL , PROF_LAST_UPDATE TIMESTAMP  NOT NULL , PROF_MAIL VARCHAR (256) , PROF_MAIL_LOWER VARCHAR (256) , PROF_GUID VARCHAR (256)  NOT NULL , PROF_SOURCE_UID VARCHAR (256)  NOT NULL , PROF_DISPLAY_NAME VARCHAR (256) , PROF_LOGIN VARCHAR (256) , PROF_LOGIN_LOWER VARCHAR (256) , PROF_GIVEN_NAME VARCHAR (128) , PROF_SURNAME VARCHAR (128) , PROF_ALTERNATE_LAST_NAME VARCHAR (64) , PROF_PREFERRED_FIRST_NAME VARCHAR (32) , PROF_PREFERRED_LAST_NAME VARCHAR (64) , PROF_TYPE VARCHAR (64) , PROF_MANAGER_UID VARCHAR (256) , PROF_MANAGER_UID_LOWER VARCHAR (256) , PROF_SECRETARY_UID VARCHAR (256) , PROF_IS_MANAGER CHARACTER (1) , PROF_GROUPWARE_EMAIL VARCHAR (128) , PROF_GW_EMAIL_LOWER VARCHAR (128) , PROF_JOB_RESPONSIBILITIES VARCHAR (128) , PROF_ORGANIZATION_IDENTIFIER VARCHAR (64) , PROF_ISO_COUNTRY_CODE VARCHAR (3) , PROF_FAX_TELEPHONE_NUMBER VARCHAR (32) , PROF_IP_TELEPHONE_NUMBER VARCHAR (32) , PROF_MOBILE VARCHAR (32) , PROF_PAGER VARCHAR (32) , PROF_TELEPHONE_NUMBER VARCHAR (32) , PROF_WORK_LOCATION VARCHAR (32) , PROF_BUILDING_IDENTIFIER VARCHAR (64) , PROF_DEPARTMENT_NUMBER VARCHAR (24) , PROF_EMPLOYEE_TYPE VARCHAR (256) , PROF_FLOOR VARCHAR (16) , PROF_EMPLOYEE_NUMBER VARCHAR (16) , PROF_PAGER_TYPE VARCHAR (16) , PROF_PAGER_ID VARCHAR (32) , PROF_PAGER_SERVICE_PROVIDER VARCHAR (50) , PROF_PHYSICAL_DELIVERY_OFFICE VARCHAR (32) , PROF_PREFERRED_LANGUAGE VARCHAR (100) , PROF_SHIFT VARCHAR (4) , PROF_TITLE VARCHAR (256) , PROF_COURTESY_TITLE VARCHAR (64) , PROF_TIMEZONE VARCHAR (64) , PROF_NATIVE_LAST_NAME VARCHAR (256) , PROF_NATIVE_FIRST_NAME VARCHAR (256) , PROF_BLOG_URL VARCHAR (256) , PROF_FREEBUSY_URL VARCHAR (256) , PROF_CALENDAR_URL VARCHAR (256) , PROF_DESCRIPTION CLOB  (1048576   )  LOGGED  NOT  COMPACT , PROF_EXPERIENCE CLOB  (1048576   )  LOGGED  NOT  COMPACT , PROF_SOURCE_URL “LONG VARCHAR” , PROF_SRC_UID_LOWER VARCHAR (256)  NOT NULL , TENANT_KEY VARCHAR (36)  NOT NULL  WITH DEFAULT ‘00000000-0000-0000-0000-040508202233’ , PROF_STATE INTEGER  NOT NULL  WITH DEFAULT 0   ) IN USERSPACE32K INDEX IN USERSPACE4K LONG IN USERSPACE32K ‘, -1, ? );
CONNECT RESET;

After running this the datatype of the column changed to LONG VARCHAR.

We run the definitions dump again and compared the contents with the pre-change information. The contents were the same albeit in a different order.

db2look -d PEOPLEDB -f -l -e -x > D:\db2look_peopledb_prechange.txt

This gave us confidence to continue and started Connections. At which point the sync_all_dns completed successfully and the users in the nested groups are populated to Profiles. Checking EMPLOYEE.PEOPLEDB shows the very long search filter in the PROF_SOURCE_URL for those that were added recently.

Error when installing TDI on CentOS

I was stuck on this for a little while today when trying to install TDI 7 (Tivoli Directory Integrator) as part of a Connections installation on (unsupported) CentOS.

After extracting C1IF4ML and running ./launchpad.sh up popped the installation web page but when selecting “Tivoli Directory Integrator 7.0 Installer” I was presented with the following error:

“Bundled JRE is not binary compatible with the host OS/Arch or it is corrupt. Testing bundled JRE failed.”

After a bit of reading around people suggested it was due to the /tmp directory not having adequate space for the installation, but there seemed to be enough based on IBM’s disk space requirements.

Eventually I ran the following command which worked after creating another temporary directory:

./install_tdiv70_linux_x86_64.bin -is:tempdir /opt/temp