Next JsWorld release scheduled for November 2010

JsWorld iconToday I looked up Unicode’s tentative schedule for their next CLDR 1.9 release. It is planned for the 10th of November, which means that I’ll be able to update the JsWorld locale definition files soon thereafter and release in turn a JsWorld update. The next JsWorld version will probably be 2.4.

For those if you who don’t know, JsWorld is a comprehensive JavaScript library for formatting and parsing of numbers, currency and date/time inside the browser, in a user specified locale. The locale data strictly follows Unicode’s sources and I strive to update it as soon as the consortium releases a new snapshot of their database. Currently Unicode keeps track of over 300+ world locales, 200+ languages and 100+ currencies.

The future of the web is cross-domain, not same-origin

CORS FilterLast month I released CORS Filter, the first universal software solution for fitting Cross-Origin Resource Sharing (CORS) support to Java web applications. CORS is a recent W3C effort to introduce a standard mechanism for enabling cross-site requests in web browsers and servers.

Since the early days of the web (think Netscape 2.0) browsers have enforced, to various degrees, a same origin policy to prevent leaking of confidential user data to third party sites. The same origin policy was carried over to the game-changing XHR which appeared in the early 2000’s. Modern web applications, however, incresingly seek to dynamically integrate content and services from third parties, which is currently achieved through “hacks” such as JSONP. CORS was created in recognition that cross-domain requests are here to stay and therefore they’d better be standardised.

The web context of CORS
The web context of CORS

The philosophy of CORS

CORS works in two directions:

  1. From a browser script perspective: By imposing tighter controls on data exchanged during a cross-site request. Cookies, for instance, are blocked unless specifically requested by the XHR caller and allowed by the remote web service. This is done to reduce the said risk of data leaks.
  2. From a web service perspective: By requiring the browser to report the origin URL to the target cross-site service, so the latter can determine, based on its origin policy, whether to allow or deny the request.

The original CORS specification is available at http://www.w3.org/TR/cors/

Note that in order for CORS to work, it must be supported by both browser and web server.

Browsers supporting CORS

The following major browsers support CORS as of October 2010:

  1. Firefox 3.5+
  2. Internet Explorer 8+ (Partial support via the XDomainRequest object)
  3. Apple Safari 4+
  4. Google Chrome 3+

Ajax style LDAP access

Json2Ldap iconAjax-style directory access is easy with Json2Ldap.

What you need:

  1. A Json2Ldap installation to take in directory requests in the form of JSON messages and translate them to the binary LDAP protocol (and then back).
  2. An LDAP v3 compatible directory server, such as OpenLDAP, Microsoft Active Directory, IBM Tivoli Directory Server or Novell eDirectory.
  3. A JavaScript library to streamline the dispatch of XMLHttpRequests, my favourite is jQuery. Also a JSON encoder/decoder.

To utilise a remote directory you must connect to it first. Instead of devising its own message schema, Json2Ldap speaks standard JSON-RPC 2.0. Here is how the connect request is composed in JavaScript:

var request = {};
request.method = "ldap.connect";
request.params = {};
request.params.host = "ldap.host.net";
request.params.port = 389;
request.id = 0;
request.jsonrpc = "2.0";

The host and port parameters specify the network location of the LDAP server. Serialised to JSON the request may look like that:

{
 "method" : "ldap.connect",
 "params" : { "host" : "ldap.host.net", "port" : 389 },
 "id" : 0,
 "jsonrpc" : "2.0"
}

We then send off the request using jQuery’s HTTP POST wrapper. If all goes well the callback function will receive a result that contains an LDAP connection identifier, which we must save for later requests.

// The Json2Ldap URL
var url = "http://tomcat.host.net:8080/json2ldap/";

// The LDAP connection CID
var cid = null;

// The callback function
function saveCID(response) {
	if (response.result)
		cid = response.result;
	else if (response.error)
		alert("Connect error: " + response.error.message);
};

