conjur-api 5.0.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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"
|