You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

188 lines
7.4 KiB

package de.hsfulda.informatik;
import com.google.common.base.Strings;
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.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;
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 {
private final Properties properties = new Properties();
private final static Pattern pattern = Pattern.compile("^fd([a-z][a-z])?([0-9]*?)$");
private Map<String, Integer> userDefinedMappings = Collections.emptyMap();
public LdapSync() throws IOException, LDAPException, GeneralSecurityException {
// lade Konfiguration
properties.load(new FileReader("ldap-sync.properties"));
this.userDefinedMappings = parseUserDefinedMappings(properties.getProperty("sync.src.map", ""));
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);
}
static Map<String, Integer> parseUserDefinedMappings(final String property) {
if (Strings.isNullOrEmpty(property)) {
return Collections.emptyMap();
}
final Map<String, Integer> mappingsMap = new TreeMap<>();
final String mappingArr[] = property.split("\\s*,\\s*");
for (final String mapping : mappingArr) {
final String accountUidArr[] = mapping.split(":");
if (accountUidArr.length == 2) {
final String uid = accountUidArr[0].toLowerCase();
final int uidNumber;
try {
uidNumber = Integer.valueOf(accountUidArr[1]);
} catch (NumberFormatException e) {
System.out.println("Fehler beim Parsen der uidNumber für uid '" + uid + "'");
continue;
}
System.out.println("-> Benutzerdefinierte Zuordnung '" + uid + "' -> " + uidNumber);
mappingsMap.put(uid, uidNumber);
} else {
System.out.println("Fehler beim Parsen der Zuordnung '" + mapping + "'");
}
}
return mappingsMap;
}
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", "person"));
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, this.userDefinedMappings))));
e.addAttribute(new Attribute("gidNumber", properties.getProperty("sync.dst.gid", "1000")));
if (properties.getProperty("sync.dst.shell") != null) {
e.addAttribute(new Attribute("loginShell", properties.getProperty("sync.dst.shell")));
}
e.addAttribute(new Attribute("homeDirectory", String.format(properties.getProperty("sync.dst.home", "/home/%s"), cn)));
if (properties.getProperty("sync.dst.sasl-password", "false").equals("true")) {
e.addAttribute(new Attribute("userPassword", "{SASL}" + cn));
}
e.addAttribute(new Attribute("seeAlso", s.getDN()));
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) {
return computeUid(cn, Collections.emptyMap());
}
static Integer computeUid(final String cn, final Map<String, Integer> mappings) {
if (mappings.containsKey(cn)) {
return mappings.get(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();
}
}
}