-
Notifications
You must be signed in to change notification settings - Fork 150
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add a custom token mapper to get project ids from api-db
- Loading branch information
1 parent
5cd4871
commit 2835fb4
Showing
8 changed files
with
308 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<groupId>net.cake.keycloak.custom</groupId> | ||
<artifactId>custom-protocol-mapper</artifactId> | ||
<version>1.0.0</version> | ||
<packaging>jar</packaging> | ||
|
||
<properties> | ||
<keycloak.version>17.0.1</keycloak.version> | ||
</properties> | ||
|
||
<dependencies> | ||
<!-- provided keycloak dependencies --> | ||
<dependency> | ||
<groupId>org.keycloak</groupId> | ||
<artifactId>keycloak-core</artifactId> | ||
<version>${keycloak.version}</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.keycloak</groupId> | ||
<artifactId>keycloak-server-spi</artifactId> | ||
<version>${keycloak.version}</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.keycloak</groupId> | ||
<artifactId>keycloak-server-spi-private</artifactId> | ||
<version>${keycloak.version}</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.keycloak</groupId> | ||
<artifactId>keycloak-services</artifactId> | ||
<version>${keycloak.version}</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.mariadb.jdbc</groupId> | ||
<artifactId>mariadb-java-client</artifactId> | ||
<version>LATEST</version> | ||
</dependency> | ||
|
||
</dependencies> | ||
|
||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-compiler-plugin</artifactId> | ||
<version>3.7.0</version> | ||
<configuration> | ||
<forceJavacCompilerUse>true</forceJavacCompilerUse> | ||
<source>1.8</source> | ||
<target>1.8</target> | ||
</configuration> | ||
</plugin> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-shade-plugin</artifactId> | ||
<version>3.1.0</version> | ||
<executions> | ||
<!-- Run shade goal on package phase --> | ||
<execution> | ||
<phase>package</phase> | ||
<goals> | ||
<goal>shade</goal> | ||
</goals> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
</project> |
167 changes: 167 additions & 0 deletions
167
services/keycloak/custom-mapper/src/main/java/CustomOIDCProtocolMapper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
import org.keycloak.models.ClientSessionContext; | ||
import org.keycloak.models.KeycloakSession; | ||
import org.keycloak.models.ProtocolMapperModel; | ||
import org.keycloak.models.UserSessionModel; | ||
import org.keycloak.protocol.oidc.OIDCLoginProtocol; | ||
import org.keycloak.protocol.oidc.mappers.*; | ||
import org.keycloak.provider.ProviderConfigProperty; | ||
import org.keycloak.representations.AccessToken; | ||
import org.jboss.logging.Logger; | ||
|
||
import org.keycloak.models.IdentityProviderMapperModel; | ||
import org.keycloak.models.RealmModel; | ||
import org.keycloak.models.GroupModel; | ||
import org.keycloak.models.UserModel; | ||
import org.keycloak.models.RoleModel; | ||
import org.keycloak.models.utils.KeycloakModelUtils; | ||
|
||
import java.util.ArrayList; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.Iterator; | ||
import java.sql.Connection; | ||
import java.sql.ResultSet; | ||
import java.sql.ResultSetMetaData; | ||
import java.sql.Statement; | ||
import java.sql.PreparedStatement; | ||
|
||
public class CustomOIDCProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper { | ||
|
||
public static final String PROVIDER_ID = "lagoon-search-customprotocolmapper"; | ||
|
||
private static final Logger logger = Logger.getLogger(CustomOIDCProtocolMapper.class); | ||
|
||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>(); | ||
|
||
/** | ||
* Maybe you want to have config fields for your Mapper | ||
*/ | ||
/* | ||
static { | ||
ProviderConfigProperty property; | ||
property = new ProviderConfigProperty(); | ||
property.setName(ProtocolMapperUtils.USER_ATTRIBUTE); | ||
property.setLabel(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_LABEL); | ||
property.setHelpText(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_HELP_TEXT); | ||
property.setType(ProviderConfigProperty.STRING_TYPE); | ||
configProperties.add(property); | ||
property = new ProviderConfigProperty(); | ||
property.setName(ProtocolMapperUtils.MULTIVALUED); | ||
property.setLabel(ProtocolMapperUtils.MULTIVALUED_LABEL); | ||
property.setHelpText(ProtocolMapperUtils.MULTIVALUED_HELP_TEXT); | ||
property.setType(ProviderConfigProperty.BOOLEAN_TYPE); | ||
configProperties.add(property); | ||
} | ||
*/ | ||
@Override | ||
public List<ProviderConfigProperty> getConfigProperties() { | ||
return configProperties; | ||
} | ||
|
||
@Override | ||
public String getDisplayCategory() { | ||
return TOKEN_MAPPER_CATEGORY; | ||
} | ||
|
||
@Override | ||
public String getDisplayType() { | ||
return "Lagoon Project Group Mapper"; | ||
} | ||
|
||
@Override | ||
public String getId() { | ||
return PROVIDER_ID; | ||
} | ||
|
||
@Override | ||
public String getHelpText() { | ||
return "A mapper that can retrieve groups and projects from the lagoon API to store in the token"; | ||
} | ||
|
||
public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession keycloakSession, | ||
UserSessionModel userSession, ClientSessionContext clientSessionCtx) { | ||
|
||
List<Object> groupsAndRoles = new ArrayList(); | ||
Map<String, String> groupProjectIds = new HashMap(); | ||
Map<String, String> projectGroupProjectIds = new HashMap(); | ||
UserModel user = userSession.getUser(); | ||
Set<GroupModel> groups = user.getGroups(); | ||
for (GroupModel group : groups) { | ||
if(group.getFirstAttribute("type").equals("role-subgroup")) { | ||
GroupModel parent = group.getParent(); | ||
String parentName = parent.getName(); | ||
List<String> projectIds = new ArrayList(); | ||
try (Connection c = DbUtil.getConnection()) { | ||
PreparedStatement statement = c.prepareStatement("SELECT project_id FROM group_projects WHERE group_id='"+parent.getId()+"'"); | ||
ResultSet resultSet = statement.executeQuery(); | ||
while (resultSet.next()) { | ||
projectIds.add(resultSet.getString(1)); | ||
} | ||
} | ||
catch(Exception ex) { | ||
// don't throw an exception, just log and continue so that the token still generates | ||
logger.tracef("Issue connecting to database to perform query on group id %s: %v", parent.getId(), ex); | ||
} | ||
String temp = parent.getFirstAttribute("type"); | ||
String result = ""; | ||
if(temp != null && !temp.isEmpty()){ | ||
result = temp; | ||
} | ||
if(result.equals("project-default-group")) { | ||
if(projectIds != null) { | ||
for (String projectId : projectIds) { | ||
projectGroupProjectIds.put(projectId, parentName); | ||
}; | ||
} | ||
} else { | ||
if(projectIds != null) { | ||
// add the group so group-tenant association works properly | ||
groupsAndRoles.add(parentName); | ||
// calculate the groupprojectids | ||
for (String projectId : projectIds) { | ||
groupProjectIds.put(projectId, parentName); | ||
}; | ||
} | ||
} | ||
} | ||
}; | ||
// that remains are project ids that are not already associated to an existing group | ||
projectGroupProjectIds.keySet().removeAll(groupProjectIds.keySet()); | ||
|
||
for (Object projectId : projectGroupProjectIds.keySet()) { | ||
groupsAndRoles.add("p"+projectId); | ||
} | ||
|
||
// add all roles the user is part of | ||
Set<RoleModel> userRoles = user.getRoleMappings(); | ||
for (RoleModel role : userRoles) { | ||
String roleName = role.getName(); | ||
groupsAndRoles.add(roleName); | ||
}; | ||
|
||
token.getOtherClaims().put("groups", groupsAndRoles); | ||
|
||
setClaim(token, mappingModel, userSession, keycloakSession, clientSessionCtx); | ||
return token; | ||
} | ||
|
||
public static ProtocolMapperModel create(String name, | ||
boolean accessToken, boolean idToken, boolean userInfo) { | ||
ProtocolMapperModel mapper = new ProtocolMapperModel(); | ||
mapper.setName(name); | ||
mapper.setProtocolMapper(PROVIDER_ID); | ||
mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); | ||
Map<String, String> config = new HashMap<String, String>(); | ||
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); | ||
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); | ||
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true"); | ||
mapper.setConfig(config); | ||
return mapper; | ||
} | ||
|
||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import java.sql.Connection; | ||
import java.sql.DriverManager; | ||
import java.sql.SQLException; | ||
import org.mariadb.jdbc.Driver; | ||
|
||
import org.keycloak.component.ComponentModel; | ||
|
||
public class DbUtil { | ||
|
||
public static Connection getConnection() throws SQLException{ | ||
String driverClass = System.getenv("LAGOON_DB_VENDOR"); | ||
String username = System.getenv("LAGOON_DB_USER"); | ||
String password = System.getenv("LAGOON_DB_PASSWORD"); | ||
String database = System.getenv("LAGOON_DB_DATABASE"); | ||
String host = System.getenv("LAGOON_DB_HOST"); | ||
|
||
String jdbcUrl = "jdbc:"+driverClass+"://"+host+":3306/"+database+"?user="+username+"&password="+password; | ||
return DriverManager.getConnection(jdbcUrl); | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
services/keycloak/custom-mapper/src/main/resources/META-INF/jboss-deployment-structure.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<jboss-deployment-structure> | ||
<deployment> | ||
<dependencies> | ||
<module name="org.keycloak.keycloak-services" /> | ||
</dependencies> | ||
</deployment> | ||
</jboss-deployment-structure> |
1 change: 1 addition & 0 deletions
1
...k/custom-mapper/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
CustomOIDCProtocolMapper |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters