From fd4927ee4695e0f559603201e6d0aaf0db47524f Mon Sep 17 00:00:00 2001 From: "mohit.tejani" Date: Fri, 16 Oct 2026 12:27:22 +0530 Subject: [PATCH 1/3] grant_token with categories support --- .../lib/events/grant-categories.yml | 114 ++++++++++++++++++ lib/pubnub/events/grant_token.rb | 17 ++- lib/pubnub/validators/grant_token.rb | 22 ++++ spec/lib/events/grant_token_spec.rb | 87 +++++++++++++ 4 files changed, 239 insertions(+), 1 deletion(-) create mode 100644 fixtures/vcr_cassettes/lib/events/grant-categories.yml diff --git a/fixtures/vcr_cassettes/lib/events/grant-categories.yml b/fixtures/vcr_cassettes/lib/events/grant-categories.yml new file mode 100644 index 000000000..325d3b37d --- /dev/null +++ b/fixtures/vcr_cassettes/lib/events/grant-categories.yml @@ -0,0 +1,114 @@ +--- +http_interactions: +- request: + method: get + uri: http://ps.pndsn.com/v2/auth/grant/sub-key/sub-a-mock-key?auth=category-auth&category=channels,uuids&g=1&m=0&pnsdk=PubNub-Ruby/4.2.7&r=0&signature=v2.OdCTZXne_jEBewh3yY8iNDWcMCHC6BXNrg1Er9JoSTQ×tamp=1602537897&ttl=1440&uuid=ruby-test-uuid&w=0 + body: + encoding: UTF-8 + string: '' + headers: + User-Agent: + - HTTPClient/1.0 (2.8.3, ruby 2.6.3 (2019-04-16)) + Accept: + - "*/*" + Date: + - Mon, 12 Oct 2020 21:24:57 GMT + response: + status: + code: 200 + message: OK + headers: + Date: + - Mon, 12 Oct 2020 21:24:57 GMT + Content-Type: + - text/javascript; charset=UTF-8 + Connection: + - keep-alive + Access-Control-Allow-Origin: + - "*" + Access-Control-Allow-Methods: + - GET + Access-Control-Allow-Headers: + - Origin, X-Requested-With, Content-Type, Accept + Cache-Control: + - no-cache, no-store, must-revalidate + body: + encoding: UTF-8 + string: '{"message":"Success","payload":{"level":"category","subscribe_key":"sub-a-mock-key","ttl":1440,"categories":{"channels":{"auths":{"category-auth":{"r":0,"w":0,"m":0,"d":0,"g":1,"u":0,"j":0}}},"uuids":{"auths":{"category-auth":{"r":0,"w":0,"m":0,"d":0,"g":1,"u":0,"j":0}}}}},"service":"Access + Manager","status":200}' + recorded_at: Mon, 12 Oct 2020 21:24:57 GMT +- request: + method: get + uri: http://ps.pndsn.com/v2/auth/grant/sub-key/sub-a-mock-key?auth=category-auth&category=uuids&channel=demo&g=1&m=0&pnsdk=PubNub-Ruby/4.2.7&r=0&signature=v2.OdCTZXne_jEBewh3yY8iNDWcMCHC6BXNrg1Er9JoSTQ×tamp=1602537897&ttl=1440&uuid=ruby-test-uuid&w=0 + body: + encoding: UTF-8 + string: '' + headers: + User-Agent: + - HTTPClient/1.0 (2.8.3, ruby 2.6.3 (2019-04-16)) + Accept: + - "*/*" + Date: + - Mon, 12 Oct 2020 21:24:57 GMT + response: + status: + code: 200 + message: OK + headers: + Date: + - Mon, 12 Oct 2020 21:24:57 GMT + Content-Type: + - text/javascript; charset=UTF-8 + Connection: + - keep-alive + Access-Control-Allow-Origin: + - "*" + Access-Control-Allow-Methods: + - GET + Access-Control-Allow-Headers: + - Origin, X-Requested-With, Content-Type, Accept + Cache-Control: + - no-cache, no-store, must-revalidate + body: + encoding: UTF-8 + string: '{"message":"Success","payload":{"level":"category","subscribe_key":"sub-a-mock-key","ttl":1440,"categories":{"uuids":{"auths":{"category-auth":{"r":0,"w":0,"m":0,"d":0,"g":1,"u":0,"j":0}}}}},"service":"Access + Manager","status":200}' + recorded_at: Mon, 12 Oct 2020 21:24:57 GMT +- request: + method: get + uri: http://ps.pndsn.com/v2/auth/grant/sub-key/sub-a-mock-key?auth=category-auth&category=channels&g=1&m=0&pnsdk=PubNub-Ruby/4.2.7&r=1&signature=v2.OdCTZXne_jEBewh3yY8iNDWcMCHC6BXNrg1Er9JoSTQ×tamp=1602537897&ttl=1440&uuid=ruby-test-uuid&w=0 + body: + encoding: UTF-8 + string: '' + headers: + User-Agent: + - HTTPClient/1.0 (2.8.3, ruby 2.6.3 (2019-04-16)) + Accept: + - "*/*" + Date: + - Mon, 12 Oct 2020 21:24:57 GMT + response: + status: + code: 400 + message: Bad Request + headers: + Date: + - Mon, 12 Oct 2020 21:24:57 GMT + Content-Type: + - text/javascript; charset=UTF-8 + Connection: + - keep-alive + Access-Control-Allow-Origin: + - "*" + Access-Control-Allow-Methods: + - GET + Access-Control-Allow-Headers: + - Origin, X-Requested-With, Content-Type, Accept + Cache-Control: + - no-cache, no-store, must-revalidate + body: + encoding: UTF-8 + string: '{"message":"Category permissions must be exactly the get permission + (`g`)","error":true,"service":"Access Manager","status":400}' + recorded_at: Mon, 12 Oct 2020 21:24:57 GMT +recorded_with: VCR 6.0.0 diff --git a/lib/pubnub/events/grant_token.rb b/lib/pubnub/events/grant_token.rb index 611defa4f..afadf5c04 100644 --- a/lib/pubnub/events/grant_token.rb +++ b/lib/pubnub/events/grant_token.rb @@ -12,6 +12,7 @@ def initialize(options, app) options[:channel_groups] = options[:channel_groups] || {} @spaces_permissions = options[:spaces_permissions] || {} @users_permissions = options[:users_permissions] || {} + @categories = options[:categories] || {} super end @@ -31,7 +32,8 @@ def fire meta: @meta, uuid: uuid, resources: prepare_permissions(:resource, @channels, @channel_groups, @uuids, @spaces_permissions, @users_permissions), - patterns: prepare_permissions(:pattern, @channels, @channel_groups, @uuids, @spaces_permissions, @users_permissions) + patterns: prepare_permissions(:pattern, @channels, @channel_groups, @uuids, @spaces_permissions, @users_permissions), + categories: prepare_categories(@categories) }.select { |_, v| v } } body = Formatter.format_message(raw_body, nil, false) @@ -70,6 +72,12 @@ def prepare_single_permissions(type, resources) .to_h { |k, v| [k, v.calculate_bitmask] } end + def prepare_categories(categories) + return nil if categories.empty? + + categories.to_h { |k, v| [k.to_s, v.calculate_bitmask] } + end + def valid_envelope(parsed_response, req_res_objects) Pubnub::Envelope.new( event: @event, @@ -146,6 +154,13 @@ def res(read: false, write: false, manage: false, delete: false, create: false, join: join ) end + + def cat(get: false) + Permissions.new( + type: :category, + get: get + ) + end end def initialize(options) diff --git a/lib/pubnub/validators/grant_token.rb b/lib/pubnub/validators/grant_token.rb index 829118624..5560b7dbb 100644 --- a/lib/pubnub/validators/grant_token.rb +++ b/lib/pubnub/validators/grant_token.rb @@ -12,6 +12,7 @@ def validate! validate_permissions!(@uuids, ":uuids") validate_permissions!(@channels, ":channels") validate_permissions!(@channel_groups, ":uuids") + validate_categories! validate_objects_entities_separation! end @@ -47,6 +48,27 @@ def validate_permissions!(arg, name) ) unless arg.is_a?(Hash) end + def validate_categories! + return if @categories.nil? + + raise( + ArgumentError.new(object: self, message: ':categories has to be kind of Hash for grant token event.'), + ':categories has to be kind of Hash for grant token event.' + ) unless @categories.is_a?(Hash) + + @categories.each do |key, value| + raise( + ArgumentError.new(object: self, message: ':categories supports only :channels and :uuids keys for grant token event.'), + ':categories supports only :channels and :uuids keys for grant token event.' + ) unless %w[channels uuids].include?(key.to_s) + + raise( + ArgumentError.new(object: self, message: ':categories values have to be created with Pubnub::Permissions.cat for grant token event.'), + ':categories values have to be created with Pubnub::Permissions.cat for grant token event.' + ) unless value.is_a?(Pubnub::Permissions) && value.type == :category + end + end + def validate_objects_entities_separation! entities_set = !@spaces_permissions.empty? || !@users_permissions.empty? diff --git a/spec/lib/events/grant_token_spec.rb b/spec/lib/events/grant_token_spec.rb index 9dc3abae1..4dd420e1f 100644 --- a/spec/lib/events/grant_token_spec.rb +++ b/spec/lib/events/grant_token_spec.rb @@ -67,6 +67,93 @@ end end + context "with categories" do + let(:body_matching_options) do + { + record: :none, + match_requests_on: VCR.configuration.default_cassette_options[:match_requests_on] + [:body] + } + end + + it "builds category permissions with GET bitmask" do + expect(Pubnub::Permissions.cat(get: true).calculate_bitmask).to eq 32 + expect(Pubnub::Permissions.cat(get: true).type).to eq :category + expect(Pubnub::Permissions.cat.calculate_bitmask).to eq 0 + end + + it "sends categories along with resources" do + VCR.use_cassette("lib/events/grant-token-categories", **body_matching_options) do + envelope = pubnub.grant_token( + ttl: 60, + authorized_uuid: "authorized_uuid", + channels: { + "id": Pubnub::Permissions.res(read: true) + }, + categories: { + channels: Pubnub::Permissions.cat(get: true), + uuids: Pubnub::Permissions.cat(get: true) + } + ).value + + expect(envelope.is_a?(Pubnub::ErrorEnvelope)).to eq false + expect(envelope.status).to satisfies_schema Pubnub::Schemas::Envelope::StatusSchema.new + expect(envelope.result).to satisfies_schema Pubnub::Schemas::Envelope::ResultSchema.new + end + end + + it "sends categories only" do + VCR.use_cassette("lib/events/grant-token-categories", **body_matching_options) do + envelope = pubnub.grant_token( + ttl: 60, + authorized_uuid: "authorized_uuid", + categories: { + "uuids" => Pubnub::Permissions.cat(get: true) + } + ).value + + expect(envelope.is_a?(Pubnub::ErrorEnvelope)).to eq false + expect(envelope.status).to satisfies_schema Pubnub::Schemas::Envelope::StatusSchema.new + expect(envelope.result).to satisfies_schema Pubnub::Schemas::Envelope::ResultSchema.new + end + end + + it "sends ttl and a single category without authorized uuid" do + VCR.use_cassette("lib/events/grant-token-categories", **body_matching_options) do + envelope = pubnub.grant_token( + ttl: 60, + categories: { + channels: Pubnub::Permissions.cat(get: true) + } + ).value + + expect(envelope.is_a?(Pubnub::ErrorEnvelope)).to eq false + expect(envelope.status).to satisfies_schema Pubnub::Schemas::Envelope::StatusSchema.new + expect(envelope.result).to satisfies_schema Pubnub::Schemas::Envelope::ResultSchema.new + end + end + + it "raises error for unsupported category key" do + expect { pubnub.grant_token( + ttl: 60, + categories: { groups: Pubnub::Permissions.cat(get: true) } + ).value }.to raise_error(Pubnub::ArgumentError) + end + + it "raises error for non-category permissions" do + expect { pubnub.grant_token( + ttl: 60, + categories: { channels: Pubnub::Permissions.res(get: true) } + ).value }.to raise_error(Pubnub::ArgumentError) + end + + it "raises error when categories is not a Hash" do + expect { pubnub.grant_token( + ttl: 60, + categories: "channels" + ).value }.to raise_error(Pubnub::ArgumentError) + end + end + it "forms valid ErrorEnvelope on error" do VCR.use_cassette("lib/events/grant-token-error", record: :once) do expect(envelope_with_objects.is_a?(Pubnub::ErrorEnvelope)).to eq true From 3faeaa7ad8209b583000ba1631465fe966dbe583 Mon Sep 17 00:00:00 2001 From: "mohit.tejani" Date: Fri, 16 Oct 2026 12:27:47 +0530 Subject: [PATCH 2/3] grant with categories support --- .../lib/events/grant-token-categories.yml | 126 ++++++++++++++++++ lib/pubnub/events/grant.rb | 22 ++- lib/pubnub/validators/grant.rb | 22 +++ spec/lib/events/grant_spec.rb | 69 ++++++++++ 4 files changed, 235 insertions(+), 4 deletions(-) create mode 100644 fixtures/vcr_cassettes/lib/events/grant-token-categories.yml diff --git a/fixtures/vcr_cassettes/lib/events/grant-token-categories.yml b/fixtures/vcr_cassettes/lib/events/grant-token-categories.yml new file mode 100644 index 000000000..db659a556 --- /dev/null +++ b/fixtures/vcr_cassettes/lib/events/grant-token-categories.yml @@ -0,0 +1,126 @@ +--- +http_interactions: +- request: + method: post + uri: http://ps.pndsn.com/v3/pam/sub-a-mock-key/grant?pnsdk=PubNub-Ruby/4.6.2&signature=v2.yZaPGX91wlB3GD03L8BdyJVNmMT6uJMsgpLitWSRPm8×tamp=1635159481&uuid=ruby-test-uuid + body: + encoding: UTF-8 + string: '{"ttl":60,"permissions":{"uuid":"authorized_uuid","resources":{"channels":{"id":1},"groups":{},"uuids":{}},"patterns":{"channels":{},"groups":{},"uuids":{}},"categories":{"channels":32,"uuids":32}}}' + headers: + User-Agent: + - HTTPClient/1.0 (2.8.3, ruby 2.7.0 (2019-12-25)) + Accept: + - "*/*" + Date: + - Mon, 25 Oct 2021 10:58:26 GMT + Content-Type: + - application/json + response: + status: + code: 200 + message: OK + headers: + Date: + - Mon, 25 Oct 2021 10:58:26 GMT + Content-Type: + - text/javascript; charset=UTF-8 + Content-Length: + - '281' + Connection: + - keep-alive + Access-Control-Allow-Origin: + - "*" + Access-Control-Allow-Methods: + - GET + Access-Control-Allow-Headers: + - Origin, X-Requested-With, Content-Type, Accept + Cache-Control: + - no-cache, no-store, must-revalidate + body: + encoding: UTF-8 + string: '{"data":{"message":"Success","token":"qEF2AkF0GmF2jdJDdHRsGDxDcmVzpURjaGFuoWJpZAFDZ3JwoEN1c3KgQ3NwY6BEdXVpZKBDcGF0pURjaGFuoENncnCgQ3VzcqBDc3BjoER1dWlkoERtZXRhoER1dWlkb2F1dGhvcml6ZWRfdXVpZENzaWdYIDdEOLM5Z0KUgMQmmNqjLaRQo-742ZKMV2Z69-fZipCf"},"service":"Access + Manager","status":200}' + recorded_at: Mon, 25 Oct 2021 10:58:26 GMT +- request: + method: post + uri: http://ps.pndsn.com/v3/pam/sub-a-mock-key/grant?pnsdk=PubNub-Ruby/4.6.2&signature=v2.yZaPGX91wlB3GD03L8BdyJVNmMT6uJMsgpLitWSRPm8×tamp=1635159481&uuid=ruby-test-uuid + body: + encoding: UTF-8 + string: '{"ttl":60,"permissions":{"uuid":"authorized_uuid","resources":{"channels":{},"groups":{},"uuids":{}},"patterns":{"channels":{},"groups":{},"uuids":{}},"categories":{"uuids":32}}}' + headers: + User-Agent: + - HTTPClient/1.0 (2.8.3, ruby 2.7.0 (2019-12-25)) + Accept: + - "*/*" + Date: + - Mon, 25 Oct 2021 10:58:26 GMT + Content-Type: + - application/json + response: + status: + code: 200 + message: OK + headers: + Date: + - Mon, 25 Oct 2021 10:58:26 GMT + Content-Type: + - text/javascript; charset=UTF-8 + Content-Length: + - '281' + Connection: + - keep-alive + Access-Control-Allow-Origin: + - "*" + Access-Control-Allow-Methods: + - GET + Access-Control-Allow-Headers: + - Origin, X-Requested-With, Content-Type, Accept + Cache-Control: + - no-cache, no-store, must-revalidate + body: + encoding: UTF-8 + string: '{"data":{"message":"Success","token":"qEF2AkF0GmF2jdJDdHRsGDxDcmVzpURjaGFuoWJpZAFDZ3JwoEN1c3KgQ3NwY6BEdXVpZKBDcGF0pURjaGFuoENncnCgQ3VzcqBDc3BjoER1dWlkoERtZXRhoER1dWlkb2F1dGhvcml6ZWRfdXVpZENzaWdYIDdEOLM5Z0KUgMQmmNqjLaRQo-742ZKMV2Z69-fZipCf"},"service":"Access + Manager","status":200}' + recorded_at: Mon, 25 Oct 2021 10:58:26 GMT +- request: + method: post + uri: http://ps.pndsn.com/v3/pam/sub-a-mock-key/grant?pnsdk=PubNub-Ruby/4.6.2&signature=v2.yZaPGX91wlB3GD03L8BdyJVNmMT6uJMsgpLitWSRPm8×tamp=1635159481&uuid=ruby-test-uuid + body: + encoding: UTF-8 + string: '{"ttl":60,"permissions":{"resources":{"channels":{},"groups":{},"uuids":{}},"patterns":{"channels":{},"groups":{},"uuids":{}},"categories":{"channels":32}}}' + headers: + User-Agent: + - HTTPClient/1.0 (2.8.3, ruby 2.7.0 (2019-12-25)) + Accept: + - "*/*" + Date: + - Mon, 25 Oct 2021 10:58:26 GMT + Content-Type: + - application/json + response: + status: + code: 200 + message: OK + headers: + Date: + - Mon, 25 Oct 2021 10:58:26 GMT + Content-Type: + - text/javascript; charset=UTF-8 + Content-Length: + - '281' + Connection: + - keep-alive + Access-Control-Allow-Origin: + - "*" + Access-Control-Allow-Methods: + - GET + Access-Control-Allow-Headers: + - Origin, X-Requested-With, Content-Type, Accept + Cache-Control: + - no-cache, no-store, must-revalidate + body: + encoding: UTF-8 + string: '{"data":{"message":"Success","token":"qEF2AkF0GmF2jdJDdHRsGDxDcmVzpURjaGFuoWJpZAFDZ3JwoEN1c3KgQ3NwY6BEdXVpZKBDcGF0pURjaGFuoENncnCgQ3VzcqBDc3BjoER1dWlkoERtZXRhoER1dWlkb2F1dGhvcml6ZWRfdXVpZENzaWdYIDdEOLM5Z0KUgMQmmNqjLaRQo-742ZKMV2Z69-fZipCf"},"service":"Access + Manager","status":200}' + recorded_at: Mon, 25 Oct 2021 10:58:26 GMT +recorded_with: VCR 6.0.0 diff --git a/lib/pubnub/events/grant.rb b/lib/pubnub/events/grant.rb index 599880c83..26ab9f1bd 100644 --- a/lib/pubnub/events/grant.rb +++ b/lib/pubnub/events/grant.rb @@ -7,6 +7,7 @@ class Grant < PAM def initialize(options, app) @event = current_operation + @categories = format_categories(options[:categories]) super @uuids = Formatter::format_uuid(options[:uuids]) unless options[:uuids].nil? @ttl ||= Pubnub::Constants::DEFAULT_TTL @@ -18,10 +19,22 @@ def current_operation Pubnub::Constants::OPERATION_GRANT end + def format_categories(categories) + formatted = case categories + when NilClass then return nil + when Array then categories.map { |category| category.to_s.strip } + when String, Symbol then categories.to_s.split(',').map(&:strip) + else return categories + end + + formatted.empty? ? nil : formatted + end + def parameters(signature = false) - write = [0, '0', false].include?(@write) ? 0 : 1 - read = [0, '0', false].include?(@read) ? 0 : 1 - manage = [0, '0', false].include?(@manage) ? 0 : 1 # unless @group.blank? + category_grant = !@categories.nil? + write = (category_grant && @write.nil?) || [0, '0', false].include?(@write) ? 0 : 1 + read = (category_grant && @read.nil?) || [0, '0', false].include?(@read) ? 0 : 1 + manage = (category_grant && @manage.nil?) || [0, '0', false].include?(@manage) ? 0 : 1 # unless @group.blank? delete = [0, '0', false].include?(@delete) ? 0 : 1 unless @delete.nil? get = [0, '0', false].include?(@get) ? 0 : 1 unless @get.nil? update = [0, '0', false].include?(@update) ? 0 : 1 unless @update.nil? @@ -35,7 +48,8 @@ def parameters(signature = false) g: get, u: update, j: join, - ttl: @ttl }.delete_if { |_k, v| v.nil? }.merge(super(signature)) + ttl: @ttl, + category: (@categories.join(',') unless @categories.nil?) }.delete_if { |_k, v| v.nil? }.merge(super(signature)) end def path diff --git a/lib/pubnub/validators/grant.rb b/lib/pubnub/validators/grant.rb index a003f779d..24345e59f 100644 --- a/lib/pubnub/validators/grant.rb +++ b/lib/pubnub/validators/grant.rb @@ -12,6 +12,7 @@ def validate! validate_keys! validate_ttl! validate_uuids! + validate_categories! end private @@ -37,6 +38,27 @@ def validate_ttl! ) end + def validate_categories! + return if @categories.nil? + + raise( + ArgumentError.new(object: self, message: ':categories has to be kind of String, Symbol or Array for grant event.'), + ':categories has to be kind of String, Symbol or Array for grant event.' + ) unless @categories.is_a?(Array) + + raise( + ArgumentError.new(object: self, message: ':categories supports only channels and uuids for grant event.'), + ':categories supports only channels and uuids for grant event.' + ) unless (@categories - %w[channels uuids]).empty? + + # Use the per-call option: PAM#initialize resets @auth_key to options[:auth_key], so a client-level + # auth_key is never sent as the grant target `auth`. + raise( + ArgumentError.new(object: self, message: ':auth_key is required for grant event on categories.'), + ':auth_key is required for grant event on categories.' + ) if @given_options[:auth_key].nil? || @given_options[:auth_key].to_s.empty? + end + def validate_uuids! return if @uuids.nil? diff --git a/spec/lib/events/grant_spec.rb b/spec/lib/events/grant_spec.rb index a6509c7f0..b3e2de174 100644 --- a/spec/lib/events/grant_spec.rb +++ b/spec/lib/events/grant_spec.rb @@ -39,6 +39,75 @@ end end + context "with categories" do + it "grants category permissions with r/w/m defaulted to 0" do + VCR.use_cassette("lib/events/grant-categories", record: :none) do + envelope = pubnub.grant( + auth_key: "category-auth", + categories: [:channels, :uuids], + get: true + ).value + + expect(envelope.is_a?(Pubnub::ErrorEnvelope)).to eq false + expect(envelope.status).to satisfies_schema Pubnub::Schemas::Envelope::StatusSchema.new + expect(envelope.result).to satisfies_schema Pubnub::Schemas::Envelope::ResultSchema.new + expect(envelope.result[:data]["categories"]["channels"]["auths"]["category-auth"]["g"]).to eq 1 + end + end + + it "grants category permissions along with a channel" do + VCR.use_cassette("lib/events/grant-categories", record: :none) do + envelope = pubnub.grant( + auth_key: "category-auth", + channel: :demo, + categories: "uuids", + get: true + ).value + + expect(envelope.is_a?(Pubnub::ErrorEnvelope)).to eq false + expect(envelope.result[:data]["categories"]["uuids"]).not_to be_nil + end + end + + it "sends explicit permissions and forms ErrorEnvelope on server rejection" do + VCR.use_cassette("lib/events/grant-categories", record: :none) do + envelope = pubnub.grant( + auth_key: "category-auth", + categories: :channels, + get: true, + read: true + ).value + + expect(envelope.is_a?(Pubnub::ErrorEnvelope)).to eq true + expect(envelope.status[:code]).to eq 400 + expect(envelope.status).to satisfies_schema Pubnub::Schemas::Envelope::StatusSchema.new + end + end + + it "raises error for unsupported category" do + expect { pubnub.grant( + auth_key: "category-auth", + categories: [:users], + get: true + ).value }.to raise_error(Pubnub::ArgumentError) + end + + it "raises error for unsupported categories type" do + expect { pubnub.grant( + auth_key: "category-auth", + categories: 123, + get: true + ).value }.to raise_error(Pubnub::ArgumentError) + end + + it "raises error when auth_key is not passed with the call" do + expect { pubnub.grant( + categories: [:channels], + get: true + ).value }.to raise_error(Pubnub::ArgumentError) + end + end + it "forms valid ErrorEnvelope on HTTPX::TimeoutError" do allow_any_instance_of(Pubnub::HttpDispatcher).to receive(:get).and_raise(HTTPX::TimeoutError.new(nil, "timeout")) From f9118dd99ace508454b61326851db903d5aa7150 Mon Sep 17 00:00:00 2001 From: PubNub Release Bot <120067856+pubnub-release-bot@users.noreply.github.com> Date: Wed, 21 Oct 2026 05:08:15 +0000 Subject: [PATCH 3/3] PubNub SDK v6.2.0 release. --- .pubnub.yml | 13 +++++++++---- CHANGELOG.md | 6 ++++++ Gemfile.lock | 2 +- VERSION | 2 +- lib/pubnub/version.rb | 2 +- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/.pubnub.yml b/.pubnub.yml index 6dadee3f1..177c2a659 100644 --- a/.pubnub.yml +++ b/.pubnub.yml @@ -1,6 +1,11 @@ --- -version: "6.1.1" +version: "6.2.0" changelog: + - date: 2026-10-21 + version: v6.2.0 + changes: + - type: feature + text: "Add `categories` support to `grant` and `grant_token` for granting enumeration (`get`) permission on all `channels` and `uuids` metadata." - date: 2026-07-21 version: v6.1.1 changes: @@ -656,7 +661,7 @@ sdks: - x86-64 - distribution-type: package distribution-repository: RubyGems - package-name: pubnub-6.1.1.gem + package-name: pubnub-6.2.0.gem location: https://rubygems.org/gems/pubnub requires: - name: addressable @@ -761,8 +766,8 @@ sdks: - x86-64 - distribution-type: library distribution-repository: GitHub release - package-name: pubnub-6.1.1.gem - location: https://github.com/pubnub/ruby/releases/download/v6.1.1/pubnub-6.1.1.gem + package-name: pubnub-6.2.0.gem + location: https://github.com/pubnub/ruby/releases/download/v6.2.0/pubnub-6.2.0.gem requires: - name: addressable min-version: 2.0.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index dba99b0ea..2a3a2f408 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## v6.2.0 +October 21 2026 + +#### Added +- Add `categories` support to `grant` and `grant_token` for granting enumeration (`get`) permission on all `channels` and `uuids` metadata. + ## v6.1.1 July 21 2026 diff --git a/Gemfile.lock b/Gemfile.lock index 385485b10..7436756db 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - pubnub (6.1.1) + pubnub (6.2.0) addressable (>= 2.0.0) base64 concurrent-ruby (~> 1.3.4) diff --git a/VERSION b/VERSION index f3b5af39e..6abaeb2f9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.1.1 +6.2.0 diff --git a/lib/pubnub/version.rb b/lib/pubnub/version.rb index f4f9faec3..6a568a699 100644 --- a/lib/pubnub/version.rb +++ b/lib/pubnub/version.rb @@ -1,4 +1,4 @@ # Toplevel Pubnub module. module Pubnub - VERSION = '6.1.1'.freeze + VERSION = '6.2.0'.freeze end