@johnnyboy11234/esg-idea-contest 0.3.0 → 0.3.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.
Files changed (138) hide show
  1. package/backend/api/openapi.yaml +119 -0
  2. package/backend/cmd/server/main.go +36 -2
  3. package/backend/db/migration/V005__idea.sql +49 -0
  4. package/backend/db/migration/V006__login_lock.sql +15 -0
  5. package/backend/db/migration/V007__login_attempt.sql +15 -0
  6. package/backend/db/migration/V008__pending_file_deletion.sql +27 -0
  7. package/backend/db/migration/V009__login_throttle_audit.sql +13 -0
  8. package/backend/go.mod +17 -3
  9. package/backend/go.sum +48 -7
  10. package/backend/internal/account/account_test.go +6 -6
  11. package/backend/internal/account/integration_test.go +45 -9
  12. package/backend/internal/account/internal/passwords/NOTICE +36 -0
  13. package/backend/internal/account/internal/passwords/common-passwords.txt +10001 -0
  14. package/backend/internal/account/internal/passwords/passwords.go +37 -0
  15. package/backend/internal/account/internal/passwords/passwords_test.go +35 -0
  16. package/backend/internal/account/keyboard.go +75 -0
  17. package/backend/internal/account/keyboard_test.go +43 -0
  18. package/backend/internal/account/logging_integration_test.go +1 -1
  19. package/backend/internal/account/password.go +131 -0
  20. package/backend/internal/account/sequence.go +54 -0
  21. package/backend/internal/account/sequence_test.go +38 -0
  22. package/backend/internal/account/validate.go +4 -21
  23. package/backend/internal/api/api.gen.go +262 -0
  24. package/backend/internal/auth/internal/store/login_attempt.go +53 -0
  25. package/backend/internal/auth/internal/store/login_lock.go +77 -0
  26. package/backend/internal/auth/internal/store/login_throttle_audit.go +42 -0
  27. package/backend/internal/auth/internal/store/session.go +15 -7
  28. package/backend/internal/auth/login.go +16 -4
  29. package/backend/internal/auth/login_integration_test.go +14 -1
  30. package/backend/internal/auth/login_ip_integration_test.go +153 -0
  31. package/backend/internal/auth/login_lock_integration_test.go +205 -0
  32. package/backend/internal/auth/login_test.go +15 -0
  33. package/backend/internal/auth/login_throttle_audit_integration_test.go +88 -0
  34. package/backend/internal/auth/purge.go +93 -0
  35. package/backend/internal/auth/purge_integration_test.go +220 -0
  36. package/backend/internal/auth/purge_test.go +51 -0
  37. package/backend/internal/auth/session.go +0 -6
  38. package/backend/internal/auth/session_integration_test.go +3 -3
  39. package/backend/internal/auth/throttle.go +122 -0
  40. package/backend/internal/deadline/gate.go +80 -0
  41. package/backend/internal/deadline/gate_integration_test.go +67 -0
  42. package/backend/internal/deadline/gate_test.go +61 -0
  43. package/backend/internal/idea/create.go +356 -0
  44. package/backend/internal/idea/create_integration_test.go +680 -0
  45. package/backend/internal/idea/download.go +193 -0
  46. package/backend/internal/idea/download_integration_test.go +311 -0
  47. package/backend/internal/idea/download_test.go +25 -0
  48. package/backend/internal/idea/handler.go +151 -0
  49. package/backend/internal/idea/handler_integration_test.go +107 -0
  50. package/backend/internal/idea/integration_test.go +149 -0
  51. package/backend/internal/idea/internal/store/list.go +146 -0
  52. package/backend/internal/idea/internal/store/list_test.go +33 -0
  53. package/backend/internal/idea/internal/store/store.go +166 -0
  54. package/backend/internal/idea/list.go +166 -0
  55. package/backend/internal/idea/list_integration_test.go +252 -0
  56. package/backend/internal/idea/list_test.go +96 -0
  57. package/backend/internal/idea/service.go +54 -0
  58. package/backend/internal/server/api.go +22 -0
  59. package/backend/internal/server/binding.go +4 -0
  60. package/backend/internal/server/public.go +1 -0
  61. package/backend/internal/server/server.go +20 -1
  62. package/backend/internal/server/slides.go +21 -0
  63. package/backend/internal/server/slides_test.go +82 -0
  64. package/backend/internal/shared/config/config.go +44 -1
  65. package/backend/internal/shared/config/config_test.go +21 -13
  66. package/backend/internal/shared/db/intx_integration_test.go +179 -0
  67. package/backend/internal/shared/dbtest/dbtest.go +21 -3
  68. package/backend/internal/shared/dbtest/faults.go +162 -0
  69. package/backend/internal/shared/job/job.go +54 -0
  70. package/backend/internal/shared/job/job_test.go +105 -0
  71. package/backend/internal/shared/web/multipart.go +175 -0
  72. package/backend/internal/shared/web/multipart_test.go +169 -0
  73. package/backend/internal/shared/web/problem.go +1 -0
  74. package/backend/internal/shared/web/request.go +42 -1
  75. package/backend/internal/shared/web/transport.go +4 -3
  76. package/backend/internal/shared/web/web_test.go +36 -0
  77. package/backend/internal/storage/expectedfilename.go +43 -0
  78. package/backend/internal/storage/expectedfilename_test.go +16 -0
  79. package/backend/internal/storage/internal/store/pending.go +41 -0
  80. package/backend/internal/storage/minio.go +139 -0
  81. package/backend/internal/storage/minio_integration_test.go +87 -0
  82. package/backend/internal/storage/minio_test.go +84 -0
  83. package/backend/internal/storage/pending.go +144 -0
  84. package/backend/internal/storage/pending_integration_test.go +324 -0
  85. package/backend/internal/storage/pending_test.go +23 -0
  86. package/backend/internal/storage/validator.go +100 -0
  87. package/backend/internal/storage/validator_test.go +95 -0
  88. package/backend/test/arch/arch_test.go +5 -1
  89. package/backend/test/contract/expected_filename_test.go +60 -0
  90. package/backend/test/contract/password_test.go +46 -0
  91. package/backend/test/matrix/load_test.go +12 -0
  92. package/backend/test/matrix/matrix_integration_test.go +92 -5
  93. package/backend/test/migration/pending_file_deletion_test.go +46 -0
  94. package/docs/API/api-endpoints.md +1 -1
  95. package/docs/API/error-codes.md +1 -1
  96. package/docs/Architecture/Architecture.md +1 -1
  97. package/docs/Architecture/Backend.md +2 -1
  98. package/frontend/Dockerfile +2 -0
  99. package/frontend/package-lock.json +1 -0
  100. package/frontend/package.json +1 -0
  101. package/frontend/src/app/App.tsx +11 -2
  102. package/frontend/src/app/pages/ideaCreate.test.tsx +308 -0
  103. package/frontend/src/app/pages/ideas.tsx +23 -9
  104. package/frontend/src/app/pages/login.test.tsx +38 -1
  105. package/frontend/src/app/pages/register.test.tsx +32 -0
  106. package/frontend/src/features/auth/IdleWarning.test.tsx +82 -0
  107. package/frontend/src/features/auth/IdleWarning.tsx +66 -0
  108. package/frontend/src/features/auth/LoginForm.tsx +21 -3
  109. package/frontend/src/features/auth/PasswordChecklist.test.tsx +73 -0
  110. package/frontend/src/features/auth/PasswordChecklist.tsx +96 -0
  111. package/frontend/src/features/auth/RegisterForm.tsx +11 -2
  112. package/frontend/src/features/auth/common-passwords.ts +31 -0
  113. package/frontend/src/features/auth/password-policy.test.ts +64 -0
  114. package/frontend/src/features/auth/password-policy.ts +168 -0
  115. package/frontend/src/features/auth/useCountdown.test.tsx +33 -0
  116. package/frontend/src/features/auth/useCountdown.ts +28 -0
  117. package/frontend/src/features/idea/CreateIdeaForm.tsx +279 -0
  118. package/frontend/src/features/idea/create.ts +123 -0
  119. package/frontend/src/shared/api/activity.ts +20 -0
  120. package/frontend/src/shared/api/http.ts +2 -0
  121. package/frontend/src/shared/api/index.ts +1 -0
  122. package/frontend/src/shared/api/types.generated.ts +319 -1
  123. package/frontend/src/shared/i18n/locales/en.ts +46 -4
  124. package/frontend/src/shared/i18n/locales/zh-TW.ts +46 -4
  125. package/frontend/src/shared/slides/index.ts +74 -0
  126. package/frontend/src/shared/slides/slides.test.ts +51 -0
  127. package/frontend/src/shared/ui/copy-button.tsx +52 -0
  128. package/frontend/src/shared/ui/native-select.tsx +27 -0
  129. package/frontend/src/shared/ui/textarea.tsx +19 -0
  130. package/frontend/src/test-setup.ts +37 -2
  131. package/frontend/vite.config.ts +3 -1
  132. package/package.json +1 -1
  133. package/testdata/expected-filename.json +93 -0
  134. package/testdata/password.json +582 -0
  135. package/testdata/permission-matrix.json +71 -0
  136. package/testdata/schema/expected-filename.schema.json +32 -0
  137. package/testdata/schema/password.schema.json +59 -0
  138. package/testdata/schema/permission-matrix.schema.json +22 -1
