mirror of
https://github.com/YunoHost-Apps/rocketchat_ynh.git
synced 2024-09-03 20:16:25 +02:00
1054 lines
94 KiB
JavaScript
1054 lines
94 KiB
JavaScript
(function () {
|
|
|
|
/* Imports */
|
|
var Meteor = Package.meteor.Meteor;
|
|
var _ = Package.underscore._;
|
|
var check = Package.check.check;
|
|
var Match = Package.check.Match;
|
|
|
|
/* Package-scope variables */
|
|
var Slingshot, matchAllowedFileTypes;
|
|
|
|
(function(){
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// packages/edgee_slingshot/packages/edgee_slingshot.js //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
(function () { // 1
|
|
// 2
|
|
/////////////////////////////////////////////////////////////////////////////////// // 3
|
|
// // // 4
|
|
// packages/edgee:slingshot/lib/restrictions.js // // 5
|
|
// // // 6
|
|
/////////////////////////////////////////////////////////////////////////////////// // 7
|
|
// // 8
|
|
/** // 1 // 9
|
|
* @module meteor-slingshot // 2 // 10
|
|
*/ // 3 // 11
|
|
// 4 // 12
|
|
Slingshot = {}; // 5 // 13
|
|
// 6 // 14
|
|
/* global matchAllowedFileTypes: true */ // 7 // 15
|
|
matchAllowedFileTypes = Match.OneOf(String, [String], RegExp, null); // 8 // 16
|
|
// 9 // 17
|
|
/** // 10 // 18
|
|
* List of configured restrictions by name. // 11 // 19
|
|
* // 12 // 20
|
|
* @type {Object.<String, Function>} // 13 // 21
|
|
* @private // 14 // 22
|
|
*/ // 15 // 23
|
|
// 16 // 24
|
|
Slingshot._restrictions = {}; // 17 // 25
|
|
// 18 // 26
|
|
/** // 19 // 27
|
|
* Creates file upload restrictions for a specific directive. // 20 // 28
|
|
* // 21 // 29
|
|
* @param {string} name - A unique identifier of the directive. // 22 // 30
|
|
* @param {Object} restrictions - The file upload restrictions. // 23 // 31
|
|
* @returns {Object} // 24 // 32
|
|
*/ // 25 // 33
|
|
// 26 // 34
|
|
Slingshot.fileRestrictions = function (name, restrictions) { // 27 // 35
|
|
check(restrictions, { // 28 // 36
|
|
authorize: Match.Optional(Function), // 29 // 37
|
|
maxSize: Match.Optional(Match.OneOf(Number, null)), // 30 // 38
|
|
allowedFileTypes: Match.Optional(matchAllowedFileTypes) // 31 // 39
|
|
}); // 32 // 40
|
|
// 33 // 41
|
|
if (Meteor.isServer) { // 34 // 42
|
|
var directive = Slingshot.getDirective(name); // 35 // 43
|
|
if (directive) { // 36 // 44
|
|
_.extend(directive._directive, restrictions); // 37 // 45
|
|
} // 38 // 46
|
|
} // 39 // 47
|
|
// 40 // 48
|
|
return (Slingshot._restrictions[name] = // 41 // 49
|
|
_.extend(Slingshot._restrictions[name] || {}, restrictions)); // 42 // 50
|
|
}; // 43 // 51
|
|
// 44 // 52
|
|
/** // 45 // 53
|
|
* @param {string} name - The unique identifier of the directive to // 46 // 54
|
|
* retrieve the restrictions for. // 47 // 55
|
|
* @returns {Object} // 48 // 56
|
|
*/ // 49 // 57
|
|
// 50 // 58
|
|
Slingshot.getRestrictions = function (name) { // 51 // 59
|
|
return this._restrictions[name] || {}; // 52 // 60
|
|
}; // 53 // 61
|
|
// 54 // 62
|
|
/////////////////////////////////////////////////////////////////////////////////// // 63
|
|
// 64
|
|
}).call(this); // 65
|
|
// 66
|
|
// 67
|
|
// 68
|
|
// 69
|
|
// 70
|
|
// 71
|
|
(function () { // 72
|
|
// 73
|
|
/////////////////////////////////////////////////////////////////////////////////// // 74
|
|
// // // 75
|
|
// packages/edgee:slingshot/lib/validators.js // // 76
|
|
// // // 77
|
|
/////////////////////////////////////////////////////////////////////////////////// // 78
|
|
// // 79
|
|
Slingshot.Validators = { // 1 // 80
|
|
// 2 // 81
|
|
/** // 3 // 82
|
|
* // 4 // 83
|
|
* @method checkAll // 5 // 84
|
|
* // 6 // 85
|
|
* @throws Meteor.Error // 7 // 86
|
|
* // 8 // 87
|
|
* @param {Object} context // 9 // 88
|
|
* @param {FileInfo} file // 10 // 89
|
|
* @param {Object} [meta] // 11 // 90
|
|
* @param {Object} [restrictions] // 12 // 91
|
|
* // 13 // 92
|
|
* @returns {Boolean} // 14 // 93
|
|
*/ // 15 // 94
|
|
// 16 // 95
|
|
checkAll: function (context, file, meta, restrictions) { // 17 // 96
|
|
return this.checkFileSize(file.size, restrictions.maxSize) && // 18 // 97
|
|
this.checkFileType(file.type, restrictions.allowedFileTypes) && // 19 // 98
|
|
(typeof restrictions.authorize !== 'function' || // 20 // 99
|
|
restrictions.authorize.call(context, file, meta)); // 21 // 100
|
|
}, // 22 // 101
|
|
// 23 // 102
|
|
/** // 24 // 103
|
|
* @throws Meteor.Error // 25 // 104
|
|
* // 26 // 105
|
|
* @param {Number} size - Size of file in bytes. // 27 // 106
|
|
* @param {Number} maxSize - Max size of file in bytes. // 28 // 107
|
|
* @returns {boolean} // 29 // 108
|
|
*/ // 30 // 109
|
|
// 31 // 110
|
|
checkFileSize: function (size, maxSize) { // 32 // 111
|
|
maxSize = Math.min(maxSize, Infinity); // 33 // 112
|
|
// 34 // 113
|
|
if (maxSize && size > maxSize) // 35 // 114
|
|
throw new Meteor.Error("Upload denied", "File exceeds allowed size of " + // 36 // 115
|
|
formatBytes(maxSize)); // 37 // 116
|
|
// 38 // 117
|
|
return true; // 39 // 118
|
|
}, // 40 // 119
|
|
// 41 // 120
|
|
/** // 42 // 121
|
|
* // 43 // 122
|
|
* @throws Meteor.Error // 44 // 123
|
|
* // 45 // 124
|
|
* @param {String} type - Mime type // 46 // 125
|
|
* @param {(RegExp|Array|String)} [allowed] - Allowed file type(s) // 47 // 126
|
|
* @returns {boolean} // 48 // 127
|
|
*/ // 49 // 128
|
|
// 50 // 129
|
|
checkFileType: function (type, allowed) { // 51 // 130
|
|
if (allowed instanceof RegExp) { // 52 // 131
|
|
// 53 // 132
|
|
if (!allowed.test(type)) // 54 // 133
|
|
throw new Meteor.Error("Upload denied", // 55 // 134
|
|
type + " is not an allowed file type"); // 56 // 135
|
|
// 57 // 136
|
|
return true; // 58 // 137
|
|
} // 59 // 138
|
|
// 60 // 139
|
|
if (_.isArray(allowed)) { // 61 // 140
|
|
if (allowed.indexOf(type) < 0) { // 62 // 141
|
|
throw new Meteor.Error("Upload denied", // 63 // 142
|
|
type + " is not one of the followed allowed file types: " + // 64 // 143
|
|
allowed.join(", ")); // 65 // 144
|
|
} // 66 // 145
|
|
// 67 // 146
|
|
return true; // 68 // 147
|
|
} // 69 // 148
|
|
// 70 // 149
|
|
if (allowed && allowed !== type) { // 71 // 150
|
|
throw new Meteor.Error("Upload denied", "Only files of type " + allowed + // 72 // 151
|
|
" can be uploaded"); // 73 // 152
|
|
} // 74 // 153
|
|
// 75 // 154
|
|
return true; // 76 // 155
|
|
} // 77 // 156
|
|
}; // 78 // 157
|
|
// 79 // 158
|
|
/** Human readable data-size in bytes. // 80 // 159
|
|
* // 81 // 160
|
|
* @param size {Number} // 82 // 161
|
|
* @returns {string} // 83 // 162
|
|
*/ // 84 // 163
|
|
// 85 // 164
|
|
function formatBytes(size) { // 86 // 165
|
|
var units = ['Bytes', 'KB', 'MB', 'GB', 'TB'], // 87 // 166
|
|
unit = units.shift(); // 88 // 167
|
|
// 89 // 168
|
|
while (size >= 0x400 && units.length) { // 90 // 169
|
|
size /= 0x400; // 91 // 170
|
|
unit = units.shift(); // 92 // 171
|
|
} // 93 // 172
|
|
// 94 // 173
|
|
return (Math.round(size * 100) / 100) + " " + unit; // 95 // 174
|
|
} // 96 // 175
|
|
// 97 // 176
|
|
/////////////////////////////////////////////////////////////////////////////////// // 177
|
|
// 178
|
|
}).call(this); // 179
|
|
// 180
|
|
// 181
|
|
// 182
|
|
// 183
|
|
// 184
|
|
// 185
|
|
(function () { // 186
|
|
// 187
|
|
/////////////////////////////////////////////////////////////////////////////////// // 188
|
|
// // // 189
|
|
// packages/edgee:slingshot/lib/directive.js // // 190
|
|
// // // 191
|
|
/////////////////////////////////////////////////////////////////////////////////// // 192
|
|
// // 193
|
|
/** // 1 // 194
|
|
* @callback Directive~authorize // 2 // 195
|
|
* // 3 // 196
|
|
* The meteor method context is passed on to this function, including // 4 // 197
|
|
* this.userId // 5 // 198
|
|
* // 6 // 199
|
|
* @throws Meteor.Error // 7 // 200
|
|
* // 8 // 201
|
|
* @param {{size: Number, type: String, name: String}} file - File to be // 9 // 202
|
|
* uploaded // 10 // 203
|
|
* @param {Object} [meta] - Meta information provided by the client. // 11 // 204
|
|
* // 12 // 205
|
|
* @returns Boolean Return true to authorize the requested upload. // 13 // 206
|
|
*/ // 14 // 207
|
|
// 15 // 208
|
|
/** // 16 // 209
|
|
* @typedef {Object} Directive // 17 // 210
|
|
* // 18 // 211
|
|
* @property {Number} maxSize - Maximum size in bytes // 19 // 212
|
|
* @property {(string, Array.<String>, RegExp, null)} allowedFileTypes - MIME // 20 // 213
|
|
* types that can be uploaded. If null is passed, then all file types are // 21 // 214
|
|
* allowed. // 22 // 215
|
|
* // 23 // 216
|
|
* @property {Directive~authorize} authorize - Function to determine whether a // 24 // 217
|
|
* file-upload is authorized or not. // 25 // 218
|
|
* // 26 // 219
|
|
* @property {String} [cacheControl] - rfc2616 Cache-Control directive (if // 27 // 220
|
|
* applicable to the selected storage service) // 28 // 221
|
|
* // 29 // 222
|
|
* @property {String} [contentDisposition] - rfc2616 Content-Disposition // 30 // 223
|
|
* directive. Defaults to "inline; <uploaded file name>" // 31 // 224
|
|
* // 32 // 225
|
|
* @property {String} // 33 // 226
|
|
*/ // 34 // 227
|
|
// 35 // 228
|
|
/** // 36 // 229
|
|
* @typedef {Object} FileInfo // 37 // 230
|
|
* // 38 // 231
|
|
* @property {String} [name] - Given name to the file. // 39 // 232
|
|
* @property {Number} size - File-size in bytes. // 40 // 233
|
|
* @property {String} [type] - mime type. // 41 // 234
|
|
* // 42 // 235
|
|
*/ // 43 // 236
|
|
// 44 // 237
|
|
/** // 45 // 238
|
|
* @typedef {Object} UploadInstructions // 46 // 239
|
|
* // 47 // 240
|
|
* @property {String} upload - POST URL // 48 // 241
|
|
* @property {String} download - Download URL // 49 // 242
|
|
* @property {Array.<{name: String, value: Object}>} postData - POST data to be // 50 // 243
|
|
* transferred to storage service along with credentials. // 51 // 244
|
|
*/ // 52 // 245
|
|
// 53 // 246
|
|
/** // 54 // 247
|
|
* List of installed directives by name. // 55 // 248
|
|
* // 56 // 249
|
|
* @type {Object.<string, Directive>} // 57 // 250
|
|
* @private // 58 // 251
|
|
*/ // 59 // 252
|
|
// 60 // 253
|
|
Slingshot._directives = {}; // 61 // 254
|
|
// 62 // 255
|
|
/** // 63 // 256
|
|
* Creates file upload directive that defines a set of rule by which a file may // 64 // 257
|
|
* be uploaded. // 65 // 258
|
|
* // 66 // 259
|
|
* @param {string} name - A unique identifier of the directive. // 67 // 260
|
|
* @param {Object} service - A storage service to use. // 68 // 261
|
|
* @param {Directive} options // 69 // 262
|
|
* @returns {Slingshot.Directive} // 70 // 263
|
|
*/ // 71 // 264
|
|
// 72 // 265
|
|
Slingshot.createDirective = function (name, service, options) { // 73 // 266
|
|
if (_.has(Slingshot._directives, name)) // 74 // 267
|
|
throw new Error("Directive '" + name + "' already exists"); // 75 // 268
|
|
// 76 // 269
|
|
var restrictions = Slingshot.getRestrictions(name); // 77 // 270
|
|
_.defaults(options, restrictions); // 78 // 271
|
|
// 79 // 272
|
|
return (Slingshot._directives[name] = // 80 // 273
|
|
new Slingshot.Directive(service, options)); // 81 // 274
|
|
}; // 82 // 275
|
|
// 83 // 276
|
|
/** // 84 // 277
|
|
* @param {string} name - The unique identifier of the directive to be // 85 // 278
|
|
* retrieved. // 86 // 279
|
|
* @returns {Slingshot.Directive} // 87 // 280
|
|
*/ // 88 // 281
|
|
// 89 // 282
|
|
Slingshot.getDirective = function (name) { // 90 // 283
|
|
return this._directives[name]; // 91 // 284
|
|
}; // 92 // 285
|
|
// 93 // 286
|
|
/** // 94 // 287
|
|
* @param {Object} service // 95 // 288
|
|
* @param {Directive} directive // 96 // 289
|
|
* @constructor // 97 // 290
|
|
*/ // 98 // 291
|
|
// 99 // 292
|
|
Slingshot.Directive = function (service, directive) { // 100
|
|
check(this, Slingshot.Directive); // 101
|
|
// 102
|
|
//service does not have to be a plain-object, so checking fields individually // 103
|
|
check(service.directiveMatch, Object); // 104
|
|
check(service.upload, Function); // 105
|
|
check(service.maxSize, Match.Optional(Number)); // 106
|
|
check(service.allowedFileTypes, Match.Optional(matchAllowedFileTypes)); // 107
|
|
// 108
|
|
_.defaults(directive, service.directiveDefault); // 109
|
|
// 110
|
|
check(directive, _.extend({ // 111
|
|
authorize: Function, // 112
|
|
maxSize: Match.Where(function (size) { // 113
|
|
check(size, Match.OneOf(Number, null)); // 114
|
|
// 115
|
|
return !size || size > 0 && size <= (service.maxSize || Infinity); // 116
|
|
}), // 117
|
|
allowedFileTypes: matchAllowedFileTypes, // 118
|
|
cdn: Match.Optional(String) // 119
|
|
}, service.directiveMatch)); // 120
|
|
// 121
|
|
/** // 122
|
|
* @method storageService // 123
|
|
* @returns {Object} // 124
|
|
*/ // 125
|
|
// 126
|
|
this.storageService = function () { // 127
|
|
return service; // 128
|
|
}; // 129
|
|
// 130
|
|
/** // 131
|
|
* @private // 132
|
|
* @property {Directive} _directive // 133
|
|
*/ // 134
|
|
// 135
|
|
this._directive = directive; // 136
|
|
}; // 137
|
|
// 138
|
|
_.extend(Slingshot.Directive.prototype, { // 139
|
|
// 140
|
|
/** // 141
|
|
* @param {{userId: String}} method // 142
|
|
* @param {FileInfo} file // 143
|
|
* @param {Object} [meta] // 144
|
|
* // 145
|
|
* @returns UploadInstructions // 146
|
|
*/ // 147
|
|
// 148
|
|
getInstructions: function (method, file, meta) { // 149
|
|
var instructions = this.storageService().upload(method, this._directive, // 150
|
|
file, meta); // 151
|
|
// 152
|
|
check(instructions, { // 153
|
|
upload: String, // 154
|
|
download: String, // 155
|
|
postData: [{ // 156
|
|
name: String, // 157
|
|
value: Match.OneOf(String, Number, null) // 158
|
|
}], // 159
|
|
headers: Match.Optional(Object) // 160
|
|
}); // 161
|
|
// 162
|
|
return instructions; // 163
|
|
}, // 164
|
|
// 165
|
|
/** // 166
|
|
* // 167
|
|
* @method requestAuthorization // 168
|
|
* // 169
|
|
* @throws Meteor.Error // 170
|
|
* // 171
|
|
* @param {Object} context // 172
|
|
* @param {FileInfo} file // 173
|
|
* @param {Object} [meta] // 174
|
|
* // 175
|
|
* @returns {Boolean} // 176
|
|
*/ // 177
|
|
// 178
|
|
requestAuthorization: function (context, file, meta) { // 179
|
|
var validators = Slingshot.Validators, // 180
|
|
restrictions = _.pick(this._directive, // 181
|
|
['authorize', 'maxSize', 'allowedFileTypes'] // 182
|
|
); // 183
|
|
// 184
|
|
return validators.checkAll(context, file, meta, restrictions); // 185
|
|
} // 186
|
|
// 187
|
|
}); // 188
|
|
// 189
|
|
Meteor.methods({ // 190
|
|
/** // 191
|
|
* Requests to perform a file upload. // 192
|
|
* // 193
|
|
* @param {String} directiveName // 194
|
|
* @param {FileInfo} file // 195
|
|
* @param {Object} [meta] // 196
|
|
* // 197
|
|
* @returns {UploadInstructions} // 198
|
|
*/ // 199
|
|
// 200
|
|
"slingshot/uploadRequest": function (directiveName, file, meta) { // 201
|
|
check(directiveName, String); // 202
|
|
check(file, { // 203
|
|
type: Match.Optional(Match.Where(function (type) { // 204
|
|
check(type, String); // 205
|
|
return !type || /^[^\/]+\/[^\/]+$/.test(type); // 206
|
|
})), // 207
|
|
name: Match.Optional(String), // 208
|
|
size: Match.Where(function (size) { // 209
|
|
check(size, Number); // 210
|
|
return size >= 0; // 211
|
|
}) // 212
|
|
}); // 213
|
|
// 214
|
|
if (!file.type) // 215
|
|
delete file.type; // 216
|
|
// 217
|
|
check(meta, Match.Optional(Match.OneOf(Object, null))); // 218
|
|
// 219
|
|
var directive = Slingshot.getDirective(directiveName); // 220
|
|
// 221
|
|
if (!directive) { // 222
|
|
throw new Meteor.Error("Invalid directive", // 223
|
|
"The directive " + directiveName + " does not seem to exist"); // 224
|
|
} // 225
|
|
// 226
|
|
if (!directive.requestAuthorization(this, file, meta)) { // 227
|
|
throw new Meteor.Error("Unauthorized", "You are not allowed to " + // 228
|
|
"upload this file"); // 229
|
|
} // 230
|
|
// 231
|
|
return directive.getInstructions(this, file, meta); // 232
|
|
} // 233
|
|
}); // 234
|
|
// 235
|
|
function quoteString(string, quotes) { // 236
|
|
return quotes + string.replace(quotes, '\\' + quotes) + quotes; // 237
|
|
} // 238
|
|
// 239
|
|
/////////////////////////////////////////////////////////////////////////////////// // 433
|
|
// 434
|
|
}).call(this); // 435
|
|
// 436
|
|
// 437
|
|
// 438
|
|
// 439
|
|
// 440
|
|
// 441
|
|
(function () { // 442
|
|
// 443
|
|
/////////////////////////////////////////////////////////////////////////////////// // 444
|
|
// // // 445
|
|
// packages/edgee:slingshot/lib/storage-policy.js // // 446
|
|
// // // 447
|
|
/////////////////////////////////////////////////////////////////////////////////// // 448
|
|
// // 449
|
|
// 1 // 450
|
|
/** // 2 // 451
|
|
* @constructor // 3 // 452
|
|
*/ // 4 // 453
|
|
// 5 // 454
|
|
Slingshot.StoragePolicy = function () { // 6 // 455
|
|
// 7 // 456
|
|
/** // 8 // 457
|
|
* @type {{[expiration]: String, conditions: Array.<(Object|Array)>}} // 9 // 458
|
|
*/ // 10 // 459
|
|
// 11 // 460
|
|
var policy = {conditions: []}; // 12 // 461
|
|
// 13 // 462
|
|
var self = this; // 14 // 463
|
|
// 15 // 464
|
|
_.extend(self, { // 16 // 465
|
|
// 17 // 466
|
|
/** Set policy expiration time (as an absolute value). // 18 // 467
|
|
* // 19 // 468
|
|
* Subsequent calls override previous expiration values. // 20 // 469
|
|
* // 21 // 470
|
|
* @param {Date} deadline // 22 // 471
|
|
* // 23 // 472
|
|
* @returns {Slingshot.StoragePolicy} // 24 // 473
|
|
*/ // 25 // 474
|
|
// 26 // 475
|
|
expire: function (deadline) { // 27 // 476
|
|
check(deadline, Date); // 28 // 477
|
|
// 29 // 478
|
|
policy.expiration = deadline.toISOString(); // 30 // 479
|
|
// 31 // 480
|
|
return self; // 32 // 481
|
|
}, // 33 // 482
|
|
// 34 // 483
|
|
// 35 // 484
|
|
/** Adds a constraint in which a property must equal a value. // 36 // 485
|
|
* // 37 // 486
|
|
* @param {(String|Object.<String, String>)} property // 38 // 487
|
|
* @param {String} [value] // 39 // 488
|
|
* // 40 // 489
|
|
* @returns {Slingshot.StoragePolicy} // 41 // 490
|
|
*/ // 42 // 491
|
|
// 43 // 492
|
|
match: function (property, value) { // 44 // 493
|
|
if (_.isObject(property)) { // 45 // 494
|
|
_.each(property, function (value, property) { // 46 // 495
|
|
self.match(property, value); // 47 // 496
|
|
}); // 48 // 497
|
|
} // 49 // 498
|
|
else if (property && !_.isUndefined(value)) { // 50 // 499
|
|
var constraint = {}; // 51 // 500
|
|
// 52 // 501
|
|
constraint[property] = value; // 53 // 502
|
|
// 54 // 503
|
|
policy.conditions.push(constraint); // 55 // 504
|
|
} // 56 // 505
|
|
// 57 // 506
|
|
return self; // 58 // 507
|
|
}, // 59 // 508
|
|
// 60 // 509
|
|
/** Set expiration time to a future value (relative from now) // 61 // 510
|
|
* // 62 // 511
|
|
* Subsequent calls override previous expiration values. // 63 // 512
|
|
* // 64 // 513
|
|
* @param {Number} ms - Number of milliseconds in the future. // 65 // 514
|
|
* // 66 // 515
|
|
* @return {Slingshot.StoragePolicy} // 67 // 516
|
|
*/ // 68 // 517
|
|
// 69 // 518
|
|
expireIn: function (ms) { // 70 // 519
|
|
return self.expire(new Date(Date.now() + ms)); // 71 // 520
|
|
}, // 72 // 521
|
|
// 73 // 522
|
|
/** Adds a starts-with constraint. // 74 // 523
|
|
* // 75 // 524
|
|
* @param {string} field - Name of the field without the preceding '$' // 76 // 525
|
|
* @param {string} constraint - Value that the field must start with // 77 // 526
|
|
* @returns {Slingshot.StoragePolicy} // 78 // 527
|
|
*/ // 79 // 528
|
|
// 80 // 529
|
|
startsWith: function (field, constraint) { // 81 // 530
|
|
policy.conditions.push(["starts-with", "$" + field, constraint]); // 82 // 531
|
|
return self; // 83 // 532
|
|
}, // 84 // 533
|
|
// 85 // 534
|
|
/** Adds a file-size constraint // 86 // 535
|
|
* // 87 // 536
|
|
* @param minimum {Number} Minimum file-size // 88 // 537
|
|
* @param maximum {Number} Maximum file-size // 89 // 538
|
|
* @returns {Slingshot.StoragePolicy} // 90 // 539
|
|
*/ // 91 // 540
|
|
// 92 // 541
|
|
contentLength: function (minimum, maximum) { // 93 // 542
|
|
policy.conditions.push(["content-length-range", minimum, maximum]); // 94 // 543
|
|
return self; // 95 // 544
|
|
}, // 96 // 545
|
|
// 97 // 546
|
|
/** // 98 // 547
|
|
* @returns {string} // 99 // 548
|
|
*/ // 100
|
|
// 101
|
|
stringify: function (encoding) { // 102
|
|
/* global Buffer: false */ // 103
|
|
return Buffer(JSON.stringify(policy), "utf-8") // 104
|
|
.toString(encoding || "base64"); // 105
|
|
} // 106
|
|
}); // 107
|
|
}; // 108
|
|
// 109
|
|
// 110
|
|
/////////////////////////////////////////////////////////////////////////////////// // 560
|
|
// 561
|
|
}).call(this); // 562
|
|
// 563
|
|
// 564
|
|
// 565
|
|
// 566
|
|
// 567
|
|
// 568
|
|
(function () { // 569
|
|
// 570
|
|
/////////////////////////////////////////////////////////////////////////////////// // 571
|
|
// // // 572
|
|
// packages/edgee:slingshot/services/aws-s3.js // // 573
|
|
// // // 574
|
|
/////////////////////////////////////////////////////////////////////////////////// // 575
|
|
// // 576
|
|
Slingshot.S3Storage = { // 1 // 577
|
|
// 2 // 578
|
|
accessId: "AWSAccessKeyId", // 3 // 579
|
|
secretKey: "AWSSecretAccessKey", // 4 // 580
|
|
// 5 // 581
|
|
directiveMatch: { // 6 // 582
|
|
bucket: String, // 7 // 583
|
|
bucketUrl: Match.OneOf(String, Function), // 8 // 584
|
|
// 9 // 585
|
|
region: Match.Where(function (region) { // 10 // 586
|
|
check(region, String); // 11 // 587
|
|
// 12 // 588
|
|
return /^[a-z]{2}-\w+-\d+$/.test(region); // 13 // 589
|
|
}), // 14 // 590
|
|
// 15 // 591
|
|
AWSAccessKeyId: String, // 16 // 592
|
|
AWSSecretAccessKey: String, // 17 // 593
|
|
// 18 // 594
|
|
acl: Match.Optional(Match.Where(function (acl) { // 19 // 595
|
|
check(acl, String); // 20 // 596
|
|
// 21 // 597
|
|
return [ // 22 // 598
|
|
"private", // 23 // 599
|
|
"public-read", // 24 // 600
|
|
"public-read-write", // 25 // 601
|
|
"authenticated-read", // 26 // 602
|
|
"bucket-owner-read", // 27 // 603
|
|
"bucket-owner-full-control", // 28 // 604
|
|
"log-delivery-write" // 29 // 605
|
|
].indexOf(acl) >= 0; // 30 // 606
|
|
})), // 31 // 607
|
|
// 32 // 608
|
|
key: Match.OneOf(String, Function), // 33 // 609
|
|
// 34 // 610
|
|
expire: Match.Where(function (expire) { // 35 // 611
|
|
check(expire, Number); // 36 // 612
|
|
// 37 // 613
|
|
return expire > 0; // 38 // 614
|
|
}), // 39 // 615
|
|
// 40 // 616
|
|
cacheControl: Match.Optional(String), // 41 // 617
|
|
contentDisposition: Match.Optional(Match.OneOf(String, Function, null)) // 42 // 618
|
|
}, // 43 // 619
|
|
// 44 // 620
|
|
directiveDefault: _.chain(Meteor.settings) // 45 // 621
|
|
.pick("AWSAccessKeyId", "AWSSecretAccessKey") // 46 // 622
|
|
.extend({ // 47 // 623
|
|
bucket: Meteor.settings.S3Bucket, // 48 // 624
|
|
bucketUrl: function (bucket, region) { // 49 // 625
|
|
var bucketDomain = "s3-" + region + ".amazonaws.com"; // 50 // 626
|
|
if (region === "us-east-1") // 51 // 627
|
|
bucketDomain = "s3.amazonaws.com"; // 52 // 628
|
|
// 53 // 629
|
|
if (bucket.indexOf(".") !== -1) // 54 // 630
|
|
return "https://" + bucketDomain + "/" + bucket; // 55 // 631
|
|
// 56 // 632
|
|
return "https://" + bucket + "." + bucketDomain; // 57 // 633
|
|
}, // 58 // 634
|
|
region: Meteor.settings.AWSRegion || "us-east-1", // 59 // 635
|
|
expire: 5 * 60 * 1000 //in 5 minutes // 60 // 636
|
|
}) // 61 // 637
|
|
.value(), // 62 // 638
|
|
// 63 // 639
|
|
getContentDisposition: function (method, directive, file, meta) { // 64 // 640
|
|
var getContentDisposition = directive.contentDisposition; // 65 // 641
|
|
// 66 // 642
|
|
if (!_.isFunction(getContentDisposition)) { // 67 // 643
|
|
getContentDisposition = function () { // 68 // 644
|
|
var filename = file.name && encodeURIComponent(file.name); // 69 // 645
|
|
// 70 // 646
|
|
return directive.contentDisposition || filename && // 71 // 647
|
|
"inline; filename=\"" + filename + "\"; filename*=utf-8''" + // 72 // 648
|
|
filename; // 73 // 649
|
|
}; // 74 // 650
|
|
} // 75 // 651
|
|
// 76 // 652
|
|
return getContentDisposition.call(method, file, meta); // 77 // 653
|
|
}, // 78 // 654
|
|
// 79 // 655
|
|
/** // 80 // 656
|
|
* // 81 // 657
|
|
* @param {{userId: String}} method // 82 // 658
|
|
* @param {Directive} directive // 83 // 659
|
|
* @param {FileInfo} file // 84 // 660
|
|
* @param {Object} [meta] // 85 // 661
|
|
* // 86 // 662
|
|
* @returns {UploadInstructions} // 87 // 663
|
|
*/ // 88 // 664
|
|
// 89 // 665
|
|
upload: function (method, directive, file, meta) { // 90 // 666
|
|
var policy = new Slingshot.StoragePolicy() // 91 // 667
|
|
.expireIn(directive.expire) // 92 // 668
|
|
.contentLength(0, Math.min(file.size, directive.maxSize || Infinity)), // 93 // 669
|
|
// 94 // 670
|
|
payload = { // 95 // 671
|
|
key: _.isFunction(directive.key) ? // 96 // 672
|
|
directive.key.call(method, file, meta) : directive.key, // 97 // 673
|
|
// 98 // 674
|
|
bucket: directive.bucket, // 99 // 675
|
|
// 100
|
|
"Content-Type": file.type, // 101
|
|
"acl": directive.acl, // 102
|
|
// 103
|
|
"Cache-Control": directive.cacheControl, // 104
|
|
"Content-Disposition": this.getContentDisposition(method, directive, // 105
|
|
file, meta) // 106
|
|
}, // 107
|
|
// 108
|
|
bucketUrl = _.isFunction(directive.bucketUrl) ? // 109
|
|
directive.bucketUrl(directive.bucket, directive.region) : // 110
|
|
directive.bucketUrl, // 111
|
|
// 112
|
|
downloadUrl = [ // 113
|
|
(directive.cdn || bucketUrl), // 114
|
|
payload.key // 115
|
|
].map(function (part) { // 116
|
|
return part.replace(/\/+$/, ''); // 117
|
|
}).join("/"); // 118
|
|
// 119
|
|
this.applySignature(payload, policy, directive); // 120
|
|
// 121
|
|
return { // 122
|
|
upload: bucketUrl, // 123
|
|
download: downloadUrl, // 124
|
|
postData: [{ // 125
|
|
name: "key", // 126
|
|
value: payload.key // 127
|
|
}].concat(_.chain(payload).omit("key").map(function (value, name) { // 128
|
|
return !_.isUndefined(value) && { // 129
|
|
name: name, // 130
|
|
value: value // 131
|
|
}; // 132
|
|
}).compact().value()) // 133
|
|
}; // 134
|
|
}, // 135
|
|
// 136
|
|
/** Applies signature an upload payload // 137
|
|
* // 138
|
|
* @param {Object} payload - Data to be upload along with file // 139
|
|
* @param {Slingshot.StoragePolicy} policy // 140
|
|
* @param {Directive} directive // 141
|
|
*/ // 142
|
|
// 143
|
|
applySignature: function (payload, policy, directive) { // 144
|
|
var now = new Date(), // 145
|
|
today = now.getUTCFullYear() + formatNumber(now.getUTCMonth() + 1, 2) + // 146
|
|
formatNumber(now.getUTCDate(), 2), // 147
|
|
service = "s3"; // 148
|
|
// 149
|
|
_.extend(payload, { // 150
|
|
"x-amz-algorithm": "AWS4-HMAC-SHA256", // 151
|
|
"x-amz-credential": [ // 152
|
|
directive[this.accessId], // 153
|
|
today, // 154
|
|
directive.region, // 155
|
|
service, // 156
|
|
"aws4_request" // 157
|
|
].join("/"), // 158
|
|
"x-amz-date": today + "T000000Z" // 159
|
|
}); // 160
|
|
// 161
|
|
payload.policy = policy.match(payload).stringify(); // 162
|
|
payload["x-amz-signature"] = this.signAwsV4(payload.policy, // 163
|
|
directive[this.secretKey], today, directive.region, service); // 164
|
|
}, // 165
|
|
// 166
|
|
/** Generate a AWS Signature Version 4 // 167
|
|
* // 168
|
|
* @param {String} policy - Base64 encoded policy to sign. // 169
|
|
* @param {String} secretKey - AWSSecretAccessKey // 170
|
|
* @param {String} date - Signature date (yyyymmdd) // 171
|
|
* @param {String} region - AWS Data-Center region // 172
|
|
* @param {String} service - type of service to use // 173
|
|
* @returns {String} hex encoded HMAC-256 signature // 174
|
|
*/ // 175
|
|
// 176
|
|
signAwsV4: function (policy, secretKey, date, region, service) { // 177
|
|
var dateKey = hmac256("AWS4" + secretKey, date), // 178
|
|
dateRegionKey = hmac256(dateKey, region), // 179
|
|
dateRegionServiceKey= hmac256(dateRegionKey, service), // 180
|
|
signingKey = hmac256(dateRegionServiceKey, "aws4_request"); // 181
|
|
// 182
|
|
return hmac256(signingKey, policy, "hex"); // 183
|
|
} // 184
|
|
}; // 185
|
|
// 186
|
|
Slingshot.S3Storage.TempCredentials = _.defaults({ // 187
|
|
// 188
|
|
directiveMatch: _.chain(Slingshot.S3Storage.directiveMatch) // 189
|
|
.omit("AWSAccessKeyId", "AWSSecretAccessKey") // 190
|
|
.extend({ // 191
|
|
temporaryCredentials: Function // 192
|
|
}) // 193
|
|
.value(), // 194
|
|
// 195
|
|
directiveDefault: _.omit(Slingshot.S3Storage.directiveDefault, // 196
|
|
"AWSAccessKeyId", "AWSSecretAccessKey"), // 197
|
|
// 198
|
|
applySignature: function (payload, policy, directive) { // 199
|
|
var credentials = directive.temporaryCredentials(directive.expire); // 200
|
|
// 201
|
|
check(credentials, Match.ObjectIncluding({ // 202
|
|
AccessKeyId: Slingshot.S3Storage.directiveMatch.AWSAccessKeyId, // 203
|
|
SecretAccessKey: Slingshot.S3Storage.directiveMatch.AWSSecretAccessKey, // 204
|
|
SessionToken: String // 205
|
|
})); // 206
|
|
// 207
|
|
payload["x-amz-security-token"] = credentials.SessionToken; // 208
|
|
// 209
|
|
return Slingshot.S3Storage.applySignature // 210
|
|
.call(this, payload, policy, _.defaults({ // 211
|
|
AWSAccessKeyId: credentials.AccessKeyId, // 212
|
|
AWSSecretAccessKey: credentials.SecretAccessKey // 213
|
|
}, directive)); // 214
|
|
} // 215
|
|
}, Slingshot.S3Storage); // 216
|
|
// 217
|
|
// 218
|
|
function formatNumber(num, digits) { // 219
|
|
var string = String(num); // 220
|
|
// 221
|
|
return Array(digits - string.length + 1).join("0").concat(string); // 222
|
|
} // 223
|
|
// 224
|
|
var crypto = Npm.require("crypto"); // 225
|
|
// 226
|
|
function hmac256(key, data, encoding) { // 227
|
|
/* global Buffer: false */ // 228
|
|
return crypto // 229
|
|
.createHmac("sha256", key) // 230
|
|
.update(new Buffer(data, "utf-8")) // 231
|
|
.digest(encoding); // 232
|
|
} // 233
|
|
// 234
|
|
/////////////////////////////////////////////////////////////////////////////////// // 811
|
|
// 812
|
|
}).call(this); // 813
|
|
// 814
|
|
// 815
|
|
// 816
|
|
// 817
|
|
// 818
|
|
// 819
|
|
(function () { // 820
|
|
// 821
|
|
/////////////////////////////////////////////////////////////////////////////////// // 822
|
|
// // // 823
|
|
// packages/edgee:slingshot/services/google-cloud.js // // 824
|
|
// // // 825
|
|
/////////////////////////////////////////////////////////////////////////////////// // 826
|
|
// // 827
|
|
//GoogleCloud is based on the very same api as AWS S3, so we extend it: // 1 // 828
|
|
// 2 // 829
|
|
Slingshot.GoogleCloud = _.defaults({ // 3 // 830
|
|
// 4 // 831
|
|
accessId: "GoogleAccessId", // 5 // 832
|
|
secretKey: "GoogleSecretKey", // 6 // 833
|
|
// 7 // 834
|
|
directiveMatch: _.chain(Slingshot.S3Storage.directiveMatch) // 8 // 835
|
|
.omit(Slingshot.S3Storage.accessId, Slingshot.S3Storage.secretKey, "region") // 9 // 836
|
|
.extend({ // 10 // 837
|
|
GoogleAccessId: String, // 11 // 838
|
|
GoogleSecretKey: String, // 12 // 839
|
|
// 13 // 840
|
|
acl: Match.Optional(Match.Where(function (acl) { // 14 // 841
|
|
check(acl, String); // 15 // 842
|
|
// 16 // 843
|
|
return [ // 17 // 844
|
|
"project-private", // 18 // 845
|
|
"private", // 19 // 846
|
|
"public-read", // 20 // 847
|
|
"public-read-write", // 21 // 848
|
|
"authenticated-read", // 22 // 849
|
|
"bucket-owner-read", // 23 // 850
|
|
"bucket-owner-full-control" // 24 // 851
|
|
].indexOf(acl) >= 0; // 25 // 852
|
|
})) // 26 // 853
|
|
}) // 27 // 854
|
|
.value(), // 28 // 855
|
|
// 29 // 856
|
|
directiveDefault: _.chain(Meteor.settings) // 30 // 857
|
|
.pick("GoogleAccessId") // 31 // 858
|
|
.extend(Slingshot.S3Storage.directiveDefault, { // 32 // 859
|
|
bucketUrl: function (bucket) { // 33 // 860
|
|
return "https://" + bucket + ".storage.googleapis.com"; // 34 // 861
|
|
} // 35 // 862
|
|
}) // 36 // 863
|
|
.omit(Slingshot.S3Storage.accessId, Slingshot.S3Storage.secretKey, "region") // 37 // 864
|
|
.value(), // 38 // 865
|
|
// 39 // 866
|
|
applySignature: function (payload, policy, directive) { // 40 // 867
|
|
payload[this.accessId] = directive[this.accessId]; // 41 // 868
|
|
payload.policy = policy.match(_.omit(payload, this.accessId)).stringify(); // 42 // 869
|
|
payload.signature = this.sign(directive[this.secretKey], payload.policy); // 43 // 870
|
|
}, // 44 // 871
|
|
// 45 // 872
|
|
/** // 46 // 873
|
|
* @param {String} secretKey - pem private key // 47 // 874
|
|
* @param {String} policy // 48 // 875
|
|
* @returns {*|String} // 49 // 876
|
|
*/ // 50 // 877
|
|
// 51 // 878
|
|
sign: function (secretKey, policy) { // 52 // 879
|
|
return Npm.require("crypto") // 53 // 880
|
|
.createSign('RSA-SHA256') // 54 // 881
|
|
.update(policy) // 55 // 882
|
|
.sign(secretKey, "base64"); // 56 // 883
|
|
} // 57 // 884
|
|
}, Slingshot.S3Storage); // 58 // 885
|
|
// 59 // 886
|
|
/////////////////////////////////////////////////////////////////////////////////// // 887
|
|
// 888
|
|
}).call(this); // 889
|
|
// 890
|
|
// 891
|
|
// 892
|
|
// 893
|
|
// 894
|
|
// 895
|
|
(function () { // 896
|
|
// 897
|
|
/////////////////////////////////////////////////////////////////////////////////// // 898
|
|
// // // 899
|
|
// packages/edgee:slingshot/services/rackspace.js // // 900
|
|
// // // 901
|
|
/////////////////////////////////////////////////////////////////////////////////// // 902
|
|
// // 903
|
|
Slingshot.RackspaceFiles = { // 1 // 904
|
|
// 2 // 905
|
|
directiveMatch: { // 3 // 906
|
|
RackspaceAccountId: String, // 4 // 907
|
|
RackspaceMetaDataKey: String, // 5 // 908
|
|
container: String, // 6 // 909
|
|
region: String, // 7 // 910
|
|
pathPrefix: Match.OneOf(String, Function), // 8 // 911
|
|
expire: Match.Where(function (expire) { // 9 // 912
|
|
check(expire, Number); // 10 // 913
|
|
// 11 // 914
|
|
return expire > 0; // 12 // 915
|
|
}), // 13 // 916
|
|
deleteAt: Match.Optional(Date), // 14 // 917
|
|
deleteAfter: Match.Optional(Number) // 15 // 918
|
|
}, // 16 // 919
|
|
// 17 // 920
|
|
directiveDefault: _.chain(Meteor.settings) // 18 // 921
|
|
.pick("RackspaceAccountId", "RackspaceMetaDataKey") // 19 // 922
|
|
.extend({ // 20 // 923
|
|
region: "iad3", // 21 // 924
|
|
expire: 5 * 60 * 1000 //in 5 minutes // 22 // 925
|
|
}) // 23 // 926
|
|
.value(), // 24 // 927
|
|
// 25 // 928
|
|
version: "v1", // 26 // 929
|
|
// 27 // 930
|
|
path: function (directive, prefix) { // 28 // 931
|
|
return "/" + [ // 29 // 932
|
|
this.version, // 30 // 933
|
|
"MossoCloudFS_" + directive.RackspaceAccountId, // 31 // 934
|
|
directive.container, // 32 // 935
|
|
prefix // 33 // 936
|
|
].join("/").replace(/\/+/, "/"); // 34 // 937
|
|
}, // 35 // 938
|
|
// 36 // 939
|
|
pathPrefix: function (method, directive, file, meta) { // 37 // 940
|
|
if ("pathPrefix" in directive) { // 38 // 941
|
|
return (_.isFunction(directive.pathPrefix) ? // 39 // 942
|
|
directive.pathPrefix.call(method, file, meta) : directive.pathPrefix); // 40 // 943
|
|
} // 41 // 944
|
|
else { // 42 // 945
|
|
return ""; // 43 // 946
|
|
} // 44 // 947
|
|
}, // 45 // 948
|
|
// 46 // 949
|
|
host: function (region) { // 47 // 950
|
|
return "https://storage101." + region + ".clouddrive.com"; // 48 // 951
|
|
}, // 49 // 952
|
|
// 50 // 953
|
|
maxSize: 0x140000000, //5GB // 51 // 954
|
|
// 52 // 955
|
|
upload: function (method, directive, file, meta) { // 53 // 956
|
|
var pathPrefix = this.pathPrefix(method, directive, file, meta), // 54 // 957
|
|
path = this.path(directive, pathPrefix), // 55 // 958
|
|
host = this.host(directive.region), // 56 // 959
|
|
url = host + path, // 57 // 960
|
|
data = [ // 58 // 961
|
|
{ // 59 // 962
|
|
name: "redirect", // 60 // 963
|
|
value: "" // 61 // 964
|
|
}, // 62 // 965
|
|
{ // 63 // 966
|
|
name: "max_file_size", // 64 // 967
|
|
value: Math.min(file.size, directive.maxSize || this.maxSize) // 65 // 968
|
|
}, // 66 // 969
|
|
{ // 67 // 970
|
|
name: "max_file_count", // 68 // 971
|
|
value: 1 // 69 // 972
|
|
}, // 70 // 973
|
|
{ // 71 // 974
|
|
name: "expires", // 72 // 975
|
|
value: Date.now() + directive.expire // 73 // 976
|
|
} // 74 // 977
|
|
]; // 75 // 978
|
|
// 76 // 979
|
|
data.push({ // 77 // 980
|
|
name: "signature", // 78 // 981
|
|
value: this.sign(directive.RackspaceMetaDataKey, path, data) // 79 // 982
|
|
}); // 80 // 983
|
|
// 81 // 984
|
|
if ("deleteAt" in directive) // 82 // 985
|
|
data.push({ // 83 // 986
|
|
name: "x_delete_at", // 84 // 987
|
|
value: directive.deleteAt.getTime() // 85 // 988
|
|
}); // 86 // 989
|
|
// 87 // 990
|
|
if ("deleteAfter" in directive) // 88 // 991
|
|
data.push({ // 89 // 992
|
|
name: "x_delete_after", // 90 // 993
|
|
value: Math.round(directive.deleteAfter / 1000) // 91 // 994
|
|
}); // 92 // 995
|
|
// 93 // 996
|
|
var cdn = directive.cdn; // 94 // 997
|
|
// 95 // 998
|
|
return { // 96 // 999
|
|
upload: url, // 97 // 1000
|
|
download: (cdn && cdn + "/" + pathPrefix || host + path) + file.name, // 98 // 1001
|
|
postData: data // 99 // 1002
|
|
}; // 100
|
|
}, // 101
|
|
// 102
|
|
sign: function (secretkey, path, data) { // 103
|
|
/* global Buffer: false */ // 104
|
|
var policy = path + "\n" + _.pluck(data, "value").join("\n"); // 105
|
|
// 106
|
|
return Npm.require("crypto") // 107
|
|
.createHmac("sha1", secretkey) // 108
|
|
.update(new Buffer(policy, "utf-8")) // 109
|
|
.digest("hex"); // 110
|
|
} // 111
|
|
// 112
|
|
}; // 113
|
|
// 114
|
|
/////////////////////////////////////////////////////////////////////////////////// // 1018
|
|
// 1019
|
|
}).call(this); // 1020
|
|
// 1021
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
}).call(this);
|
|
|
|
|
|
/* Exports */
|
|
if (typeof Package === 'undefined') Package = {};
|
|
Package['edgee:slingshot'] = {
|
|
Slingshot: Slingshot
|
|
};
|
|
|
|
})();
|
|
|
|
//# sourceMappingURL=edgee_slingshot.js.map
|