Browse Source

Working version

main
Christian Pape 3 years ago
committed by Dustin Frisch
commit
8c4852b13b
No known key found for this signature in database GPG Key ID: B4C3BF012D9B26BE
  1. 6
      .gitignore
  2. 2
      ldap-sync.sh
  3. 119
      pom.xml
  4. 101
      src/main/java/de/hsfulda/informatik/AccountSource.java
  5. 135
      src/main/java/de/hsfulda/informatik/LdapSync.java
  6. 111
      src/test/java/de/hsfulda/informatik/LdapSyncTest.java

6
.gitignore

@ -0,0 +1,6 @@
*.iws
*.iml
*.ipr
target/
.DS_Store
.idea/

2
ldap-sync.sh

@ -0,0 +1,2 @@
#!/bin/bash
java -jar target/ldap-sync-1.0-SNAPSHOT-jar-with-dependencies.jar

119
pom.xml

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.hsfulda.informatik</groupId>
<artifactId>ldap-sync</artifactId>
<version>1.0-SNAPSHOT</version>
<name>ldap-sync</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.unboundid</groupId>
<artifactId>unboundid-ldapsdk</artifactId>
<version>6.0.1</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1.1-jre</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>foobar</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>de.hsfulda.informatik.LdapSync</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

101
src/main/java/de/hsfulda/informatik/AccountSource.java

@ -0,0 +1,101 @@
package de.hsfulda.informatik;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.ExtendedResult;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.ldap.sdk.SearchResult;
import com.unboundid.ldap.sdk.SearchResultEntry;
import com.unboundid.ldap.sdk.SearchScope;
import com.unboundid.ldap.sdk.extensions.StartTLSExtendedRequest;
import com.unboundid.util.ssl.SSLUtil;
import com.unboundid.util.ssl.TrustAllTrustManager;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class AccountSource {
final LDAPConnection connection;
final List<SearchResultEntry> searchResultEntryList;
final Set<String> users;
final String baseDN;
AccountSource(final String baseDN, final List<SearchResultEntry> searchResultEntryList) {
this.baseDN = baseDN;
this.searchResultEntryList = searchResultEntryList;
this.users = getEntries().stream()
.map(e -> e.getAttribute("cn").getValue().toLowerCase())
.filter(e -> e.startsWith("fd"))
.collect(Collectors.toSet());
connection = null;
}
public AccountSource(final String host, final int port, final String bindDN, final String password, final String baseDN, final String filter, final String... attributes) throws LDAPException, GeneralSecurityException {
this.baseDN = baseDN;
final SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());
this.connection = new LDAPConnection(host, port);
final ExtendedResult extendedResult = connection.processExtendedOperation(new StartTLSExtendedRequest(sslUtil.createSSLContext()));
this.connection.bind(bindDN, password);
if (extendedResult.getResultCode() != ResultCode.SUCCESS) {
throw new LDAPException(extendedResult.getResultCode());
}
final SearchResult searchResult = connection.search(baseDN, SearchScope.SUB, filter, attributes);
this.searchResultEntryList = searchResult.getSearchEntries();
this.users = getEntries().stream()
.map(e -> e.getAttribute("cn").getValue().toLowerCase())
.filter(e -> e.startsWith("fd"))
.collect(Collectors.toSet());
}
public List<SearchResultEntry> getEntries() {
return Collections.unmodifiableList(this.searchResultEntryList);
}
public Set<String> getUsers() {
return Collections.unmodifiableSet(this.users);
}
public void add(final List<Entry> usersToBeAdded) {
for (final Entry entry : usersToBeAdded) {
System.out.print("-> Füge Benutzer " + entry.getDN() + "...");
final LDAPResult ldapResult;
try {
ldapResult = connection.add(entry);
if (ldapResult.getResultCode().intValue() == 0) {
System.out.print("Ok\n");
} else {
System.out.print("Failed (" + ldapResult.getResultCode() + ")\n");
}
} catch (LDAPException e) {
System.out.print("Failed (" + e.getMessage() + ")\n");
}
}
}
public void del(final Set<String> usersToBeDeleted) {
for (final String user : usersToBeDeleted) {
System.out.print("-> Lösche Benutzer cn=" + user + "," + this.baseDN + "...");
final LDAPResult ldapResult;
try {
ldapResult = connection.delete("cn=" + user + "," + this.baseDN);
if (ldapResult.getResultCode().intValue() == 0) {
System.out.print("Ok\n");
} else {
System.out.print("Failed (" + ldapResult.getResultCode() + ")\n");
}
} catch (LDAPException e) {
System.out.print("Failed (" + e.getMessage() + ")\n");
}
}
}
}

