aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/controllers/api/users/index.ts5
-rw-r--r--server/controllers/api/users/me.ts2
-rw-r--r--server/controllers/api/video-channel.ts10
-rw-r--r--server/lib/avatar.ts11
-rw-r--r--server/lib/oauth-model.ts40
-rw-r--r--server/models/account/user.ts9
-rw-r--r--server/models/oauth/oauth-token.ts21
7 files changed, 79 insertions, 19 deletions
diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts
index d1163900b..8b8ebcd23 100644
--- a/server/controllers/api/users/index.ts
+++ b/server/controllers/api/users/index.ts
@@ -37,6 +37,7 @@ import { UserModel } from '../../../models/account/user'
37import { OAuthTokenModel } from '../../../models/oauth/oauth-token' 37import { OAuthTokenModel } from '../../../models/oauth/oauth-token'
38import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger' 38import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger'
39import { meRouter } from './me' 39import { meRouter } from './me'
40import { deleteUserToken } from '../../../lib/oauth-model'
40 41
41const auditLogger = auditLoggerFactory('users') 42const auditLogger = auditLoggerFactory('users')
42 43
@@ -267,7 +268,7 @@ async function updateUser (req: express.Request, res: express.Response, next: ex
267 const user = await userToUpdate.save() 268 const user = await userToUpdate.save()
268 269
269 // Destroy user token to refresh rights 270 // Destroy user token to refresh rights
270 if (roleChanged) await OAuthTokenModel.deleteUserToken(userToUpdate.id) 271 if (roleChanged) await deleteUserToken(userToUpdate.id)
271 272
272 auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()), oldUserAuditView) 273 auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()), oldUserAuditView)
273 274
@@ -330,7 +331,7 @@ async function changeUserBlock (res: express.Response, user: UserModel, block: b
330 user.blockedReason = reason || null 331 user.blockedReason = reason || null
331 332
332 await sequelizeTypescript.transaction(async t => { 333 await sequelizeTypescript.transaction(async t => {
333 await OAuthTokenModel.deleteUserToken(user.id, t) 334 await deleteUserToken(user.id, t)
334 335
335 await user.save({ transaction: t }) 336 await user.save({ transaction: t })
336 }) 337 })
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts
index eba1e7edd..ff3a87b7f 100644
--- a/server/controllers/api/users/me.ts
+++ b/server/controllers/api/users/me.ts
@@ -353,7 +353,7 @@ async function updateMyAvatar (req: express.Request, res: express.Response, next
353 353
354 const userAccount = await AccountModel.load(user.Account.id) 354 const userAccount = await AccountModel.load(user.Account.id)
355 355
356 const avatar = await updateActorAvatarFile(avatarPhysicalFile, userAccount.Actor, userAccount) 356 const avatar = await updateActorAvatarFile(avatarPhysicalFile, userAccount)
357 357
358 auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()), oldUserAuditView) 358 auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()), oldUserAuditView)
359 359
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts
index 8fc340224..ff6bbe44c 100644
--- a/server/controllers/api/video-channel.ts
+++ b/server/controllers/api/video-channel.ts
@@ -56,7 +56,7 @@ videoChannelRouter.post('/:nameWithHost/avatar/pick',
56 // Check the rights 56 // Check the rights
57 asyncMiddleware(videoChannelsUpdateValidator), 57 asyncMiddleware(videoChannelsUpdateValidator),
58 updateAvatarValidator, 58 updateAvatarValidator,
59 asyncMiddleware(updateVideoChannelAvatar) 59 asyncRetryTransactionMiddleware(updateVideoChannelAvatar)
60) 60)
61 61
62videoChannelRouter.put('/:nameWithHost', 62videoChannelRouter.put('/:nameWithHost',
@@ -107,13 +107,9 @@ async function updateVideoChannelAvatar (req: express.Request, res: express.Resp
107 const videoChannel = res.locals.videoChannel as VideoChannelModel 107 const videoChannel = res.locals.videoChannel as VideoChannelModel
108 const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannel.toFormattedJSON()) 108 const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannel.toFormattedJSON())
109 109
110 const avatar = await updateActorAvatarFile(avatarPhysicalFile, videoChannel.Actor, videoChannel) 110 const avatar = await updateActorAvatarFile(avatarPhysicalFile, videoChannel)
111 111
112 auditLogger.update( 112 auditLogger.update(getAuditIdFromRes(res), new VideoChannelAuditView(videoChannel.toFormattedJSON()), oldVideoChannelAuditKeys)
113 getAuditIdFromRes(res),
114 new VideoChannelAuditView(videoChannel.toFormattedJSON()),
115 oldVideoChannelAuditKeys
116 )
117 113
118 return res 114 return res
119 .json({ 115 .json({
diff --git a/server/lib/avatar.ts b/server/lib/avatar.ts
index 5cfb81fc7..14f0a05f5 100644
--- a/server/lib/avatar.ts
+++ b/server/lib/avatar.ts
@@ -3,23 +3,18 @@ import { sendUpdateActor } from './activitypub/send'
3import { AVATARS_SIZE, CONFIG, sequelizeTypescript } from '../initializers' 3import { AVATARS_SIZE, CONFIG, sequelizeTypescript } from '../initializers'
4import { updateActorAvatarInstance } from './activitypub' 4import { updateActorAvatarInstance } from './activitypub'
5import { processImage } from '../helpers/image-utils' 5import { processImage } from '../helpers/image-utils'
6import { ActorModel } from '../models/activitypub/actor'
7import { AccountModel } from '../models/account/account' 6import { AccountModel } from '../models/account/account'
8import { VideoChannelModel } from '../models/video/video-channel' 7import { VideoChannelModel } from '../models/video/video-channel'
9import { extname, join } from 'path' 8import { extname, join } from 'path'
10 9
11async function updateActorAvatarFile ( 10async function updateActorAvatarFile (avatarPhysicalFile: Express.Multer.File, accountOrChannel: AccountModel | VideoChannelModel) {
12 avatarPhysicalFile: Express.Multer.File,
13 actor: ActorModel,
14 accountOrChannel: AccountModel | VideoChannelModel
15) {
16 const extension = extname(avatarPhysicalFile.filename) 11 const extension = extname(avatarPhysicalFile.filename)
17 const avatarName = actor.uuid + extension 12 const avatarName = accountOrChannel.Actor.uuid + extension
18 const destination = join(CONFIG.STORAGE.AVATARS_DIR, avatarName) 13 const destination = join(CONFIG.STORAGE.AVATARS_DIR, avatarName)
19 await processImage(avatarPhysicalFile, destination, AVATARS_SIZE) 14 await processImage(avatarPhysicalFile, destination, AVATARS_SIZE)
20 15
21 return sequelizeTypescript.transaction(async t => { 16 return sequelizeTypescript.transaction(async t => {
22 const updatedActor = await updateActorAvatarInstance(actor, avatarName, t) 17 const updatedActor = await updateActorAvatarInstance(accountOrChannel.Actor, avatarName, t)
23 await updatedActor.save({ transaction: t }) 18 await updatedActor.save({ transaction: t })
24 19
25 await sendUpdateActor(accountOrChannel, t) 20 await sendUpdateActor(accountOrChannel, t)
diff --git a/server/lib/oauth-model.ts b/server/lib/oauth-model.ts
index 2f8667e19..5cbe60b82 100644
--- a/server/lib/oauth-model.ts
+++ b/server/lib/oauth-model.ts
@@ -4,15 +4,50 @@ import { UserModel } from '../models/account/user'
4import { OAuthClientModel } from '../models/oauth/oauth-client' 4import { OAuthClientModel } from '../models/oauth/oauth-client'
5import { OAuthTokenModel } from '../models/oauth/oauth-token' 5import { OAuthTokenModel } from '../models/oauth/oauth-token'
6import { CONFIG } from '../initializers/constants' 6import { CONFIG } from '../initializers/constants'
7import { Transaction } from 'sequelize'
7 8
8type TokenInfo = { accessToken: string, refreshToken: string, accessTokenExpiresAt: Date, refreshTokenExpiresAt: Date } 9type TokenInfo = { accessToken: string, refreshToken: string, accessTokenExpiresAt: Date, refreshTokenExpiresAt: Date }
10const accessTokenCache: { [ accessToken: string ]: OAuthTokenModel } = {}
11const userHavingToken: { [ userId: number ]: string } = {}
9 12
10// --------------------------------------------------------------------------- 13// ---------------------------------------------------------------------------
11 14
15function deleteUserToken (userId: number, t?: Transaction) {
16 clearCacheByUserId(userId)
17
18 return OAuthTokenModel.deleteUserToken(userId, t)
19}
20
21function clearCacheByUserId (userId: number) {
22 const token = userHavingToken[userId]
23 if (token !== undefined) {
24 accessTokenCache[ token ] = undefined
25 userHavingToken[ userId ] = undefined
26 }
27}
28
29function clearCacheByToken (token: string) {
30 const tokenModel = accessTokenCache[ token ]
31 if (tokenModel !== undefined) {
32 userHavingToken[tokenModel.userId] = undefined
33 accessTokenCache[ token ] = undefined
34 }
35}
36
12function getAccessToken (bearerToken: string) { 37function getAccessToken (bearerToken: string) {
13 logger.debug('Getting access token (bearerToken: ' + bearerToken + ').') 38 logger.debug('Getting access token (bearerToken: ' + bearerToken + ').')
14 39
40 if (accessTokenCache[bearerToken] !== undefined) return accessTokenCache[bearerToken]
41
15 return OAuthTokenModel.getByTokenAndPopulateUser(bearerToken) 42 return OAuthTokenModel.getByTokenAndPopulateUser(bearerToken)
43 .then(tokenModel => {
44 if (tokenModel) {
45 accessTokenCache[ bearerToken ] = tokenModel
46 userHavingToken[ tokenModel.userId ] = tokenModel.accessToken
47 }
48
49 return tokenModel
50 })
16} 51}
17 52
18function getClient (clientId: string, clientSecret: string) { 53function getClient (clientId: string, clientSecret: string) {
@@ -48,6 +83,8 @@ async function getUser (usernameOrEmail: string, password: string) {
48async function revokeToken (tokenInfo: TokenInfo) { 83async function revokeToken (tokenInfo: TokenInfo) {
49 const token = await OAuthTokenModel.getByRefreshTokenAndPopulateUser(tokenInfo.refreshToken) 84 const token = await OAuthTokenModel.getByRefreshTokenAndPopulateUser(tokenInfo.refreshToken)
50 if (token) { 85 if (token) {
86 clearCacheByToken(token.accessToken)
87
51 token.destroy() 88 token.destroy()
52 .catch(err => logger.error('Cannot destroy token when revoking token.', { err })) 89 .catch(err => logger.error('Cannot destroy token when revoking token.', { err }))
53 } 90 }
@@ -85,6 +122,9 @@ async function saveToken (token: TokenInfo, client: OAuthClientModel, user: User
85 122
86// See https://github.com/oauthjs/node-oauth2-server/wiki/Model-specification for the model specifications 123// See https://github.com/oauthjs/node-oauth2-server/wiki/Model-specification for the model specifications
87export { 124export {
125 deleteUserToken,
126 clearCacheByUserId,
127 clearCacheByToken,
88 getAccessToken, 128 getAccessToken,
89 getClient, 129 getClient,
90 getRefreshToken, 130 getRefreshToken,
diff --git a/server/models/account/user.ts b/server/models/account/user.ts
index 680b1d52d..e56b0bf40 100644
--- a/server/models/account/user.ts
+++ b/server/models/account/user.ts
@@ -1,5 +1,7 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import { 2import {
3 AfterDelete,
4 AfterUpdate,
3 AllowNull, 5 AllowNull,
4 BeforeCreate, 6 BeforeCreate,
5 BeforeUpdate, 7 BeforeUpdate,
@@ -39,6 +41,7 @@ import { AccountModel } from './account'
39import { NSFWPolicyType } from '../../../shared/models/videos/nsfw-policy.type' 41import { NSFWPolicyType } from '../../../shared/models/videos/nsfw-policy.type'
40import { values } from 'lodash' 42import { values } from 'lodash'
41import { NSFW_POLICY_TYPES } from '../../initializers' 43import { NSFW_POLICY_TYPES } from '../../initializers'
44import { clearCacheByUserId } from '../../lib/oauth-model'
42 45
43enum ScopeNames { 46enum ScopeNames {
44 WITH_VIDEO_CHANNEL = 'WITH_VIDEO_CHANNEL' 47 WITH_VIDEO_CHANNEL = 'WITH_VIDEO_CHANNEL'
@@ -168,6 +171,12 @@ export class UserModel extends Model<UserModel> {
168 } 171 }
169 } 172 }
170 173
174 @AfterUpdate
175 @AfterDelete
176 static removeTokenCache (instance: UserModel) {
177 return clearCacheByUserId(instance.id)
178 }
179
171 static countTotal () { 180 static countTotal () {
172 return this.count() 181 return this.count()
173 } 182 }
diff --git a/server/models/oauth/oauth-token.ts b/server/models/oauth/oauth-token.ts
index 1dd5e0289..ef9592c04 100644
--- a/server/models/oauth/oauth-token.ts
+++ b/server/models/oauth/oauth-token.ts
@@ -1,10 +1,23 @@
1import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' 1import {
2 AfterDelete,
3 AfterUpdate,
4 AllowNull,
5 BelongsTo,
6 Column,
7 CreatedAt,
8 ForeignKey,
9 Model,
10 Scopes,
11 Table,
12 UpdatedAt
13} from 'sequelize-typescript'
2import { logger } from '../../helpers/logger' 14import { logger } from '../../helpers/logger'
3import { UserModel } from '../account/user' 15import { UserModel } from '../account/user'
4import { OAuthClientModel } from './oauth-client' 16import { OAuthClientModel } from './oauth-client'
5import { Transaction } from 'sequelize' 17import { Transaction } from 'sequelize'
6import { AccountModel } from '../account/account' 18import { AccountModel } from '../account/account'
7import { ActorModel } from '../activitypub/actor' 19import { ActorModel } from '../activitypub/actor'
20import { clearCacheByToken } from '../../lib/oauth-model'
8 21
9export type OAuthTokenInfo = { 22export type OAuthTokenInfo = {
10 refreshToken: string 23 refreshToken: string
@@ -112,6 +125,12 @@ export class OAuthTokenModel extends Model<OAuthTokenModel> {
112 }) 125 })
113 OAuthClients: OAuthClientModel[] 126 OAuthClients: OAuthClientModel[]
114 127
128 @AfterUpdate
129 @AfterDelete
130 static removeTokenCache (token: OAuthTokenModel) {
131 return clearCacheByToken(token.accessToken)
132 }
133
115 static getByRefreshTokenAndPopulateClient (refreshToken: string) { 134 static getByRefreshTokenAndPopulateClient (refreshToken: string) {
116 const query = { 135 const query = {
117 where: { 136 where: {