diff options
-rw-r--r-- | server/middlewares/validators/videos/videos.ts | 6 | ||||
-rw-r--r-- | server/tests/api/users/users.ts | 18 | ||||
-rw-r--r-- | shared/models/server/server-error-code.enum.ts | 54 | ||||
-rw-r--r-- | support/doc/api/openapi.yaml | 38 |
4 files changed, 93 insertions, 23 deletions
diff --git a/server/middlewares/validators/videos/videos.ts b/server/middlewares/validators/videos/videos.ts index fd0e543f1..64e09234e 100644 --- a/server/middlewares/validators/videos/videos.ts +++ b/server/middlewares/validators/videos/videos.ts | |||
@@ -252,7 +252,7 @@ async function checkVideoFollowConstraints (req: express.Request, res: express.R | |||
252 | 252 | ||
253 | return res.fail({ | 253 | return res.fail({ |
254 | status: HttpStatusCode.FORBIDDEN_403, | 254 | status: HttpStatusCode.FORBIDDEN_403, |
255 | message: 'Cannot get this video regarding follow constraints.', | 255 | message: 'Cannot get this video regarding follow constraints', |
256 | type: ServerErrorCode.DOES_NOT_RESPECT_FOLLOW_CONSTRAINTS, | 256 | type: ServerErrorCode.DOES_NOT_RESPECT_FOLLOW_CONSTRAINTS, |
257 | data: { | 257 | data: { |
258 | originUrl: video.url | 258 | originUrl: video.url |
@@ -285,11 +285,11 @@ const videosCustomGetValidator = ( | |||
285 | 285 | ||
286 | const user = res.locals.oauth ? res.locals.oauth.token.User : null | 286 | const user = res.locals.oauth ? res.locals.oauth.token.User : null |
287 | 287 | ||
288 | // Only the owner or a user that have blacklist rights can see the video | 288 | // Only the owner or a user that have blocklist rights can see the video |
289 | if (!user || !user.canGetVideo(video)) { | 289 | if (!user || !user.canGetVideo(video)) { |
290 | return res.fail({ | 290 | return res.fail({ |
291 | status: HttpStatusCode.FORBIDDEN_403, | 291 | status: HttpStatusCode.FORBIDDEN_403, |
292 | message: 'Cannot get this private/internal or blacklisted video.' | 292 | message: 'Cannot get this private/internal or blocklisted video' |
293 | }) | 293 | }) |
294 | } | 294 | } |
295 | 295 | ||
diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts index 464c11d34..87ba775f6 100644 --- a/server/tests/api/users/users.ts +++ b/server/tests/api/users/users.ts | |||
@@ -3,7 +3,7 @@ | |||
3 | import 'mocha' | 3 | import 'mocha' |
4 | import * as chai from 'chai' | 4 | import * as chai from 'chai' |
5 | import { AbuseState, AbuseUpdate, MyUser, User, UserRole, Video, VideoPlaylistType } from '@shared/models' | 5 | import { AbuseState, AbuseUpdate, MyUser, User, UserRole, Video, VideoPlaylistType } from '@shared/models' |
6 | import { CustomConfig } from '@shared/models/server' | 6 | import { CustomConfig, OAuth2ErrorCode } from '@shared/models/server' |
7 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | 7 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
8 | import { | 8 | import { |
9 | addVideoCommentThread, | 9 | addVideoCommentThread, |
@@ -93,20 +93,20 @@ describe('Test users', function () { | |||
93 | const client = { id: 'client', secret: server.client.secret } | 93 | const client = { id: 'client', secret: server.client.secret } |
94 | const res = await login(server.url, client, server.user, HttpStatusCode.BAD_REQUEST_400) | 94 | const res = await login(server.url, client, server.user, HttpStatusCode.BAD_REQUEST_400) |
95 | 95 | ||
96 | expect(res.body.code).to.equal('invalid_client') | 96 | expect(res.body.code).to.equal(OAuth2ErrorCode.INVALID_CLIENT) |
97 | expect(res.body.error).to.contain('client is invalid') | 97 | expect(res.body.error).to.contain('client is invalid') |
98 | expect(res.body.type.startsWith('https://')).to.be.true | 98 | expect(res.body.type.startsWith('https://')).to.be.true |
99 | expect(res.body.type).to.contain('invalid_client') | 99 | expect(res.body.type).to.contain(OAuth2ErrorCode.INVALID_CLIENT) |
100 | }) | 100 | }) |
101 | 101 | ||
102 | it('Should not login with an invalid client secret', async function () { | 102 | it('Should not login with an invalid client secret', async function () { |
103 | const client = { id: server.client.id, secret: 'coucou' } | 103 | const client = { id: server.client.id, secret: 'coucou' } |
104 | const res = await login(server.url, client, server.user, HttpStatusCode.BAD_REQUEST_400) | 104 | const res = await login(server.url, client, server.user, HttpStatusCode.BAD_REQUEST_400) |
105 | 105 | ||
106 | expect(res.body.code).to.equal('invalid_client') | 106 | expect(res.body.code).to.equal(OAuth2ErrorCode.INVALID_CLIENT) |
107 | expect(res.body.error).to.contain('client is invalid') | 107 | expect(res.body.error).to.contain('client is invalid') |
108 | expect(res.body.type.startsWith('https://')).to.be.true | 108 | expect(res.body.type.startsWith('https://')).to.be.true |
109 | expect(res.body.type).to.contain('invalid_client') | 109 | expect(res.body.type).to.contain(OAuth2ErrorCode.INVALID_CLIENT) |
110 | }) | 110 | }) |
111 | }) | 111 | }) |
112 | 112 | ||
@@ -116,20 +116,20 @@ describe('Test users', function () { | |||
116 | const user = { username: 'captain crochet', password: server.user.password } | 116 | const user = { username: 'captain crochet', password: server.user.password } |
117 | const res = await login(server.url, server.client, user, HttpStatusCode.BAD_REQUEST_400) | 117 | const res = await login(server.url, server.client, user, HttpStatusCode.BAD_REQUEST_400) |
118 | 118 | ||
119 | expect(res.body.code).to.equal('invalid_grant') | 119 | expect(res.body.code).to.equal(OAuth2ErrorCode.INVALID_GRANT) |
120 | expect(res.body.error).to.contain('credentials are invalid') | 120 | expect(res.body.error).to.contain('credentials are invalid') |
121 | expect(res.body.type.startsWith('https://')).to.be.true | 121 | expect(res.body.type.startsWith('https://')).to.be.true |
122 | expect(res.body.type).to.contain('invalid_grant') | 122 | expect(res.body.type).to.contain(OAuth2ErrorCode.INVALID_GRANT) |
123 | }) | 123 | }) |
124 | 124 | ||
125 | it('Should not login with an invalid password', async function () { | 125 | it('Should not login with an invalid password', async function () { |
126 | const user = { username: server.user.username, password: 'mew_three' } | 126 | const user = { username: server.user.username, password: 'mew_three' } |
127 | const res = await login(server.url, server.client, user, HttpStatusCode.BAD_REQUEST_400) | 127 | const res = await login(server.url, server.client, user, HttpStatusCode.BAD_REQUEST_400) |
128 | 128 | ||
129 | expect(res.body.code).to.equal('invalid_grant') | 129 | expect(res.body.code).to.equal(OAuth2ErrorCode.INVALID_GRANT) |
130 | expect(res.body.error).to.contain('credentials are invalid') | 130 | expect(res.body.error).to.contain('credentials are invalid') |
131 | expect(res.body.type.startsWith('https://')).to.be.true | 131 | expect(res.body.type.startsWith('https://')).to.be.true |
132 | expect(res.body.type).to.contain('invalid_grant') | 132 | expect(res.body.type).to.contain(OAuth2ErrorCode.INVALID_GRANT) |
133 | }) | 133 | }) |
134 | 134 | ||
135 | it('Should not be able to upload a video', async function () { | 135 | it('Should not be able to upload a video', async function () { |
diff --git a/shared/models/server/server-error-code.enum.ts b/shared/models/server/server-error-code.enum.ts index f9fe47830..93b9ce20d 100644 --- a/shared/models/server/server-error-code.enum.ts +++ b/shared/models/server/server-error-code.enum.ts | |||
@@ -1,6 +1,52 @@ | |||
1 | export const enum ServerErrorCode { | 1 | export const enum ServerErrorCode { |
2 | DOES_NOT_RESPECT_FOLLOW_CONSTRAINTS = 'DOES_NOT_RESPECT_FOLLOW_CONSTRAINTS', | 2 | /** |
3 | MAX_INSTANCE_LIVES_LIMIT_REACHED = 'MAX_INSTANCE_LIVES_LIMIT_REACHED', | 3 | * Error yielded upon trying to access a video that is not federated, nor can |
4 | MAX_USER_LIVES_LIMIT_REACHED = 'MAX_USER_LIVES_LIMIT_REACHED', | 4 | * be. This may be due to: remote videos on instances that are not followed by |
5 | INCORRECT_FILES_IN_TORRENT = 'INCORRECT_FILES_IN_TORRENT' | 5 | * yours, and with your instance disallowing unknown instances being accessed. |
6 | */ | ||
7 | DOES_NOT_RESPECT_FOLLOW_CONSTRAINTS = 'does_not_respect_follow_constraints', | ||
8 | |||
9 | /** | ||
10 | * Pretty self-explanatory: the set maximum number of simultaneous lives was | ||
11 | * reached, and this error is typically there to inform the user trying to | ||
12 | * broadcast one. | ||
13 | */ | ||
14 | MAX_INSTANCE_LIVES_LIMIT_REACHED = 'max_instance_lives_limit_reached', | ||
15 | |||
16 | /** | ||
17 | * Pretty self-explanatory: the set maximum number of simultaneous lives FOR | ||
18 | * THIS USER was reached, and this error is typically there to inform the user | ||
19 | * trying to broadcast one. | ||
20 | */ | ||
21 | MAX_USER_LIVES_LIMIT_REACHED = 'max_user_lives_limit_reached', | ||
22 | |||
23 | /** | ||
24 | * A torrent should have at most one correct video file. Any more and we will | ||
25 | * not be able to choose automatically. | ||
26 | */ | ||
27 | INCORRECT_FILES_IN_TORRENT = 'incorrect_files_in_torrent' | ||
28 | } | ||
29 | |||
30 | /** | ||
31 | * oauthjs/oauth2-server error codes | ||
32 | * @see https://datatracker.ietf.org/doc/html/rfc6749#section-5.2 | ||
33 | **/ | ||
34 | export const enum OAuth2ErrorCode { | ||
35 | /** | ||
36 | * The provided authorization grant (e.g., authorization code, resource owner | ||
37 | * credentials) or refresh token is invalid, expired, revoked, does not match | ||
38 | * the redirection URI used in the authorization request, or was issued to | ||
39 | * another client. | ||
40 | * | ||
41 | * @see https://github.com/oauthjs/node-oauth2-server/blob/master/lib/errors/invalid-grant-error.js | ||
42 | */ | ||
43 | INVALID_GRANT = 'invalid_grant', | ||
44 | |||
45 | /** | ||
46 | * Client authentication failed (e.g., unknown client, no client authentication | ||
47 | * included, or unsupported authentication method). | ||
48 | * | ||
49 | * @see https://github.com/oauthjs/node-oauth2-server/blob/master/lib/errors/invalid-client-error.js | ||
50 | */ | ||
51 | INVALID_CLIENT = 'invalid_client' | ||
6 | } | 52 | } |
diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml index 9f40d74c6..ae4855878 100644 --- a/support/doc/api/openapi.yaml +++ b/support/doc/api/openapi.yaml | |||
@@ -53,14 +53,30 @@ info: | |||
53 | } | 53 | } |
54 | ``` | 54 | ``` |
55 | 55 | ||
56 | We provide error types for [a growing number of cases](https://github.com/Chocobozzz/PeerTube/blob/develop/shared/models/server/server-error-code.enum.ts), | 56 | We provide error `type` values for [a growing number of cases](https://github.com/Chocobozzz/PeerTube/blob/develop/shared/models/server/server-error-code.enum.ts), |
57 | but it is still optional. | 57 | but it is still optional. Types are used to disambiguate errors that bear the same status code |
58 | and are non-obvious: | ||
59 | |||
60 | ``` | ||
61 | HTTP 1.1 403 Forbidden | ||
62 | Content-Type: application/problem+json; charset=utf-8 | ||
63 | |||
64 | { | ||
65 | "detail": "Cannot get this video regarding follow constraints", | ||
66 | "docs": "https://docs.joinpeertube.org/api-rest-reference.html#operation/getVideo", | ||
67 | "status": 403, | ||
68 | "title": "Forbidden", | ||
69 | "type": "https://docs.joinpeertube.org/api-rest-reference.html#section/Errors/does_not_respect_follow_constraints" | ||
70 | } | ||
71 | ``` | ||
72 | |||
73 | Here a 403 error could otherwise mean that the video is private or blocklisted. | ||
58 | 74 | ||
59 | ### Validation errors | 75 | ### Validation errors |
60 | 76 | ||
61 | Each parameter is evaluated on its own against a set of rules before the route validator | 77 | Each parameter is evaluated on its own against a set of rules before the route validator |
62 | proceeds with potential testing involving parameter combinations. Errors coming from validation | 78 | proceeds with potential testing involving parameter combinations. Errors coming from validation |
63 | errors appear earlier and benefit from a more detailed error type: | 79 | errors appear earlier and benefit from a more detailed error description: |
64 | 80 | ||
65 | ``` | 81 | ``` |
66 | HTTP 1.1 400 Bad Request | 82 | HTTP 1.1 400 Bad Request |
@@ -89,6 +105,12 @@ info: | |||
89 | `invalid-params.<field>.value` reports the value that didn't pass validation whose `invalid-params.<field>.msg` | 105 | `invalid-params.<field>.value` reports the value that didn't pass validation whose `invalid-params.<field>.msg` |
90 | is about. | 106 | is about. |
91 | 107 | ||
108 | ### Deprecated error fields | ||
109 | |||
110 | Some fields could be included with previous versions. They are still included but their use is deprecated: | ||
111 | - `error`: superseded by `detail` | ||
112 | - `code`: superseded by `type` (which is now an URI) | ||
113 | |||
92 | # Rate limits | 114 | # Rate limits |
93 | 115 | ||
94 | We are rate-limiting all endpoints of PeerTube's API. Custom values can be set by administrators: | 116 | We are rate-limiting all endpoints of PeerTube's API. Custom values can be set by administrators: |
@@ -932,6 +954,12 @@ paths: | |||
932 | type: integer | 954 | type: integer |
933 | minimum: 0 | 955 | minimum: 0 |
934 | example: 1209600 | 956 | example: 1209600 |
957 | '400': | ||
958 | x-summary: client or credentials are invalid | ||
959 | description: | | ||
960 | Disambiguate via `type`: | ||
961 | - `invalid_client` for an unmatched `client_id` | ||
962 | - `invalid_grant` for unmatched credentials | ||
935 | x-codeSamples: | 963 | x-codeSamples: |
936 | - lang: Shell | 964 | - lang: Shell |
937 | source: | | 965 | source: | |
@@ -1812,8 +1840,6 @@ paths: | |||
1812 | application/json: | 1840 | application/json: |
1813 | schema: | 1841 | schema: |
1814 | $ref: '#/components/schemas/VideoUploadResponse' | 1842 | $ref: '#/components/schemas/VideoUploadResponse' |
1815 | '400': | ||
1816 | description: invalid file field, schedule date or parameter | ||
1817 | '403': | 1843 | '403': |
1818 | description: video didn't pass upload filter | 1844 | description: video didn't pass upload filter |
1819 | '408': | 1845 | '408': |
@@ -1918,8 +1944,6 @@ paths: | |||
1918 | schema: | 1944 | schema: |
1919 | type: number | 1945 | type: number |
1920 | example: 0 | 1946 | example: 0 |
1921 | '400': | ||
1922 | description: invalid file field, schedule date or parameter | ||
1923 | '413': | 1947 | '413': |
1924 | description: video file too large, due to quota, absolute max file size or concurrent partial upload limit | 1948 | description: video file too large, due to quota, absolute max file size or concurrent partial upload limit |
1925 | '415': | 1949 | '415': |