@@ -0,0 +1,582 @@
1
+ {
2
+ "description": "Shared by backend (Go) and frontend (TS). password and empNo are raw strings; empNo is \"\" when the submitted employee ID is malformed (CONTAINS_EMP_NO is then skipped). wantRules lists every violated rule id in PRD 6.4 table order ([] = valid). One substring may violate several rules (\"1234\" is KEYBOARD_RUN and SEQUENCE). COMMON_PASSWORD uses backend/internal/account/internal/passwords/common-passwords.txt (whole password, case-insensitive, nothing trimmed). PRD 6.4.",
3
+ "cases": [
4
+ {
5
+ "id": "valid-typical",
6
+ "password": "Correct-Horse-1",
7
+ "empNo": "089290",
8
+ "wantRules": []
9
+ },
10
+ {
11
+ "id": "valid-min-length-12",
12
+ "password": "Azkqmvrt1!xY",
13
+ "empNo": "",
14
+ "wantRules": []
15
+ },
16
+ {
17
+ "id": "valid-max-length-64",
18
+ "password": "Ab1!Ab1!Ab1!Ab1!Ab1!Ab1!Ab1!Ab1!Ab1!Ab1!Ab1!Ab1!Ab1!Ab1!Ab1!Ab1!",
19
+ "empNo": "",
20
+ "wantRules": []
21
+ },
22
+ {
23
+ "id": "valid-space-is-symbol",
24
+ "password": "Correct Horse1",
25
+ "empNo": "",
26
+ "wantRules": []
27
+ },
28
+ {
29
+ "id": "valid-three-classes-no-symbol",
30
+ "password": "CorrectHorse12",
31
+ "empNo": "",
32
+ "wantRules": []
33
+ },
34
+ {
35
+ "id": "valid-three-classes-no-digit",
36
+ "password": "Correct-Horse-x",
37
+ "empNo": "",
38
+ "wantRules": []
39
+ },
40
+ {
41
+ "id": "valid-three-classes-no-upper",
42
+ "password": "correct-horse-1",
43
+ "empNo": "",
44
+ "wantRules": []
45
+ },
46
+ {
47
+ "id": "valid-three-classes-no-lower",
48
+ "password": "CORRECT-HORSE-1",
49
+ "empNo": "",
50
+ "wantRules": []
51
+ },
52
+ {
53
+ "id": "length-11",
54
+ "password": "Azkqmvrt1!x",
55
+ "empNo": "",
56
+ "wantRules": [
57
+ "LENGTH"
58
+ ]
59
+ },
60
+ {
61
+ "id": "length-65",
62
+ "password": "Ab1!Ab1!Ab1!Ab1!Ab1!Ab1!Ab1!Ab1!Ab1!Ab1!Ab1!Ab1!Ab1!Ab1!Ab1!Ab1!x",
63
+ "empNo": "",
64
+ "wantRules": [
65
+ "LENGTH"
66
+ ]
67
+ },
68
+ {
69
+ "id": "length-empty",
70
+ "password": "",
71
+ "empNo": "",
72
+ "wantRules": [
73
+ "LENGTH",
74
+ "COMPLEXITY"
75
+ ]
76
+ },
77
+ {
78
+ "id": "length-counts-code-points-not-bytes",
79
+ "password": "Aa1!Aa1!Aa1é",
80
+ "empNo": "",
81
+ "wantRules": [
82
+ "CHARSET"
83
+ ]
84
+ },
85
+ {
86
+ "id": "charset-non-ascii",
87
+ "password": "Correct-Horse-1é",
88
+ "empNo": "",
89
+ "wantRules": [
90
+ "CHARSET"
91
+ ]
92
+ },
93
+ {
94
+ "id": "charset-cjk-only-12",
95
+ "password": "密碼密碼密碼密碼密碼密碼",
96
+ "empNo": "",
97
+ "wantRules": [
98
+ "CHARSET",
99
+ "COMPLEXITY"
100
+ ]
101
+ },
102
+ {
103
+ "id": "charset-tab",
104
+ "password": "Correct\tHorse-1",
105
+ "empNo": "",
106
+ "wantRules": [
107
+ "CHARSET"
108
+ ]
109
+ },
110
+ {
111
+ "id": "charset-del",
112
+ "password": "Correct-Horse-1",
113
+ "empNo": "",
114
+ "wantRules": [
115
+ "CHARSET"
116
+ ]
117
+ },
118
+ {
119
+ "id": "charset-emoji-counts-as-one-code-point",
120
+ "password": "Correct-Hor1😀",
121
+ "empNo": "",
122
+ "wantRules": [
123
+ "CHARSET"
124
+ ]
125
+ },
126
+ {
127
+ "id": "complexity-two-classes-lower-digit",
128
+ "password": "azkqmvrtjx12",
129
+ "empNo": "",
130
+ "wantRules": [
131
+ "COMPLEXITY"
132
+ ]
133
+ },
134
+ {
135
+ "id": "complexity-two-classes-lower-symbol",
136
+ "password": "whatever-goes",
137
+ "empNo": "",
138
+ "wantRules": [
139
+ "COMPLEXITY"
140
+ ]
141
+ },
142
+ {
143
+ "id": "complexity-two-classes-upper-lower",
144
+ "password": "CorrectHorseXy",
145
+ "empNo": "",
146
+ "wantRules": [
147
+ "COMPLEXITY"
148
+ ]
149
+ },
150
+ {
151
+ "id": "complexity-non-ascii-is-no-class",
152
+ "password": "azkqmvrtjxp1é",
153
+ "empNo": "",
154
+ "wantRules": [
155
+ "CHARSET",
156
+ "COMPLEXITY"
157
+ ]
158
+ },
159
+ {
160
+ "id": "repeat-three-lower",
161
+ "password": "Correct-Hoooorse1",
162
+ "empNo": "",
163
+ "wantRules": [
164
+ "REPEAT"
165
+ ]
166
+ },
167
+ {
168
+ "id": "repeat-three-digit",
169
+ "password": "Correct-Horse111",
170
+ "empNo": "",
171
+ "wantRules": [
172
+ "REPEAT"
173
+ ]
174
+ },
175
+ {
176
+ "id": "repeat-exactly-two-ok",
177
+ "password": "Correct-Hoorse-11",
178
+ "empNo": "",
179
+ "wantRules": []
180
+ },
181
+ {
182
+ "id": "repeat-case-sensitive",
183
+ "password": "Correct-HoOorse-1",
184
+ "empNo": "",
185
+ "wantRules": []
186
+ },
187
+ {
188
+ "id": "repeat-symbols",
189
+ "password": "Correct-Horse1!!!",
190
+ "empNo": "",
191
+ "wantRules": [
192
+ "REPEAT"
193
+ ]
194
+ },
195
+ {
196
+ "id": "emp-no-contained",
197
+ "password": "Correct-089290-Ab",
198
+ "empNo": "089290",
199
+ "wantRules": [
200
+ "CONTAINS_EMP_NO"
201
+ ]
202
+ },
203
+ {
204
+ "id": "emp-no-contained-at-start",
205
+ "password": "089290-Correct-Ab",
206
+ "empNo": "089290",
207
+ "wantRules": [
208
+ "CONTAINS_EMP_NO"
209
+ ]
210
+ },
211
+ {
212
+ "id": "emp-no-skipped-when-malformed",
213
+ "password": "Correct-089290-Ab",
214
+ "empNo": "",
215
+ "wantRules": []
216
+ },
217
+ {
218
+ "id": "emp-no-other-number-ok",
219
+ "password": "Correct-089291-Ab",
220
+ "empNo": "089290",
221
+ "wantRules": []
222
+ },
223
+ {
224
+ "id": "table-order-all-violated",
225
+ "password": "aaaé",
226
+ "empNo": "123456",
227
+ "wantRules": [
228
+ "LENGTH",
229
+ "CHARSET",
230
+ "COMPLEXITY",
231
+ "REPEAT"
232
+ ]
233
+ },
234
+ {
235
+ "id": "table-order-with-emp-no",
236
+ "password": "aaa123456",
237
+ "empNo": "123456",
238
+ "wantRules": [
239
+ "LENGTH",
240
+ "COMPLEXITY",
241
+ "KEYBOARD_RUN",
242
+ "SEQUENCE",
243
+ "REPEAT",
244
+ "CONTAINS_EMP_NO"
245
+ ]
246
+ },
247
+ {
248
+ "id": "table-order-full-set",
249
+ "password": "aaaaaaaaaaaaé123456",
250
+ "empNo": "123456",
251
+ "wantRules": [
252
+ "CHARSET",
253
+ "COMPLEXITY",
254
+ "KEYBOARD_RUN",
255
+ "SEQUENCE",
256
+ "REPEAT",
257
+ "CONTAINS_EMP_NO"
258
+ ]
259
+ },
260
+ {
261
+ "id": "keyboard-row-digits",
262
+ "password": "Xk9-1234-Pmzj",
263
+ "empNo": "",
264
+ "wantRules": [
265
+ "KEYBOARD_RUN",
266
+ "SEQUENCE"
267
+ ]
268
+ },
269
+ {
270
+ "id": "keyboard-row-qwer",
271
+ "password": "Zq-qwer-Mj81",
272
+ "empNo": "",
273
+ "wantRules": [
274
+ "KEYBOARD_RUN"
275
+ ]
276
+ },
277
+ {
278
+ "id": "keyboard-row-asdf",
279
+ "password": "Zq-Asdf-Mj81",
280
+ "empNo": "",
281
+ "wantRules": [
282
+ "KEYBOARD_RUN"
283
+ ]
284
+ },
285
+ {
286
+ "id": "keyboard-row-bottom",
287
+ "password": "Zq-xcvb-Mj81",
288
+ "empNo": "",
289
+ "wantRules": [
290
+ "KEYBOARD_RUN"
291
+ ]
292
+ },
293
+ {
294
+ "id": "keyboard-reverse",
295
+ "password": "Zq-rewq-Mj81",
296
+ "empNo": "",
297
+ "wantRules": [
298
+ "KEYBOARD_RUN"
299
+ ]
300
+ },
301
+ {
302
+ "id": "keyboard-reverse-digits",
303
+ "password": "Zq-4321-Mj8P",
304
+ "empNo": "",
305
+ "wantRules": [
306
+ "KEYBOARD_RUN",
307
+ "SEQUENCE"
308
+ ]
309
+ },
310
+ {
311
+ "id": "keyboard-case-insensitive",
312
+ "password": "Zq-qWeR-Mj81",
313
+ "empNo": "",
314
+ "wantRules": [
315
+ "KEYBOARD_RUN"
316
+ ]
317
+ },
318
+ {
319
+ "id": "keyboard-shifted-symbols",
320
+ "password": "Zq-!@#$-Mjkp",
321
+ "empNo": "",
322
+ "wantRules": [
323
+ "KEYBOARD_RUN"
324
+ ]
325
+ },
326
+ {
327
+ "id": "keyboard-mixed-shift-and-plain",
328
+ "password": "Zq-1@3$-Mjkp",
329
+ "empNo": "",
330
+ "wantRules": [
331
+ "KEYBOARD_RUN"
332
+ ]
333
+ },
334
+ {
335
+ "id": "keyboard-longer-run-reported-once",
336
+ "password": "Zq-qwertyu-M1",
337
+ "empNo": "",
338
+ "wantRules": [
339
+ "KEYBOARD_RUN"
340
+ ]
341
+ },
342
+ {
343
+ "id": "keyboard-run-at-start",
344
+ "password": "qwerZk-Mj81xP",
345
+ "empNo": "",
346
+ "wantRules": [
347
+ "KEYBOARD_RUN"
348
+ ]
349
+ },
350
+ {
351
+ "id": "keyboard-run-at-end",
352
+ "password": "Zk-Mj81xP-poiu",
353
+ "empNo": "",
354
+ "wantRules": [
355
+ "KEYBOARD_RUN"
356
+ ]
357
+ },
358
+ {
359
+ "id": "keyboard-punctuation-row",
360
+ "password": "Zk-Mj81xP;lkj",
361
+ "empNo": "",
362
+ "wantRules": [
363
+ "KEYBOARD_RUN"
364
+ ]
365
+ },
366
+ {
367
+ "id": "keyboard-three-keys-ok",
368
+ "password": "Zq-qwe-Mj81xP",
369
+ "empNo": "",
370
+ "wantRules": []
371
+ },
372
+ {
373
+ "id": "keyboard-row-end-does-not-wrap-to-next-row",
374
+ "password": "Zq-90-qw-Mj81",
375
+ "empNo": "",
376
+ "wantRules": []
377
+ },
378
+ {
379
+ "id": "keyboard-rows-do-not-join",
380
+ "password": "Zq-pasdMj81xP",
381
+ "empNo": "",
382
+ "wantRules": []
383
+ },
384
+ {
385
+ "id": "keyboard-direction-change-ok",
386
+ "password": "Zq-qwewq-Mj81",
387
+ "empNo": "",
388
+ "wantRules": []
389
+ },
390
+ {
391
+ "id": "keyboard-gap-ok",
392
+ "password": "Zq-qwdf-Mj81xP",
393
+ "empNo": "",
394
+ "wantRules": []
395
+ },
396
+ {
397
+ "id": "keyboard-non-row-glyph-breaks-run",
398
+ "password": "Zq-qw~er-Mj81",
399
+ "empNo": "",
400
+ "wantRules": []
401
+ },
402
+ {
403
+ "id": "keyboard-repeat-is-not-a-run",
404
+ "password": "Zq-qqqq-Mj81xP",
405
+ "empNo": "",
406
+ "wantRules": [
407
+ "REPEAT"
408
+ ]
409
+ },
410
+ {
411
+ "id": "keyboard-and-complexity",
412
+ "password": "qwertyuiopas",
413
+ "empNo": "",
414
+ "wantRules": [
415
+ "COMPLEXITY",
416
+ "KEYBOARD_RUN"
417
+ ]
418
+ },
419
+ {
420
+ "id": "keyboard-before-repeat-in-table-order",
421
+ "password": "Zq-1234-aaa-Mj",
422
+ "empNo": "",
423
+ "wantRules": [
424
+ "KEYBOARD_RUN",
425
+ "SEQUENCE",
426
+ "REPEAT"
427
+ ]
428
+ },
429
+ {
430
+ "id": "sequence-alpha-ascending",
431
+ "password": "Zq-abcd-M81x",
432
+ "empNo": "",
433
+ "wantRules": [
434
+ "SEQUENCE"
435
+ ]
436
+ },
437
+ {
438
+ "id": "sequence-alpha-descending",
439
+ "password": "Zq-dcba-M81x",
440
+ "empNo": "",
441
+ "wantRules": [
442
+ "SEQUENCE"
443
+ ]
444
+ },
445
+ {
446
+ "id": "sequence-case-insensitive",
447
+ "password": "Zq-aBcD-M81x",
448
+ "empNo": "",
449
+ "wantRules": [
450
+ "SEQUENCE"
451
+ ]
452
+ },
453
+ {
454
+ "id": "sequence-digits-not-a-keyboard-run",
455
+ "password": "Zq-0123-Mkpx",
456
+ "empNo": "",
457
+ "wantRules": [
458
+ "SEQUENCE"
459
+ ]
460
+ },
461
+ {
462
+ "id": "sequence-digits-descending-not-a-keyboard-run",
463
+ "password": "Zq-3210-Mkpx",
464
+ "empNo": "",
465
+ "wantRules": [
466
+ "SEQUENCE"
467
+ ]
468
+ },
469
+ {
470
+ "id": "sequence-longer-run-reported-once",
471
+ "password": "Zq-abcdefg-M1",
472
+ "empNo": "",
473
+ "wantRules": [
474
+ "SEQUENCE"
475
+ ]
476
+ },
477
+ {
478
+ "id": "sequence-three-ok",
479
+ "password": "Zq-abc-M81xPk",
480
+ "empNo": "",
481
+ "wantRules": []
482
+ },
483
+ {
484
+ "id": "sequence-alpha-does-not-wrap",
485
+ "password": "Zq-xyza-M81p",
486
+ "empNo": "",
487
+ "wantRules": []
488
+ },
489
+ {
490
+ "id": "sequence-digits-do-not-wrap",
491
+ "password": "Zq-9012-Mkpx",
492
+ "empNo": "",
493
+ "wantRules": []
494
+ },
495
+ {
496
+ "id": "sequence-direction-change-ok",
497
+ "password": "Zq-abcba-M81x",
498
+ "empNo": "",
499
+ "wantRules": []
500
+ },
501
+ {
502
+ "id": "sequence-repeat-breaks-run",
503
+ "password": "Zq-abbc-M81xP",
504
+ "empNo": "",
505
+ "wantRules": []
506
+ },
507
+ {
508
+ "id": "sequence-letters-and-digits-do-not-join",
509
+ "password": "Zq-89ab-Mkpx",
510
+ "empNo": "",
511
+ "wantRules": []
512
+ },
513
+ {
514
+ "id": "sequence-ascii-neighbour-does-not-join",
515
+ "password": "Zq-xyz{-Mk81",
516
+ "empNo": "",
517
+ "wantRules": []
518
+ },
519
+ {
520
+ "id": "common-exact",
521
+ "password": "password",
522
+ "empNo": "",
523
+ "wantRules": [
524
+ "LENGTH",
525
+ "COMPLEXITY",
526
+ "COMMON_PASSWORD"
527
+ ]
528
+ },
529
+ {
530
+ "id": "common-case-insensitive",
531
+ "password": "Films+Pic+Galeries",
532
+ "empNo": "",
533
+ "wantRules": [
534
+ "COMMON_PASSWORD"
535
+ ]
536
+ },
537
+ {
538
+ "id": "common-lower-case-entry",
539
+ "password": "films+pic+galeries",
540
+ "empNo": "",
541
+ "wantRules": [
542
+ "COMPLEXITY",
543
+ "COMMON_PASSWORD"
544
+ ]
545
+ },
546
+ {
547
+ "id": "common-upper-case",
548
+ "password": "PASSWORD",
549
+ "empNo": "",
550
+ "wantRules": [
551
+ "LENGTH",
552
+ "COMPLEXITY",
553
+ "COMMON_PASSWORD"
554
+ ]
555
+ },
556
+ {
557
+ "id": "common-not-trimmed",
558
+ "password": " Films+Pic+Galeries",
559
+ "empNo": "",
560
+ "wantRules": []
561
+ },
562
+ {
563
+ "id": "common-entry-inside-longer-password-ok",
564
+ "password": "Films+Pic+Galeries1",
565
+ "empNo": "",
566
+ "wantRules": []
567
+ },
568
+ {
569
+ "id": "table-order-common-and-runs",
570
+ "password": "123456",
571
+ "empNo": "123456",
572
+ "wantRules": [
573
+ "LENGTH",
574
+ "COMPLEXITY",
575
+ "COMMON_PASSWORD",
576
+ "KEYBOARD_RUN",
577
+ "SEQUENCE",
578
+ "CONTAINS_EMP_NO"
579
+ ]
580
+ }
581
+ ]
582
+ }
@@ -68,6 +68,77 @@
68
68
  "admin": { "status": 200 }
