Created
August 7, 2017 11:31
-
-
Save johnymachine/812d4c21e822f32fb953b4d5086c01c0 to your computer and use it in GitHub Desktop.
Thingworx custom header Authentificator with optional blacklist or whitelists.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package cz.foxon.auth.headerauthentificator; | |
import javax.servlet.http.HttpServletRequest; | |
import javax.servlet.http.HttpServletResponse; | |
import org.slf4j.Logger; | |
import com.thingworx.logging.LogUtilities; | |
import com.thingworx.metadata.annotations.ThingworxConfigurationTableDefinition; | |
import com.thingworx.metadata.annotations.ThingworxConfigurationTableDefinitions; | |
import com.thingworx.metadata.annotations.ThingworxDataShapeDefinition; | |
import com.thingworx.metadata.annotations.ThingworxFieldDefinition; | |
import com.thingworx.security.authentication.AuthenticationUtilities; | |
import com.thingworx.security.authentication.AuthenticatorException; | |
import com.thingworx.security.authentication.CustomAuthenticator; | |
import com.thingworx.types.InfoTable; | |
import com.thingworx.types.collections.ValueCollection; | |
@ThingworxConfigurationTableDefinitions(tables = { | |
@ThingworxConfigurationTableDefinition(name = "HeaderProcessing", description = "Please select options for header processing and filtering.", isMultiRow = false, ordinal = 0, dataShape = @ThingworxDataShapeDefinition(fields = { | |
@ThingworxFieldDefinition(name = "HeaderName", description = "Name of the header containing username.", baseType = "STRING", ordinal = 0, aspects = { | |
"isRequired:true", "defaultValue:iv-user", "friendlyName:Header Name" }), | |
@ThingworxFieldDefinition(name = "HeaderValueTransformation", description = "If there is problem with letter casing, use transformation.", baseType = "STRING", ordinal = 1, aspects = { | |
"isRequired:true", "defaultValue:N", "selectOptions:N:DoNothing|L:LowerCase|U:UpperCase", | |
"friendlyName:Header Value Transformation" }), | |
@ThingworxFieldDefinition(name = "UserFiltering", description = "Select how to handle user filtering.", baseType = "STRING", ordinal = 2, aspects = { | |
"isRequired:true", "defaultValue:N", | |
"selectOptions:N:DoNothing|B:UseAsBlacklist|W:UseAsWhitelist", | |
"friendlyName:User Filtering" }), | |
@ThingworxFieldDefinition(name = "AddressFiltering", description = "Select how to handle address filtering.", baseType = "STRING", ordinal = 3, aspects = { | |
"isRequired:true", "defaultValue:N", | |
"selectOptions:N:DoNothing|B:UseAsBlacklist|W:UseAsWhitelist", | |
"friendlyName:Address Filtering" }) })), | |
@ThingworxConfigurationTableDefinition(name = "UserFilter", description = "User Filter", isMultiRow = true, ordinal = 1, dataShape = @ThingworxDataShapeDefinition(fields = { | |
@ThingworxFieldDefinition(name = "Active", description = "Select if option is active", baseType = "BOOLEAN", ordinal = 0, aspects = { | |
"isRequired:true", "friendlyName:Active" }), | |
@ThingworxFieldDefinition(name = "Note", description = "Note or Description", baseType = "STRING", ordinal = 1, aspects = { | |
"friendlyName:Note" }), | |
@ThingworxFieldDefinition(name = "User", description = "User", baseType = "USERNAME", ordinal = 2, aspects = { | |
"isRequired:true", "friendlyName:User" }) })), | |
@ThingworxConfigurationTableDefinition(name = "AddressFilter", description = "Address Filter", isMultiRow = true, ordinal = 2, dataShape = @ThingworxDataShapeDefinition(fields = { | |
@ThingworxFieldDefinition(name = "Active", description = "Active", baseType = "BOOLEAN", ordinal = 0, aspects = { | |
"isRequired:true", "friendlyName:Active" }), | |
@ThingworxFieldDefinition(name = "Note", description = "Note or Description", baseType = "STRING", ordinal = 1, aspects = { | |
"friendlyName:Note" }), | |
@ThingworxFieldDefinition(name = "Address", description = "Ip Address", baseType = "STRING", ordinal = 2, aspects = { | |
"isRequired:true", "friendlyName:IpAddress" }) })) }) | |
public final class HeaderAuthentificator extends CustomAuthenticator { | |
private static final long serialVersionUID = 7916496069061091834L; | |
protected static Logger _logger = LogUtilities.getInstance().getApplicationLogger(HeaderAuthentificator.class); | |
private String transformedUserName; | |
public HeaderAuthentificator() { | |
} | |
private String applyHeaderTransformation(String userName){ | |
if(userName == null){ | |
return null; | |
} | |
String headerValueTransformation = this.getStringConfigurationSetting("HeaderProcessing", "HeaderValueTransformation"); | |
if (headerValueTransformation.equals("L")){ | |
return userName.toLowerCase(); | |
} | |
else if (headerValueTransformation.equals("U")){ | |
return userName.toUpperCase(); | |
} | |
else { | |
return userName; | |
} | |
} | |
private boolean isUserAllowed(String userName){ | |
// Get UserFiltering options | |
String userFiltering = this.getStringConfigurationSetting("HeaderProcessing", "UserFiltering"); | |
// Allow if disabled | |
if (userFiltering.equals("N")){ | |
return true; | |
} | |
// Search in filter list | |
Boolean isUserOnFilterList = false; | |
try { | |
InfoTable userFilter = this.GetConfigurationTable("UserFilter"); | |
for (ValueCollection row : userFilter.getRows()) { | |
if((Boolean) row.getValue("Active") && (row.getStringValue("User")).equals(userName)){ | |
isUserOnFilterList = true; | |
} | |
} | |
} catch (Exception e) { | |
_logger.error(HeaderAuthentificator.class.getSimpleName() + " error: Unable to retrieve UserFilter list, all users are blocked."); | |
return false; | |
} | |
// Decide based on list type | |
if(isUserOnFilterList && userFiltering.equals("W") || !isUserOnFilterList && userFiltering.equals("B")){ | |
return true; | |
} | |
else { | |
return false; | |
} | |
} | |
private boolean isRemoteAddressAllowed(String remoteAddress){ | |
// Get addressFiltering options | |
String addressFiltering = this.getStringConfigurationSetting("HeaderProcessing", "AddressFiltering"); | |
// Allow if disabled | |
if (addressFiltering.equals("N")){ | |
return true; | |
} | |
// Search in filter list | |
Boolean isAddressOnFilterList = false; | |
try { | |
InfoTable addressFilter = this.GetConfigurationTable("AddressFilter"); | |
for (ValueCollection row : addressFilter.getRows()) { | |
if((Boolean) row.getValue("Active") && (row.getStringValue("Address")).equals(remoteAddress)){ | |
isAddressOnFilterList = true; | |
} | |
} | |
} catch (Exception e) { | |
_logger.error(HeaderAuthentificator.class.getSimpleName() + " error: Unable to retrieve AddressFilter list, all Adresses are blocked."); | |
return false; | |
} | |
// Decide based on list type | |
if(isAddressOnFilterList && addressFiltering.equals("W") || !isAddressOnFilterList && addressFiltering.equals("B")){ | |
return true; | |
} | |
else { | |
return false; | |
} | |
} | |
@Override | |
public void authenticate(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws AuthenticatorException { | |
// Get transformedUserName | |
String transformedUserName = this.transformedUserName; | |
try { | |
//TW 7.3 | |
AuthenticationUtilities.validateEnabledThingworxUser(transformedUserName); | |
//TW 7.4 and up | |
//AuthenticationUtilities.validateThingworxUser(transformedUserName); | |
this.setCredentials(transformedUserName); | |
} catch (Exception e) { | |
//this.setRequiresChallenge(false); | |
throw new AuthenticatorException( | |
HeaderAuthentificator.class.getSimpleName() + " error: failed to login, user ("+ transformedUserName +") is disabled, locked or does not exists."); | |
} | |
} | |
@Override | |
public void issueAuthenticationChallenge(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws AuthenticatorException { | |
throw new AuthenticatorException(HeaderAuthentificator.class.getSimpleName() + " error: Authentificator failed, passing to next in order of priority."); | |
} | |
@Override | |
public boolean matchesAuthRequest(HttpServletRequest httpRequest) throws AuthenticatorException { | |
// Retrieve userName from header | |
String headerName = this.getStringConfigurationSetting("HeaderProcessing", "HeaderName"); | |
String userName = httpRequest.getHeader(this.getStringConfigurationSetting("HeaderProcessing", "HeaderName")); | |
String transformedUserName = this.applyHeaderTransformation(userName); | |
// Retrieve remoteAddress from header | |
String remoteAddress = httpRequest.getRemoteAddr(); | |
// Set conditions | |
Boolean isHeaderMissing = (transformedUserName == null) ? true : false; | |
Boolean isUserAllowed = this.isUserAllowed(transformedUserName); | |
Boolean isRemoteAddressAllowed = this.isRemoteAddressAllowed(remoteAddress); | |
if (isHeaderMissing || !isUserAllowed || !isRemoteAddressAllowed){ | |
// Set logging outputs | |
if(isHeaderMissing){ | |
transformedUserName = "Unknown (missing header)"; | |
} | |
else if (!isUserAllowed){ | |
transformedUserName = transformedUserName + " (not allowed)"; | |
} | |
if (!isRemoteAddressAllowed) { | |
remoteAddress = remoteAddress + " (not allowed)"; | |
} | |
// Log error | |
_logger.warn(HeaderAuthentificator.class.getSimpleName() + " error: Unauthorized access attempt by user: " + transformedUserName + " from: " + remoteAddress + " using header: " + headerName + "."); | |
// Do not continue with authentication | |
return false; | |
} | |
else { | |
// Continue with authentication | |
this.transformedUserName = transformedUserName; | |
return true; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment