diff options
-rw-r--r-- | client/src/app/+my-account/my-account-applications/my-account-applications.component.ts | 1 | ||||
-rw-r--r-- | client/src/app/+videos/video-list/video-user-subscriptions.component.ts | 8 | ||||
-rw-r--r-- | server/controllers/feeds.ts | 15 | ||||
-rw-r--r-- | server/helpers/middlewares/accounts.ts | 4 | ||||
-rw-r--r-- | server/initializers/constants.ts | 2 | ||||
-rw-r--r-- | server/initializers/migrations/0560-user-feed-token.ts (renamed from server/initializers/migrations/0530-user-feed-token.ts) | 23 | ||||
-rw-r--r-- | server/middlewares/validators/feeds.ts | 66 | ||||
-rw-r--r-- | server/tests/api/check-params/users.ts | 32 | ||||
-rw-r--r-- | server/tests/feeds/feeds.ts | 53 | ||||
-rw-r--r-- | shared/extra-utils/feeds/feeds.ts | 4 | ||||
-rw-r--r-- | shared/extra-utils/users/users.ts | 32 |
11 files changed, 161 insertions, 79 deletions
diff --git a/client/src/app/+my-account/my-account-applications/my-account-applications.component.ts b/client/src/app/+my-account/my-account-applications/my-account-applications.component.ts index 233e42c83..756644990 100644 --- a/client/src/app/+my-account/my-account-applications/my-account-applications.component.ts +++ b/client/src/app/+my-account/my-account-applications/my-account-applications.component.ts | |||
@@ -1,4 +1,3 @@ | |||
1 | |||
2 | import { Component, OnInit } from '@angular/core' | 1 | import { Component, OnInit } from '@angular/core' |
3 | import { AuthService, Notifier, ConfirmService, ScopedTokensService } from '@app/core' | 2 | import { AuthService, Notifier, ConfirmService, ScopedTokensService } from '@app/core' |
4 | import { VideoService } from '@app/shared/shared-main' | 3 | import { VideoService } from '@app/shared/shared-main' |
diff --git a/client/src/app/+videos/video-list/video-user-subscriptions.component.ts b/client/src/app/+videos/video-list/video-user-subscriptions.component.ts index 03881c295..bd0337c1d 100644 --- a/client/src/app/+videos/video-list/video-user-subscriptions.component.ts +++ b/client/src/app/+videos/video-list/video-user-subscriptions.component.ts | |||
@@ -1,3 +1,4 @@ | |||
1 | |||
1 | import { Component, OnDestroy, OnInit } from '@angular/core' | 2 | import { Component, OnDestroy, OnInit } from '@angular/core' |
2 | import { ActivatedRoute, Router } from '@angular/router' | 3 | import { ActivatedRoute, Router } from '@angular/router' |
3 | import { AuthService, LocalStorageService, Notifier, ScopedTokensService, ScreenService, ServerService, UserService } from '@app/core' | 4 | import { AuthService, LocalStorageService, Notifier, ScopedTokensService, ScreenService, ServerService, UserService } from '@app/core' |
@@ -6,10 +7,9 @@ import { immutableAssign } from '@app/helpers' | |||
6 | import { VideoService } from '@app/shared/shared-main' | 7 | import { VideoService } from '@app/shared/shared-main' |
7 | import { UserSubscriptionService } from '@app/shared/shared-user-subscription' | 8 | import { UserSubscriptionService } from '@app/shared/shared-user-subscription' |
8 | import { AbstractVideoList, OwnerDisplayType } from '@app/shared/shared-video-miniature' | 9 | import { AbstractVideoList, OwnerDisplayType } from '@app/shared/shared-video-miniature' |
9 | import { VideoSortField, FeedFormat } from '@shared/models' | 10 | import { FeedFormat, VideoSortField } from '@shared/models' |
10 | import { copyToClipboard } from '../../../root-helpers/utils' | ||
11 | import { environment } from '../../../environments/environment' | 11 | import { environment } from '../../../environments/environment' |
12 | import { forkJoin } from 'rxjs' | 12 | import { copyToClipboard } from '../../../root-helpers/utils' |
13 | 13 | ||
14 | @Component({ | 14 | @Component({ |
15 | selector: 'my-videos-user-subscriptions', | 15 | selector: 'my-videos-user-subscriptions', |
@@ -56,7 +56,7 @@ export class VideoUserSubscriptionsComponent extends AbstractVideoList implement | |||
56 | this.scopedTokensService.getScopedTokens().subscribe( | 56 | this.scopedTokensService.getScopedTokens().subscribe( |
57 | tokens => { | 57 | tokens => { |
58 | const feeds = this.videoService.getVideoSubscriptionFeedUrls(user.account.id, tokens.feedToken) | 58 | const feeds = this.videoService.getVideoSubscriptionFeedUrls(user.account.id, tokens.feedToken) |
59 | feedUrl = feedUrl + feeds.find((f: any) => f.format === FeedFormat.RSS).url | 59 | feedUrl = feedUrl + feeds.find(f => f.format === FeedFormat.RSS).url |
60 | }, | 60 | }, |
61 | 61 | ||
62 | err => { | 62 | err => { |
diff --git a/server/controllers/feeds.ts b/server/controllers/feeds.ts index 5c95069fc..2182b42f5 100644 --- a/server/controllers/feeds.ts +++ b/server/controllers/feeds.ts | |||
@@ -12,7 +12,7 @@ import { | |||
12 | videoCommentsFeedsValidator, | 12 | videoCommentsFeedsValidator, |
13 | videoFeedsValidator, | 13 | videoFeedsValidator, |
14 | videosSortValidator, | 14 | videosSortValidator, |
15 | videoSubscriptonFeedsValidator | 15 | videoSubscriptionFeedsValidator |
16 | } from '../middlewares' | 16 | } from '../middlewares' |
17 | import { cacheRoute } from '../middlewares/cache' | 17 | import { cacheRoute } from '../middlewares/cache' |
18 | import { VideoModel } from '../models/video/video' | 18 | import { VideoModel } from '../models/video/video' |
@@ -60,7 +60,7 @@ feedsRouter.get('/feeds/subscriptions.:format', | |||
60 | ] | 60 | ] |
61 | })(ROUTE_CACHE_LIFETIME.FEEDS)), | 61 | })(ROUTE_CACHE_LIFETIME.FEEDS)), |
62 | commonVideosFiltersValidator, | 62 | commonVideosFiltersValidator, |
63 | asyncMiddleware(videoSubscriptonFeedsValidator), | 63 | asyncMiddleware(videoSubscriptionFeedsValidator), |
64 | asyncMiddleware(generateVideoFeedForSubscriptions) | 64 | asyncMiddleware(generateVideoFeedForSubscriptions) |
65 | ) | 65 | ) |
66 | 66 | ||
@@ -198,20 +198,17 @@ async function generateVideoFeedForSubscriptions (req: express.Request, res: exp | |||
198 | queryString: new URL(WEBSERVER.URL + req.url).search | 198 | queryString: new URL(WEBSERVER.URL + req.url).search |
199 | }) | 199 | }) |
200 | 200 | ||
201 | const options = { | ||
202 | followerActorId: res.locals.user.Account.Actor.id, | ||
203 | user: res.locals.user | ||
204 | } | ||
205 | |||
206 | const resultList = await VideoModel.listForApi({ | 201 | const resultList = await VideoModel.listForApi({ |
207 | start, | 202 | start, |
208 | count: FEEDS.COUNT, | 203 | count: FEEDS.COUNT, |
209 | sort: req.query.sort, | 204 | sort: req.query.sort, |
210 | includeLocalVideos: true, | 205 | includeLocalVideos: false, |
211 | nsfw, | 206 | nsfw, |
212 | filter: req.query.filter as VideoFilter, | 207 | filter: req.query.filter as VideoFilter, |
213 | withFiles: true, | 208 | withFiles: true, |
214 | ...options | 209 | |
210 | followerActorId: res.locals.user.Account.Actor.id, | ||
211 | user: res.locals.user | ||
215 | }) | 212 | }) |
216 | 213 | ||
217 | addVideosToFeed(feed, resultList.data) | 214 | addVideosToFeed(feed, resultList.data) |
diff --git a/server/helpers/middlewares/accounts.ts b/server/helpers/middlewares/accounts.ts index fa4a51e6c..e9b981262 100644 --- a/server/helpers/middlewares/accounts.ts +++ b/server/helpers/middlewares/accounts.ts | |||
@@ -39,11 +39,11 @@ async function doesAccountExist (p: Bluebird<MAccountDefault>, res: Response, se | |||
39 | return true | 39 | return true |
40 | } | 40 | } |
41 | 41 | ||
42 | async function doesUserFeedTokenCorrespond (id: number | string, token: string, res: Response) { | 42 | async function doesUserFeedTokenCorrespond (id: number, token: string, res: Response) { |
43 | const user = await UserModel.loadByIdWithChannels(parseInt(id + '', 10)) | 43 | const user = await UserModel.loadByIdWithChannels(parseInt(id + '', 10)) |
44 | 44 | ||
45 | if (token !== user.feedToken) { | 45 | if (token !== user.feedToken) { |
46 | res.status(401) | 46 | res.status(403) |
47 | .json({ error: 'User and token mismatch' }) | 47 | .json({ error: 'User and token mismatch' }) |
48 | 48 | ||
49 | return false | 49 | return false |
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 945185f62..6c44d703e 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -24,7 +24,7 @@ import { CONFIG, registerConfigChangedHandler } from './config' | |||
24 | 24 | ||
25 | // --------------------------------------------------------------------------- | 25 | // --------------------------------------------------------------------------- |
26 | 26 | ||
27 | const LAST_MIGRATION_VERSION = 555 | 27 | const LAST_MIGRATION_VERSION = 560 |
28 | 28 | ||
29 | // --------------------------------------------------------------------------- | 29 | // --------------------------------------------------------------------------- |
30 | 30 | ||
diff --git a/server/initializers/migrations/0530-user-feed-token.ts b/server/initializers/migrations/0560-user-feed-token.ts index 421016b11..7c61def17 100644 --- a/server/initializers/migrations/0530-user-feed-token.ts +++ b/server/initializers/migrations/0560-user-feed-token.ts | |||
@@ -9,13 +9,15 @@ async function up (utils: { | |||
9 | }): Promise<void> { | 9 | }): Promise<void> { |
10 | const q = utils.queryInterface | 10 | const q = utils.queryInterface |
11 | 11 | ||
12 | // Create uuid column for users | 12 | { |
13 | const userFeedTokenUUID = { | 13 | // Create uuid column for users |
14 | type: Sequelize.UUID, | 14 | const userFeedTokenUUID = { |
15 | defaultValue: Sequelize.UUIDV4, | 15 | type: Sequelize.UUID, |
16 | allowNull: true | 16 | defaultValue: Sequelize.UUIDV4, |
17 | allowNull: true | ||
18 | } | ||
19 | await q.addColumn('user', 'feedToken', userFeedTokenUUID) | ||
17 | } | 20 | } |
18 | await q.addColumn('user', 'feedToken', userFeedTokenUUID) | ||
19 | 21 | ||
20 | // Set UUID to previous users | 22 | // Set UUID to previous users |
21 | { | 23 | { |
@@ -28,6 +30,15 @@ async function up (utils: { | |||
28 | await utils.sequelize.query(queryUpdate) | 30 | await utils.sequelize.query(queryUpdate) |
29 | } | 31 | } |
30 | } | 32 | } |
33 | |||
34 | { | ||
35 | const userFeedTokenUUID = { | ||
36 | type: Sequelize.UUID, | ||
37 | defaultValue: Sequelize.UUIDV4, | ||
38 | allowNull: false | ||
39 | } | ||
40 | await q.changeColumn('user', 'feedToken', userFeedTokenUUID) | ||
41 | } | ||
31 | } | 42 | } |
32 | 43 | ||
33 | function down (options) { | 44 | function down (options) { |
diff --git a/server/middlewares/validators/feeds.ts b/server/middlewares/validators/feeds.ts index 35080ffca..18469bad3 100644 --- a/server/middlewares/validators/feeds.ts +++ b/server/middlewares/validators/feeds.ts | |||
@@ -1,17 +1,17 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { param, query } from 'express-validator' | 2 | import { param, query } from 'express-validator' |
3 | import { isIdOrUUIDValid, isIdValid } from '../../helpers/custom-validators/misc' | ||
4 | import { logger } from '../../helpers/logger' | ||
5 | import { areValidationErrors } from './utils' | ||
6 | import { isValidRSSFeed } from '../../helpers/custom-validators/feeds' | 3 | import { isValidRSSFeed } from '../../helpers/custom-validators/feeds' |
7 | import { doesVideoExist } from '../../helpers/middlewares/videos' | 4 | import { exists, isIdOrUUIDValid, isIdValid } from '../../helpers/custom-validators/misc' |
5 | import { logger } from '../../helpers/logger' | ||
8 | import { | 6 | import { |
9 | doesAccountIdExist, | 7 | doesAccountIdExist, |
10 | doesAccountNameWithHostExist, | 8 | doesAccountNameWithHostExist, |
9 | doesUserFeedTokenCorrespond, | ||
11 | doesVideoChannelIdExist, | 10 | doesVideoChannelIdExist, |
12 | doesVideoChannelNameWithHostExist, | 11 | doesVideoChannelNameWithHostExist |
13 | doesUserFeedTokenCorrespond | ||
14 | } from '../../helpers/middlewares' | 12 | } from '../../helpers/middlewares' |
13 | import { doesVideoExist } from '../../helpers/middlewares/videos' | ||
14 | import { areValidationErrors } from './utils' | ||
15 | 15 | ||
16 | const feedsFormatValidator = [ | 16 | const feedsFormatValidator = [ |
17 | param('format').optional().custom(isValidRSSFeed).withMessage('Should have a valid format (rss, atom, json)'), | 17 | param('format').optional().custom(isValidRSSFeed).withMessage('Should have a valid format (rss, atom, json)'), |
@@ -35,19 +35,31 @@ function setFeedFormatContentType (req: express.Request, res: express.Response, | |||
35 | if (req.accepts(acceptableContentTypes)) { | 35 | if (req.accepts(acceptableContentTypes)) { |
36 | res.set('Content-Type', req.accepts(acceptableContentTypes) as string) | 36 | res.set('Content-Type', req.accepts(acceptableContentTypes) as string) |
37 | } else { | 37 | } else { |
38 | return res.status(406).send({ | 38 | return res.status(406) |
39 | message: `You should accept at least one of the following content-types: ${acceptableContentTypes.join(', ')}` | 39 | .json({ |
40 | }).end() | 40 | message: `You should accept at least one of the following content-types: ${acceptableContentTypes.join(', ')}` |
41 | }) | ||
41 | } | 42 | } |
42 | 43 | ||
43 | return next() | 44 | return next() |
44 | } | 45 | } |
45 | 46 | ||
46 | const videoFeedsValidator = [ | 47 | const videoFeedsValidator = [ |
47 | query('accountId').optional().custom(isIdValid), | 48 | query('accountId') |
48 | query('accountName').optional(), | 49 | .optional() |
49 | query('videoChannelId').optional().custom(isIdValid), | 50 | .custom(isIdValid) |
50 | query('videoChannelName').optional(), | 51 | .withMessage('Should have a valid account id'), |
52 | |||
53 | query('accountName') | ||
54 | .optional(), | ||
55 | |||
56 | query('videoChannelId') | ||
57 | .optional() | ||
58 | .custom(isIdValid) | ||
59 | .withMessage('Should have a valid channel id'), | ||
60 | |||
61 | query('videoChannelName') | ||
62 | .optional(), | ||
51 | 63 | ||
52 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 64 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
53 | logger.debug('Checking feeds parameters', { parameters: req.query }) | 65 | logger.debug('Checking feeds parameters', { parameters: req.query }) |
@@ -63,19 +75,22 @@ const videoFeedsValidator = [ | |||
63 | } | 75 | } |
64 | ] | 76 | ] |
65 | 77 | ||
66 | const videoSubscriptonFeedsValidator = [ | 78 | const videoSubscriptionFeedsValidator = [ |
67 | query('accountId').custom(isIdValid), | 79 | query('accountId') |
68 | query('token'), | 80 | .custom(isIdValid) |
81 | .withMessage('Should have a valid account id'), | ||
82 | |||
83 | query('token') | ||
84 | .custom(exists) | ||
85 | .withMessage('Should have a token'), | ||
69 | 86 | ||
70 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 87 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
71 | logger.debug('Checking feeds parameters', { parameters: req.query }) | 88 | logger.debug('Checking subscription feeds parameters', { parameters: req.query }) |
72 | 89 | ||
73 | if (areValidationErrors(req, res)) return | 90 | if (areValidationErrors(req, res)) return |
74 | 91 | ||
75 | // a token alone is erroneous | 92 | if (!await doesAccountIdExist(req.query.accountId, res)) return |
76 | if (req.query.token && !req.query.accountId) return | 93 | if (!await doesUserFeedTokenCorrespond(res.locals.account.userId, req.query.token, res)) return |
77 | if (req.query.accountId && !await doesAccountIdExist(req.query.accountId, res)) return | ||
78 | if (req.query.token && !await doesUserFeedTokenCorrespond(res.locals.account.userId, req.query.token, res)) return | ||
79 | 94 | ||
80 | return next() | 95 | return next() |
81 | } | 96 | } |
@@ -90,9 +105,10 @@ const videoCommentsFeedsValidator = [ | |||
90 | if (areValidationErrors(req, res)) return | 105 | if (areValidationErrors(req, res)) return |
91 | 106 | ||
92 | if (req.query.videoId && (req.query.videoChannelId || req.query.videoChannelName)) { | 107 | if (req.query.videoId && (req.query.videoChannelId || req.query.videoChannelName)) { |
93 | return res.status(400).send({ | 108 | return res.status(400) |
94 | message: 'videoId cannot be mixed with a channel filter' | 109 | .json({ |
95 | }).end() | 110 | message: 'videoId cannot be mixed with a channel filter' |
111 | }) | ||
96 | } | 112 | } |
97 | 113 | ||
98 | if (req.query.videoId && !await doesVideoExist(req.query.videoId, res)) return | 114 | if (req.query.videoId && !await doesVideoExist(req.query.videoId, res)) return |
@@ -107,6 +123,6 @@ export { | |||
107 | feedsFormatValidator, | 123 | feedsFormatValidator, |
108 | setFeedFormatContentType, | 124 | setFeedFormatContentType, |
109 | videoFeedsValidator, | 125 | videoFeedsValidator, |
110 | videoSubscriptonFeedsValidator, | 126 | videoSubscriptionFeedsValidator, |
111 | videoCommentsFeedsValidator | 127 | videoCommentsFeedsValidator |
112 | } | 128 | } |
diff --git a/server/tests/api/check-params/users.ts b/server/tests/api/check-params/users.ts index 2a220be83..da7dc9704 100644 --- a/server/tests/api/check-params/users.ts +++ b/server/tests/api/check-params/users.ts | |||
@@ -14,6 +14,7 @@ import { | |||
14 | flushAndRunServer, | 14 | flushAndRunServer, |
15 | getMyUserInformation, | 15 | getMyUserInformation, |
16 | getMyUserVideoRating, | 16 | getMyUserVideoRating, |
17 | getUserScopedTokens, | ||
17 | getUsersList, | 18 | getUsersList, |
18 | immutableAssign, | 19 | immutableAssign, |
19 | killallServers, | 20 | killallServers, |
@@ -23,6 +24,7 @@ import { | |||
23 | makeUploadRequest, | 24 | makeUploadRequest, |
24 | registerUser, | 25 | registerUser, |
25 | removeUser, | 26 | removeUser, |
27 | renewUserScopedTokens, | ||
26 | reRunServer, | 28 | reRunServer, |
27 | ServerInfo, | 29 | ServerInfo, |
28 | setAccessTokensToServers, | 30 | setAccessTokensToServers, |
@@ -38,7 +40,7 @@ import { | |||
38 | checkBadStartPagination | 40 | checkBadStartPagination |
39 | } from '../../../../shared/extra-utils/requests/check-api-params' | 41 | } from '../../../../shared/extra-utils/requests/check-api-params' |
40 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | 42 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' |
41 | import { getMagnetURI, getMyVideoImports, getGoodVideoUrl, importVideo } from '../../../../shared/extra-utils/videos/video-imports' | 43 | import { getGoodVideoUrl, getMagnetURI, getMyVideoImports, importVideo } from '../../../../shared/extra-utils/videos/video-imports' |
42 | import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model' | 44 | import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model' |
43 | import { VideoPrivacy } from '../../../../shared/models/videos' | 45 | import { VideoPrivacy } from '../../../../shared/models/videos' |
44 | 46 | ||
@@ -609,6 +611,34 @@ describe('Test users API validators', function () { | |||
609 | }) | 611 | }) |
610 | }) | 612 | }) |
611 | 613 | ||
614 | describe('When managing my scoped tokens', function () { | ||
615 | |||
616 | it('Should fail to get my scoped tokens with an non authenticated user', async function () { | ||
617 | await getUserScopedTokens(server.url, null, 401) | ||
618 | }) | ||
619 | |||
620 | it('Should fail to get my scoped tokens with a bad token', async function () { | ||
621 | await getUserScopedTokens(server.url, 'bad', 401) | ||
622 | |||
623 | }) | ||
624 | |||
625 | it('Should succeed to get my scoped tokens', async function () { | ||
626 | await getUserScopedTokens(server.url, server.accessToken) | ||
627 | }) | ||
628 | |||
629 | it('Should fail to renew my scoped tokens with an non authenticated user', async function () { | ||
630 | await renewUserScopedTokens(server.url, null, 401) | ||
631 | }) | ||
632 | |||
633 | it('Should fail to renew my scoped tokens with a bad token', async function () { | ||
634 | await renewUserScopedTokens(server.url, 'bad', 401) | ||
635 | }) | ||
636 | |||
637 | it('Should succeed to renew my scoped tokens', async function () { | ||
638 | await renewUserScopedTokens(server.url, server.accessToken) | ||
639 | }) | ||
640 | }) | ||
641 | |||
612 | describe('When getting a user', function () { | 642 | describe('When getting a user', function () { |
613 | 643 | ||
614 | it('Should fail with an non authenticated user', async function () { | 644 | it('Should fail with an non authenticated user', async function () { |
diff --git a/server/tests/feeds/feeds.ts b/server/tests/feeds/feeds.ts index 175ea9102..92a468192 100644 --- a/server/tests/feeds/feeds.ts +++ b/server/tests/feeds/feeds.ts | |||
@@ -8,28 +8,29 @@ import { | |||
8 | addAccountToServerBlocklist, | 8 | addAccountToServerBlocklist, |
9 | removeAccountFromServerBlocklist | 9 | removeAccountFromServerBlocklist |
10 | } from '@shared/extra-utils/users/blocklist' | 10 | } from '@shared/extra-utils/users/blocklist' |
11 | import { addUserSubscription, listUserSubscriptionVideos } from '@shared/extra-utils/users/user-subscriptions' | ||
11 | import { VideoPrivacy } from '@shared/models' | 12 | import { VideoPrivacy } from '@shared/models' |
13 | import { ScopedToken } from '@shared/models/users/user-scoped-token' | ||
12 | import { | 14 | import { |
13 | cleanupTests, | 15 | cleanupTests, |
14 | createUser, | 16 | createUser, |
15 | doubleFollow, | 17 | doubleFollow, |
16 | flushAndRunMultipleServers, | 18 | flushAndRunMultipleServers, |
19 | flushAndRunServer, | ||
17 | getJSONfeed, | 20 | getJSONfeed, |
18 | getMyUserInformation, | 21 | getMyUserInformation, |
22 | getUserScopedTokens, | ||
19 | getXMLfeed, | 23 | getXMLfeed, |
24 | renewUserScopedTokens, | ||
20 | ServerInfo, | 25 | ServerInfo, |
21 | setAccessTokensToServers, | 26 | setAccessTokensToServers, |
22 | uploadVideo, | 27 | uploadVideo, |
23 | uploadVideoAndGetId, | 28 | uploadVideoAndGetId, |
24 | userLogin, | 29 | userLogin |
25 | flushAndRunServer, | ||
26 | getUserScopedTokens | ||
27 | } from '../../../shared/extra-utils' | 30 | } from '../../../shared/extra-utils' |
28 | import { waitJobs } from '../../../shared/extra-utils/server/jobs' | 31 | import { waitJobs } from '../../../shared/extra-utils/server/jobs' |
29 | import { addVideoCommentThread } from '../../../shared/extra-utils/videos/video-comments' | 32 | import { addVideoCommentThread } from '../../../shared/extra-utils/videos/video-comments' |
30 | import { User } from '../../../shared/models/users' | 33 | import { User } from '../../../shared/models/users' |
31 | import { ScopedToken } from '@shared/models/users/user-scoped-token' | ||
32 | import { listUserSubscriptionVideos, addUserSubscription } from '@shared/extra-utils/users/user-subscriptions' | ||
33 | 34 | ||
34 | chai.use(require('chai-xml')) | 35 | chai.use(require('chai-xml')) |
35 | chai.use(require('chai-json-schema')) | 36 | chai.use(require('chai-json-schema')) |
@@ -298,14 +299,10 @@ describe('Test syndication feeds', () => { | |||
298 | }) | 299 | }) |
299 | 300 | ||
300 | describe('Video feed from my subscriptions', function () { | 301 | describe('Video feed from my subscriptions', function () { |
301 | /** | 302 | let feeduserAccountId: number |
302 | * use the 'version' query parameter to bust cache between tests | 303 | let feeduserFeedToken: string |
303 | */ | ||
304 | 304 | ||
305 | it('Should list no videos for a user with no videos and no subscriptions', async function () { | 305 | it('Should list no videos for a user with no videos and no subscriptions', async function () { |
306 | let feeduserAccountId: number | ||
307 | let feeduserFeedToken: string | ||
308 | |||
309 | const attr = { username: 'feeduser', password: 'password' } | 306 | const attr = { username: 'feeduser', password: 'password' } |
310 | await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: attr.username, password: attr.password }) | 307 | await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: attr.username, password: attr.password }) |
311 | const feeduserAccessToken = await userLogin(servers[0], attr) | 308 | const feeduserAccessToken = await userLogin(servers[0], attr) |
@@ -332,15 +329,21 @@ describe('Test syndication feeds', () => { | |||
332 | } | 329 | } |
333 | }) | 330 | }) |
334 | 331 | ||
332 | it('Should fail with an invalid token', async function () { | ||
333 | await getJSONfeed(servers[0].url, 'subscriptions', { accountId: feeduserAccountId, token: 'toto' }, 403) | ||
334 | }) | ||
335 | |||
336 | it('Should fail with a token of another user', async function () { | ||
337 | await getJSONfeed(servers[0].url, 'subscriptions', { accountId: feeduserAccountId, token: userFeedToken }, 403) | ||
338 | }) | ||
339 | |||
335 | it('Should list no videos for a user with videos but no subscriptions', async function () { | 340 | it('Should list no videos for a user with videos but no subscriptions', async function () { |
336 | { | 341 | const res = await listUserSubscriptionVideos(servers[0].url, userAccessToken) |
337 | const res = await listUserSubscriptionVideos(servers[0].url, userAccessToken) | 342 | expect(res.body.total).to.equal(0) |
338 | expect(res.body.total).to.equal(0) | ||
339 | 343 | ||
340 | const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken }) | 344 | const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken }) |
341 | const jsonObj = JSON.parse(json.text) | 345 | const jsonObj = JSON.parse(json.text) |
342 | expect(jsonObj.items.length).to.be.equal(0) // no subscription, it should not list the instance's videos but list 0 videos | 346 | expect(jsonObj.items.length).to.be.equal(0) // no subscription, it should not list the instance's videos but list 0 videos |
343 | } | ||
344 | }) | 347 | }) |
345 | 348 | ||
346 | it('Should list self videos for a user with a subscription to themselves', async function () { | 349 | it('Should list self videos for a user with a subscription to themselves', async function () { |
@@ -376,6 +379,20 @@ describe('Test syndication feeds', () => { | |||
376 | } | 379 | } |
377 | }) | 380 | }) |
378 | 381 | ||
382 | it('Should renew the token, and so have an invalid old token', async function () { | ||
383 | await renewUserScopedTokens(servers[0].url, userAccessToken) | ||
384 | |||
385 | await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken, version: 3 }, 403) | ||
386 | }) | ||
387 | |||
388 | it('Should succeed with the new token', async function () { | ||
389 | const res2 = await getUserScopedTokens(servers[0].url, userAccessToken) | ||
390 | const token: ScopedToken = res2.body | ||
391 | userFeedToken = token.feedToken | ||
392 | |||
393 | await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken, version: 4 }) | ||
394 | }) | ||
395 | |||
379 | }) | 396 | }) |
380 | 397 | ||
381 | after(async function () { | 398 | after(async function () { |
diff --git a/shared/extra-utils/feeds/feeds.ts b/shared/extra-utils/feeds/feeds.ts index bafbb9f94..957d4499c 100644 --- a/shared/extra-utils/feeds/feeds.ts +++ b/shared/extra-utils/feeds/feeds.ts | |||
@@ -13,14 +13,14 @@ function getXMLfeed (url: string, feed: FeedType, format?: string) { | |||
13 | .expect('Content-Type', /xml/) | 13 | .expect('Content-Type', /xml/) |
14 | } | 14 | } |
15 | 15 | ||
16 | function getJSONfeed (url: string, feed: FeedType, query: any = {}) { | 16 | function getJSONfeed (url: string, feed: FeedType, query: any = {}, statusCodeExpected = 200) { |
17 | const path = '/feeds/' + feed + '.json' | 17 | const path = '/feeds/' + feed + '.json' |
18 | 18 | ||
19 | return request(url) | 19 | return request(url) |
20 | .get(path) | 20 | .get(path) |
21 | .query(query) | 21 | .query(query) |
22 | .set('Accept', 'application/json') | 22 | .set('Accept', 'application/json') |
23 | .expect(200) | 23 | .expect(statusCodeExpected) |
24 | .expect('Content-Type', /json/) | 24 | .expect('Content-Type', /json/) |
25 | } | 25 | } |
26 | 26 | ||
diff --git a/shared/extra-utils/users/users.ts b/shared/extra-utils/users/users.ts index 4d0986ce3..ebb8bc257 100644 --- a/shared/extra-utils/users/users.ts +++ b/shared/extra-utils/users/users.ts | |||
@@ -1,12 +1,12 @@ | |||
1 | import { omit } from 'lodash' | ||
1 | import * as request from 'supertest' | 2 | import * as request from 'supertest' |
2 | import { makePostBodyRequest, makePutBodyRequest, updateAvatarRequest } from '../requests/requests' | 3 | import { UserUpdateMe } from '../../models/users' |
3 | import { UserAdminFlag } from '../../models/users/user-flag.model' | 4 | import { UserAdminFlag } from '../../models/users/user-flag.model' |
4 | import { UserRegister } from '../../models/users/user-register.model' | 5 | import { UserRegister } from '../../models/users/user-register.model' |
5 | import { UserRole } from '../../models/users/user-role' | 6 | import { UserRole } from '../../models/users/user-role' |
7 | import { makeGetRequest, makePostBodyRequest, makePutBodyRequest, updateAvatarRequest } from '../requests/requests' | ||
6 | import { ServerInfo } from '../server/servers' | 8 | import { ServerInfo } from '../server/servers' |
7 | import { userLogin } from './login' | 9 | import { userLogin } from './login' |
8 | import { UserUpdateMe } from '../../models/users' | ||
9 | import { omit } from 'lodash' | ||
10 | 10 | ||
11 | type CreateUserArgs = { | 11 | type CreateUserArgs = { |
12 | url: string | 12 | url: string |
@@ -109,15 +109,26 @@ function getMyUserInformation (url: string, accessToken: string, specialStatus = | |||
109 | .expect('Content-Type', /json/) | 109 | .expect('Content-Type', /json/) |
110 | } | 110 | } |
111 | 111 | ||
112 | function getUserScopedTokens (url: string, accessToken: string, specialStatus = 200) { | 112 | function getUserScopedTokens (url: string, token: string, statusCodeExpected = 200) { |
113 | const path = '/api/v1/users/scoped-tokens' | 113 | const path = '/api/v1/users/scoped-tokens' |
114 | 114 | ||
115 | return request(url) | 115 | return makeGetRequest({ |
116 | .get(path) | 116 | url, |
117 | .set('Accept', 'application/json') | 117 | path, |
118 | .set('Authorization', 'Bearer ' + accessToken) | 118 | token, |
119 | .expect(specialStatus) | 119 | statusCodeExpected |
120 | .expect('Content-Type', /json/) | 120 | }) |
121 | } | ||
122 | |||
123 | function renewUserScopedTokens (url: string, token: string, statusCodeExpected = 200) { | ||
124 | const path = '/api/v1/users/scoped-tokens' | ||
125 | |||
126 | return makePostBodyRequest({ | ||
127 | url, | ||
128 | path, | ||
129 | token, | ||
130 | statusCodeExpected | ||
131 | }) | ||
121 | } | 132 | } |
122 | 133 | ||
123 | function deleteMe (url: string, accessToken: string, specialStatus = 204) { | 134 | function deleteMe (url: string, accessToken: string, specialStatus = 204) { |
@@ -359,6 +370,7 @@ export { | |||
359 | unblockUser, | 370 | unblockUser, |
360 | askResetPassword, | 371 | askResetPassword, |
361 | resetPassword, | 372 | resetPassword, |
373 | renewUserScopedTokens, | ||
362 | updateMyAvatar, | 374 | updateMyAvatar, |
363 | askSendVerifyEmail, | 375 | askSendVerifyEmail, |
364 | generateUserAccessToken, | 376 | generateUserAccessToken, |