#1 2020-11-30 15:05:38

Stéphane
Member
Registered: 2020-09-30

ldapimport.groovy

Hello,

We try ldapimport.groovy to populate our users:
for our test, we use just one login (example=user41)

we obtain this message:
error message: javax.script.ScriptException: java.lang.RuntimeException: Error processing search result: uid=user41,ou=people,dc=exemple,dc=fr (java.lang.RuntimeException)

where is our problem ?

here is the groovy file

package ldap

import java.util.Map.Entry
import java.util.logging.Level
import java.util.logging.Logger

import javax.naming.Context
import javax.naming.NamingEnumeration
import javax.naming.NamingException
import javax.naming.directory.Attribute
import javax.naming.directory.DirContext
import javax.naming.directory.InitialDirContext
import javax.naming.directory.SearchControls
import javax.naming.directory.SearchResult
import javax.naming.ldap.LdapName

import net.datenwerke.security.service.usermanager.UserManagerService
import net.datenwerke.security.service.usermanager.entities.AbstractUserManagerNode
import net.datenwerke.security.service.usermanager.entities.Group
import net.datenwerke.security.service.usermanager.entities.OrganisationalUnit
import net.datenwerke.security.service.usermanager.entities.User




UserManagerService userManagerService = GLOBALS.getRsService(UserManagerService.class);

LdapUserLoader lul = new LdapUserLoader();

lul.setProviderUrl("ldap://adresse_IP:389");
lul.setSecurityPrincipal("cn=compte,ou=system,dc=exemple,dc=fr");
lul.setSecurityCredentials("mdp");

lul.setLdapBase("ou=people,dc=exemple,dc=fr");
OrganisationalUnit targetNode = (GLOBALS.findObject("/usermanager/external"));

if(null == targetNode){
    AbstractUserManagerNode umRoot = userManagerService.getRoots().get(0);
    targetNode = new OrganisationalUnit("external");
    umRoot.addChild(targetNode);
    userManagerService.persist(targetNode);
}

lul.setTargetNode(targetNode);
lul.run();


public class LdapUserLoader {

    private final Logger logger = Logger.getLogger(getClass().getName());


    private String ldapBase;
    //private String ldapFilter = "(|(objectClass=organizationalUnit)(objectClass=user)(objectClass=group))";
    private String ldapFilter = "(|(uid=user41))";

    private String providerUrl;
    private String securityCredentials;

    private String securityPrincipal;
    private OrganisationalUnit targetNode;

    private boolean includeNamespace = false;

    private Map<String, AbstractUserManagerNode> guidMap;
    private Map<LdapName, AbstractUserManagerNode> nodesInDirectoryByName;
    private Map<String, AbstractUserManagerNode> nodesInDirectoryByGuid;
    private TreeMap<LdapName, SearchResult> searchResults;

    private List<AbstractUserManagerNode> removedNodes;
    private List<AbstractUserManagerNode> addedNodes;



    private class AdGUID {
        byte[] bytes;

        public AdGUID(byte[] bytes) {
            this.bytes = bytes;
        }

        private void addByte(StringBuffer sb, int k) {
            if(k<=0xF)
                sb.append("0");
            sb.append(Integer.toHexString(k));
        }

        @Override
        public String toString() {
            StringBuffer sb = new StringBuffer();
            addByte(sb, (int)bytes[3] & 0xFF);
            addByte(sb, (int)bytes[2] & 0xFF);
            addByte(sb, (int)bytes[1] & 0xFF);
            addByte(sb, (int)bytes[0] & 0xFF);
            sb.append("-");
            addByte(sb, (int)bytes[5] & 0xFF);
            addByte(sb, (int)bytes[4] & 0xFF);
            sb.append("-");
            addByte(sb, (int)bytes[7] & 0xFF);
            addByte(sb, (int)bytes[6] & 0xFF);
            sb.append("-");
            addByte(sb, (int)bytes[8] & 0xFF);
            addByte(sb, (int)bytes[9] & 0xFF);
            sb.append("-");
            addByte(sb, (int)bytes[10] & 0xFF);
            addByte(sb, (int)bytes[11] & 0xFF);
            addByte(sb, (int)bytes[12] & 0xFF);
            addByte(sb, (int)bytes[13] & 0xFF);
            addByte(sb, (int)bytes[14] & 0xFF);
            addByte(sb, (int)bytes[15] & 0xFF);

            return sb.toString();
        }

    }

    public LdapUserLoader() {

    }


    public String getLdapBase() {
        return ldapBase;
    }

    public String getProviderUrl() {
        return providerUrl;
    }

    public String getSecurityCredentials() {
        return securityCredentials;
    }

    public String getSecurityPrincipal() {
        return securityPrincipal;
    }

    public void setLdapBase(String ldapBase) {
        this.ldapBase = ldapBase;
    }

    public void setProviderUrl(String providerUrl) {
        this.providerUrl = providerUrl;
    }

    public void setSecurityCredentials(String securityCredentials) {
        this.securityCredentials = securityCredentials;
    }

    public void setSecurityPrincipal(String securityPrincipal) {
        this.securityPrincipal = securityPrincipal;
    }

    public void setTargetNode(OrganisationalUnit targetNode) {
        this.targetNode = targetNode;
    }

    public boolean isIncludeNamespace() {
        return includeNamespace;
    }

    public void setIncludeNamespace(boolean includeNamespace) {
        this.includeNamespace = includeNamespace;
    }

    public String getLdapFilter() {
        return ldapFilter;
    }

    public void setLdapFilter(String ldapFilter) {
        this.ldapFilter = ldapFilter;
    }



    private Properties compileProperties(){
        Properties props = new Properties();

        props.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        props.setProperty(Context.PROVIDER_URL, providerUrl);
        props.setProperty(Context.URL_PKG_PREFIXES, "com.sun.jndi.url");
        props.setProperty(Context.REFERRAL, "throw");
        props.setProperty(Context.SECURITY_AUTHENTICATION, "simple");

        props.setProperty(Context.SECURITY_PRINCIPAL, securityPrincipal);
        props.setProperty(Context.SECURITY_CREDENTIALS, securityCredentials);

        /* return these as binary */
        props.put("java.naming.ldap.attributes.binary","objectGUID");
        return props;
    }

    private void createGuidMap(AbstractUserManagerNode current) {
        Map<String, AbstractUserManagerNode> map = new HashMap<>();
        createGuidMap(current, map );

        guidMap = map;
    }

    private void createGuidMap(AbstractUserManagerNode current, Map<String, AbstractUserManagerNode> map){
        map.put(current.getGuid(), current);

        for(AbstractUserManagerNode cn : current.getChildren()){
            createGuidMap(cn, map);
        }
    }

    private String getStringAttribute(SearchResult sr, String attributeName) throws NamingException{
        try{
            return sr.getAttributes().get(attributeName).get().toString();
        }catch(Exception e){
            logger.log(Level.WARNING,"failed to retrieve attribute '" + attributeName + "' from " + sr.getNameInNamespace(), e);
            return null;
        }
    }

    private String getGuid(SearchResult sr) throws NamingException{
        try{
            AdGUID guid = new AdGUID((byte[]) sr.getAttributes().get("objectGUID").get());
            return guid.toString();
        }catch(Exception e){
            throw new RuntimeException("failed to retrieve objectGUID from " + sr.getNameInNamespace(), e);
        }
    }

