Merge branch 'hotfix/21.56.9' into master
[gitter.git] / server / api / private / transloadit-signature.js
blob86182c8d4c7426f18d5c5b318e7e5c4c1e94e1c7
1 'use strict';
3 var env = require('gitter-web-env');
4 var nconf = env.config;
5 var redis = require('gitter-web-utils/lib/redis');
6 var uuid = require('uuid/v4');
7 var StatusError = require('statuserror');
8 var Promise = require('bluebird');
9 const policyFactory = require('gitter-web-permissions/lib/policy-factory');
11 var singletonTransloaditClient;
13 function getTransloaditClient() {
14   if (singletonTransloaditClient) {
15     return singletonTransloaditClient;
16   }
18   var TransloaditClient = require('transloadit');
19   singletonTransloaditClient = new TransloaditClient({
20     authKey: nconf.get('transloadit:key'),
21     authSecret: nconf.get('transloadit:secret')
22   });
24   return singletonTransloaditClient;
27 var redisClient = redis.getClient();
29 function randomString(length) {
30   var chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
31   var result = '';
32   for (var i = length; i > 0; --i) result += chars[Math.round(Math.random() * (chars.length - 1))];
33   return result;
36 async function parseAndValidateTransloadit(user, input) {
37   var templateId;
39   switch (input.type) {
40     case 'image':
41       templateId = nconf.get('transloadit:template_image_id');
42       break;
44     case 'avatar':
45       templateId = nconf.get('transloadit:template_avatar_id');
46       break;
48     default:
49       // default to a document type
50       templateId = nconf.get('transloadit:template_id');
51       break;
52   }
54   /*
55   NOTE: All the ngonf config vars used above are defined in all the config
56   files, but for some reason the old code validated that template_image_id is
57   truthy before using it, so just making sure here for now because using the
58   document template type in place of an avatar would just be broken anyway.
59   */
60   if (!templateId) {
61     throw new StatusError(500, 'templateId required');
62   }
64   var params = {
65     auth: {},
66     template_id: templateId,
67     fields: {},
68     steps: {}
69   };
71   var metadata = {
72     type: input.type,
73     user_id: user.id
74   };
76   // NOTE: This doesn't actually check that room or group uri makes sense for
77   // room or group id.
79   if (input.room_id) {
80     // Any member of the room who can write a message, can upload something to a room.
81     const policy = await policyFactory.createPolicyForRoomId(user, input.room_id);
82     const writeAccess = await policy.canWrite();
83     if (!writeAccess) {
84       throw new StatusError(403);
85     }
87     // upload a document or image to a room
88     metadata.room_id = input.room_id;
90     params.auth.max_size = 20971520; // 20MB
91     params.fields.room_id = input.room_id;
92     params.steps.export_originals = {
93       path: '${fields.room_id}/${fields.token}/${file.url_name}'
94     };
95     params.steps.export_thumbs = {
96       path: '${fields.room_id}/${fields.token}/thumb/${file.url_name}'
97     };
98   } else if (input.type === 'avatar' && input.group_id) {
99     const policy = await policyFactory.createPolicyForGroupId(user, input.group_id);
100     const writeAccess = await policy.canAdmin();
101     if (!writeAccess) {
102       throw new StatusError(403);
103     }
105     // upload an avatar to a group
106     metadata.group_id = input.group_id;
108     params.auth.max_size = 5242880; // 5MB
110     params.steps.export_original = {
111       path: 'groups/' + input.group_id + '/original',
112       bucket: nconf.get('transloadit:avatars:bucket')
113     };
114     params.steps.export_thumbs = {
115       path: 'groups/' + input.group_id + '/${file.meta.width}',
116       bucket: nconf.get('transloadit:avatars:bucket')
117     };
118   } else {
119     throw new StatusError(400, 'room or group info required');
120   }
122   return {
123     params: params,
124     metadata: metadata
125   };
128 function transloaditSignature(req, res, next) {
129   return Promise.try(async () => {
130     const info = await parseAndValidateTransloadit(req.user, req.query);
132     var params = info.params;
133     var metadata = info.metadata;
135     var apiBasePath = nconf.get('web:apiBasePath');
136     var token = uuid();
138     params.fields.token = randomString(4);
139     params.notify_url = apiBasePath + '/private/transloadit/' + token;
141     // Store the token temporarily to verify Transloadit callback
142     var expiry = 30 * 60; // 30 mins to be safe, S3 uploads, etc
143     redisClient.setex('transloadit:' + token, expiry, JSON.stringify(metadata));
145     var signed = getTransloaditClient().calcSignature(params);
146     res.send({
147       sig: signed.signature,
148       params: signed.params
149     });
150   }).catch(next);
153 module.exports = transloaditSignature;
154 module.exports.testOnly = {
155   parseAndValidateTransloadit: parseAndValidateTransloadit