diff --git a/ad-connector/src/main/java/net/jami/jams/ad/connector/ADConnector.java b/ad-connector/src/main/java/net/jami/jams/ad/connector/ADConnector.java index d4e5a6266af7a352822c82ab9d38546935d88817..28867e97d37b16bca7c31eb2053f3c38533451cb 100644 --- a/ad-connector/src/main/java/net/jami/jams/ad/connector/ADConnector.java +++ b/ad-connector/src/main/java/net/jami/jams/ad/connector/ADConnector.java @@ -36,6 +36,7 @@ import net.jami.jams.common.objects.user.User; import net.jami.jams.common.objects.user.UserProfile; import net.jami.jams.common.serialization.JsoniterRegistry; +import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; @Slf4j @@ -92,7 +93,7 @@ public class ADConnector implements AuthenticationSource { } @Override - public UserProfile[] getUserProfile(String queryString, String field) { + public List<UserProfile> getUserProfile(String queryString, String field) { return userProfileService.getUserProfile(queryString, field); } diff --git a/ad-connector/src/main/java/net/jami/jams/ad/connector/service/UserProfileService.java b/ad-connector/src/main/java/net/jami/jams/ad/connector/service/UserProfileService.java index c181fbb8cfbc10e03a1ae6b41baed62eef51cb1d..e3d62c44247909d7bc9cb00da2f1b43d56d3ccda 100644 --- a/ad-connector/src/main/java/net/jami/jams/ad/connector/service/UserProfileService.java +++ b/ad-connector/src/main/java/net/jami/jams/ad/connector/service/UserProfileService.java @@ -43,6 +43,7 @@ import net.jami.jams.common.objects.user.UserProfile; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; @Slf4j @@ -50,7 +51,7 @@ public class UserProfileService { private static final HashMap<String, String> fieldMap = ADConnector.settings.getFieldMappings(); - public UserProfile[] getUserProfile(String queryString, String field) { + public List<UserProfile> getUserProfile(String queryString, String field) { Endpoint endpoint = ADConnector.getConnection(); UserProfile[] profiles = null; try { @@ -75,16 +76,8 @@ public class UserProfileService { queryResponse = connector.execute(); } List<List<Field>> results = queryResponse.getAll().stream().map(EntityResponse::getValue).collect(Collectors.toList()); - if (results.size() > 0) { - profiles = new UserProfile[results.size()]; - } else { - return null; - } - for (int i = 0; i < profiles.length; i++) { - profiles[i] = profileFromResponse(results.get(i)); - } - profiles = removeNull(profiles); - return profiles; + return results.stream().map(UserProfileService::profileFromResponse).collect(Collectors.toList()) + .stream().filter(Objects::nonNull).collect(Collectors.toList()); } catch (Exception e) { log.error("Could not find entity with specified parameters."); return null; @@ -93,14 +86,6 @@ public class UserProfileService { } } - public UserProfile[] removeNull(UserProfile[] a) { - ArrayList<UserProfile> removedNull = new ArrayList<UserProfile>(); - for (UserProfile p : a) - if (p != null) - removedNull.add(p); - return removedNull.toArray(new UserProfile[0]); - } - public static QueryRequest buildRequest(Endpoint endpoint) { QueryRequest queryRequest = new QueryRequest(); queryRequest.setDirectoryType(DirectoryType.MS_ACTIVE_DIRECTORY); diff --git a/datastore/src/main/java/net/jami/datastore/main/DataStore.java b/datastore/src/main/java/net/jami/datastore/main/DataStore.java index 2765ad302280b85d344102a19b55c133b3126581..7e83e187fbd018bc268e10169987daa21fdf0392 100644 --- a/datastore/src/main/java/net/jami/datastore/main/DataStore.java +++ b/datastore/src/main/java/net/jami/datastore/main/DataStore.java @@ -80,7 +80,7 @@ public class DataStore implements AuthenticationSource { return userDao.storeObject(user); } @Override - public UserProfile[] getUserProfile(String queryString, String field) { + public List<UserProfile> getUserProfile(String queryString, String field) { List<UserProfile> userList; if (!queryString.equals("*")) { @@ -97,7 +97,7 @@ public class DataStore implements AuthenticationSource { } else userList = userProfileDao.getObjects(null); - return userList.toArray(new UserProfile[userList.size()]); + return userList; } @Override diff --git a/jami-nameserver/src/main/java/net/jami/jams/nameserver/LocalNameServer.java b/jami-nameserver/src/main/java/net/jami/jams/nameserver/LocalNameServer.java index f9bae1a01f8eeb82b04a219fcfa94fc9dd4b1819..7d6b7964613e1c554834cb86e3b58c913e31f244 100644 --- a/jami-nameserver/src/main/java/net/jami/jams/nameserver/LocalNameServer.java +++ b/jami-nameserver/src/main/java/net/jami/jams/nameserver/LocalNameServer.java @@ -69,8 +69,8 @@ public class LocalNameServer implements NameServer { //Reattempt resolution via directory lookups. final User user = new User(); for(AuthModuleKey key : authenticationModule.getAuthSources().keySet()){ - UserProfile[] profiles = authenticationModule.getAuthSources().get(key).getUserProfile(username,"LOGON_NAME"); - if(profiles != null && profiles.length == 1){ + List<UserProfile> profiles = authenticationModule.getAuthSources().get(key).getUserProfile(username,"LOGON_NAME"); + if(profiles != null && profiles.size() == 1){ user.setUsername(username); user.setRealm(key.getRealm()); user.setUserType(key.getType()); diff --git a/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSource.java b/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSource.java index d1b56425367d6c4c56dec00ccfdf8f787c2450e6..170e5bb37f0447a0d7568dcf53ab52499306f67f 100644 --- a/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSource.java +++ b/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSource.java @@ -25,10 +25,12 @@ package net.jami.jams.common.authentication; import net.jami.jams.common.objects.user.User; import net.jami.jams.common.objects.user.UserProfile; +import java.util.List; + public interface AuthenticationSource { boolean createUser(User user); - UserProfile[] getUserProfile(String queryString, String field); + List<UserProfile> getUserProfile(String queryString, String field); boolean setUserProfile(UserProfile userProfile); boolean authenticate(String username, String password); AuthenticationSourceInfo getInfo(); diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/user/UserProfile.java b/jams-common/src/main/java/net/jami/jams/common/objects/user/UserProfile.java index ff2740848953f3c2a5deb59898566ddf49bb69ce..08c6b08999aff5a765c21d840299fdd8ebcdf47e 100644 --- a/jams-common/src/main/java/net/jami/jams/common/objects/user/UserProfile.java +++ b/jams-common/src/main/java/net/jami/jams/common/objects/user/UserProfile.java @@ -70,6 +70,7 @@ public class UserProfile implements DatabaseObject { private String phoneNumberExtension; private String faxNumber; private String mobileNumber; + private String jamiId; public UserProfile(ResultSet rs) throws Exception { this.username = rs.getString("username"); @@ -84,6 +85,14 @@ public class UserProfile implements DatabaseObject { this.mobileNumber = rs.getString("mobileNumber"); } + public String getJamiId(){ + return jamiId; + } + + public void setJamiId(User user){ + jamiId = user.getJamiId(); + } + @JsonIgnore //Ignore this if we pass through JSON iterator. public String getAsVCard(){ @@ -100,6 +109,7 @@ public class UserProfile implements DatabaseObject { if(this.getFaxNumber() != null) vCard.addTelephoneNumber(this.getFaxNumber(), TelephoneType.FAX); if(this.getEmail() != null) vCard.addEmail(this.getEmail(), EmailType.WORK); if(this.getOrganization() != null) vCard.setOrganization(this.getOrganization()); + if(this.getJamiId() != null) vCard.setExtendedProperty("JamiID",this.jamiId); /*I think this is how Base64 will work in this case*/ if(this.getProfilePicture() != null) { diff --git a/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterDeviceFlow.java b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterDeviceFlow.java index 3361566a5bc88fab82cb767a889bd1f1dd6b23b9..09f67e6935a547ed23151e69fd238792f80ec418 100644 --- a/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterDeviceFlow.java +++ b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterDeviceFlow.java @@ -50,7 +50,7 @@ public class RegisterDeviceFlow { User user = dataStore.getUserDao().getObjects(statementList).get(0); UserProfile userProfile = userAuthenticationModule.getAuthSources() .get(new AuthModuleKey(user.getRealm(),user.getUserType())) - .getUserProfile(username,"LOGON_NAME")[0]; + .getUserProfile(username,"LOGON_NAME").get(0); if (user == null) { log.error("Tried to enroll a device, but could not find a user, this is impossible!"); diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/DirectoryEntryServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/DirectoryEntryServlet.java index 8a7c87fe99d61cc34b622f8d00458592e3f73197..3511994c258b0447181ba60efe8ad86e6119fc20 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/DirectoryEntryServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/DirectoryEntryServlet.java @@ -193,19 +193,18 @@ public class DirectoryEntryServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (req.getParameter("directory") != null && req.getParameter("directoryType") != null) { - UserProfile[] profiles = userAuthenticationModule.getAuthSources() + List<UserProfile> profiles = userAuthenticationModule.getAuthSources() .get(new AuthModuleKey(req.getParameter("directory"), AuthenticationSourceType.fromString(req.getParameter("directoryType")))) .getUserProfile(req.getParameter("username"), "LOGON_NAME"); if(req.getParameter("format") != null && req.getParameter("format").equals("vcard")){ - resp.getOutputStream().write(profiles[0].getAsVCard().getBytes()); + resp.getOutputStream().write(profiles.get(0).getAsVCard().getBytes()); } - else resp.getOutputStream().write(JsonStream.serialize(profiles[0]).getBytes()); + else resp.getOutputStream().write(JsonStream.serialize(profiles.get(0)).getBytes()); return; } List<UserProfile> userProfiles = new ArrayList<>(); userAuthenticationModule.getAuthSources().forEach((k, v) -> { - UserProfile[] profiles = v.getUserProfile(req.getParameter("username"), "LOGON_NAME"); - if (profiles != null && profiles.length != 0) userProfiles.addAll(Arrays.asList(profiles)); + userProfiles.addAll(v.getUserProfile(req.getParameter("username"), "LOGON_NAME")); }); if(req.getParameter("format") != null && req.getParameter("format").equals("vcard")){ resp.getOutputStream().write(userProfiles.get(0).getAsVCard().getBytes()); diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java index e236985fbafb24e516fe49c63d52db8ece7fc4a5..de295eac806cc48ad061657dd44a04611c955ad1 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java @@ -1,25 +1,26 @@ /* -* Copyright (C) 2020 by Savoir-faire Linux -* Authors: William Enright <william.enright@savoirfairelinux.com> -* Ndeye Anna Ndiaye <anna.ndiaye@savoirfairelinux.com> -* Johnny Flores <johnny.flores@savoirfairelinux.com> -* Mohammed Raza <mohammed.raza@savoirfairelinux.com> -* Felix Sidokhine <felix.sidokhine@savoirfairelinux.com> -* -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see <https://www.gnu.org/licenses/>. -*/ + * Copyright (C) 2020 by Savoir-faire Linux + * Authors: William Enright <william.enright@savoirfairelinux.com> + * Ndeye Anna Ndiaye <anna.ndiaye@savoirfairelinux.com> + * Johnny Flores <johnny.flores@savoirfairelinux.com> + * Mohammed Raza <mohammed.raza@savoirfairelinux.com> + * Felix Sidokhine <felix.sidokhine@savoirfairelinux.com> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + package net.jami.jams.server.servlets.api.auth.directory; import com.jsoniter.output.JsonStream; @@ -27,6 +28,10 @@ import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import net.jami.jams.common.dao.StatementElement; +import net.jami.jams.common.dao.StatementList; +import net.jami.jams.common.objects.user.AccessLevel; +import net.jami.jams.common.objects.user.User; import net.jami.jams.common.objects.user.UserProfile; import java.io.IOException; @@ -34,6 +39,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import static net.jami.jams.server.Server.dataStore; +import static net.jami.jams.server.Server.nameServer; import static net.jami.jams.server.Server.userAuthenticationModule; @WebServlet("/api/auth/directory/search") @@ -42,45 +49,40 @@ public class SearchDirectoryServlet extends HttpServlet { //The search directory function does not automatically create users, this would be costly at this point //right now, we will implement it when Jami supports lists of users. this is a work in progress as it //requires changes on the name server as well. + /** - * @apiVersion 1.0.0 - * @api {get} /api/auth/directory/search Search profiles within a directory - * @apiName getDirectoryEntries - * @apiGroup Directory Search - * - * @apiParam {query} [queryString] the full-text query * - * @apiSuccess (200) {body} UserProfile[] json-representation of the user profiles - * @apiSuccessExample {json} Success-Response: - * [{ - * "username":"jdoe", - * "firstName":"John", - * "lastName":"Doe", - * "phoneNumber":"+1-514-000-0000", - * "phoneNumberExtension":"363", - * "mobileNumber":"+1-514-000-0000", - * "faxNumber":"+1-514-000-0000", - * "profilePicture":"base64_encoded_picture", - * "email":"jdoe@savoirfairelinux.com", - * "organization":"SFL" - * }] - * @apiError (500) {null} null could not return any authentication sources */ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { List<UserProfile> userProfiles = new ArrayList<>(); - userAuthenticationModule.getAuthSources().forEach( (k,v) ->{ - UserProfile[] profiles = v.getUserProfile(req.getParameter("queryString"),"FULL_TEXT_NAME"); - if(profiles != null && profiles.length != 0) userProfiles.addAll(Arrays.asList(profiles)); - }); - - // check logon names if nothing was found - if (userProfiles.isEmpty()) { - userAuthenticationModule.getAuthSources().forEach( (k,v) ->{ - UserProfile[] profiles = v.getUserProfile(req.getParameter("queryString"),"LOGON_NAME"); - if(profiles != null && profiles.length != 0) userProfiles.addAll(Arrays.asList(profiles)); + userAuthenticationModule.getAuthSources().forEach((k, v) -> { + List<UserProfile> profiles = v.getUserProfile(req.getParameter("queryString"), "FULL_TEXT_NAME"); + if(profiles == null || profiles.size() == 0){ + // check logon names if nothing was found + profiles = v.getUserProfile(req.getParameter("queryString"), "LOGON_NAME"); + } + profiles.parallelStream().forEach(profile -> { + StatementList statementList = new StatementList(); + StatementElement statementElement = new StatementElement("username", "=", profile.getUsername(), ""); + statementList.addStatement(statementElement); + List<User> results = new ArrayList<>(); + while (results.size() == 0) { + results = dataStore.getUserDao().getObjects(statementList); + if (results.size() == 0) { + User user = new User(); + user.setUsername(profile.getUsername()); + user.setRealm(k.getRealm()); + user.setUserType(k.getType()); + user.setAccessLevel(AccessLevel.USER); + userAuthenticationModule.createUser(user.getUserType(), user.getRealm(), nameServer, user); + } else { + profile.setJamiId(results.get(0)); + } + } + userProfiles.add(profile); }); - } + }); resp.getOutputStream().write(JsonStream.serialize(userProfiles).getBytes()); } } diff --git a/ldap-connector/src/main/java/net/jami/jams/ldap/connector/LDAPConnector.java b/ldap-connector/src/main/java/net/jami/jams/ldap/connector/LDAPConnector.java index 7669e7b28cb860e144214c2df936dc059ef9d440..bd6f4495705eb0cf35410cdba65fab6b77ee8159 100644 --- a/ldap-connector/src/main/java/net/jami/jams/ldap/connector/LDAPConnector.java +++ b/ldap-connector/src/main/java/net/jami/jams/ldap/connector/LDAPConnector.java @@ -38,6 +38,8 @@ import org.ldaptive.ConnectionConfig; import org.ldaptive.Credential; import org.ldaptive.DefaultConnectionFactory; +import java.util.List; + @Slf4j public class LDAPConnector implements AuthenticationSource { @@ -67,7 +69,7 @@ public class LDAPConnector implements AuthenticationSource { } @Override - public UserProfile[] getUserProfile(String queryString, String field) { + public List<UserProfile> getUserProfile(String queryString, String field) { return userProfileService.getUserProfile(queryString,field); } @@ -89,7 +91,7 @@ public class LDAPConnector implements AuthenticationSource { @Override public boolean test() { - return (getUserProfile("*","LOGON_NAME").length != 0); + return (getUserProfile("*","LOGON_NAME").size() != 0); } diff --git a/ldap-connector/src/main/java/net/jami/jams/ldap/connector/service/UserProfileService.java b/ldap-connector/src/main/java/net/jami/jams/ldap/connector/service/UserProfileService.java index 1fe4a9d5cb105a3ccd0297bf72f784b407ad4a29..711392288337fd748040c26ce3eedf35f8862cf8 100644 --- a/ldap-connector/src/main/java/net/jami/jams/ldap/connector/service/UserProfileService.java +++ b/ldap-connector/src/main/java/net/jami/jams/ldap/connector/service/UserProfileService.java @@ -34,8 +34,11 @@ import org.ldaptive.SearchResponse; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; @Slf4j public class UserProfileService { @@ -47,8 +50,7 @@ public class UserProfileService { this.connectionFactory = connectionFactory; } - public UserProfile[] getUserProfile(String queryString, String field){ - UserProfile[] profiles = null; + public List<UserProfile> getUserProfile(String queryString, String field){ Connection connection = null; try { queryString = queryString.replaceAll("[^\\x00-\\x7F]","*"); @@ -57,15 +59,8 @@ public class UserProfileService { connection.open(); SearchOperation search = new SearchOperation(connectionFactory); SearchResponse res = search.execute(buildRequest(queryString,field)); - if (res.getEntries().size() > 0) profiles = new UserProfile[res.getEntries().size()]; - else return null; - Iterator<LdapEntry> iterator = res.getEntries().iterator(); - int i = 0; - while(iterator.hasNext()){ - profiles[i] = profileFromResponse(iterator.next()); - i++; - } - return profiles; + if (res.getEntries().size() == 0) return null; + return res.getEntries().stream().map(UserProfileService::profileFromResponse).collect(Collectors.toList()); } catch (Exception e) { log.error("Could not search LDAP directory with error " + e.toString()); return null; diff --git a/ldap-connector/src/test/java/tests/GenericLDAPTest.java b/ldap-connector/src/test/java/tests/GenericLDAPTest.java index 1b194abdbdd3a8071a1897cd12aa2cf4993d4865..1525e1bb4681db67f307509db9667ee83ee167b9 100644 --- a/ldap-connector/src/test/java/tests/GenericLDAPTest.java +++ b/ldap-connector/src/test/java/tests/GenericLDAPTest.java @@ -10,6 +10,7 @@ import org.zapodot.junit.ldap.EmbeddedLdapRule; import org.zapodot.junit.ldap.EmbeddedLdapRuleBuilder; import java.io.InputStream; +import java.util.List; public class GenericLDAPTest { @@ -33,10 +34,10 @@ public class GenericLDAPTest { @Test public void testLookUp() throws Exception{ initLdapConnector(); - UserProfile[] profiles = ldapConnector.getUserProfile("*","FULL_TEXT_NAME"); - Assertions.assertEquals(2,profiles.length); - Assertions.assertNotNull(profiles[0].getFirstName()); - Assertions.assertNotNull(profiles[1].getFirstName()); + List<UserProfile> profiles = ldapConnector.getUserProfile("*","FULL_TEXT_NAME"); + Assertions.assertEquals(2,profiles.size()); + Assertions.assertNotNull(profiles.get(0).getFirstName()); + Assertions.assertNotNull(profiles.get(1).getFirstName()); } @Test @@ -52,10 +53,10 @@ public class GenericLDAPTest { @Test public void getVcard() throws Exception{ initLdapConnector(); - UserProfile[] profiles = ldapConnector.getUserProfile("Felix","FULL_TEXT_NAME"); - Assert.assertEquals(1,profiles.length); - Assert.assertNotNull(profiles[0].getUsername()); - String vcard = profiles[0].getAsVCard(); + List<UserProfile> profiles = ldapConnector.getUserProfile("Felix","FULL_TEXT_NAME"); + Assert.assertEquals(1,profiles.size()); + Assert.assertNotNull(profiles.get(0).getUsername()); + String vcard = profiles.get(0).getAsVCard(); Assert.assertNotNull(vcard); }