Skip to content

Commit

Permalink
Merge pull request #6977 from MihaiIliescu/propagate_hhtp_headers_coc…
Browse files Browse the repository at this point in the history
…o_pharma_example

Propagate HTTP headers - Coco Pharma example
  • Loading branch information
Ljupcho Palashevski committed Jan 25, 2023
2 parents c39f979 + 5062110 commit b8c0f65
Show file tree
Hide file tree
Showing 7 changed files with 310 additions and 1 deletion.
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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}")
Expand Down Expand Up @@ -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}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@
<artifactId>repository-services-apis</artifactId>
</dependency>

<dependency>
<groupId>org.odpi.egeria</groupId>
<artifactId>http-helper</artifactId>
</dependency>

<dependency>
<groupId>org.odpi.egeria</groupId>
<artifactId>open-connector-framework</artifactId>
Expand All @@ -54,6 +59,74 @@
<artifactId>metadata-security-apis</artifactId>
</dependency>

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
</dependency>

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
</dependency>

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<archive>
<manifest>
<mainClass>
org.odpi.openmetadata.metadatasecurity.samples.CocoPharmaServerSecurityConnectorTokenBased
</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>analyze</id>
<goals>
<goal>analyze-only</goal>
</goals>
<configuration>
<ignoredUnusedDeclaredDependencies combine.children="append">
<!-- Runtime dependencies for Coco Pharma token based connector-->
<ignoredUnusedDeclaredDependency>io.jsonwebtoken:jjwt-impl:*
</ignoredUnusedDeclaredDependency>
<ignoredUnusedDeclaredDependency>io.jsonwebtoken:jjwt-jackson:*
</ignoredUnusedDeclaredDependency>
</ignoredUnusedDeclaredDependencies>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -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<String> getUserActionsFromToken(String userId) {
Map<String, String> headersMap = HttpHeadersThreadLocal.getHeadersThreadLocal().get();
if (headersMap != null && !headersMap.isEmpty()) {
Jws<Claims> jwtClaims = Jwts.parserBuilder()
.setSigningKey(Keys.hmacShaKeyFor(secret))
.build().parseClaimsJws(headersMap.get("authorization"));

String username = jwtClaims.getBody().getSubject();
List<String> 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<String> userActions = getUserActionsFromToken(userId);

if (userActions != null && !userActions.isEmpty() && userActions.contains(role.getName())) {
return true;
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -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);
}

}
24 changes: 24 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,9 @@
<jena.version>4.2.0</jena.version>
<commons-lang.version>3.12.0</commons-lang.version>
<jwt.version>0.9.1</jwt.version>
<jjwt-api.version>0.11.5</jjwt-api.version>
<jjwt-impl.version>0.11.5</jjwt-impl.version>
<jjwt-jackson.version>0.11.5</jjwt-jackson.version>
<jakarta.persistence.version>3.1.0</jakarta.persistence.version>
<jakarta-validation.version>3.0.2</jakarta-validation.version>
<javax-validation.version>2.0.1.Final</javax-validation.version>
Expand Down Expand Up @@ -1937,6 +1940,27 @@
<version>${open-metadata.version}</version>
</dependency>

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<scope>compile</scope>
<version>${jjwt-api.version}</version>
</dependency>

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<scope>runtime</scope>
<version>${jjwt-impl.version}</version>
</dependency>

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<scope>runtime</scope>
<version>${jjwt-jackson.version}</version>
</dependency>

<dependency>
<groupId>org.odpi.egeria</groupId>
<artifactId>metadata-security-connectors</artifactId>
Expand Down

0 comments on commit b8c0f65

Please sign in to comment.