Skip to content

Commit 5748abe

Browse files
committed
Merge branch '252-sync-client-add-new-jwt-and-multiple-credentials' into dev
2 parents 621c6a9 + 2729b0b commit 5748abe

13 files changed

+438
-41
lines changed

objectbox-java/src/main/java/io/objectbox/sync/CredentialsType.java

+16
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,21 @@ private CredentialsType() { }
5454
* Generic credential type suitable for ObjectBox admin (and possibly others in the future)
5555
*/
5656
public static final int UserPassword = 6;
57+
/**
58+
* JSON Web Token (JWT): an ID token that typically provides identity information about the authenticated user.
59+
*/
60+
public static final int JwtId = 7;
61+
/**
62+
* JSON Web Token (JWT): an access token that is used to access resources.
63+
*/
64+
public static final int JwtAccess = 8;
65+
/**
66+
* JSON Web Token (JWT): a refresh token that is used to obtain a new access token.
67+
*/
68+
public static final int JwtRefresh = 9;
69+
/**
70+
* JSON Web Token (JWT): a token that is neither an ID, access, nor refresh token.
71+
*/
72+
public static final int JwtCustom = 10;
5773
}
5874

objectbox-java/src/main/java/io/objectbox/sync/Sync.java

+30
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,19 @@ public static SyncBuilder client(BoxStore boxStore, String url, SyncCredentials
6363
return new SyncBuilder(boxStore, url, credentials);
6464
}
6565

66+
/**
67+
* Starts building a {@link SyncClient}. Once done, complete with {@link SyncBuilder#build() build()}.
68+
*
69+
* @param boxStore The {@link BoxStore} the client should use.
70+
* @param url The URL of the Sync server on which the Sync protocol is exposed. This is typically a WebSockets URL
71+
* starting with {@code ws://} or {@code wss://} (for encrypted connections), for example
72+
* {@code ws://127.0.0.1:9999}.
73+
* @param multipleCredentials An array of {@link SyncCredentials} to be used to authenticate the user.
74+
*/
75+
public static SyncBuilder client(BoxStore boxStore, String url, SyncCredentials[] multipleCredentials) {
76+
return new SyncBuilder(boxStore, url, multipleCredentials);
77+
}
78+
6679
/**
6780
* Starts building a {@link SyncServer}. Once done, complete with {@link SyncServerBuilder#build() build()}.
6881
* <p>
@@ -80,6 +93,23 @@ public static SyncServerBuilder server(BoxStore boxStore, String url, SyncCreden
8093
return new SyncServerBuilder(boxStore, url, authenticatorCredentials);
8194
}
8295

96+
/**
97+
* Starts building a {@link SyncServer}. Once done, complete with {@link SyncServerBuilder#build() build()}.
98+
* <p>
99+
* Note: when also using Admin, make sure it is started before the server.
100+
*
101+
* @param boxStore The {@link BoxStore} the server should use.
102+
* @param url The URL of the Sync server on which the Sync protocol is exposed. This is typically a WebSockets URL
103+
* starting with {@code ws://} or {@code wss://} (for encrypted connections), for example
104+
* {@code ws://0.0.0.0:9999}.
105+
* @param multipleAuthenticatorCredentials An authentication method available to Sync clients and peers. Additional
106+
* authenticator credentials can be supplied using the returned builder. For the embedded server, currently only
107+
* {@link SyncCredentials#sharedSecret} and {@link SyncCredentials#none} are supported.
108+
*/
109+
public static SyncServerBuilder server(BoxStore boxStore, String url, SyncCredentials[] multipleAuthenticatorCredentials) {
110+
return new SyncServerBuilder(boxStore, url, multipleAuthenticatorCredentials);
111+
}
112+
83113
/**
84114
* Starts building a {@link SyncHybrid}, a client/server hybrid typically used for embedded cluster setups.
85115
* <p>

objectbox-java/src/main/java/io/objectbox/sync/SyncBuilder.java

+33-6
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@
1717
package io.objectbox.sync;
1818

1919
import java.util.Arrays;
20+
import java.util.Collections;
21+
import java.util.List;
2022

2123
import javax.annotation.Nullable;
2224

2325
import io.objectbox.BoxStore;
2426
import io.objectbox.annotation.apihint.Internal;
27+
import io.objectbox.exception.FeatureNotAvailableException;
2528
import io.objectbox.sync.internal.Platform;
2629
import io.objectbox.sync.listener.SyncChangeListener;
2730
import io.objectbox.sync.listener.SyncCompletedListener;
@@ -40,7 +43,7 @@ public final class SyncBuilder {
4043
final Platform platform;
4144
final BoxStore boxStore;
4245
String url;
43-
final SyncCredentials credentials;
46+
final List<SyncCredentials> credentials;
4447

4548
@Nullable SyncLoginListener loginListener;
4649
@Nullable SyncCompletedListener completedListener;
@@ -83,18 +86,35 @@ public enum RequestUpdatesMode {
8386
AUTO_NO_PUSHES
8487
}
8588

89+
private static void checkSyncFeatureAvailable() {
90+
if (!BoxStore.isSyncAvailable()) {
91+
throw new FeatureNotAvailableException(
92+
"This library does not include ObjectBox Sync. " +
93+
"Please visit https://objectbox.io/sync/ for options.");
94+
}
95+
}
96+
97+
8698
@Internal
8799
public SyncBuilder(BoxStore boxStore, SyncCredentials credentials) {
88100
checkNotNull(boxStore, "BoxStore is required.");
89101
checkNotNull(credentials, "Sync credentials are required.");
90-
if (!BoxStore.isSyncAvailable()) {
91-
throw new IllegalStateException(
92-
"This library does not include ObjectBox Sync. " +
93-
"Please visit https://objectbox.io/sync/ for options.");
102+
checkSyncFeatureAvailable();
103+
this.platform = Platform.findPlatform();
104+
this.boxStore = boxStore;
105+
this.credentials = Collections.singletonList(credentials);
106+
}
107+
108+
@Internal
109+
public SyncBuilder(BoxStore boxStore, SyncCredentials[] multipleCredentials) {
110+
checkNotNull(boxStore, "BoxStore is required.");
111+
if (multipleCredentials.length == 0) {
112+
throw new IllegalArgumentException("At least one Sync credential is required.");
94113
}
114+
checkSyncFeatureAvailable();
95115
this.platform = Platform.findPlatform();
96116
this.boxStore = boxStore;
97-
this.credentials = credentials;
117+
this.credentials = Arrays.asList(multipleCredentials);
98118
}
99119

100120
@Internal
@@ -104,6 +124,13 @@ public SyncBuilder(BoxStore boxStore, String url, SyncCredentials credentials) {
104124
this.url = url;
105125
}
106126

127+
@Internal
128+
public SyncBuilder(BoxStore boxStore, String url, SyncCredentials[] multipleCredentials) {
129+
this(boxStore, multipleCredentials);
130+
checkNotNull(url, "Sync server URL is required.");
131+
this.url = url;
132+
}
133+
107134
/**
108135
* Allows internal code to set the Sync server URL after creating this builder.
109136
*/

