diff options
Diffstat (limited to 'server/tests/api')
-rw-r--r-- | server/tests/api/users/index.ts | 1 | ||||
-rw-r--r-- | server/tests/api/users/oauth.ts | 192 | ||||
-rw-r--r-- | server/tests/api/users/users.ts | 184 |
3 files changed, 199 insertions, 178 deletions
diff --git a/server/tests/api/users/index.ts b/server/tests/api/users/index.ts index 643f1a531..0313845ef 100644 --- a/server/tests/api/users/index.ts +++ b/server/tests/api/users/index.ts | |||
@@ -1,3 +1,4 @@ | |||
1 | import './oauth' | ||
1 | import './two-factor' | 2 | import './two-factor' |
2 | import './user-subscriptions' | 3 | import './user-subscriptions' |
3 | import './user-videos' | 4 | import './user-videos' |
diff --git a/server/tests/api/users/oauth.ts b/server/tests/api/users/oauth.ts new file mode 100644 index 000000000..6a3da5ea2 --- /dev/null +++ b/server/tests/api/users/oauth.ts | |||
@@ -0,0 +1,192 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | ||
2 | |||
3 | import { expect } from 'chai' | ||
4 | import { wait } from '@shared/core-utils' | ||
5 | import { HttpStatusCode, OAuth2ErrorCode, PeerTubeProblemDocument } from '@shared/models' | ||
6 | import { cleanupTests, createSingleServer, killallServers, PeerTubeServer, setAccessTokensToServers } from '@shared/server-commands' | ||
7 | |||
8 | describe('Test oauth', function () { | ||
9 | let server: PeerTubeServer | ||
10 | |||
11 | before(async function () { | ||
12 | this.timeout(30000) | ||
13 | |||
14 | server = await createSingleServer(1, { | ||
15 | rates_limit: { | ||
16 | login: { | ||
17 | max: 30 | ||
18 | } | ||
19 | } | ||
20 | }) | ||
21 | |||
22 | await setAccessTokensToServers([ server ]) | ||
23 | }) | ||
24 | |||
25 | describe('OAuth client', function () { | ||
26 | |||
27 | function expectInvalidClient (body: PeerTubeProblemDocument) { | ||
28 | expect(body.code).to.equal(OAuth2ErrorCode.INVALID_CLIENT) | ||
29 | expect(body.error).to.contain('client is invalid') | ||
30 | expect(body.type.startsWith('https://')).to.be.true | ||
31 | expect(body.type).to.contain(OAuth2ErrorCode.INVALID_CLIENT) | ||
32 | } | ||
33 | |||
34 | it('Should create a new client') | ||
35 | |||
36 | it('Should return the first client') | ||
37 | |||
38 | it('Should remove the last client') | ||
39 | |||
40 | it('Should not login with an invalid client id', async function () { | ||
41 | const client = { id: 'client', secret: server.store.client.secret } | ||
42 | const body = await server.login.login({ client, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | ||
43 | |||
44 | expectInvalidClient(body) | ||
45 | }) | ||
46 | |||
47 | it('Should not login with an invalid client secret', async function () { | ||
48 | const client = { id: server.store.client.id, secret: 'coucou' } | ||
49 | const body = await server.login.login({ client, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | ||
50 | |||
51 | expectInvalidClient(body) | ||
52 | }) | ||
53 | }) | ||
54 | |||
55 | describe('Login', function () { | ||
56 | |||
57 | function expectInvalidCredentials (body: PeerTubeProblemDocument) { | ||
58 | expect(body.code).to.equal(OAuth2ErrorCode.INVALID_GRANT) | ||
59 | expect(body.error).to.contain('credentials are invalid') | ||
60 | expect(body.type.startsWith('https://')).to.be.true | ||
61 | expect(body.type).to.contain(OAuth2ErrorCode.INVALID_GRANT) | ||
62 | } | ||
63 | |||
64 | it('Should not login with an invalid username', async function () { | ||
65 | const user = { username: 'captain crochet', password: server.store.user.password } | ||
66 | const body = await server.login.login({ user, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | ||
67 | |||
68 | expectInvalidCredentials(body) | ||
69 | }) | ||
70 | |||
71 | it('Should not login with an invalid password', async function () { | ||
72 | const user = { username: server.store.user.username, password: 'mew_three' } | ||
73 | const body = await server.login.login({ user, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | ||
74 | |||
75 | expectInvalidCredentials(body) | ||
76 | }) | ||
77 | |||
78 | it('Should be able to login', async function () { | ||
79 | await server.login.login({ expectedStatus: HttpStatusCode.OK_200 }) | ||
80 | }) | ||
81 | |||
82 | it('Should be able to login with an insensitive username', async function () { | ||
83 | const user = { username: 'RoOt', password: server.store.user.password } | ||
84 | await server.login.login({ user, expectedStatus: HttpStatusCode.OK_200 }) | ||
85 | |||
86 | const user2 = { username: 'rOoT', password: server.store.user.password } | ||
87 | await server.login.login({ user: user2, expectedStatus: HttpStatusCode.OK_200 }) | ||
88 | |||
89 | const user3 = { username: 'ROOt', password: server.store.user.password } | ||
90 | await server.login.login({ user: user3, expectedStatus: HttpStatusCode.OK_200 }) | ||
91 | }) | ||
92 | }) | ||
93 | |||
94 | describe('Logout', function () { | ||
95 | |||
96 | it('Should logout (revoke token)', async function () { | ||
97 | await server.login.logout({ token: server.accessToken }) | ||
98 | }) | ||
99 | |||
100 | it('Should not be able to get the user information', async function () { | ||
101 | await server.users.getMyInfo({ expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) | ||
102 | }) | ||
103 | |||
104 | it('Should not be able to upload a video', async function () { | ||
105 | await server.videos.upload({ attributes: { name: 'video' }, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) | ||
106 | }) | ||
107 | |||
108 | it('Should be able to login again', async function () { | ||
109 | const body = await server.login.login() | ||
110 | server.accessToken = body.access_token | ||
111 | server.refreshToken = body.refresh_token | ||
112 | }) | ||
113 | |||
114 | it('Should be able to get my user information again', async function () { | ||
115 | await server.users.getMyInfo() | ||
116 | }) | ||
117 | |||
118 | it('Should have an expired access token', async function () { | ||
119 | this.timeout(60000) | ||
120 | |||
121 | await server.sql.setTokenField(server.accessToken, 'accessTokenExpiresAt', new Date().toISOString()) | ||
122 | await server.sql.setTokenField(server.accessToken, 'refreshTokenExpiresAt', new Date().toISOString()) | ||
123 | |||
124 | await killallServers([ server ]) | ||
125 | await server.run() | ||
126 | |||
127 | await server.users.getMyInfo({ expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) | ||
128 | }) | ||
129 | |||
130 | it('Should not be able to refresh an access token with an expired refresh token', async function () { | ||
131 | await server.login.refreshToken({ refreshToken: server.refreshToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | ||
132 | }) | ||
133 | |||
134 | it('Should refresh the token', async function () { | ||
135 | this.timeout(50000) | ||
136 | |||
137 | const futureDate = new Date(new Date().getTime() + 1000 * 60).toISOString() | ||
138 | await server.sql.setTokenField(server.accessToken, 'refreshTokenExpiresAt', futureDate) | ||
139 | |||
140 | await killallServers([ server ]) | ||
141 | await server.run() | ||
142 | |||
143 | const res = await server.login.refreshToken({ refreshToken: server.refreshToken }) | ||
144 | server.accessToken = res.body.access_token | ||
145 | server.refreshToken = res.body.refresh_token | ||
146 | }) | ||
147 | |||
148 | it('Should be able to get my user information again', async function () { | ||
149 | await server.users.getMyInfo() | ||
150 | }) | ||
151 | }) | ||
152 | |||
153 | describe('Custom token lifetime', function () { | ||
154 | before(async function () { | ||
155 | this.timeout(120_000) | ||
156 | |||
157 | await server.kill() | ||
158 | await server.run({ | ||
159 | oauth2: { | ||
160 | token_lifetime: { | ||
161 | access_token: '2 seconds', | ||
162 | refresh_token: '2 seconds' | ||
163 | } | ||
164 | } | ||
165 | }) | ||
166 | }) | ||
167 | |||
168 | it('Should have a very short access token lifetime', async function () { | ||
169 | this.timeout(50000) | ||
170 | |||
171 | const { access_token: accessToken } = await server.login.login() | ||
172 | await server.users.getMyInfo({ token: accessToken }) | ||
173 | |||
174 | await wait(3000) | ||
175 | await server.users.getMyInfo({ token: accessToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) | ||
176 | }) | ||
177 | |||
178 | it('Should have a very short refresh token lifetime', async function () { | ||
179 | this.timeout(50000) | ||
180 | |||
181 | const { refresh_token: refreshToken } = await server.login.login() | ||
182 | await server.login.refreshToken({ refreshToken }) | ||
183 | |||
184 | await wait(3000) | ||
185 | await server.login.refreshToken({ refreshToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | ||
186 | }) | ||
187 | }) | ||
188 | |||
189 | after(async function () { | ||
190 | await cleanupTests([ server ]) | ||
191 | }) | ||
192 | }) | ||
diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts index 421b3ce16..93e2e489a 100644 --- a/server/tests/api/users/users.ts +++ b/server/tests/api/users/users.ts | |||
@@ -2,15 +2,8 @@ | |||
2 | 2 | ||
3 | import { expect } from 'chai' | 3 | import { expect } from 'chai' |
4 | import { testImage } from '@server/tests/shared' | 4 | import { testImage } from '@server/tests/shared' |
5 | import { AbuseState, HttpStatusCode, OAuth2ErrorCode, UserAdminFlag, UserRole, VideoPlaylistType } from '@shared/models' | 5 | import { AbuseState, HttpStatusCode, UserAdminFlag, UserRole, VideoPlaylistType } from '@shared/models' |
6 | import { | 6 | import { cleanupTests, createSingleServer, PeerTubeServer, setAccessTokensToServers } from '@shared/server-commands' |
7 | cleanupTests, | ||
8 | createSingleServer, | ||
9 | killallServers, | ||
10 | makePutBodyRequest, | ||
11 | PeerTubeServer, | ||
12 | setAccessTokensToServers | ||
13 | } from '@shared/server-commands' | ||
14 | 7 | ||
15 | describe('Test users', function () { | 8 | describe('Test users', function () { |
16 | let server: PeerTubeServer | 9 | let server: PeerTubeServer |
@@ -39,166 +32,6 @@ describe('Test users', function () { | |||
39 | await server.plugins.install({ npmName: 'peertube-theme-background-red' }) | 32 | await server.plugins.install({ npmName: 'peertube-theme-background-red' }) |
40 | }) | 33 | }) |
41 | 34 | ||
42 | describe('OAuth client', function () { | ||
43 | it('Should create a new client') | ||
44 | |||
45 | it('Should return the first client') | ||
46 | |||
47 | it('Should remove the last client') | ||
48 | |||
49 | it('Should not login with an invalid client id', async function () { | ||
50 | const client = { id: 'client', secret: server.store.client.secret } | ||
51 | const body = await server.login.login({ client, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | ||
52 | |||
53 | expect(body.code).to.equal(OAuth2ErrorCode.INVALID_CLIENT) | ||
54 | expect(body.error).to.contain('client is invalid') | ||
55 | expect(body.type.startsWith('https://')).to.be.true | ||
56 | expect(body.type).to.contain(OAuth2ErrorCode.INVALID_CLIENT) | ||
57 | }) | ||
58 | |||
59 | it('Should not login with an invalid client secret', async function () { | ||
60 | const client = { id: server.store.client.id, secret: 'coucou' } | ||
61 | const body = await server.login.login({ client, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | ||
62 | |||
63 | expect(body.code).to.equal(OAuth2ErrorCode.INVALID_CLIENT) | ||
64 | expect(body.error).to.contain('client is invalid') | ||
65 | expect(body.type.startsWith('https://')).to.be.true | ||
66 | expect(body.type).to.contain(OAuth2ErrorCode.INVALID_CLIENT) | ||
67 | }) | ||
68 | }) | ||
69 | |||
70 | describe('Login', function () { | ||
71 | |||
72 | it('Should not login with an invalid username', async function () { | ||
73 | const user = { username: 'captain crochet', password: server.store.user.password } | ||
74 | const body = await server.login.login({ user, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | ||
75 | |||
76 | expect(body.code).to.equal(OAuth2ErrorCode.INVALID_GRANT) | ||
77 | expect(body.error).to.contain('credentials are invalid') | ||
78 | expect(body.type.startsWith('https://')).to.be.true | ||
79 | expect(body.type).to.contain(OAuth2ErrorCode.INVALID_GRANT) | ||
80 | }) | ||
81 | |||
82 | it('Should not login with an invalid password', async function () { | ||
83 | const user = { username: server.store.user.username, password: 'mew_three' } | ||
84 | const body = await server.login.login({ user, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | ||
85 | |||
86 | expect(body.code).to.equal(OAuth2ErrorCode.INVALID_GRANT) | ||
87 | expect(body.error).to.contain('credentials are invalid') | ||
88 | expect(body.type.startsWith('https://')).to.be.true | ||
89 | expect(body.type).to.contain(OAuth2ErrorCode.INVALID_GRANT) | ||
90 | }) | ||
91 | |||
92 | it('Should not be able to upload a video', async function () { | ||
93 | token = 'my_super_token' | ||
94 | |||
95 | await server.videos.upload({ token, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) | ||
96 | }) | ||
97 | |||
98 | it('Should not be able to follow', async function () { | ||
99 | token = 'my_super_token' | ||
100 | |||
101 | await server.follows.follow({ | ||
102 | hosts: [ 'http://example.com' ], | ||
103 | token, | ||
104 | expectedStatus: HttpStatusCode.UNAUTHORIZED_401 | ||
105 | }) | ||
106 | }) | ||
107 | |||
108 | it('Should not be able to unfollow') | ||
109 | |||
110 | it('Should be able to login', async function () { | ||
111 | const body = await server.login.login({ expectedStatus: HttpStatusCode.OK_200 }) | ||
112 | |||
113 | token = body.access_token | ||
114 | }) | ||
115 | |||
116 | it('Should be able to login with an insensitive username', async function () { | ||
117 | const user = { username: 'RoOt', password: server.store.user.password } | ||
118 | await server.login.login({ user, expectedStatus: HttpStatusCode.OK_200 }) | ||
119 | |||
120 | const user2 = { username: 'rOoT', password: server.store.user.password } | ||
121 | await server.login.login({ user: user2, expectedStatus: HttpStatusCode.OK_200 }) | ||
122 | |||
123 | const user3 = { username: 'ROOt', password: server.store.user.password } | ||
124 | await server.login.login({ user: user3, expectedStatus: HttpStatusCode.OK_200 }) | ||
125 | }) | ||
126 | }) | ||
127 | |||
128 | describe('Logout', function () { | ||
129 | it('Should logout (revoke token)', async function () { | ||
130 | await server.login.logout({ token: server.accessToken }) | ||
131 | }) | ||
132 | |||
133 | it('Should not be able to get the user information', async function () { | ||
134 | await server.users.getMyInfo({ expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) | ||
135 | }) | ||
136 | |||
137 | it('Should not be able to upload a video', async function () { | ||
138 | await server.videos.upload({ attributes: { name: 'video' }, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) | ||
139 | }) | ||
140 | |||
141 | it('Should not be able to rate a video', async function () { | ||
142 | const path = '/api/v1/videos/' | ||
143 | const data = { | ||
144 | rating: 'likes' | ||
145 | } | ||
146 | |||
147 | const options = { | ||
148 | url: server.url, | ||
149 | path: path + videoId, | ||
150 | token: 'wrong token', | ||
151 | fields: data, | ||
152 | expectedStatus: HttpStatusCode.UNAUTHORIZED_401 | ||
153 | } | ||
154 | await makePutBodyRequest(options) | ||
155 | }) | ||
156 | |||
157 | it('Should be able to login again', async function () { | ||
158 | const body = await server.login.login() | ||
159 | server.accessToken = body.access_token | ||
160 | server.refreshToken = body.refresh_token | ||
161 | }) | ||
162 | |||
163 | it('Should be able to get my user information again', async function () { | ||
164 | await server.users.getMyInfo() | ||
165 | }) | ||
166 | |||
167 | it('Should have an expired access token', async function () { | ||
168 | this.timeout(60000) | ||
169 | |||
170 | await server.sql.setTokenField(server.accessToken, 'accessTokenExpiresAt', new Date().toISOString()) | ||
171 | await server.sql.setTokenField(server.accessToken, 'refreshTokenExpiresAt', new Date().toISOString()) | ||
172 | |||
173 | await killallServers([ server ]) | ||
174 | await server.run() | ||
175 | |||
176 | await server.users.getMyInfo({ expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) | ||
177 | }) | ||
178 | |||
179 | it('Should not be able to refresh an access token with an expired refresh token', async function () { | ||
180 | await server.login.refreshToken({ refreshToken: server.refreshToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | ||
181 | }) | ||
182 | |||
183 | it('Should refresh the token', async function () { | ||
184 | this.timeout(50000) | ||
185 | |||
186 | const futureDate = new Date(new Date().getTime() + 1000 * 60).toISOString() | ||
187 | await server.sql.setTokenField(server.accessToken, 'refreshTokenExpiresAt', futureDate) | ||
188 | |||
189 | await killallServers([ server ]) | ||
190 | await server.run() | ||
191 | |||
192 | const res = await server.login.refreshToken({ refreshToken: server.refreshToken }) | ||
193 | server.accessToken = res.body.access_token | ||
194 | server.refreshToken = res.body.refresh_token | ||
195 | }) | ||
196 | |||
197 | it('Should be able to get my user information again', async function () { | ||
198 | await server.users.getMyInfo() | ||
199 | }) | ||
200 | }) | ||
201 | |||
202 | describe('Creating a user', function () { | 35 | describe('Creating a user', function () { |
203 | 36 | ||
204 | it('Should be able to create a new user', async function () { | 37 | it('Should be able to create a new user', async function () { |
@@ -512,6 +345,7 @@ describe('Test users', function () { | |||
512 | }) | 345 | }) |
513 | 346 | ||
514 | describe('Updating another user', function () { | 347 | describe('Updating another user', function () { |
348 | |||
515 | it('Should be able to update another user', async function () { | 349 | it('Should be able to update another user', async function () { |
516 | await server.users.update({ | 350 | await server.users.update({ |
517 | userId, | 351 | userId, |
@@ -562,13 +396,6 @@ describe('Test users', function () { | |||
562 | }) | 396 | }) |
563 | }) | 397 | }) |
564 | 398 | ||
565 | describe('Video blacklists', function () { | ||
566 | |||
567 | it('Should be able to list my video blacklist', async function () { | ||
568 | await server.blacklist.list({ token: userToken }) | ||
569 | }) | ||
570 | }) | ||
571 | |||
572 | describe('Remove a user', function () { | 399 | describe('Remove a user', function () { |
573 | 400 | ||
574 | before(async function () { | 401 | before(async function () { |
@@ -653,8 +480,9 @@ describe('Test users', function () { | |||
653 | }) | 480 | }) |
654 | 481 | ||
655 | describe('User blocking', function () { | 482 | describe('User blocking', function () { |
656 | let user16Id | 483 | let user16Id: number |
657 | let user16AccessToken | 484 | let user16AccessToken: string |
485 | |||
658 | const user16 = { | 486 | const user16 = { |
659 | username: 'user_16', | 487 | username: 'user_16', |
660 | password: 'my super password' | 488 | password: 'my super password' |