$.post(url, JSON.stringify(request), saveCID, "json");

Json2Ldap’s web API covers the entire set of standard LDAP commands as well as a few extended controls and operations. Search is however perhaps the most commonly used directory command.

function displaySearchResult(response) {	
	if (response.result)
		alert("Found " + response.result.matches.length + "match(es)");		
	else if (response.error)
		alert("Search error: " + response.error.message);
};

var request = {};
request.method = "ldap.search";
request.params = {};
request.params.CID = cid;
request.params.baseDN = "ou=people,dc=example,dc=com";
request.params.scope = "ONE";
request.params.filter = "(givenName=Agnese)";
request.id = 1;
request.jsonrpc = "2.0";

$.post(url, JSON.stringify(request), displaySearchResult, "json");

Here is an example search result entry, formatted as LDIF (users have got choice – Json2Ldap allows for JSON as well as LDIF result formatting).

Sample search result entry in LDIF format

In one of my next posts I’ll give you some cool mashup examples utilising the Json2Ldap web service 🙂

Json2Ldap with improved directory search

Json2Ldap iconJson2Ldap 1.5 is out.

The most recent release of the web gateway for connecting to LDAP v4 compatible directories via JSON-RPC delivers several incremental improvements, the most notable being the expanded capabilities of the ldap.search command. It now gives programmers finer control over the entry attributes which the method returns.

The available choices:

  • Return all user attributes.
  • Return all operational attributes (attributes associated with a directory object for administrative purposes).
  • Return only the specified attributes.
  • Don’t return any attributes.

Let’s illustrate these choices with a few example JSON-RPC 2.0 requests/responses.

First, connect to the default remote directory with ldap.connect.

The connect request:

{ "method"  : "ldap.connect",
  "id"      : 1,
  "jsonrpc" : "2.0" }

The connect response, returning a string token to identify the LDAP connection (CID = connection identifier) in later requests:

{ "result"  : "-31ccd4bdbe6170b69956fe1c2eeffb42", 
  "id"      : 1,
  "jsonrpc" : "2.0" }

Example 1: Return all user attributes.

This is the default action. To do that simply omit the attributes parameter (or set it to “*”).

The request:

{ "method" : "ldap.search",
  "params" : { "CID"    : "-31ccd4bdbe6170b69956fe1c2eeffb42",
               "scope"  : "ONE",
               "baseDN" : "ou=people,dc=example,dc=com",
	       "filter" : "(uid=user.0)" },
  "id":1,
  "jsonrpc" : "2.0" }

The response:

{ "result"  : { "matches" : [ { "DN"          : "uid=user.0,ou=People,dc=example,dc=com",
                                "objectClass" : [ "person",
						  "inetorgperson",
						  "organizationalperson",
						  "top"],
			        "uid"         : ["user.0" ],
				"cn"          : ["Aaccf Amar"],
				"sn"          : ["Amar"],
				"givenName"   : ["Aaccf"],
				"initials"    : ["ASA"],
				"mail"        : ["[email protected]"],
				"street"      : ["0125 Chestnut Street"],
				"l"           : ["Panama City"],
				"mobile"      : ["+1 010 154 3228"] } ],
                "referrals" : [] },
  "id"      : 1,						
  "jsonrpc" : "2.0" }

Example 2: Return all operational attributes

Set the attributes parameter to “+”:

The request:

{ "method" : "ldap.search",
  "params" : { "CID"        : "-31ccd4bdbe6170b69956fe1c2eeffb42",
               "scope"      : "ONE",
               "baseDN"     : "ou=people,dc=example,dc=com",
	       "filter"     : "(uid=user.0)",
	       "attributes" : "+" },
  "id":1,
  "jsonrpc" : "2.0" }

The response:

