From 3c1925f90ab0ed624804e0f80b689d7062796a6e Mon Sep 17 00:00:00 2001 From: Hiroyuki Wada Date: Sun, 4 Nov 2018 02:47:03 +0900 Subject: [PATCH] Add guild checking when federation --- .../discord/DiscordIdentityProvider.java | 51 ++++++++++++++--- .../DiscordIdentityProviderConfig.java | 57 +++++++++++++++++++ .../DiscordIdentityProviderFactory.java | 7 ++- .../messages/admin-messages_en.properties | 4 +- .../realm-identity-provider-discord-ext.html | 7 +++ .../realm-identity-provider-discord.html | 1 + 6 files changed, 115 insertions(+), 12 deletions(-) create mode 100755 ejb/src/main/java/org/keycloak/social/discord/DiscordIdentityProviderConfig.java create mode 100755 ejb/src/main/resources/theme/discord/admin/resources/partials/realm-identity-provider-discord-ext.html diff --git a/ejb/src/main/java/org/keycloak/social/discord/DiscordIdentityProvider.java b/ejb/src/main/java/org/keycloak/social/discord/DiscordIdentityProvider.java index c35c97a..601de35 100755 --- a/ejb/src/main/java/org/keycloak/social/discord/DiscordIdentityProvider.java +++ b/ejb/src/main/java/org/keycloak/social/discord/DiscordIdentityProvider.java @@ -17,9 +17,14 @@ package org.keycloak.social.discord; +import java.io.IOException; +import java.util.Set; +import java.util.stream.Stream; + +import javax.ws.rs.core.Response; + import org.jboss.logging.Logger; import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider; -import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig; import org.keycloak.broker.oidc.mappers.AbstractJsonUserAttributeMapper; import org.keycloak.broker.provider.BrokeredIdentityContext; import org.keycloak.broker.provider.IdentityBrokerException; @@ -27,6 +32,8 @@ import org.keycloak.broker.provider.util.SimpleHttp; import org.keycloak.broker.social.SocialIdentityProvider; import org.keycloak.events.EventBuilder; import org.keycloak.models.KeycloakSession; +import org.keycloak.services.ErrorPageException; +import org.keycloak.services.messages.Messages; import org.keycloak.social.linkedin.LinkedInIdentityProvider; import com.fasterxml.jackson.databind.JsonNode; @@ -34,17 +41,19 @@ import com.fasterxml.jackson.databind.JsonNode; /** * @author Hiroyuki Wada */ -public class DiscordIdentityProvider extends AbstractOAuth2IdentityProvider - implements SocialIdentityProvider { +public class DiscordIdentityProvider extends AbstractOAuth2IdentityProvider + implements SocialIdentityProvider { private static final Logger log = Logger.getLogger(LinkedInIdentityProvider.class); public static final String AUTH_URL = "https://discordapp.com/api/oauth2/authorize"; public static final String TOKEN_URL = "https://discordapp.com/api/oauth2/token"; public static final String PROFILE_URL = "https://discordapp.com/api/users/@me"; + public static final String GROUP_URL = "https://discordapp.com/api/users/@me/guilds"; public static final String DEFAULT_SCOPE = "identify email"; + public static final String GUILDS_SCOPE = "guilds"; - public DiscordIdentityProvider(KeycloakSession session, OAuth2IdentityProviderConfig config) { + public DiscordIdentityProvider(KeycloakSession session, DiscordIdentityProviderConfig config) { super(session, config); config.setAuthorizationUrl(AUTH_URL); config.setTokenUrl(TOKEN_URL); @@ -78,17 +87,43 @@ public class DiscordIdentityProvider extends AbstractOAuth2IdentityProvider allowedGuilds = getConfig().getAllowedGuildsAsSet(); + for (JsonNode guild : guilds) { + String guildId = getJsonProperty(guild, "id"); + if (allowedGuilds.contains(guildId)) { + return true; + } + } + return false; + } catch (Exception e) { + throw new IdentityBrokerException("Could not obtain guilds the current user is a member of from discord.", e); + } } @Override protected String getDefaultScopes() { - return DEFAULT_SCOPE; + if (getConfig().hasAllowedGuilds()) { + return DEFAULT_SCOPE + " " + GUILDS_SCOPE; + } else { + return DEFAULT_SCOPE; + } } } \ No newline at end of file diff --git a/ejb/src/main/java/org/keycloak/social/discord/DiscordIdentityProviderConfig.java b/ejb/src/main/java/org/keycloak/social/discord/DiscordIdentityProviderConfig.java new file mode 100755 index 0000000..6a8170f --- /dev/null +++ b/ejb/src/main/java/org/keycloak/social/discord/DiscordIdentityProviderConfig.java @@ -0,0 +1,57 @@ +/* + * Copyright 2018 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * 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.keycloak.social.discord; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; +import java.util.stream.Collectors; + +import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig; +import org.keycloak.models.IdentityProviderModel; + +/** + * @author Hiroyuki Wada + */ +public class DiscordIdentityProviderConfig extends OAuth2IdentityProviderConfig { + + public DiscordIdentityProviderConfig(IdentityProviderModel model) { + super(model); + } + + public String getAllowedGuilds() { + return getConfig().get("allowedGuilds"); + } + + public void setAllowedGuilds(String allowedGuilds) { + getConfig().put("allowedGuilds", allowedGuilds); + } + + public boolean hasAllowedGuilds() { + String guilds = getConfig().get("allowedGuilds"); + return guilds != null && !guilds.trim().isEmpty(); + } + + public Set getAllowedGuildsAsSet() { + if (hasAllowedGuilds()) { + String guilds = getConfig().get("allowedGuilds"); + return Arrays.stream(guilds.split(",")).map(x -> x.trim()).collect(Collectors.toSet()); + } + return Collections.emptySet(); + } +} \ No newline at end of file diff --git a/ejb/src/main/java/org/keycloak/social/discord/DiscordIdentityProviderFactory.java b/ejb/src/main/java/org/keycloak/social/discord/DiscordIdentityProviderFactory.java index 4e1732c..f35a845 100755 --- a/ejb/src/main/java/org/keycloak/social/discord/DiscordIdentityProviderFactory.java +++ b/ejb/src/main/java/org/keycloak/social/discord/DiscordIdentityProviderFactory.java @@ -14,9 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.keycloak.social.discord; -import org.keycloak.broker.oidc.OIDCIdentityProviderConfig; import org.keycloak.broker.provider.AbstractIdentityProviderFactory; import org.keycloak.broker.social.SocialIdentityProviderFactory; import org.keycloak.models.IdentityProviderModel; @@ -25,7 +25,8 @@ import org.keycloak.models.KeycloakSession; /** * @author Hiroyuki Wada */ -public class DiscordIdentityProviderFactory extends AbstractIdentityProviderFactory implements SocialIdentityProviderFactory { +public class DiscordIdentityProviderFactory extends AbstractIdentityProviderFactory + implements SocialIdentityProviderFactory { public static final String PROVIDER_ID = "discord"; @@ -36,7 +37,7 @@ public class DiscordIdentityProviderFactory extends AbstractIdentityProviderFact @Override public DiscordIdentityProvider create(KeycloakSession session, IdentityProviderModel model) { - return new DiscordIdentityProvider(session, new OIDCIdentityProviderConfig(model)); + return new DiscordIdentityProvider(session, new DiscordIdentityProviderConfig(model)); } @Override diff --git a/ejb/src/main/resources/theme/discord/admin/messages/admin-messages_en.properties b/ejb/src/main/resources/theme/discord/admin/messages/admin-messages_en.properties index 2a062dc..8b39257 100755 --- a/ejb/src/main/resources/theme/discord/admin/messages/admin-messages_en.properties +++ b/ejb/src/main/resources/theme/discord/admin/messages/admin-messages_en.properties @@ -1,5 +1,7 @@ discord-client-id=Client Id discord-client-secret=Client Secret +discord-allowed-guilds=Guild Id(s) to allow federation discord.client-id.tooltip=Client Id for the application you created in your discord developer portal. discord.client-secret.tooltip=Client Secret for the application that you created in your discord developer portal. -discord.default-scopes.tooltip=The scopes to be sent when asking for authorization. See discord OAuth2 documentation for possible values. If you do not specify anything, scope defaults to 'identify email'. \ No newline at end of file +discord.allowed-guilds.tooltip=If you want to allow federation for specific guild, enter the guild id. Please use a comma as a separator for multiple guilds. +discord.default-scopes.tooltip=The scopes to be sent when asking for authorization. See discord OAuth2 documentation for possible values. If you do not specify anything, scope defaults to 'identify email' In addition, plus 'guilds' if you enter guild id(s) to allow federation. \ No newline at end of file diff --git a/ejb/src/main/resources/theme/discord/admin/resources/partials/realm-identity-provider-discord-ext.html b/ejb/src/main/resources/theme/discord/admin/resources/partials/realm-identity-provider-discord-ext.html new file mode 100755 index 0000000..320fa2c --- /dev/null +++ b/ejb/src/main/resources/theme/discord/admin/resources/partials/realm-identity-provider-discord-ext.html @@ -0,0 +1,7 @@ +
+ +
+ +
+ {{:: 'discord.allowed-guilds.tooltip' | translate}} +
\ No newline at end of file diff --git a/ejb/src/main/resources/theme/discord/admin/resources/partials/realm-identity-provider-discord.html b/ejb/src/main/resources/theme/discord/admin/resources/partials/realm-identity-provider-discord.html index 92fabe3..bd48b37 100755 --- a/ejb/src/main/resources/theme/discord/admin/resources/partials/realm-identity-provider-discord.html +++ b/ejb/src/main/resources/theme/discord/admin/resources/partials/realm-identity-provider-discord.html @@ -32,6 +32,7 @@ {{:: 'discord.client-secret.tooltip' | translate}} +