diff --git a/build.gradle b/build.gradle
index 0e0cc644403..45856e0d546 100644
--- a/build.gradle
+++ b/build.gradle
@@ -94,6 +94,9 @@ allprojects {
junitjupiterVersion = '5.9.1'
junitplatformVersion = '1.9.1'
jwtVersion = '0.9.1'
+ jwtApiVersion = '0.11.5'
+ jwtImplVersion = '0.11.5'
+ jwtJacksonVersion = '0.11.5'
kafkaVersion = '3.3.1'
lang3Version = '3.12.0'
logbackVersion = '1.2.11'
@@ -162,6 +165,7 @@ allprojects {
implementation("org.apache.commons:commons-text:${commonstextVersion}")
implementation("io.github.classgraph:classgraph:${classgraphVersion}")
implementation("io.jsonwebtoken:jjwt:${jwtVersion}")
+ implementation("io.jsonwebtoken:jjwt-api:${jwtApiVersion}")
implementation("io.lettuce:lettuce-core:${lettuceVersion}")
implementation("io.micrometer:micrometer-registry-prometheus:${prometheusVersion}")
implementation("io.netty:netty-handler:${nettyVersion}")
@@ -280,6 +284,8 @@ allprojects {
runtimeOnly("org.janusgraph:janusgraph-es:${janusVersion}")
runtimeOnly("org.xerial.snappy:snappy-java:${snappyVersion}")
runtimeOnly("javax.servlet:javax.servlet-api:${servletVersion}")
+ runtimeOnly("io.jsonwebtoken:jjwt-impl:${jwtImplVersion}")
+ runtimeOnly("io.jsonwebtoken:jjwt-jackson:${jwtJacksonVersion}")
testImplementation("junit:junit:${junitVersion}")
testImplementation("org.glassfish:javax.json:${glassfishVersion}")
testImplementation("org.junit.jupiter:junit-jupiter:${junitjupiterVersion}")
diff --git a/open-metadata-implementation/adapters/authentication-plugins/http-helper/src/main/java/org/odpi/openmetadata/http/HttpRequestHeadersFilter.java b/open-metadata-implementation/adapters/authentication-plugins/http-helper/src/main/java/org/odpi/openmetadata/http/HttpRequestHeadersFilter.java
index 812bb57eb6d..7c2c983071b 100644
--- a/open-metadata-implementation/adapters/authentication-plugins/http-helper/src/main/java/org/odpi/openmetadata/http/HttpRequestHeadersFilter.java
+++ b/open-metadata-implementation/adapters/authentication-plugins/http-helper/src/main/java/org/odpi/openmetadata/http/HttpRequestHeadersFilter.java
@@ -42,7 +42,10 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
for (String headerName : headerNames) {
String headerValue = req.getHeader(headerName);
- threadLocalHeaders.put(headerName, headerValue);
+
+ if (headerValue != null && !headerValue.isEmpty()) {
+ threadLocalHeaders.put(headerName, headerValue);
+ }
}
HttpHeadersThreadLocal.getHeadersThreadLocal().set(threadLocalHeaders);
diff --git a/open-metadata-resources/open-metadata-samples/open-metadata-security-samples/build.gradle b/open-metadata-resources/open-metadata-samples/open-metadata-security-samples/build.gradle
index b4618c5f789..02fb5981cec 100644
--- a/open-metadata-resources/open-metadata-samples/open-metadata-security-samples/build.gradle
+++ b/open-metadata-resources/open-metadata-samples/open-metadata-security-samples/build.gradle
@@ -5,12 +5,17 @@
dependencies {
+ implementation 'io.jsonwebtoken:jjwt-api'
+ implementation 'org.slf4j:slf4j-api'
implementation project(':open-metadata-implementation:common-services:metadata-security:metadata-security-connectors')
implementation project(':open-metadata-implementation:repository-services:repository-services-apis')
implementation project(':open-metadata-implementation:frameworks:open-connector-framework')
implementation project(':open-metadata-implementation:frameworks:audit-log-framework')
implementation project(':open-metadata-implementation:common-services:metadata-security:metadata-security-apis')
+ implementation project(':open-metadata-implementation:adapters:authentication-plugins:http-helper')
compileOnly 'com.fasterxml.jackson.core:jackson-annotations'
+ runtimeOnly 'io.jsonwebtoken:jjwt-impl'
+ runtimeOnly 'io.jsonwebtoken:jjwt-jackson'
}
diff --git a/open-metadata-resources/open-metadata-samples/open-metadata-security-samples/pom.xml b/open-metadata-resources/open-metadata-samples/open-metadata-security-samples/pom.xml
index 371ad045d08..3d33d58b087 100644
--- a/open-metadata-resources/open-metadata-samples/open-metadata-security-samples/pom.xml
+++ b/open-metadata-resources/open-metadata-samples/open-metadata-security-samples/pom.xml
@@ -39,6 +39,11 @@
repository-services-apis
+
+ org.odpi.egeria
+ http-helper
+
+
org.odpi.egeria
open-connector-framework
@@ -54,6 +59,74 @@
metadata-security-apis
+
+ io.jsonwebtoken
+ jjwt-api
+
+
+
+ io.jsonwebtoken
+ jjwt-impl
+
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+
+
+
+ org.slf4j
+ slf4j-api
+
+
+
+
+ maven-assembly-plugin
+
+
+ package
+
+ single
+
+
+
+
+
+ org.odpi.openmetadata.metadatasecurity.samples.CocoPharmaServerSecurityConnectorTokenBased
+
+
+
+
+ jar-with-dependencies
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ analyze
+
+ analyze-only
+
+
+
+
+ io.jsonwebtoken:jjwt-impl:*
+
+ io.jsonwebtoken:jjwt-jackson:*
+
+
+
+
+
+
+
+
+
diff --git a/open-metadata-resources/open-metadata-samples/open-metadata-security-samples/src/main/java/org/odpi/openmetadata/metadatasecurity/samples/CocoPharmaPlatformSecurityConnectorTokenBased.java b/open-metadata-resources/open-metadata-samples/open-metadata-security-samples/src/main/java/org/odpi/openmetadata/metadatasecurity/samples/CocoPharmaPlatformSecurityConnectorTokenBased.java
new file mode 100644
index 00000000000..acd25192180
--- /dev/null
+++ b/open-metadata-resources/open-metadata-samples/open-metadata-security-samples/src/main/java/org/odpi/openmetadata/metadatasecurity/samples/CocoPharmaPlatformSecurityConnectorTokenBased.java
@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: Apache 2.0 */
+/* Copyright Contributors to the ODPi Egeria project. */
+package org.odpi.openmetadata.metadatasecurity.samples;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jws;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.security.Keys;
+import org.odpi.openmetadata.frameworks.connectors.ffdc.UserNotAuthorizedException;
+import org.odpi.openmetadata.http.HttpHeadersThreadLocal;
+import org.odpi.openmetadata.metadatasecurity.connectors.OpenMetadataPlatformSecurityConnector;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Base64;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * CocoPharmaPlatformSecurityConnector overrides the default behavior for the security connector
+ * to allow requests the Coco Pharmaceutical's server administrator APIs. In this example,
+ * only Gary Geeke is allowed to issue these requests.
+ *
+ * To generate a JWT for this example, we used the following payload:
+ * {
+ * "sub": "garygeeke",
+ * "name": "Gary Geeke",
+ * "actions":["platform-administrator","platform-operator","platform-investigator"],
+ * "iat": {Epoch timestamp},
+ * "exp": {Epoch timestamp}
+ * }
+ */
+public class CocoPharmaPlatformSecurityConnectorTokenBased extends OpenMetadataPlatformSecurityConnector {
+ private enum PlatformRoles {
+ PLATFORM_ADMINISTRATOR,
+ PLATFORM_OPERATOR,
+ PLATFORM_INVESTIGATOR;
+
+ private String getName() {
+ return this.toString().toLowerCase();
+ }
+ }
+
+ //secret used to decrypt the given token
+ private final byte[] secret = Base64.getDecoder().decode("d14uaEwsGU3cXopmxaEDqhQTow81zixFWbFUuu3budQ");
+
+ private static final Logger log = LoggerFactory.getLogger(CocoPharmaPlatformSecurityConnectorTokenBased.class);
+
+
+ /**
+ * Check that the calling user is authorized to create new servers.
+ *
+ * @param userId calling user
+ * @throws UserNotAuthorizedException the user is not authorized to access this platform
+ */
+ @Override
+ public void validateUserForNewServer(String userId) throws UserNotAuthorizedException {
+ final String methodName = "validateUserForNewServer";
+
+ if (!isAllowedToPerformAction(userId, PlatformRoles.PLATFORM_ADMINISTRATOR)) {
+ super.throwUnauthorizedPlatformAccess(userId, methodName);
+ }
+ }
+
+
+ /**
+ * Check that the calling user is authorized to issue operator requests to the OMAG Server Platform.
+ *
+ * @param userId calling user
+ * @throws UserNotAuthorizedException the user is not authorized to issue operator commands to this platform
+ */
+ @Override
+ public void validateUserAsOperatorForPlatform(String userId) throws UserNotAuthorizedException {
+ final String methodName = "validateUserAsOperatorForPlatform";
+
+ if (!isAllowedToPerformAction(userId, PlatformRoles.PLATFORM_OPERATOR)) {
+ super.throwUnauthorizedPlatformAccess(userId, methodName);
+ }
+ }
+
+
+ /**
+ * Check that the calling user is authorized to issue operator requests to the OMAG Server Platform.
+ *
+ * @param userId calling user
+ * @throws UserNotAuthorizedException the user is not authorized to issue diagnostic commands to this platform
+ */
+ @Override
+ public void validateUserAsInvestigatorForPlatform(String userId) throws UserNotAuthorizedException {
+ final String methodName = "validateUserAsInvestigatorForPlatform";
+
+ if (!isAllowedToPerformAction(userId, PlatformRoles.PLATFORM_INVESTIGATOR)) {
+ super.throwUnauthorizedPlatformAccess(userId, methodName);
+ }
+ }
+
+ private List getUserActionsFromToken(String userId) {
+ Map headersMap = HttpHeadersThreadLocal.getHeadersThreadLocal().get();
+ if (headersMap != null && !headersMap.isEmpty()) {
+ Jws jwtClaims = Jwts.parserBuilder()
+ .setSigningKey(Keys.hmacShaKeyFor(secret))
+ .build().parseClaimsJws(headersMap.get("authorization"));
+
+ String username = jwtClaims.getBody().getSubject();
+ List actions = jwtClaims.getBody().get("actions", List.class);
+ if (username.equals(userId) && actions != null && !actions.isEmpty()) {
+ log.info("User {} validated for issuing requests.", username);
+ return actions;
+ }
+ }
+ return null;
+ }
+
+ private Boolean isAllowedToPerformAction(String userId, PlatformRoles role) {
+ List userActions = getUserActionsFromToken(userId);
+
+ if (userActions != null && !userActions.isEmpty() && userActions.contains(role.getName())) {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/open-metadata-resources/open-metadata-samples/open-metadata-security-samples/src/main/java/org/odpi/openmetadata/metadatasecurity/samples/CocoPharmaPlatformSecurityProviderTokenBased.java b/open-metadata-resources/open-metadata-samples/open-metadata-security-samples/src/main/java/org/odpi/openmetadata/metadatasecurity/samples/CocoPharmaPlatformSecurityProviderTokenBased.java
new file mode 100644
index 00000000000..ceeda68b186
--- /dev/null
+++ b/open-metadata-resources/open-metadata-samples/open-metadata-security-samples/src/main/java/org/odpi/openmetadata/metadatasecurity/samples/CocoPharmaPlatformSecurityProviderTokenBased.java
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: Apache 2.0 */
+/* Copyright Contributors to the ODPi Egeria project. */
+package org.odpi.openmetadata.metadatasecurity.samples;
+
+import org.odpi.openmetadata.frameworks.auditlog.AuditLogReportingComponent;
+import org.odpi.openmetadata.frameworks.connectors.properties.beans.ConnectorType;
+import org.odpi.openmetadata.metadatasecurity.connectors.OpenMetadataPlatformSecurityProvider;
+
+/**
+ * CocoPharmaPlatformSecurityProviderTokenBased is the connector provider to the
+ * sample platform security connector for the Coco Pharmaceuticals scenarios.
+ */
+public class CocoPharmaPlatformSecurityProviderTokenBased extends OpenMetadataPlatformSecurityProvider
+{
+ /*
+ * Unique identifier of the connector for the audit log.
+ */
+ private static final int connectorComponentId = 93;
+
+ /*
+ * Unique identifier for the connector type.
+ */
+ private static final String connectorTypeGUID = "5e6f852f-5912-44fb-aa6c-784efe25af47";
+
+ /*
+ * Descriptive information about the connector for the connector type and audit log.
+ */
+ private static final String connectorQualifiedName = "Egeria:Sample:TokenBased:PlatformSecurity:CocoPharmaceuticals";
+ private static final String connectorDisplayName = "Coco Pharmaceuticals Platform Security Connector Token Based";
+ private static final String connectorDescription = "Connector that exposes the usability of custom authorisation headers for Coco Pharmaceuticals.";
+
+ /*
+ * Class of the connector.
+ */
+ private static final Class> connectorClass = CocoPharmaPlatformSecurityConnectorTokenBased.class;
+
+
+ /**
+ * Constructor used to initialize the ConnectorProviderBase with the Java class name of the specific
+ * registry store implementation.
+ */
+ public CocoPharmaPlatformSecurityProviderTokenBased()
+ {
+ super();
+
+ /*
+ * Set up the class name of the connector that this provider creates.
+ */
+ super.setConnectorClassName(connectorClass.getName());
+
+ /*
+ * Set up the connector type that should be included in a connection used to configure this connector.
+ */
+ ConnectorType connectorType = new ConnectorType();
+ connectorType.setType(ConnectorType.getConnectorTypeType());
+ connectorType.setGUID(connectorTypeGUID);
+ connectorType.setQualifiedName(connectorQualifiedName);
+ connectorType.setDisplayName(connectorDisplayName);
+ connectorType.setDescription(connectorDescription);
+ connectorType.setConnectorProviderClassName(this.getClass().getName());
+
+ super.connectorTypeBean = connectorType;
+
+ /*
+ * Set up the component description used in the connector's audit log messages.
+ */
+ AuditLogReportingComponent componentDescription = new AuditLogReportingComponent();
+
+ componentDescription.setComponentId(connectorComponentId);
+ componentDescription.setComponentName(connectorQualifiedName);
+ componentDescription.setComponentDescription(connectorDescription);
+
+ super.setConnectorComponentDescription(componentDescription);
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index 9c6b23ea4d1..c20b3a29ec5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -177,6 +177,9 @@
4.2.0
3.12.0
0.9.1
+ 0.11.5
+ 0.11.5
+ 0.11.5
3.1.0
3.0.2
2.0.1.Final
@@ -1937,6 +1940,27 @@
${open-metadata.version}
+
+ io.jsonwebtoken
+ jjwt-api
+ compile
+ ${jjwt-api.version}
+
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ runtime
+ ${jjwt-impl.version}
+
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ runtime
+ ${jjwt-jackson.version}
+
+
org.odpi.egeria
metadata-security-connectors