    private void loadFromDirectory() throws NamingException {
        Properties props = compileProperties();
        String originBase = this.providerUrl.endsWith("/")?providerUrl:providerUrl + "/";

        this.nodesInDirectoryByName = new HashMap<>();
        this.nodesInDirectoryByGuid = new HashMap<>();
        this.addedNodes = new ArrayList<>();
        this.removedNodes = new ArrayList<>();

        DirContext ctx = null ;

        try {
            ctx = new InitialDirContext(props);

            SearchControls searchControls = new SearchControls();
            searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);

            LdapName ldapBaseName = new LdapName(getLdapBase());
            NamingEnumeration<SearchResult> results = ctx.search(ldapBaseName, this.ldapFilter, searchControls);

            /* order search results by name to make sure children never get processed before their parent */
            searchResults = new TreeMap<>();
            while (results.hasMoreElements()) {
                SearchResult sr = (SearchResult) results.next();
                searchResults.put(new LdapName(sr.getNameInNamespace()), sr);
            }

            for(SearchResult sr : searchResults.values()){
                try {
                    LdapName nodeName = new LdapName(isIncludeNamespace() ? sr.getNameInNamespace() : sr.getName());
                    LdapName nodeNameInNamespace = new LdapName(sr.getNameInNamespace());

                    /* skip empty nodes */
                    if(nodeName.size() == 0)
                        continue;


                    /* get parent node */
                    LdapName parentName = (LdapName) nodeNameInNamespace.getPrefix(Math.max(0, nodeNameInNamespace.size() - 1));
                    AbstractUserManagerNode parent = this.nodesInDirectoryByName.get(parentName);
                    if(null == parent){
                        if(parentName.equals(new LdapName(ldapBase))){
                            /* root node */
                            parent = targetNode;
                        }else{
                            throw new IllegalStateException("Missing parent for " + sr.getNameInNamespace());
                        }
                    }

                    /* create node */
                    Attribute objectClass = sr.getAttributes().get("objectClass");
                    AbstractUserManagerNode umNode = null;
                    if(objectClass.contains("organizationalUnit")) {
                        umNode = createOUNode(sr, parent);

                    } else if(objectClass.contains("user")) {
                        umNode = createUserNode(sr, parent);

                    } else if(objectClass.contains("group")){
                        umNode = createGroupNode(sr, parent);
                    }

                    /* set common attributes */
                    umNode.setWriteProtection(true);
                    umNode.setGuid(getGuid(sr));
                    umNode.setOrigin(originBase + sr.getNameInNamespace());

                    nodesInDirectoryByName.put(new LdapName(sr.getNameInNamespace()), umNode);
                    nodesInDirectoryByGuid.put(getGuid(sr), umNode);
                }catch(Exception e){
                    throw new RuntimeException("Error processing search result: " + sr.getNameInNamespace() , e);
                }
            }


        }finally{
            try {
                if(null != ctx)
                    ctx.close();
            } catch (NamingException e) {
                logger.log(Level.WARNING, e.getMessage(), e);
            }
        }
    }


    private AbstractUserManagerNode createGroupNode(SearchResult sr, AbstractUserManagerNode parent) throws NamingException {
        Group node = (Group) guidMap.get(getGuid(sr));
        if(null == node){
            node = new Group();
            addedNodes.add(node);
        }
        parent.addChild(node);

        /* copy Group attributes */
        node.setName(getStringAttribute(sr, "name"));

        return node;
    }


    private AbstractUserManagerNode createUserNode(SearchResult sr, AbstractUserManagerNode parent) throws NamingException {
        User node = (User) guidMap.get(getGuid(sr));
        if(null == node){
            node = new User();
            addedNodes.add(node);
        }
        parent.addChild(node);

        /* copy User attributes */
        node.setFirstname(getStringAttribute(sr, "givenName"));
        node.setLastname(getStringAttribute(sr, "sn"));
        node.setUsername(getStringAttribute(sr, "sAMAccountName"));

        return node;
    }

    private AbstractUserManagerNode createOUNode(SearchResult sr, AbstractUserManagerNode parent) throws NamingException {
        OrganisationalUnit node = (OrganisationalUnit) guidMap.get(getGuid(sr));
        if(null == node){
            node = new OrganisationalUnit();
            addedNodes.add(node);
        }
        parent.addChild(node);

        /* copy OU attributes */
        node.setName(getStringAttribute(sr, "name"));

        return node;
    }


    private void postprocessGroups() throws NamingException {

        /* clear */
        for(Entry<LdapName, AbstractUserManagerNode> entry : nodesInDirectoryByName.entrySet()){
            if(entry.getValue() instanceof Group){
                Group group = (Group) entry.getValue();
                group.getUsers().clear();
                group.getOus().clear();
                group.getReferencedGroups().clear();
            }
        }

        /* add appropriate users */
        for(Entry<LdapName, AbstractUserManagerNode> entry : nodesInDirectoryByName.entrySet()){
            if(entry.getValue() instanceof Group){
                Group group = (Group) entry.getValue();
                SearchResult sr = searchResults.get(entry.getKey());
                if(null != sr.getAttributes().get("member")){
                    NamingEnumeration<?> members = sr.getAttributes().get("member").getAll();
                    while(members.hasMore()){
                        LdapName memberName = new LdapName(members.next().toString());
                        AbstractUserManagerNode member = nodesInDirectoryByName.get(memberName);
                        if(null != member){
                            if(member instanceof User)
                                group.addUser((User) member);
                            if(member instanceof OrganisationalUnit)
                                group.addOu((OrganisationalUnit) member);
                            if(member instanceof Group)
                                group.addReferencedGroup((Group) member);
                        }
                    }
                }
            }
        }
    }

    private void printTree(AbstractUserManagerNode current){
        StringBuilder sb = new StringBuilder();
        List<AbstractUserManagerNode> rl = current.getRootLine();
        Collections.reverse(rl);
        for(AbstractUserManagerNode node : rl){
            sb.append(node.getName()).append(".");
        }
        sb.append(current.getName() + " [" + current.getClass().getSimpleName() + "]" );

        if(current instanceof Group){
            Group group = (Group) current;
            sb.append(" (").append(group.getUsers().size() + group.getOus().size() + group.getReferencedGroups().size()).append(" members)");
        }

        System.out.println(sb.toString());

        for(AbstractUserManagerNode cn : current.getChildren()){
            printTree(cn);
        }
    }


    private void deleteRemovedUsers(AbstractUserManagerNode current) {
        for(AbstractUserManagerNode c : current.getChildren()){
            deleteRemovedUsers(c);
        }

        if(null != current.getOrigin() && current.getOrigin().startsWith(providerUrl) && !nodesInDirectoryByGuid.containsKey(current.getGuid())){
            current.getParent().removeChild(current);
            removedNodes.add(current);
        }
    }

    public void run() throws NamingException{
        createGuidMap(targetNode);

        loadFromDirectory();
        postprocessGroups();

        deleteRemovedUsers(targetNode);

        System.out.println("Retrieved nodes from directory: " + nodesInDirectoryByGuid.size() );
        System.out.println("Nodes added: " + addedNodes.size() );
        System.out.println("Nodes removed: " + removedNodes.size() );
        int overallCount = countNodes(targetNode) - 1;
        System.out.println("Overall nodes in rs: " + overallCount);

        if(overallCount != nodesInDirectoryByGuid.size())
            throw new RuntimeException("Failed to import user data from directory");
        else
            System.out.println("done.");

        //        printTree(targetNode);
    }


    private int countNodes(AbstractUserManagerNode current) {
        int i = 1;
        for(AbstractUserManagerNode n : current.getChildren()){
            i += countNodes(n);
        }
        return i;
    }

}

