pubnub 4.6.2 → 4.7.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of pubnub might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +3 -1
- data/.github/workflows/commands-handler.yml +27 -0
- data/.github/workflows/release/versions.json +17 -0
- data/.github/workflows/release.yml +58 -0
- data/.github/workflows/run_acceptance_tests.yml +46 -0
- data/.gitignore +18 -0
- data/.pubnub.yml +9 -4
- data/.travis.yml +0 -4
- data/CHANGELOG.md +6 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +42 -1
- data/VERSION +1 -1
- data/config/cucumber.yml +7 -0
- data/features/step_definitions/grant_token_steps.rb +167 -0
- data/features/support/env.rb +15 -0
- data/features/support/helper.rb +39 -0
- data/features/support/hooks.rb +97 -0
- data/lib/pubnub/cbor.rb +182 -0
- data/lib/pubnub/client/events.rb +1 -1
- data/lib/pubnub/client.rb +13 -0
- data/lib/pubnub/constants.rb +9 -7
- data/lib/pubnub/event.rb +18 -9
- data/lib/pubnub/events/grant_token.rb +172 -0
- data/lib/pubnub/validators/grant_token.rb +51 -0
- data/lib/pubnub/version.rb +1 -1
- metadata +20 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 60893402c5e2e35de1e5f78237b7a031920bf5c1eb3533c33bfc0b20441d2616
|
4
|
+
data.tar.gz: e3d018155805ab0a09453d8560aaf1ddcc35fe9d040229c0a3e9ec1c235bafdd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '09fd547306bf18cc940d87ca6fe568c9e7fca6b5a6a309dc7e0fb159af537d604b0ff846857df420716d9092963d116bb08be6fb573de55c9913599eafae8873'
|
7
|
+
data.tar.gz: bd33c010676d951a71cadcc76184357441c911e4ab763a95e34f783033a942c88ecffb3e3afecb0fc74184964eff11d43e07cb6e908161045e8a730b9438b25a
|
data/.github/CODEOWNERS
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
name: Commands processor
|
2
|
+
|
3
|
+
on:
|
4
|
+
issue_comment:
|
5
|
+
types: [created]
|
6
|
+
|
7
|
+
jobs:
|
8
|
+
process:
|
9
|
+
name: Process command
|
10
|
+
if: ${{ github.event.issue.pull_request && endsWith(github.repository, '-private') != true && startsWith(github.event.comment.body, '@client-engineering-bot ') }}
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
steps:
|
13
|
+
- name: Checkout repository
|
14
|
+
uses: actions/checkout@v2
|
15
|
+
- name: Checkout release actions
|
16
|
+
uses: actions/checkout@v2
|
17
|
+
with:
|
18
|
+
repository: pubnub/client-engineering-deployment-tools
|
19
|
+
ref: v1
|
20
|
+
token: ${{ secrets.GH_TOKEN }}
|
21
|
+
path: .github/.release/actions
|
22
|
+
- name: Process changelog entries
|
23
|
+
uses: ./.github/.release/actions/actions/commands
|
24
|
+
with:
|
25
|
+
token: ${{ secrets.GH_TOKEN }}
|
26
|
+
listener: client-engineering-bot
|
27
|
+
jira-api-key: ${{ secrets.JIRA_API_KEY }}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
{
|
2
|
+
".pubnub.yml": [
|
3
|
+
{ "pattern": "^version: \"(.+)\"$", "cleared": true },
|
4
|
+
{ "pattern": "\\s+package-name: pubnub-(v?(\\d+\\.?){2,}([a-zA-Z0-9-]+(\\.?\\d+)?)?).gem$", "cleared": true },
|
5
|
+
{ "pattern": "/releases/download/(v?(\\d+\\.?){2,}([a-zA-Z0-9-]+(\\.?\\d+)?)?)/pubnub-.*.gem$", "cleared": false },
|
6
|
+
{ "pattern": "/releases/download/.*/pubnub-(v?(\\d+\\.?){2,}([a-zA-Z0-9-]+(\\.?\\d+)?)?).gem$", "cleared": true }
|
7
|
+
],
|
8
|
+
"VERSION": [
|
9
|
+
{ "pattern": "(v?(\\d+\\.?){2,}([a-zA-Z0-9-]+(\\.?\\d+)?)?)", "cleared": true }
|
10
|
+
],
|
11
|
+
"Gemfile.lock": [
|
12
|
+
{ "pattern": "^\\s{2,}pubnub \\((v?(\\d+\\.?){2,}([a-zA-Z0-9-]+(\\.?\\d+)?)?)\\)$", "cleared": true }
|
13
|
+
],
|
14
|
+
"lib/pubnub/version.rb": [
|
15
|
+
{ "pattern": "^\\s{2,}VERSION = '(v?(\\d+\\.?){2,}([a-zA-Z0-9-]+(\\.?\\d+)?)?)'.freeze", "cleared": true }
|
16
|
+
]
|
17
|
+
}
|
@@ -0,0 +1,58 @@
|
|
1
|
+
name: Automated product release
|
2
|
+
|
3
|
+
on:
|
4
|
+
pull_request:
|
5
|
+
branches: [ master ]
|
6
|
+
types: [ closed ]
|
7
|
+
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
check-release:
|
11
|
+
name: Check release required
|
12
|
+
runs-on: ubuntu-latest
|
13
|
+
if: ${{ github.event.pull_request.merged && endsWith(github.repository, '-private') != true }}
|
14
|
+
outputs:
|
15
|
+
release: ${{ steps.check.outputs.ready }}
|
16
|
+
steps:
|
17
|
+
- name: Checkout actions
|
18
|
+
uses: actions/checkout@v2
|
19
|
+
with:
|
20
|
+
repository: pubnub/client-engineering-deployment-tools
|
21
|
+
ref: v1
|
22
|
+
token: ${{ secrets.GH_TOKEN }}
|
23
|
+
path: .github/.release/actions
|
24
|
+
- id: check
|
25
|
+
name: Check pre-release completed
|
26
|
+
uses: ./.github/.release/actions/actions/checks/release
|
27
|
+
with:
|
28
|
+
token: ${{ secrets.GH_TOKEN }}
|
29
|
+
publish:
|
30
|
+
name: Publish package
|
31
|
+
runs-on: ubuntu-latest
|
32
|
+
needs: check-release
|
33
|
+
if: ${{ needs.check-release.outputs.release == 'true' }}
|
34
|
+
steps:
|
35
|
+
- name: Checkout repository
|
36
|
+
uses: actions/checkout@v2
|
37
|
+
with:
|
38
|
+
# This should be the same as the one specified for on.pull_request.branches
|
39
|
+
ref: master
|
40
|
+
- name: Checkout actions
|
41
|
+
uses: actions/checkout@v2
|
42
|
+
with:
|
43
|
+
repository: pubnub/client-engineering-deployment-tools
|
44
|
+
ref: v1
|
45
|
+
token: ${{ secrets.GH_TOKEN }}
|
46
|
+
path: .github/.release/actions
|
47
|
+
- name: Publish to RubyGems
|
48
|
+
uses: ./.github/.release/actions/actions/services/rubygems
|
49
|
+
with:
|
50
|
+
token: ${{ secrets.GH_TOKEN }}
|
51
|
+
gem-username: ${{ secrets.GEMS_ACCOUNT }}
|
52
|
+
gem-token: ${{ secrets.GEMS_API_KEY }}
|
53
|
+
- name: Create Release
|
54
|
+
uses: ./.github/.release/actions/actions/services/github-release
|
55
|
+
with:
|
56
|
+
token: ${{ secrets.GH_TOKEN }}
|
57
|
+
jira-api-key: ${{ secrets.JIRA_API_KEY }}
|
58
|
+
last-service: true
|
@@ -0,0 +1,46 @@
|
|
1
|
+
name: run_acceptance_tests
|
2
|
+
|
3
|
+
on: [push]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
name: Perform Acceptance BDD tests
|
8
|
+
runs-on: ubuntu-latest
|
9
|
+
steps:
|
10
|
+
- name: Checkout project
|
11
|
+
uses: actions/checkout@v2
|
12
|
+
- name: Checkout mock-server action
|
13
|
+
uses: actions/checkout@v2
|
14
|
+
with:
|
15
|
+
repository: pubnub/client-engineering-deployment-tools
|
16
|
+
ref: github-actions
|
17
|
+
token: ${{ secrets.GH_TOKEN }}
|
18
|
+
path: client-engineering-deployment-tools
|
19
|
+
- name: Run mock server action
|
20
|
+
uses: ./client-engineering-deployment-tools/actions/mock-server
|
21
|
+
with:
|
22
|
+
token: ${{ secrets.GH_TOKEN }}
|
23
|
+
- name: Install dependencies
|
24
|
+
run: |
|
25
|
+
sudo gem install bundler &&
|
26
|
+
bundle install
|
27
|
+
- name: Run acceptance tests (optional)
|
28
|
+
run: bundle exec cucumber sdk-specifications/features/ -p mock -p run_beta -p report_beta
|
29
|
+
continue-on-error: true
|
30
|
+
- name: Run acceptance tests (required)
|
31
|
+
run: bundle exec cucumber sdk-specifications/features/ -p mock -p run_main -p report_main
|
32
|
+
- name: Combine test results
|
33
|
+
if: always()
|
34
|
+
run: |
|
35
|
+
sudo npm install -g junit-report-merger &&
|
36
|
+
jrm ./main.xml "./main/**/*.xml" &&
|
37
|
+
jrm ./beta.xml "./beta/**/*.xml"
|
38
|
+
- name: Expose main report
|
39
|
+
uses: actions/upload-artifact@v2
|
40
|
+
if: always()
|
41
|
+
with:
|
42
|
+
name: acceptance-test-reports
|
43
|
+
path: |
|
44
|
+
main.xml
|
45
|
+
beta.xml
|
46
|
+
retention-days: 7
|
data/.gitignore
CHANGED
@@ -8,3 +8,21 @@ log/*
|
|
8
8
|
examples/test.rb
|
9
9
|
coverage
|
10
10
|
dev_scripts
|
11
|
+
|
12
|
+
# GitHub Actions #
|
13
|
+
##################
|
14
|
+
.github/.release
|
15
|
+
.bundle/ruby
|
16
|
+
|
17
|
+
.travis/README.md
|
18
|
+
.travis/scripts
|
19
|
+
|
20
|
+
deployment_keys
|
21
|
+
deployment_keys-private
|
22
|
+
deployment_keys.tar
|
23
|
+
|
24
|
+
.travis/github-labels-policy.json
|
25
|
+
.travis/github-labels.json
|
26
|
+
/.rvmrc
|
27
|
+
/.bundle/
|
28
|
+
/features/*.feature
|
data/.pubnub.yml
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
---
|
2
|
-
version: "4.
|
2
|
+
version: "4.7.0"
|
3
3
|
changelog:
|
4
|
+
- date: 2021-11-09
|
5
|
+
version: v4.7.0
|
6
|
+
changes:
|
7
|
+
- type: feature
|
8
|
+
text: "Grant_token allows generation of signed token with permissions for channels, channel groups and uuids."
|
4
9
|
-
|
5
10
|
changes:
|
6
11
|
-
|
@@ -600,7 +605,7 @@ sdks:
|
|
600
605
|
- x86-64
|
601
606
|
- distribution-type: package
|
602
607
|
distribution-repository: RubyGems
|
603
|
-
package-name: pubnub-4.
|
608
|
+
package-name: pubnub-4.7.0.gem
|
604
609
|
location: https://rubygems.org/gems/pubnub
|
605
610
|
requires:
|
606
611
|
- name: addressable
|
@@ -705,8 +710,8 @@ sdks:
|
|
705
710
|
- x86-64
|
706
711
|
- distribution-type: library
|
707
712
|
distribution-repository: GitHub release
|
708
|
-
package-name: pubnub-
|
709
|
-
location: https://github.com/pubnub/ruby/releases/download/v4.
|
713
|
+
package-name: pubnub-v4.7.0.gem
|
714
|
+
location: https://github.com/pubnub/ruby/releases/download/v4.7.0/pubnub-4.7.0.gem
|
710
715
|
requires:
|
711
716
|
- name: addressable
|
712
717
|
min-version: 2.0.0
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
## v4.7.0
|
2
|
+
November 09 2021
|
3
|
+
|
4
|
+
#### Added
|
5
|
+
- Grant_token allows generation of signed token with permissions for channels, channel groups and uuids.
|
6
|
+
|
1
7
|
##### v4.6.2
|
2
8
|
|
3
9
|
- Add new `sdks` section to `.pubnub.yml` with information about available artifacts and distribution variants.
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
pubnub (4.
|
4
|
+
pubnub (4.7.0)
|
5
5
|
addressable (>= 2.0.0)
|
6
6
|
concurrent-ruby (~> 1.1.5)
|
7
7
|
concurrent-ruby-edge (~> 0.5.0)
|
@@ -19,6 +19,7 @@ GEM
|
|
19
19
|
awesome_print (1.8.0)
|
20
20
|
binding_of_caller (0.8.0)
|
21
21
|
debug_inspector (>= 0.0.1)
|
22
|
+
builder (3.2.4)
|
22
23
|
codacy-coverage (2.2.1)
|
23
24
|
simplecov
|
24
25
|
coderay (1.1.3)
|
@@ -27,6 +28,37 @@ GEM
|
|
27
28
|
concurrent-ruby (~> 1.1.5)
|
28
29
|
crack (0.4.3)
|
29
30
|
safe_yaml (~> 1.0.0)
|
31
|
+
cucumber (7.0.0)
|
32
|
+
builder (~> 3.2, >= 3.2.4)
|
33
|
+
cucumber-core (~> 10.0, >= 10.0.1)
|
34
|
+
cucumber-create-meta (~> 6.0, >= 6.0.1)
|
35
|
+
cucumber-cucumber-expressions (~> 12.1, >= 12.1.1)
|
36
|
+
cucumber-gherkin (~> 20.0, >= 20.0.1)
|
37
|
+
cucumber-html-formatter (~> 16.0, >= 16.0.1)
|
38
|
+
cucumber-messages (~> 17.0, >= 17.0.1)
|
39
|
+
cucumber-wire (~> 6.0, >= 6.0.1)
|
40
|
+
diff-lcs (~> 1.4, >= 1.4.4)
|
41
|
+
mime-types (~> 3.3, >= 3.3.1)
|
42
|
+
multi_test (~> 0.1, >= 0.1.2)
|
43
|
+
sys-uname (~> 1.2, >= 1.2.2)
|
44
|
+
cucumber-core (10.0.1)
|
45
|
+
cucumber-gherkin (~> 20.0, >= 20.0.1)
|
46
|
+
cucumber-messages (~> 17.0, >= 17.0.1)
|
47
|
+
cucumber-tag-expressions (~> 3.0, >= 3.0.1)
|
48
|
+
cucumber-create-meta (6.0.1)
|
49
|
+
cucumber-messages (~> 17.0, >= 17.0.1)
|
50
|
+
sys-uname (~> 1.2, >= 1.2.2)
|
51
|
+
cucumber-cucumber-expressions (12.1.3)
|
52
|
+
cucumber-gherkin (20.0.1)
|
53
|
+
cucumber-messages (~> 17.0, >= 17.0.1)
|
54
|
+
cucumber-html-formatter (16.0.1)
|
55
|
+
cucumber-messages (~> 17.0, >= 17.0.1)
|
56
|
+
cucumber-messages (17.1.1)
|
57
|
+
cucumber-tag-expressions (3.0.1)
|
58
|
+
cucumber-wire (6.1.1)
|
59
|
+
cucumber-core (~> 10.0, >= 10.0.1)
|
60
|
+
cucumber-cucumber-expressions (~> 12.1, >= 12.1.2)
|
61
|
+
cucumber-messages (~> 17.0, >= 17.0.1)
|
30
62
|
debug_inspector (0.0.3)
|
31
63
|
diff-lcs (1.4.4)
|
32
64
|
docile (1.3.2)
|
@@ -68,6 +100,7 @@ GEM
|
|
68
100
|
dry-equalizer (~> 0.2)
|
69
101
|
dry-initializer (~> 3.0)
|
70
102
|
dry-schema (~> 1.5)
|
103
|
+
ffi (1.13.1)
|
71
104
|
ffi (1.13.1-java)
|
72
105
|
hashdiff (1.0.1)
|
73
106
|
httpclient (2.8.3)
|
@@ -75,6 +108,10 @@ GEM
|
|
75
108
|
json (2.3.1)
|
76
109
|
json (2.3.1-java)
|
77
110
|
method_source (1.0.0)
|
111
|
+
mime-types (3.3.1)
|
112
|
+
mime-types-data (~> 3.2015)
|
113
|
+
mime-types-data (3.2021.0901)
|
114
|
+
multi_test (0.1.2)
|
78
115
|
parallel (1.19.2)
|
79
116
|
parser (2.7.1.4)
|
80
117
|
ast (~> 2.4.1)
|
@@ -130,6 +167,8 @@ GEM
|
|
130
167
|
simplecov-html (0.12.2)
|
131
168
|
spoon (0.0.6)
|
132
169
|
ffi
|
170
|
+
sys-uname (1.2.2)
|
171
|
+
ffi (~> 1.1)
|
133
172
|
timers (4.3.0)
|
134
173
|
unicode-display_width (1.7.0)
|
135
174
|
vcr (6.0.0)
|
@@ -145,12 +184,14 @@ PLATFORMS
|
|
145
184
|
DEPENDENCIES
|
146
185
|
awesome_print
|
147
186
|
codacy-coverage
|
187
|
+
cucumber
|
148
188
|
pry
|
149
189
|
pry-rescue
|
150
190
|
pry-stack_explorer
|
151
191
|
pubnub!
|
152
192
|
rr
|
153
193
|
rspec
|
194
|
+
rspec-expectations
|
154
195
|
rspec-retry
|
155
196
|
rubocop
|
156
197
|
simplecov (>= 0.12)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
4.
|
1
|
+
4.7.0
|
data/config/cucumber.yml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
default: -p mock -p run_main
|
2
|
+
run_main: -t "not @skip and not @na=ruby and not @beta"
|
3
|
+
report_main: -f junit --out main
|
4
|
+
run_beta: -t "not @skip and not @na=ruby and @beta"
|
5
|
+
report_beta: -f junit --out beta
|
6
|
+
mock: SERVER_MOCK=true PAM_SUB_KEY=subKey PAM_PUB_KEY=pubKey PAM_SEC_KEY=secKey SERVER_HOST=localhost SERVER_PORT=8090
|
7
|
+
prod: SERVER_MOCK=false
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'pubnub'
|
2
|
+
|
3
|
+
token_with_all = "qEF2AkF0GmEI03xDdHRsGDxDcmVzpURjaGFuoWljaGFubmVsLTEY70NncnChb2NoYW5uZWxfZ3JvdXAtMQVDdXNyoENzcGOgRHV1aWShZnV1aWQtMRhoQ3BhdKVEY2hhbqFtXmNoYW5uZWwtXFMqJBjvQ2dycKF0XjpjaGFubmVsX2dyb3VwLVxTKiQFQ3VzcqBDc3BjoER1dWlkoWpedXVpZC1cUyokGGhEbWV0YaBEdXVpZHR0ZXN0LWF1dGhvcml6ZWQtdXVpZENzaWdYIPpU-vCe9rkpYs87YUrFNWkyNq8CVvmKwEjVinnDrJJc"
|
4
|
+
|
5
|
+
Given('I have a keyset with access manager enabled') do
|
6
|
+
expect(ENV['PAM_SUB_KEY']).not_to be_nil
|
7
|
+
expect(ENV['PAM_PUB_KEY']).not_to be_nil
|
8
|
+
expect(ENV['PAM_SEC_KEY']).not_to be_nil
|
9
|
+
@pn_configuration['subscribe_key'] = ENV['PAM_SUB_KEY']
|
10
|
+
@pn_configuration['publish_key'] = ENV['PAM_PUB_KEY']
|
11
|
+
@pn_configuration['secret_key'] = ENV['PAM_SEC_KEY']
|
12
|
+
logger = Logger.new(STDOUT)
|
13
|
+
logger.level = Logger::DEBUG
|
14
|
+
@pn_configuration['logger'] = logger
|
15
|
+
|
16
|
+
@pubnub = Pubnub.new(@pn_configuration)
|
17
|
+
end
|
18
|
+
|
19
|
+
Given('the authorized UUID {string}') do |uuid|
|
20
|
+
@grant_token_state[:authorized_uuid] = uuid
|
21
|
+
end
|
22
|
+
|
23
|
+
Given('the TTL {int}') do |ttl|
|
24
|
+
@grant_token_state[:ttl] = ttl
|
25
|
+
end
|
26
|
+
|
27
|
+
Given('the {string} {resourceType} resource access permissions') do |name, resource_type|
|
28
|
+
@grant_token_state[:current_name] = name
|
29
|
+
|
30
|
+
@grant_token_state[:current_grant][name] = {
|
31
|
+
:name => name,
|
32
|
+
:resource_type => resource_type,
|
33
|
+
:permission_type => []
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
And('grant resource permission {permissionType}') do |permission_type|
|
38
|
+
current_name = @grant_token_state[:current_name]
|
39
|
+
@grant_token_state[:current_grant][current_name][:permission_type].push(permission_type)
|
40
|
+
end
|
41
|
+
|
42
|
+
Given('the {string} {resourceType} pattern access permissions') do |pattern, resource_type|
|
43
|
+
@grant_token_state[:current_pattern] = pattern
|
44
|
+
@grant_token_state[:current_grant][pattern] = {
|
45
|
+
:pattern => pattern,
|
46
|
+
:resource_type => resource_type,
|
47
|
+
:permission_type => []
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
And('grant pattern permission {permissionType}') do |permission_type|
|
52
|
+
current_pattern = @grant_token_state[:current_pattern]
|
53
|
+
@grant_token_state[:current_grant][current_pattern][:permission_type].push(permission_type)
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
When('I grant a token specifying those permissions') do
|
58
|
+
res = call_grant_token(@pubnub, @grant_token_state)
|
59
|
+
@grant_token_state[:parsed_token] = @pubnub.parse_token(res.result[:data]["token"])
|
60
|
+
end
|
61
|
+
|
62
|
+
Then('the token contains the authorized UUID {string}') do |expected_uuid|
|
63
|
+
expect(@grant_token_state[:parsed_token]["uuid"]).to eq expected_uuid
|
64
|
+
end
|
65
|
+
|
66
|
+
Then('the token contains the TTL {int}') do |ttl|
|
67
|
+
expect(@grant_token_state[:parsed_token]["ttl"]).to eq ttl
|
68
|
+
end
|
69
|
+
|
70
|
+
Then('the token has {string} {resourceType} resource access permissions') do |resource, resource_type|
|
71
|
+
case resource_type
|
72
|
+
when "CHANNEL"
|
73
|
+
@grant_token_state[:token_resource] = @grant_token_state[:parsed_token]["res"]["chan"][resource]
|
74
|
+
when "CHANNEL_GROUP"
|
75
|
+
@grant_token_state[:token_resource] = @grant_token_state[:parsed_token]["res"]["grp"][resource]
|
76
|
+
when "UUID"
|
77
|
+
@grant_token_state[:token_resource] = @grant_token_state[:parsed_token]["res"]["uuid"][resource]
|
78
|
+
else
|
79
|
+
fail
|
80
|
+
end
|
81
|
+
|
82
|
+
expect(@grant_token_state[:token_resource]).not_to eq nil
|
83
|
+
end
|
84
|
+
|
85
|
+
Then('token resource permission {permissionType}') do |permission_type|
|
86
|
+
expect(has_permission(@grant_token_state[:token_resource], permission_type)).to eq true
|
87
|
+
end
|
88
|
+
|
89
|
+
Then('the token has {string} {resourceType} pattern access permissions') do |pattern, resourceType|
|
90
|
+
case resourceType
|
91
|
+
when "CHANNEL"
|
92
|
+
@grant_token_state[:token_resource] = @grant_token_state[:parsed_token]["pat"]["chan"][pattern]
|
93
|
+
when "CHANNEL_GROUP"
|
94
|
+
@grant_token_state[:token_resource] = @grant_token_state[:parsed_token]["pat"]["grp"][pattern]
|
95
|
+
when "UUID"
|
96
|
+
@grant_token_state[:token_resource] = @grant_token_state[:parsed_token]["pat"]["uuid"][pattern]
|
97
|
+
else
|
98
|
+
fail
|
99
|
+
end
|
100
|
+
|
101
|
+
expect(@grant_token_state[:token_resource]).not_to eq nil
|
102
|
+
end
|
103
|
+
|
104
|
+
And('token pattern permission {permissionType}') do |permission_type|
|
105
|
+
expect(has_permission(@grant_token_state[:token_resource], permission_type)).to eq true
|
106
|
+
end
|
107
|
+
|
108
|
+
Then('the token does not contain an authorized uuid') do
|
109
|
+
expect(@grant_token_state[:parsed_token]["uuid"]).to eq nil
|
110
|
+
end
|
111
|
+
|
112
|
+
Given('deny resource permission {permissionType}') do |permission_type|
|
113
|
+
current_name = @grant_token_state[:current_name]
|
114
|
+
@grant_token_state[:current_grant][current_name][:permission_type].filter { |p| p != permission_type }
|
115
|
+
end
|
116
|
+
|
117
|
+
When('I attempt to grant a token specifying those permissions') do
|
118
|
+
@grant_token_state[:error_response] = call_grant_token(@pubnub, @grant_token_state)
|
119
|
+
end
|
120
|
+
|
121
|
+
Then('an error is returned') do
|
122
|
+
expect(@grant_token_state[:error_response]).not_to eq nil
|
123
|
+
end
|
124
|
+
|
125
|
+
Then('the error status code is {int}') do |code|
|
126
|
+
expect(@grant_token_state[:error_response].status[:code]).to eq code
|
127
|
+
end
|
128
|
+
|
129
|
+
Then('the error message is {string}') do |error_message|
|
130
|
+
expect(parse_error_body(@grant_token_state[:error_response])["error"]["message"]).to eq error_message
|
131
|
+
end
|
132
|
+
|
133
|
+
Then('the error source is {string}') do |error_source|
|
134
|
+
expect(parse_error_body(@grant_token_state[:error_response])["error"]["source"]).to eq error_source
|
135
|
+
end
|
136
|
+
|
137
|
+
Then('the error detail message is {string}') do |details_message|
|
138
|
+
expect(parse_error_body(@grant_token_state[:error_response])["error"]["details"][0]["message"]).to eq details_message
|
139
|
+
end
|
140
|
+
|
141
|
+
Then('the error detail location is {string}') do |details_location|
|
142
|
+
expect(parse_error_body(@grant_token_state[:error_response])["error"]["details"][0]["location"]).to eq details_location
|
143
|
+
end
|
144
|
+
|
145
|
+
Then('the error detail location type is {string}') do |location_type|
|
146
|
+
expect(parse_error_body(@grant_token_state[:error_response])["error"]["details"][0]["locationType"]).to eq location_type
|
147
|
+
end
|
148
|
+
|
149
|
+
Given('I have a known token containing an authorized UUID') do
|
150
|
+
@grant_token_state[:token] = token_with_all
|
151
|
+
end
|
152
|
+
|
153
|
+
When('I parse the token') do
|
154
|
+
@grant_token_state[:parsed_token] = @pubnub.parse_token(@grant_token_state[:token])
|
155
|
+
end
|
156
|
+
|
157
|
+
Then('the parsed token output contains the authorized UUID {string}') do |expected_uuid|
|
158
|
+
expect(@grant_token_state[:parsed_token]["uuid"]).to eq expected_uuid
|
159
|
+
end
|
160
|
+
|
161
|
+
Given('I have a known token containing UUID resource permissions') do
|
162
|
+
@grant_token_state[:token] = token_with_all
|
163
|
+
end
|
164
|
+
|
165
|
+
Given('I have a known token containing UUID pattern Permissions') do
|
166
|
+
@grant_token_state[:token] = token_with_all
|
167
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
ParameterType(
|
2
|
+
name: 'resourceType',
|
3
|
+
regexp: /CHANNEL|CHANNEL_GROUP|UUID/,
|
4
|
+
type: String,
|
5
|
+
transformer: ->(s) { s }
|
6
|
+
)
|
7
|
+
|
8
|
+
ParameterType(
|
9
|
+
name: 'permissionType',
|
10
|
+
regexp: /READ|WRITE|GET|MANAGE|UPDATE|JOIN|DELETE|/,
|
11
|
+
type: String,
|
12
|
+
transformer: ->(s) { s }
|
13
|
+
)
|
14
|
+
|
15
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
def has_permission(perms, permission_type)
|
2
|
+
current_perm = nil
|
3
|
+
eval "current_perm = Pubnub::Permissions.res(#{permission_type.downcase}: true).calculate_bitmask"
|
4
|
+
perms & current_perm != 0
|
5
|
+
end
|
6
|
+
|
7
|
+
def prepare_permissions_map(grants, resource_type)
|
8
|
+
grants
|
9
|
+
.select { |_, value|
|
10
|
+
value[:resource_type] == resource_type
|
11
|
+
}
|
12
|
+
.to_h { |name, value|
|
13
|
+
empty_permissions = if value[:pattern]
|
14
|
+
Pubnub::Permissions.pat
|
15
|
+
else
|
16
|
+
Pubnub::Permissions.res
|
17
|
+
end
|
18
|
+
permissions = value[:permission_type].reduce(empty_permissions) { |accumulated_permissions, string_perm|
|
19
|
+
accumulated_permissions.instance_variable_set("@" + string_perm.downcase, true)
|
20
|
+
accumulated_permissions
|
21
|
+
}
|
22
|
+
[name, permissions]
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def call_grant_token(pubnub, grant_token_state)
|
27
|
+
pubnub.grant_token(
|
28
|
+
ttl: grant_token_state[:ttl],
|
29
|
+
http_sync: true,
|
30
|
+
channels: prepare_permissions_map(grant_token_state[:current_grant], "CHANNEL"),
|
31
|
+
channel_groups: prepare_permissions_map(grant_token_state[:current_grant], "CHANNEL_GROUP"),
|
32
|
+
uuids: prepare_permissions_map(grant_token_state[:current_grant], "UUID"),
|
33
|
+
authorized_uuid: grant_token_state[:authorized_uuid]
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def parse_error_body(error_response)
|
38
|
+
Pubnub::Formatter.parse_json(error_response.status[:server_response].body)[0]
|
39
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
Before do |scenario|
|
5
|
+
@grant_token_state = {}
|
6
|
+
@grant_token_state[:current_grant] = {}
|
7
|
+
@pn_configuration = {}
|
8
|
+
|
9
|
+
when_mock_server_used {
|
10
|
+
puts "Using mock"
|
11
|
+
expect(ENV['SERVER_HOST']).not_to be_nil
|
12
|
+
expect(ENV['SERVER_PORT']).not_to be_nil
|
13
|
+
@pn_configuration = {
|
14
|
+
origin: ENV['SERVER_HOST'] + ":" + ENV['SERVER_PORT'],
|
15
|
+
isSecure: false,
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
when_mock_server_used {
|
20
|
+
init_mock(scenario)
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
After do |scenario|
|
25
|
+
when_mock_server_used {
|
26
|
+
expect_mock(scenario)
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def when_mock_server_used(&block)
|
31
|
+
if ENV['SERVER_MOCK']&.to_s&.downcase == 'true'
|
32
|
+
block.call
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def init_mock(scenario)
|
37
|
+
contract_name = contract_name(scenario)
|
38
|
+
if contract_name
|
39
|
+
call_init_endpoint(contract_name)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def call_init_endpoint(contract_name)
|
44
|
+
Net::HTTP.get_response("#{ENV['SERVER_HOST']}", "/init?__contract__script__=#{contract_name}", "#{ENV['SERVER_PORT']}")
|
45
|
+
end
|
46
|
+
|
47
|
+
def expect_mock(scenario)
|
48
|
+
contract_name = contract_name(scenario)
|
49
|
+
if contract_name
|
50
|
+
expect_result = call_expect_endpoint()
|
51
|
+
print(expect_result)
|
52
|
+
if still_pending?(expect_result) || some_failed?(expect_result)
|
53
|
+
message = "Scenario #{extract_contract(expect_result)} considered failure:
|
54
|
+
pending - #{extract_pending(expect_result)},
|
55
|
+
failed - #{extract_failed(expect_result)}"
|
56
|
+
RSpec::Expectations.fail_with(message)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def extract_contract(expect_result)
|
62
|
+
expect_result['contract']
|
63
|
+
end
|
64
|
+
|
65
|
+
def extract_pending(expect_result)
|
66
|
+
expect_result['expectations']['pending'].join(", ")
|
67
|
+
end
|
68
|
+
|
69
|
+
def extract_failed(expect_result)
|
70
|
+
expect_result['expectations']['failed'].join(", ")
|
71
|
+
end
|
72
|
+
|
73
|
+
def still_pending?(expect_result)
|
74
|
+
pending = expect_result['expectations']['pending']
|
75
|
+
not pending.empty?
|
76
|
+
end
|
77
|
+
|
78
|
+
def some_failed?(expect_result)
|
79
|
+
pending = expect_result['expectations']['failed']
|
80
|
+
not pending.empty?
|
81
|
+
end
|
82
|
+
|
83
|
+
def call_expect_endpoint
|
84
|
+
expect_response = Net::HTTP.get_response("#{ENV['SERVER_HOST']}", "/expect", "#{ENV['SERVER_PORT']}")
|
85
|
+
|
86
|
+
if expect_response == nil || expect_response.body == nil
|
87
|
+
RSpec::Expectations.fail_with("Expect response body is null")
|
88
|
+
else
|
89
|
+
JSON.parse(expect_response.body)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def contract_name(scenario)
|
94
|
+
scenario.source_tag_names&.
|
95
|
+
select { |tagName| tagName.start_with?("@contract") }&.
|
96
|
+
map { |tagName| tagName.split("=")[1] }&.first
|
97
|
+
end
|
data/lib/pubnub/cbor.rb
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
module Pubnub
|
2
|
+
class Cbor
|
3
|
+
|
4
|
+
private
|
5
|
+
|
6
|
+
TYPE_MASK = 0b11100000
|
7
|
+
ADDITIONAL_MASK = 0b00011111
|
8
|
+
|
9
|
+
TYPE_UNSIGNED_INT = 0b00000000
|
10
|
+
TYPE_NEGATIVE_INT = 0b00100000
|
11
|
+
TYPE_BYTE_STRING = 0b01000000
|
12
|
+
TYPE_TEXT_STRING = 0b01100000
|
13
|
+
TYPE_ARRAY = 0b10000000
|
14
|
+
TYPE_HASHMAP = 0b10100000
|
15
|
+
TYPE_TAG = 0b11000000
|
16
|
+
TYPE_FLOAT = 0b11100000
|
17
|
+
|
18
|
+
ADDITIONAL_TYPE_INDEFINITE = 31
|
19
|
+
INDEFINITE_BREAK = 0b11111111
|
20
|
+
|
21
|
+
ADDITIONAL_LENGTH_1B = 24
|
22
|
+
ADDITIONAL_LENGTH_2B = 25
|
23
|
+
ADDITIONAL_LENGTH_4B = 26
|
24
|
+
ADDITIONAL_LENGTH_8B = 27
|
25
|
+
|
26
|
+
ADDITIONAL_LENGTH_BYTES = {
|
27
|
+
ADDITIONAL_LENGTH_1B => 1,
|
28
|
+
ADDITIONAL_LENGTH_2B => 2,
|
29
|
+
ADDITIONAL_LENGTH_4B => 4,
|
30
|
+
ADDITIONAL_LENGTH_8B => 8,
|
31
|
+
}
|
32
|
+
|
33
|
+
SIMPLE_VALUE_FALSE = 0xF4
|
34
|
+
SIMPLE_VALUE_TRUE = 0xF5
|
35
|
+
SIMPLE_VALUE_NULL = 0xF6
|
36
|
+
SIMPLE_VALUE_UNDEF = 0xF7
|
37
|
+
|
38
|
+
def bytearray_to_i(byte_array)
|
39
|
+
byte_array.reverse_each.map.with_index do |b, i|
|
40
|
+
b << 8 * i
|
41
|
+
end.reduce(0) do |reduced, byte|
|
42
|
+
reduced | byte
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def decode_integer(data, additional)
|
47
|
+
if ADDITIONAL_LENGTH_BYTES.member?(additional)
|
48
|
+
bytearray_to_i(data.shift(ADDITIONAL_LENGTH_BYTES[additional]))
|
49
|
+
else
|
50
|
+
additional
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def decode_float(data, additional)
|
55
|
+
if additional <= 23
|
56
|
+
additional
|
57
|
+
else
|
58
|
+
bytes = bytearray_to_i(data.shift(ADDITIONAL_LENGTH_BYTES[additional]))
|
59
|
+
case (additional)
|
60
|
+
when ADDITIONAL_LENGTH_1B
|
61
|
+
bytes
|
62
|
+
when ADDITIONAL_LENGTH_2B
|
63
|
+
sign = (bytes >> 15) != 0 ? -1 : 1
|
64
|
+
exp = (bytes & 0b0111110000000000) >> 10
|
65
|
+
mant = bytes & 0b1111111111
|
66
|
+
if exp == 0
|
67
|
+
result = (2 ** -14) * (mant / 1024.to_f)
|
68
|
+
elsif exp == 0b11111
|
69
|
+
result = Float::INFINITY
|
70
|
+
else
|
71
|
+
result = (2 ** (exp - 15)) * (1 + mant / 1024.to_f)
|
72
|
+
end
|
73
|
+
sign * result
|
74
|
+
when ADDITIONAL_LENGTH_4B
|
75
|
+
sign = (bytes >> 31) != 0 ? -1 : 1
|
76
|
+
x = (bytes & ((1 << 23) - 1)) + (1 << 23)
|
77
|
+
exp = (bytes >> 23 & 0xFF) - 127
|
78
|
+
x * (2 ** (exp - 23)) * sign
|
79
|
+
when ADDITIONAL_LENGTH_8B
|
80
|
+
sign = (bytes >> 63) != 0 ? -1 : 1
|
81
|
+
exp = (bytes >> 52) & 0x7FF
|
82
|
+
|
83
|
+
mant = bytes & 0xFFFFFFFFFFFFF
|
84
|
+
|
85
|
+
if 0 == exp
|
86
|
+
val = mant * 2 ** (-(1022 + 52))
|
87
|
+
elsif 0b11111111111 != exp
|
88
|
+
val = (mant + (1 << 52)) * 2 ** (exp - (1023 + 52))
|
89
|
+
else
|
90
|
+
val = 0 == mant ? Float::INFINITY : Float::NAN
|
91
|
+
end
|
92
|
+
sign * val
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def indefinite_data(data)
|
98
|
+
result = []
|
99
|
+
|
100
|
+
loop do
|
101
|
+
byte = data.shift
|
102
|
+
break if byte == INDEFINITE_BREAK
|
103
|
+
result.append(byte)
|
104
|
+
break if data.empty?
|
105
|
+
end
|
106
|
+
result
|
107
|
+
end
|
108
|
+
|
109
|
+
def compute_length(data, additional)
|
110
|
+
if ADDITIONAL_LENGTH_BYTES.member?(additional)
|
111
|
+
bytearray_to_i(data.shift(ADDITIONAL_LENGTH_BYTES[additional]))
|
112
|
+
else
|
113
|
+
additional
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def decode_string(data, additional)
|
118
|
+
if additional == ADDITIONAL_TYPE_INDEFINITE
|
119
|
+
indefinite_data(data).pack('C*').force_encoding('UTF-8')
|
120
|
+
else
|
121
|
+
length = compute_length(data, additional)
|
122
|
+
data.shift(length).pack('C*').force_encoding('UTF-8')
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def decode_map(data, additional)
|
127
|
+
length = compute_length(data, additional)
|
128
|
+
result = Hash.new
|
129
|
+
(1..length).each { result.store(parse_data(data), parse_data(data)) }
|
130
|
+
result
|
131
|
+
end
|
132
|
+
|
133
|
+
def decode_array(data, additional)
|
134
|
+
length = compute_length(data, additional)
|
135
|
+
(1..length).map { parse_data(data) }
|
136
|
+
end
|
137
|
+
|
138
|
+
def parse_data(data)
|
139
|
+
byte = data.shift
|
140
|
+
|
141
|
+
case (byte)
|
142
|
+
when SIMPLE_VALUE_NULL
|
143
|
+
return nil
|
144
|
+
when SIMPLE_VALUE_TRUE
|
145
|
+
return true
|
146
|
+
when SIMPLE_VALUE_FALSE
|
147
|
+
return false
|
148
|
+
when SIMPLE_VALUE_UNDEF
|
149
|
+
return nil
|
150
|
+
else
|
151
|
+
type = byte & TYPE_MASK
|
152
|
+
additional = byte & ADDITIONAL_MASK
|
153
|
+
|
154
|
+
case (type)
|
155
|
+
when TYPE_NEGATIVE_INT
|
156
|
+
-1 - decode_integer(data, additional)
|
157
|
+
when TYPE_UNSIGNED_INT
|
158
|
+
decode_integer(data, additional)
|
159
|
+
when TYPE_FLOAT
|
160
|
+
decode_float(data, additional).to_f
|
161
|
+
when TYPE_BYTE_STRING
|
162
|
+
decode_string(data, additional)
|
163
|
+
when TYPE_TEXT_STRING
|
164
|
+
decode_string(data, additional)
|
165
|
+
when TYPE_ARRAY
|
166
|
+
decode_array(data, additional)
|
167
|
+
when TYPE_HASHMAP
|
168
|
+
decode_map(data, additional)
|
169
|
+
else
|
170
|
+
nil
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
public
|
176
|
+
|
177
|
+
def decode(value)
|
178
|
+
parse_data(value)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
data/lib/pubnub/client/events.rb
CHANGED
@@ -4,7 +4,7 @@ module Pubnub
|
|
4
4
|
class Client
|
5
5
|
# Module that holds generator for all events
|
6
6
|
module Events
|
7
|
-
EVENTS = %w[publish subscribe presence leave history here_now audit grant delete_messages
|
7
|
+
EVENTS = %w[publish subscribe presence leave history here_now audit grant grant_token delete_messages
|
8
8
|
revoke time heartbeat where_now set_state state channel_registration message_counts signal
|
9
9
|
add_channels_to_push list_push_provisions remove_channels_from_push remove_device_from_push
|
10
10
|
set_uuid_metadata set_channel_metadata remove_uuid_metadata remove_channel_metadata
|
data/lib/pubnub/client.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
1
3
|
require 'pubnub/error'
|
2
4
|
require 'pubnub/uuid'
|
3
5
|
require 'pubnub/formatter'
|
@@ -34,6 +36,7 @@ require 'pubnub/validators/client'
|
|
34
36
|
require 'pubnub/validators/audit'
|
35
37
|
require 'pubnub/validators/channel_registration'
|
36
38
|
require 'pubnub/validators/grant'
|
39
|
+
require 'pubnub/validators/grant_token'
|
37
40
|
require 'pubnub/validators/heartbeat'
|
38
41
|
require 'pubnub/validators/here_now'
|
39
42
|
require 'pubnub/validators/history'
|
@@ -67,6 +70,7 @@ require 'pubnub/validators/set_channel_members'
|
|
67
70
|
require 'pubnub/validators/set_memberships'
|
68
71
|
require 'pubnub/validators/remove_channel_members'
|
69
72
|
require 'pubnub/validators/remove_memberships'
|
73
|
+
require 'pubnub/cbor'
|
70
74
|
|
71
75
|
Dir[File.join(File.dirname(__dir__), 'pubnub', 'events', '*.rb')].each do |file|
|
72
76
|
require file
|
@@ -319,6 +323,15 @@ module Pubnub
|
|
319
323
|
@telemetry.await.fetch_average(event).value
|
320
324
|
end
|
321
325
|
|
326
|
+
def parse_token(token)
|
327
|
+
token_bytes = Base64.urlsafe_decode64(token)
|
328
|
+
Cbor.new.decode(token_bytes.bytes)
|
329
|
+
end
|
330
|
+
|
331
|
+
def set_token(token)
|
332
|
+
@env[:token] = token
|
333
|
+
end
|
334
|
+
|
322
335
|
private
|
323
336
|
|
324
337
|
def create_state_pools(event)
|
data/lib/pubnub/constants.rb
CHANGED
@@ -57,6 +57,7 @@ module Pubnub
|
|
57
57
|
OPERATION_CHANNEL_GROUP_REMOVE = :channel_group_remove
|
58
58
|
OPERATION_AUDIT = :audit
|
59
59
|
OPERATION_GRANT = :grant
|
60
|
+
OPERATION_GRANT_TOKEN = :grant_token
|
60
61
|
OPERATION_REVOKE = :revoke
|
61
62
|
OPERATION_DELETE = :delete
|
62
63
|
OPERATION_LIST_ALL_CHANNEL_GROUPS = :list_all_channel_groups
|
@@ -86,13 +87,14 @@ module Pubnub
|
|
86
87
|
OPERATION_SUBSCRIBE, OPERATION_HEARTBEAT, OPERATION_PRESENCE, OPERATION_TIME, OPERATION_HISTORY,
|
87
88
|
OPERATION_HERE_NOW, OPERATION_GLOBAL_HERE_NOW, OPERATION_GET_STATE, OPERATION_LIST_ALL_CHANNEL_GROUPS,
|
88
89
|
OPERATION_LIST_ALL_CHANNELS_IN_CHANNEL_GROUP, OPERATION_CHANNEL_GROUP_ADD, OPERATION_CHANNEL_GROUP_REMOVE,
|
89
|
-
OPERATION_AUDIT, OPERATION_GRANT, OPERATION_REVOKE, OPERATION_WHERE_NOW,
|
90
|
-
OPERATION_ADD_CHANNELS_TO_PUSH, OPERATION_LIST_PUSH_PROVISIONS,
|
91
|
-
OPERATION_REMOVE_DEVICE_FROM_PUSH, OPERATION_SIGNAL,
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
90
|
+
OPERATION_AUDIT, OPERATION_GRANT, OPERATION_GRANT_TOKEN, OPERATION_REVOKE, OPERATION_WHERE_NOW,
|
91
|
+
OPERATION_MESSAGE_COUNTS, OPERATION_ADD_CHANNELS_TO_PUSH, OPERATION_LIST_PUSH_PROVISIONS,
|
92
|
+
OPERATION_REMOVE_CHANNELS_FROM_PUSH, OPERATION_REMOVE_DEVICE_FROM_PUSH, OPERATION_SIGNAL,
|
93
|
+
OPERATION_SET_UUID_METADATA, OPERATION_GET_UUID_METADATA, OPERATION_GET_ALL_UUID_METADATA,
|
94
|
+
OPERATION_REMOVE_UUID_METADATA, OPERATION_SET_CHANNEL_METADATA, OPERATION_GET_CHANNEL_METADATA,
|
95
|
+
OPERATION_GET_ALL_CHANNELS_METADATA, OPERATION_REMOVE_CHANNEL_METADATA, OPERATION_GET_CHANNEL_MEMBERS,
|
96
|
+
OPERATION_SET_CHANNEL_MEMBERS, OPERATION_REMOVE_CHANNEL_MEMBERS, OPERATION_GET_MEMBERSHIPS,
|
97
|
+
OPERATION_SET_MEMBERSHIPS, OPERATION_REMOVE_MEMBERSHIPS
|
96
98
|
].freeze
|
97
99
|
|
98
100
|
# Announcements
|
data/lib/pubnub/event.rb
CHANGED
@@ -24,8 +24,8 @@ module Pubnub
|
|
24
24
|
env.delete(:state)
|
25
25
|
create_variables_from_options(env.merge(options))
|
26
26
|
@origin = @app.current_origin
|
27
|
-
format_channels
|
28
|
-
format_group
|
27
|
+
format_channels if enable_format_channels?
|
28
|
+
format_group if enable_format_group?
|
29
29
|
set_timestamp
|
30
30
|
validate!
|
31
31
|
telemetry = @app.telemetry_for(@telemetry_name)
|
@@ -44,7 +44,7 @@ module Pubnub
|
|
44
44
|
envelopes
|
45
45
|
end
|
46
46
|
|
47
|
-
def send_request(compressed_body = '')
|
47
|
+
def send_request(compressed_body = '', header = {})
|
48
48
|
Pubnub.logger.debug('Pubnub::Event') { '#send_request called' }
|
49
49
|
|
50
50
|
@compressed_body = compressed_body
|
@@ -54,13 +54,13 @@ module Pubnub
|
|
54
54
|
telemetry_time_start = ::Time.now.to_f
|
55
55
|
response = case operation_http_method
|
56
56
|
when "get"
|
57
|
-
sender.get(uri.to_s)
|
57
|
+
sender.get(uri.to_s, header: header)
|
58
58
|
when "post"
|
59
|
-
sender.post(uri.to_s, body: compressed_body)
|
59
|
+
sender.post(uri.to_s, body: compressed_body, header: header)
|
60
60
|
when "patch"
|
61
|
-
sender.patch(uri.to_s, body: compressed_body)
|
61
|
+
sender.patch(uri.to_s, body: compressed_body, header: header)
|
62
62
|
else
|
63
|
-
sender.delete(uri.to_s)
|
63
|
+
sender.delete(uri.to_s, header: header)
|
64
64
|
end
|
65
65
|
|
66
66
|
begin
|
@@ -102,6 +102,14 @@ module Pubnub
|
|
102
102
|
|
103
103
|
private
|
104
104
|
|
105
|
+
def enable_format_channels?
|
106
|
+
true
|
107
|
+
end
|
108
|
+
|
109
|
+
def enable_format_group?
|
110
|
+
true
|
111
|
+
end
|
112
|
+
|
105
113
|
def operation_http_method
|
106
114
|
case @event
|
107
115
|
when Pubnub::Constants::OPERATION_DELETE, Pubnub::Constants::OPERATION_REMOVE_CHANNEL_METADATA, Pubnub::Constants::OPERATION_REMOVE_UUID_METADATA
|
@@ -136,8 +144,9 @@ module Pubnub
|
|
136
144
|
pnsdk: @app.sdk_version
|
137
145
|
}
|
138
146
|
|
147
|
+
token = @app.env[:token]
|
139
148
|
empty_if_blank = {
|
140
|
-
auth: @auth_key,
|
149
|
+
auth: token ? token : @auth_key,
|
141
150
|
uuid: @app.env[:uuid],
|
142
151
|
@telemetry_name => @current_telemetry
|
143
152
|
}
|
@@ -164,7 +173,7 @@ module Pubnub
|
|
164
173
|
end count limit reverse presence_callback store skip_validate
|
165
174
|
state channel_group channel_groups compressed meta customs include_token
|
166
175
|
replicate with_presence cipher_key_selector include_meta join update get
|
167
|
-
add remove push_token push_gateway environment topic
|
176
|
+
add remove push_token push_gateway environment topic authorized_uuid
|
168
177
|
]
|
169
178
|
|
170
179
|
options = options.each_with_object({}) { |option, obj| obj[option.first.to_sym] = option.last }
|
@@ -0,0 +1,172 @@
|
|
1
|
+
module Pubnub
|
2
|
+
# Holds grant token functionality
|
3
|
+
class GrantToken < SingleEvent
|
4
|
+
include Concurrent::Async
|
5
|
+
include Pubnub::Validator::GrantToken
|
6
|
+
|
7
|
+
def initialize(options, app)
|
8
|
+
@event = current_operation
|
9
|
+
@telemetry_name = :l_pamv3
|
10
|
+
@uuids = options[:uuids] || {}
|
11
|
+
options[:channels] = options[:channels] || {}
|
12
|
+
options[:channel_groups] = options[:channel_groups] || {}
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def fire
|
17
|
+
Pubnub.logger.debug('Pubnub::GrantToken') { "Fired event #{self.class}" }
|
18
|
+
|
19
|
+
raw_body = {
|
20
|
+
ttl: @ttl,
|
21
|
+
permissions: {
|
22
|
+
meta: @meta,
|
23
|
+
uuid: @authorized_uuid,
|
24
|
+
resources: prepare_permissions(:resource, @channels, @channel_groups, @uuids),
|
25
|
+
patterns: prepare_permissions(:pattern, @channels, @channel_groups, @uuids)
|
26
|
+
}.select { |_, v| v }
|
27
|
+
}
|
28
|
+
body = Formatter.format_message(raw_body, "", false, false)
|
29
|
+
response = send_request(body, { "Content-Type": "application/json" })
|
30
|
+
|
31
|
+
envelopes = fire_callbacks(handle(response, uri))
|
32
|
+
finalize_event(envelopes)
|
33
|
+
envelopes
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def enable_format_channels?
|
39
|
+
false
|
40
|
+
end
|
41
|
+
|
42
|
+
def enable_format_group?
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
def current_operation
|
47
|
+
Pubnub::Constants::OPERATION_GRANT_TOKEN
|
48
|
+
end
|
49
|
+
|
50
|
+
def prepare_permissions(type, channels, groups, uuids)
|
51
|
+
{
|
52
|
+
channels: prepare_single_permissions(type, channels),
|
53
|
+
groups: prepare_single_permissions(type, groups),
|
54
|
+
uuids: prepare_single_permissions(type, uuids)
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
def prepare_single_permissions(type, resources)
|
59
|
+
resources
|
60
|
+
.select { |_, v| v.type == type }
|
61
|
+
.to_h { |k, v| [k, v.calculate_bitmask] }
|
62
|
+
end
|
63
|
+
|
64
|
+
def valid_envelope(parsed_response, req_res_objects)
|
65
|
+
Pubnub::Envelope.new(
|
66
|
+
event: @event,
|
67
|
+
event_options: @given_options,
|
68
|
+
timetoken: nil,
|
69
|
+
status: {
|
70
|
+
code: req_res_objects[:response].code,
|
71
|
+
client_request: req_res_objects[:request],
|
72
|
+
server_response: req_res_objects[:response],
|
73
|
+
category: Pubnub::Constants::STATUS_ACK,
|
74
|
+
error: false,
|
75
|
+
auto_retried: false,
|
76
|
+
|
77
|
+
current_timetoken: nil,
|
78
|
+
last_timetoken: nil,
|
79
|
+
subscribed_channels: nil,
|
80
|
+
subscribed_channel_groups: nil,
|
81
|
+
|
82
|
+
data: nil,
|
83
|
+
|
84
|
+
config: get_config
|
85
|
+
|
86
|
+
},
|
87
|
+
result: {
|
88
|
+
code: req_res_objects[:response].code,
|
89
|
+
operation: current_operation,
|
90
|
+
client_request: req_res_objects[:request],
|
91
|
+
server_response: req_res_objects[:response],
|
92
|
+
|
93
|
+
data: parsed_response['data']
|
94
|
+
}
|
95
|
+
)
|
96
|
+
end
|
97
|
+
|
98
|
+
def path
|
99
|
+
'/' + [
|
100
|
+
'v3',
|
101
|
+
'pam',
|
102
|
+
@subscribe_key,
|
103
|
+
'grant'
|
104
|
+
].join('/')
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class Permissions
|
109
|
+
attr_reader :type
|
110
|
+
|
111
|
+
class << Permissions
|
112
|
+
def pat(read: false, write: false, manage: false, delete: false,
|
113
|
+
create: false, get: false, update: false, join: false)
|
114
|
+
Permissions.new(
|
115
|
+
type: :pattern,
|
116
|
+
read: read,
|
117
|
+
write: write,
|
118
|
+
manage: manage,
|
119
|
+
delete: delete,
|
120
|
+
create: create,
|
121
|
+
get: get,
|
122
|
+
update: update,
|
123
|
+
join: join
|
124
|
+
)
|
125
|
+
end
|
126
|
+
|
127
|
+
def res(read: false, write: false, manage: false, delete: false, create: false, get: false, update: false, join: false)
|
128
|
+
Permissions.new(
|
129
|
+
type: :resource,
|
130
|
+
read: read,
|
131
|
+
write: write,
|
132
|
+
manage: manage,
|
133
|
+
delete: delete,
|
134
|
+
create: create,
|
135
|
+
get: get,
|
136
|
+
update: update,
|
137
|
+
join: join
|
138
|
+
)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def initialize(options)
|
143
|
+
@type = options[:type]
|
144
|
+
@read = options[:read] || false
|
145
|
+
@write = options[:write] || false
|
146
|
+
@manage = options[:manage] || false
|
147
|
+
@delete = options[:delete] || false
|
148
|
+
@create = options[:create] || false
|
149
|
+
@get = options[:get] || false
|
150
|
+
@update = options[:update] || false
|
151
|
+
@join = options[:join] || false
|
152
|
+
end
|
153
|
+
|
154
|
+
def to_s
|
155
|
+
"Permissions: {:read => #{@read}, :write => #{@write}, :manage => #{@manage} , :delete => #{@delete}, :create => #{@create}, :get => #{@get}, :update => #{@update}, :join => #{@join}}"
|
156
|
+
end
|
157
|
+
|
158
|
+
def calculate_bitmask
|
159
|
+
sum = 0
|
160
|
+
|
161
|
+
sum |= 1 if @read
|
162
|
+
sum |= 2 if @write
|
163
|
+
sum |= 4 if @manage
|
164
|
+
sum |= 8 if @delete
|
165
|
+
sum |= 16 if @create
|
166
|
+
sum |= 32 if @get
|
167
|
+
sum |= 64 if @update
|
168
|
+
sum |= 128 if @join
|
169
|
+
sum
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# Toplevel Pubnub module.
|
2
|
+
module Pubnub
|
3
|
+
# Validator module that holds all validators modules
|
4
|
+
module Validator
|
5
|
+
# Validator for Grant event
|
6
|
+
module GrantToken
|
7
|
+
include CommonValidator
|
8
|
+
|
9
|
+
def validate!
|
10
|
+
validate_keys!
|
11
|
+
validate_ttl!
|
12
|
+
validate_permissions!(@uuids, ":uuids")
|
13
|
+
validate_permissions!(@channels, ":channels")
|
14
|
+
validate_permissions!(@channel_groups, ":uuids")
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def validate_keys!
|
20
|
+
raise(
|
21
|
+
ArgumentError.new(object: self, message: ':subscribe_key is required for grant token event.'),
|
22
|
+
':subscribe_key is required for grant token event.'
|
23
|
+
) if @subscribe_key.nil? || @subscribe_key.empty?
|
24
|
+
|
25
|
+
raise(
|
26
|
+
ArgumentError.new(object: self, message: ':secret_key is required for grant token event.'),
|
27
|
+
':publish_key is required for grant token event.'
|
28
|
+
) if @secret_key.nil? || @secret_key.empty?
|
29
|
+
end
|
30
|
+
|
31
|
+
def validate_ttl!
|
32
|
+
return unless !@ttl.nil? && !@ttl.is_a?(Integer)
|
33
|
+
|
34
|
+
raise(
|
35
|
+
ArgumentError.new(object: self, message: ':ttl has to be kind of Integer for grant token event.'),
|
36
|
+
':ttl has to be kind of Integer for grant token event.'
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
def validate_permissions!(arg, name)
|
41
|
+
return if arg.nil?
|
42
|
+
|
43
|
+
raise(
|
44
|
+
ArgumentError.new(object: self, message: ":#{name} has to be kind of Hash for grant token event."),
|
45
|
+
":#{name} has to be kind of Hash for grant token event."
|
46
|
+
) unless arg.is_a?(Hash)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
data/lib/pubnub/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pubnub
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- PubNub
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-11-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -130,6 +130,10 @@ files:
|
|
130
130
|
- ".bundle/config"
|
131
131
|
- ".codeclimate.yml"
|
132
132
|
- ".github/CODEOWNERS"
|
133
|
+
- ".github/workflows/commands-handler.yml"
|
134
|
+
- ".github/workflows/release.yml"
|
135
|
+
- ".github/workflows/release/versions.json"
|
136
|
+
- ".github/workflows/run_acceptance_tests.yml"
|
133
137
|
- ".github/workflows/validate-pubnub-yml.yml"
|
134
138
|
- ".github/workflows/validate-yml.js"
|
135
139
|
- ".gitignore"
|
@@ -145,7 +149,12 @@ files:
|
|
145
149
|
- README.md
|
146
150
|
- Rakefile
|
147
151
|
- VERSION
|
152
|
+
- config/cucumber.yml
|
148
153
|
- docs.md
|
154
|
+
- features/step_definitions/grant_token_steps.rb
|
155
|
+
- features/support/env.rb
|
156
|
+
- features/support/helper.rb
|
157
|
+
- features/support/hooks.rb
|
149
158
|
- gemfiles/celluloid_017.gemfile
|
150
159
|
- gemfiles/celluloid_017.gemfile.lock
|
151
160
|
- gemfiles/celluloid_018.gemfile
|
@@ -155,6 +164,7 @@ files:
|
|
155
164
|
- gemfiles/concurrent-ruby-1.0.5.gem
|
156
165
|
- gemfiles/concurrent-ruby-edge-0.3.1.gem
|
157
166
|
- lib/pubnub.rb
|
167
|
+
- lib/pubnub/cbor.rb
|
158
168
|
- lib/pubnub/client.rb
|
159
169
|
- lib/pubnub/client/events.rb
|
160
170
|
- lib/pubnub/client/getters_setters.rb
|
@@ -180,6 +190,7 @@ files:
|
|
180
190
|
- lib/pubnub/events/get_memberships.rb
|
181
191
|
- lib/pubnub/events/get_uuid_metadata.rb
|
182
192
|
- lib/pubnub/events/grant.rb
|
193
|
+
- lib/pubnub/events/grant_token.rb
|
183
194
|
- lib/pubnub/events/heartbeat.rb
|
184
195
|
- lib/pubnub/events/here_now.rb
|
185
196
|
- lib/pubnub/events/history.rb
|
@@ -235,6 +246,7 @@ files:
|
|
235
246
|
- lib/pubnub/validators/get_memberships.rb
|
236
247
|
- lib/pubnub/validators/get_uuid_metadata.rb
|
237
248
|
- lib/pubnub/validators/grant.rb
|
249
|
+
- lib/pubnub/validators/grant_token.rb
|
238
250
|
- lib/pubnub/validators/heartbeat.rb
|
239
251
|
- lib/pubnub/validators/here_now.rb
|
240
252
|
- lib/pubnub/validators/history.rb
|
@@ -281,8 +293,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
281
293
|
- !ruby/object:Gem::Version
|
282
294
|
version: '0'
|
283
295
|
requirements: []
|
284
|
-
rubygems_version: 3.2
|
296
|
+
rubygems_version: 3.1.2
|
285
297
|
signing_key:
|
286
298
|
specification_version: 4
|
287
299
|
summary: PubNub Official Ruby gem.
|
288
|
-
test_files:
|
300
|
+
test_files:
|
301
|
+
- features/step_definitions/grant_token_steps.rb
|
302
|
+
- features/support/env.rb
|
303
|
+
- features/support/helper.rb
|
304
|
+
- features/support/hooks.rb
|