Skip to content

Commit fd696e8

Browse files
authored
Login with username and password (#89)
* Login with username and password * update doc
1 parent 8d7d40f commit fd696e8

File tree

7 files changed

+213
-1
lines changed

7 files changed

+213
-1
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Release Notes.
1010
* Bump up the API to support sharding_key.
1111
* Bump up the API to support version 0.9.
1212
* Support stage query on TopN.
13+
* Support auth with username and password.
1314

1415
0.8.0
1516
------------------

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ options are listed below,
4141
| forceReconnectionThreshold | Threshold of force gRPC reconnection if network issue is encountered | 1 |
4242
| forceTLS | Force use TLS for gRPC | false |
4343
| sslTrustCAPath | SSL: Trusted CA Path | |
44+
| sslCertChainPath | SSL: Cert Chain Path, BanyanDB server not support mTLS yet | |
45+
| sslKeyPath | SSL: Cert Key Path, BanyanDB server not support mTLS yet | |
46+
| username | Basic Auth: username of BanyanDB server | |
47+
| password | Basic Auth: password of BanyanDB server | |
4448

4549
## Schema Management
4650

src/main/java/org/apache/skywalking/banyandb/v1/client/BanyanDBClient.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.google.common.base.Strings;
2424
import com.google.protobuf.Timestamp;
2525
import io.grpc.Channel;
26+
import io.grpc.ClientInterceptors;
2627
import io.grpc.ManagedChannel;
2728
import io.grpc.Status;
2829
import io.grpc.stub.StreamObserver;
@@ -51,6 +52,7 @@
5152
import org.apache.skywalking.banyandb.measure.v1.MeasureServiceGrpc;
5253
import org.apache.skywalking.banyandb.stream.v1.BanyandbStream;
5354
import org.apache.skywalking.banyandb.stream.v1.StreamServiceGrpc;
55+
import org.apache.skywalking.banyandb.v1.client.auth.AuthInterceptor;
5456
import org.apache.skywalking.banyandb.v1.client.grpc.HandleExceptionsWith;
5557
import org.apache.skywalking.banyandb.v1.client.grpc.channel.ChannelManager;
5658
import org.apache.skywalking.banyandb.v1.client.grpc.channel.DefaultChannelFactory;
@@ -183,8 +185,18 @@ public void connect() throws IOException {
183185
for (int i = 0; i < this.targets.length; i++) {
184186
addresses[i] = URI.create("//" + this.targets[i]);
185187
}
186-
this.channel = ChannelManager.create(this.options.buildChannelManagerSettings(),
188+
Channel rawChannel = ChannelManager.create(this.options.buildChannelManagerSettings(),
187189
new DefaultChannelFactory(addresses, this.options));
190+
Channel interceptedChannel = rawChannel;
191+
// register auth interceptor
192+
String username = options.getUsername();
193+
String password = options.getPassword();
194+
if (!"".equals(username) && !"".equals(password)) {
195+
interceptedChannel = ClientInterceptors.intercept(rawChannel,
196+
new AuthInterceptor(username, password));
197+
}
198+
// Ensure this.channel is assigned only once.
199+
this.channel = interceptedChannel;
188200
streamServiceBlockingStub = StreamServiceGrpc.newBlockingStub(this.channel);
189201
measureServiceBlockingStub = MeasureServiceGrpc.newBlockingStub(this.channel);
190202
streamServiceStub = StreamServiceGrpc.newStub(this.channel);

src/main/java/org/apache/skywalking/banyandb/v1/client/Options.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ public class Options {
6363
* SSL: Cert Key Path, BanyanDB server not support mTLS yet
6464
*/
6565
private String sslKeyPath = "";
66+
/**
67+
* Basic Auth: username of BanyanDB server
68+
*/
69+
private String username = "";
70+
/**
71+
* Basic Auth: password of BanyanDB server
72+
*/
73+
private String password = "";
6674

6775
public Options() {
6876
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
package org.apache.skywalking.banyandb.v1.client.auth;
20+
21+
import io.grpc.CallOptions;
22+
import io.grpc.Channel;
23+
import io.grpc.ClientCall;
24+
import io.grpc.ClientInterceptor;
25+
import io.grpc.ForwardingClientCall;
26+
import io.grpc.Metadata;
27+
import io.grpc.MethodDescriptor;
28+
29+
public class AuthInterceptor implements ClientInterceptor {
30+
private final String username;
31+
private final String password;
32+
33+
private static final Metadata.Key<String> USERNAME_KEY =
34+
Metadata.Key.of("username", Metadata.ASCII_STRING_MARSHALLER);
35+
private static final Metadata.Key<String> PASSWORD_KEY =
36+
Metadata.Key.of("password", Metadata.ASCII_STRING_MARSHALLER);
37+
38+
public AuthInterceptor(String username, String password) {
39+
this.username = username;
40+
this.password = password;
41+
}
42+
43+
@Override
44+
public <REQ_T, RESP_T> ClientCall<REQ_T, RESP_T> interceptCall(
45+
MethodDescriptor<REQ_T, RESP_T> method,
46+
CallOptions callOptions,
47+
Channel next) {
48+
49+
return new ForwardingClientCall.SimpleForwardingClientCall<REQ_T, RESP_T>(
50+
next.newCall(method, callOptions)) {
51+
@Override
52+
public void start(Listener<RESP_T> responseListener, Metadata headers) {
53+
headers.put(USERNAME_KEY, username);
54+
headers.put(PASSWORD_KEY, password);
55+
56+
super.start(responseListener, headers);
57+
}
58+
};
59+
}
60+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
package org.apache.skywalking.banyandb.v1.client;
20+
21+
import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
22+
import org.apache.skywalking.banyandb.v1.client.grpc.exception.UnauthenticatedException;
23+
import org.junit.Assert;
24+
import org.junit.Rule;
25+
import org.junit.Test;
26+
import org.testcontainers.containers.GenericContainer;
27+
import org.testcontainers.containers.wait.strategy.Wait;
28+
import org.testcontainers.utility.DockerImageName;
29+
import org.testcontainers.utility.MountableFile;
30+
31+
import java.io.IOException;
32+
import java.nio.file.Files;
33+
import java.nio.file.Path;
34+
import java.nio.file.Paths;
35+
import java.nio.file.attribute.PosixFilePermissions;
36+
import java.util.List;
37+
import java.util.concurrent.TimeUnit;
38+
39+
import static org.awaitility.Awaitility.await;
40+
import static org.junit.Assert.assertThrows;
41+
42+
public class ITBanyanDBAuthTest {
43+
private static final String REGISTRY = "ghcr.io";
44+
private static final String IMAGE_NAME = "apache/skywalking-banyandb";
45+
private static final String TAG = "42ec9df7457868926eb80157b36355d94fcd6bba";
46+
47+
private static final String IMAGE = REGISTRY + "/" + IMAGE_NAME + ":" + TAG;
48+
49+
protected static final int GRPC_PORT = 17912;
50+
protected static final int HTTP_PORT = 17913;
51+
52+
@Rule
53+
public GenericContainer<?> banyanDB;
54+
55+
public ITBanyanDBAuthTest() throws Exception {
56+
// Step 1: prepare config file with 0600 permissions
57+
Path tempConfigPath = Files.createTempFile("bydb_server_config", ".yaml");
58+
Files.write(tempConfigPath, Files.readAllBytes(
59+
Paths.get(getClass().getClassLoader().getResource("config.yaml").toURI()))
60+
);
61+
Files.setPosixFilePermissions(tempConfigPath, PosixFilePermissions.fromString("rw-------"));
62+
63+
// Step 2: create container
64+
banyanDB = new GenericContainer<>(DockerImageName.parse(IMAGE))
65+
.withCopyFileToContainer(
66+
MountableFile.forHostPath(tempConfigPath),
67+
"/tmp/bydb_server_config.yaml"
68+
)
69+
.withCommand("standalone",
70+
"--auth-config-file", "/tmp/bydb_server_config.yaml"
71+
)
72+
.withExposedPorts(GRPC_PORT, HTTP_PORT)
73+
.waitingFor(Wait.forHttp("/api/healthz").forPort(HTTP_PORT));
74+
}
75+
76+
@Test
77+
public void testAuthWithCorrect() throws IOException {
78+
BanyanDBClient client = createClient("admin", "123456");
79+
client.connect();
80+
await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> {
81+
// get api version
82+
client.getAPIVersion();
83+
// list all groups
84+
List<BanyandbCommon.Group> groupList = client.findGroups();
85+
Assert.assertEquals(0, groupList.size());
86+
});
87+
client.close();
88+
}
89+
90+
@Test
91+
public void testAuthWithWrong() throws IOException {
92+
BanyanDBClient client = createClient("admin", "123456" + "wrong");
93+
client.connect();
94+
await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> {
95+
assertThrows(UnauthenticatedException.class, client::getAPIVersion);
96+
});
97+
client.close();
98+
}
99+
100+
private BanyanDBClient createClient(String username, String password) {
101+
Options options = new Options();
102+
options.setUsername(username);
103+
options.setPassword(password);
104+
String url = String.format("%s:%d", banyanDB.getHost(), banyanDB.getMappedPort(GRPC_PORT));
105+
return new BanyanDBClient(new String[]{url}, options);
106+
}
107+
}

src/test/resources/config.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one or more
2+
# contributor license agreements. See the NOTICE file distributed with
3+
# this work for additional information regarding copyright ownership.
4+
# The ASF licenses this file to You under the Apache License, Version 2.0
5+
# (the "License"); you may not use this file except in compliance with
6+
# the License. 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+
users:
17+
- username: admin
18+
password: 123456
19+
- username: test
20+
password: 123456

0 commit comments

Comments
 (0)