From Iguana 5.6 onwards it is possible to use an External Identity Store to validate user logins.
Use of an External Identity Store in Iguana is optional, and defaults to disabled. When this feature is enabled, Iguana will query the External Identity Store first and then revert to using the local Iguana validation if the external validation fails. Using external logins is transparent to the user as it uses the same credentials (username and password) as a local login. Iguana local logins continue to work in exactly the same manner as previous releases.
The sample script provides demonstrates the use of LDAP authentication, you can adapt this to your own requirements. Notice that the LDAP call has been commented out and replaced with hard-coded authentication to make the script “plug and play”.
How it Works
Configuration
First you need to configure Iguana Authentication to use your Identity Store.
Warning: You must use a web service (http:// or https://), a direct LDAP link (like ldap://10.211.55.4:6544/checkLogin) does not work.
There are two configuration options correspond to two option keys in IguanaConfiguration.xml:
[...] <auth_config use_auth_url="true" auth_url="http://10.211.55.4:6544/checkLogin"> [...]
The use_auth_url key can only be set to true when a valid URL is also provided for auth_url (it is valid to have auth_url set while the use_auth_url is set to false). Some basic checking is done to ensure that a properly formed HTTP URL is used for the second setting.
Requests
When enabled, all login requests will query this URL to validate user credentials (and fallback to local Iguana validation if the query fails). Requests are formatted as HTTP GET, and credentials are passed in clear plaintext. The entity listening for these requests will test the credentials (or pass those credentials to some process that can) and respond with a plaintext line-formatted reply.
The query URL that is sent is built up from the base URL plus the user credentials. For example, for the username “John” with the password “Fnord”, the query sent by Iguana would be:
http://10.211.55.4:6544/checkLogin?name=John&password=Fnord
If a request fails, Iguana will fallback to local authentication using the same credentials, which behaves in the same manner as it always has. Basic success and failure is captured in the Iguana server log.
Responses
The response to this request must be formatted in a very specific and simple manner. The response can contain one or two pieces of information, each separated by a newline:
- A flag that signals success or failure, formatted as a single-character string; “0” for failure and “1” for success
- (Optional, on success) A list of Iguana registry roles this login can be associated with, each separated by a newline
For example, the following reply could be expected for the request shown above (control characters are shown for informational purposes only):
1<cr><lf> Administrators<cr><lf> Network Administrators<cr><lf> Developers<cr><lf>
Upon receiving this reply, Iguana would know that John’s credentials are correct and he belongs to the following three Roles:
- Administrators
- Network Administrators
- Developers
These Roles do not need to be present in the local registry, nor are they added to the registry if fetched from a reply. But Iguana will try to honour Role membership for externally authenticated users just as it always has for locally authenticated users.
All login successes and failures are logged to the Iguana server log, regardless of whether the authentication occurred via local or external authentication.
Code
Paste the code into a From HTTPS component it should work immediately (but only for the hard-coded users). Comments are provided inline.
function main(Data) -- Get the request details local Request = net.http.parseRequest{data = Data} local name = Request.params['name'] or "UNKNOWN" local password = Request.params['password'] local success = false --success, roles = validateViaPasswd(name, password) success = validateViaLdap(name, password) if success then body = '1' for _, role in pairs(roles) do body = body .. '\r\n' .. role end else body = '0' end local Response = net.http.respond{ body = body, entity_type = "text/plain", debug_result = true, use_gzip = false, } iguana.logInfo('Returning "' .. body .. '" for: ' .. name) end -- Plaintext 'passwd' style authentication function validateViaPasswd(name, password) local passwd = { ['foo'] = 'bar', ['fnord'] = 'fnord', ['John'] = 'password', ['fnørd'] = 'πåßß∑ø®∂', } isGoodPassword = false for uid, p in pairs(passwd) do if name == uid then -- Found a matching UID to the name. if password == p then -- And passwords match. isGoodPassword = true break end end end return isGoodPassword, getRoles(name) end -- Good-enough LDAP authentication via shell command -- This works only if we can find ldapexop on the local machine. function validateViaLdap(name, password) -- Directory and environment specific details. This will need to be -- tweaked. local ldapCommand = '/usr/bin/ldapexop' local baseDN = ',ou=people,dc=fnord,dc=test' local bindDN = 'cn=' .. name .. baseDN -- We quote the DN and password, just to be safe. local cmdArgs = ' -D "' .. bindDN .. '" -w "' .. password .. '" whoami' local h = io.popen(ldapCommand .. cmdArgs) local result = h:read() -- If the whoami query succeeds the command returns a valid DN, nil otherwise. if result == nil then return false else return true, getRoles(name) end end function getRoles(name) local roles = {} -- Only some users are Admins if name == 'John' or name == 'admin' then roles = { "A Test Role", "Fnord", "Administrators", "ƒ˜ø®∂", "Iguana Administrators", } else roles = { "A Test Role", "Fnord", "ƒ˜ø®∂", "Users", "Iguana Users", } end return roles end