Our developers use Java on Linux for various things (like checking membership of groups etc). It works - no problem with that!
The problem is that they have hardcoded the servernames of our Domain Controllers (LDAP-servers) in their code. So now when we need to replace them with newer DCs, they need to change the code.
Active Directory by nature is redundant. The domain name (example: domain.local) is a round-robin of all the DC:s available for our AD.
Is there any way for the developer to NOT specify Domain Controller server names but simply the Active Directory domain name and then their Linux server will find the DC:s available and use whichever one is up and running?
Examples/links appreciated. Thanks!
Obviously, the server name should at least be configurable, not hard coded into the application.
However, you should be able to find the server by looking up a special DNS record, namely a SRV record for _ldap._tcp.DOMAINNAME. The linux servers have to be configured to use the same DNS server as your AD updates.
To determine whether this is feasible, run the command host -t srv _ldap._tcp.DOMAINNAME on your linux server
See also Querying the DNS service records to find the hostname and TCP/IP provides some info on how to look up SRV records in java, and https://community.oracle.com/blogs/kohsuke/2008/06/12/more-active-directory-integration-java
We use the follow code that work on a large amount of systems:
/**
* Detect the default LDAP server
* #return server:port or null
*/
String getDefaultLdapHost() {
try {
Hashtable<String, String> env = new Hashtable();
env.put( "java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory" );
DirContext dns = new InitialDirContext( env );
InetAddress address = InetAddress.getLocalHost();
String domain = address.getCanonicalHostName();
if( domain.equals( address.getHostAddress() ) ) {
//domain is a ip address
domain = getDnsPtr( dns );
}
int idx = domain.indexOf( '.' );
if( idx < 0 ) {
//computer is not in a domain? We will look in the DNS self.
domain = getDnsPtr( dns );
idx = domain.indexOf( '.' );
if( idx < 0 ) {
//computer is not in a domain
return null;
}
}
domain = domain.substring( idx + 1 );
Attributes attrs = dns.getAttributes( "_ldap._tcp." + domain, new String[] { "SRV" } );
Attribute attr = attrs.getAll().nextElement();
String srv = attr.get().toString();
String[] parts = srv.split( " " );
return parts[3] + ":" + parts[2];
} catch( Exception ex ) {
ex.printStackTrace();
return null;
}
}
/**
* Look for a reverse PTR record on any available ip address
* #param dns DNS context
* #return the PTR value
* #throws Exception if the PTR entry was not found
*/
private String getDnsPtr( DirContext dns ) throws Exception {
Exception exception = null;
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while(interfaces.hasMoreElements()) {
NetworkInterface nif = interfaces.nextElement();
if( nif.isLoopback() ) {
continue;
}
Enumeration<InetAddress> adresses = nif.getInetAddresses();
while(adresses.hasMoreElements()) {
InetAddress address = adresses.nextElement();
if( address.isLoopbackAddress() || address instanceof Inet6Address) {
continue;
}
String domain = address.getCanonicalHostName();
if( !domain.equals( address.getHostAddress() ) && (domain.indexOf( '.' ) > 0) ) {
return domain;
}
String ip = address.getHostAddress();
String[] digits = ip.split( "\\." );
StringBuilder builder = new StringBuilder();
builder.append( digits[3] ).append( '.' );
builder.append( digits[2] ).append( '.' );
builder.append( digits[1] ).append( '.' );
builder.append( digits[0] ).append( ".in-addr.arpa." );
try {
Attributes attrs = dns.getAttributes( builder.toString(), new String[] { "PTR" } );
return attrs.get( "PTR" ).get().toString();
} catch( Exception ex ) {
exception = ex;
}
}
}
if( exception != null ) {
throw exception;
}
throw new IllegalStateException("No network");
}
Related
This question already has answers here:
How do I compare strings in Java?
(23 answers)
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 1 year ago.
Previously I created a login system which used a HashMap, but no database. I then ran into the problem of not being able to change the passwords of the system, as they would be reset everytime I booted up the program.
It was like this:
HashMap<String, String> credentials = new HashMap<String, String>();
UserData() {
credentials.put("Manager", "adminpass");
credentials.put("Employee", "employeepass");
credentials.put("Customer", "customerpass");
}
I then realised I want to use text files to store the passwords, so I could edit them and the changes would take effect.
So I created 3 text files. adminpass.txt, employeepass.txt, customerpass.txt
They all contain the passwords which are 'adminpass', 'employeepass', 'customerpass'
With the previous system, I used .equals to compare the password of user input and the real password as it was a string. Now it's a variable, so I am using ==.
Here is my code for logging in:
public void actionPerformed(ActionEvent ae) {
if (ae.getSource() == authenticateButton) {
String roleID = usernameField.getText();
String password = String.valueOf(((JPasswordField) passwordField).getPassword());
if (credentials.containsKey(roleID)) {
if (credentials.get(roleID) == password) {
messageLabel.setForeground(Color.green);
messageLabel.setText("Login successful");
frame.dispose();
m.launch(roleID);
} else {
messageLabel.setForeground(Color.red);
messageLabel.setText("Incorrect password");
}
} else {
messageLabel.setForeground(Color.red);
messageLabel.setText("Incorrect username");
}
}
}
});
I also have read from the text files on startup, assigning what the system has read from the files to the adminpass, employeepass, and customerpass variables.
Everytime I login I get 'incorrect password' even though they're correct.
So I decided to do
System.out.println(credentials.get(roleID))
And it just returns null.
I'm completely confused here, I am grateful if anyone can help or point me in the right direction!
Thanks!
EDIT: when i use .equals(password), i get
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException: Cannot invoke "String.equals(Object)" because the return value of "java.util.HashMap.get(Object)" is null
This syntax makes no sense to me:
UserData() {
You have not provided quite enough detail to pinpoint your problem. But this error message:
"java.util.HashMap.get(Object)" is null
… tells us you likely have a problem with populating your Map.
Map.of
I suggest starting with a simple hard-coded map, to test your code. Take baby steps, building up your app piece by piece.
package work.basil.example.authenticate;
import java.util.Map;
public class App
{
public static void main ( String[] args )
{
App app = new App();
app.demo();
}
private void demo ( )
{
Map < String, String > credentials =
Map.of(
"Manager" , "adminpass" ,
"Employee" , "employeepass" ,
"Customer" , "customerpass"
);
boolean authenticatedManager = credentials.get( "Manager" ).equals( "adminpass" );
boolean authenticatedEmployee = credentials.get( "Employee" ).equals( "employeepass" );
boolean authenticatedCustomer = credentials.get( "Customer" ).equals( "customerpass" );
System.out.println( "credentials = " + credentials );
System.out.println( "authenticatedManager = " + authenticatedManager );
System.out.println( "authenticatedEmployee = " + authenticatedEmployee );
System.out.println( "authenticatedCustomer = " + authenticatedCustomer );
}
}
When run.
credentials = {Employee=employeepass, Customer=customerpass, Manager=adminpass}
authenticatedManager = true
authenticatedEmployee = true
authenticatedCustomer = true
After getting that to work, then replace Map.of hard-coding with a Map populated from your text file or database.
By the way, I'll ignore the issue of saving passwords in plain text. That is obviously the wrong way to manage authentication. I assume you are just learning/practicing, not doing real work for production use.
Populating a map
Here is how to populate a map from this example text file:
Scott,tiger
Alice,password123
Bob,letmein
Example code using NIO.2. See tutorial by Oracle.
String pathText = "/Users/basilbourque/cred.txt";
Path path = Paths.get( pathText );
Map < String, String > tempCredentials = new HashMap <>();
try ( BufferedReader reader = Files.newBufferedReader( path , StandardCharsets.UTF_8 ) )
{
String line = null;
while ( ( line = reader.readLine() ) != null )
{
System.out.println( line );
String[] parts = line.split( "," );
String user = Objects.requireNonNull( parts[ 0 ] ).strip();
String pw = Objects.requireNonNull( parts[ 1 ] ).strip();
if ( user.isBlank() || pw.isBlank() ) { System.out.println( "Add code here to deal with invalid blank inputs. " ); }
tempCredentials.put( user , pw );
}
}
catch ( IOException x )
{
System.err.format( "IOException: %s%n" , x );
}
Map < String, String > credentials = Map.copyOf( tempCredentials ); // Make an unmodifiable map, copied from temporary map.
System.out.println( "credentials = " + credentials );
When run.
Scott,tiger
Alice,password123
Bob,letmein
credentials = {Bob=letmein, Alice=password123, Scott=tiger}
Testing equality
You said:
so I am using ==.
Use == syntax only when asking if two references point to the very same object, the very same chunk of memory.
More commonly you want to compare the content of the objects to see if they are equivalent. For that, use the equals method or some similar method.
I want to delete repeated occurrence of a file in documentum leaving only one
file if it exists more than once.
Following is my code.
import com.documentum.fc.client.IDfQuery;
import com.documentum.fc.client.IDfSession;
import com.documentum.fc.client.IDfSessionManager;
import com.documentum.fc.common.DfException;
import com.documentum.fc.common.DfId;
import com.documentum.fc.common.IDfLoginInfo;
import com.documentum.operations.IDfDeleteOperation;
public class CountFiles {
// Documentum target repository where the files will be imported
private static final String REPO_NAME = "rep";
public static void main( String[] args ) throws Exception {
try {
String username = "user";
String password = "pwd";
System.out.println( "Starting to connect ..." );
IDfSessionManager sessMgr = createSessionManager( );
addIdentity( sessMgr, username, password);
IDfSession sess = sessMgr.getSession(REPO_NAME );
System.out.println( "Successfully connected to the server.");
queryDocumentum(sess);
} catch( Exception ex ) {
System.out.println( ex );
ex.printStackTrace( );
}
}
private static void queryDocumentum(IDfSession sess) throws DfException {
IDfQuery query = new DfQuery();
String queryStr= "select count(*) from dm_document where folder('/XXX/YYY', DESCEND) and object_name = 'abc.pdf' ";
query.setDQL(queryStr);
IDfCollection coll = query.execute(sess,IDfQuery.DF_EXEC_QUERY);
while(coll.next())
{
int count = coll.getValueAt(0);
if(count>1)
{
String qry = "delete dm_sysobject (all) objects where object_name='abc.pdf';";
IDfQuery q= new DfQuery();
query.setDQL(qry);
IDfCollection col = query.execute(sess,IDfQuery.DF_EXEC_QUERY);
}
}
coll.close();
}
/**
* Creates a new session manager instance. The session manager does not have
* any identities associated with it.
*
* #return a new session manager object.
* #throws DfException
*/
private static IDfSessionManager createSessionManager( )
throws Exception {
IDfClientX clientX = new DfClientX( );
IDfClient localClient = clientX.getLocalClient( );
IDfSessionManager sessMgr = localClient.newSessionManager( );
System.out.println( "Created session manager." );
return sessMgr;
}
/**
* Adds a new identity to the session manager.
*
*/
private static void addIdentity( final IDfSessionManager sm,
final String username, final String password )
throws Exception {
IDfClientX clientX = new DfClientX( );
IDfLoginInfo li = clientX.getLoginInfo( );
li.setUser( username );
li.setPassword( password );
// check if session manager already has an identity.
// if yes, remove it.
if( sm.hasIdentity( REPO_NAME ) ) {
sm.clearIdentity( REPO_NAME );
System.out.println( "Cleared identity on :" + REPO_NAME );
}
sm.setIdentity( REPO_NAME, li );
System.out.println( "Set up identity for the user." );
}
}
But something is wrong in the way that I am doing the operation. It is not working.I am not giving the path of the file here because I don't know the exact path of the file.Without giving the path of the file is it possible to delete all the occurrences of the file except one.
If you are coding the logic in Java anyway have a look at the IDfOperatiins. Also this method for doing bulk stuff is described well in the guides.
I would suggest the following changes in your DQL and in the logic, this is mostly because you are using DFC API:
DQL:
//Retrieves all the root object ids of the documents version tree where there are duplicates as their name.
//Here I thought the latest created document is the one to keep, you can adapt it to your needs - **see ORDER by clause**.
select i_chronicle_id from dm_document where folder('/XXX/YYYY', DESCEND) and object_name in (select object_name from dm_document where folder('/XXX/YYYY', DESCEND) and object_name = 'abc.pdf' group by object_name having count(*) > 1) order by r_creation_date asc;
Logic:
//"pseudo-code" - deletes the entire version tree of the document that is not the last in the returned list
while 'not last i_chronicle_id from the result collection'
//execute this dql:
`delete dm_document (all) objects where i_chronicle_id='<i_chronicle_id from the list>';`
Hope this helps,
Emilian
P.S. The DQLs I have tested against a CS 7.3
I have this code to get the external ip of instances in GCP project
private static void printInstances(Compute compute, String projectId) throws IOException {
final Compute.Instances.List instances = compute.instances().list(projectId, zoneName);
final InstanceList list = instances.execute();
if ( list.getItems() == null ) {
System.out.println("No instances found. Sign in to the Google APIs Console and create an instance at: code.google.com/apis/console");
} else {
for ( final Instance instance : list.getItems() ) {
//System.out.println(instance.toPrettyString());
System.out.println("------------- " + instance.getName() + " (" + instance.getId() + ")");
final List<NetworkInterface> networkInterfaces = instance.getNetworkInterfaces();
for ( final NetworkInterface networkInterface : networkInterfaces ) {
String extIP = null;
final List<AccessConfig> accessConfigs = networkInterface.getAccessConfigs();
for ( final AccessConfig accessConfig : accessConfigs ) { // More than one?
extIP = accessConfig.getNatIP();
}
System.out.println(" Private=[" + networkInterface.getNetworkIP() + "] Public=[" + extIP + "]");
}
}
}
}
I want to get the same (meaning accessConfig.getNatIP) form instance of GCP ManagedInstance.
Like this:
Compute.InstanceGroupManagers.ListManagedInstances listInstances =
compute.instanceGroupManagers().listManagedInstances(projectId, zoneName, groupName);
List<ManagedInstance> list = listInstances.execute().getManagedInstances();
But I have found no way to get this.
Compute.InstanceGroupManagers.ListManagedInstances contains the URLs of the instances. Then use instances.get API to get the external IP in the instance.
I am pretty new to working with http/certs/etc. I have my own hostnameVerifier set up for an apache httpclient. (Trying to allow FQDN to not match as long as cert FQDN is still internal to our domain - not the best I know - but better than nothing)
It works fine for most of the servers I have tested with, but there are a couple where I get
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
when I try to
session.getPeerCertificateChain().
I am able to view the certificate just fine when I go to the same link in firefox.
Below is the full code for the method.
#Override
public void verify( String arg0, SSLSocket arg1 ) throws IOException
{
String certFQDN = null;
SSLSession session = arg1.getSession();
javax.security.cert.X509Certificate[] certs = session.getPeerCertificateChain();
if ( certs.length > 0 )
{
String name = certs[ 0 ].getSubjectDN().getName();
for ( String s : name.split( "," ) )
{
String part[] = s.split( "=" );
if ( part[ 0 ].trim().equals( "CN" ) )
{
certFQDN = part[ 1 ].trim();
}
}
}
else
{
throw new IOException( "Could not find certificate chain." );
}
if ( !certFQDN.substring( certFQDN.length() - 11 ).equals( ".domain.com" ) )
{
throw new SSLException( "Not an internal host: " + certFQDN );
}
}
#Override public void verify( String arg0, SSLSocket arg1 ) throws IOException {
arg1.getSession();
javax.security.cert.X509Certificate[] certs = session.getPeerCertificateChain();
].getSubjectDN().getName();
) )
);
].trim().equals( "CN" ) )
].trim();
not find certificate chain." );
certFQDN.length() - 11 ).equals( ".domain.com" ) )
internal host: " + certFQDN );
}
I am currently using Jasypt for my web application. It works fine, but the encryption is different depending which server it is hosted on.
For this reason, I can't just get the data in the live DB and use it for debugging in my dev environment. It would be helpful, but I can live without it.
What worries me is that at the moment I am using a hosting provider. All has been ok so far, but I worry that if at some point they replace the server, or move my application on to another one, the encrypted data ( such as emails and passwords for the login, and more ) will not be encrypted in the same way and all the data will become unusable.
Does anybody know an alternative to Jasypt which is platform independent?
Or is there a way to make Jasypt itself platform independent?
Thanks, Dan
PS : I would need a method that has these basic functionalities: string encryption ( reversible ), password encryption ( not reversible but comparable ) and "SHA-1" encryption. Apologies if the terminology in the last paragraph is not the most correct, but I am not an encryption expert at all.
Thanks!
Edited to add code, results and exceptions:
public class Test
{
public static void main ( String [] args )
{
System.out.println ( "String encryption = " + new EncryptionUtil ( ).encryptString ( "test string" ) );
System.out.println ( "Password encryption = " + new EncryptionUtil ( ).encryptPassword ( "test password" ) );
}
}
produces this in the dev environment :
String encryption = ybXukKBN57QSY8ITPgu9RmJQrZP4Py6g
Password encryption = nNX82PuKx5TrqBFSCy6yzNpco7Asov2S
Everytime the output is different, but it is possible to decrypt the string, and compare the password by doing this:
public class Decryption
{
public static void main ( String [] args )
{
System.out.println ( new EncryptionUtil ( ).decryptString ( "ybXukKBN57QSY8ITPgu9RmJQrZP4Py6g" ) );
System.out.println ( new EncryptionUtil ( ).passwordsMatch ( "test password", "nNX82PuKx5TrqBFSCy6yzNpco7Asov2S" ) );
}
}
Which gives this output:
test string
true
This is the encryption util class that I created:
public class EncryptionUtil
{
private String password = "<<=Encryption-Password=>>";
// ============================================================ Encrypt password string
public String encryptPassword ( String pwd )
{
if ( null != pwd && ! "".equals ( pwd ) )
{
return new BasicPasswordEncryptor ().encryptPassword ( pwd );
}
else
{
return "";
}
}
// ====================================== Check if password entered matches that stored
public boolean passwordsMatch ( String enteredPassword, String storedPassword )
{
return new BasicPasswordEncryptor().checkPassword ( enteredPassword, storedPassword );
}
//===================================================================== Encrypt string
public String encryptString ( String text )
{
if ( null != password && ! "".equals ( password ) )
{
BasicTextEncryptor textEncryptor = new BasicTextEncryptor();
textEncryptor.setPassword ( password );
return textEncryptor.encrypt ( text );
}
else
{
return "";
}
}
// ===================================================================== Decrypt string
public String decryptString ( String text )
{
try
{
if ( null != text && ! "".equals ( text ) )
{
BasicTextEncryptor textEncryptor = new BasicTextEncryptor();
textEncryptor.setPassword ( password );
return textEncryptor.decrypt ( text );
}
else
{
return "";
}
}
catch ( Exception e )
{
return text;
}
}
// =============== Encrypt email. Used for login and registration only, not decryptable
public String encryptEmail ( String email )
{
if ( null != email && ! "".equals ( email ) )
{
return new String ( new Digester("SHA-1").digest ( email.getBytes () ) );
}
else
{
return "";
}
}
}
The same in the LIVE environment gives me this:
String encryption = L/UlkJjYhLnYiov7XeDjb9W7+k8Gduvz
Password encryption = P+LJM7VJHu/hudSQOrmvcvV/DrzCv+pj
When I try to decrypt the string and check the password, I get this:
public class Decryption
{
public static void main ( String [] args )
{
System.out.println ( new EncryptionUtil ( ).decryptString ( "L/UlkJjYhLnYiov7XeDjb9W7+k8Gduvz" ) );
System.out.println ( new EncryptionUtil ( ).passwordsMatch ( "test password", "P+LJM7VJHu/hudSQOrmvcvV/DrzCv+pj" ) );
}
}
The result with the strings obtained from LIVE ( those above ) gives this:
test string
false
This time the encryption of the string works ( I had it not working in the past, I am a bit surprised and confused to be honest ), but the password fails.
New edit - some strings, when encrypted, end with ==. The strings that do not have "==" at the end, are decryptable across systems. Those which do do not work. Maybe this can be a clue?
I am not sure what the problem was - anyway I managed to solve it with a random attempt which somehow worked...
If anybody finds him / her self in the same situation, an attempt ( lucky random one in my case ) is to change the password used for the text encryptor.
I cannot give any explanation on why it worked.
You should take a look at BouncyCastle
There´s an example here