objectbox-java/src/main/java/io/objectbox/sync/SyncClient.java

+6
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,12 @@ public interface SyncClient extends Closeable {
133133
*/
134134
void setLoginCredentials(SyncCredentials credentials);
135135

136+
/**
137+
* Updates the login credentials. This should not be required during regular use.
138+
* It allows passing login credentials that the client can use to authenticate with the server.
139+
*/
140+
void setLoginCredentials(SyncCredentials[] multipleCredentials);
141+
136142
/**
137143
* Waits until the sync client receives a response to its first (connection and) login attempt
138144
* or until the given time has expired.

objectbox-java/src/main/java/io/objectbox/sync/SyncClientImpl.java

+31-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,13 @@ public final class SyncClientImpl implements SyncClient {
9696
this.internalListener = new InternalSyncClientListener();
9797
nativeSetListener(handle, internalListener);
9898

99-
setLoginCredentials(builder.credentials);
99+
if (builder.credentials.size() == 1) {
100+
setLoginCredentials(builder.credentials.get(0));
101+
} else if (builder.credentials.size() > 1) {
102+
setLoginCredentials(builder.credentials.toArray(new SyncCredentials[0]));
103+
} else {
104+
throw new IllegalArgumentException("No credentials provided");
105+
}
100106

101107
// If created successfully, let store keep a reference so the caller does not have to.
102108
InternalAccess.setSyncClient(builder.boxStore, this);
@@ -196,6 +202,26 @@ public void setLoginCredentials(SyncCredentials credentials) {
196202
}
197203
}
198204

205+
@Override
206+
public void setLoginCredentials(SyncCredentials[] multipleCredentials) {
207+
for (int i = 0; i < multipleCredentials.length; i++) {
208+
SyncCredentials credentials = multipleCredentials[i];
209+
boolean isLast = i == (multipleCredentials.length - 1);
210+
if (credentials instanceof SyncCredentialsToken) {
211+
SyncCredentialsToken credToken = (SyncCredentialsToken) credentials;
212+
nativeAddLoginCredentials(getHandle(), credToken.getTypeId(), credToken.getTokenBytes(), isLast);
213+
credToken.clear(); // Clear immediately, not needed anymore.
214+
} else if (credentials instanceof SyncCredentialsUserPassword) {
215+
SyncCredentialsUserPassword credUserPassword = (SyncCredentialsUserPassword) credentials;
216+
nativeAddLoginCredentialsUserPassword(getHandle(), credUserPassword.getTypeId(), credUserPassword.getUsername(),
217+
credUserPassword.getPassword(), isLast);
218+
} else {
219+
throw new IllegalArgumentException("credentials is not a supported type");
220+
}
221+
}
222+
}
223+
224+
199225
@Override
200226
public boolean awaitFirstLogin(long millisToWait) {
201227
if (!started) {
@@ -322,6 +348,10 @@ public ObjectsMessageBuilder startObjectsMessage(long flags, @Nullable String to
322348

323349
private native void nativeSetLoginInfoUserPassword(long handle, long credentialsType, String username, String password);
324350

351+
private native void nativeAddLoginCredentials(long handle, long credentialsType, @Nullable byte[] credentials, boolean complete);
352+
353+
private native void nativeAddLoginCredentialsUserPassword(long handle, long credentialsType, String username, String password, boolean complete);
354+
325355
private native void nativeSetListener(long handle, @Nullable InternalSyncClientListener listener);
326356

327357
private native void nativeSetSyncChangesListener(long handle, @Nullable SyncChangeListener advancedListener);

objectbox-java/src/main/java/io/objectbox/sync/SyncCredentials.java

+44-2
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,46 @@ public static SyncCredentials google(String idToken) {
4848
return new SyncCredentialsToken(CredentialsType.GOOGLE, idToken);
4949
}
5050

51+
/**
52+
* ObjectBox admin users (username/password)
53+
*/
54+
public static SyncCredentials obxAdminUser(String user, String password) {
55+
return new SyncCredentialsUserPassword(CredentialsType.OBX_ADMIN_USER, user, password);
56+
}
57+
58+
/**
59+
* Generic credential type suitable for ObjectBox admin (and possibly others in the future)
60+
*/
5161
public static SyncCredentials userAndPassword(String user, String password) {
52-
return new SyncCredentialsUserPassword(user, password);
62+
return new SyncCredentialsUserPassword(CredentialsType.USER_PASSWORD, user, password);
63+
}
64+
65+
/**
66+
* JSON Web Token (JWT): an ID token that typically provides identity information about the authenticated user.
67+
*/
68+
public static SyncCredentials jwtIdToken(String jwtIdToken) {
69+
return new SyncCredentialsToken(CredentialsType.JWT_ID_TOKEN, jwtIdToken);
70+
}
71+
72+
/**
73+
* JSON Web Token (JWT): an access token that is used to access resources.
74+
*/
75+
public static SyncCredentials jwtAccessToken(String jwtAccessToken) {
76+
return new SyncCredentialsToken(CredentialsType.JWT_ACCESS_TOKEN, jwtAccessToken);
77+
}
78+
79+
/**
80+
* JSON Web Token (JWT): a refresh token that is used to obtain a new access token.
81+
*/
82+
public static SyncCredentials jwtRefreshToken(String jwtRefreshToken) {
83+
return new SyncCredentialsToken(CredentialsType.JWT_REFRESH_TOKEN, jwtRefreshToken);
84+
}
85+
86+
/**
87+
* JSON Web Token (JWT): a token that is neither an ID, access, nor refresh token.
88+
*/
89+
public static SyncCredentials jwtCustomToken(String jwtCustomToken) {
90+
return new SyncCredentialsToken(CredentialsType.JWT_CUSTOM_TOKEN, jwtCustomToken);
5391
}
5492

5593
/**
@@ -65,7 +103,11 @@ public enum CredentialsType {
65103
GOOGLE(io.objectbox.sync.CredentialsType.GoogleAuth),
66104
SHARED_SECRET_SIPPED(io.objectbox.sync.CredentialsType.SharedSecretSipped),
67105
OBX_ADMIN_USER(io.objectbox.sync.CredentialsType.ObxAdminUser),
68-
USER_PASSWORD(io.objectbox.sync.CredentialsType.UserPassword);
106+
USER_PASSWORD(io.objectbox.sync.CredentialsType.UserPassword),
107+
JWT_ID_TOKEN(io.objectbox.sync.CredentialsType.JwtId),
108+
JWT_ACCESS_TOKEN(io.objectbox.sync.CredentialsType.JwtAccess),
109+
JWT_REFRESH_TOKEN(io.objectbox.sync.CredentialsType.JwtRefresh),
110+
JWT_CUSTOM_TOKEN(io.objectbox.sync.CredentialsType.JwtCustom);
69111

70112
public final long id;
71113

objectbox-java/src/main/java/io/objectbox/sync/SyncCredentialsUserPassword.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ public final class SyncCredentialsUserPassword extends SyncCredentials {
2828
private final String username;
2929
private final String password;
3030

31-
SyncCredentialsUserPassword(String username, String password) {
32-
super(CredentialsType.USER_PASSWORD);
31+
SyncCredentialsUserPassword(CredentialsType type, String username, String password) {
32+
super(type);
3333
this.username = username;
3434
this.password = password;
3535
}
@@ -44,6 +44,6 @@ public String getPassword() {
4444

4545
@Override
4646
SyncCredentials createClone() {
47-
return new SyncCredentialsUserPassword(this.username, this.password);
47+
return new SyncCredentialsUserPassword(getType(), this.username, this.password);
4848
}
4949
}

0 commit comments

Comments
 (0)