diff --git a/.pubnub.yml b/.pubnub.yml
index e5962fe11..a539941bd 100644
--- a/.pubnub.yml
+++ b/.pubnub.yml
@@ -1,9 +1,9 @@
name: java
-version: 5.0.0
+version: 5.1.0
schema: 1
scm: github.com/pubnub/java
files:
- - build/libs/pubnub-gson-5.0.0-all.jar
+ - build/libs/pubnub-gson-5.1.0-all.jar
sdks:
-
type: library
@@ -234,6 +234,13 @@ sdks:
is-required: Required
changelog:
+ - version: v5.1.0
+ date: 2021-05-20
+ changes:
+ - type: feature
+ text: "Method grantToken has beed added. It allows generation of signed token with permissions for channels and channel groups."
+ - type: bug
+ text: "UUID is now exposed as PNMembership field which make is accessible from PNMembershipResult argument of SubscribeCallback.membership() method."
- version: v5.0.0
date: 2021-05-12
changes:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 84e3d4198..43c0df323 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,14 @@
+## [v5.1.0](https://github.com/pubnub/java/releases/tag/v5.1.0)
+May-20-2021
+
+[Full Changelog](https://github.com/pubnub/java/compare/v5.0.0...v5.1.0)
+
+- 🌟️ Method grantToken has beed added. It allows generation of signed token with permissions for channels and channel groups.
+- 🐛 UUID is now exposed as PNMembership field which make is accessible from PNMembershipResult argument of SubscribeCallback.membership() method.
+
## [v5.0.0](https://github.com/pubnub/java/releases/tag/v5.0.0)
May-12-2021
-[Full Changelog](https://github.com/pubnub/java/compare/v4.36.0...v5.0.0)
-
- 🌟️ Now random initialisation vector used when encryption enabled is now default behaviour.
- 🐛 There were some non daemon threads running in background preventing VM from exiting. Now they are daemon threads.
diff --git a/README.md b/README.md
index 3246be6a6..93dab668e 100644
--- a/README.md
+++ b/README.md
@@ -23,13 +23,13 @@ You will need the publish and subscribe keys to authenticate your app. Get your
com.pubnub
pubnub-gson
- 5.0.0
+ 5.1.0
```
* for Gradle, add the following dependency in your `gradle.build`:
```groovy
- compile group: 'com.pubnub', name: 'pubnub-gson', version: '5.0.0'
+ compile group: 'com.pubnub', name: 'pubnub-gson', version: '5.1.0'
```
2. Configure your keys:
diff --git a/build.gradle b/build.gradle
index 46f64b819..d43f870d0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -11,7 +11,7 @@ plugins {
}
group = 'com.pubnub'
-version = '5.0.0'
+version = '5.1.0'
description = """"""
diff --git a/src/integrationTest/java/com/pubnub/api/integration/objects/memberships/CustomMetadataInMembershipPropagationIT.java b/src/integrationTest/java/com/pubnub/api/integration/objects/memberships/CustomMetadataInMembershipPropagationIT.java
index 40645996b..a3151aa94 100644
--- a/src/integrationTest/java/com/pubnub/api/integration/objects/memberships/CustomMetadataInMembershipPropagationIT.java
+++ b/src/integrationTest/java/com/pubnub/api/integration/objects/memberships/CustomMetadataInMembershipPropagationIT.java
@@ -1,21 +1,29 @@
package com.pubnub.api.integration.objects.memberships;
+import com.pubnub.api.PubNub;
import com.pubnub.api.PubNubException;
+import com.pubnub.api.integration.managers.subscription.SubscribeCallbackAdapter;
import com.pubnub.api.integration.objects.ObjectsApiBaseIT;
import com.pubnub.api.models.consumer.objects_api.channel.PNSetChannelMetadataResult;
import com.pubnub.api.models.consumer.objects_api.membership.PNChannelMembership;
import com.pubnub.api.models.consumer.objects_api.membership.PNGetMembershipsResult;
+import com.pubnub.api.models.consumer.objects_api.membership.PNMembershipResult;
import com.pubnub.api.models.consumer.objects_api.membership.PNSetMembershipResult;
+import org.awaitility.core.ThrowingRunnable;
import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.TimeUnit;
import static com.pubnub.api.endpoints.objects_api.utils.Include.PNChannelDetailsLevel.CHANNEL;
import static com.pubnub.api.endpoints.objects_api.utils.Include.PNChannelDetailsLevel.CHANNEL_WITH_CUSTOM;
+import static org.awaitility.Awaitility.await;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasProperty;
@@ -39,6 +47,22 @@ public class CustomMetadataInMembershipPropagationIT extends ObjectsApiBaseIT {
private PNSetMembershipResult setMembershipResult;
+ private CopyOnWriteArrayList pnMembershipResults = new CopyOnWriteArrayList<>();
+
+ @Before
+ public void setCallbackListener() {
+ pubNubUnderTest.addListener(new SubscribeCallbackAdapter() {
+ @Override
+ public void membership(final PubNub pubnub, final PNMembershipResult pnMembershipResult) {
+ pnMembershipResults.add(pnMembershipResult);
+ }
+ });
+
+ pubNubUnderTest.subscribe()
+ .channels(Collections.singletonList(testChannelMetadataId))
+ .execute();
+ }
+
@Test
public void setMembershipCustomHappyPath() throws PubNubException {
final String testChannelName = "The Name of the Channel";
@@ -86,6 +110,14 @@ public void setMembershipCustomHappyPath() throws PubNubException {
hasProperty("name", is(testChannelName)),
hasProperty("description", is(testDescription)),
hasProperty("custom", nullValue()))))))));
+
+ await().atMost(1, TimeUnit.SECONDS).untilAsserted(new ThrowingRunnable() {
+ @Override
+ public void run() throws Throwable {
+ assertThat(pnMembershipResults, hasItem(
+ hasProperty("data", hasProperty("uuid", is(pubNubUnderTest.getConfiguration().getUuid())))));
+ }
+ });
}
@After
diff --git a/src/integrationTest/java/com/pubnub/api/integration/pam/GrantTokenIT.java b/src/integrationTest/java/com/pubnub/api/integration/pam/GrantTokenIT.java
new file mode 100644
index 000000000..d730eaa61
--- /dev/null
+++ b/src/integrationTest/java/com/pubnub/api/integration/pam/GrantTokenIT.java
@@ -0,0 +1,53 @@
+package com.pubnub.api.integration.pam;
+
+import com.pubnub.api.PNConfiguration;
+import com.pubnub.api.PubNub;
+import com.pubnub.api.PubNubException;
+import com.pubnub.api.integration.util.BaseIntegrationTest;
+import com.pubnub.api.models.consumer.access_manager.v3.ChannelGrant;
+import com.pubnub.api.models.consumer.access_manager.v3.ChannelGroupGrant;
+import com.pubnub.api.models.consumer.access_manager.v3.PNGrantTokenResult;
+import com.pubnub.api.models.consumer.access_manager.v3.PNToken;
+import org.junit.Test;
+
+import java.util.Arrays;
+import static org.junit.Assert.assertEquals;
+
+
+public class GrantTokenIT extends BaseIntegrationTest {
+ private final PubNub pubNubUnderTest = getServer();
+
+ @Test
+ public void happyPath() throws PubNubException {
+ //given
+ final int expectedTTL = 1337;
+ final String expectedChannelResourceName = "channelResource";
+ final String expectedChannelPattern = "channel.*";
+ final String expectedChannelGroupResourceId = "channelGroup";
+ final String expectedChannelGroupPattern = "channelGroup.*";
+
+ //when
+ final PNGrantTokenResult grantTokenResponse = pubNubUnderTest
+ .grantToken()
+ .ttl(expectedTTL)
+ .channels(Arrays.asList(ChannelGrant.name(expectedChannelResourceName).delete(),
+ ChannelGrant.pattern(expectedChannelPattern).write()))
+ .channelGroups(Arrays.asList(ChannelGroupGrant.id(expectedChannelGroupResourceId).read(),
+ ChannelGroupGrant.pattern(expectedChannelGroupPattern).manage()))
+ .sync();
+
+ final PNToken pnToken = pubNubUnderTest.parseToken(grantTokenResponse.getToken());
+
+ //then
+ assertEquals(expectedTTL, pnToken.getTtl());
+ assertEquals(new PNToken.PNResourcePermissions(false, false, false, false, true),
+ pnToken.getResources().getChannels().get(expectedChannelResourceName));
+ assertEquals(new PNToken.PNResourcePermissions(false, true, false, false, false),
+ pnToken.getResources().getChannelGroups().get(expectedChannelGroupResourceId));
+ assertEquals(new PNToken.PNResourcePermissions(false, false, true, false, false),
+ pnToken.getPatterns().getChannels().get(expectedChannelPattern));
+ assertEquals(new PNToken.PNResourcePermissions(false, false, false, true, false),
+ pnToken.getPatterns().getChannelGroups().get(expectedChannelGroupPattern));
+ }
+
+}
diff --git a/src/main/java/com/pubnub/api/PubNub.java b/src/main/java/com/pubnub/api/PubNub.java
index da48acaba..1e3f80e64 100644
--- a/src/main/java/com/pubnub/api/PubNub.java
+++ b/src/main/java/com/pubnub/api/PubNub.java
@@ -11,6 +11,7 @@
import com.pubnub.api.endpoints.MessageCounts;
import com.pubnub.api.endpoints.Time;
import com.pubnub.api.endpoints.access.Grant;
+import com.pubnub.api.endpoints.access.GrantToken;
import com.pubnub.api.endpoints.channel_groups.AddChannelChannelGroup;
import com.pubnub.api.endpoints.channel_groups.AllChannelsChannelGroup;
import com.pubnub.api.endpoints.channel_groups.DeleteChannelGroup;
@@ -62,6 +63,8 @@
import com.pubnub.api.managers.StateManager;
import com.pubnub.api.managers.SubscriptionManager;
import com.pubnub.api.managers.TelemetryManager;
+import com.pubnub.api.managers.token_manager.TokenParser;
+import com.pubnub.api.models.consumer.access_manager.v3.PNToken;
import com.pubnub.api.vendor.Crypto;
import com.pubnub.api.vendor.FileEncryptionUtil;
import lombok.Getter;
@@ -94,10 +97,12 @@ public class PubNub {
private RetrofitManager retrofitManager;
+ private final TokenParser tokenParser;
+
private static final int TIMESTAMP_DIVIDER = 1000;
private static final int MAX_SEQUENCE = 65535;
- private static final String SDK_VERSION = "5.0.0";
+ private static final String SDK_VERSION = "5.1.0";
private final ListenerManager listenerManager;
private final StateManager stateManager;
@@ -121,6 +126,7 @@ public PubNub(@NotNull PNConfiguration initialConfig) {
delayedReconnectionManager,
duplicationManager);
this.publishSequenceManager = new PublishSequenceManager(MAX_SEQUENCE);
+ this.tokenParser = new TokenParser();
instanceId = UUID.randomUUID().toString();
}
@@ -217,6 +223,11 @@ public Grant grant() {
return new Grant(this, this.telemetryManager, this.retrofitManager);
}
+ @NotNull
+ public GrantToken grantToken() {
+ return new GrantToken(this, this.telemetryManager, this.retrofitManager);
+ }
+
@NotNull
public GetState getPresenceState() {
return new GetState(this, this.telemetryManager, this.retrofitManager);
@@ -575,4 +586,8 @@ public List getSubscribedChannelGroups() {
public void unsubscribeAll() {
subscriptionManager.unsubscribeAll();
}
+
+ public PNToken parseToken(String token) throws PubNubException {
+ return tokenParser.unwrapToken(token);
+ }
}
diff --git a/src/main/java/com/pubnub/api/PubNubException.java b/src/main/java/com/pubnub/api/PubNubException.java
index a3aa267ed..b663e44fc 100644
--- a/src/main/java/com/pubnub/api/PubNubException.java
+++ b/src/main/java/com/pubnub/api/PubNubException.java
@@ -33,6 +33,12 @@ public PubNubException(final String errormsg,
this.affectedCall = affectedCall;
}
+ @Override
+ @ToString.Include
+ public Throwable getCause() {
+ return super.getCause();
+ }
+
@Override
public String getMessage() {
return errormsg;
@@ -42,3 +48,4 @@ public String getMessage() {
@ToString.Exclude
private Call affectedCall;
}
+
diff --git a/src/main/java/com/pubnub/api/PubNubUtil.java b/src/main/java/com/pubnub/api/PubNubUtil.java
index 485d7e114..6bea9355f 100644
--- a/src/main/java/com/pubnub/api/PubNubUtil.java
+++ b/src/main/java/com/pubnub/api/PubNubUtil.java
@@ -17,6 +17,7 @@
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -285,4 +286,8 @@ public static byte[] readBytes(final InputStream inputStream) throws IOException
}
}
+ public static boolean isNullOrEmpty(final Collection collection) {
+ return collection == null || collection.isEmpty();
+ }
+
}
diff --git a/src/main/java/com/pubnub/api/endpoints/access/GrantToken.java b/src/main/java/com/pubnub/api/endpoints/access/GrantToken.java
new file mode 100644
index 000000000..77d229509
--- /dev/null
+++ b/src/main/java/com/pubnub/api/endpoints/access/GrantToken.java
@@ -0,0 +1,120 @@
+package com.pubnub.api.endpoints.access;
+
+import com.google.gson.JsonObject;
+import com.pubnub.api.PubNub;
+import com.pubnub.api.PubNubException;
+import com.pubnub.api.builder.PubNubErrorBuilder;
+import com.pubnub.api.endpoints.Endpoint;
+import com.pubnub.api.enums.PNOperationType;
+import com.pubnub.api.managers.RetrofitManager;
+import com.pubnub.api.managers.TelemetryManager;
+import com.pubnub.api.models.consumer.access_manager.v3.ChannelGrant;
+import com.pubnub.api.models.consumer.access_manager.v3.ChannelGroupGrant;
+import com.pubnub.api.models.consumer.access_manager.v3.PNGrantTokenResult;
+import com.pubnub.api.models.server.access_manager.v3.GrantTokenRequestBody;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+import retrofit2.Call;
+import retrofit2.Response;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import static com.pubnub.api.PubNubUtil.isNullOrEmpty;
+
+@Accessors(chain = true, fluent = true)
+public class GrantToken extends Endpoint {
+
+ @Setter
+ private Integer ttl;
+ @Setter
+ private Object meta;
+ @Setter
+ private List channels = Collections.emptyList();
+ @Setter
+ private List channelGroups = Collections.emptyList();
+
+ public GrantToken(PubNub pubnub, TelemetryManager telemetryManager, RetrofitManager retrofit) {
+ super(pubnub, telemetryManager, retrofit);
+ }
+
+ @Override
+ protected List getAffectedChannels() {
+ final ArrayList affectedChannels = new ArrayList<>();
+ for (ChannelGrant channelGrant : channels) {
+ affectedChannels.add(channelGrant.getId());
+ }
+ return affectedChannels;
+ }
+
+ @Override
+ protected List getAffectedChannelGroups() {
+ final ArrayList affectedChannelGroups = new ArrayList<>();
+ for (ChannelGroupGrant channelGroupGrant : channelGroups) {
+ affectedChannelGroups.add(channelGroupGrant.getId());
+ }
+ return affectedChannelGroups;
+ }
+
+ @Override
+ protected void validateParams() throws PubNubException {
+ if (this.getPubnub().getConfiguration().getSecretKey() == null || this.getPubnub()
+ .getConfiguration()
+ .getSecretKey()
+ .isEmpty()) {
+ throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_SECRET_KEY_MISSING).build();
+ }
+ if (this.getPubnub().getConfiguration().getSubscribeKey() == null || this.getPubnub()
+ .getConfiguration()
+ .getSubscribeKey()
+ .isEmpty()) {
+ throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_SUBSCRIBE_KEY_MISSING).build();
+ }
+ if (isNullOrEmpty(channels)
+ && isNullOrEmpty(channelGroups)) {
+ throw PubNubException.builder()
+ .pubnubError(PubNubErrorBuilder.PNERROBJ_RESOURCES_MISSING)
+ .build();
+ }
+ if (this.ttl == null) {
+ throw PubNubException.builder()
+ .pubnubError(PubNubErrorBuilder.PNERROBJ_TTL_MISSING)
+ .build();
+ }
+ }
+
+ @Override
+ protected Call doWork(Map queryParams) throws PubNubException {
+ GrantTokenRequestBody requestBody = GrantTokenRequestBody.builder()
+ .ttl(ttl)
+ .channels(channels)
+ .groups(channelGroups)
+ .meta(meta)
+ .build();
+
+ return this.getRetrofit()
+ .getAccessManagerService()
+ .grantToken(this.getPubnub().getConfiguration().getSubscribeKey(), requestBody, queryParams);
+ }
+
+ @Override
+ protected PNGrantTokenResult createResponse(Response input) throws PubNubException {
+ if (input.body() == null) {
+ return null;
+ }
+
+ return new PNGrantTokenResult(input.body().getAsJsonObject("data").get("token").getAsString());
+ }
+
+ @Override
+ protected PNOperationType getOperationType() {
+ return PNOperationType.PNAccessManagerGrantToken;
+ }
+
+ @Override
+ protected boolean isAuthRequired() {
+ return false;
+ }
+}
diff --git a/src/main/java/com/pubnub/api/managers/token_manager/TokenParser.java b/src/main/java/com/pubnub/api/managers/token_manager/TokenParser.java
new file mode 100644
index 000000000..20a30697c
--- /dev/null
+++ b/src/main/java/com/pubnub/api/managers/token_manager/TokenParser.java
@@ -0,0 +1,27 @@
+package com.pubnub.api.managers.token_manager;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.cbor.CBORFactory;
+import com.pubnub.api.PubNubException;
+import com.pubnub.api.models.consumer.access_manager.v3.PNToken;
+import com.pubnub.api.vendor.Base64;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import static com.pubnub.api.builder.PubNubErrorBuilder.PNERROBJ_INVALID_ACCESS_TOKEN;
+
+public class TokenParser {
+ private final ObjectMapper mapper = new ObjectMapper(new CBORFactory());
+
+ public PNToken unwrapToken(String token) throws PubNubException {
+ try {
+ byte[] byteArray = Base64.decode(token.getBytes(StandardCharsets.UTF_8), Base64.URL_SAFE);
+ return mapper.readValue(byteArray, PNToken.class);
+ } catch (IOException e) {
+ throw PubNubException.builder()
+ .pubnubError(PNERROBJ_INVALID_ACCESS_TOKEN)
+ .build();
+ }
+ }
+}
diff --git a/src/main/java/com/pubnub/api/models/TokenBitmask.java b/src/main/java/com/pubnub/api/models/TokenBitmask.java
new file mode 100644
index 000000000..8f2107a78
--- /dev/null
+++ b/src/main/java/com/pubnub/api/models/TokenBitmask.java
@@ -0,0 +1,12 @@
+package com.pubnub.api.models;
+
+public class TokenBitmask {
+ private TokenBitmask() {
+ }
+
+ public static final int READ = 1;
+ public static final int WRITE = 2;
+ public static final int MANAGE = 4;
+ public static final int DELETE = 8;
+ public static final int CREATE = 16;
+}
diff --git a/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/Channel.java b/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/Channel.java
deleted file mode 100644
index fc206e7e2..000000000
--- a/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/Channel.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.pubnub.api.models.consumer.access_manager.v3;
-
-public class Channel extends PNResource {
-
- private Channel() {
-
- }
-
- public static Channel name(String channelName) {
- Channel channel = new Channel();
- channel.resourceName = channelName;
- return channel;
- }
-
- public static Channel pattern(String channelPattern) {
- Channel channel = new Channel();
- channel.resourcePattern = channelPattern;
- return channel;
- }
-
- @Override
- public Channel read() {
- return super.read();
- }
-
- @Override
- public Channel delete() {
- return super.delete();
- }
-
- @Override
- public Channel write() {
- return super.write();
- }
-}
diff --git a/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/ChannelGrant.java b/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/ChannelGrant.java
new file mode 100644
index 000000000..153e6e426
--- /dev/null
+++ b/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/ChannelGrant.java
@@ -0,0 +1,35 @@
+package com.pubnub.api.models.consumer.access_manager.v3;
+
+public class ChannelGrant extends PNResource {
+
+ private ChannelGrant() {
+
+ }
+
+ public static ChannelGrant name(String channelName) {
+ ChannelGrant channelGrant = new ChannelGrant();
+ channelGrant.resourceName = channelName;
+ return channelGrant;
+ }
+
+ public static ChannelGrant pattern(String channelPattern) {
+ ChannelGrant channelGrant = new ChannelGrant();
+ channelGrant.resourcePattern = channelPattern;
+ return channelGrant;
+ }
+
+ @Override
+ public ChannelGrant read() {
+ return super.read();
+ }
+
+ @Override
+ public ChannelGrant delete() {
+ return super.delete();
+ }
+
+ @Override
+ public ChannelGrant write() {
+ return super.write();
+ }
+}
diff --git a/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/ChannelGroupGrant.java b/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/ChannelGroupGrant.java
new file mode 100644
index 000000000..edde89f27
--- /dev/null
+++ b/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/ChannelGroupGrant.java
@@ -0,0 +1,29 @@
+package com.pubnub.api.models.consumer.access_manager.v3;
+
+public class ChannelGroupGrant extends PNResource {
+
+ private ChannelGroupGrant() {
+ }
+
+ public static ChannelGroupGrant id(String groupName) {
+ ChannelGroupGrant channelGroupGrant = new ChannelGroupGrant();
+ channelGroupGrant.resourceName = groupName;
+ return channelGroupGrant;
+ }
+
+ public static ChannelGroupGrant pattern(String groupPattern) {
+ ChannelGroupGrant channelGroupGrant = new ChannelGroupGrant();
+ channelGroupGrant.resourcePattern = groupPattern;
+ return channelGroupGrant;
+ }
+
+ @Override
+ public ChannelGroupGrant read() {
+ return super.read();
+ }
+
+ @Override
+ public ChannelGroupGrant manage() {
+ return super.manage();
+ }
+}
diff --git a/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/Group.java b/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/Group.java
deleted file mode 100644
index 08459a8b1..000000000
--- a/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/Group.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.pubnub.api.models.consumer.access_manager.v3;
-
-public class Group extends PNResource {
-
- private Group() {
-
- }
-
- public static Group id(String groupName) {
- Group group = new Group();
- group.resourceName = groupName;
- return group;
- }
-
- public static Group pattern(String groupPattern) {
- Group group = new Group();
- group.resourcePattern = groupPattern;
- return group;
- }
-
- @Override
- public Group read() {
- return super.read();
- }
-
- @Override
- public Group manage() {
- return super.manage();
- }
-}
diff --git a/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/PNGrantTokenResult.java b/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/PNGrantTokenResult.java
index ee9aea960..ee079bc90 100644
--- a/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/PNGrantTokenResult.java
+++ b/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/PNGrantTokenResult.java
@@ -1,13 +1,10 @@
package com.pubnub.api.models.consumer.access_manager.v3;
-import lombok.Builder;
-import lombok.Getter;
-import lombok.ToString;
+import lombok.Data;
+import lombok.NonNull;
-@Builder
-@ToString
-@Getter
+@Data
public class PNGrantTokenResult {
-
- private String token;
+ @NonNull
+ private final String token;
}
diff --git a/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/PNResource.java b/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/PNResource.java
index 274e8f294..31af4e4a6 100644
--- a/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/PNResource.java
+++ b/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/PNResource.java
@@ -3,7 +3,7 @@
import lombok.AccessLevel;
import lombok.Getter;
-@Getter()
+@Getter
public abstract class PNResource {
@Getter(AccessLevel.NONE)
diff --git a/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/PNToken.java b/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/PNToken.java
new file mode 100644
index 000000000..9ee565d71
--- /dev/null
+++ b/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/PNToken.java
@@ -0,0 +1,71 @@
+package com.pubnub.api.models.consumer.access_manager.v3;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.pubnub.api.models.TokenBitmask;
+import lombok.Data;
+import lombok.NonNull;
+
+import java.util.Map;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class PNToken {
+ private final int version;
+ private final long timestamp;
+ private final long ttl;
+ @NonNull
+ private final PNTokenResources resources;
+ @NonNull
+ private final PNTokenResources patterns;
+
+ @JsonCreator
+ public static PNToken of(
+ @JsonProperty("v")
+ final int v,
+ @JsonProperty("t")
+ final long t,
+ @JsonProperty("ttl")
+ final long ttl,
+ @JsonProperty("res")
+ final PNTokenResources res,
+ @JsonProperty("pat")
+ final PNTokenResources pat) {
+ return new PNToken(v, t, ttl, res, pat);
+ }
+
+ @Data
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class PNTokenResources {
+ @NonNull
+ private final Map channels;
+ @NonNull
+ private final Map channelGroups;
+
+ @JsonCreator
+ public static PNTokenResources of(@JsonProperty("chan") final Map chan,
+ @JsonProperty("grp") final Map grp) {
+
+ return new PNTokenResources(chan, grp);
+ }
+ }
+
+ @Data
+ public static class PNResourcePermissions {
+ private final boolean create;
+ private final boolean read;
+ private final boolean write;
+ private final boolean manage;
+ private final boolean delete;
+
+ @JsonCreator
+ public static PNResourcePermissions of(int grant) {
+ return new PNResourcePermissions((grant & TokenBitmask.CREATE) != 0,
+ (grant & TokenBitmask.READ) != 0,
+ (grant & TokenBitmask.WRITE) != 0,
+ (grant & TokenBitmask.MANAGE) != 0,
+ (grant & TokenBitmask.DELETE) != 0);
+ }
+ }
+}
diff --git a/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/Space.java b/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/Space.java
deleted file mode 100644
index ea076b9ed..000000000
--- a/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/Space.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.pubnub.api.models.consumer.access_manager.v3;
-
-public class Space extends PNResource {
-
- private Space() {
-
- }
-
- public static Space id(String spaceId) {
- Space space = new Space();
- space.resourceName = spaceId;
- return space;
- }
-
- public static Space pattern(String spacePattern) {
- Space space = new Space();
- space.resourcePattern = spacePattern;
- return space;
- }
-
- @Override
- public Space read() {
- return super.read();
- }
-
- @Override
- public Space delete() {
- return super.delete();
- }
-
- @Override
- public Space write() {
- return super.write();
- }
-
- @Override
- public Space manage() {
- return super.manage();
- }
-
- @Override
- public Space create() {
- return super.create();
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/User.java b/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/User.java
deleted file mode 100644
index 998071b0c..000000000
--- a/src/main/java/com/pubnub/api/models/consumer/access_manager/v3/User.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.pubnub.api.models.consumer.access_manager.v3;
-
-public class User extends PNResource {
-
- private User() {
-
- }
-
- public static User id(String userId) {
- User user = new User();
- user.resourceName = userId;
- return user;
- }
-
- public static User pattern(String userPattern) {
- User user = new User();
- user.resourcePattern = userPattern;
- return user;
- }
-
- @Override
- public User read() {
- return super.read();
- }
-
- @Override
- public User delete() {
- return super.delete();
- }
-
- @Override
- public User write() {
- return super.write();
- }
-
- @Override
- public User manage() {
- return super.manage();
- }
-
- @Override
- public User create() {
- return super.create();
- }
-}
diff --git a/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNMembership.java b/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNMembership.java
index 8b02370fa..35ca8af8d 100644
--- a/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNMembership.java
+++ b/src/main/java/com/pubnub/api/models/consumer/objects_api/membership/PNMembership.java
@@ -3,13 +3,12 @@
import com.google.gson.annotations.JsonAdapter;
import com.pubnub.api.models.consumer.objects_api.channel.PNChannelMetadata;
import com.pubnub.api.models.consumer.objects_api.util.CustomPayloadJsonInterceptor;
+import com.pubnub.api.utils.UnwrapSingleField;
import lombok.*;
import lombok.experimental.Accessors;
-@Getter
+@Data
@Accessors(chain = true)
-@EqualsAndHashCode
-@ToString
@RequiredArgsConstructor
public class PNMembership {
@NonNull
@@ -18,6 +17,9 @@ public class PNMembership {
@JsonAdapter(CustomPayloadJsonInterceptor.class)
protected Object custom;
+ @JsonAdapter(UnwrapSingleField.class)
+ protected String uuid;
+
protected String updated;
protected String eTag;
}
diff --git a/src/main/java/com/pubnub/api/models/consumer/pubsub/objects/ObjectResult.java b/src/main/java/com/pubnub/api/models/consumer/pubsub/objects/ObjectResult.java
index 13e76b9bc..c5bb3d5c6 100644
--- a/src/main/java/com/pubnub/api/models/consumer/pubsub/objects/ObjectResult.java
+++ b/src/main/java/com/pubnub/api/models/consumer/pubsub/objects/ObjectResult.java
@@ -9,6 +9,7 @@ public abstract class ObjectResult extends BasePubSubResult {
@Getter
protected String event;
+ @Getter
protected T data;
public ObjectResult(BasePubSubResult result, String event, T data) {
diff --git a/src/main/java/com/pubnub/api/models/server/access_manager/v3/GrantTokenRequestBody.java b/src/main/java/com/pubnub/api/models/server/access_manager/v3/GrantTokenRequestBody.java
index 9197aca1a..9c8acc9f7 100644
--- a/src/main/java/com/pubnub/api/models/server/access_manager/v3/GrantTokenRequestBody.java
+++ b/src/main/java/com/pubnub/api/models/server/access_manager/v3/GrantTokenRequestBody.java
@@ -1,121 +1,90 @@
package com.pubnub.api.models.server.access_manager.v3;
-import com.google.gson.JsonObject;
-import com.pubnub.api.PubNub;
import com.pubnub.api.PubNubException;
import com.pubnub.api.builder.PubNubErrorBuilder;
-import com.pubnub.api.models.consumer.access_manager.v3.Channel;
-import com.pubnub.api.models.consumer.access_manager.v3.Group;
+import com.pubnub.api.models.TokenBitmask;
+import com.pubnub.api.models.consumer.access_manager.v3.ChannelGrant;
+import com.pubnub.api.models.consumer.access_manager.v3.ChannelGroupGrant;
import com.pubnub.api.models.consumer.access_manager.v3.PNResource;
-import com.pubnub.api.models.consumer.access_manager.v3.Space;
-import com.pubnub.api.models.consumer.access_manager.v3.User;
import lombok.Builder;
-import lombok.extern.java.Log;
+import lombok.Data;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
-@Log
-@Builder
+@Data
public class GrantTokenRequestBody {
+ private final Integer ttl;
+ private final GrantTokenPermissions permissions;
+
+ @Data
+ private static class GrantTokenPermissions {
+ private final GrantTokenPermission resources;
+ private final GrantTokenPermission patterns;
+ private final Object meta;
+ }
- private static final int READ = 1;
- private static final int WRITE = 2;
- private static final int MANAGE = 4;
- private static final int DELETE = 8;
- private static final int CREATE = 16;
-
- private Integer ttl;
- private List channels;
- private List groups;
- private List users;
- private List spaces;
- private Object meta;
- private PubNub pubNub;
-
- public JsonObject assemble() throws PubNubException {
- JsonObject payload = new JsonObject();
-
- payload.addProperty("ttl", this.ttl);
-
- JsonObject permissions = new JsonObject();
-
- JsonObject resources = new JsonObject();
- JsonObject patterns = new JsonObject();
-
- parse(channels, "channels", resources, patterns);
- parse(groups, "groups", resources, patterns);
- parse(users, "users", resources, patterns);
- parse(spaces, "spaces", resources, patterns);
+ @Data
+ public static class GrantTokenPermission {
+ private final Map channels;
+ private final Map groups;
+ private final Map spaces = Collections.emptyMap();
+ private final Map users = Collections.emptyMap();
+ }
- permissions.add("resources", resources);
- permissions.add("patterns", patterns);
+ @Builder
+ public static GrantTokenRequestBody of(Integer ttl,
+ List channels,
+ List groups,
+ Object meta) throws PubNubException {
+
+ GrantTokenPermission resources = new GrantTokenPermission(getResources(channels),
+ getResources(groups));
+ GrantTokenPermission patterns = new GrantTokenPermission(getPatterns(channels),
+ getPatterns(groups));
+ GrantTokenPermissions permissions = new GrantTokenPermissions(resources, patterns, meta == null ? Collections.emptyMap() : meta);
+ return new GrantTokenRequestBody(ttl, permissions);
+ }
- if (this.meta != null) {
- try {
- permissions.add("meta", pubNub.getMapper().convertValue(this.meta, JsonObject.class));
- } catch (PubNubException e) {
- throw PubNubException.builder()
- .pubnubError(PubNubErrorBuilder.PNERROBJ_INVALID_META)
- .cause(e)
- .build();
+ private static > Map getResources(List resources) throws PubNubException {
+ final Map result = new HashMap<>();
+ for (T resource : resources) {
+ if (!resource.isPatternResource()) {
+ result.put(resource.getId(), calculateBitmask(resource));
}
- } else {
- permissions.add("meta", new JsonObject());
}
-
- payload.add("permissions", permissions);
-
- return payload;
+ return result;
}
- private void parse(List extends PNResource> list, String resourceSetName, JsonObject resources,
- JsonObject patterns) throws PubNubException {
- if (list != null) {
- for (PNResource pnResource : list) {
- JsonObject resourceObject = new JsonObject();
-
- JsonObject determinedObject;
-
- if (pnResource.isPatternResource()) {
- determinedObject = patterns;
- } else {
- determinedObject = resources;
- }
-
- if (determinedObject.has(resourceSetName)) {
- determinedObject.get(resourceSetName).getAsJsonObject()
- .addProperty(pnResource.getId(), calculateBitmask(pnResource));
- } else {
- resourceObject.addProperty(pnResource.getId(), calculateBitmask(pnResource));
- determinedObject.add(resourceSetName, resourceObject);
- }
+ private static > Map getPatterns(List resources) throws PubNubException {
+ final Map result = new HashMap<>();
+ for (T resource : resources) {
+ if (resource.isPatternResource()) {
+ result.put(resource.getId(), calculateBitmask(resource));
}
}
+ return result;
- if (!resources.has(resourceSetName)) {
- resources.add(resourceSetName, new JsonObject());
- }
- if (!patterns.has(resourceSetName)) {
- patterns.add(resourceSetName, new JsonObject());
- }
}
- private int calculateBitmask(PNResource resource) throws PubNubException {
+ private static int calculateBitmask(PNResource> resource) throws PubNubException {
int sum = 0;
if (resource.isRead()) {
- sum += READ;
+ sum += TokenBitmask.READ;
}
if (resource.isWrite()) {
- sum += WRITE;
+ sum += TokenBitmask.WRITE;
}
if (resource.isManage()) {
- sum += MANAGE;
+ sum += TokenBitmask.MANAGE;
}
if (resource.isDelete()) {
- sum += DELETE;
+ sum += TokenBitmask.DELETE;
}
if (resource.isCreate()) {
- sum += CREATE;
+ sum += TokenBitmask.CREATE;
}
if (sum == 0) {
throw PubNubException.builder()
diff --git a/src/main/java/com/pubnub/api/utils/UnwrapSingleField.java b/src/main/java/com/pubnub/api/utils/UnwrapSingleField.java
new file mode 100644
index 000000000..5f26177b5
--- /dev/null
+++ b/src/main/java/com/pubnub/api/utils/UnwrapSingleField.java
@@ -0,0 +1,22 @@
+package com.pubnub.api.utils;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+
+import java.lang.reflect.Type;
+
+public class UnwrapSingleField implements JsonDeserializer {
+ @Override
+ public T deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
+ final JsonObject jsonObject = json.getAsJsonObject();
+ if (jsonObject.keySet().size() != 1) {
+ throw new IllegalStateException("Couldn't unwrap field for object containing more than 1 field. Actual number of fields: " + jsonObject.keySet().size());
+ }
+ final String key = jsonObject.keySet().toArray(new String[]{})[0];
+ final JsonElement element = jsonObject.get(key);
+ return context.deserialize(element, typeOfT);
+ }
+}
diff --git a/src/test/java/com/pubnub/api/PubNubTest.java b/src/test/java/com/pubnub/api/PubNubTest.java
index 933c7e465..fefb2830b 100644
--- a/src/test/java/com/pubnub/api/PubNubTest.java
+++ b/src/test/java/com/pubnub/api/PubNubTest.java
@@ -99,7 +99,7 @@ public void getVersionAndTimeStamp() {
pubnub = new PubNub(pnConfiguration);
String version = pubnub.getVersion();
int timeStamp = pubnub.getTimestamp();
- Assert.assertEquals("5.0.0", version);
+ Assert.assertEquals("5.1.0", version);
Assert.assertTrue(timeStamp > 0);
}
diff --git a/src/test/java/com/pubnub/api/endpoints/access/GrantTokenEndpointTest.java b/src/test/java/com/pubnub/api/endpoints/access/GrantTokenEndpointTest.java
new file mode 100644
index 000000000..8eb8db550
--- /dev/null
+++ b/src/test/java/com/pubnub/api/endpoints/access/GrantTokenEndpointTest.java
@@ -0,0 +1,100 @@
+package com.pubnub.api.endpoints.access;
+
+import com.pubnub.api.PNConfiguration;
+import com.pubnub.api.PubNub;
+import com.pubnub.api.PubNubException;
+import com.pubnub.api.endpoints.TestHarness;
+import com.pubnub.api.models.consumer.access_manager.v3.ChannelGrant;
+import com.pubnub.api.models.consumer.access_manager.v3.ChannelGroupGrant;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.Collections;
+
+import static com.pubnub.api.builder.PubNubErrorBuilder.PNERR_PERMISSION_MISSING;
+import static com.pubnub.api.builder.PubNubErrorBuilder.PNERR_RESOURCES_MISSING;
+import static com.pubnub.api.builder.PubNubErrorBuilder.PNERR_SECRET_KEY_MISSING;
+import static com.pubnub.api.builder.PubNubErrorBuilder.PNERR_SUBSCRIBE_KEY_MISSING;
+import static com.pubnub.api.builder.PubNubErrorBuilder.PNERR_TTL_MISSING;
+import static org.junit.Assert.assertEquals;
+
+public class GrantTokenEndpointTest extends TestHarness {
+
+ private final PubNub pubnub = this.createPubNubInstance();
+
+ @Before
+ public void beforeEach() throws IOException {
+ pubnub.getConfiguration().setSecretKey("secretKey").setIncludeInstanceIdentifier(true);
+ }
+
+ @Test
+ public void validate_NoResourceSet() {
+ try {
+ pubnub.grantToken()
+ .ttl(1)
+ .sync();
+ } catch (PubNubException e) {
+ assertEquals(PNERR_RESOURCES_MISSING, e.getPubnubError().getErrorCode());
+ }
+ }
+
+ @Test
+ public void validate_NoTTLSet() {
+ try {
+ pubnub.grantToken()
+ .channels(Collections.singletonList(ChannelGrant.name("test").read()))
+ .sync();
+ } catch (PubNubException e) {
+ assertEquals(PNERR_TTL_MISSING, e.getPubnubError().getErrorCode());
+ }
+ }
+
+ @Test
+ public void validate_ChannelResourceMissingAnyPermissions() {
+ try {
+ pubnub.grantToken()
+ .ttl(1)
+ .channels(Collections.singletonList(ChannelGrant.name("test")))
+ .sync();
+ } catch (PubNubException e) {
+ assertEquals(PNERR_PERMISSION_MISSING, e.getPubnubError().getErrorCode());
+ }
+ }
+
+ @Test
+ public void validate_ChannelGroupPatterMissingAnyPermissions() {
+ try {
+ pubnub.grantToken()
+ .ttl(1)
+ .channelGroups(Collections.singletonList(ChannelGroupGrant.id("test")))
+ .sync();
+ } catch (PubNubException e) {
+ assertEquals(PNERR_PERMISSION_MISSING, e.getPubnubError().getErrorCode());
+ }
+ }
+
+ @Test
+ public void validate_SecretKeyMissing() {
+ try {
+ createPubNubInstance().grantToken()
+ .ttl(1)
+ .channelGroups(Collections.singletonList(ChannelGroupGrant.id("test").read()))
+ .sync();
+ } catch (PubNubException e) {
+ assertEquals(PNERR_SECRET_KEY_MISSING, e.getPubnubError().getErrorCode());
+ }
+ }
+
+ @Test
+ public void validate_SubscribeKeyMissing() {
+ try {
+ new PubNub(new PNConfiguration().setSecretKey("secret")).grantToken()
+ .ttl(1)
+ .channelGroups(Collections.singletonList(ChannelGroupGrant.id("test").read()))
+ .sync();
+ } catch (PubNubException e) {
+ assertEquals(PNERR_SUBSCRIBE_KEY_MISSING, e.getPubnubError().getErrorCode());
+ }
+ }
+}
diff --git a/src/test/java/com/pubnub/api/managers/token_manager/TokenParserTest.java b/src/test/java/com/pubnub/api/managers/token_manager/TokenParserTest.java
new file mode 100644
index 000000000..526a47b41
--- /dev/null
+++ b/src/test/java/com/pubnub/api/managers/token_manager/TokenParserTest.java
@@ -0,0 +1,33 @@
+package com.pubnub.api.managers.token_manager;
+
+import com.pubnub.api.PubNubException;
+import com.pubnub.api.models.consumer.access_manager.v3.PNToken;
+import org.junit.Test;
+
+import java.util.Collections;
+
+import static java.util.Collections.emptyMap;
+import static org.junit.Assert.assertEquals;
+
+public class TokenParserTest {
+
+ private final TokenParser tokenParser = new TokenParser();
+
+ @Test
+ public void stringTokenIsProperlyDeserialized() throws PubNubException {
+ //given
+ String stringToken = "p0F2AkF0Gl043rhDdHRsCkNyZXOkRGNoYW6hZnNlY3JldAFDZ3JwoEN1c3KgQ3NwY6BDcGF0pERjaGFuoENncnCgQ3VzcqBDc3BjoERtZXRhoENzaWdYIGOAeTyWGJI-blahPGD9TuKlaW1YQgiB4uR_edmfq-61";
+ PNToken expectedToken = new PNToken(2, 1564008120, 10,
+ new PNToken.PNTokenResources(
+ Collections.singletonMap("secret",
+ new PNToken.PNResourcePermissions(false, true, false, false, false)),
+ emptyMap()),
+ new PNToken.PNTokenResources(emptyMap(), emptyMap()));
+
+ //when
+ PNToken token = tokenParser.unwrapToken(stringToken);
+
+ //then
+ assertEquals(expectedToken, token);
+ }
+}