{ "result"  : { "matches" : [ { "DN"                : "uid=user.0,ou=People,dc=example,dc=com",
                                "subschemaSubentry" : ["cn=schema"],
			        "entryUUID"	    : ["ad55a34a-763f-358f-93f9-da86f9ecd9e4"],
			        "entryDN"	    : ["uid=user.0,ou=people,dc=example,dc=com"],
			        "modifiersName"     : ["cn=Directory Manager,cn=Root DNs,cn=config"],
			        "modifyTimestamp"   : ["20100622033521Z"] } ],
                "referrals" : [] },
  "id"      : 1,						
  "jsonrpc" : "2.0" }

Example 3: Return only the specified attributes

Set the attributes parameter to a string listing the required attribute names.

The request:

{ "method" : "ldap.search",
  "params" : { "CID"        : "-31ccd4bdbe6170b69956fe1c2eeffb42",
               "scope"      : "ONE",
               "baseDN"     : "ou=people,dc=example,dc=com",
	       "filter"     : "(uid=user.0)",
	       "attributes" : "cn mail" },
  "id":1,
  "jsonrpc" : "2.0" }

The response:

{ "result"  : { "matches" : [ { "DN"   : "uid=user.0,ou=People,dc=example,dc=com",
                                "cn"   : ["Aaccf Amar"],
				"mail" : ["[email protected]"] } ],
                "referrals" : [] },
  "id"      : 1,						
  "jsonrpc" : "2.0" }

Example 4: Don’t return any attributes

Set the attributes parameter to an empty string.

The request:

{ "method" : "ldap.search",
  "params" : { "CID"        : "-31ccd4bdbe6170b69956fe1c2eeffb42",
               "scope"      : "ONE",
               "baseDN"     : "ou=people,dc=example,dc=com",
	       "filter"     : "(uid=user.0)",
	       "attributes" : "" },
  "id":1,
  "jsonrpc" : "2.0" }

The response:

{ "result"  : { "matches"   : [ { "DN" : "uid=user.0,ou=People,dc=example,dc=com" } ],
                "referrals" : [] },
  "id"      : 1,						
  "jsonrpc" : "2.0" }

So, what’s next on the Json2Ldap roadmap? HTTP Cross-Origin Resource Sharing (CORS) support!

Retrieving enum parameters from JSON-RPC 2.0 requests

JSON-RPC 2.0Since version 1.9.2 of the JsonRpc2-Base library its utility classes add support for convenient retrieval of enumerated request parameters. Such occur in situations when an RPC parameter has a fixed set of possible values. A typical example is a person’s sex, which can be either “male” or “female”.

So, if you’ve got an enum class defined, you can directly retrieve the parameter as the matching enum constant.

The enum class definition:

public enum Sex { 
        MALE, 
        FEMALE 
}

Retrieving the enum parameter from a JSON-RPC 2.0 request (assuming named parameters):

JSONRPC2Request req = JSONRPC2Request.parse("... the request string ...");

// Create a new retriever for named parameters
Map params = (Map)req.getParams();
NamedParamsRetriever ret = new NamedParamsRetriever(params);

// Get the enum param as constant
Sex sex = ret.getEnum("sex", Sex.class);

if (sex == Sex.MALE)
       System.out.println("Got male");
else
       System.out.println("Got female");

The first argument of the getEnum method specifies the parameter name, the second the defining enum class.

Clever bit: If the parameter value is not matched by a constant in the enum class the method will automatically throw a standard “Invalid parameters” JSONRPC2Error with code -32602.

There are also variations of the getEnum method that allow for case insensitive matching or for optional parameters that will be given a default value if they are omitted from the request.

24 часовият маратон издание 2010 наближава!

На 11+12 септември Крива спица събира байк терминатори от всички краища на България на 24 часов планински маратон по жестоки пътечки около х. Здравец. Елате, преживяването ще бъде яко и за всички – както за състезатели, така и за зрители!!!

