Skip to content

Commit 0b72100

Browse files
authored
Enable test mode on API gateway (#389)
1 parent 965c78e commit 0b72100

File tree

41 files changed

+1084
-191
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1084
-191
lines changed

bin/langstream

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ if [ ! -d langstream-cli/target/cli ]; then
3737
fi
3838
popd > /dev/null
3939
LANGSTREAM_CLI_CONFIG=${LANGSTREAM_CLI_CONFIG:-"conf/cli.yaml"}
40-
echo "Using development CLI config file $(realpath $LANGSTREAM_CLI_CONFIG). To use the global config file, set LANGSTREAM_CLI_CONFIG=\$HOME/.langstream/config"
40+
if [ "$LANGSTREAM_CLI_CONFIG" == "conf/cli.yaml" ]; then
41+
echo "Using development CLI config file $(realpath $LANGSTREAM_CLI_CONFIG). To use the global config file, set LANGSTREAM_CLI_CONFIG=\$HOME/.langstream/config"
42+
fi
4143

4244
"$ROOT_DIR/langstream-cli/target/cli/bin/langstream" --conf "$LANGSTREAM_CLI_CONFIG" "$@"
4345

docker/build.sh

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@ docker_platforms() {
2727
fi
2828
}
2929

30+
common_flags="-DskipTests -PskipPython -Dlicense.skip -Dspotless.skip"
31+
3032

3133
build_docker_image() {
3234
module=$1
33-
./mvnw clean install -am -DskipTests -pl $module -T 1C -PskipPython
34-
./mvnw package -DskipTests -Pdocker -pl $module -Ddocker.platforms="$(docker_platforms)" -PskipPython
35+
./mvnw install -am -pl $module -T 1C $common_flags
36+
./mvnw package -Pdocker -pl $module -Ddocker.platforms="$(docker_platforms)" $common_flags
3537
docker images | head -n 2
3638
}
3739

@@ -47,9 +49,9 @@ elif [ "$only_image" == "api-gateway" ]; then
4749
build_docker_image langstream-api-gateway
4850
else
4951
# Build all artifacts
50-
./mvnw install -DskipTests -T 1C -Ddocker.platforms="$(docker_platforms)" -PskipPython
52+
./mvnw install -T 1C -Ddocker.platforms="$(docker_platforms)" $common_flags
5153
# Build docker images
52-
./mvnw package -DskipTests -Pdocker -Ddocker.platforms="$(docker_platforms)" -PskipPython
54+
./mvnw package -Pdocker -Ddocker.platforms="$(docker_platforms)" $common_flags
5355
docker images | head -n 6
5456
fi
5557

examples/applications/gateway-authentication/gateways.yaml

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,20 @@ gateways:
2121
topic: input-topic
2222
parameters:
2323
- sessionId
24-
produceOptions:
24+
produce-options:
2525
headers:
2626
- key: langstream-client-session-id
27-
valueFromParameters: sessionId
27+
value-from-parameters: sessionId
2828
- id: consume-output-no-auth
2929
type: consume
3030
topic: output-topic
3131
parameters:
3232
- sessionId
33-
consumeOptions:
33+
consume-options:
3434
filters:
3535
headers:
3636
- key: langstream-client-session-id
37-
valueFromParameters: sessionId
37+
value-from-parameters: sessionId
3838

3939
- id: produce-input-auth-google
4040
type: produce
@@ -43,31 +43,33 @@ gateways:
4343
- sessionId
4444
authentication:
4545
provider: google
46+
allow-test-mode: true
4647
configuration:
4748
clientId: "{{ secrets.google.client-id }}"
48-
produceOptions:
49+
produce-options:
4950
headers:
5051
- key: langstream-client-user-id
51-
valueFromAuthentication: subject
52+
value-from-authentication: subject
5253
- key: langstream-client-session-id
53-
valueFromParameters: sessionId
54+
value-from-parameters: sessionId
5455

5556
- id: consume-output-auth-google
5657
type: consume
5758
topic: output-topic
5859
parameters:
5960
- sessionId
6061
authentication:
62+
allow-test-mode: true
6163
provider: google
6264
configuration:
6365
clientId: "{{ secrets.google.client-id }}"
64-
consumeOptions:
66+
consume-options:
6567
filters:
6668
headers:
6769
- key: langstream-client-user-id
68-
valueFromAuthentication: subject
70+
value-from-authentication: subject
6971
- key: langstream-client-session-id
70-
valueFromParameters: sessionId
72+
value-from-parameters: sessionId
7173

7274
- id: produce-input-auth-github
7375
type: produce
@@ -78,12 +80,12 @@ gateways:
7880
provider: github
7981
configuration:
8082
clientId: "{{ secrets.github.client-id }}"
81-
produceOptions:
83+
produce-options:
8284
headers:
8385
- key: langstream-client-user-id
84-
valueFromAuthentication: login
86+
value-from-authentication: login
8587
- key: langstream-client-session-id
86-
valueFromParameters: sessionId
88+
value-from-parameters: sessionId
8789

8890
- id: consume-output-auth-github
8991
type: consume
@@ -94,10 +96,10 @@ gateways:
9496
provider: github
9597
configuration:
9698
clientId: "{{ secrets.github.client-id }}"
97-
consumeOptions:
99+
consume-options:
98100
filters:
99101
headers:
100102
- key: langstream-client-user-id
101-
valueFromAuthentication: login
103+
value-from-authentication: login
102104
- key: langstream-client-session-id
103-
valueFromParameters: sessionId
105+
value-from-parameters: sessionId

examples/applications/openai-completions/gateways.yaml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,21 @@ gateways:
2121
topic: input-topic
2222
parameters:
2323
- sessionId
24-
produceOptions:
24+
produce-options:
2525
headers:
2626
- key: langstream-client-session-id
27-
valueFromParameters: sessionId
27+
value-from-parameters: sessionId
2828

2929
- id: consume-output
3030
type: consume
3131
topic: output-topic
3232
parameters:
3333
- sessionId
34-
consumeOptions:
34+
consume-options:
3535
filters:
3636
headers:
3737
- key: langstream-client-session-id
38-
valueFromParameters: sessionId
38+
value-from-parameters: sessionId
3939

4040
- id: consume-history
4141
type: consume
@@ -48,10 +48,10 @@ gateways:
4848
provider: google
4949
configuration:
5050
clientId: "{{ secrets.google.client-id }}"
51-
produceOptions:
51+
produce-options:
5252
headers:
5353
- key: langstream-client-user-id
54-
valueFromAuthentication: subject
54+
value-from-authentication: subject
5555

5656
- id: consume-output-auth
5757
type: consume
@@ -60,9 +60,9 @@ gateways:
6060
provider: google
6161
configuration:
6262
clientId: "{{ secrets.google.client-id }}"
63-
consumeOptions:
63+
consume-options:
6464
filters:
6565
headers:
6666
- key: langstream-client-user-id
67-
valueFromAuthentication: subject
67+
value-from-authentication: subject
6868

langstream-api-gateway-auth/langstream-github-api-gateway-auth/src/main/java/ai/langstream/apigateway/auth/impl/github/GitHubAuthenticationProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public GatewayAuthenticationResult authenticate(GatewayRequestContext context) {
8181
log.info("X-OAuth-Client-Id: {}", responseClientId);
8282
log.info("Required: X-OAuth-Client-Id: {}", clientId);
8383

84-
Map<String, String> result = new ObjectMapper().readValue(body, Map.class);
84+
Map<String, String> result = mapper.readValue(body, Map.class);
8585
if (log.isDebugEnabled()) {
8686
response.headers().map().forEach((k, v) -> log.debug("Header {}: {}", k, v));
8787
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
Copyright DataStax, Inc.
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
18+
-->
19+
<project xmlns="http://maven.apache.org/POM/4.0.0"
20+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
21+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
22+
<parent>
23+
<artifactId>langstream-api-gateway-auth</artifactId>
24+
<groupId>ai.langstream</groupId>
25+
<version>0.0.16-SNAPSHOT</version>
26+
</parent>
27+
<modelVersion>4.0.0</modelVersion>
28+
29+
<artifactId>langstream-http-api-gateway-auth</artifactId>
30+
31+
<dependencies>
32+
<dependency>
33+
<groupId>ai.langstream</groupId>
34+
<artifactId>langstream-api</artifactId>
35+
<version>${project.version}</version>
36+
</dependency>
37+
<dependency>
38+
<groupId>org.slf4j</groupId>
39+
<artifactId>slf4j-api</artifactId>
40+
<scope>provided</scope>
41+
</dependency>
42+
<dependency>
43+
<groupId>com.fasterxml.jackson.core</groupId>
44+
<artifactId>jackson-databind</artifactId>
45+
</dependency>
46+
</dependencies>
47+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright DataStax, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package ai.langstream.apigateway.auth.impl.jwt.admin;
17+
18+
import ai.langstream.api.gateway.GatewayAuthenticationProvider;
19+
import ai.langstream.api.gateway.GatewayAuthenticationResult;
20+
import ai.langstream.api.gateway.GatewayRequestContext;
21+
import com.fasterxml.jackson.databind.ObjectMapper;
22+
import java.net.URI;
23+
import java.net.http.HttpClient;
24+
import java.net.http.HttpRequest;
25+
import java.net.http.HttpResponse;
26+
import java.time.Duration;
27+
import java.util.Map;
28+
import lombok.SneakyThrows;
29+
import lombok.extern.slf4j.Slf4j;
30+
31+
@Slf4j
32+
public class HttpAuthenticationProvider implements GatewayAuthenticationProvider {
33+
34+
private static final ObjectMapper mapper = new ObjectMapper();
35+
private HttpAuthenticationProviderConfiguration httpConfiguration;
36+
private HttpClient httpClient;
37+
38+
@Override
39+
public String type() {
40+
return "http";
41+
}
42+
43+
@Override
44+
@SneakyThrows
45+
public void initialize(Map<String, Object> configuration) {
46+
httpConfiguration =
47+
mapper.convertValue(configuration, HttpAuthenticationProviderConfiguration.class);
48+
httpClient =
49+
HttpClient.newBuilder()
50+
.connectTimeout(Duration.ofSeconds(30))
51+
.followRedirects(HttpClient.Redirect.ALWAYS)
52+
.build();
53+
}
54+
55+
@Override
56+
public GatewayAuthenticationResult authenticate(GatewayRequestContext context) {
57+
58+
final Map<String, String> placeholders = Map.of("tenant", context.tenant());
59+
final String uri = resolvePlaceholders(placeholders, httpConfiguration.getPathTemplate());
60+
final String url = httpConfiguration.getBaseUrl() + uri;
61+
62+
log.info("Authenticating admin with url: {}", url);
63+
64+
final HttpRequest.Builder builder = HttpRequest.newBuilder().uri(URI.create(url));
65+
66+
httpConfiguration.getHeaders().forEach(builder::header);
67+
builder.header("Authorization", "Bearer " + context.credentials());
68+
final HttpRequest request = builder.GET().build();
69+
70+
final HttpResponse<Void> response;
71+
try {
72+
response = httpClient.send(request, HttpResponse.BodyHandlers.discarding());
73+
} catch (InterruptedException e) {
74+
Thread.currentThread().interrupt();
75+
throw new RuntimeException(e);
76+
} catch (Throwable e) {
77+
return GatewayAuthenticationResult.authenticationFailed(e.getMessage());
78+
}
79+
if (httpConfiguration.getAcceptedStatuses().contains(response.statusCode())) {
80+
return GatewayAuthenticationResult.authenticationSuccessful(Map.of());
81+
}
82+
return GatewayAuthenticationResult.authenticationFailed(
83+
"Http authentication failed: " + response.statusCode());
84+
}
85+
86+
private static String resolvePlaceholders(Map<String, String> placeholders, String url) {
87+
for (Map.Entry<String, String> entry : placeholders.entrySet()) {
88+
url = url.replace("{" + entry.getKey() + "}", entry.getValue());
89+
}
90+
return url;
91+
}
92+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright DataStax, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package ai.langstream.apigateway.auth.impl.jwt.admin;
17+
18+
import com.fasterxml.jackson.annotation.JsonAlias;
19+
import java.util.HashMap;
20+
import java.util.List;
21+
import java.util.Map;
22+
import lombok.AllArgsConstructor;
23+
import lombok.Data;
24+
import lombok.NoArgsConstructor;
25+
26+
@Data
27+
@NoArgsConstructor
28+
@AllArgsConstructor
29+
public class HttpAuthenticationProviderConfiguration {
30+
31+
@JsonAlias({"base-url", "baseurl"})
32+
private String baseUrl;
33+
34+
@JsonAlias({"path-template", "pathtemplate"})
35+
private String pathTemplate;
36+
37+
private Map<String, String> headers = new HashMap<>();
38+
39+
@JsonAlias({"accepted-statuses", "acceptedstatuses"})
40+
private List<Integer> acceptedStatuses = List.of(200, 201);
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ai.langstream.apigateway.auth.impl.jwt.admin.HttpAuthenticationProvider

0 commit comments

Comments
 (0)