69
69
  }
70
70
  },
71
+ {
72
+ "id": "create-idea",
73
+ "operation": "create Idea",
74
+ "method": "POST",
75
+ "path": "/api/v1/ideas",
76
+ "multipart": {
77
+ "fields": { "category": "COOL_IDEA", "org": "BSID", "direction": "TALENT_DEVELOPMENT", "name": "Matrix Idea", "summary": "Matrix summary" },
78
+ "file": { "field": "slides", "filename": "I_BSID_${actorName}_${actorEmpNo}_Matrix Idea.pdf", "content": "pdf" }
79
+ },
80
+ "state": "OPEN",
81
+ "expect": {
82
+ "visitor": { "status": 401, "code": "UNAUTHENTICATED" },
83
+ "user": { "status": 201 },
84
+ "admin": { "status": 403, "code": "FORBIDDEN" }
85
+ }
86
+ },
87
+ {
88
+ "id": "create-idea-not-open",
89
+ "operation": "create Idea before the contest opens",
90
+ "method": "POST",
91
+ "path": "/api/v1/ideas",
92
+ "multipart": {
93
+ "fields": { "category": "COOL_IDEA", "org": "BSID", "direction": "TALENT_DEVELOPMENT", "name": "Matrix Idea", "summary": "Matrix summary" },
94
+ "file": { "field": "slides", "filename": "I_BSID_${actorName}_${actorEmpNo}_Matrix Idea.pdf", "content": "pdf" }
95
+ },
96
+ "state": "NOT_OPEN",
97
+ "expect": {
98
+ "visitor": { "status": 401, "code": "UNAUTHENTICATED" },
99
+ "user": { "status": 403, "code": "SUBMISSION_NOT_OPEN" },
100
+ "admin": { "status": 403, "code": "FORBIDDEN" }
101
+ }
102
+ },
103
+ {
104
+ "id": "create-idea-closed",
105
+ "operation": "create Idea after the deadline",
106
+ "method": "POST",
107
+ "path": "/api/v1/ideas",
108
+ "multipart": {
109
+ "fields": { "category": "COOL_IDEA", "org": "BSID", "direction": "TALENT_DEVELOPMENT", "name": "Matrix Idea", "summary": "Matrix summary" },
110
+ "file": { "field": "slides", "filename": "I_BSID_${actorName}_${actorEmpNo}_Matrix Idea.pdf", "content": "pdf" }
111
+ },
112
+ "state": "CLOSED",
113
+ "expect": {
114
+ "visitor": { "status": 401, "code": "UNAUTHENTICATED" },
115
+ "user": { "status": 403, "code": "SUBMISSION_CLOSED" },
116
+ "admin": { "status": 403, "code": "FORBIDDEN" }
117
+ }
118
+ },
119
+ {
120
+ "id": "download-slides-missing",
121
+ "operation": "download slides of a missing Idea (uniform 404; cells for an existing Idea are in internal/idea/download_integration_test.go)",
122
+ "method": "GET",
123
+ "path": "/api/v1/ideas/00000000-0000-4000-8000-000000000000/slides",
124
+ "expect": {
125
+ "visitor": { "status": 401, "code": "UNAUTHENTICATED" },
126
+ "user": { "status": 404, "code": "NOT_FOUND" },
127
+ "admin": { "status": 404, "code": "NOT_FOUND" }
128
+ }
129
+ },
130
+ {
131
+ "id": "download-slides-head",
132
+ "operation": "HEAD on the slide download: 405, never the GET handler (D47)",
133
+ "method": "HEAD",
134
+ "path": "/api/v1/ideas/00000000-0000-4000-8000-000000000000/slides",
135
+ "outsideSpec": true,
136
+ "expect": {
137
+ "visitor": { "status": 401, "code": "UNAUTHENTICATED" },
138
+ "user": { "status": 405, "code": "METHOD_NOT_ALLOWED" },
139
+ "admin": { "status": 405, "code": "METHOD_NOT_ALLOWED" }
140
+ }
141
+ },
71
142
  {
72
143
  "id": "order-logout-no-csrf",
73
144
  "operation": "check order: a write without a CSRF token",
@@ -0,0 +1,32 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "expected-filename.schema.json",
4
+ "title": "Expected filename cases",
5
+ "description": "Shared front-end/back-end cases for PRD 6.8 (filename base) and PRD 6.2 (255 UTF-8 byte limit measured with .pptx).",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["cases"],
9
+ "properties": {
10
+ "description": { "type": "string" },
11
+ "cases": {
12
+ "type": "array",
13
+ "minItems": 1,
14
+ "items": {
15
+ "type": "object",
16
+ "additionalProperties": false,
17
+ "required": ["id", "category", "org", "leaderName", "leaderEmpNo", "ideaName", "wantBase", "wantBytes", "wantTooLong"],
18
+ "properties": {
19
+ "id": { "type": "string", "minLength": 1 },
20
+ "category": { "enum": ["COOL_IDEA", "OUTSTANDING_IMPACT"] },
21
+ "org": { "enum": ["AAID", "BSID", "ICSD", "TSID", "PLED", "DEID"] },
22
+ "leaderName": { "type": "string" },
23
+ "leaderEmpNo": { "type": "string" },
24
+ "ideaName": { "type": "string" },
25
+ "wantBase": { "type": "string" },
26
+ "wantBytes": { "type": "integer", "minimum": 1 },
27
+ "wantTooLong": { "type": "boolean" }
28
+ }
29
+ }
30
+ }
31
+ }
32
+ }