package org.joget.commons.util;
import java.net.URI;
import java.util.List;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import org.owasp.csrfguard.CsrfGuard;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
/**
* Utility methods used by security feature
*
*/
@Service("securityUtil")
public class SecurityUtil implements ApplicationContextAware {
public final static String ENVELOPE = "%%%%";
private static ApplicationContext appContext;
private static DataEncryption de;
private static NonceGenerator ng;
/**
* Utility method to retrieve the ApplicationContext of the system
* @return
*/
public static ApplicationContext getApplicationContext() {
return appContext;
}
/**
* Method used by system to set an ApplicationContext
* @param context
* @throws BeansException
*/
public void setApplicationContext(ApplicationContext context) throws BeansException {
appContext = context;
}
/**
* Sets a data encryption implementation
* @param deImpl
*/
public void setDataEncryption(DataEncryption deImpl) {
if (de == null) {
de = deImpl;
}
}
/**
* Gets the data encryption implementation
* @return
*/
public static DataEncryption getDataEncryption() {
if (de == null) {
try {
de = (DataEncryption) getApplicationContext().getBean("dataEncryption");
} catch (Exception e) {
}
}
return de;
}
/**
* Sets a nonce generator implementation
* @param ngImpl
*/
public void setNonceGenerator(NonceGenerator ngImpl) {
if (ng == null) {
ng = ngImpl;
}
}
/**
* Gets the nonce generator implementation
* @return
*/
public static NonceGenerator getNonceGenerator() {
if (ng == null) {
try {
ng = (NonceGenerator) getApplicationContext().getBean("nonceGenerator");
} catch (Exception e) {
}
}
return ng;
}
/**
* Encrypt raw content if data encryption implementation is exist
* @param rawContent
* @return
*/
public static String encrypt(String rawContent) {
if (rawContent != null && getDataEncryption() != null) {
try {
return ENVELOPE + getDataEncryption().encrypt(rawContent) + ENVELOPE;
} catch (Exception e) {
//Ignore
}
}
return rawContent;
}
/**
* Decrypt protected content if data encryption implementation is exist
* @param protectedContent
* @return
*/
public static String decrypt(String protectedContent) {
if (protectedContent.startsWith(ENVELOPE) && protectedContent.endsWith(ENVELOPE) && getDataEncryption() != null) {
try {
String tempProtectedContent = cleanPrefixPostfix(protectedContent);
return getDataEncryption().decrypt(tempProtectedContent);
} catch (Exception e) {
//Ignore
}
}
return protectedContent;
}
/**
* Computes the hash of a raw content if data encryption implementation is exist
* @param rawContent
* @param randomSalt
* @return
*/
public static String computeHash(String rawContent, String randomSalt) {
if (rawContent != null && !rawContent.isEmpty()) {
if (getDataEncryption() != null) {
return ENVELOPE + getDataEncryption().computeHash(rawContent, randomSalt) + ENVELOPE;
} else {
return StringUtil.md5Base16(rawContent);
}
}
return rawContent;
}
/**
* Verify the hash is belong to the raw content if data encryption
* implementation is exist
* @param hash
* @param randomSalt
* @param rawContent
* @return
*/
public static Boolean verifyHash(String hash, String randomSalt, String rawContent) {
if (hash != null && !hash.isEmpty() && rawContent != null && !rawContent.isEmpty()) {
if (hash.startsWith(ENVELOPE) && hash.endsWith(ENVELOPE) && getDataEncryption() != null) {
hash = cleanPrefixPostfix(hash);
return getDataEncryption().verifyHash(hash, randomSalt, rawContent);
} else {
return hash.equals(StringUtil.md5Base16(rawContent));
}
}
return false;
}
/**
* Generate a random salt value if data encryption implementation is exist
* @return
*/
public static String generateRandomSalt() {
if (getDataEncryption() != null) {
return getDataEncryption().generateRandomSalt();
}
return "";
}
/**
* Check the content is a wrapped in a security envelop if data encryption
* implementation is exist
* @param content
* @return
*/
public static boolean hasSecurityEnvelope(String content) {
if (content != null && content.startsWith(ENVELOPE) && content.endsWith(ENVELOPE) && getDataEncryption() != null) {
return true;
}
return false;
}
protected static String cleanPrefixPostfix(String content) {
content = content.replaceAll(ENVELOPE, "");
return content;
}
/**
* Generate a nonce value based on attributes if Nonce Generator implementation is exist
* @param attributes
* @param lifepanHour
* @return
*/
public static String generateNonce(String[] attributes, int lifepanHour) {
if (getNonceGenerator() != null) {
try {
return getNonceGenerator().generateNonce(attributes, lifepanHour);
} catch (Exception e) {
//Ignore
}
}
return "";
}
/**
* Verify the nonce is a valid nonce against the attributes if Nonce
* Generator implementation is exist
* @param nonce
* @param attributes
* @return
*/
public static boolean verifyNonce(String nonce, String[] attributes) {
if (nonce != null && !nonce.isEmpty() && getNonceGenerator() != null) {
try {
return getNonceGenerator().verifyNonce(nonce, attributes);
} catch (Exception e) {
//Ignore
}
}
return false;
}
/**
* Gets the domain name from a given URL
* @param url
* @return
*/
public static String getDomainName(String url) {
try {
URI uri = new URI(url);
return uri.getHost();
} catch (Exception e) {}
return null;
}
/**
* Verify the domain name against a whitelist
* @param domain
* @param whitelist
* @return
*/
public static boolean isAllowedDomain(String domain, List<String> whitelist) {
return whitelist != null && domain != null && whitelist.contains(domain);
}
/**
* Returns the name of the CRSF token
* @return
*/
public static String getCsrfTokenName() {
CsrfGuard csrfGuard = CsrfGuard.getInstance();
return csrfGuard.getTokenName();
}
/**
* Returns the value of the CRSF token in the request
* @param request
* @return
*/
public static String getCsrfTokenValue(HttpServletRequest request) {
CsrfGuard csrfGuard = CsrfGuard.getInstance();
return csrfGuard.getTokenValue(request);
}
/**
* Validates a boolean String
* @param input
* @return
* @throws IllegalArgumentException if the input is invalid
*/
public static Boolean validateBooleanInput(Boolean input) throws IllegalArgumentException {
return input;
}
/**
* Validates an input String
* @param input
* @return
* @throws IllegalArgumentException if the input is invalid
*/
public static String validateStringInput(String input) throws IllegalArgumentException {
return validateInput(input, "^[0-9a-zA-Z_\\-\\.\\#\\:]+$");
}
/**
* Validates input
* @param input
* @param patternStr
* @return
* @throws IllegalArgumentException if the input is invalid
*/
public static String validateInput(String input, String patternStr) throws IllegalArgumentException {
Pattern pattern = Pattern.compile(patternStr);
if (input != null && !input.isEmpty() && !pattern.matcher(input).matches()) {
throw new IllegalArgumentException("Invalid input: " + input);
}
return input;
}
}