package auth.models; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ning.http.util.Base64; import play.mvc.Http; public class UserToken extends User { private static Logger logger = LoggerFactory.getLogger(UserToken.class); public String tok; private String timeStamp; private String origIP; public final static String TOKEN = "tkn"; private static final long TIME_OUT = 30 * 60 * 1000; // miliseconds // shhhh ... this is my secred key! private static final byte[] KEY = "73edbe4f4c8d46ce".getBytes(); private static final SecretKeySpec KEYSPEC = new SecretKeySpec(KEY, "AES"); /** * Add the token to the url and make sure it is properly encoded. * * @param url - a string representing the url * @return properly encoded url string. */ public String toHttp(String url) { String u = url == null ? "" : url; if (u.indexOf("?") == -1) { u += "?"; } else if (!u.endsWith("?") && !u.endsWith("&")) { u += "&"; } logger.trace("Token before encoding:" + tok + " length=" + tok.length()); String utok; try { utok = URLEncoder.encode(tok, "UTF8"); logger.trace("Token after encoding:" + utok + " length=" + utok.length()); u += TOKEN + "=" + utok; } catch (UnsupportedEncodingException e) { logger.info("", e); } return u; } /** * Use this method instead of getToken() if you need to pass the token via HTTP. * * @return a URL encoded token string. * */ public String getUrlEncodedToken() { String token = this.tok; try { // encode the string to make sure no funny characters are passed in request token = URLEncoder.encode(this.tok, "UTF8"); } catch (UnsupportedEncodingException e) { logger.info("Cannot encode token", e); } return token; } public static UserToken createUserToken(String userNm, String passwd, Http.Request req) { return createUserToken(userNm, passwd, getClientIP(req)); } /** * * @param userNm * @param passwd * @return */ public static UserToken createUserToken(String userNm, String passwd, String origIP) { if (userNm == null || passwd == null) { logger.info("Cannot create token"); return null; } UserToken ut = new UserToken(); try { ut.name = userNm; ut.password = passwd; ut.origIP = origIP; ut.timeStamp = String.valueOf(System.currentTimeMillis()); ut.tok = encrypt(ut.timeStamp + "/" + userNm + "/" + passwd + "/" + origIP); } catch (Exception e) { logger.info("cannot create token", e); return null; } if (ut.tok == null) { logger.info("cannot encrypt token !"); return null; } logger.trace("created UserToken: " + ut.tok + " length=" + ut.tok.length()); return ut; } /** * * @param token * @param env * @return */ public static UserToken createUserToken(String token, Http.Request req) { if (token == null) { logger.info("Cannot create token"); return null; } UserToken ut = new UserToken(); logger.trace("create UserToken from: " + token + " length=" + token.length()); try { String decrypted = decrypt(token); if (decrypted == null) { logger.info("Cannot decrypt token."); return null; } int idx = decrypted.indexOf("/"); String timeStr = decrypted.substring(0, idx++); ut.timeStamp = timeStr; long time = Long.parseLong(timeStr); long currTime = System.currentTimeMillis(); long diff = currTime - time; if (diff > UserToken.TIME_OUT) { logger.info("token has expired for " + diff + " ms; timeout=" + UserToken.TIME_OUT + " ms."); return null; } int idx1 = decrypted.indexOf("/", idx); ut.name = decrypted.substring(idx, idx1++); int idx2 = decrypted.indexOf("/", idx1); ut.password = decrypted.substring(idx1, idx2++); ut.origIP = decrypted.substring(idx2); ut.tok = token; if (req != null) { // validate token IP String clientIP = getClientIP(req); if (!ut.origIP.equalsIgnoreCase(clientIP)) { logger.info("Token orginated from different IP than request"); return null; } } } catch (Exception e) { logger.info("cannot create token", e); return null; } return ut; } private static String encrypt(String in) { try { Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, KEYSPEC); byte[] encrypted = cipher.doFinal(in.getBytes()); return Base64.encode(encrypted); } catch (Exception e) { logger.info("failed encrypting string ...", e); return in; } } private static String decrypt(String in) { try { byte[] base64Decoded = Base64.decode(in); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.DECRYPT_MODE, KEYSPEC); byte[] plainText = cipher.doFinal(base64Decoded); return new String(plainText); } catch (Exception e) { logger.info("failed decrypting string ...", e); return in; } } /** * @param ctx * @return */ private static String getClientIP(Http.Request req) { if (checkIP(req, "HTTP_CLIENT_IP")) { return req.getHeader("HTTP_CLIENT_IP"); } // foreach (explode(",",$_SERVER["HTTP_X_FORWARDED_FOR"]) as $ip) { // if (checkIP(trim($ip))) return $ip; // } if (checkIP(req, "HTTP_X_FORWARDED")) { return req.getHeader("HTTP_X_FORWARDED"); } else if (checkIP(req, "HTTP_X_CLUSTER_CLIENT_IP")) { return req.getHeader("HTTP_X_CLUSTER_CLIENT_IP"); } else if (checkIP(req, "HTTP_FORWARDED_FOR")) { return req.getHeader("HTTP_FORWARDED_FOR"); } else if (checkIP(req, "HTTP_FORWARDED")) { return req.getHeader("HTTP_FORWARDED"); } else { return req.getHeader("REMOTE_ADDR"); } } private static boolean checkIP(Http.Request req, String headerAttrNm) { String ip = req.getHeader(headerAttrNm); // TODO - additionally I could validate that this is in fact an IP if (ip != null && !ip.isEmpty()) { return true; } return false; } // private String asHex(byte buf[]) { // StringBuffer strbuf = new StringBuffer(buf.length * 2); // for (int i = 0; i < buf.length; i++) { // if ((buf[i] & 0xff) < 0x10) // strbuf.append("0"); // strbuf.append(Long.toString(buf[i] & 0xff, 16)); // } // return strbuf.toString(); // } public static void main(String[] args) { UserToken ut = UserToken.createUserToken("aally", "parola", "127.0.0.1"); System.out.println(ut.tok); UserToken ut1 = UserToken.createUserToken(ut.tok, null); System.out.println(ut1.name + " : " + ut.password + " : " + ut.origIP + " : " + ut.timeStamp + " => " + ut.tok.equals(ut.tok)); } }