dce-reactkit 3.9.1 → 3.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,6 +9,12 @@ import LogFunction from '../types/LogFunction';
9
9
  * @param opts.handler function that processes the request
10
10
  * @param [opts.skipSessionCheck] if true, skip the session check (allow users
11
11
  * to not be logged in and launched via LTI)
12
+ * @param [opts.allowedHosts] if included, only allow requests from these hosts
13
+ * (start a hostname with a "*" to only check the end of the hostname)
14
+ * you can include just one string instead of an array
15
+ * @param [opts.bannedHosts] if included, do not allow requests from these hosts
16
+ * (start a hostname with a "*" to only check the end of the hostname)
17
+ * you can include just one string instead of an array
12
18
  * @param [opts.unhandledErrorMessagePrefix] if included, when an error that
13
19
  * is not of type ErrorWithCode is thrown, the client will receive an error
14
20
  * where the error message is prefixed with this string. For example,
@@ -63,6 +69,8 @@ declare const genRouteHandler: (opts: {
63
69
  logServerEvent: LogFunction;
64
70
  }) => any;
65
71
  skipSessionCheck?: boolean | undefined;
72
+ allowedHosts?: string | string[] | undefined;
73
+ bannedHosts?: string | string[] | undefined;
66
74
  unhandledErrorMessagePrefix?: string | undefined;
67
75
  }) => (req: any, res: any, next: () => void) => Promise<undefined>;
68
76
  export default genRouteHandler;
@@ -8,6 +8,8 @@ declare enum ReactKitErrorCode {
8
8
  SessionExpired = "DRK3",
9
9
  MissingParameter = "DRK4",
10
10
  InvalidParameter = "DRK5",
11
+ HostNotAllowed = "DRK17",
12
+ HostBanned = "DRK18",
11
13
  WrongCourse = "DRK6",
12
14
  NoCACCLSendRequestFunction = "DRK7",
13
15
  NoCACCLGetLaunchInfoFunction = "DRK8",
package/dist/esm/index.js CHANGED
@@ -31,7 +31,7 @@ function __awaiter(thisArg, _arguments, P, generator) {
31
31
  });
32
32
  }
33
33
 
34
- // Highest error code = DRK16
34
+ // Highest error code = DRK18
35
35
  /**
36
36
  * List of error codes built into the react kit
37
37
  * @author Gabe Abrams
@@ -43,6 +43,8 @@ var ReactKitErrorCode;
43
43
  ReactKitErrorCode["SessionExpired"] = "DRK3";
44
44
  ReactKitErrorCode["MissingParameter"] = "DRK4";
45
45
  ReactKitErrorCode["InvalidParameter"] = "DRK5";
46
+ ReactKitErrorCode["HostNotAllowed"] = "DRK17";
47
+ ReactKitErrorCode["HostBanned"] = "DRK18";
46
48
  ReactKitErrorCode["WrongCourse"] = "DRK6";
47
49
  ReactKitErrorCode["NoCACCLSendRequestFunction"] = "DRK7";
48
50
  ReactKitErrorCode["NoCACCLGetLaunchInfoFunction"] = "DRK8";
@@ -14441,6 +14443,12 @@ const parseUserAgent = (userAgent) => {
14441
14443
  * @param opts.handler function that processes the request
14442
14444
  * @param [opts.skipSessionCheck] if true, skip the session check (allow users
14443
14445
  * to not be logged in and launched via LTI)
14446
+ * @param [opts.allowedHosts] if included, only allow requests from these hosts
14447
+ * (start a hostname with a "*" to only check the end of the hostname)
14448
+ * you can include just one string instead of an array
14449
+ * @param [opts.bannedHosts] if included, do not allow requests from these hosts
14450
+ * (start a hostname with a "*" to only check the end of the hostname)
14451
+ * you can include just one string instead of an array
14444
14452
  * @param [opts.unhandledErrorMessagePrefix] if included, when an error that
14445
14453
  * is not of type ErrorWithCode is thrown, the client will receive an error
14446
14454
  * where the error message is prefixed with this string. For example,
@@ -14468,10 +14476,76 @@ const parseUserAgent = (userAgent) => {
14468
14476
  const genRouteHandler = (opts) => {
14469
14477
  // Return a route handler
14470
14478
  return (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
14471
- var _a, _b, _c;
14479
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
14472
14480
  // Output params
14473
14481
  const output = {};
14474
14482
  /*----------------------------------------*/
