This is an automated email from the ASF dual-hosted git repository. yasith pushed a commit to branch feat/spring-module-reorg in repository https://gitbox.apache.org/repos/asf/airavata.git
commit 764c18810d264dbd9c12b541e8fa933664ec1aa4 Author: yasithdev <[email protected]> AuthorDate: Fri Mar 27 22:26:55 2026 -0500 feat: create airavata-grpc-server with centralized gRPC auth interceptor --- airavata-grpc-server/pom.xml | 122 +++++++++++++++++++++ .../grpc/server/AiravataGrpcServerConfig.java | 35 ++++++ .../grpc/server/config/GrpcAuthInterceptor.java | 104 ++++++++++++++++++ pom.xml | 1 + 4 files changed, 262 insertions(+) diff --git a/airavata-grpc-server/pom.xml b/airavata-grpc-server/pom.xml new file mode 100644 index 0000000000..6852ae093d --- /dev/null +++ b/airavata-grpc-server/pom.xml @@ -0,0 +1,122 @@ +<!-- +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you 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. +--> +<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> + + <parent> + <groupId>org.apache.airavata</groupId> + <artifactId>airavata</artifactId> + <version>0.21-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + + <artifactId>airavata-grpc-server</artifactId> + <name>Airavata gRPC Server</name> + + <dependencies> + <!-- Project modules --> + <dependency> + <groupId>org.apache.airavata</groupId> + <artifactId>airavata-api</artifactId> + <version>${project.version}</version> + <exclusions> + <exclusion> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> + </exclusion> + <exclusion> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-core</artifactId> + </exclusion> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.apache.airavata</groupId> + <artifactId>agent-service</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.airavata</groupId> + <artifactId>research-service</artifactId> + <version>${project.version}</version> + </dependency> + + <!-- gRPC --> + <dependency> + <groupId>net.devh</groupId> + <artifactId>grpc-server-spring-boot-starter</artifactId> + <exclusions> + <exclusion> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> + </exclusion> + <exclusion> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-core</artifactId> + </exclusion> + </exclusions> + </dependency> + + <!-- Spring Boot --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-data-jpa</artifactId> + <exclusions> + <exclusion> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-logging</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-actuator</artifactId> + <exclusions> + <exclusion> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-logging</artifactId> + </exclusion> + </exclusions> + </dependency> + + <!-- Database --> + <dependency> + <groupId>org.mariadb.jdbc</groupId> + <artifactId>mariadb-java-client</artifactId> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <release>17</release> + </configuration> + </plugin> + </plugins> + </build> +</project> diff --git a/airavata-grpc-server/src/main/java/org/apache/airavata/grpc/server/AiravataGrpcServerConfig.java b/airavata-grpc-server/src/main/java/org/apache/airavata/grpc/server/AiravataGrpcServerConfig.java new file mode 100644 index 0000000000..6bdc8c2fae --- /dev/null +++ b/airavata-grpc-server/src/main/java/org/apache/airavata/grpc/server/AiravataGrpcServerConfig.java @@ -0,0 +1,35 @@ +/** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you 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.apache.airavata.grpc.server; + +import org.apache.airavata.agent.connection.service.config.AgentServiceConfig; +import org.apache.airavata.common.config.ConditionalOnServer; +import org.apache.airavata.research.service.config.ResearchServiceConfig; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +@Configuration +@ConditionalOnServer("grpc") +@Import({ + AgentServiceConfig.class, + ResearchServiceConfig.class +}) +public class AiravataGrpcServerConfig { +} diff --git a/airavata-grpc-server/src/main/java/org/apache/airavata/grpc/server/config/GrpcAuthInterceptor.java b/airavata-grpc-server/src/main/java/org/apache/airavata/grpc/server/config/GrpcAuthInterceptor.java new file mode 100644 index 0000000000..142a3e78d0 --- /dev/null +++ b/airavata-grpc-server/src/main/java/org/apache/airavata/grpc/server/config/GrpcAuthInterceptor.java @@ -0,0 +1,104 @@ +/** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you 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.apache.airavata.grpc.server.config; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.grpc.ForwardingServerCallListener; +import io.grpc.Metadata; +import io.grpc.ServerCall; +import io.grpc.ServerCallHandler; +import io.grpc.ServerInterceptor; +import io.grpc.Status; +import net.devh.boot.grpc.server.interceptor.GrpcGlobalServerInterceptor; +import org.apache.airavata.common.security.UserContext; +import org.apache.airavata.model.security.AuthzToken; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Map; + +@GrpcGlobalServerInterceptor +public class GrpcAuthInterceptor implements ServerInterceptor { + + private static final Logger log = LoggerFactory.getLogger(GrpcAuthInterceptor.class); + private static final ObjectMapper objectMapper = new ObjectMapper(); + + private static final Metadata.Key<String> AUTHORIZATION_KEY = + Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER); + private static final Metadata.Key<String> X_CLAIMS_KEY = + Metadata.Key.of("x-claims", Metadata.ASCII_STRING_MARSHALLER); + + @Override + public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall( + ServerCall<ReqT, RespT> call, + Metadata headers, + ServerCallHandler<ReqT, RespT> next) { + + String authHeader = headers.get(AUTHORIZATION_KEY); + String accessToken = null; + if (authHeader != null && authHeader.startsWith("Bearer ")) { + accessToken = authHeader.substring(7); + } + + if (accessToken == null) { + call.close(Status.UNAUTHENTICATED.withDescription("Missing authorization metadata"), new Metadata()); + return new ServerCall.Listener<>() {}; + } + + AuthzToken authzToken = new AuthzToken(accessToken); + + Map<String, String> claimsMap = new HashMap<>(); + String claimsHeader = headers.get(X_CLAIMS_KEY); + if (claimsHeader != null && !claimsHeader.isBlank()) { + try { + claimsMap = objectMapper.readValue(claimsHeader, new TypeReference<Map<String, String>>() {}); + } catch (Exception e) { + log.warn("Failed to parse x-claims metadata: {}", e.getMessage()); + } + } + + authzToken.setClaimsMap(claimsMap); + UserContext.setAuthzToken(authzToken); + + ServerCall.Listener<ReqT> delegate = next.startCall(call, headers); + + return new ForwardingServerCallListener.SimpleForwardingServerCallListener<>(delegate) { + @Override + public void onComplete() { + try { + super.onComplete(); + } finally { + UserContext.clear(); + } + } + + @Override + public void onCancel() { + try { + super.onCancel(); + } finally { + UserContext.clear(); + } + } + }; + } +} diff --git a/pom.xml b/pom.xml index d202121e86..ffee99715e 100644 --- a/pom.xml +++ b/pom.xml @@ -75,6 +75,7 @@ under the License. <module>modules/agent-framework/agent-service</module> <module>modules/research-framework/research-service</module> <module>airavata-rest-server</module> + <module>airavata-grpc-server</module> </modules> <properties>
