Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Propagate HTTP headers - Coco Pharma example #6977

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,69 @@
<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>
</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,126 @@
/* 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.OpenMetadataServerSecurity;
import org.odpi.openmetadata.metadatasecurity.connectors.OpenMetadataServerSecurityConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* This class is a mock. The purpose of this class is to highlight the usage of HTTP headers passed through ThreadLocal.
lpalashevski marked this conversation as resolved.
Show resolved Hide resolved
*/
public class CocoPharmaServerSecurityConnectorTokenBased extends OpenMetadataServerSecurityConnector implements OpenMetadataServerSecurity {
/*
* These variables represent users and user tokens. Typically these would be
* implemented as a look up to a user directory such as LDAP rather than in memory lists.
* The lists are used here to make the demo easier to set up.
*/
private List<String> allUsers = new ArrayList<>();

private List<String> serverAdmins = new ArrayList<>();
private List<String> serverOperators = new ArrayList<>();
private List<String> serverInvestigators = new ArrayList<>();
private List<String> defaultZoneMembership = new ArrayList<>();

private Map<String, String> usersJWT = new HashMap<>();

private static final Logger log = LoggerFactory.getLogger(CocoPharmaServerSecurityConnectorTokenBased.class);


/*
* Zones requiring special processing
*/
private final String quarantineZoneName = "quarantine";


private final byte[] secret = Base64.getDecoder().decode("d14uaEwsGU3cXopmxaEDqhQTow81zixFWbFUuu3budQ");

public CocoPharmaServerSecurityConnectorTokenBased() {
final String garyGeekeUserId = "garygeeke";

allUsers.add(garyGeekeUserId);

serverAdmins.add(garyGeekeUserId);
serverOperators.add(garyGeekeUserId);
serverInvestigators.add(garyGeekeUserId);

/*
* Set up default zone membership
*/
defaultZoneMembership.add(quarantineZoneName);

usersJWT.put(garyGeekeUserId, this.getJWT(garyGeekeUserId));
}

/**
* This method is just an example. It returns a JWT with 5 minutes expiration date.
*
* @return a JSON web token for the specified user
lpalashevski marked this conversation as resolved.
Show resolved Hide resolved
*/
private String getJWT(String userId) {
Instant now = Instant.now();

return Jwts.builder()
.setSubject(userId)
.claim("allowedToIssueRequests", Boolean.TRUE)
.setIssuedAt(Date.from(now))
.setExpiration(Date.from(now.plus(30, ChronoUnit.DAYS)))
.signWith(Keys.hmacShaKeyFor(secret))
.compact();
}


@Override
public void validateUserForServer(String userId) throws UserNotAuthorizedException {
Map<String, String> headersMap = HttpHeadersThreadLocal.getHeadersThreadLocal().get();
if (headersMap != null) {
Jws<Claims> jwtClaims = Jwts.parserBuilder()
.setSigningKey(Keys.hmacShaKeyFor(secret))
.build().parseClaimsJws(headersMap.get("authorisation"));

String username = jwtClaims.getBody().getSubject();
Boolean allowedToIssueRequests = jwtClaims.getBody().get("allowedToIssueRequests", Boolean.class);
if (username.equals(userId) && allUsers.contains(userId) && allowedToIssueRequests) {
log.info("User {} validated for issuing requests.", username);
return;
}
}
super.validateUserForServer(userId);
}

/**
* The following methods are from OpenMetadataServerSecurity interface and must be implemented in a real use-case.
* For this example, we have shown the usage of JWT in validateUserForServer method.
*/
@Override
public void validateUserAsServerAdmin(String userId) throws UserNotAuthorizedException {

}

@Override
public void validateUserAsServerOperator(String userId) throws UserNotAuthorizedException {

}

@Override
public void validateUserAsServerInvestigator(String userId) throws UserNotAuthorizedException {

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/* 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.OpenMetadataServerSecurityProvider;

public class CocoPharmaServerSecurityTokenBasedProvider extends OpenMetadataServerSecurityProvider {

/*
* Unique identifier of the connector for the audit log.
*/
private static final int connectorComponentId = 92;

/*
* Unique identifier for the connector type.
*/
private static final String connectorTypeGUID = "4daa31ca-d001-4a10-8756-3c88a33e9b92";

/*
* Descriptive information about the connector for the connector type and audit log.
*/
private static final String connectorQualifiedName = "Egeria:Sample:ServerSecurity:CocoPharmaceuticals";
private static final String connectorDisplayName = "Coco Pharmaceuticals Server 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 = CocoPharmaServerSecurityConnectorTokenBased.class;


/**
* Constructor used to initialize the ConnectorProviderBase with the Java class name of the specific
* registry store implementation.
*/
public CocoPharmaServerSecurityTokenBasedProvider()
{
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 @@ -178,6 +178,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 @@ -1938,6 +1941,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