Маратонът, тази година в четвъртото си издание, вече се превръща в традиция. Какво да очакваме този път? Ново трасе, по-големи награди, а може би и нови лица и нови спортни постижения 🙂 Крива спица осигурява перфектната организация, а вие – състезатели и зрители – хубавата емоция 🙂

За подробности – регистрация, трасе, правила – вижте специалния 24h сайт на Крива спица.

Seatpost wisdom

Always choose the longer postpost

… particularly if you’re getting a new bike frame.

But why would you need a new one?

Because bike manufacturers have gone overboard with sizes and today when you buy a new frame it’s very likely it would have its own seatpost diameter. A quick wikipedia check shows how many “standard” diameters are in existence today. Hold your breath:

22.0, 22.2, 23.4, 23.8, 24.0, 25.0, 25.4, 25.8, 26.0, 26.2, 26.4, 26.6, 26.8, 27.0, 27.2, 27.4, 27.8, 28.0, 28.6, 29.4, 29.6, 29.8, 30.0, 30.4, 30.8, 30.9, 31.4, 31.6, 31.8, 32

Something like 30 if you bother to count them.

First look at JsonSSO

Earlier in August I began work on JsonSSO, a web service that provides single sign-on and session management. It naturally complements Json2Ldap, another product of mine which provides web-friendly JSON-RPC access to LDAP v3 compatible directories such as OpenLDAP, MS AD and Novell eDirectory.

The recent years we saw a proliferation of single sign-on (SSO) solutions. While the underlying concept of SSO is relatively simple, the IT context (participating apps, authentication methods, back-ends, platforms, policy, etc.) can vary significantly, which has prompted the development of so many implementations.

JsonSSO has three defining features:

  1. User authentication is done against a back-end LDAP directory via Json2Ldap.
  2. Once a user session is established, participating web clients may be given an open LDAP connection (by means of Json2Ldap) bound as the currently logged-in user. This connection allows web clients convenient and flexible access to user details such as user ID, name, email, photo, application preferences, etc.
  3. An internal database records the details of all active and expired user sessions. It can be queried for audit and management purposes via a JSON-RPC interface.

For JsonSSO to be easy to understand and work with I intend to stick to these three main features. Diversions, such as adding DB-based authentication, will be avoided. I want to have JsonSSO as web-friendly as possible, keeping all incoming (from clients) and outgoing (to back-end) connections in the form of HTTP.

Here is a preliminary overview of the JsonSSO API and its configuration settings. These may change somewhat by the time JsonSSO is officially released (Q4 2010).

JSON-RPC 2.0 API

  • sso.login Initial login with an authenticating ID (username, email, etc.) and password. Returns a new session identifier (SID) which can be passed between the participating web clients and apps.
  • sso.logout Closes a user session. Can be invoked by any of the participating web clients and apps that holding the corresponding SID.
  • sso.getUserID By passing a valid SID, clients can get the user’s system/org-wide ID.
  • sso.getUserDN By passing a valid SID, clients can get the distinct name (DN) of the user, i.e. their directory record.
  • sso.getJson2LdapURL Returns the URL of the Json2Ldap web service.
  • sso.getAnonymousLdapConnection Returns an anonymous LDAP connection (via Json2Ldap) to the back-end directory (if permitted by config).
  • sso.getBoundLdapConnection Returns an LDAP connection (via Json2Ldap) bound as the currently logged-in user (if permitted by config and the web client/app has authorisation).
  • sso.refresh Allows clients/apps to extend a user session by presenting its SID, otherwise it would eventually expire after a preconfigured idle time.
  • sso.getSessionSettings Returns the max idle time, max duration and other settings for a session represented by a given SID.
  • sso.registerLogoutCallback Allows participating web apps to receive a notification that the user has logged out and the session has ended.
  • sso.unregisterLogoutCallback Allows to cancel a previously registered logout notification.
  • sso.listRegisteredCallbacks Lists all web apps that have requested to receive a logout notification.
  • sso.listSessions Lists the details of current or expired sessions. Regular users can only access their own session history. Administrators have full access.
  • ws.getName Returns the web service name.
  • ws.getVersion Returns the web service version.
  • ws.getTime Returns the local web service time.

