// 2012-08-29 johnpfeiffer requires JohnStringUtils, validation-api, bval-core, bval-jsr303, commons-beanutils-core, commons-lang3
import java.util.ArrayList;
import java.util.Set;
import javax.naming.InvalidNameException;
import javax.naming.ldap.LdapName;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
class AuthgatewayService
{
protected static final String PARAMACTIVE = "active";
protected static final String PARAMHOST = "ldapServerHost";
protected static final String PARAMPORT = "ldapServerPort";
protected static final String PARAMSSL = "ldapSSL";
protected static final String PARAMBASEDN = "ldapBaseDn";
protected static final String PARAMUSERDN = "ldapManagerUserDn";
protected static final String PARAMUSERDNPASSWORD = "ldapManagerPassword";
protected static final String PARAMSEARCHBASE = "ldapUserSearchBase";
protected static final String PARAMLDAPUSERATTRIBUTE = "ldapUserAttribute";
protected static final String PARAMSEARCHFILTER = "ldapUserSearchFilter";
protected static final String PARAMSEARCHSUBTREE = "ldapSearchSubtree";
protected static final String PARAMSEARCHGROUPS = "ldapSearchGroups";
protected static final String PARAMSEARCHGROUPSBASE = "ldapGroupSearchBase";
protected static final String PARAMSEARCHGROUPSUSERATTRIBUTE = "ldapGroupRoleAttributeName";
protected static final String PARAMSEARCHGROUPSMEMBERATTRIBUTE = "ldapGroupMemberAttributeName";
private boolean active = true;
@NotNull( message = "ERROR: hostname cannot be null" )
private String ldapServerHost;
@Min( value = 0 , message = "ERROR: port number too small, must be between 0 and 65535" )
@Max( value = 65535 , message = "ERROR: port number too large, must be between 0 and 65535" )
private int port = 389;
private boolean ldapSSL = false;
@NotNull( message = "ERROR: baseDN cannot be null" )
private LdapName ldapBaseDn;
@NotNull( message = "ERROR: binding user DN cannot be null" )
private LdapName ldapManagerUserDn;
@NotNull( message = "ERROR: binding user password cannot be null" )
private String ldapManagerPassword;
@NotNull( message = "ERROR: user attribute cannot be null" )
private String ldapUserAttribute = "sAMAccountName"; // ldap uses uid
@NotNull( message = "ERROR: User search filter cannot be null" )
private String ldapUserSearchFilter = "";
@NotNull( message = "ERROR: User search base cannot be null" )
private LdapName ldapUserSearchBase;
private boolean ldapSearchSubtree = true;
private boolean ldapSearchGroups = false;
@NotNull( message = "ERROR: Groups search base cannot be null" )
private LdapName ldapGroupSearchBase;
@NotNull( message = "ERROR: Group search attribute cannot be null" )
private String ldapGroupRoleAttributeName;
@NotNull( message = "ERROR: Group search member attribute cannot be null" )
private String ldapGroupMemberAttributeName;
public static class Builder
{
private boolean active = true;
private String ldapServerHost;
private int port = 389;
private boolean ldapSSL = false;
private LdapName ldapBaseDn;
private LdapName ldapManagerUserDn;
private String ldapManagerPassword;
private String ldapUserAttribute;
private String ldapUserSearchFilter = "";
private LdapName ldapUserSearchBase;
private boolean ldapSearchSubtree = true;
private boolean ldapSearchGroups = false;
private LdapName ldapGroupSearchBase;
private String ldapGroupRoleAttributeName;
private String ldapGroupMemberAttributeName;
Builder()
{
try
{
ldapGroupSearchBase = new LdapName( "cn=Users" );
}catch( InvalidNameException e )
{
throw new IllegalArgumentException( "ERROR: default constructor search base is invalid " + e.getMessage() );
}
ldapGroupRoleAttributeName = "cn";
ldapGroupMemberAttributeName = "member";
}
public Builder active( boolean value )
{
this.active = value;
return this;
}
public Builder hostname( String value )
{
this.ldapServerHost = value;
return this;
}
public Builder port( int value )
{
this.port = value;
return this;
}
public Builder ldapSSL( boolean value )
{
this.ldapSSL = value;
return this;
}
public Builder ldapBaseDn( LdapName value )
{
this.ldapBaseDn = value;
return this;
}
public Builder ldapManagerUserDn( LdapName value )
{
this.ldapManagerUserDn = value;
return this;
}
public Builder ldapManagerPassword( String value )
{
this.ldapManagerPassword = value;
return this;
}
public Builder ldapUserAttribute( String value )
{
this.ldapUserAttribute = value;
return this;
}
public Builder ldapUserSearchFilter( String value )
{
this.ldapUserSearchFilter = value;
return this;
}
public Builder ldapUserSearchBase( LdapName value )
{
this.ldapUserSearchBase = value;
return this;
}
public Builder ldapSearchSubtree( boolean value )
{
this.ldapSearchSubtree = value;
return this;
}
public Builder ldapSearchGroups( boolean value )
{
this.ldapSearchGroups = value;
return this;
}
public Builder ldapGroupSearchBase( LdapName value )
{
this.ldapGroupSearchBase = value;
return this;
}
public Builder ldapGroupRoleAttributeName( String value )
{
this.ldapGroupRoleAttributeName = value;
return this;
}
public Builder ldapGroupMemberAttributeName( String value )
{
this.ldapGroupMemberAttributeName = value;
return this;
}
public AuthgatewayService build() throws IllegalArgumentException
{
AuthgatewayService authgateway = new AuthgatewayService( this ); // object may exist in an incomplete or illegal state
AuthgatewayServiceValidator( authgateway );
return authgateway;
}
private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
private void AuthgatewayServiceValidator( AuthgatewayService authgateway ) throws IllegalArgumentException
{
Set <ConstraintViolation <AuthgatewayService>> violations = validator.validate( authgateway );
if( !violations.isEmpty() )
{
for( ConstraintViolation <?> v : violations )
{
throw new IllegalArgumentException( v.getMessage() );
}
}
// ConstraintViolations ensure all of the values are non null
if( this.active ) // only apply constraints to an active service
{
if( this.ldapServerHost.isEmpty() )
{
throw new IllegalArgumentException( "ERROR: hostname cannot be empty" );
}
if( JohnStringUtils.containsWhiteSpace( ldapServerHost ) )
{
throw new IllegalArgumentException( "ERROR: hostname cannot contain whitespace" );
}
if( this.ldapManagerPassword.isEmpty() )
{
throw new IllegalArgumentException( "ERROR: password cannot be empty" );
}
if( JohnStringUtils.containsWhiteSpace( ldapManagerPassword ) )
{
throw new IllegalArgumentException( "ERROR: password cannot contain whitespace" );
}
if( !this.ldapUserAttribute.equalsIgnoreCase( "uid" ) && !this.ldapUserAttribute.toLowerCase().equalsIgnoreCase( "samaccountname" ) )
{
throw new IllegalArgumentException( "ERROR: userAttribute is unsupported: " + this.ldapUserAttribute );
}
}
}
} // end inner class Builder
private AuthgatewayService( Builder builder ) throws IllegalArgumentException
{
this.active = builder.active;
this.ldapServerHost = builder.ldapServerHost;
this.port = builder.port;
this.ldapSSL = builder.ldapSSL;
this.ldapBaseDn = builder.ldapBaseDn;
this.ldapManagerUserDn = builder.ldapManagerUserDn;
this.ldapManagerPassword = builder.ldapManagerPassword;
this.ldapUserAttribute = builder.ldapUserAttribute;
this.ldapUserSearchFilter = builder.ldapUserSearchFilter;
this.ldapUserSearchBase = builder.ldapUserSearchBase;
this.ldapSearchSubtree = builder.ldapSearchSubtree;
this.ldapSearchGroups = builder.ldapSearchGroups;
this.ldapGroupSearchBase = builder.ldapGroupSearchBase;
this.ldapGroupRoleAttributeName = builder.ldapGroupRoleAttributeName;
this.ldapGroupMemberAttributeName = builder.ldapGroupMemberAttributeName;
}
protected boolean isActive()
{
return active;
}
protected String TestConnection() throws IllegalArgumentException
{
LdapSearch ldaptest = new LdapSearch.Builder().hostname( ldapServerHost ).port( port ).baseDN( ldapBaseDn ).bindUserDN( ldapManagerUserDn )
.password( ldapManagerPassword ).userAttribute( ldapUserAttribute ).searchBase( ldapUserSearchBase ).build();
ArrayList <String> queryResults = ldaptest.queryForBindingUser();
return queryResults.toString();
}
// primary use is for saving to persistence (file)
protected String getString( String header )
{
String newline = System.getProperty( "line.separator" );
StringBuilder strb = new StringBuilder();
strb.append( header + PARAMACTIVE + "=" + active + newline );
strb.append( header + PARAMHOST + "=" + ldapServerHost + newline );
strb.append( header + PARAMPORT + "=" + port + newline );
strb.append( header + PARAMSSL + "=" + ldapSSL + newline );
strb.append( header + PARAMBASEDN + "=" + ldapBaseDn + newline );
strb.append( header + PARAMUSERDN + "=" + ldapManagerUserDn + newline );
strb.append( header + PARAMUSERDNPASSWORD + "=" + ldapManagerPassword + newline );
strb.append( header + PARAMSEARCHBASE + "=" + ldapUserSearchBase + newline );
strb.append( header + PARAMLDAPUSERATTRIBUTE + "=" + ldapUserAttribute + newline );
strb.append( header + PARAMSEARCHFILTER + "=" + ldapUserSearchFilter + newline );
strb.append( header + PARAMSEARCHSUBTREE + "=" + ldapSearchSubtree + newline );
strb.append( header + PARAMSEARCHGROUPS + "=" + ldapSearchGroups + newline );
strb.append( header + PARAMSEARCHGROUPSBASE + "=" + ldapGroupSearchBase + newline );
strb.append( header + PARAMSEARCHGROUPSUSERATTRIBUTE + "=" + ldapGroupRoleAttributeName + newline );
strb.append( header + PARAMSEARCHGROUPSMEMBERATTRIBUTE + "=" + ldapGroupMemberAttributeName + newline );
return strb.toString();
}
protected String getHTMLForm()
{
StringBuilder strb = new StringBuilder();
String newline = "<br /><br />" + System.getProperty( "line.separator" );
String space = " ";
strb.append( "<label>AD/LDAP Server Address: </label>" + space + space + " <input type='text' name='" + PARAMHOST + "' size='40' value='"
+ JohnStringUtils.safeHTML( ldapServerHost ) + "' /> " + newline );
strb.append( "<label>AD/LDAP Port: </label> " );
strb.append( "<input type='radio' name='" + PARAMPORT + "' value='389' " );
if( port == 389 )
{
strb.append( "checked='checked'" );
}
strb.append( " /> <label>389</label>" );
strb.append( "<input type='radio' name='" + PARAMPORT + "' value='636' " );
if( port == 636 )
{
strb.append( "checked='checked'" );
}
strb.append( " /> <label> 636 </label> " + newline );
// SSL true false is handled in Controller depending if port is 389 or 636
strb.append( "<label>AD/LDAP Base DN: </label>" + space + space + "<input type='text' id='" + PARAMBASEDN + "' name='" + PARAMBASEDN
+ "' size='40' value='" + JohnStringUtils.safeHTML( ldapBaseDn.toString() ) + "' /> " + newline );
strb.append( "<label>AD/LDAP Binding User DN: </label>" + space + space + "<input type='text' id='" + PARAMUSERDN + "' name='" + PARAMUSERDN
+ "' size='80' value='" + JohnStringUtils.safeHTML( ldapManagerUserDn.toString() ) + "' /> " + newline );
strb.append( "<label>AD/LDAP Binding User password: </label>" + space + space + "<input type='password' id='" + PARAMUSERDNPASSWORD + "' name='"
+ PARAMUSERDNPASSWORD + "' size='40' /> " + newline );
strb.append( "<label>AD/LDAP Search Base (if left empty it will use the BaseDN): </label>" + space + space + "<input type='text' id='" + PARAMSEARCHBASE
+ "' name='" + PARAMSEARCHBASE + "' size='80' value='" + JohnStringUtils.safeHTML( ldapUserSearchBase.toString() ) + "' /> " + newline );
strb.append( "<label>Search Attribute (AD = sAMAccountName , LDAP = uid) </label> " );
strb.append( "<input type='radio' name='" + PARAMLDAPUSERATTRIBUTE + "' value='sAMAccountName' " );
if( "sAMAccountName".equals( ldapUserAttribute ) )
{
strb.append( "checked='checked'" );
}
strb.append( " /> <label>sAMAccountName</label>" );
strb.append( "<input type='radio' name='" + PARAMLDAPUSERATTRIBUTE + "' value='uid' " );
if( "uid".equals( ldapUserAttribute ) )
{
strb.append( "checked='checked'" );
}
strb.append( " /> <label> uid </label> " + newline );
strb.append( "<label>AD/LDAP Search Subtree: </label><input name='" + PARAMSEARCHSUBTREE + "' type='checkbox' " );
if( ldapSearchSubtree )
{
strb.append( " checked='checked' " );
}
strb.append( " /> " + newline );
return strb.toString();
}
// TODO: more generic validation from the Data Model
protected static String getValidateFormOnSubmit()
{
StringBuilder strb = new StringBuilder();
String newline = System.getProperty( "line.separator" );
String javascriptBackslash = "\\\\";
strb.append( newline + "<script type='text/javascript'>" + newline );
strb.append( "function validateFormOnSubmit() {" + newline );
strb.append( "var userDN=document.getElementById( '" + PARAMUSERDN + "' ).value;" + newline );
strb.append( "if( userDN==null || userDN=='' )" + newline );
strb.append( "{ alert('Error: userDN cannot be blank');" + newline );
strb.append( " return false; }" + newline );
strb.append( "if( userDN.indexOf( '" + javascriptBackslash + "' ) != -1 )" + newline );
strb.append( "{ alert('Error: userDN should not include back slashes');" + newline );
strb.append( " return false; }" + newline );
strb.append( "var baseDN=document.getElementById( '" + PARAMBASEDN + "' ).value;" + newline );
strb.append( "if( baseDN==null || baseDN=='' )" + newline );
strb.append( "{ alert('Error: baseDN cannot be blank');" + newline );
strb.append( " return false; }" + newline );
strb.append( "if( baseDN.indexOf( '" + javascriptBackslash + "' ) != -1 )" + newline );
strb.append( "{ alert('Error: BaseDN should not include back slashes');" + newline );
strb.append( " return false; }" + newline );
strb.append( "var searchBase=document.getElementById( '" + PARAMSEARCHBASE + "' ).value;" + newline );
strb.append( "var baseDNUpperCase=baseDN.toUpperCase();" );
strb.append( "var searchBaseUpperCase=searchBase.toUpperCase();" );
strb.append( "if( searchBaseUpperCase.indexOf( baseDNUpperCase ) != -1 )" + newline );
strb.append( "{ alert('Error: Search Base does not need to contain the BaseDN');" + newline );
strb.append( " return false; }" + newline );
strb.append( "if( searchBase.indexOf( '" + javascriptBackslash + "' ) != -1 )" + newline );
strb.append( "{ alert('Error: searchBase should not include back slashes');" + newline );
strb.append( " return false; }" + newline );
strb.append( "var password=document.getElementById( '" + PARAMUSERDNPASSWORD + "' ).value;" + newline );
strb.append( "if( password==null || password=='' )" + newline );
strb.append( "{ alert('Error: password cannot be blank');" + newline );
strb.append( " return false; }" + newline );
strb.append( "if( password.indexOf( '" + javascriptBackslash + "' ) != -1 )" + newline );
strb.append( "{ alert('Error: password should not include back slashes');" + newline );
strb.append( " return false; }" + newline );
strb.append( "}" + newline );
strb.append( "</script>" + newline );
return strb.toString();
}
} // end class