14483
+ /* ----------- Hostname Check ----------- */
14484
+ /*----------------------------------------*/
14485
+ // Get hostnames
14486
+ const originURL = String(req.get('origin')
14487
+ || req.headers.origin
14488
+ || req.headers.referer);
14489
+ const originHostname = (originURL
14490
+ // Remove protocol
14491
+ .replace(/(^\w+:|^)\/\//, '')
14492
+ // Remove port
14493
+ .replace(/:\d+$/, ''));
14494
+ const serverHostname = String(req.hostname);
14495
+ // Check allowed
14496
+ if (opts.allowedHosts) {
14497
+ // Only accept requests from allowed hosts
14498
+ const allowedArray = (Array.isArray(opts.allowedHosts)
14499
+ ? opts.allowedHosts
14500
+ : [opts.allowedHosts]);
14501
+ // Check if server is localhost
14502
+ if (serverHostname === 'localhost') {
14503
+ // Allow localhost
14504
+ allowedArray.push('localhost');
14505
+ }
14506
+ // Check if current host is allowed
14507
+ const allowed = allowedArray.some((allowedHost) => {
14508
+ if (allowedHost.startsWith('*')) {
14509
+ // Check end of hostname
14510
+ return originHostname.endsWith(allowedHost.substring(1));
14511
+ }
14512
+ // Check full hostname
14513
+ return originHostname.toLowerCase() === allowedHost.toLowerCase();
14514
+ });
14515
+ // If not allowed, return error
14516
+ if (!allowed) {
14517
+ return handleError(res, {
14518
+ message: 'You are not allowed to access this endpoint.',
14519
+ code: ReactKitErrorCode$1.HostNotAllowed,
14520
+ status: 403,
14521
+ });
14522
+ }
14523
+ }
14524
+ // Check banned
14525
+ if (opts.bannedHosts) {
14526
+ // Do not allow requests from banned hosts
14527
+ const bannedArray = (Array.isArray(opts.bannedHosts)
14528
+ ? opts.bannedHosts
14529
+ : [opts.bannedHosts]);
14530
+ // Check if current host is banned
14531
+ const banned = bannedArray.some((bannedHost) => {
14532
+ if (bannedHost.startsWith('*')) {
14533
+ // Check end of hostname
14534
+ return originHostname.endsWith(bannedHost.substring(1));
14535
+ }
14536
+ // Check full hostname
14537
+ return originHostname.toLowerCase() === bannedHost.toLowerCase();
14538
+ });
14539
+ // If banned, return error
14540
+ if (banned) {
14541
+ return handleError(res, {
14542
+ message: 'You are not allowed to access this endpoint.',
14543
+ code: ReactKitErrorCode$1.HostBanned,
14544
+ status: 403,
14545
+ });
14546
+ }
14547
+ }
14548
+ /*----------------------------------------*/
14475
14549
  /* ------------ Parse Params ------------ */
14476
14550
  /*----------------------------------------*/
14477
14551
  // Process items one by one
@@ -14669,34 +14743,34 @@ const genRouteHandler = (opts) => {
14669
14743
  // Add launch info to output
14670
14744
  output.userId = (launchInfo
14671
14745
  ? launchInfo.userId
14672
- : undefined);
14746
+ : ((_b = output.userId) !== null && _b !== void 0 ? _b : undefined));
14673
14747
  output.userFirstName = (launchInfo
14674
14748
  ? launchInfo.userFirstName
14675
- : undefined);
14749
+ : ((_c = output.userFirstName) !== null && _c !== void 0 ? _c : undefined));
14676
14750
  output.userLastName = (launchInfo
14677
14751
  ? launchInfo.userLastName
14678
- : undefined);
14752
+ : ((_d = output.userLastName) !== null && _d !== void 0 ? _d : undefined));
14679
14753
  output.userEmail = (launchInfo
14680
14754
  ? launchInfo.userEmail
14681
- : undefined);
14755
+ : ((_e = output.userEmail) !== null && _e !== void 0 ? _e : undefined));
14682
14756
  output.userAvatarURL = (launchInfo
14683
- ? ((_b = launchInfo.userImage) !== null && _b !== void 0 ? _b : 'http://www.gravatar.com/avatar/?d=identicon')
14684
- : undefined);
14757
+ ? ((_f = launchInfo.userImage) !== null && _f !== void 0 ? _f : 'http://www.gravatar.com/avatar/?d=identicon')
14758
+ : ((_g = output.userAvatarURL) !== null && _g !== void 0 ? _g : undefined));
14685
14759
  output.isLearner = (launchInfo
14686
14760
  ? !!launchInfo.isLearner
14687
- : undefined);
14761
+ : ((_h = output.isLearner) !== null && _h !== void 0 ? _h : undefined));
14688
14762
  output.isTTM = (launchInfo
14689
14763
  ? !!launchInfo.isTTM
14690
- : undefined);
14764
+ : ((_j = output.isTTM) !== null && _j !== void 0 ? _j : undefined));
14691
14765
  output.isAdmin = (launchInfo
14692
14766
  ? !!launchInfo.isAdmin
14693
- : undefined);
14767
+ : ((_k = output.isAdmin) !== null && _k !== void 0 ? _k : undefined));
14694
14768
  output.courseId = (launchInfo
14695
- ? ((_c = output.courseId) !== null && _c !== void 0 ? _c : launchInfo.courseId)
14696
- : undefined);
14769
+ ? ((_l = output.courseId) !== null && _l !== void 0 ? _l : launchInfo.courseId)
14770
+ : ((_m = output.courseId) !== null && _m !== void 0 ? _m : undefined));
14697
14771
  output.courseName = (launchInfo
14698
14772
  ? launchInfo.contextLabel
14699
- : undefined);
14773
+ : ((_o = output.courseName) !== null && _o !== void 0 ? _o : undefined));
14700
14774
  // Add other session variables