JsonSSO configuration parameters

This set of parameters governs web client/app access to the JsonSSO service:

  • jsonsso.clients.requireHttps
  • jsonsso.clients.returnAnonymousLdapConnection
  • jsonsso.clients.returnBoundLdapConnection
  • jsonsso.clients.allowLogoutCallbacks

User session limits:

  • jsonsso.sessions.maxTime
  • jsonsso.sessions.maxIdleTime
  • jsonsso.sessions.quotaPerUser
  • jsonsso.sessions.onQuotaExhaustion

Specifies the Json2Ldap URL through which the back-end LDAP directory will be accessed:

  • jsonsso.json2ldap.url
  • jsonsso.json2ldap.trustSelfSignedCerts

Specifies the server details of the back-end LDAP directory. If the useDefault parameter is true JsonSSO will use the default LDAP server for the configured Json2Ldap gateway/proxy.

  • jsonsso.ldapServer.useDefault
  • jsonsso.ldapServer.host
  • jsonsso.ldapServer.port
  • jsonsso.ldapServer.timeout
  • jsonsso.ldapServer.security
  • jsonsso.ldapServer.trustSelfSignedCerts

The uidAttribute parameter specifies the name of the LDAP attribute that holds the system/org-wide user IDs (typically userid but may be something else). If set, the groupDn parameter governs which users are allowed to login via JsonSSO.

  • jsonsso.users.uidAttribute
  • jsonsso.users.groupDn

This set of parameters determines how to derive the user directory record (DN) from the username or email entered at login:

  • jsonsso.dnResolution.method
  • jsonsso.dnResolution.dnTemplate
  • jsonsso.dnResolution.searchFilter
  • jsonsso.dnResolution.searchBaseDn
  • jsonsso.dnResolution.searchUserDn
  • jsonsso.dnResolution.searchUserPassword

This set of parameters determine which users have admin access to the session logs:

  • jsonsso.admin.dn
  • jsonsso.admin.groupDn

Със Салса Буда на Водната планина

С Илиан от пловдивската Salsa de Cuba си направихме епичен поход из “Водната планина” или както траките са наричали Рила. Маршрутът ни мина през стръмните зъбери на Мальовица, после по билото до хижа Иван Вазов, след което се спуснахме и до Седемте рилски езера.

Там където водата и планината докосват толкова високо небето, там спокойствието и усещането за чистота са наистина невероятни!

Салса Буда на Елениното езеро
Салса Буда на Елениното езеро

LDAP web service software updated

Json2Ldap iconJson2Ldap 1.4, released yesterday, simplifies its JSON-RPC 2.0 API to become an even friendlier web service for working with LDAP compatible back-end directories.

The calls to make plain, secure and default LDAP connections are now merged into a single RPC method named ldap.connect.

To make a connection to the default LDAP server (specified in the Json2Ldap configuration file by the admin) just send an ldap.connect request with no parameters:

{ "id" : 1,
  "method" : "ldap.connect",
  "jsonrpc" : "2.0"
}

To make a plain LDAP connection to a particular directory server specify its host and port:

{ "id" : 1,
  "method" : "ldap.connect",
  "params" : { "port" : 1389, "host" : "dir.example.com" },
  "jsonrpc" : "2.0"
}

To make a secure (encrypted) connection, set the optional security parameter to StartTLS or SSL. You may also set the optional trustSelfSignedCerts parameter:

{ "id" : 1,
  "method" : "ldap.connect",
  "params" : { "host" : "192.168.0.1", 
               "port" : 1389, 
               "security" : "StartTLS",  
               "trustSelfSignedCerts" : true },
  "jsonrpc":"2.0"
}

The full description of the ldap.connect JSON-RPC call is available in the online API docs.