135
src/main/java/de/hsfulda/informatik/LdapSync.java

@ -0,0 +1,135 @@
package de.hsfulda.informatik;
import com.unboundid.ldap.sdk.Attribute;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Ldap sync utility
*/
public class LdapSync {
final Properties properties = new Properties();
final static Pattern pattern = Pattern.compile("^fd([a-z][a-z])?([0-9]*?)$");
public LdapSync() throws IOException, LDAPException, GeneralSecurityException {
// lade Konfiguration
properties.load(new FileReader("ldap-sync.properties"));
System.out.print("Abfrage der Benutzer im eDirectory...");
// lade Daten des Remote-Systems
final AccountSource remote = new AccountSource(
properties.getProperty("sync.src.host"),
Integer.parseInt(properties.getProperty("sync.src.port")),
properties.getProperty("sync.src.binddn"),
properties.getProperty("sync.src.bindpw"),
properties.getProperty("sync.src.basedn"),
properties.getProperty("sync.src.filter"),
new String[]{"cn", "givenname", "sn", "uid"}
);
System.out.print("Ok\nAbfrage der Benutzer im OpenLDAP...");
// lade Daten des lokalen Systems
final AccountSource local = new AccountSource(
properties.getProperty("sync.dst.host"),
Integer.parseInt(properties.getProperty("sync.dst.port")),
properties.getProperty("sync.dst.binddn"),
properties.getProperty("sync.dst.bindpw"),
properties.getProperty("sync.dst.basedn"),
properties.getProperty("sync.dst.filter"),
new String[]{}
);
System.out.print("Ok\n");
sync(remote, local);
}
LdapSync(final AccountSource remote, final AccountSource local) throws IOException, LDAPException, GeneralSecurityException {
sync(remote, local);
}
private void sync(final AccountSource remote, final AccountSource local) throws LDAPException {
Set<String> usersToBeAdded = new TreeSet<>(remote.getUsers());
usersToBeAdded.removeAll(local.getUsers());
Set<String> usersToBeDeleted = new TreeSet<>(local.getUsers());
usersToBeDeleted.removeAll(remote.getUsers());
final List<Entry> entriesToBeAdded = remote.getEntries().stream()
.filter(s -> usersToBeAdded.contains(s.getAttributeValue("cn").toLowerCase()))
.map(s -> {
final String cn = s.getAttributeValue("cn").toLowerCase();
final String dn = "cn=" + cn + "," + local.baseDN;
final Entry e = new Entry(dn);
e.addAttribute(new Attribute("objectClass", "inetOrgPerson"));
e.addAttribute(new Attribute("objectClass", "shadowAccount"));
e.addAttribute(new Attribute("objectClass", "top"));
e.addAttribute(new Attribute("objectClass", "posixAccount"));
e.addAttribute(new Attribute("cn", cn));
e.addAttribute(new Attribute("sn", s.getAttributeValue("sn")));
e.addAttribute(new Attribute("givenname", s.getAttributeValue("givenName")));
e.addAttribute(new Attribute("uid", cn));
e.addAttribute(new Attribute("uidNumber", String.valueOf(computeUid(cn))));
e.addAttribute(new Attribute("gidNumber", "20"));
e.addAttribute(new Attribute("loginShell", "/bin/zsh"));
e.addAttribute(new Attribute("homeDirectory", "/Users/" + cn));
e.addAttribute(new Attribute("userPassword", "{SASL}" + cn));
return e;
}).collect(Collectors.toList());
System.out.print("Zu erzeugende Benutzer: " + entriesToBeAdded.size() + ", Zu löschende Benutzer: " + usersToBeDeleted.size() + "\n");
local.del(usersToBeDeleted);
local.add(entriesToBeAdded);
}
static Integer computeUid(final String cn) {
final Matcher m = pattern.matcher(cn);
if (m.find()) {
int a = 0;
int b = 0;
if (m.group(1) != null) {
a = m.group(1).charAt(0) - 96;
b = m.group(1).charAt(1) - 96;
}
final int s = m.group(2).length();
final int z = Integer.parseInt(m.group(2));
final int uid = (a * 1000 + b * 10 + s) * 10000 + z;
return uid;
}
return null;
}
public static void main(String[] args) {
try {
final LdapSync ldapSync = new LdapSync();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (LDAPException e) {
e.printStackTrace();
} catch (GeneralSecurityException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

111
src/test/java/de/hsfulda/informatik/LdapSyncTest.java

@ -0,0 +1,111 @@
package de.hsfulda.informatik;
import com.unboundid.ldap.sdk.Attribute;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.SearchResultEntry;
import org.junit.Test;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class LdapSyncTest {
private List<Entry> addList;
private Set<String> delSet;
@Test
public void testSync() throws LDAPException, GeneralSecurityException, IOException {
final List<SearchResultEntry> remoteEntries = new ArrayList<>();
remoteEntries.add(searchResultEntry("dn=Fd1234,dc=remote,dc=de", "fD1234", "Mustermann", "Mustermann"));
remoteEntries.add(searchResultEntry("dn=fDai1235,dc=remote,dc=de", "FDai1235", "Schuster", "Schuster"));
remoteEntries.add(searchResultEntry("dn=fdaI1236,dc=remote,dc=de", "fdAI1236", "Bunsen", "Bunsen"));
remoteEntries.add(searchResultEntry("dn=fDeT123,dc=remote,dc=de", "FDET123", "Bauer", "Bauer"));
remoteEntries.add(searchResultEntry("dn=fdXx9999,dc=remote,dc=de", "fdxX9999", "Schmidt", "Schmidt"));
final AccountSource remote = new AccountSource("dc=remote,dc=de", remoteEntries);
final List<SearchResultEntry> localEntries = new ArrayList<>();
localEntries.add(searchResultEntry("dn=Fdai1236,dc=local,dc=de", "fDai1236", "Bunsen", "Bunsen"));
localEntries.add(searchResultEntry("dn=fDai1237,dc=local,dc=de", "fdAi1237", "Beaker", "Beaker"));
localEntries.add(searchResultEntry("dn=fdAi1238,dc=local,dc=de", "fdaI1238", "Hopper", "Hopper"));
final AccountSource local = new AccountSource("dc=local,dc=de", localEntries) {
@Override
public void add(List<Entry> usersToBeAdded) {
addList = usersToBeAdded;
}
@Override
public void del(Set<String> usersToBeDeleted) {
delSet = usersToBeDeleted;
}
};
final LdapSync ldapSync = new LdapSync(remote, local);
assertEquals(delSet.size(), 2);
assertTrue(delSet.contains("fdai1237"));
assertTrue(delSet.contains("fdai1238"));
assertEquals(addList.size(), 4);
for (final Entry entry : addList) {
assertTrue(entry.getDN().endsWith(",dc=local,dc=de"));
final List<String> objectClasses = Arrays.asList(entry.getObjectClassValues());
assertTrue(objectClasses.contains("shadowAccount"));
assertTrue(objectClasses.contains("posixAccount"));
assertTrue(objectClasses.contains("top"));
assertTrue(objectClasses.contains("inetOrgPerson"));
final List<String> attributes = entry.getAttributes().stream().map(a -> a.getName()).collect(Collectors.toList());
assertTrue(attributes.contains("uid"));
assertTrue(attributes.contains("cn"));
assertTrue(attributes.contains("givenname"));
assertTrue(attributes.contains("sn"));
assertTrue(attributes.contains("uidNumber"));
assertTrue(attributes.contains("gidNumber"));
assertTrue(attributes.contains("homeDirectory"));
assertTrue(attributes.contains("loginShell"));
assertEquals(entry.getAttributeValue("cn").toLowerCase(), entry.getAttributeValue("cn"));
assertEquals(entry.getAttributeValue("uid").toLowerCase(), entry.getAttributeValue("uid"));
assertEquals(entry.getDN().toLowerCase(), entry.getDN());
System.out.println();
for (final String string : entry.toLDIF()) {
System.out.println(string);
}
}
}
@Test
public void testUidNumber() {
assertEquals(41000, (int) LdapSync.computeUid("fd1000"));
assertEquals(49999, (int) LdapSync.computeUid("fd9999"));
assertEquals(10141000, (int) LdapSync.computeUid("fdaa1000"));
assertEquals(10149999, (int) LdapSync.computeUid("fdaa9999"));
assertEquals(262641000, (int) LdapSync.computeUid("fdzz1000"));
assertEquals(262649999, (int) LdapSync.computeUid("fdzz9999"));
}
private SearchResultEntry searchResultEntry(final String dn, final String cn, final String sn, final String givenname) {
final Attribute[] attributes = {
new Attribute("cn", cn),
new Attribute("uid", cn),
new Attribute("sn", sn),
new Attribute("givenname", givenname),
new Attribute("objectClass", "inetOrgPerson"),
new Attribute("objectClass", "top"),
};
return new SearchResultEntry(dn, attributes);
}
}
Loading…
Cancel
Save