Offline

#2 2020-12-01 15:50:47

eduardo
Administrator
Registered: 2016-11-01
Website

Re: ldapimport.groovy

Hi Stéphane,

the ldap script provided is just a template which has to be changed / adapted to your specific ldap configuration. Pls take a look here: https://forum.reportserver.net/viewtopic.php?id=710 and https://forum.reportserver.net/viewtopic.php?id=711

In most of the cases, these methods / parameters should be adapted. Pls check below for a jumpcloud ldap example:

1. Credentials:

lul.setProviderUrl("ldap://ldap.jumpcloud.com:389");
lul.setSecurityPrincipal("uid=LDAP_BINDING_USER,ou=Users,o=YOUR_ORG_ID,dc=jumpcloud,dc=com"); // replace LDAP_BINDING_USER and YOUR_ORG_ID with your account details
lul.setSecurityCredentials("******");    //your password

2. change getGUID() in order to return something unique in the jumpcloud LDAP installation, e.g.:

private String getGuid(SearchResult sr) throws NamingException{
    return sr.getName().toString(); //you can also return a unique id analogously as the original script
}

3. change your filter:
private String ldapFilter = "(|(objectClass=organizationalUnit)(objectClass=person)(objectClass=posixGroup))";

4. change the username property:

//node.setUsername(getStringAttribute(sr, "sAMAccountName"));
node.setUsername(getStringAttribute(sr, "uid")); // set uid or any attribute you have identifying your username

