stitches 4.1.0RC2 → 4.2.0.RC1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +0 -38
  3. data/.gitignore +1 -0
  4. data/README.md +36 -4
  5. data/lib/stitches/add_disabled_at_to_api_clients_generator.rb +18 -0
  6. data/lib/stitches/allowlist_middleware.rb +20 -6
  7. data/lib/stitches/api_client_access_wrapper.rb +42 -11
  8. data/lib/stitches/api_key.rb +5 -5
  9. data/lib/stitches/configuration.rb +4 -0
  10. data/lib/stitches/generator_files/db/migrate/add_disabled_at_to_api_clients.rb +9 -0
  11. data/lib/stitches/generator_files/db/migrate/create_api_clients.rb +1 -0
  12. data/lib/stitches/render_timestamps_in_iso8601_in_json.rb +2 -6
  13. data/lib/stitches/valid_mime_type.rb +1 -1
  14. data/lib/stitches/version.rb +1 -1
  15. data/lib/stitches_norailtie.rb +1 -0
  16. data/spec/api_key_middleware_spec.rb +368 -0
  17. data/spec/api_version_constraint_middleware_spec.rb +58 -0
  18. data/spec/configuration_spec.rb +1 -1
  19. data/spec/deprecation_spec.rb +1 -1
  20. data/spec/error_spec.rb +1 -1
  21. data/spec/errors_spec.rb +3 -3
  22. data/spec/fake_app/.rspec +1 -0
  23. data/spec/fake_app/.ruby-version +1 -0
  24. data/spec/fake_app/Gemfile +53 -0
  25. data/spec/fake_app/README.md +24 -0
  26. data/spec/fake_app/Rakefile +6 -0
  27. data/spec/fake_app/app/assets/config/manifest.js +2 -0
  28. data/spec/fake_app/app/assets/stylesheets/application.css +15 -0
  29. data/spec/fake_app/app/controllers/api.rb +2 -0
  30. data/spec/fake_app/app/controllers/api/api_controller.rb +31 -0
  31. data/spec/fake_app/app/controllers/api/v1.rb +2 -0
  32. data/spec/fake_app/app/controllers/api/v1/hellos_controller.rb +7 -0
  33. data/spec/fake_app/app/controllers/api/v1/pings_controller.rb +16 -0
  34. data/spec/fake_app/app/controllers/api/v2.rb +2 -0
  35. data/spec/fake_app/app/controllers/api/v2/hellos_controller.rb +7 -0
  36. data/spec/fake_app/app/controllers/api/v2/pings_controller.rb +16 -0
  37. data/spec/fake_app/app/controllers/application_controller.rb +2 -0
  38. data/spec/fake_app/app/helpers/application_helper.rb +2 -0
  39. data/spec/fake_app/app/models/api_client.rb +2 -0
  40. data/spec/fake_app/app/models/application_record.rb +3 -0
  41. data/spec/fake_app/bin/rails +4 -0
  42. data/spec/fake_app/bin/rake +4 -0
  43. data/spec/fake_app/bin/setup +33 -0
  44. data/spec/fake_app/config.ru +6 -0
  45. data/spec/fake_app/config/application.rb +35 -0
  46. data/spec/fake_app/config/boot.rb +3 -0
  47. data/spec/fake_app/config/credentials.yml.enc +1 -0
  48. data/spec/fake_app/config/database.yml +25 -0
  49. data/spec/fake_app/config/environment.rb +5 -0
  50. data/spec/fake_app/config/environments/development.rb +71 -0
  51. data/spec/fake_app/config/environments/production.rb +109 -0
  52. data/spec/fake_app/config/environments/test.rb +52 -0
  53. data/spec/fake_app/config/initializers/assets.rb +12 -0
  54. data/spec/fake_app/config/initializers/cookies_serializer.rb +5 -0
  55. data/spec/fake_app/config/initializers/filter_parameter_logging.rb +6 -0
  56. data/spec/fake_app/config/initializers/stitches.rb +24 -0
  57. data/spec/fake_app/config/locales/en.yml +33 -0
  58. data/spec/fake_app/config/master.key +1 -0
  59. data/spec/fake_app/config/puma.rb +43 -0
  60. data/spec/fake_app/config/routes.rb +17 -0
  61. data/spec/fake_app/config/storage.yml +34 -0
  62. data/spec/fake_app/db/development.sqlite3 +0 -0
  63. data/spec/fake_app/db/migrate/20210802153118_enable_uuid_ossp_extension.rb +7 -0
  64. data/spec/fake_app/db/migrate/20210802153119_create_api_clients.rb +14 -0
  65. data/spec/fake_app/db/schema_missing_disabled_at.rb +12 -0
  66. data/spec/fake_app/db/schema_missing_enabled.rb +11 -0
  67. data/spec/fake_app/db/schema_modern.rb +13 -0
  68. data/spec/fake_app/db/seeds.rb +7 -0
  69. data/spec/fake_app/db/test.sqlite3 +0 -0
  70. data/spec/fake_app/doc/api.md +4 -0
  71. data/spec/fake_app/lib/tasks/generate_api_key.rake +10 -0
  72. data/spec/fake_app/public/404.html +67 -0
  73. data/spec/fake_app/public/422.html +67 -0
  74. data/spec/fake_app/public/500.html +66 -0
  75. data/spec/fake_app/public/apple-touch-icon-precomposed.png +0 -0
  76. data/spec/fake_app/public/apple-touch-icon.png +0 -0
  77. data/spec/fake_app/public/favicon.ico +0 -0
  78. data/spec/fake_app/public/javascripts/apitome/application.js +31 -0
  79. data/spec/fake_app/public/robots.txt +1 -0
  80. data/spec/fake_app/public/stylesheets/apitome/application.css +269 -0
  81. data/spec/fake_app/test/application_system_test_case.rb +5 -0
  82. data/spec/fake_app/test/test_helper.rb +13 -0
  83. data/spec/fake_app/tmp/development_secret.txt +1 -0
  84. data/spec/integration/add_to_rails_app_spec.rb +9 -1
  85. data/spec/rails_helper.rb +64 -0
  86. data/spec/valid_mime_type_middleware_spec.rb +59 -0
  87. data/spec/valid_mime_type_spec.rb +6 -4
  88. data/stitches.gemspec +2 -0
  89. metadata +165 -9
  90. data/spec/api_client_access_wrapper_spec.rb +0 -52
  91. data/spec/api_key_spec.rb +0 -208
  92. data/spec/api_version_constraint_spec.rb +0 -33
