From 9ab4a6e303c0e8a4997252b4c6a8b2dd601d73af Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 5 Apr 2023 17:40:11 +0100 Subject: Implement OpenID auto discovery --- src/main/java/org/traccar/config/Keys.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'src/main/java/org/traccar/config') diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java index 3ff423ad1..3ed6c6026 100644 --- a/src/main/java/org/traccar/config/Keys.java +++ b/src/main/java/org/traccar/config/Keys.java @@ -636,11 +636,20 @@ public final class Keys { "openid.clientSecret", List.of(KeyType.CONFIG)); + /** + * OpenID Connect Issuer (Base) URL. + * This is used to automatically configure the authorization, token and user info URLs if + * they are not provided. + */ + public static final ConfigKey OPENID_ISSUERURL = new StringConfigKey( + "openid.issuerUrl", + List.of(KeyType.CONFIG)); + /** * OpenID Connect Authorization URL. * This can usually be found in the documentation of your identity provider or by using the well-known * configuration endpoint, eg. https://auth.example.com//.well-known/openid-configuration - * Required to enable SSO. + * Required to enable SSO if openid.issuerUrl is not set. */ public static final ConfigKey OPENID_AUTHURL = new StringConfigKey( "openid.authUrl", @@ -648,7 +657,7 @@ public final class Keys { /** * OpenID Connect Token URL. * This can be found in the same ways at openid.authUrl. - * Required to enable SSO. + * Required to enable SSO if openid.issuerUrl is not set. */ public static final ConfigKey OPENID_TOKENURL = new StringConfigKey( "openid.tokenUrl", @@ -657,7 +666,7 @@ public final class Keys { /** * OpenID Connect User Info URL. * This can be found in the same ways at openid.authUrl. - * Required to enable SSO. + * Required to enable SSO if openid.issuerUrl is not set. */ public static final ConfigKey OPENID_USERINFOURL = new StringConfigKey( "openid.userInfoUrl", -- cgit v1.2.3 From c56b136a328bc1781ccc74aa27fdecf4a17b9595 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 5 Apr 2023 17:59:04 +0100 Subject: Added openid.allowGroup --- src/main/java/org/traccar/config/Keys.java | 9 +++++++++ src/main/java/org/traccar/database/OpenIdProvider.java | 10 +++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'src/main/java/org/traccar/config') diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java index 3ed6c6026..363d4a472 100644 --- a/src/main/java/org/traccar/config/Keys.java +++ b/src/main/java/org/traccar/config/Keys.java @@ -672,6 +672,15 @@ public final class Keys { "openid.userInfoUrl", List.of(KeyType.CONFIG)); + /** + * OpenID Connect group to restrict access to. + * If this is not provided, all OpenID users will have access to Traccar. + * This option will only work if your OpenID provider supports the groups scope. + */ + public static final ConfigKey OPENID_ALLOWGROUP = new StringConfigKey( + "openid.allowGroup", + List.of(KeyType.CONFIG)); + /** * OpenID Connect group to grant admin access. * If this is not provided, no groups will be granted admin access. diff --git a/src/main/java/org/traccar/database/OpenIdProvider.java b/src/main/java/org/traccar/database/OpenIdProvider.java index 2b0f9d290..370876ed9 100644 --- a/src/main/java/org/traccar/database/OpenIdProvider.java +++ b/src/main/java/org/traccar/database/OpenIdProvider.java @@ -30,6 +30,7 @@ import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse.BodyHandlers; import java.security.GeneralSecurityException; +import java.util.List; import java.util.Map; import java.io.IOException; import javax.servlet.http.HttpServletRequest; @@ -76,6 +77,7 @@ public class OpenIdProvider { private URI userInfoUrl; private URI baseUrl; private final String adminGroup; + private final String allowGroup; private LoginService loginService; @@ -129,6 +131,7 @@ public class OpenIdProvider { } adminGroup = config.getString(Keys.OPENID_ADMINGROUP); + allowGroup = config.getString(Keys.OPENID_ALLOWGROUP); } public URI createAuthUri() { @@ -200,7 +203,12 @@ public class OpenIdProvider { UserInfo userInfo = getUserInfo(bearerToken); - Boolean administrator = adminGroup != null && userInfo.getStringListClaim("groups").contains(adminGroup); + List userGroups = userInfo.getStringListClaim("groups"); + Boolean administrator = adminGroup != null && userGroups.contains(adminGroup); + + if (!(administrator || allowGroup == null || userGroups.contains(allowGroup))) { + throw new GeneralSecurityException("Your OpenID Groups do not permit access to Traccar."); + } User user = loginService.login(userInfo.getEmailAddress(), userInfo.getName(), administrator); -- cgit v1.2.3 From 5ba5cc8291d0bd5d497563ddebf08dfc6d239ee9 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 5 Apr 2023 20:40:09 +0100 Subject: Review changes --- src/main/java/org/traccar/MainModule.java | 12 +++-- src/main/java/org/traccar/config/Keys.java | 16 +++--- .../java/org/traccar/database/OpenIdProvider.java | 62 +++++++++------------- 3 files changed, 41 insertions(+), 49 deletions(-) (limited to 'src/main/java/org/traccar/config') diff --git a/src/main/java/org/traccar/MainModule.java b/src/main/java/org/traccar/MainModule.java index 51097511a..220798767 100644 --- a/src/main/java/org/traccar/MainModule.java +++ b/src/main/java/org/traccar/MainModule.java @@ -97,6 +97,7 @@ import javax.ws.rs.client.ClientBuilder; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; +import java.net.http.HttpClient; import java.util.Properties; public class MainModule extends AbstractModule { @@ -174,11 +175,12 @@ public class MainModule extends AbstractModule { @Singleton @Provides - public static OpenIdProvider provideOpenIDProvider(Config config, LoginService loginService) { - if (config.hasKey(Keys.OPENID_CLIENTID)) { - return new OpenIdProvider(config, loginService); - } - return null; + public static OpenIdProvider provideOpenIDProvider( + Config config, LoginService loginService, ObjectMapper objectMapper) throws InterruptedException, IOException { + if (config.hasKey(Keys.OPENID_CLIENT_ID)) { + return new OpenIdProvider(config, loginService, HttpClient.newHttpClient(), objectMapper); + } + return null; } @Provides diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java index 363d4a472..6c46ef390 100644 --- a/src/main/java/org/traccar/config/Keys.java +++ b/src/main/java/org/traccar/config/Keys.java @@ -623,7 +623,7 @@ public final class Keys { * This is a unique ID assigned to each application you register with your identity provider. * Required to enable SSO. */ - public static final ConfigKey OPENID_CLIENTID = new StringConfigKey( + public static final ConfigKey OPENID_CLIENT_ID = new StringConfigKey( "openid.clientId", List.of(KeyType.CONFIG)); @@ -632,7 +632,7 @@ public final class Keys { * This is a secret assigned to each application you register with your identity provider. * Required to enable SSO. */ - public static final ConfigKey OPENID_CLIENTSECRET = new StringConfigKey( + public static final ConfigKey OPENID_CLIENT_SECRET = new StringConfigKey( "openid.clientSecret", List.of(KeyType.CONFIG)); @@ -641,7 +641,7 @@ public final class Keys { * This is used to automatically configure the authorization, token and user info URLs if * they are not provided. */ - public static final ConfigKey OPENID_ISSUERURL = new StringConfigKey( + public static final ConfigKey OPENID_ISSUER_URL = new StringConfigKey( "openid.issuerUrl", List.of(KeyType.CONFIG)); @@ -651,7 +651,7 @@ public final class Keys { * configuration endpoint, eg. https://auth.example.com//.well-known/openid-configuration * Required to enable SSO if openid.issuerUrl is not set. */ - public static final ConfigKey OPENID_AUTHURL = new StringConfigKey( + public static final ConfigKey OPENID_AUTH_URL = new StringConfigKey( "openid.authUrl", List.of(KeyType.CONFIG)); /** @@ -659,7 +659,7 @@ public final class Keys { * This can be found in the same ways at openid.authUrl. * Required to enable SSO if openid.issuerUrl is not set. */ - public static final ConfigKey OPENID_TOKENURL = new StringConfigKey( + public static final ConfigKey OPENID_TOKEN_URL = new StringConfigKey( "openid.tokenUrl", List.of(KeyType.CONFIG)); @@ -668,7 +668,7 @@ public final class Keys { * This can be found in the same ways at openid.authUrl. * Required to enable SSO if openid.issuerUrl is not set. */ - public static final ConfigKey OPENID_USERINFOURL = new StringConfigKey( + public static final ConfigKey OPENID_USERINFO_URL = new StringConfigKey( "openid.userInfoUrl", List.of(KeyType.CONFIG)); @@ -677,7 +677,7 @@ public final class Keys { * If this is not provided, all OpenID users will have access to Traccar. * This option will only work if your OpenID provider supports the groups scope. */ - public static final ConfigKey OPENID_ALLOWGROUP = new StringConfigKey( + public static final ConfigKey OPENID_ALLOW_GROUP = new StringConfigKey( "openid.allowGroup", List.of(KeyType.CONFIG)); @@ -686,7 +686,7 @@ public final class Keys { * If this is not provided, no groups will be granted admin access. * This option will only work if your OpenID provider supports the groups scope. */ - public static final ConfigKey OPENID_ADMINGROUP = new StringConfigKey( + public static final ConfigKey OPENID_ADMIN_GROUP = new StringConfigKey( "openid.adminGroup", List.of(KeyType.CONFIG)); diff --git a/src/main/java/org/traccar/database/OpenIdProvider.java b/src/main/java/org/traccar/database/OpenIdProvider.java index 370876ed9..8b93feea7 100644 --- a/src/main/java/org/traccar/database/OpenIdProvider.java +++ b/src/main/java/org/traccar/database/OpenIdProvider.java @@ -82,56 +82,46 @@ public class OpenIdProvider { private LoginService loginService; @Inject - public OpenIdProvider(Config config, LoginService loginService) { - this.loginService = loginService; - - force = config.getBoolean(Keys.OPENID_FORCE); - clientId = new ClientID(config.getString(Keys.OPENID_CLIENTID)); - clientAuth = new ClientSecretBasic(clientId, new Secret(config.getString(Keys.OPENID_CLIENTSECRET))); - - try { - callbackUrl = new URI(config.getString(Keys.WEB_URL, "") + "/api/session/openid/callback"); - baseUrl = new URI(config.getString(Keys.WEB_URL, "")); - - if ( - config.hasKey(Keys.OPENID_ISSUERURL) - && ( - !config.hasKey(Keys.OPENID_AUTHURL) - || !config.hasKey(Keys.OPENID_TOKENURL) - || !config.hasKey(Keys.OPENID_USERINFOURL)) - ) { - HttpClient httpClient = HttpClient.newHttpClient(); + public OpenIdProvider( + Config config, LoginService loginService, HttpClient httpClient, ObjectMapper objectMapper + ) throws InterruptedException, IOException { + this.loginService = loginService; + force = config.getBoolean(Keys.OPENID_FORCE); + clientId = new ClientID(config.getString(Keys.OPENID_CLIENT_ID)); + clientAuth = new ClientSecretBasic(clientId, new Secret(config.getString(Keys.OPENID_CLIENT_SECRET))); + + try { + callbackUrl = new URI(config.getString(Keys.WEB_URL, "") + "/api/session/openid/callback"); + baseUrl = new URI(config.getString(Keys.WEB_URL, "")); + + if (config.hasKey(Keys.OPENID_ISSUER_URL)) { HttpRequest httpRequest = HttpRequest.newBuilder( - URI.create( - config.getString(Keys.OPENID_ISSUERURL) + "/.well-known/openid-configuration") - ) - .header("accept", "application/json") + URI.create(config.getString(Keys.OPENID_ISSUER_URL) + "/.well-known/openid-configuration")) + .header("Accept", "application/json") .build(); String httpResponse = httpClient.send(httpRequest, BodyHandlers.ofString()).body(); - Map discoveryMap = new ObjectMapper().readValue( + Map discoveryMap = objectMapper.readValue( httpResponse, new TypeReference>() { }); - authUrl = new URI(discoveryMap.get("authorization_endpoint").toString()); - tokenUrl = new URI(discoveryMap.get("token_endpoint").toString()); - userInfoUrl = new URI(discoveryMap.get("userinfo_endpoint").toString()); + authUrl = new URI((String) discoveryMap.get("authorization_endpoint")); + tokenUrl = new URI((String) discoveryMap.get("token_endpoint")); + userInfoUrl = new URI((String) discoveryMap.get("userinfo_endpoint")); LOGGER.info("OpenID Connect auto discovery successful"); - } else { - authUrl = new URI(config.getString(Keys.OPENID_AUTHURL)); - tokenUrl = new URI(config.getString(Keys.OPENID_TOKENURL)); - userInfoUrl = new URI(config.getString(Keys.OPENID_USERINFOURL)); - } + } else { + authUrl = new URI(config.getString(Keys.OPENID_AUTH_URL)); + tokenUrl = new URI(config.getString(Keys.OPENID_TOKEN_URL)); + userInfoUrl = new URI(config.getString(Keys.OPENID_USERINFO_URL)); + } } catch (URISyntaxException error) { LOGGER.error("Invalid URIs provided in OpenID configuration"); - } catch (InterruptedException | IOException error) { - LOGGER.error("OpenID Connect auto discovery failed"); } - adminGroup = config.getString(Keys.OPENID_ADMINGROUP); - allowGroup = config.getString(Keys.OPENID_ALLOWGROUP); + adminGroup = config.getString(Keys.OPENID_ADMIN_GROUP); + allowGroup = config.getString(Keys.OPENID_ALLOW_GROUP); } public URI createAuthUri() { -- cgit v1.2.3 From 1ecd7efca41344f29f8caa42fc6caaed8c7294a1 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 5 Apr 2023 20:49:10 +0100 Subject: Config doc change --- src/main/java/org/traccar/config/Keys.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/main/java/org/traccar/config') diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java index 6c46ef390..b97acfd66 100644 --- a/src/main/java/org/traccar/config/Keys.java +++ b/src/main/java/org/traccar/config/Keys.java @@ -638,8 +638,7 @@ public final class Keys { /** * OpenID Connect Issuer (Base) URL. - * This is used to automatically configure the authorization, token and user info URLs if - * they are not provided. + * This is used to automatically configure the authorization, token and user info URLs if provided. */ public static final ConfigKey OPENID_ISSUER_URL = new StringConfigKey( "openid.issuerUrl", -- cgit v1.2.3