In order to check your settings, I would recommend you using an external tool, e.g. ldapsearch.
For example:

ldapsearch -x -b "ou=Users,o=58b57552f9,dc=jumpcloud,dc=com" -D "uid=danna,ou=Users,o=58b57552f9,dc=jumpcloud,dc=com" -w "myPassword" -H ldap://ldap.jumpcloud.com:389 "(|(objectClass=organizationalUnit)(objectClass=person)(objectClass=group))"

Here you see the filter "(|(objectClass=organizationalUnit)(objectClass=person)(objectClass=group))"  and the rest of the parameters. So you can change them until you get the list of all the users you need. When you find out the correct parameters, you can enter them to the script and it should work analogously as the external LDAP client.

Regards,
Eduardo

Offline

#3 2020-12-04 08:03:24

Stéphane
Member
Registered: 2020-09-30

Re: ldapimport.groovy

Thanks Eduardo,
I hadn't seen changes 2 and 4.

I also had to adapt the type of recovered objects

//} else if (objectClass.contains ("user")) {
} else if (objectClass.contains ("person")) {

Now, it's OK for this.

I still have a problem with user variables:
I have created the variable "Affectation" but I cannot give it a value for each user

Set<UserProperty> userProperties = new HashSet();
userProperties.add(new UserProperty ("Affectation", getStringAttribute(sr, "supannEntiteAffectationPrincipale")));
node.setProperties(userProperties);

I have no error but it doesn't work

Offline

#4 2020-12-09 12:40:48

eduardo
Administrator
Registered: 2016-11-01
Website

Re: ldapimport.groovy

Hi Stéphane,

thanks for letting us know.

For setting user properties, you can use this method:

net.datenwerke.security.service.usermanager.UserPropertiesService.setProperty(User, UserProperty)

or this:

net.datenwerke.security.service.usermanager.UserPropertiesService.setPropertyValue(User, String, Object)

You can use this service analogously as the current service:

UserManagerService userManagerService = GLOBALS.getRsService(UserManagerService.class);

Regards,
Eduardo

Offline

#5 2020-12-09 12:56:14

Stéphane
Member
Registered: 2020-09-30

Re: ldapimport.groovy

Thanks, but it was not a property , it was a user variable.

i use this and it's working

        StringUserVariableInstance instance = new StringUserVariableInstance();
        instance.setDefinition(getVarDefinition());  //out of the class and then private: StringUserVariableDefinition varDefinition = GLOBALS.findEntity(StringUserVariableDefinition.class, 18745l);
        instance.setFolk(node);
        instance.setValue(getStringAttribute(sr, "supannEntiteAffectationPrincipale").replace("{LOC}",""));
	getUserVarService().persist(instance);  //out of the class and then private: UserVariableService userVarService = GLOBALS.getRsService(UserVariableService.class);

Offline

#6 2021-05-07 11:37:00

eduardo
Administrator
Registered: 2016-11-01
Website

Re: ldapimport.groovy

Hi Stéphane,

pls note we published the ldaptester.groovy script here:
https://github.com/infofabrik/reportserver-samples/

which allows you to safely test your LDAP settings before importing them into the "real" ldapimport.groovy script (or ldap.cf configuration file).

Regards,
Eduardo

Offline

Board footer

Powered by FluxBB