@@ -1,52 +0,0 @@
1
- require 'spec_helper.rb'
2
-
3
- module MyApp
4
- class Application
5
- end
6
- end
7
-
8
- unless defined? ApiClient
9
- class ApiClient
10
- def self.column_names
11
- ["enabled"]
12
- end
13
- end
14
- end
15
-
16
- describe Stitches::ApiClientAccessWrapper do
17
- let(:api_client) {
18
- double(ApiClient, id: 42)
19
- }
20
- before do
21
- Stitches.configuration.reset_to_defaults!
22
- end
23
- describe '#fetch_by_key' do
24
- context "cache is disabled" do
25
- before do
26
- expect(ApiClient).to receive(:find_by).and_return(api_client).twice
27
- end
28
-
29
- it "fetchs object from db twice" do
30
- expect(described_class.fetch_for_key("123").id).to eq(42)
31
- expect(described_class.fetch_for_key("123").id).to eq(42)
32
- end
33
- end
34
-
35
- context "cache is configured" do
36
- before do
37
- Stitches.configure do |config|
38
- config.max_cache_ttl = 5
39
- config.max_cache_size = 10
40
- end
41
-
42
- expect(ApiClient).to receive(:find_by).and_return(api_client).once
43
- end
44
-
45
- it "fetchs object from cache" do
46
- expect(described_class.fetch_for_key("123").id).to eq(42)
47
- # This should hit the cache
48
- expect(described_class.fetch_for_key("123").id).to eq(42)
49
- end
50
- end
51
- end
52
- end
data/spec/api_key_spec.rb DELETED
@@ -1,208 +0,0 @@
1
- require 'spec_helper.rb'
2
-
3
- module MyApp
4
- class Application
5
- end
6
- end
7
-
8
- unless defined? ApiClient
9
- class ApiClient
10
- def self.column_names
11
- ["enabled"]
12
- end
13
- end
14
- end
15
-
16
- describe Stitches::ApiKey do
17
- let(:app) { double("rack app") }
18
- let(:api_client) {
19
- double(ApiClient, id: 42)
20
- }
21
-
22
- before do
23
- Stitches.configuration.reset_to_defaults!
24
- Stitches.configuration.custom_http_auth_scheme = 'MyAwesomeInternalScheme'
25
- fake_rails_app = MyApp::Application.new
26
- allow(Rails).to receive(:application).and_return(fake_rails_app)
27
- allow(app).to receive(:call).with(env)
28
- allow(ApiClient).to receive(:find_by).and_return(api_client)
29
- Stitches::ApiClientAccessWrapper.clear_api_cache
30
- end
31
-
32
- subject(:middleware) { described_class.new(app, namespace: "/api") }
33
-
34
- shared_examples "an unauthorized response" do
35
- it "returns a 401" do
36
- status, _headers, _body = @response
37
- expect(status).to eq(401)
38
- end
39
- it "sets the proper header" do
40
- _status, headers, _body = @response
41
- expect(headers["WWW-Authenticate"]).to eq("MyAwesomeInternalScheme realm=MyApp")
42
- end
43
- it "stops the call chain preventing anything from happening" do
44
- expect(app).not_to have_received(:call)
45
- end
46
- it "sends a reasonable message" do
47
- _status, _headers, body = @response
48
- expect(body).to eq([expected_body])
49
- end
50
- end
51
-
52
- describe "#call" do
53
- context "not in namespace" do
54
- context "not allowlisted" do
55
- let(:env) {
56
- {
57
- "PATH_INFO" => "/index/apifoolingyou/home",
58
- }
59
- }
60
-
61
- before do
62
- @response = middleware.call(env)
63
- end
64
-
65
- it_behaves_like "an unauthorized response" do
66
- let(:expected_body) { "Unauthorized - no authorization header" }
67
- end
68
- end
69
- context "allowlisting" do
70
- context "allowlist is explicit in middleware usage" do
71
- before do
72
- @response = middleware.call(env)
73
- end
74
- context "passes the allowlist" do
75
- subject(:middleware) { described_class.new(app, except: %r{\A/resque\/.*\Z}) }
76
- let(:env) {
77
- {
78
- "PATH_INFO" => "/resque/overview"
79
- }
80
- }
81
- it "calls through to the rest of the chain" do
82
- expect(app).to have_received(:call).with(env)
83
- end
84
- end
85
-
86
- context "fails the allowlist" do
87
- subject(:middleware) { described_class.new(app, except: %r{\A/resque\/.*\Z}) }
88
- let(:env) {
89
- {
90
- "PATH_INFO" => "//resque/overview" # subtle
91
- }
92
- }
93
- it_behaves_like "an unauthorized response" do
94
- let(:expected_body) { "Unauthorized - no authorization header" }
95
- end
96
- end
97
- context "except: is not given a regexp" do
98
- let(:env) {
99
- {
100
- "PATH_INFO" => "//resque/overview" # subtle
101
- }
102
- }
103
- it "blows up" do
104
- expect {
105
- described_class.new(app, except: "/resque")
106
- }.to raise_error(/must be a Regexp/i)
107
- end
108
- end
109
- end
110
- context "allowlist is implicit from the configuration" do
111
-
112
- before do
113
- Stitches.configuration.allowlist_regexp = %r{\A/resque/.*\Z}
114
- @response = middleware.call(env)
115
- end
116
-
117
- context "passes the allowlist" do
118
- subject(:middleware) { described_class.new(app) }
119
- let(:env) {
120
- {
121
- "PATH_INFO" => "/resque/overview"
122
- }
123
- }
124
- it "calls through to the rest of the chain" do
125
- expect(app).to have_received(:call).with(env)
126
- end
127
- end
128
-
129
- context "fails the allowlist" do
130
- subject(:middleware) { described_class.new(app) }
131
- let(:env) {
132
- {
133
- "PATH_INFO" => "//resque/overview" # subtle
134
- }
135
- }
136
- it_behaves_like "an unauthorized response" do
137
- let(:expected_body) { "Unauthorized - no authorization header" }
138
- end
139
- end
140
- end
141
- end
142
- end
143
-
144
- context "valid key" do
145
- let(:env) {
146
- {
147
- "PATH_INFO" => "/api/ping",
148
- "HTTP_AUTHORIZATION" => "MyAwesomeInternalScheme key=foobar",
149
- }
150
- }
151
-
152
- before do
153
- @response = middleware.call(env)
154
- end
155
- it "calls through to the rest of the chain" do
156
- expect(app).to have_received(:call).with(env)
157
- end
158
-
159
- it "sets the api_client's ID in the environment" do
160
- expect(env[Stitches.configuration.env_var_to_hold_api_client_primary_key]).to eq(api_client.id)
161
- end
162
-
163
- it "sets the api_client itself in the environment" do
164
- expect(env[Stitches.configuration.env_var_to_hold_api_client]).to eq(api_client)
165
- end
166
- end
167
-
168
- context "unauthorized responses" do
169
- before do
170
- @response = middleware.call(env)
171
- end
172
- context "invalid key" do
173
- let(:env) {
174
- {
175
- "PATH_INFO" => "/api/ping",
176
- "HTTP_AUTHORIZATION" => "MyAwesomeInternalScheme key=foobar",
177
- }
178
- }
179
- let(:api_client) { nil }
180
-
181
- it_behaves_like "an unauthorized response" do
182
- let(:expected_body) { "Unauthorized - key invalid" }
183
- end
184
- end
185
- context "bad authorization type" do
186
- let(:env) {
187
- {
188
- "PATH_INFO" => "/api/ping",
189
- "HTTP_AUTHORIZATION" => "foobar",
190
- }
191
- }
192
- it_behaves_like "an unauthorized response" do
193
- let(:expected_body) { "Unauthorized - bad authorization type" }
194
- end
195
- end
196
- context "no auth header" do
197
- let(:env) {
198
- {
199
- "PATH_INFO" => "/api/ping",
200
- }
201
- }
202
- it_behaves_like "an unauthorized response" do
203
- let(:expected_body) { "Unauthorized - no authorization header" }
204
- end
205
- end
206
- end
207
- end
208
- end
@@ -1,33 +0,0 @@
1
- require 'spec_helper.rb'
2
-
3
- describe Stitches::ApiVersionConstraint do
4
- let(:version) { 2 }
5
- let(:request) { double("request", headers: headers) }
6
-
7
- subject(:constraint) { described_class.new(version) }
8
-
9
- context "no accept header" do
10
- let(:headers) { {} }
11
- it "doesn't match" do
12
- expect(constraint.matches?(request)).to eq(false)
13
- end
14
- end
15
- context "accept header missing version" do
16
- let(:headers) { { accept: "application/json" } }
17
- it "doesn't match" do
18
- expect(constraint.matches?(request)).to eq(false)
19
- end
20
- end
21
- context "accept header has wrong version" do
22
- let(:headers) { { accept: "application/json; version=1" } }
23
- it "doesn't match" do
24
- expect(constraint.matches?(request)).to eq(false)
25
- end
26
- end
27
- context "accept header has correct version" do
28
- let(:headers) { { accept: "application/json; version=2" } }
29
- it "matcheds" do
30
- expect(constraint.matches?(request)).to eq(true)
31
- end
32
- end
33
- end