Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Release Notes.
* Bump up the API to support sharding_key.
* Bump up the API to support version 0.9.
* Support stage query on TopN.
* Support auth with username and password.

0.8.0
------------------
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ options are listed below,
| forceReconnectionThreshold | Threshold of force gRPC reconnection if network issue is encountered | 1 |
| forceTLS | Force use TLS for gRPC | false |
| sslTrustCAPath | SSL: Trusted CA Path | |
| sslCertChainPath | SSL: Cert Chain Path, BanyanDB server not support mTLS yet | |
| sslKeyPath | SSL: Cert Key Path, BanyanDB server not support mTLS yet | |
| username | Basic Auth: username of BanyanDB server | |
| password | Basic Auth: password of BanyanDB server | |

## Schema Management

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.google.common.base.Strings;
import com.google.protobuf.Timestamp;
import io.grpc.Channel;
import io.grpc.ClientInterceptors;
import io.grpc.ManagedChannel;
import io.grpc.Status;
import io.grpc.stub.StreamObserver;
Expand Down Expand Up @@ -51,6 +52,7 @@
import org.apache.skywalking.banyandb.measure.v1.MeasureServiceGrpc;
import org.apache.skywalking.banyandb.stream.v1.BanyandbStream;
import org.apache.skywalking.banyandb.stream.v1.StreamServiceGrpc;
import org.apache.skywalking.banyandb.v1.client.auth.AuthInterceptor;
import org.apache.skywalking.banyandb.v1.client.grpc.HandleExceptionsWith;
import org.apache.skywalking.banyandb.v1.client.grpc.channel.ChannelManager;
import org.apache.skywalking.banyandb.v1.client.grpc.channel.DefaultChannelFactory;
Expand Down Expand Up @@ -183,8 +185,18 @@ public void connect() throws IOException {
for (int i = 0; i < this.targets.length; i++) {
addresses[i] = URI.create("//" + this.targets[i]);
}
this.channel = ChannelManager.create(this.options.buildChannelManagerSettings(),
Channel rawChannel = ChannelManager.create(this.options.buildChannelManagerSettings(),
new DefaultChannelFactory(addresses, this.options));
Channel interceptedChannel = rawChannel;
// register auth interceptor
String username = options.getUsername();
String password = options.getPassword();
if (!"".equals(username) && !"".equals(password)) {
interceptedChannel = ClientInterceptors.intercept(rawChannel,
new AuthInterceptor(username, password));
}
// Ensure this.channel is assigned only once.
this.channel = interceptedChannel;
streamServiceBlockingStub = StreamServiceGrpc.newBlockingStub(this.channel);
measureServiceBlockingStub = MeasureServiceGrpc.newBlockingStub(this.channel);
streamServiceStub = StreamServiceGrpc.newStub(this.channel);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ public class Options {
* SSL: Cert Key Path, BanyanDB server not support mTLS yet
*/
private String sslKeyPath = "";
/**
* Basic Auth: username of BanyanDB server
*/
private String username = "";
/**
* Basic Auth: password of BanyanDB server
*/
private String password = "";

public Options() {
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* 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.skywalking.banyandb.v1.client.auth;

import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientCall;
import io.grpc.ClientInterceptor;
import io.grpc.ForwardingClientCall;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;

public class AuthInterceptor implements ClientInterceptor {
private final String username;
private final String password;

private static final Metadata.Key<String> USERNAME_KEY =
Metadata.Key.of("username", Metadata.ASCII_STRING_MARSHALLER);
private static final Metadata.Key<String> PASSWORD_KEY =
Metadata.Key.of("password", Metadata.ASCII_STRING_MARSHALLER);

public AuthInterceptor(String username, String password) {
this.username = username;
this.password = password;
}

@Override
public <REQ_T, RESP_T> ClientCall<REQ_T, RESP_T> interceptCall(
MethodDescriptor<REQ_T, RESP_T> method,
CallOptions callOptions,
Channel next) {

return new ForwardingClientCall.SimpleForwardingClientCall<REQ_T, RESP_T>(
next.newCall(method, callOptions)) {
@Override
public void start(Listener<RESP_T> responseListener, Metadata headers) {
headers.put(USERNAME_KEY, username);
headers.put(PASSWORD_KEY, password);

super.start(responseListener, headers);
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* 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.skywalking.banyandb.v1.client;

import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
import org.apache.skywalking.banyandb.v1.client.grpc.exception.UnauthenticatedException;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.DockerImageName;
import org.testcontainers.utility.MountableFile;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static org.awaitility.Awaitility.await;
import static org.junit.Assert.assertThrows;

public class ITBanyanDBAuthTest {
private static final String REGISTRY = "ghcr.io";
private static final String IMAGE_NAME = "apache/skywalking-banyandb";
private static final String TAG = "42ec9df7457868926eb80157b36355d94fcd6bba";

private static final String IMAGE = REGISTRY + "/" + IMAGE_NAME + ":" + TAG;

protected static final int GRPC_PORT = 17912;
protected static final int HTTP_PORT = 17913;

@Rule
public GenericContainer<?> banyanDB;

public ITBanyanDBAuthTest() throws Exception {
// Step 1: prepare config file with 0600 permissions
Path tempConfigPath = Files.createTempFile("bydb_server_config", ".yaml");
Files.write(tempConfigPath, Files.readAllBytes(
Paths.get(getClass().getClassLoader().getResource("config.yaml").toURI()))
);
Files.setPosixFilePermissions(tempConfigPath, PosixFilePermissions.fromString("rw-------"));

// Step 2: create container
banyanDB = new GenericContainer<>(DockerImageName.parse(IMAGE))
.withCopyFileToContainer(
MountableFile.forHostPath(tempConfigPath),
"/tmp/bydb_server_config.yaml"
)
.withCommand("standalone",
"--auth-config-file", "/tmp/bydb_server_config.yaml"
)
.withExposedPorts(GRPC_PORT, HTTP_PORT)
.waitingFor(Wait.forHttp("/api/healthz").forPort(HTTP_PORT));
}

@Test
public void testAuthWithCorrect() throws IOException {
BanyanDBClient client = createClient("admin", "123456");
client.connect();
await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> {
// get api version
client.getAPIVersion();
// list all groups
List<BanyandbCommon.Group> groupList = client.findGroups();
Assert.assertEquals(0, groupList.size());
});
client.close();
}

@Test
public void testAuthWithWrong() throws IOException {
BanyanDBClient client = createClient("admin", "123456" + "wrong");
client.connect();
await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> {
assertThrows(UnauthenticatedException.class, client::getAPIVersion);
});
client.close();
}

private BanyanDBClient createClient(String username, String password) {
Options options = new Options();
options.setUsername(username);
options.setPassword(password);
String url = String.format("%s:%d", banyanDB.getHost(), banyanDB.getMappedPort(GRPC_PORT));
return new BanyanDBClient(new String[]{url}, options);
}
}
20 changes: 20 additions & 0 deletions src/test/resources/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# 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.

users:
- username: admin
password: 123456
- username: test
password: 123456
Loading