conjur-api 5.0.0 → 5.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +6 -0
- data/Dockerfile +2 -0
- data/Jenkinsfile +2 -8
- data/README.md +85 -2
- data/Rakefile +9 -3
- data/ci/configure_v4.sh +12 -0
- data/ci/configure_v5.sh +14 -0
- data/conjur-api.gemspec +1 -1
- data/docker-compose.yml +47 -12
- data/example/demo_v4.rb +49 -0
- data/example/demo_v5.rb +57 -0
- data/features/authn_local.feature +32 -0
- data/features/support/env.rb +1 -0
- data/features/variable_value.feature +6 -13
- data/features_v4/authn_local.feature +27 -0
- data/features_v4/exists.feature +29 -0
- data/features_v4/host.feature +18 -0
- data/features_v4/host_factory_token.feature +49 -0
- data/features_v4/members.feature +39 -0
- data/features_v4/permitted.feature +15 -0
- data/features_v4/permitted_roles.feature +8 -0
- data/features_v4/resource_fields.feature +47 -0
- data/features_v4/rotate_api_key.feature +13 -0
- data/features_v4/step_definitions/api_steps.rb +17 -0
- data/features_v4/step_definitions/result_steps.rb +3 -0
- data/features_v4/support/env.rb +23 -0
- data/features_v4/support/policy.yml +34 -0
- data/features_v4/support/world.rb +12 -0
- data/features_v4/variable_fields.feature +11 -0
- data/features_v4/variable_value.feature +54 -0
- data/lib/conjur-api/version.rb +1 -1
- data/lib/conjur/acts_as_resource.rb +3 -17
- data/lib/conjur/acts_as_role.rb +2 -4
- data/lib/conjur/acts_as_user.rb +1 -2
- data/lib/conjur/api.rb +1 -0
- data/lib/conjur/api/authn.rb +22 -8
- data/lib/conjur/api/host_factories.rb +2 -5
- data/lib/conjur/api/policies.rb +1 -1
- data/lib/conjur/api/pubkeys.rb +1 -9
- data/lib/conjur/api/resources.rb +1 -6
- data/lib/conjur/api/router/v4.rb +149 -0
- data/lib/conjur/api/router/v5.rb +150 -0
- data/lib/conjur/api/variables.rb +2 -8
- data/lib/conjur/base.rb +61 -18
- data/lib/conjur/base_object.rb +1 -6
- data/lib/conjur/configuration.rb +26 -0
- data/lib/conjur/group.rb +7 -1
- data/lib/conjur/has_attributes.rb +11 -3
- data/lib/conjur/host_factory.rb +1 -1
- data/lib/conjur/routing.rb +29 -0
- data/lib/conjur/user.rb +7 -1
- data/lib/conjur/variable.rb +26 -11
- data/spec/has_attributes_spec.rb +4 -2
- data/test.sh +25 -11
- metadata +33 -12
- data/ci/wait_for_server.sh +0 -10
- data/dev/docker-compose.yml +0 -23
- data/dev/empty.yml +0 -2
- data/dev/start.sh +0 -15
- data/dev/stop.sh +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a7dc24a2726ea693a242271b1f3d5a89ce22189d
|
4
|
+
data.tar.gz: 7676f82fc5f389b1b4eed5f75be20a31e7e6b8f5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 124f290a448e5f08bbcbe926ba3aa3a680fe9d8300a7f3e16d8f2ab891b37113797e2e2427681288ba9b70628238af7b8727482353430bae786aa455fd4bbdd6
|
7
|
+
data.tar.gz: 4861bf1424b924c503a0fcd76c8401617121c1c831369d588c0729afe1c741bbe486f44a1ddb998e951286965ebde88cf83aa96fd37dd24a667603a3e928a3ba
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Latest
|
2
2
|
|
3
|
+
# v5.1.0
|
4
|
+
|
5
|
+
* Introduces backwards compatibility with Conjur 4.x for most API methods.
|
6
|
+
* Adds the configuration setting `version`, which is auto-populated from the environment variable `CONJUR_VERSION`.
|
7
|
+
* Adds support for the `authn-local` service, which can be used when the API client runs on the server.
|
8
|
+
|
3
9
|
# v5.0.0
|
4
10
|
|
5
11
|
* Provides compatibility with [cyberark/conjur](https://github.com/cyberark/conjur), Conjur 5 CE.
|
data/Dockerfile
CHANGED
data/Jenkinsfile
CHANGED
@@ -16,6 +16,7 @@ pipeline {
|
|
16
16
|
|
17
17
|
junit 'spec/reports/*.xml'
|
18
18
|
junit 'features/reports/*.xml'
|
19
|
+
junit 'features_v4/reports/*.xml'
|
19
20
|
}
|
20
21
|
}
|
21
22
|
|
@@ -62,14 +63,7 @@ pipeline {
|
|
62
63
|
|
63
64
|
post {
|
64
65
|
always {
|
65
|
-
|
66
|
-
deleteDir()
|
67
|
-
}
|
68
|
-
failure {
|
69
|
-
slackSend(color: 'danger', message: "${env.JOB_NAME} #${env.BUILD_NUMBER} FAILURE (<${env.BUILD_URL}|Open>)")
|
70
|
-
}
|
71
|
-
unstable {
|
72
|
-
slackSend(color: 'warning', message: "${env.JOB_NAME} #${env.BUILD_NUMBER} UNSTABLE (<${env.BUILD_URL}|Open>)")
|
66
|
+
cleanupAndNotify(currentBuild.currentResult)
|
73
67
|
}
|
74
68
|
}
|
75
69
|
}
|
data/README.md
CHANGED
@@ -2,6 +2,26 @@
|
|
2
2
|
|
3
3
|
Programmatic Ruby access to the Conjur API.
|
4
4
|
|
5
|
+
# Server Versions
|
6
|
+
|
7
|
+
The Conjur server comes in two major versions:
|
8
|
+
|
9
|
+
* **4.x** Conjur 4 is a commercial, non-open-source product, which is documented at [https://developer.conjur.net/](https://developer.conjur.net/).
|
10
|
+
* **5.x** Conjur 5 is open-source software, hosted and documented at [https://www.conjur.org/](https://www.conjur.org/).
|
11
|
+
|
12
|
+
You can use the `master` branch of this project, which is `conjur-api` version `5.x`, to do all of the following things against either type of Conjur server:
|
13
|
+
|
14
|
+
* Authenticate
|
15
|
+
* Fetch secrets
|
16
|
+
* Check permissions
|
17
|
+
* List roles, resources, members, memberships and permitted roles.
|
18
|
+
* Create hosts using host factory
|
19
|
+
* Rotate API keys
|
20
|
+
|
21
|
+
Use the configuration setting `Conjur.configuration.version` to select your server version, or set the environment variable `CONJUR_VERSION`. In either case, the valid values are `4` and `5`; the default is `5`.
|
22
|
+
|
23
|
+
If you are using Conjur server version `4.x`, you can also choose to use the `conjur-api` version `4.x`. In this case, the `Configuration.version` setting is not required (actually, it doesn't exist).
|
24
|
+
|
5
25
|
# Installation
|
6
26
|
|
7
27
|
Add this line to your application's Gemfile:
|
@@ -95,7 +115,70 @@ Conjur::API.new_from_key login, api_key
|
|
95
115
|
Note that if you are connecting as a [Host](http://developer.conjur.net/reference/services/directory/host), the login should be
|
96
116
|
prefixed with `host/`. For example: `host/myhost.example.com`, not just `myhost.example.com`.
|
97
117
|
|
98
|
-
|
118
|
+
# Development
|
119
|
+
|
120
|
+
The file `docker-compose.yml` is a self-contained development environment for the project.
|
121
|
+
|
122
|
+
## Starting
|
123
|
+
|
124
|
+
To bring it up, run:
|
125
|
+
|
126
|
+
```sh-session
|
127
|
+
$ docker-compose build
|
128
|
+
$ docker-compose up -d pg conjur_4 conjur_5
|
129
|
+
```
|
130
|
+
|
131
|
+
Then configure the v4 and v5 servers:
|
132
|
+
|
133
|
+
```sh-session
|
134
|
+
$ ./ci/configure_v4.sh
|
135
|
+
...
|
136
|
+
$ ./ci/configure_v5.sh
|
137
|
+
...
|
138
|
+
```
|
139
|
+
|
140
|
+
## Using
|
141
|
+
|
142
|
+
Obtain the API key for the v5 admin user:
|
143
|
+
|
144
|
+
```
|
145
|
+
$ docker-compose exec conjur_5 rake 'role:retrieve-key[cucumber:user:admin]'
|
146
|
+
3aezp05q3wkem3hmegymwzz8wh3bs3dr6xx3y3m2q41k5ymebkc
|
147
|
+
```
|
148
|
+
|
149
|
+
The password of the v4 admin user is "secret".
|
150
|
+
|
151
|
+
Now you can run the client `dev` container:
|
152
|
+
|
153
|
+
```sh-session
|
154
|
+
$ docker-compose run --rm dev
|
155
|
+
```
|
156
|
+
|
157
|
+
This gives you a shell session with `conjur_4` and `conjur_5` available as linked containers.
|
158
|
+
|
159
|
+
## Demos
|
160
|
+
|
161
|
+
For a v5 demo, run:
|
162
|
+
|
163
|
+
```sh-session
|
164
|
+
$ bundle exec ./example/demo_v5.rb <admin-api-key>
|
165
|
+
```
|
166
|
+
|
167
|
+
For a v4 demo, run:
|
168
|
+
|
169
|
+
```sh-session
|
170
|
+
$ bundle exec ./example/demo_v4.rb
|
171
|
+
```
|
172
|
+
|
173
|
+
## Stopping
|
174
|
+
|
175
|
+
To bring it down, run:
|
176
|
+
|
177
|
+
```sh-session
|
178
|
+
$ docker-compose down
|
179
|
+
```
|
180
|
+
|
181
|
+
# Contributing
|
99
182
|
|
100
183
|
1. Fork it
|
101
184
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
@@ -103,7 +186,7 @@ prefixed with `host/`. For example: `host/myhost.example.com`, not just `myhost.
|
|
103
186
|
4. Push to the branch (`git push origin my-new-feature`)
|
104
187
|
5. Create new Pull Request
|
105
188
|
|
106
|
-
|
189
|
+
# License
|
107
190
|
|
108
191
|
Copyright 2016-2017 CyberArk
|
109
192
|
|
data/Rakefile
CHANGED
@@ -23,15 +23,21 @@ begin
|
|
23
23
|
require 'cucumber'
|
24
24
|
require 'cucumber/rake/task'
|
25
25
|
|
26
|
-
Cucumber::Rake::Task.new(:
|
26
|
+
Cucumber::Rake::Task.new(:cucumber_4) do |t|
|
27
|
+
t.cucumber_opts = "--tags ~@wip --format pretty --format junit --out features_v4/reports -r features_v4/step_definitions/ -r features_v4/support/ features_v4/"
|
28
|
+
end
|
29
|
+
|
30
|
+
Cucumber::Rake::Task.new(:cucumber_5) do |t|
|
27
31
|
t.cucumber_opts = "--tags ~@wip --format pretty --format junit --out features/reports"
|
28
32
|
end
|
29
33
|
|
30
34
|
begin
|
31
35
|
require 'ci/reporter/rake/rspec'
|
32
36
|
desc "Run the spec and cucumber suites, compute the test results and coverage statistics, build Yard docs"
|
33
|
-
task :
|
34
|
-
task
|
37
|
+
task :jenkins_init => [ :init_coverage, :cuke_report_cleanup ]
|
38
|
+
task :jenkins_spec => [ :"ci:setup:rspec", :spec ]
|
39
|
+
task :jenkins_cucumber_v4 => [ :cucumber_4 ]
|
40
|
+
task :jenkins_cucumber_v5 => [ :cucumber_5 ]
|
35
41
|
rescue LoadError
|
36
42
|
warn "ci_reporter_rspec not found, jenkins task will be unavailable"
|
37
43
|
end
|
data/ci/configure_v4.sh
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/bin/bash -e
|
2
|
+
|
3
|
+
cat << "CONFIGURE" | docker exec -i $(docker-compose ps -q conjur_4) bash
|
4
|
+
set -e
|
5
|
+
|
6
|
+
/opt/conjur/evoke/bin/wait_for_conjur
|
7
|
+
evoke ca regenerate conjur_4
|
8
|
+
/opt/conjur/evoke/bin/wait_for_conjur
|
9
|
+
env CONJUR_AUTHN_LOGIN=admin CONJUR_AUTHN_API_KEY=secret conjur policy load --as-group security_admin /etc/policy.yml
|
10
|
+
CONFIGURE
|
11
|
+
|
12
|
+
docker cp $(docker-compose ps -q conjur_4):/opt/conjur/etc/ssl/ca.pem ./tmp/conjur.pem
|
data/ci/configure_v5.sh
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/bin/bash -e
|
2
|
+
|
3
|
+
cat << "CONFIGURE" | docker exec -i $(docker-compose ps -q conjur_5) bash
|
4
|
+
set -e
|
5
|
+
|
6
|
+
for _ in $(seq 20); do
|
7
|
+
curl -o /dev/null -fs -X OPTIONS http://localhost > /dev/null && break
|
8
|
+
echo .
|
9
|
+
sleep 2
|
10
|
+
done
|
11
|
+
|
12
|
+
# So we fail if the server isn't up yet:
|
13
|
+
curl -o /dev/null -fs -X OPTIONS http://localhost > /dev/null
|
14
|
+
CONFIGURE
|
data/conjur-api.gemspec
CHANGED
@@ -25,7 +25,7 @@ Gem::Specification.new do |gem|
|
|
25
25
|
gem.add_development_dependency 'rspec', '~> 3'
|
26
26
|
gem.add_development_dependency 'rspec-expectations', '~> 3.4'
|
27
27
|
gem.add_development_dependency 'json_spec'
|
28
|
-
gem.add_development_dependency 'cucumber'
|
28
|
+
gem.add_development_dependency 'cucumber', '~> 2.99'
|
29
29
|
gem.add_development_dependency 'ci_reporter_rspec'
|
30
30
|
gem.add_development_dependency 'simplecov'
|
31
31
|
gem.add_development_dependency 'io-grab'
|
data/docker-compose.yml
CHANGED
@@ -1,27 +1,62 @@
|
|
1
1
|
version: '2.1'
|
2
2
|
services:
|
3
|
-
|
3
|
+
pg:
|
4
4
|
image: postgres:9.3
|
5
5
|
|
6
|
-
|
7
|
-
image:
|
6
|
+
conjur_5:
|
7
|
+
image: cyberark/conjur
|
8
8
|
command: server -a cucumber
|
9
9
|
environment:
|
10
|
-
DATABASE_URL: postgres://postgres@
|
10
|
+
DATABASE_URL: postgres://postgres@pg/postgres
|
11
11
|
CONJUR_DATA_KEY: 'WMfApcDBtocRWV+ZSUP3Tjr5XNU+Z2FdBb6BEezejIs='
|
12
|
+
volumes:
|
13
|
+
- authn_local_5:/run/authn-local
|
12
14
|
depends_on:
|
13
|
-
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
- pg
|
16
|
+
|
17
|
+
conjur_4:
|
18
|
+
image: registry2.itci.conjur.net/conjur-appliance-cuke-master:4.9-stable
|
19
|
+
security_opt:
|
20
|
+
- seccomp:unconfined
|
21
|
+
volumes:
|
22
|
+
- ./features_v4/support/policy.yml:/etc/policy.yml
|
23
|
+
- authn_local_4:/run/authn-local
|
19
24
|
|
20
|
-
|
25
|
+
tester_5:
|
21
26
|
build: .
|
22
27
|
volumes:
|
23
28
|
- ./spec/reports:/src/conjur-api/spec/reports
|
24
29
|
- ./features/reports:/src/conjur-api/features/reports
|
30
|
+
- authn_local_5:/run/authn-local-5
|
31
|
+
environment:
|
32
|
+
CONJUR_APPLIANCE_URL: http://conjur_5
|
33
|
+
CONJUR_VERSION: 5
|
34
|
+
CONJUR_ACCOUNT: cucumber
|
35
|
+
|
36
|
+
tester_4:
|
37
|
+
build: .
|
38
|
+
volumes:
|
39
|
+
- ./features_v4/reports:/src/conjur-api/features_v4/reports
|
40
|
+
- ./tmp/conjur.pem:/src/conjur-api/tmp/conjur.pem
|
41
|
+
- authn_local_4:/run/authn-local-4
|
25
42
|
environment:
|
26
|
-
CONJUR_APPLIANCE_URL:
|
43
|
+
CONJUR_APPLIANCE_URL: https://conjur_4/api
|
44
|
+
CONJUR_VERSION: 4
|
27
45
|
CONJUR_ACCOUNT: cucumber
|
46
|
+
|
47
|
+
dev:
|
48
|
+
build: .
|
49
|
+
entrypoint: bash
|
50
|
+
volumes:
|
51
|
+
- .:/src/conjur-api
|
52
|
+
- authn_local_4:/run/authn-local-4
|
53
|
+
- authn_local_5:/run/authn-local-5
|
54
|
+
environment:
|
55
|
+
CONJUR_ACCOUNT: cucumber
|
56
|
+
depends_on:
|
57
|
+
- conjur_4
|
58
|
+
- conjur_5
|
59
|
+
|
60
|
+
volumes:
|
61
|
+
authn_local_4:
|
62
|
+
authn_local_5:
|
data/example/demo_v4.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'conjur-api'
|
4
|
+
require 'securerandom'
|
5
|
+
|
6
|
+
username = "admin"
|
7
|
+
password = "secret"
|
8
|
+
|
9
|
+
Conjur.configuration.appliance_url = "https://conjur_4/api"
|
10
|
+
Conjur.configuration.account = "cucumber"
|
11
|
+
Conjur.configuration.cert_file = "./tmp/conjur.pem"
|
12
|
+
Conjur.configuration.version = 4
|
13
|
+
Conjur.configuration.apply_cert_config!
|
14
|
+
|
15
|
+
puts "Configured with Conjur version: #{Conjur.configuration.version}"
|
16
|
+
puts
|
17
|
+
|
18
|
+
api_key = Conjur::API.login username, password
|
19
|
+
api = Conjur::API.new_from_key username, api_key
|
20
|
+
|
21
|
+
db_password = SecureRandom.hex(12)
|
22
|
+
puts "Populating variable 'db-password' = #{db_password.inspect}"
|
23
|
+
api.resource("cucumber:variable:db-password").add_value db_password
|
24
|
+
puts "Value added"
|
25
|
+
puts
|
26
|
+
|
27
|
+
puts "Creating host factory token for 'myapp'"
|
28
|
+
expiration = Time.now + 1.day
|
29
|
+
hf_token = api.resource("cucumber:host_factory:myapp").create_token expiration
|
30
|
+
puts "Created: #{hf_token.token}"
|
31
|
+
puts
|
32
|
+
|
33
|
+
puts "Creating new host 'host-01' with host factory"
|
34
|
+
host = Conjur::API.host_factory_create_host(hf_token, "host-01")
|
35
|
+
puts "Created: #{host}"
|
36
|
+
puts
|
37
|
+
|
38
|
+
puts "Logging in as #{host.id}"
|
39
|
+
host_api = Conjur::API.new_from_key "host/host-01", host.api_key
|
40
|
+
puts "Logged in"
|
41
|
+
puts
|
42
|
+
|
43
|
+
|
44
|
+
puts "Fetching db-password as #{host.id}"
|
45
|
+
value = host_api.resource("cucumber:variable:db-password").value
|
46
|
+
puts value
|
47
|
+
puts
|
48
|
+
|
49
|
+
puts "Done!"
|
data/example/demo_v5.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'conjur-api'
|
4
|
+
require 'securerandom'
|
5
|
+
|
6
|
+
username = "admin"
|
7
|
+
|
8
|
+
arguments = ARGV.dup
|
9
|
+
|
10
|
+
api_key = arguments.shift or raise "Usage: ./demo_v5 <admin-api-key>"
|
11
|
+
|
12
|
+
Conjur.configuration.appliance_url = "http://conjur_5"
|
13
|
+
Conjur.configuration.account = "cucumber"
|
14
|
+
# This is the default
|
15
|
+
# Conjur.configuration.version = 5
|
16
|
+
|
17
|
+
puts "Configured with Conjur version: #{Conjur.configuration.version}"
|
18
|
+
puts
|
19
|
+
|
20
|
+
api = Conjur::API.new_from_key username, api_key
|
21
|
+
|
22
|
+
policy = File.read("features_v4/support/policy.yml")
|
23
|
+
|
24
|
+
puts "Loading policy 'root'"
|
25
|
+
policy_result = api.load_policy "root", policy
|
26
|
+
puts "Loaded: #{policy_result}"
|
27
|
+
puts
|
28
|
+
|
29
|
+
db_password = SecureRandom.hex(12)
|
30
|
+
puts "Populating variable 'db-password' = #{db_password.inspect}"
|
31
|
+
api.resource("cucumber:variable:db-password").add_value db_password
|
32
|
+
puts "Value added"
|
33
|
+
puts
|
34
|
+
|
35
|
+
puts "Creating host factory token for 'myapp'"
|
36
|
+
expiration = Time.now + 1.day
|
37
|
+
hf_token = api.resource("cucumber:host_factory:myapp").create_token expiration
|
38
|
+
puts "Created: #{hf_token.token}"
|
39
|
+
puts
|
40
|
+
|
41
|
+
puts "Creating new host 'host-01' with host factory"
|
42
|
+
host = Conjur::API.host_factory_create_host(hf_token, "host-01")
|
43
|
+
puts "Created: #{host}"
|
44
|
+
puts
|
45
|
+
|
46
|
+
puts "Logging in as #{host.id}"
|
47
|
+
host_api = Conjur::API.new_from_key "host/host-01", host.api_key
|
48
|
+
puts "Logged in"
|
49
|
+
puts
|
50
|
+
|
51
|
+
|
52
|
+
puts "Fetching db-password as #{host.id}"
|
53
|
+
value = host_api.resource("cucumber:variable:db-password").value
|
54
|
+
puts value
|
55
|
+
puts
|
56
|
+
|
57
|
+
puts "Done!"
|
@@ -0,0 +1,32 @@
|
|
1
|
+
Feature: When co-located with the Conjur server, the API can use the authn-local service to authenticate.
|
2
|
+
|
3
|
+
Scenario: authn-local can be used to obtain an access token.
|
4
|
+
When I run the code:
|
5
|
+
"""
|
6
|
+
Conjur::API.authenticate_local "alice"
|
7
|
+
"""
|
8
|
+
Then the JSON should have "payload"
|
9
|
+
And I run the code:
|
10
|
+
"""
|
11
|
+
JSON.parse(Base64.decode64(@result['payload']))
|
12
|
+
"""
|
13
|
+
Then the JSON should have "sub"
|
14
|
+
And the JSON should have "iat"
|
15
|
+
|
16
|
+
Scenario: Conjur API supports construction from authn-local.
|
17
|
+
When I run the code:
|
18
|
+
"""
|
19
|
+
@api = Conjur::API.new_from_authn_local "alice"
|
20
|
+
@api.token
|
21
|
+
"""
|
22
|
+
Then the JSON should have "payload"
|
23
|
+
|
24
|
+
Scenario: Conjur API will automatically refresh the token.
|
25
|
+
When I run the code:
|
26
|
+
"""
|
27
|
+
@api = Conjur::API.new_from_authn_local "alice"
|
28
|
+
@api.token
|
29
|
+
@api.force_token_refresh
|
30
|
+
@api.token
|
31
|
+
"""
|
32
|
+
Then the JSON should have "payload"
|