2015-11-30 17:55:04 +01:00
package org.gcube.portal.ldapexport ;
import java.util.ArrayList ;
import java.util.List ;
import java.util.Properties ;
import java.util.Random ;
import javax.naming.Context ;
import javax.naming.NamingEnumeration ;
import javax.naming.NamingException ;
import javax.naming.directory.Attribute ;
import javax.naming.directory.Attributes ;
import javax.naming.directory.BasicAttribute ;
import javax.naming.directory.BasicAttributes ;
import javax.naming.directory.DirContext ;
import javax.naming.directory.InitialDirContext ;
import javax.naming.directory.SearchControls ;
import javax.naming.directory.SearchResult ;
import org.gcube.common.portal.PortalContext ;
2016-07-04 15:37:55 +02:00
import org.gcube.vomanagement.usermanagement.GroupManager ;
import org.gcube.vomanagement.usermanagement.impl.LiferayGroupManager ;
2016-09-26 18:06:54 +02:00
import org.gcube.vomanagement.usermanagement.impl.LiferayUserManager ;
2016-07-04 15:37:55 +02:00
import org.gcube.vomanagement.usermanagement.model.GCubeGroup ;
2015-11-30 17:55:04 +01:00
import com.liferay.portal.kernel.cache.CacheRegistryUtil ;
import com.liferay.portal.kernel.exception.SystemException ;
2020-05-29 10:35:58 +02:00
import com.liferay.portal.kernel.log.Log ;
import com.liferay.portal.kernel.log.LogFactoryUtil ;
2015-11-30 17:55:04 +01:00
import com.liferay.portal.model.User ;
import com.liferay.portal.service.UserLocalServiceUtil ;
public class LDAPSync implements Runnable {
2020-05-29 10:35:58 +02:00
private static Log _log = LogFactoryUtil . getLog ( LDAPSync . class ) ;
2015-11-30 17:55:04 +01:00
private static final String LDAP_ORG_FILTER = " (objectClass=organizationalUnit) " ;
private static final String LDAP_GROUP_FILTER = " (objectClass=posixGroup) " ;
2015-12-03 18:03:28 +01:00
private static final String USER_CONTEXT = " ,ou=People,o=D4Science,ou=Organizations,dc=d4science,dc=org " ;
private static final String DEFAULT_GID_NUMBER = " 1000 " ;
2015-11-30 17:55:04 +01:00
2016-09-26 18:06:54 +02:00
private static final String SSH_PUBLIC_KEY_ATTR = " SSH-public-key " ;
2015-11-30 17:55:04 +01:00
private String ldapUrl ;
private String filter ;
private String principal ;
private String pwd ;
2016-09-26 18:06:54 +02:00
2015-11-30 17:55:04 +01:00
public LDAPSync ( String ldapUrl , String filter , String principal , String pwd ) {
this . ldapUrl = ldapUrl ;
this . filter = filter ;
this . principal = principal ;
this . pwd = pwd ;
_log . info ( " Starting LDAPSync over " + ldapUrl ) ;
}
/ * *
*
* @return the Liferay mapped as Root Organization
* /
2016-07-04 15:37:55 +02:00
private GCubeGroup getRootVO ( ) {
2015-11-30 17:55:04 +01:00
try {
2016-07-04 15:37:55 +02:00
GroupManager gm = new LiferayGroupManager ( ) ;
String rootVoName = gm . getRootVOName ( ) ;
_log . debug ( " Root organization name found: " + rootVoName ) ;
return gm . getGroup ( gm . getGroupIdFromInfrastructureScope ( " / " + rootVoName ) ) ;
}
catch ( Exception e ) {
_log . error ( " There were problems retrieving root VO group " , e ) ;
2015-11-30 17:55:04 +01:00
}
_log . error ( " Could not find any root organization " ) ;
return null ;
}
@Override
public void run ( ) {
_log . debug ( " Reading Portal Users ... " ) ;
List < User > users = null ;
try {
users = getAllLiferayUsers ( ) ;
2020-05-29 10:35:58 +02:00
_log . info ( " ***Read " + users . size ( ) + " from LR DB \ n " ) ;
2015-11-30 17:55:04 +01:00
} catch ( Exception e1 ) {
e1 . printStackTrace ( ) ;
}
_log . debug ( " Reading Portal Organizations ... " ) ;
2016-07-04 15:37:55 +02:00
GCubeGroup rootVO = getRootVO ( ) ;
2015-11-30 17:55:04 +01:00
_log . debug ( " Initializing LDAP exporter ... " ) ;
Properties env = new Properties ( ) ;
env . put ( Context . INITIAL_CONTEXT_FACTORY , " com.sun.jndi.ldap.LdapCtxFactory " ) ;
env . put ( Context . PROVIDER_URL , ldapUrl ) ;
env . put ( Context . SECURITY_PRINCIPAL , principal ) ;
env . put ( Context . SECURITY_CREDENTIALS , pwd ) ;
try {
DirContext ctx = new InitialDirContext ( env ) ;
_log . debug ( " Initiating LDAP Sync ... " ) ;
2015-12-03 18:03:28 +01:00
createUsersOrganizationalUnit ( ctx ) ;
2015-11-30 17:55:04 +01:00
//crate or update the whole list of organizations (objectClass=organizationalUnit, ou="+orgName+",dc=d4science,dc=org) and groups ( objectClass=top and POSIXGroup)
2016-07-04 15:37:55 +02:00
2015-11-30 17:55:04 +01:00
updateGroups ( ctx , rootVO ) ;
2015-12-03 18:03:28 +01:00
//and update the users list
exportSingleUsers ( ctx , env , users ) ;
2015-11-30 17:55:04 +01:00
} catch ( NamingException e ) {
_log . error ( " Something went Wrong during LDAP Sync in Exporting to LDAP " ) ;
e . printStackTrace ( ) ;
2016-09-26 18:06:54 +02:00
} catch ( Exception es ) {
2015-11-30 17:55:04 +01:00
_log . error ( " Something went Wrong during LDAP Sync in retrieving Liferay Organization " ) ;
es . printStackTrace ( ) ;
}
}
2016-07-04 15:37:55 +02:00
2015-12-03 18:03:28 +01:00
/ * *
* create the following : ou = People , o = D4Science , ou = Organizations , dc = d4science , dc = org
* @param ctx
* @throws NamingException
* /
private void createUsersOrganizationalUnit ( DirContext ctx ) throws NamingException {
if ( ! checkIfLDAPOrganizationalUnitExists ( ctx , " ou=Organizations,dc=d4science,dc=org " ) ) {
Attributes attributes = new BasicAttributes ( ) ;
Attribute objectClass = new BasicAttribute ( " objectClass " ) ;
objectClass . add ( " organizationalUnit " ) ;
attributes . put ( objectClass ) ;
Attribute description = new BasicAttribute ( " description " ) ;
description . add ( " Where to find users " ) ;
attributes . put ( description ) ;
// private static final String USER_CONTEXT = ",";
ctx . createSubcontext ( " ou=Organizations,dc=d4science,dc=org " , attributes ) ;
attributes = new BasicAttributes ( ) ;
objectClass = new BasicAttribute ( " objectClass " ) ;
objectClass . add ( " Organization " ) ;
attributes . put ( objectClass ) ;
description . add ( " Default Organization " ) ;
ctx . createSubcontext ( " o=D4Science,ou=Organizations,dc=d4science,dc=org " , attributes ) ;
attributes = new BasicAttributes ( ) ;
objectClass = new BasicAttribute ( " objectClass " ) ;
objectClass . add ( " organizationalUnit " ) ;
attributes . put ( objectClass ) ;
description . add ( " People Org Unit " ) ;
ctx . createSubcontext ( " ou=People,o=D4Science,ou=Organizations,dc=d4science,dc=org " , attributes ) ;
}
else
_log . info ( " ou=Organizations,dc=d4science,dc=org already present, skip " ) ;
}
2015-11-30 17:55:04 +01:00
2015-12-03 18:03:28 +01:00
/ * *
*
* @param ctx
* @param root
* @throws NamingException
* @throws SystemException
* /
2016-07-04 15:37:55 +02:00
private void updateGroups ( DirContext ctx , GCubeGroup root ) throws NamingException , SystemException {
String subCtx = getOrgSubContext ( root . getGroupName ( ) ) ;
2015-11-30 17:55:04 +01:00
if ( ! checkIfLDAPOrganizationalUnitExists ( ctx , subCtx ) )
createOrganizationalUnit ( ctx , subCtx ) ;
2016-07-04 15:37:55 +02:00
for ( GCubeGroup vo : root . getChildren ( ) ) {
String orgSubCtx = " ou= " + vo . getGroupName ( ) + " , " + subCtx ;
2015-11-30 17:55:04 +01:00
if ( ! checkIfLDAPOrganizationalUnitExists ( ctx , orgSubCtx ) )
createOrganizationalUnit ( ctx , orgSubCtx ) ;
2016-07-04 15:37:55 +02:00
for ( GCubeGroup vre : vo . getChildren ( ) ) {
String vreSubCtx = " cn= " + vre . getGroupName ( ) + " , " + orgSubCtx ;
2015-11-30 17:55:04 +01:00
if ( ! checkIfLDAPGroupExists ( ctx , vreSubCtx ) )
2016-07-04 15:37:55 +02:00
createGroupVRE ( ctx , vreSubCtx , vre . getGroupName ( ) ) ;
2015-11-30 17:55:04 +01:00
//update the list of users in such VRE
updateUsersInGroup ( ctx , vreSubCtx , vre ) ;
}
}
}
2015-12-03 18:03:28 +01:00
/ * *
*
* @param ctx
* @param vreSubCtx
* @param vre
* @throws NamingException
* @throws SystemException
* /
2016-07-04 15:37:55 +02:00
private void updateUsersInGroup ( DirContext ctx , String vreSubCtx , GCubeGroup vre ) throws NamingException , SystemException {
2020-05-29 10:35:58 +02:00
_log . debug ( " updateUsersInGroup: " + vre . getGroupName ( ) ) ;
List < User > users = new ArrayList < > ( ) ;
try {
users = UserLocalServiceUtil . getGroupUsers ( vre . getGroupId ( ) ) ;
} catch ( Exception e ) {
_log . error ( " Could not retrieve members of vre: " + vre . getGroupName ( ) + " having groupid: " + vre . getGroupId ( ) , e ) ;
}
2015-11-30 17:55:04 +01:00
for ( User userObj : users ) {
String user = userObj . getScreenName ( ) ;
try {
Attribute memberUid = new BasicAttribute ( " memberUid " ) ;
memberUid . add ( user ) ;
Attributes attributes = new BasicAttributes ( ) ;
attributes . put ( memberUid ) ;
ctx . modifyAttributes ( vreSubCtx , DirContext . ADD_ATTRIBUTE , attributes ) ;
_log . info ( " Adding user: " + user ) ;
}
catch ( javax . naming . directory . AttributeInUseException ex ) {
_log . trace ( " Not adding already existing user: " + user ) ;
}
}
}
2016-09-26 18:06:54 +02:00
private void exportSingleUsers ( DirContext ctx , Properties env , List < User > users ) throws Exception {
2015-11-30 17:55:04 +01:00
for ( User user : users ) {
2016-09-26 18:06:54 +02:00
String lastName = " NoLastNameEntered " ;
if ( user . getLastName ( ) ! = null & & user . getLastName ( ) . compareTo ( " " ) ! = 0 )
lastName = user . getLastName ( ) ;
_log . debug ( " Trying read sshPublicKey for " + user . getScreenName ( ) ) ;
String sshPublicKey = new LiferayUserManager ( ) . readCustomAttr ( user . getUserId ( ) , SSH_PUBLIC_KEY_ATTR ) . toString ( ) ;
2017-09-25 15:15:36 +02:00
if ( user . getFirstName ( ) ! = null & & user . getFirstName ( ) . compareTo ( " " ) ! = 0 )
updateUserInLDAP ( user . getScreenName ( ) , user . getFirstName ( ) , lastName , user . getFullName ( ) , user . getEmailAddress ( ) , " {SHA} " + user . getPassword ( ) , sshPublicKey , ctx , filter ) ;
2016-07-04 15:37:55 +02:00
_log . debug ( " Updated " + user . getScreenName ( ) ) ;
2015-11-30 17:55:04 +01:00
}
_log . debug ( " LDAP Users Sync cycle done " ) ;
if ( ! users . isEmpty ( ) )
_log . info ( " LDAP Users Sync Completed OK! " ) ;
else
_log . warn ( " LDAP Users Sync cycle skipped this time " ) ;
}
/ * *
*
* @param ctx
* @param subContext
* @throws NamingException
* /
private void createOrganizationalUnit ( DirContext ctx , String subContext ) throws NamingException {
Attributes attributes = new BasicAttributes ( ) ;
Attribute objectClass = new BasicAttribute ( " objectClass " ) ;
objectClass . add ( " organizationalUnit " ) ;
attributes . put ( objectClass ) ;
Attribute description = new BasicAttribute ( " description " ) ;
description . add ( " Liferay Organization " ) ;
attributes . put ( description ) ;
ctx . createSubcontext ( subContext , attributes ) ;
_log . info ( " Added " + subContext ) ;
}
/ * *
*
* @param ctx
* @param subContext
* @param vreName
* @throws NamingException
* /
private void createGroupVRE ( DirContext ctx , String subContext , String vreName ) throws NamingException {
Attributes attributes = new BasicAttributes ( ) ;
Attribute objectClass = new BasicAttribute ( " objectClass " ) ;
objectClass . add ( " top " ) ;
objectClass . add ( " posixGroup " ) ;
// objectClass.add("researchProject");
// objectClass.add("groupOfMembers");
attributes . put ( objectClass ) ;
Attribute cn = new BasicAttribute ( " cn " ) ;
cn . add ( vreName ) ;
attributes . put ( cn ) ;
Attribute gidNumber = new BasicAttribute ( " gidNumber " ) ;
2015-12-03 18:03:28 +01:00
gidNumber . add ( String . valueOf ( getRandomPOSIXidentifier ( ) ) ) ;
2015-11-30 17:55:04 +01:00
attributes . put ( gidNumber ) ;
ctx . createSubcontext ( subContext , attributes ) ;
_log . info ( " Added " + subContext ) ;
}
private String getOrgSubContext ( String orgName ) {
return " ou= " + orgName + " ,dc=d4science,dc=org " ;
}
/ * *
*
* @param ctx
* @param orgSubctx
* @return true if exists
* /
private boolean checkIfLDAPOrganizationalUnitExists ( DirContext ctx , String orgSubctx ) {
SearchControls ctls = new SearchControls ( ) ;
ctls . setSearchScope ( SearchControls . SUBTREE_SCOPE ) ;
NamingEnumeration < SearchResult > answer ;
try {
answer = ctx . search ( orgSubctx , LDAP_ORG_FILTER , ctls ) ;
} catch ( NamingException e ) {
_log . debug ( " not found in LDAP (will add it): Organization: " + orgSubctx ) ;
return false ;
}
boolean toReturn = answer . hasMoreElements ( ) ;
_log . debug ( " Organization: " + orgSubctx + " exists? " + toReturn ) ;
return toReturn ;
}
/ * *
*
* @param ctx
* @param groupSubctx
* @return true if exists
* /
private boolean checkIfLDAPGroupExists ( DirContext ctx , String groupSubctx ) {
SearchControls ctls = new SearchControls ( ) ;
ctls . setSearchScope ( SearchControls . SUBTREE_SCOPE ) ;
NamingEnumeration < SearchResult > answer ;
try {
answer = ctx . search ( groupSubctx , LDAP_GROUP_FILTER , ctls ) ;
} catch ( NamingException e ) {
_log . debug ( " not found in LDAP (will add it): Group: " + groupSubctx ) ;
return false ;
}
boolean toReturn = answer . hasMoreElements ( ) ;
_log . debug ( " Group: " + groupSubctx + " exists? " + toReturn ) ;
return toReturn ;
}
/ * *
*
* @param username
* @return the single user subContext
* /
private String getSubContext ( String username ) {
2015-12-03 18:03:28 +01:00
return " uid= " + username + USER_CONTEXT ;
2015-11-30 17:55:04 +01:00
}
/ * *
*
* @param username
* @param ctx
* @param filter
2016-07-04 15:37:55 +02:00
* @return true if exists
2015-11-30 17:55:04 +01:00
* /
private boolean checkIfLDAPUserExists ( String username , DirContext ctx , String filter ) {
SearchControls ctls = new SearchControls ( ) ;
ctls . setSearchScope ( SearchControls . SUBTREE_SCOPE ) ;
NamingEnumeration < SearchResult > answer ;
try {
answer = ctx . search ( getSubContext ( username ) , filter , ctls ) ;
} catch ( NamingException e ) {
_log . info ( " user: " + username + " not found in LDAP, trying to export it " ) ;
return false ;
}
return answer . hasMoreElements ( ) ;
}
/ * *
*
* @param username
* @param name
* @param lastName
* @param email
* @param passwd
* @param ctx
* @throws NamingException
* /
2016-09-26 18:06:54 +02:00
private void updateUserInLDAP ( String username , String name , String lastName , String fullName , String email , String passwd , String sshPublicKey , DirContext ctx , String filter ) throws NamingException {
2015-11-30 17:55:04 +01:00
Attributes attributes = new BasicAttributes ( ) ;
Attribute objectClass = new BasicAttribute ( " objectClass " ) ;
objectClass . add ( " inetOrgPerson " ) ;
2015-12-03 18:03:28 +01:00
objectClass . add ( " posixAccount " ) ;
2016-09-26 18:06:54 +02:00
objectClass . add ( " organizationalPerson " ) ;
objectClass . add ( " person " ) ;
objectClass . add ( " shadowAccount " ) ;
objectClass . add ( " ldapPublicKey " ) ;
2015-11-30 17:55:04 +01:00
attributes . put ( objectClass ) ;
//the main ldap server uses 'givenName' for the First name, 'cn' for "first name last name', 'sn' for the last name
Attribute givenName = new BasicAttribute ( " givenName " ) ;
Attribute cn = new BasicAttribute ( " cn " ) ;
Attribute sn = new BasicAttribute ( " sn " ) ;
Attribute mail = new BasicAttribute ( " mail " ) ;
Attribute userPassword = new BasicAttribute ( " userPassword " ) ;
2015-12-03 18:03:28 +01:00
Attribute gidNumber = new BasicAttribute ( " gidNumber " ) ;
Attribute homeDirectory = new BasicAttribute ( " homeDirectory " ) ;
2016-09-26 18:06:54 +02:00
Attribute shell = new BasicAttribute ( " loginShell " ) ;
Attribute sshPublicKeyAttr = new BasicAttribute ( " sshPublicKey " ) ;
2015-11-30 17:55:04 +01:00
givenName . add ( name ) ;
cn . add ( fullName ) ;
sn . add ( lastName ) ;
mail . add ( email ) ;
userPassword . add ( passwd ) ;
2015-12-03 18:03:28 +01:00
gidNumber . add ( DEFAULT_GID_NUMBER ) ;
homeDirectory . add ( " /home/ " + username ) ;
2016-09-26 18:06:54 +02:00
shell . add ( " /bin/bash " ) ;
sshPublicKeyAttr . add ( sshPublicKey ) ;
2015-11-30 17:55:04 +01:00
attributes . put ( givenName ) ;
attributes . put ( cn ) ;
attributes . put ( sn ) ;
attributes . put ( mail ) ;
attributes . put ( userPassword ) ;
2015-12-03 18:03:28 +01:00
attributes . put ( gidNumber ) ;
attributes . put ( homeDirectory ) ;
2016-09-26 18:06:54 +02:00
attributes . put ( shell ) ;
attributes . put ( sshPublicKeyAttr ) ;
2015-11-30 17:55:04 +01:00
if ( checkIfLDAPUserExists ( username , ctx , filter ) ) {
//_log.debug("User " + username + " already exists, replacing attributes");
ctx . modifyAttributes ( getSubContext ( username ) , DirContext . REPLACE_ATTRIBUTE , attributes ) ;
2015-12-03 18:03:28 +01:00
_log . debug ( " Updated attributes for already existing user with uid= " + username ) ;
2015-11-30 17:55:04 +01:00
}
else {
2015-12-03 18:03:28 +01:00
int n = getRandomPOSIXidentifier ( ) ;
while ( checkIfPosixUidNumberExists ( ctx , n ) ) {
_log . info ( " Found collision on UidNumber= " + n ) ;
n = getRandomPOSIXidentifier ( ) ;
_log . info ( " Trying newone= " + n ) ;
}
Attribute uidNumber = new BasicAttribute ( " uidNumber " ) ;
attributes . put ( uidNumber ) ;
uidNumber . add ( String . valueOf ( n ) ) ;
2015-11-30 17:55:04 +01:00
ctx . createSubcontext ( getSubContext ( username ) , attributes ) ;
_log . debug ( " New User Found with uid= " + username + " created " ) ;
}
}
/ * *
*
* @return the whole list of users
* /
private List < User > getAllLiferayUsers ( ) {
String infraName = PortalContext . getConfiguration ( ) . getInfrastructureName ( ) ;
_log . info ( " TRY Reading non chached users belonging to: / " + infraName ) ;
List < User > toReturn = new ArrayList < User > ( ) ;
try {
CacheRegistryUtil . clear ( ) ; //needed to avoid cache use by liferay API
2016-07-04 15:37:55 +02:00
long groupId = new LiferayGroupManager ( ) . getGroupIdFromInfrastructureScope ( " / " + infraName ) ;
toReturn = UserLocalServiceUtil . getGroupUsers ( groupId ) ;
} catch ( Exception e ) {
2015-11-30 17:55:04 +01:00
_log . error ( " Error during LDAP Sync, could not retrieve users from LR DB: " + e . getMessage ( ) ) ;
}
return toReturn ;
}
/ * *
*
* @return an integer between 1000 and 2147483647
* /
2015-12-03 18:03:28 +01:00
private int getRandomPOSIXidentifier ( ) {
2015-11-30 17:55:04 +01:00
final int Low = 1000 ;
final int High = 2147483647 ;
Random r = new Random ( ) ;
int toReturn = r . nextInt ( High - Low ) + Low ;
2015-12-03 18:03:28 +01:00
return toReturn ;
}
2016-07-04 15:37:55 +02:00
2015-12-03 18:03:28 +01:00
private boolean checkIfPosixUidNumberExists ( DirContext ctx , int numberToCheck ) {
SearchControls ctls = new SearchControls ( ) ;
ctls . setSearchScope ( SearchControls . SUBTREE_SCOPE ) ;
NamingEnumeration < SearchResult > answer ;
try {
answer = ctx . search ( " ou=People,o=D4Science,ou=Organizations,dc=d4science,dc=org " , " (uidNumber= " + numberToCheck + " ) " , ctls ) ;
} catch ( NamingException e ) {
_log . info ( " exception " ) ;
return false ;
}
boolean toReturn = answer . hasMoreElements ( ) ;
_log . info ( " return " + toReturn ) ;
return toReturn ;
2015-11-30 17:55:04 +01:00
}
}