I’m not much of a Java programmer but I’ve coded a module that authorizes against an LDAP server. Here is the working code:
package com.example.wms.module;
import com.wowza.wms.application.*;
import com.wowza.wms.amf.*;
import com.wowza.wms.client.*;
import com.wowza.wms.module.*;
import com.wowza.wms.request.*;
import javax.naming.*;
import javax.naming.directory.*;
import java.util.Hashtable;
public class LDAPAuth extends ModuleBase {
static public void onConnect(IClient client, RequestFunction function, AMFDataList params)
{
String username = getParamString(params, PARAM1, “”).trim();
String password = getParamString(params, PARAM2, “”).trim();
if ((“”).equals(username) || (“”).equals(password))
{
System.out.println(“Empty string or password”);
}
else
{
String base = “ou=People,dc=example,dc=com”;
String dn = “cn=” + username + “,” + base;
String ldapURL = “ldap://ldap.example.com:389”;
Hashtable authEnv = new Hashtable();
authEnv.put(Context.INITIAL_CONTEXT_FACTORY,“com.sun.jndi.ldap.LdapCtxFactory”);
authEnv.put(Context.PROVIDER_URL, ldapURL);
authEnv.put(Context.SECURITY_AUTHENTICATION, “simple”);
authEnv.put(Context.SECURITY_PRINCIPAL, dn);
authEnv.put(Context.SECURITY_CREDENTIALS, password);
try
{
DirContext authContext = new InitialDirContext(authEnv);
client.acceptConnection();
getLogger().info("onConnect: " + client.getClientId());
return;
}
catch (AuthenticationException authEx)
{
System.out.println("AuthenticationException: " + authEx.getMessage());
}
catch (NamingException namEx)
{
System.out.println("NamingException: " + namEx.getMessage());
}
}
client.rejectConnection();
getLogger().info("onConnect: " + client.getClientId());
}
public void onConnectAccept(IClient client) {
getLogger().info("onConnectAccept: " + client.getClientId());
}
public void onConnectReject(IClient client) {
getLogger().info("onConnectReject: " + client.getClientId());
}
public void onDisconnect(IClient client) {
getLogger().info("onDisconnect: " + client.getClientId());
}
}
I used Charlie’s example (http://community.wowza.com/t/-/113) for the basics. I guess I’m wondering if there’s any improvements I can make. What I found interesting was that unlike Charlie’s code I had to explicitly call client.rejectConnection() or the connection would still be accepted. Not sure why.
Building on this, I have a security model that I’m developing and I was hoping for input on how effective it might be. I believe the encryption offered by RTMPE is adequate for streams but I’m not comfortable using it for LDAP passwords so I plan on authorising with RTMPS and then reconnecting for the actual streaming with RTMPE (for server performance). Here’s my plan:
On instantiation of client Flash movie generate a random hash.
Send (over RTMPS) username, password and client hash.
On successful connection (using code above), generate random hash on the server, send back to client and close connection. Store client hash, server hash and timestamp in array (Hashtable?) on server.
Client immediately makes new connection sending client hash and server hash. On successful connection (lookup element in array), relevant element is deleted from array. Otherwise connection rejected. A parameter in the connection method, “simple” or “token”, could determine whether the other params should be considered username/passwords or tokens (to differentiate between connection types).
Repeat steps 2-4 each time a new stream is requested.
Because this has the ability to cause a memory leak if elements in the array are not used or deleted, some sort of timer-based cleanup function would need to run to remove any elements whose timestamps are greater than, for example, two minutes (assuming a successful connection would only take a second or two). This process should not be too intensive as the array would only contain elements relating to failed connections.
Any suggestions?
Specifically:
a) Is it secure?
b) I think this would mean that using securetoken would be redundant?
c) As I don’t know Java or Wowza very intimately, should i use a Java timer to clean up the array or is there a regular Wowza process I can hook into?
d) Lastly a dumb Java/Wowza question, do instances of the custom module classes persist until Wowza is shutdown? That is, if I declare an array/hashtable variable and timer function in my module, will they persist?
Sorry for the length of this post. I hope at least the code is useful.