machina-auth 0.1.1 → 0.1.2
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/lib/machina/configuration.rb +3 -1
- data/lib/machina/middleware/authentication.rb +22 -0
- data/lib/machina/permission_sync.rb +35 -6
- data/lib/machina/version.rb +1 -1
- data/spec/machina/middleware/skip_paths_spec.rb +198 -0
- data/spec/machina/permission_sync_spec.rb +169 -6
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 72016a1f127a752307bd3351a7179bd514a07f01257141b5976c24512f9a58ab
|
|
4
|
+
data.tar.gz: 49e3d3a871defaf71cc93405268d6935afdbe97131b5f565fcf74fa7d336c8fa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1b1ca73243c466e604fd6922c9f60ea716d264e7dac913093fdbcaca1fa4e18b5aea6318eae5d5f04fc5809644680c6b39147339f2fa95358da4e74238efee88
|
|
7
|
+
data.tar.gz: 78582e31a57898eacd0a5abfb4c4bb812eb6683420549590a88ffbe8ffa0ded22e091cdf093f76ef3db75675719d81b4dc7de05482a7babc5d364f03e56d4b2f
|
|
@@ -11,6 +11,9 @@ module Machina
|
|
|
11
11
|
|
|
12
12
|
def call(env)
|
|
13
13
|
request = ActionDispatch::Request.new(env)
|
|
14
|
+
|
|
15
|
+
return @app.call(env) if skip_path?(request)
|
|
16
|
+
|
|
14
17
|
token = extract_token(request)
|
|
15
18
|
|
|
16
19
|
return @app.call(env) if token.blank?
|
|
@@ -30,6 +33,25 @@ module Machina
|
|
|
30
33
|
|
|
31
34
|
private
|
|
32
35
|
|
|
36
|
+
# Checks whether the request matches any configured skip path.
|
|
37
|
+
#
|
|
38
|
+
# Strings match as path prefixes. Regexes match against both the
|
|
39
|
+
# request path and the full URL, allowing pattern-based exclusions
|
|
40
|
+
# on host, path, or any combination.
|
|
41
|
+
def skip_path?(request)
|
|
42
|
+
return false if Machina.config.skip_paths.blank?
|
|
43
|
+
|
|
44
|
+
path = request.path
|
|
45
|
+
url = request.url
|
|
46
|
+
|
|
47
|
+
Machina.config.skip_paths.any? do |pattern|
|
|
48
|
+
case pattern
|
|
49
|
+
when Regexp then pattern.match?(path) || pattern.match?(url)
|
|
50
|
+
when String then path.start_with?(pattern)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
33
55
|
def extract_token(request)
|
|
34
56
|
request.cookies['machina_session'] ||
|
|
35
57
|
extract_bearer(request) ||
|
|
@@ -3,25 +3,54 @@
|
|
|
3
3
|
module Machina
|
|
4
4
|
# Reads the local permission manifest (machina.yml) and synchronises it
|
|
5
5
|
# with the Machina Console so the Console knows which permissions exist.
|
|
6
|
+
#
|
|
7
|
+
# Supports flat manifests, environment-scoped manifests, and ERB
|
|
8
|
+
# interpolation.
|
|
6
9
|
class PermissionSync
|
|
7
10
|
def self.call!
|
|
8
11
|
new.call!
|
|
9
12
|
end
|
|
10
13
|
|
|
11
14
|
def call!
|
|
12
|
-
manifest =
|
|
15
|
+
manifest = load_manifest
|
|
13
16
|
|
|
14
|
-
product_id = manifest[
|
|
17
|
+
product_id = manifest[:product_id] || Machina.config.product_id
|
|
15
18
|
if product_id.blank?
|
|
16
|
-
raise Machina::ConfigurationError,
|
|
17
|
-
'product_id is required for permission sync (set in machina.yml or Machina.config)'
|
|
19
|
+
raise Machina::ConfigurationError, 'product_id is required for permission sync (set in machina.yml or Machina.config)'
|
|
18
20
|
end
|
|
19
21
|
|
|
20
22
|
Machina.identity_client.sync_permissions(
|
|
21
23
|
product_id:,
|
|
22
|
-
permissions: manifest.fetch(
|
|
23
|
-
policies: manifest.fetch(
|
|
24
|
+
permissions: manifest.fetch(:permissions),
|
|
25
|
+
policies: manifest.fetch(:policies, []),
|
|
24
26
|
)
|
|
25
27
|
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
# Loads the manifest YAML with ERB support and optional environment scoping.
|
|
32
|
+
#
|
|
33
|
+
# Tries +Rails.application.config_for+ first, which handles ERB evaluation
|
|
34
|
+
# and environment scoping (e.g. +test:+, +production:+, +shared:+ keys).
|
|
35
|
+
# Falls back to direct ERB + YAML parsing for flat manifests that have no
|
|
36
|
+
# environment keys.
|
|
37
|
+
def load_manifest
|
|
38
|
+
path = Pathname.new(Machina.config.manifest)
|
|
39
|
+
|
|
40
|
+
result = Rails.application.config_for(path)
|
|
41
|
+
return result if result.present?
|
|
42
|
+
|
|
43
|
+
parse_flat_manifest(path)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Parses a flat (non-environment-scoped) manifest with ERB support.
|
|
47
|
+
#
|
|
48
|
+
# @param path [Pathname] absolute path to the YAML file
|
|
49
|
+
# @return [ActiveSupport::OrderedOptions]
|
|
50
|
+
def parse_flat_manifest(path)
|
|
51
|
+
raw = ERB.new(path.read).result
|
|
52
|
+
data = YAML.safe_load(raw, permitted_classes: [Symbol]).deep_symbolize_keys
|
|
53
|
+
ActiveSupport::OrderedOptions.new.update(data)
|
|
54
|
+
end
|
|
26
55
|
end
|
|
27
56
|
end
|
data/lib/machina/version.rb
CHANGED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails_helper'
|
|
4
|
+
|
|
5
|
+
RSpec.describe Machina::Middleware::Authentication, 'skip_paths' do
|
|
6
|
+
let(:app) do
|
|
7
|
+
lambda do |_env|
|
|
8
|
+
auth = Machina::Current.authorized
|
|
9
|
+
body = JSON.generate(user_id: auth&.user_id, permissions: auth&.permissions)
|
|
10
|
+
[200, { 'Content-Type' => 'application/json' }, [body]]
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
let(:middleware) { described_class.new(app) }
|
|
14
|
+
let(:identity_client) { instance_double(Machina::IdentityClient) }
|
|
15
|
+
let(:bearer_header) { { 'HTTP_AUTHORIZATION' => 'Bearer ps_123' } }
|
|
16
|
+
|
|
17
|
+
before do
|
|
18
|
+
allow(Machina).to receive(:identity_client).and_return(identity_client)
|
|
19
|
+
allow(identity_client).to receive(:resolve_session)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# -- Happy path: skipped routes pass through without resolution -----------
|
|
23
|
+
|
|
24
|
+
context 'with a string skip path' do
|
|
25
|
+
before { Machina.config.skip_paths = ['/webhooks'] }
|
|
26
|
+
|
|
27
|
+
it 'passes through without resolving the token' do
|
|
28
|
+
env = Rack::MockRequest.env_for('/webhooks', bearer_header)
|
|
29
|
+
|
|
30
|
+
status, _headers, body = middleware.call(env)
|
|
31
|
+
|
|
32
|
+
expect(status).to eq(200)
|
|
33
|
+
expect(JSON.parse(body.first)['user_id']).to be_nil
|
|
34
|
+
expect(identity_client).not_to have_received(:resolve_session)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
context 'with a regex skip path' do
|
|
39
|
+
before { Machina.config.skip_paths = [%r{\A/callbacks/}] }
|
|
40
|
+
|
|
41
|
+
it 'passes through without resolving the token' do
|
|
42
|
+
env = Rack::MockRequest.env_for('/callbacks/stripe', bearer_header)
|
|
43
|
+
|
|
44
|
+
status, = middleware.call(env)
|
|
45
|
+
|
|
46
|
+
expect(status).to eq(200)
|
|
47
|
+
expect(identity_client).not_to have_received(:resolve_session)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# -- Prefix matching for strings -----------------------------------------
|
|
52
|
+
|
|
53
|
+
context 'when request path is a sub-path of a string skip path' do
|
|
54
|
+
before { Machina.config.skip_paths = ['/webhooks'] }
|
|
55
|
+
|
|
56
|
+
it 'skips nested paths' do
|
|
57
|
+
env = Rack::MockRequest.env_for('/webhooks/stripe/events', bearer_header)
|
|
58
|
+
|
|
59
|
+
status, = middleware.call(env)
|
|
60
|
+
|
|
61
|
+
expect(status).to eq(200)
|
|
62
|
+
expect(identity_client).not_to have_received(:resolve_session)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# -- Non-matching routes still resolve ------------------------------------
|
|
67
|
+
|
|
68
|
+
context 'when the path does not match any skip path' do
|
|
69
|
+
before do
|
|
70
|
+
Machina.config.skip_paths = ['/webhooks']
|
|
71
|
+
allow(identity_client).to receive(:resolve_session).with('ps_123').and_return(
|
|
72
|
+
Machina::IdentityClient::Response.new(status: 200, body: MockResponses.session_resolution),
|
|
73
|
+
)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it 'resolves the token normally' do
|
|
77
|
+
env = Rack::MockRequest.env_for('/api/resources', bearer_header)
|
|
78
|
+
|
|
79
|
+
status, _headers, body = middleware.call(env)
|
|
80
|
+
|
|
81
|
+
expect(status).to eq(200)
|
|
82
|
+
expect(JSON.parse(body.first)['user_id']).to eq(MockResponses.session_resolution['data']['user']['id'])
|
|
83
|
+
expect(identity_client).to have_received(:resolve_session).once
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# -- Multiple skip paths -------------------------------------------------
|
|
88
|
+
|
|
89
|
+
context 'with multiple skip paths (mixed strings and regexes)' do
|
|
90
|
+
before { Machina.config.skip_paths = ['/webhooks', %r{\A/callbacks/\w+\z}] }
|
|
91
|
+
|
|
92
|
+
it 'skips a request matching the string entry' do
|
|
93
|
+
env = Rack::MockRequest.env_for('/webhooks', bearer_header)
|
|
94
|
+
middleware.call(env)
|
|
95
|
+
expect(identity_client).not_to have_received(:resolve_session)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it 'skips a request matching the regex entry' do
|
|
99
|
+
env = Rack::MockRequest.env_for('/callbacks/stripe', bearer_header)
|
|
100
|
+
middleware.call(env)
|
|
101
|
+
expect(identity_client).not_to have_received(:resolve_session)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it 'does not skip a request matching neither entry' do
|
|
105
|
+
env = Rack::MockRequest.env_for('/api/resources', bearer_header)
|
|
106
|
+
middleware.call(env)
|
|
107
|
+
expect(identity_client).to have_received(:resolve_session).once
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# -- Edge cases: near-miss paths -----------------------------------------
|
|
112
|
+
|
|
113
|
+
context 'when the path is a near-miss of a string skip path' do
|
|
114
|
+
before { Machina.config.skip_paths = ['/webhooks'] }
|
|
115
|
+
|
|
116
|
+
it 'does not skip /webhook (no trailing s)' do
|
|
117
|
+
env = Rack::MockRequest.env_for('/webhook', bearer_header)
|
|
118
|
+
middleware.call(env)
|
|
119
|
+
expect(identity_client).to have_received(:resolve_session).once
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it 'does not skip /other-webhooks (different prefix)' do
|
|
123
|
+
env = Rack::MockRequest.env_for('/other-webhooks', bearer_header)
|
|
124
|
+
middleware.call(env)
|
|
125
|
+
expect(identity_client).to have_received(:resolve_session).once
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# -- Edge case: regex against full URL ------------------------------------
|
|
130
|
+
|
|
131
|
+
context 'with a regex that matches the full request URL' do
|
|
132
|
+
before { Machina.config.skip_paths = [/stripe\.com/] }
|
|
133
|
+
|
|
134
|
+
it 'matches against the full URL when present' do
|
|
135
|
+
env = Rack::MockRequest.env_for('https://api.stripe.com/webhooks', bearer_header)
|
|
136
|
+
|
|
137
|
+
status, = middleware.call(env)
|
|
138
|
+
|
|
139
|
+
expect(status).to eq(200)
|
|
140
|
+
expect(identity_client).not_to have_received(:resolve_session)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# -- Edge case: string against full URL -----------------------------------
|
|
145
|
+
|
|
146
|
+
context 'with a string skip path checked against a full URL' do
|
|
147
|
+
before { Machina.config.skip_paths = ['/webhooks'] }
|
|
148
|
+
|
|
149
|
+
it 'still matches the path portion of a full URL' do
|
|
150
|
+
env = Rack::MockRequest.env_for('https://myapp.com/webhooks/events', bearer_header)
|
|
151
|
+
|
|
152
|
+
status, = middleware.call(env)
|
|
153
|
+
|
|
154
|
+
expect(status).to eq(200)
|
|
155
|
+
expect(identity_client).not_to have_received(:resolve_session)
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# -- Edge case: empty skip_paths (default) --------------------------------
|
|
160
|
+
|
|
161
|
+
context 'when skip_paths is empty (default)' do
|
|
162
|
+
it 'resolves tokens on all paths' do
|
|
163
|
+
env = Rack::MockRequest.env_for('/webhooks', bearer_header)
|
|
164
|
+
middleware.call(env)
|
|
165
|
+
expect(identity_client).to have_received(:resolve_session).once
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# -- Edge case: no token on a skipped path --------------------------------
|
|
170
|
+
|
|
171
|
+
context 'when a skipped path has no token' do
|
|
172
|
+
before { Machina.config.skip_paths = ['/webhooks'] }
|
|
173
|
+
|
|
174
|
+
it 'passes through without attempting resolution' do
|
|
175
|
+
env = Rack::MockRequest.env_for('/webhooks')
|
|
176
|
+
|
|
177
|
+
status, = middleware.call(env)
|
|
178
|
+
|
|
179
|
+
expect(status).to eq(200)
|
|
180
|
+
expect(identity_client).not_to have_received(:resolve_session)
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# -- Edge case: query string on a skipped path ----------------------------
|
|
185
|
+
|
|
186
|
+
context 'when a skipped path has query parameters' do
|
|
187
|
+
before { Machina.config.skip_paths = ['/webhooks'] }
|
|
188
|
+
|
|
189
|
+
it 'still skips (query string does not affect path matching)' do
|
|
190
|
+
env = Rack::MockRequest.env_for('/webhooks?verify=true', bearer_header)
|
|
191
|
+
|
|
192
|
+
status, = middleware.call(env)
|
|
193
|
+
|
|
194
|
+
expect(status).to eq(200)
|
|
195
|
+
expect(identity_client).not_to have_received(:resolve_session)
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
@@ -3,21 +3,21 @@
|
|
|
3
3
|
require_relative '../rails_helper'
|
|
4
4
|
|
|
5
5
|
RSpec.describe Machina::PermissionSync do
|
|
6
|
+
let(:client) { instance_double(Machina::IdentityClient) }
|
|
7
|
+
|
|
6
8
|
before do
|
|
7
9
|
Machina.config.product_id = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
it 'loads manifests and forwards them to the identity client' do
|
|
11
|
-
client = instance_double(Machina::IdentityClient)
|
|
12
10
|
allow(Machina).to receive(:identity_client).and_return(client)
|
|
13
11
|
allow(client).to receive(:sync_permissions)
|
|
12
|
+
end
|
|
14
13
|
|
|
14
|
+
it 'loads manifests and forwards them to the identity client' do
|
|
15
15
|
described_class.call!
|
|
16
16
|
|
|
17
17
|
expect(client).to have_received(:sync_permissions).with(
|
|
18
18
|
product_id: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
|
19
|
-
permissions: [{
|
|
20
|
-
policies: [{
|
|
19
|
+
permissions: [{ key: 'sessions.view', description: 'View sessions' }],
|
|
20
|
+
policies: [{ name: 'Viewer', api_name: 'viewer', permissions: ['sessions.view'] }],
|
|
21
21
|
)
|
|
22
22
|
end
|
|
23
23
|
|
|
@@ -26,4 +26,167 @@ RSpec.describe Machina::PermissionSync do
|
|
|
26
26
|
|
|
27
27
|
expect { described_class.call! }.to raise_error(Machina::ConfigurationError, /product_id is required/)
|
|
28
28
|
end
|
|
29
|
+
|
|
30
|
+
context 'with a flat (non-environment-scoped) manifest' do
|
|
31
|
+
let(:tmpfile) do
|
|
32
|
+
file = Tempfile.new(['machina', '.yml'])
|
|
33
|
+
file.write(<<~YAML)
|
|
34
|
+
product_id: flat-product-id
|
|
35
|
+
|
|
36
|
+
permissions:
|
|
37
|
+
- key: items.view
|
|
38
|
+
description: View items
|
|
39
|
+
|
|
40
|
+
policies:
|
|
41
|
+
- name: Reader
|
|
42
|
+
api_name: reader
|
|
43
|
+
permissions:
|
|
44
|
+
- items.view
|
|
45
|
+
YAML
|
|
46
|
+
file.close
|
|
47
|
+
file
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
before { Machina.config.manifest = tmpfile.path }
|
|
51
|
+
after { tmpfile.unlink }
|
|
52
|
+
|
|
53
|
+
it 'loads the manifest as top-level keys' do
|
|
54
|
+
described_class.call!
|
|
55
|
+
|
|
56
|
+
expect(client).to have_received(:sync_permissions).with(
|
|
57
|
+
product_id: 'flat-product-id',
|
|
58
|
+
permissions: [{ key: 'items.view', description: 'View items' }],
|
|
59
|
+
policies: [{ name: 'Reader', api_name: 'reader', permissions: ['items.view'] }],
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
context 'with an environment-scoped manifest' do
|
|
65
|
+
let(:tmpfile) do
|
|
66
|
+
file = Tempfile.new(['machina', '.yml'])
|
|
67
|
+
file.write(<<~YAML)
|
|
68
|
+
test:
|
|
69
|
+
product_id: test-product-id
|
|
70
|
+
permissions:
|
|
71
|
+
- key: reports.view
|
|
72
|
+
description: View reports
|
|
73
|
+
policies: []
|
|
74
|
+
|
|
75
|
+
production:
|
|
76
|
+
product_id: prod-product-id
|
|
77
|
+
permissions:
|
|
78
|
+
- key: reports.view
|
|
79
|
+
description: View reports
|
|
80
|
+
- key: reports.edit
|
|
81
|
+
description: Edit reports
|
|
82
|
+
policies: []
|
|
83
|
+
YAML
|
|
84
|
+
file.close
|
|
85
|
+
file
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
before do
|
|
89
|
+
Machina.config.manifest = tmpfile.path
|
|
90
|
+
Machina.config.product_id = nil
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
after { tmpfile.unlink }
|
|
94
|
+
|
|
95
|
+
it 'loads the manifest scoped to the current Rails environment' do
|
|
96
|
+
described_class.call!
|
|
97
|
+
|
|
98
|
+
expect(client).to have_received(:sync_permissions).with(
|
|
99
|
+
product_id: 'test-product-id',
|
|
100
|
+
permissions: [{ key: 'reports.view', description: 'View reports' }],
|
|
101
|
+
policies: [],
|
|
102
|
+
)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
context 'with an ERB manifest' do
|
|
107
|
+
let(:tmpfile) do
|
|
108
|
+
file = Tempfile.new(['machina', '.yml'])
|
|
109
|
+
file.write(<<~'YAML')
|
|
110
|
+
product_id: <%= "erb-product-id" %>
|
|
111
|
+
|
|
112
|
+
permissions:
|
|
113
|
+
- key: tasks.view
|
|
114
|
+
description: View tasks
|
|
115
|
+
|
|
116
|
+
policies: []
|
|
117
|
+
YAML
|
|
118
|
+
file.close
|
|
119
|
+
file
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
before do
|
|
123
|
+
Machina.config.manifest = tmpfile.path
|
|
124
|
+
Machina.config.product_id = nil
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
after { tmpfile.unlink }
|
|
128
|
+
|
|
129
|
+
it 'evaluates ERB before parsing YAML' do
|
|
130
|
+
described_class.call!
|
|
131
|
+
|
|
132
|
+
expect(client).to have_received(:sync_permissions).with(
|
|
133
|
+
product_id: 'erb-product-id',
|
|
134
|
+
permissions: [{ key: 'tasks.view', description: 'View tasks' }],
|
|
135
|
+
policies: [],
|
|
136
|
+
)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
context 'when manifest product_id is absent but config product_id is set' do
|
|
141
|
+
let(:tmpfile) do
|
|
142
|
+
file = Tempfile.new(['machina', '.yml'])
|
|
143
|
+
file.write(<<~YAML)
|
|
144
|
+
permissions:
|
|
145
|
+
- key: items.view
|
|
146
|
+
description: View items
|
|
147
|
+
policies: []
|
|
148
|
+
YAML
|
|
149
|
+
file.close
|
|
150
|
+
file
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
before { Machina.config.manifest = tmpfile.path }
|
|
154
|
+
after { tmpfile.unlink }
|
|
155
|
+
|
|
156
|
+
it 'falls back to Machina.config.product_id' do
|
|
157
|
+
described_class.call!
|
|
158
|
+
|
|
159
|
+
expect(client).to have_received(:sync_permissions).with(
|
|
160
|
+
product_id: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
|
161
|
+
permissions: [{ key: 'items.view', description: 'View items' }],
|
|
162
|
+
policies: [],
|
|
163
|
+
)
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
context 'when policies key is omitted from manifest' do
|
|
168
|
+
let(:tmpfile) do
|
|
169
|
+
file = Tempfile.new(['machina', '.yml'])
|
|
170
|
+
file.write(<<~YAML)
|
|
171
|
+
permissions:
|
|
172
|
+
- key: items.view
|
|
173
|
+
description: View items
|
|
174
|
+
YAML
|
|
175
|
+
file.close
|
|
176
|
+
file
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
before { Machina.config.manifest = tmpfile.path }
|
|
180
|
+
after { tmpfile.unlink }
|
|
181
|
+
|
|
182
|
+
it 'defaults policies to an empty array' do
|
|
183
|
+
described_class.call!
|
|
184
|
+
|
|
185
|
+
expect(client).to have_received(:sync_permissions).with(
|
|
186
|
+
product_id: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
|
187
|
+
permissions: [{ key: 'items.view', description: 'View items' }],
|
|
188
|
+
policies: [],
|
|
189
|
+
)
|
|
190
|
+
end
|
|
191
|
+
end
|
|
29
192
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: machina-auth
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ZAR
|
|
@@ -179,6 +179,7 @@ files:
|
|
|
179
179
|
- spec/machina/controller_helpers_spec.rb
|
|
180
180
|
- spec/machina/identity_client_spec.rb
|
|
181
181
|
- spec/machina/middleware/authentication_spec.rb
|
|
182
|
+
- spec/machina/middleware/skip_paths_spec.rb
|
|
182
183
|
- spec/machina/permission_sync_spec.rb
|
|
183
184
|
- spec/machina/test_helpers_spec.rb
|
|
184
185
|
- spec/machina/webhook_receiver_spec.rb
|