Skip to content

Instantly share code, notes, and snippets.

@johnymachine
Created August 7, 2017 11:31
Show Gist options
  • Save johnymachine/812d4c21e822f32fb953b4d5086c01c0 to your computer and use it in GitHub Desktop.
Save johnymachine/812d4c21e822f32fb953b4d5086c01c0 to your computer and use it in GitHub Desktop.
Thingworx custom header Authentificator with optional blacklist or whitelists.
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