14701
14775
  Object.keys(req.session).forEach((propName) => {
14702
14776
  // Skip if prop already in output
@@ -14770,7 +14844,7 @@ const genRouteHandler = (opts) => {
14770
14844
  * @author Gabe Abrams
14771
14845
  */
14772
14846
  const logServerEvent = (logOpts) => __awaiter(void 0, void 0, void 0, function* () {
14773
- var _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
14847
+ var _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1;
14774
14848
  // NOTE: internally, we slip through an opts.overrideAsClientEvent boolean
14775
14849
  // that indicates that this is actually a client event, but we don't
14776
14850
  // include that in the LogFunction type because this is internal and
@@ -14802,24 +14876,24 @@ const genRouteHandler = (opts) => {
14802
14876
  timestamp,
14803
14877
  context: (typeof logOpts.context === 'string'
14804
14878
  ? logOpts.context
14805
- : ((_e = ((_d = logOpts.context) !== null && _d !== void 0 ? _d : {})._) !== null && _e !== void 0 ? _e : LogBuiltInMetadata.Context.Uncategorized)),
14806
- subcontext: ((_f = logOpts.subcontext) !== null && _f !== void 0 ? _f : LogBuiltInMetadata.Context.Uncategorized),
14807
- tags: ((_g = logOpts.tags) !== null && _g !== void 0 ? _g : []),
14808
- level: ((_h = logOpts.level) !== null && _h !== void 0 ? _h : LogLevel$1.Info),
14809
- metadata: ((_j = logOpts.metadata) !== null && _j !== void 0 ? _j : {}),
14879
+ : ((_q = ((_p = logOpts.context) !== null && _p !== void 0 ? _p : {})._) !== null && _q !== void 0 ? _q : LogBuiltInMetadata.Context.Uncategorized)),
14880
+ subcontext: ((_r = logOpts.subcontext) !== null && _r !== void 0 ? _r : LogBuiltInMetadata.Context.Uncategorized),
14881
+ tags: ((_s = logOpts.tags) !== null && _s !== void 0 ? _s : []),
14882
+ level: ((_t = logOpts.level) !== null && _t !== void 0 ? _t : LogLevel$1.Info),
14883
+ metadata: ((_u = logOpts.metadata) !== null && _u !== void 0 ? _u : {}),
14810
14884
  };
14811
14885
  // Type-specific info
14812
14886
  const typeSpecificInfo = (('error' in opts && opts.error)
14813
14887
  ? {
14814
14888
  type: LogType$1.Error,
14815
- errorMessage: (_k = logOpts.error.message) !== null && _k !== void 0 ? _k : 'Unknown message',
14816
- errorCode: (_l = logOpts.error.code) !== null && _l !== void 0 ? _l : ReactKitErrorCode$1.NoCode,
14817
- errorStack: (_m = logOpts.error.stack) !== null && _m !== void 0 ? _m : 'No stack',
14889
+ errorMessage: (_v = logOpts.error.message) !== null && _v !== void 0 ? _v : 'Unknown message',
14890
+ errorCode: (_w = logOpts.error.code) !== null && _w !== void 0 ? _w : ReactKitErrorCode$1.NoCode,
14891
+ errorStack: (_x = logOpts.error.stack) !== null && _x !== void 0 ? _x : 'No stack',
14818
14892
  }
14819
14893
  : {
14820
14894
  type: LogType$1.Action,
14821
- target: ((_o = logOpts.target) !== null && _o !== void 0 ? _o : LogBuiltInMetadata.Target.NoTarget),
14822
- action: ((_p = logOpts.action) !== null && _p !== void 0 ? _p : LogAction$1.Unknown),
14895
+ target: ((_y = logOpts.target) !== null && _y !== void 0 ? _y : LogBuiltInMetadata.Target.NoTarget),
14896
+ action: ((_z = logOpts.action) !== null && _z !== void 0 ? _z : LogAction$1.Unknown),
14823
14897
  });
14824
14898
  // Source-specific info
14825
14899
  const sourceSpecificInfo = (logOpts.overrideAsClientEvent
@@ -14854,7 +14928,7 @@ const genRouteHandler = (opts) => {
14854
14928
  catch (err) {
14855
14929
  // Print because we cannot store the error
14856
14930
  // eslint-disable-next-line no-console
14857
- console.error('Could not log the following:', logOpts, 'due to this error:', ((_q = err) !== null && _q !== void 0 ? _q : {}).message, ((_r = err) !== null && _r !== void 0 ? _r : {}).stack);
14931
+ console.error('Could not log the following:', logOpts, 'due to this error:', ((_0 = err) !== null && _0 !== void 0 ? _0 : {}).message, ((_1 = err) !== null && _1 !== void 0 ? _1 : {}).stack);
14858
14932
  // Create a dummy log to return
14859
14933
  const dummyMainInfo = {
14860
14934
  id: '-1',