diff --git a/etc/sample.csv b/etc/sample.csv new file mode 100644 index 0000000..7acf573 --- /dev/null +++ b/etc/sample.csv @@ -0,0 +1 @@ +"Max Muster","max.muster@example.com","Max","Muster" \ No newline at end of file diff --git a/pom.xml b/pom.xml index 78bf5ae..6cdc267 100644 --- a/pom.xml +++ b/pom.xml @@ -1,120 +1,126 @@ - - + 4.0.0 de.jalin.ldapadmin ldapadmin war 1.0.2 LDAP Admin Webapp - - - UTF-8 - UTF-8 - dev - - - - dev - - dev - - - - test - - test - - - - prod - - prod - - - - - - javax.servlet - javax.servlet-api - 4.0.1 - provided - - - javax.servlet.jsp - javax.servlet.jsp-api - 2.3.3 - provided - - - javax.servlet - jstl - 1.2 - - - commons-net - commons-net - 3.8.0 - + + UTF-8 + UTF-8 + dev + + + + + dev + + dev + + + + test + + test + + + + prod + + prod + + + + - org.webjars - bootstrap - 3.4.1 - - - org.apache.directory.server - apacheds-service - 2.0.0.AM26 - - - junit - junit - 4.13.2 - test - - + javax.servlet + javax.servlet-api + 4.0.1 + provided + + + javax.servlet.jsp + javax.servlet.jsp-api + 2.3.3 + provided + + + javax.servlet + jstl + 1.2 + + + commons-net + commons-net + 3.8.0 + + + org.apache.commons + commons-csv + 1.9.0 + + + org.webjars + bootstrap + 3.4.1 + + + org.apache.directory.server + apacheds-service + 2.0.0.AM26 + + + junit + junit + 4.13.2 + test + + - - - - src/main/resources - false - - - - - org.apache.maven.plugins - maven-war-plugin - 3.2.3 - - false - - - - org.eclipse.jetty - jetty-maven-plugin - 9.4.19.v20190610 - - etc/jetty.xml - 10 - - / - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.1 - - 1.8 - 1.8 - - - - - ldapadmin - + + + + src/main/resources + false + + + + + org.apache.maven.plugins + maven-war-plugin + 3.2.3 + + false + + + + org.eclipse.jetty + jetty-maven-plugin + 9.4.19.v20190610 + + etc/jetty.xml + 10 + + / + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + + + ldapadmin + diff --git a/src/main/java/de/jalin/ldapadmin/ldap/LDAPSession.java b/src/main/java/de/jalin/ldapadmin/ldap/LDAPSession.java index 1d89cfc..638ace0 100644 --- a/src/main/java/de/jalin/ldapadmin/ldap/LDAPSession.java +++ b/src/main/java/de/jalin/ldapadmin/ldap/LDAPSession.java @@ -21,6 +21,7 @@ import javax.naming.directory.SearchResult; public class LDAPSession { private InitialDirContext ctx; + private String connectString; public LDAPSession(final String providerURL, final String principal, final String password) throws LDAPSessionException { final Properties env = new Properties(); @@ -30,6 +31,7 @@ public class LDAPSession { env.put(Context.SECURITY_AUTHENTICATION, "simple"); env.put(Context.SECURITY_PRINCIPAL, principal); env.put(Context.SECURITY_CREDENTIALS, password); + connectString = providerURL; try { ctx = new InitialDirContext(env); } catch (NamingException e) { @@ -152,6 +154,10 @@ public class LDAPSession { ctx = null; } } + + public String getSessionInfo() { + return connectString; + } private void createOrgUnitNodesIfNotExist() throws LDAPSessionException { try { diff --git a/src/main/java/de/jalin/ldapadmin/ldap/RequiredAttributeException.java b/src/main/java/de/jalin/ldapadmin/ldap/RequiredAttributeException.java index 7c7e269..94cba36 100644 --- a/src/main/java/de/jalin/ldapadmin/ldap/RequiredAttributeException.java +++ b/src/main/java/de/jalin/ldapadmin/ldap/RequiredAttributeException.java @@ -7,10 +7,12 @@ public class RequiredAttributeException extends Exception { private final String fieldname; public RequiredAttributeException(final String fieldname) { + super("required attribute '" + fieldname + "' missing"); this.fieldname = fieldname; } public String getFieldname() { return fieldname; } + } diff --git a/src/main/java/de/jalin/ldapadmin/tools/ImportUsersFromCSV.java b/src/main/java/de/jalin/ldapadmin/tools/ImportUsersFromCSV.java new file mode 100644 index 0000000..5a53b20 --- /dev/null +++ b/src/main/java/de/jalin/ldapadmin/tools/ImportUsersFromCSV.java @@ -0,0 +1,76 @@ +package de.jalin.ldapadmin.tools; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.SortedMap; + +import javax.naming.NamingException; + +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; + +import de.jalin.ldapadmin.beans.User; +import de.jalin.ldapadmin.ldap.AlreadyBoundException; +import de.jalin.ldapadmin.ldap.LDAPConfig; +import de.jalin.ldapadmin.ldap.LDAPSession; +import de.jalin.ldapadmin.ldap.LDAPSessionException; +import de.jalin.ldapadmin.ldap.RequiredAttributeException; +import de.jalin.ldapadmin.ldap.UsersDAO; + +public class ImportUsersFromCSV { + + public static void main(String[] args) { + LDAPSession ldapSession = null; + if (args.length != 2) { + System.out.println("usage:"); + System.out.println("ImportUsersFromCSV command csvfile"); + System.out.println("valid commands: verfiy, import"); + return; + } + try { + ldapSession = connectLDAP(); + System.out.println("connected " + ldapSession.getSessionInfo()); + final UsersDAO usersDAO = new UsersDAO(ldapSession); + final SortedMap users = usersDAO.loadUsers(); + System.out.println("existing users: " + users.size()); + final File csvFile = new File(args[1]); + final InputStreamReader csvStreamReader = new InputStreamReader(new FileInputStream(csvFile)); + final CSVParser csvParser = CSVFormat.DEFAULT.parse(csvStreamReader); + for (CSVRecord record : csvParser) { + final String userid = record.get(0); + final String email = record.get(1); + final String firstname = record.get(2); + final String lastname = record.get(3); + System.out.println(userid + "/" + email); + final User user = new User(userid); + user.setEmail(email); + user.setFirstname(firstname); + user.setLastname(lastname); + user.setPassword(PasswordTool.generatePassword()); + try { + usersDAO.create(user); + } catch (RequiredAttributeException | AlreadyBoundException e) { + System.err.println("user failed: " + userid + " => " + e.getLocalizedMessage()); + } + } + } catch (LDAPSessionException | IOException e) { + System.err.println(e.getLocalizedMessage()); + } finally { + if (ldapSession != null) { + try { ldapSession.close(); } catch (NamingException e) { } + } + } + } + + private static LDAPSession connectLDAP() throws LDAPSessionException { + final LDAPConfig config = LDAPConfig.getConfig(); + final String providerUrl = config.getLdapProviderUrl(); + final String securityPrincipal = config.getLdapSecurityPrincipal(); + final String securityPassword = config.getLdapSecurityPassword(); + return new LDAPSession(providerUrl, securityPrincipal, securityPassword); + } + +} diff --git a/src/main/java/de/jalin/ldapadmin/tools/PasswordGenerator.java b/src/main/java/de/jalin/ldapadmin/tools/PasswordGenerator.java new file mode 100644 index 0000000..6b58c6e --- /dev/null +++ b/src/main/java/de/jalin/ldapadmin/tools/PasswordGenerator.java @@ -0,0 +1,147 @@ +package de.jalin.ldapadmin.tools; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public final class PasswordGenerator { + + private static final String LOWER = "abcdefghijklmnopqrstuvwxyz"; + private static final String UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + private static final String DIGITS = "0123456789"; + private static final String PUNCTUATION = "!@#$%&*()_+-=[]|,./?><"; + private boolean useLower; + private boolean useUpper; + private boolean useDigits; + private boolean usePunctuation; + + private PasswordGenerator() { + throw new UnsupportedOperationException("Empty constructor is not supported."); + } + + private PasswordGenerator(PasswordGeneratorBuilder builder) { + this.useLower = builder.useLower; + this.useUpper = builder.useUpper; + this.useDigits = builder.useDigits; + this.usePunctuation = builder.usePunctuation; + } + + public static class PasswordGeneratorBuilder { + + private boolean useLower; + private boolean useUpper; + private boolean useDigits; + private boolean usePunctuation; + + public PasswordGeneratorBuilder() { + this.useLower = false; + this.useUpper = false; + this.useDigits = false; + this.usePunctuation = false; + } + + /** + * Set true in case you would like to include lower characters (abc...xyz). + * Default false. + * + * @param useLower true in case you would like to include lower characters + * (abc...xyz). Default false. + * @return the builder for chaining. + */ + public PasswordGeneratorBuilder useLower(boolean useLower) { + this.useLower = useLower; + return this; + } + + /** + * Set true in case you would like to include upper characters (ABC...XYZ). + * Default false. + * + * @param useUpper true in case you would like to include upper characters + * (ABC...XYZ). Default false. + * @return the builder for chaining. + */ + public PasswordGeneratorBuilder useUpper(boolean useUpper) { + this.useUpper = useUpper; + return this; + } + + /** + * Set true in case you would like to include digit characters (123..). Default + * false. + * + * @param useDigits true in case you would like to include digit characters + * (123..). Default false. + * @return the builder for chaining. + */ + public PasswordGeneratorBuilder useDigits(boolean useDigits) { + this.useDigits = useDigits; + return this; + } + + /** + * Set true in case you would like to include punctuation characters (!@#..). + * Default false. + * + * @param usePunctuation true in case you would like to include punctuation + * characters (!@#..). Default false. + * @return the builder for chaining. + */ + public PasswordGeneratorBuilder usePunctuation(boolean usePunctuation) { + this.usePunctuation = usePunctuation; + return this; + } + + /** + * Get an object to use. + * + * @return the {@link gr.idrymavmela.business.lib.PasswordGenerator} object. + */ + public PasswordGenerator build() { + return new PasswordGenerator(this); + } + } + + /** + * This method will generate a password depending the use* properties you + * define. It will use the categories with a probability. It is not sure that + * all of the defined categories will be used. + * + * @param length the length of the password you would like to generate. + * @return a password that uses the categories you define when constructing the + * object with a probability. + */ + public String generate(int length) { + // Argument Validation. + if (length <= 0) { + return ""; + } + + // Variables. + StringBuilder password = new StringBuilder(length); + Random random = new Random(System.nanoTime()); + + // Collect the categories to use. + List charCategories = new ArrayList<>(4); + if (useLower) { + charCategories.add(LOWER); + } + if (useUpper) { + charCategories.add(UPPER); + } + if (useDigits) { + charCategories.add(DIGITS); + } + if (usePunctuation) { + charCategories.add(PUNCTUATION); + } + + // Build the password. + for (int i = 0; i < length; i++) { + String charCategory = charCategories.get(random.nextInt(charCategories.size())); + int position = random.nextInt(charCategory.length()); + password.append(charCategory.charAt(position)); + } + return new String(password); + } +} \ No newline at end of file diff --git a/src/main/java/de/jalin/ldapadmin/tools/PasswordTool.java b/src/main/java/de/jalin/ldapadmin/tools/PasswordTool.java new file mode 100644 index 0000000..35ea909 --- /dev/null +++ b/src/main/java/de/jalin/ldapadmin/tools/PasswordTool.java @@ -0,0 +1,46 @@ +package de.jalin.ldapadmin.tools; + +import de.jalin.ldapadmin.ldap.SimplePasswordException; + +public final class PasswordTool { + + public static final int MIN_COMPLEXITY = 3; + public static final int MIN_PASSWORD_LENGTH = 6; + + private static final PasswordGenerator passwordGenerator = new PasswordGenerator.PasswordGeneratorBuilder() + .useDigits(true) + .useLower(true) + .useUpper(true) + .build(); + + public static String generatePassword() { + return passwordGenerator.generate(14); + } + + public static void checkPasswordComplexity(final String password) throws SimplePasswordException { + int containsLowercaseCharacter = 0; + int containsUppercaseCharacter = 0; + int containsSpecialCharacter = 0; + int containsDigit = 0; + for (int idx = 0; idx < password.length(); idx++) { + char chr = password.charAt(idx); + if (chr >= 'a' && chr <= 'z') { + containsLowercaseCharacter = 1; + } else + if (chr >= 'A' && chr <= 'Z') { + containsUppercaseCharacter = 1; + } else + if (chr >= '0' && chr <= '1') { + containsDigit = 1; + } else { + containsSpecialCharacter = 1; + } + } + int complexity = containsLowercaseCharacter + containsUppercaseCharacter + + containsSpecialCharacter + containsDigit; + if (complexity < MIN_COMPLEXITY || password.length() < MIN_PASSWORD_LENGTH) { + throw new SimplePasswordException("simple password"); + } + } + +}