From 404044342a9fc9891366fa4378b4b1330cbca6c3 Mon Sep 17 00:00:00 2001 From: Abyss777 Date: Wed, 8 Nov 2017 15:43:13 +0500 Subject: - Implement synchronous geocoding - Implement retry geocoding for trips/stops reports - Implement API for revers geocoding --- src/org/traccar/Context.java | 71 ++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 33 deletions(-) (limited to 'src/org/traccar/Context.java') diff --git a/src/org/traccar/Context.java b/src/org/traccar/Context.java index 3b24c6460..0671f1d29 100644 --- a/src/org/traccar/Context.java +++ b/src/org/traccar/Context.java @@ -43,6 +43,7 @@ import org.traccar.database.StatisticsManager; import org.traccar.database.UsersManager; import org.traccar.events.MotionEventHandler; import org.traccar.events.OverspeedEventHandler; +import org.traccar.geocoder.AddressFormat; import org.traccar.geocoder.BingMapsGeocoder; import org.traccar.geocoder.FactualGeocoder; import org.traccar.geocoder.GeocodeFarmGeocoder; @@ -263,6 +264,41 @@ public final class Context { config.getDouble("event.motion.speedThreshold", 0.01)); } + public static Geocoder initGeocoder() { + String type = config.getString("geocoder.type", "google"); + String url = config.getString("geocoder.url"); + String key = config.getString("geocoder.key"); + String language = config.getString("geocoder.language"); + + String formatString = config.getString("geocoder.format"); + AddressFormat addressFormat; + if (formatString != null) { + addressFormat = new AddressFormat(formatString); + } else { + addressFormat = new AddressFormat(); + } + + int cacheSize = config.getInteger("geocoder.cacheSize"); + switch (type) { + case "nominatim": + return new NominatimGeocoder(url, key, language, cacheSize, addressFormat); + case "gisgraphy": + return new GisgraphyGeocoder(url, cacheSize, addressFormat); + case "mapquest": + return new MapQuestGeocoder(url, key, cacheSize, addressFormat); + case "opencage": + return new OpenCageGeocoder(url, key, cacheSize, addressFormat); + case "bingmaps": + return new BingMapsGeocoder(url, key, cacheSize, addressFormat); + case "factual": + return new FactualGeocoder(url, key, cacheSize, addressFormat); + case "geocodefarm": + return new GeocodeFarmGeocoder(key, language, cacheSize, addressFormat); + default: + return new GoogleGeocoder(key, language, cacheSize, addressFormat); + } + } + public static void init(String[] arguments) throws Exception { config = new Config(); @@ -297,39 +333,8 @@ public final class Context { identityManager = deviceManager; - if (config.getBoolean("geocoder.enable")) { - String type = config.getString("geocoder.type", "google"); - String url = config.getString("geocoder.url"); - String key = config.getString("geocoder.key"); - String language = config.getString("geocoder.language"); - - int cacheSize = config.getInteger("geocoder.cacheSize"); - switch (type) { - case "nominatim": - geocoder = new NominatimGeocoder(url, key, language, cacheSize); - break; - case "gisgraphy": - geocoder = new GisgraphyGeocoder(url, cacheSize); - break; - case "mapquest": - geocoder = new MapQuestGeocoder(url, key, cacheSize); - break; - case "opencage": - geocoder = new OpenCageGeocoder(url, key, cacheSize); - break; - case "bingmaps": - geocoder = new BingMapsGeocoder(url, key, cacheSize); - break; - case "factual": - geocoder = new FactualGeocoder(url, key, cacheSize); - break; - case "geocodefarm": - geocoder = new GeocodeFarmGeocoder(key, language, cacheSize); - break; - default: - geocoder = new GoogleGeocoder(key, language, cacheSize); - break; - } + if (config.getBoolean("geocoder.enable") || config.getBoolean("report.retryGeocoding")) { + geocoder = initGeocoder(); } if (config.getBoolean("geolocation.enable")) { -- cgit v1.2.3 From 96d89e2e352f5cba70a346b1f71bfb3bd399556b Mon Sep 17 00:00:00 2001 From: Abyss777 Date: Fri, 10 Nov 2017 09:56:30 +0500 Subject: - Combine "getAddress" functions - Change switches logic --- src/org/traccar/BasePipelineFactory.java | 2 +- src/org/traccar/Context.java | 2 +- src/org/traccar/api/resource/ServerResource.java | 4 +- src/org/traccar/geocoder/Geocoder.java | 4 +- src/org/traccar/geocoder/JsonGeocoder.java | 95 +++++++++++------------- src/org/traccar/reports/ReportUtils.java | 15 ++-- test/org/traccar/geocoder/GeocoderTest.java | 26 ++++--- 7 files changed, 74 insertions(+), 74 deletions(-) (limited to 'src/org/traccar/Context.java') diff --git a/src/org/traccar/BasePipelineFactory.java b/src/org/traccar/BasePipelineFactory.java index 27b9678a3..aa188d209 100644 --- a/src/org/traccar/BasePipelineFactory.java +++ b/src/org/traccar/BasePipelineFactory.java @@ -141,7 +141,7 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory { filterHandler = new FilterHandler(); } - if (Context.getConfig().getBoolean("geocoder.enable")) { + if (Context.getGeocoder() != null && !Context.getConfig().getBoolean("geocoder.ignorePositions")) { geocoderHandler = new GeocoderHandler( Context.getGeocoder(), Context.getConfig().getBoolean("geocoder.processInvalidPositions")); diff --git a/src/org/traccar/Context.java b/src/org/traccar/Context.java index 0671f1d29..237da40c8 100644 --- a/src/org/traccar/Context.java +++ b/src/org/traccar/Context.java @@ -333,7 +333,7 @@ public final class Context { identityManager = deviceManager; - if (config.getBoolean("geocoder.enable") || config.getBoolean("report.retryGeocoding")) { + if (config.getBoolean("geocoder.enable")) { geocoder = initGeocoder(); } diff --git a/src/org/traccar/api/resource/ServerResource.java b/src/org/traccar/api/resource/ServerResource.java index b803c3380..c0914995b 100644 --- a/src/org/traccar/api/resource/ServerResource.java +++ b/src/org/traccar/api/resource/ServerResource.java @@ -48,10 +48,10 @@ public class ServerResource extends BaseResource { return Response.ok(entity).build(); } - @Path("geocoding") + @Path("geocode") @GET public String geocode(@QueryParam("latitude") double latitude, @QueryParam("longitude") double longitude) { - return Context.getGeocoder().getAddress(latitude, longitude); + return Context.getGeocoder().getAddress(latitude, longitude, null); } } diff --git a/src/org/traccar/geocoder/Geocoder.java b/src/org/traccar/geocoder/Geocoder.java index c3fa394c7..587a27520 100644 --- a/src/org/traccar/geocoder/Geocoder.java +++ b/src/org/traccar/geocoder/Geocoder.java @@ -25,8 +25,6 @@ public interface Geocoder { } - void getAddress(double latitude, double longitude, ReverseGeocoderCallback callback); - - String getAddress(double latitude, double longitude); + String getAddress(double latitude, double longitude, ReverseGeocoderCallback callback); } diff --git a/src/org/traccar/geocoder/JsonGeocoder.java b/src/org/traccar/geocoder/JsonGeocoder.java index 2ae1fe5aa..82a6ee604 100644 --- a/src/org/traccar/geocoder/JsonGeocoder.java +++ b/src/org/traccar/geocoder/JsonGeocoder.java @@ -28,7 +28,6 @@ import java.io.IOException; import java.util.AbstractMap; import java.util.Collections; import java.util.LinkedHashMap; -import java.util.Locale; import java.util.Map; import java.util.concurrent.ExecutionException; @@ -52,70 +51,64 @@ public abstract class JsonGeocoder implements Geocoder { } } - @Override - public void getAddress( - final double latitude, final double longitude, final ReverseGeocoderCallback callback) { - - if (cache != null) { - String cachedAddress = cache.get(new AbstractMap.SimpleImmutableEntry<>(latitude, longitude)); - if (cachedAddress != null) { - callback.onSuccess(cachedAddress); - return; - } - } - - Context.getAsyncHttpClient().prepareGet(String.format(Locale.US, url, latitude, longitude)) - .execute(new AsyncCompletionHandler() { - @Override - public Object onCompleted(Response response) throws Exception { - try (JsonReader reader = Json.createReader(response.getResponseBodyAsStream())) { - Address address = parseAddress(reader.readObject()); - if (address != null) { - String formattedAddress = addressFormat.format(address); - if (cache != null) { - cache.put(new AbstractMap.SimpleImmutableEntry<>(latitude, longitude), formattedAddress); - } - callback.onSuccess(formattedAddress); - } else { - callback.onFailure(new GeocoderException("Empty address")); - } + private String handleResponse(double latitude, double longitude, Response response, + ReverseGeocoderCallback callback) throws IOException { + try (JsonReader reader = Json.createReader(response.getResponseBodyAsStream())) { + Address address = parseAddress(reader.readObject()); + if (address != null) { + String formattedAddress = addressFormat.format(address); + if (cache != null) { + cache.put(new AbstractMap.SimpleImmutableEntry<>(latitude, longitude), formattedAddress); } - return null; - } - - @Override - public void onThrowable(Throwable t) { - callback.onFailure(t); + if (callback != null) { + callback.onSuccess(formattedAddress); + } + return formattedAddress; + } else { + if (callback != null) { + callback.onFailure(new GeocoderException("Empty address")); + } + Log.warning("Empty address"); } - }); + } + return null; } @Override - public String getAddress(final double latitude, final double longitude) { + public String getAddress( + final double latitude, final double longitude, final ReverseGeocoderCallback callback) { + if (cache != null) { String cachedAddress = cache.get(new AbstractMap.SimpleImmutableEntry<>(latitude, longitude)); if (cachedAddress != null) { + if (callback != null) { + callback.onSuccess(cachedAddress); + } return cachedAddress; } } - try { - Response response = Context.getAsyncHttpClient() - .prepareGet(String.format(Locale.US, url, latitude, longitude)).execute().get(); - try (JsonReader reader = Json.createReader(response.getResponseBodyAsStream())) { - Address address = parseAddress(reader.readObject()); - if (address != null) { - String formattedAddress = addressFormat.format(address); - if (cache != null) { - cache.put(new AbstractMap.SimpleImmutableEntry<>(latitude, longitude), formattedAddress); - } - return formattedAddress; - } else { - Log.warning("Empty address"); + if (callback != null) { + Context.getAsyncHttpClient().prepareGet(String.format(url, latitude, longitude)) + .execute(new AsyncCompletionHandler() { + @Override + public Object onCompleted(Response response) throws Exception { + return handleResponse(latitude, longitude, response, callback); } + + @Override + public void onThrowable(Throwable t) { + callback.onFailure(t); + } + }); + } else { + try { + Response response = Context.getAsyncHttpClient() + .prepareGet(String.format(url, latitude, longitude)).execute().get(); + return handleResponse(latitude, longitude, response, null); + } catch (InterruptedException | ExecutionException | IOException error) { + Log.warning("Geocoding failed", error); } - } catch (InterruptedException | ExecutionException | IOException error) { - Log.warning("Geocoding failed", error); } return null; } diff --git a/src/org/traccar/reports/ReportUtils.java b/src/org/traccar/reports/ReportUtils.java index f0d6d40c2..e04f2f90c 100644 --- a/src/org/traccar/reports/ReportUtils.java +++ b/src/org/traccar/reports/ReportUtils.java @@ -191,8 +191,9 @@ public final class ReportUtils { trip.setStartLon(startTrip.getLongitude()); trip.setStartTime(startTrip.getFixTime()); String startAddress = startTrip.getAddress(); - if (startAddress == null && Context.getConfig().getBoolean("report.retryGeocoding")) { - startAddress = Context.getGeocoder().getAddress(startTrip.getLatitude(), startTrip.getLongitude()); + if (startAddress == null && Context.getGeocoder() != null + && Context.getConfig().getBoolean("geocoder.onRequest")) { + startAddress = Context.getGeocoder().getAddress(startTrip.getLatitude(), startTrip.getLongitude(), null); } trip.setStartAddress(startAddress); @@ -201,8 +202,9 @@ public final class ReportUtils { trip.setEndLon(endTrip.getLongitude()); trip.setEndTime(endTrip.getFixTime()); String endAddress = endTrip.getAddress(); - if (endAddress == null && Context.getConfig().getBoolean("report.retryGeocoding")) { - endAddress = Context.getGeocoder().getAddress(startTrip.getLatitude(), startTrip.getLongitude()); + if (endAddress == null && Context.getGeocoder() != null + && Context.getConfig().getBoolean("geocoder.onRequest")) { + endAddress = Context.getGeocoder().getAddress(endTrip.getLatitude(), endTrip.getLongitude(), null); } trip.setEndAddress(endAddress); @@ -233,8 +235,9 @@ public final class ReportUtils { stop.setLongitude(startStop.getLongitude()); stop.setStartTime(startStop.getFixTime()); String address = startStop.getAddress(); - if (address == null && Context.getConfig().getBoolean("report.retryGeocoding")) { - address = Context.getGeocoder().getAddress(stop.getLatitude(), stop.getLongitude()); + if (address == null && Context.getGeocoder() != null + && Context.getConfig().getBoolean("geocoder.onRequest")) { + address = Context.getGeocoder().getAddress(stop.getLatitude(), stop.getLongitude(), null); } stop.setAddress(address); diff --git a/test/org/traccar/geocoder/GeocoderTest.java b/test/org/traccar/geocoder/GeocoderTest.java index 84e2fb2d6..7c6208048 100644 --- a/test/org/traccar/geocoder/GeocoderTest.java +++ b/test/org/traccar/geocoder/GeocoderTest.java @@ -1,5 +1,7 @@ package org.traccar.geocoder; +import java.util.Locale; + import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; @@ -9,6 +11,7 @@ public class GeocoderTest { @Ignore @Test public void test() throws InterruptedException { + Locale.setDefault(Locale.US); testGoogle(); } @@ -38,12 +41,13 @@ public class GeocoderTest { } @Override - public void onFailure(Throwable e) { + public void onFailure(final Throwable e) { } }); Assert.assertEquals("1 Ibn Shaprut St, Jerusalem, Jerusalem District, IL", waitAddress()); - Assert.assertEquals("1 Ibn Shaprut St, Jerusalem, Jerusalem District, IL", geocoder.getAddress(31.776797, 35.211489)); + Assert.assertEquals("1 Ibn Shaprut St, Jerusalem, Jerusalem District, IL", + geocoder.getAddress(31.776797, 35.211489, null)); } public void testNominatim() throws InterruptedException { @@ -59,9 +63,10 @@ public class GeocoderTest { public void onFailure(Throwable e) { } }); - Assert.assertEquals("35 West 9th Street, NYC, New York, US", waitAddress()); + Assert.assertEquals("35 West 9th Street, NYC, New York, US", waitAddress()); - Assert.assertEquals("35 West 9th Street, NYC, New York, US", geocoder.getAddress(40.7337807, -73.9974401)); + Assert.assertEquals("35 West 9th Street, NYC, New York, US", + geocoder.getAddress(40.7337807, -73.9974401, null)); } public void testGisgraphy() throws InterruptedException { @@ -77,9 +82,9 @@ public class GeocoderTest { public void onFailure(Throwable e) { } }); - Assert.assertEquals("Rue du Jardinet, Paris, FR", waitAddress()); + Assert.assertEquals("Rue du Jardinet, Paris, FR", waitAddress()); - Assert.assertEquals("Rue du Jardinet, Paris, FR", geocoder.getAddress(48.8530000, 2.3400000)); + Assert.assertEquals("Rue du Jardinet, Paris, FR", geocoder.getAddress(48.8530000, 2.3400000, null)); } public void testOpenCage() throws InterruptedException { @@ -96,9 +101,9 @@ public class GeocoderTest { public void onFailure(Throwable e) { } }); - Assert.assertEquals("Charleston Road, California, US", waitAddress()); + Assert.assertEquals("Charleston Road, California, US", waitAddress()); - Assert.assertEquals("Charleston Road, California, US", geocoder.getAddress(34.116302, -118.051519)); + Assert.assertEquals("Charleston Road, California, US", geocoder.getAddress(34.116302, -118.051519, null)); } public void testGeocodeFarm() throws InterruptedException { @@ -114,9 +119,10 @@ public class GeocoderTest { public void onFailure(Throwable e) { } }); - Assert.assertEquals("Estrella Avenue, Arcadia, California, United States", waitAddress()); + Assert.assertEquals("Estrella Avenue, Arcadia, California, United States", waitAddress()); - Assert.assertEquals("Estrella Avenue, Arcadia, California, United States", geocoder.getAddress(34.116302, -118.051519)); + Assert.assertEquals("Estrella Avenue, Arcadia, California, United States", + geocoder.getAddress(34.116302, -118.051519, null)); } } -- cgit v1.2.3 From 1908351b442732acc375397648a6834f09c0d9c4 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 11 Nov 2017 18:40:40 +1300 Subject: Implement LDAP authentication --- debug.xml | 4 +++ schema/changelog-3.15.xml | 7 ++++ src/org/traccar/Context.java | 11 +++++++ src/org/traccar/database/DataManager.java | 19 +++++++++-- src/org/traccar/database/LdapProvider.java | 51 ++++++++++++++++++++++++++++++ src/org/traccar/model/User.java | 10 ++++++ 6 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 src/org/traccar/database/LdapProvider.java (limited to 'src/org/traccar/Context.java') diff --git a/debug.xml b/debug.xml index 282350b8a..e8647b6fe 100644 --- a/debug.xml +++ b/debug.xml @@ -21,4 +21,8 @@ 6037 + + diff --git a/schema/changelog-3.15.xml b/schema/changelog-3.15.xml index 9756fe696..202ae21eb 100644 --- a/schema/changelog-3.15.xml +++ b/schema/changelog-3.15.xml @@ -133,5 +133,12 @@ + + + + + + + diff --git a/src/org/traccar/Context.java b/src/org/traccar/Context.java index 237da40c8..9a48b2740 100644 --- a/src/org/traccar/Context.java +++ b/src/org/traccar/Context.java @@ -34,6 +34,7 @@ import org.traccar.database.DataManager; import org.traccar.database.DeviceManager; import org.traccar.database.DriversManager; import org.traccar.database.IdentityManager; +import org.traccar.database.LdapProvider; import org.traccar.database.MediaManager; import org.traccar.database.NotificationManager; import org.traccar.database.PermissionsManager; @@ -109,6 +110,12 @@ public final class Context { return dataManager; } + private static LdapProvider ldapProvider; + + public static LdapProvider getLdapProvider() { + return ldapProvider; + } + private static MediaManager mediaManager; public static MediaManager getMediaManager() { @@ -321,6 +328,10 @@ public final class Context { dataManager = new DataManager(config); } + if (config.getBoolean("ldap.enable")) { + ldapProvider = new LdapProvider(config.getString("ldap.url"), config.getString("ldap.context")); + } + if (config.hasKey("media.path")) { mediaManager = new MediaManager(config); } diff --git a/src/org/traccar/database/DataManager.java b/src/org/traccar/database/DataManager.java index e88ff7f0d..f71e3e538 100644 --- a/src/org/traccar/database/DataManager.java +++ b/src/org/traccar/database/DataManager.java @@ -40,6 +40,7 @@ import liquibase.resource.FileSystemResourceAccessor; import liquibase.resource.ResourceAccessor; import org.traccar.Config; +import org.traccar.Context; import org.traccar.helper.Log; import org.traccar.model.Attribute; import org.traccar.model.Device; @@ -300,11 +301,23 @@ public class DataManager { User user = QueryBuilder.create(dataSource, getQuery("database.loginUser")) .setString("email", email.trim()) .executeQuerySingle(User.class); - if (user != null && user.isPasswordValid(password)) { - return user; + LdapProvider ldapProvider = Context.getLdapProvider(); + if (user != null) { + if (ldapProvider != null && ldapProvider.login(user.getLogin(), password) + || user.isPasswordValid(password)) { + return user; + } } else { - return null; + if (ldapProvider != null && ldapProvider.login(email, password)) { + user = new User(); + user.setName(email); + user.setEmail(email); + user.setLogin(email); + Context.getUsersManager().addItem(user); + return user; + } } + return null; } public void updateDeviceStatus(Device device) throws SQLException { diff --git a/src/org/traccar/database/LdapProvider.java b/src/org/traccar/database/LdapProvider.java new file mode 100644 index 000000000..6a4c244f8 --- /dev/null +++ b/src/org/traccar/database/LdapProvider.java @@ -0,0 +1,51 @@ +/* + * Copyright 2017 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.database; + +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.directory.InitialDirContext; +import java.util.Hashtable; + +public class LdapProvider { + + private String url; + private String context; + + public LdapProvider(String url, String context) { + this.url = url; + this.context = context; + } + + public boolean login(String username, String password) { + + Hashtable env = new Hashtable<>(); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); + env.put(Context.PROVIDER_URL, url); + + env.put(Context.SECURITY_AUTHENTICATION, "simple"); + env.put(Context.SECURITY_PRINCIPAL, "uid=" + username + "," + context); + env.put(Context.SECURITY_CREDENTIALS, password); + + try { + new InitialDirContext(env).close(); + return true; + } catch (NamingException e) { + return false; + } + } + +} diff --git a/src/org/traccar/model/User.java b/src/org/traccar/model/User.java index 5d89dcfae..b1883213c 100644 --- a/src/org/traccar/model/User.java +++ b/src/org/traccar/model/User.java @@ -35,6 +35,16 @@ public class User extends ExtendedModel { this.name = name; } + private String login; + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + private String email; public String getEmail() { -- cgit v1.2.3 From e0f16f7f797a3ce1bfae3be2625690ad4b048240 Mon Sep 17 00:00:00 2001 From: Abyss777 Date: Tue, 14 Nov 2017 12:44:59 +0500 Subject: Improve LDAP --- setup/default.xml | 2 +- src/org/traccar/Context.java | 2 +- src/org/traccar/database/DataManager.java | 11 ++- src/org/traccar/database/LdapProvider.java | 143 +++++++++++++++++++++++++++-- 4 files changed, 142 insertions(+), 16 deletions(-) (limited to 'src/org/traccar/Context.java') diff --git a/setup/default.xml b/setup/default.xml index 6d8bd278f..bb51472c1 100644 --- a/setup/default.xml +++ b/setup/default.xml @@ -35,7 +35,7 @@ SELECT * FROM users - WHERE email = :email + WHERE email = :email OR login = :email diff --git a/src/org/traccar/Context.java b/src/org/traccar/Context.java index 9a48b2740..511ec980e 100644 --- a/src/org/traccar/Context.java +++ b/src/org/traccar/Context.java @@ -329,7 +329,7 @@ public final class Context { } if (config.getBoolean("ldap.enable")) { - ldapProvider = new LdapProvider(config.getString("ldap.url"), config.getString("ldap.context")); + ldapProvider = new LdapProvider(config); } if (config.hasKey("media.path")) { diff --git a/src/org/traccar/database/DataManager.java b/src/org/traccar/database/DataManager.java index f71e3e538..abc424ff9 100644 --- a/src/org/traccar/database/DataManager.java +++ b/src/org/traccar/database/DataManager.java @@ -76,9 +76,13 @@ public class DataManager { private boolean generateQueries; + private boolean forceLdap; + public DataManager(Config config) throws Exception { this.config = config; + forceLdap = config.getBoolean("ldap.force"); + initDatabase(); initDatabaseSchema(); } @@ -304,15 +308,12 @@ public class DataManager { LdapProvider ldapProvider = Context.getLdapProvider(); if (user != null) { if (ldapProvider != null && ldapProvider.login(user.getLogin(), password) - || user.isPasswordValid(password)) { + || !forceLdap && user.isPasswordValid(password)) { return user; } } else { if (ldapProvider != null && ldapProvider.login(email, password)) { - user = new User(); - user.setName(email); - user.setEmail(email); - user.setLogin(email); + user = ldapProvider.getUser(email); Context.getUsersManager().addItem(user); return user; } diff --git a/src/org/traccar/database/LdapProvider.java b/src/org/traccar/database/LdapProvider.java index 6a4c244f8..b193b87c1 100644 --- a/src/org/traccar/database/LdapProvider.java +++ b/src/org/traccar/database/LdapProvider.java @@ -16,36 +16,161 @@ package org.traccar.database; import javax.naming.Context; +import javax.naming.NamingEnumeration; import javax.naming.NamingException; +import javax.naming.directory.Attribute; import javax.naming.directory.InitialDirContext; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; + +import org.traccar.Config; +import org.traccar.helper.Log; +import org.traccar.model.User; + import java.util.Hashtable; public class LdapProvider { private String url; - private String context; + private String searchBase; + private String idAttribute; + private String nameAttribute; + private String mailAttribute; + private String searchFilter; + private String adminFilter; + private String serviceUser; + private String servicePassword; - public LdapProvider(String url, String context) { - this.url = url; - this.context = context; + public LdapProvider(Config config) { + String url = config.getString("ldap.url"); + if (url != null) { + this.url = url; + } else { + this.url = "ldap://" + config.getString("ldap.server") + ":" + config.getInteger("ldap.port", 389); + } + this.searchBase = config.getString("ldap.base"); + this.idAttribute = config.getString("ldap.idAttribute", "uid"); + this.nameAttribute = config.getString("ldap.nameAttribute", "cn"); + this.mailAttribute = config.getString("ldap.mailAttribute", "mail"); + this.searchFilter = config.getString("ldap.searchFilter", "(" + idAttribute + "=:login)"); + String adminGroup = config.getString("ldap.adminGroup"); + this.adminFilter = config.getString("ldap.adminFilter"); + if (this.adminFilter == null && adminGroup != null) { + this.adminFilter = "(&(" + idAttribute + "=:login)(memberOf=" + adminGroup + "))"; + } + this.serviceUser = config.getString("ldap.user"); + this.servicePassword = config.getString("ldap.password"); } - public boolean login(String username, String password) { - + private InitialDirContext auth(String accountName, String password) throws NamingException { Hashtable env = new Hashtable<>(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.PROVIDER_URL, url); env.put(Context.SECURITY_AUTHENTICATION, "simple"); - env.put(Context.SECURITY_PRINCIPAL, "uid=" + username + "," + context); + env.put(Context.SECURITY_PRINCIPAL, accountName); env.put(Context.SECURITY_CREDENTIALS, password); + return new InitialDirContext(env); + } + + private boolean isAdmin(String accountName) { + if (this.adminFilter != null) { + try { + InitialDirContext context = initContext(); + String searchString = new String(adminFilter).replace(":login", accountName); + SearchControls searchControls = new SearchControls(); + searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); + NamingEnumeration results = context.search(searchBase, searchString, searchControls); + if (results.hasMoreElements()) { + results.nextElement(); + if (results.hasMoreElements()) { + Log.warning("Matched multiple users for the accountName: " + accountName); + return false; + } + return true; + } + } catch (NamingException e) { + return false; + } + } + return false; + } + + public InitialDirContext initContext() throws NamingException { + return auth(serviceUser, servicePassword); + } + + private SearchResult lookupUser(String accountName) throws NamingException { + InitialDirContext context = initContext(); + + String searchString = new String(searchFilter).replace(":login", accountName); + + SearchControls searchControls = new SearchControls(); + String[] attributeFilter = {idAttribute, nameAttribute, mailAttribute}; + searchControls.setReturningAttributes(attributeFilter); + searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); + + NamingEnumeration results = context.search(searchBase, searchString, searchControls); + + SearchResult searchResult = null; + if (results.hasMoreElements()) { + searchResult = (SearchResult) results.nextElement(); + if (results.hasMoreElements()) { + Log.warning("Matched multiple users for the accountName: " + accountName); + return null; + } + } + + return searchResult; + } + + public User getUser(String accountName) { + SearchResult ldapUser; + User user = new User(); + try { + ldapUser = lookupUser(accountName); + if (ldapUser != null) { + Attribute attribute = ldapUser.getAttributes().get(idAttribute); + if (attribute != null) { + user.setLogin((String) attribute.get()); + } else { + user.setLogin(accountName); + } + attribute = ldapUser.getAttributes().get(nameAttribute); + if (attribute != null) { + user.setName((String) attribute.get()); + } else { + user.setName(accountName); + } + attribute = ldapUser.getAttributes().get(mailAttribute); + if (attribute != null) { + user.setEmail((String) attribute.get()); + } else { + user.setEmail(accountName); + } + } + user.setAdmin(isAdmin(accountName)); + } catch (NamingException e) { + user.setLogin(accountName); + user.setName(accountName); + user.setEmail(accountName); + Log.warning(e); + } + return user; + } + + public boolean login(String username, String password) { try { - new InitialDirContext(env).close(); - return true; + SearchResult ldapUser = lookupUser(username); + if (ldapUser != null) { + auth(ldapUser.getNameInNamespace(), password).close(); + return true; + } } catch (NamingException e) { return false; } + return false; } } -- cgit v1.2.3