@terreno/api 0.13.0 → 0.13.2
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.
- package/dist/api.js +9 -0
- package/dist/api.test.js +2 -2
- package/dist/consentApp.d.ts +2 -2
- package/dist/consentApp.js +2 -1
- package/dist/githubAuth.test.js +409 -0
- package/dist/models/consentForm.js +8 -9
- package/dist/models/versionConfig.d.ts +1 -1
- package/dist/notifiers/slackNotifier.d.ts +2 -2
- package/dist/notifiers/slackNotifier.js +38 -7
- package/dist/plugins.d.ts +3 -3
- package/dist/plugins.js +8 -4
- package/dist/populate.test.js +5 -1
- package/dist/secretProviders.js +4 -1
- package/dist/tests.js +1 -0
- package/dist/transformers.d.ts +5 -5
- package/dist/transformers.js +38 -37
- package/dist/utils.js +13 -3
- package/package.json +1 -1
- package/src/api.test.ts +2 -2
- package/src/api.ts +9 -0
- package/src/consentApp.ts +3 -3
- package/src/githubAuth.test.ts +327 -0
- package/src/models/consentForm.ts +8 -10
- package/src/models/versionConfig.ts +1 -1
- package/src/notifiers/slackNotifier.ts +7 -6
- package/src/openApiEtag.ts +1 -1
- package/src/plugins.ts +13 -8
- package/src/populate.test.ts +45 -20
- package/src/secretProviders.ts +4 -3
- package/src/tests.ts +18 -14
- package/src/transformers.ts +32 -30
- package/src/utils.ts +13 -3
package/dist/api.js
CHANGED
|
@@ -335,6 +335,9 @@ function _buildModelRouter(model, options) {
|
|
|
335
335
|
body = (0, transformers_1.transform)(options, req.body, "create", req.user);
|
|
336
336
|
}
|
|
337
337
|
catch (error) {
|
|
338
|
+
if ((0, errors_1.isAPIError)(error)) {
|
|
339
|
+
throw error;
|
|
340
|
+
}
|
|
338
341
|
throw new errors_1.APIError({
|
|
339
342
|
disableExternalErrorTracking: (0, errors_1.getDisableExternalErrorTracking)(error),
|
|
340
343
|
error: error,
|
|
@@ -687,6 +690,9 @@ function _buildModelRouter(model, options) {
|
|
|
687
690
|
body = (0, transformers_1.transform)(options, req.body, "update", req.user);
|
|
688
691
|
}
|
|
689
692
|
catch (error) {
|
|
693
|
+
if ((0, errors_1.isAPIError)(error)) {
|
|
694
|
+
throw error;
|
|
695
|
+
}
|
|
690
696
|
throw new errors_1.APIError({
|
|
691
697
|
disableExternalErrorTracking: (0, errors_1.getDisableExternalErrorTracking)(error),
|
|
692
698
|
error: error,
|
|
@@ -968,6 +974,9 @@ function _buildModelRouter(model, options) {
|
|
|
968
974
|
body = (0, transformers_1.transform)(options, body, "update", req.user);
|
|
969
975
|
}
|
|
970
976
|
catch (error) {
|
|
977
|
+
if ((0, errors_1.isAPIError)(error)) {
|
|
978
|
+
throw error;
|
|
979
|
+
}
|
|
971
980
|
throw new errors_1.APIError({
|
|
972
981
|
disableExternalErrorTracking: (0, errors_1.getDisableExternalErrorTracking)(error),
|
|
973
982
|
error: error,
|
package/dist/api.test.js
CHANGED
|
@@ -774,7 +774,7 @@ var transformers_1 = require("./transformers");
|
|
|
774
774
|
}),
|
|
775
775
|
}));
|
|
776
776
|
server = (0, supertest_1.default)(app);
|
|
777
|
-
return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(
|
|
777
|
+
return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(403)];
|
|
778
778
|
case 1:
|
|
779
779
|
res = _a.sent();
|
|
780
780
|
(0, bun_test_1.expect)(res.body.title).toContain("cannot write fields");
|
|
@@ -1699,7 +1699,7 @@ var transformers_1 = require("./transformers");
|
|
|
1699
1699
|
}),
|
|
1700
1700
|
}));
|
|
1701
1701
|
server = (0, supertest_1.default)(app);
|
|
1702
|
-
return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(
|
|
1702
|
+
return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(403)];
|
|
1703
1703
|
case 1:
|
|
1704
1704
|
res = _a.sent();
|
|
1705
1705
|
(0, bun_test_1.expect)(res.body.title).toContain("cannot write fields");
|
package/dist/consentApp.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Provides admin CRUD for consent forms, read-only access to responses, and user-facing
|
|
6
6
|
* endpoints for fetching pending consents and submitting responses.
|
|
7
7
|
*/
|
|
8
|
-
import type
|
|
8
|
+
import { type Application } from "express";
|
|
9
9
|
import type { User } from "./auth";
|
|
10
10
|
import type { TerrenoPlugin } from "./terrenoPlugin";
|
|
11
11
|
import type { ConsentFormDocument } from "./types/consentForm";
|
|
@@ -29,5 +29,5 @@ export interface ConsentAppOptions {
|
|
|
29
29
|
export declare class ConsentApp implements TerrenoPlugin {
|
|
30
30
|
private options;
|
|
31
31
|
constructor(options?: ConsentAppOptions);
|
|
32
|
-
register(app:
|
|
32
|
+
register(app: Application): void;
|
|
33
33
|
}
|
package/dist/consentApp.js
CHANGED
|
@@ -55,6 +55,7 @@ var __values = (this && this.__values) || function(o) {
|
|
|
55
55
|
};
|
|
56
56
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
57
57
|
exports.ConsentApp = void 0;
|
|
58
|
+
var express_1 = require("express");
|
|
58
59
|
var luxon_1 = require("luxon");
|
|
59
60
|
var api_1 = require("./api");
|
|
60
61
|
var auth_1 = require("./auth");
|
|
@@ -203,7 +204,7 @@ var ConsentApp = /** @class */ (function () {
|
|
|
203
204
|
],
|
|
204
205
|
}));
|
|
205
206
|
// User-facing consent endpoints
|
|
206
|
-
var router =
|
|
207
|
+
var router = (0, express_1.Router)();
|
|
207
208
|
// GET /consents/pending - fetch pending consent forms for the current user
|
|
208
209
|
router.get("/pending", (0, auth_1.authenticateMiddleware)(), (0, api_1.asyncHandler)(function (req, res) { return __awaiter(_this, void 0, void 0, function () {
|
|
209
210
|
var user, activeForms, resolvedForms, existingResponses, respondedFormVersions, existingResponses_1, existingResponses_1_1, response, formId, respondedFormIds, respondedForms, formVersionByFormId, respondedForms_1, respondedForms_1_1, form, pendingForms, filteredOutByResolverCount, filteredOutByResponsesCount;
|
package/dist/githubAuth.test.js
CHANGED
|
@@ -77,10 +77,30 @@ var mongoose_1 = __importStar(require("mongoose"));
|
|
|
77
77
|
var passport_1 = __importDefault(require("passport"));
|
|
78
78
|
var passport_local_mongoose_1 = __importDefault(require("passport-local-mongoose"));
|
|
79
79
|
var supertest_1 = __importDefault(require("supertest"));
|
|
80
|
+
var auth_1 = require("./auth");
|
|
80
81
|
var expressServer_1 = require("./expressServer");
|
|
81
82
|
var githubAuth_1 = require("./githubAuth");
|
|
82
83
|
var logger_1 = require("./logger");
|
|
83
84
|
var plugins_1 = require("./plugins");
|
|
85
|
+
var fakeGithubOutcome = { type: "redirect", url: "http://github.com/mock" };
|
|
86
|
+
var installFakeGithubStrategy = function () {
|
|
87
|
+
var strategy = {
|
|
88
|
+
authenticate: function () {
|
|
89
|
+
var _a, _b;
|
|
90
|
+
if (fakeGithubOutcome.type === "success") {
|
|
91
|
+
this.success(fakeGithubOutcome.user);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (fakeGithubOutcome.type === "fail") {
|
|
95
|
+
this.fail((_a = fakeGithubOutcome.challenge) !== null && _a !== void 0 ? _a : { message: "auth failed" });
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
this.redirect((_b = fakeGithubOutcome.url) !== null && _b !== void 0 ? _b : "http://github.com/mock");
|
|
99
|
+
},
|
|
100
|
+
name: "github",
|
|
101
|
+
};
|
|
102
|
+
passport_1.default.use("github", strategy);
|
|
103
|
+
};
|
|
84
104
|
// Create schema for GitHub-enabled user
|
|
85
105
|
var testUserSchema = new mongoose_1.Schema({
|
|
86
106
|
admin: { default: false, description: "Whether the user has admin privileges", type: Boolean },
|
|
@@ -729,3 +749,392 @@ var invokeGitHubVerify = function (req, accessToken, refreshToken, profile) {
|
|
|
729
749
|
});
|
|
730
750
|
}); });
|
|
731
751
|
});
|
|
752
|
+
(0, bun_test_1.describe)("GitHub callback handler (fake strategy)", function () {
|
|
753
|
+
var app;
|
|
754
|
+
var agent;
|
|
755
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
756
|
+
function addRoutes(router) {
|
|
757
|
+
router.get("/test", function (_req, res) { return res.json({ ok: true }); });
|
|
758
|
+
}
|
|
759
|
+
return __generator(this, function (_a) {
|
|
760
|
+
switch (_a.label) {
|
|
761
|
+
case 0:
|
|
762
|
+
(0, bun_test_1.setSystemTime)();
|
|
763
|
+
return [4 /*yield*/, connectDb()];
|
|
764
|
+
case 1:
|
|
765
|
+
_a.sent();
|
|
766
|
+
return [4 /*yield*/, GitHubTestUserModel.deleteMany({})];
|
|
767
|
+
case 2:
|
|
768
|
+
_a.sent();
|
|
769
|
+
app = (0, expressServer_1.setupServer)({
|
|
770
|
+
addMiddleware: function (a) {
|
|
771
|
+
// The handler reads (req as unknown as {session?: {returnTo?: string}}).session?.returnTo.
|
|
772
|
+
// setupServer does not install express-session, so prime a fake session from a request
|
|
773
|
+
// header for tests.
|
|
774
|
+
a.use(function (req, _res, next) {
|
|
775
|
+
var headerReturnTo = req.headers["x-mock-return-to"];
|
|
776
|
+
if (typeof headerReturnTo === "string") {
|
|
777
|
+
req.session = { returnTo: headerReturnTo };
|
|
778
|
+
}
|
|
779
|
+
next();
|
|
780
|
+
});
|
|
781
|
+
},
|
|
782
|
+
addRoutes: addRoutes,
|
|
783
|
+
githubAuth: {
|
|
784
|
+
allowAccountLinking: true,
|
|
785
|
+
callbackURL: "http://localhost:9000/auth/github/callback",
|
|
786
|
+
clientId: "test-client-id",
|
|
787
|
+
clientSecret: "test-client-secret",
|
|
788
|
+
},
|
|
789
|
+
skipListen: true,
|
|
790
|
+
userModel: GitHubTestUserModel,
|
|
791
|
+
});
|
|
792
|
+
// Swap the github strategy with our fake after setupServer registered it.
|
|
793
|
+
installFakeGithubStrategy();
|
|
794
|
+
agent = supertest_1.default.agent(app);
|
|
795
|
+
return [2 /*return*/];
|
|
796
|
+
}
|
|
797
|
+
});
|
|
798
|
+
}); });
|
|
799
|
+
(0, bun_test_1.afterEach)(function () {
|
|
800
|
+
(0, bun_test_1.setSystemTime)();
|
|
801
|
+
fakeGithubOutcome = { type: "redirect", url: "http://github.com/mock" };
|
|
802
|
+
});
|
|
803
|
+
(0, bun_test_1.it)("GET /auth/github/callback returns JSON tokens on success", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
804
|
+
var user, res;
|
|
805
|
+
return __generator(this, function (_a) {
|
|
806
|
+
switch (_a.label) {
|
|
807
|
+
case 0: return [4 /*yield*/, GitHubTestUserModel.create({
|
|
808
|
+
email: "cb@example.com",
|
|
809
|
+
githubId: "cb-gh-1",
|
|
810
|
+
name: "CB User",
|
|
811
|
+
})];
|
|
812
|
+
case 1:
|
|
813
|
+
user = _a.sent();
|
|
814
|
+
fakeGithubOutcome = { type: "success", user: user };
|
|
815
|
+
return [4 /*yield*/, agent.get("/auth/github/callback").expect(200)];
|
|
816
|
+
case 2:
|
|
817
|
+
res = _a.sent();
|
|
818
|
+
(0, bun_test_1.expect)(res.body.data.token).toBeDefined();
|
|
819
|
+
(0, bun_test_1.expect)(res.body.data.refreshToken).toBeDefined();
|
|
820
|
+
(0, bun_test_1.expect)(res.body.data.userId).toBeDefined();
|
|
821
|
+
return [2 /*return*/];
|
|
822
|
+
}
|
|
823
|
+
});
|
|
824
|
+
}); });
|
|
825
|
+
(0, bun_test_1.it)("GET /auth/github/callback redirects to returnTo with tokens when session.returnTo is set", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
826
|
+
var user, res;
|
|
827
|
+
return __generator(this, function (_a) {
|
|
828
|
+
switch (_a.label) {
|
|
829
|
+
case 0: return [4 /*yield*/, GitHubTestUserModel.create({
|
|
830
|
+
email: "cb2@example.com",
|
|
831
|
+
githubId: "cb-gh-2",
|
|
832
|
+
name: "CB User 2",
|
|
833
|
+
})];
|
|
834
|
+
case 1:
|
|
835
|
+
user = _a.sent();
|
|
836
|
+
fakeGithubOutcome = { type: "success", user: user };
|
|
837
|
+
return [4 /*yield*/, agent
|
|
838
|
+
.get("/auth/github/callback")
|
|
839
|
+
.set("x-mock-return-to", "https://example.com/cb")
|
|
840
|
+
.expect(302)];
|
|
841
|
+
case 2:
|
|
842
|
+
res = _a.sent();
|
|
843
|
+
(0, bun_test_1.expect)(res.headers.location).toContain("https://example.com/cb");
|
|
844
|
+
(0, bun_test_1.expect)(res.headers.location).toContain("token=");
|
|
845
|
+
(0, bun_test_1.expect)(res.headers.location).toContain("refreshToken=");
|
|
846
|
+
(0, bun_test_1.expect)(res.headers.location).toContain("userId=");
|
|
847
|
+
return [2 /*return*/];
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
}); });
|
|
851
|
+
(0, bun_test_1.it)("GET /auth/github/callback redirects on failure", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
852
|
+
var res;
|
|
853
|
+
return __generator(this, function (_a) {
|
|
854
|
+
switch (_a.label) {
|
|
855
|
+
case 0:
|
|
856
|
+
fakeGithubOutcome = { challenge: { message: "denied" }, type: "fail" };
|
|
857
|
+
return [4 /*yield*/, agent.get("/auth/github/callback").expect(302)];
|
|
858
|
+
case 1:
|
|
859
|
+
res = _a.sent();
|
|
860
|
+
(0, bun_test_1.expect)(res.headers.location).toContain("/auth/github/failure");
|
|
861
|
+
return [2 /*return*/];
|
|
862
|
+
}
|
|
863
|
+
});
|
|
864
|
+
}); });
|
|
865
|
+
(0, bun_test_1.it)("GET /auth/github/callback returns 500 when token generation fails", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
866
|
+
var user, savedSecret, res;
|
|
867
|
+
return __generator(this, function (_a) {
|
|
868
|
+
switch (_a.label) {
|
|
869
|
+
case 0: return [4 /*yield*/, GitHubTestUserModel.create({
|
|
870
|
+
email: "cb3@example.com",
|
|
871
|
+
githubId: "cb-gh-3",
|
|
872
|
+
name: "CB User 3",
|
|
873
|
+
})];
|
|
874
|
+
case 1:
|
|
875
|
+
user = _a.sent();
|
|
876
|
+
fakeGithubOutcome = { type: "success", user: user };
|
|
877
|
+
savedSecret = process.env.TOKEN_SECRET;
|
|
878
|
+
process.env.TOKEN_SECRET = "";
|
|
879
|
+
_a.label = 2;
|
|
880
|
+
case 2:
|
|
881
|
+
_a.trys.push([2, , 4, 5]);
|
|
882
|
+
return [4 /*yield*/, agent.get("/auth/github/callback").expect(500)];
|
|
883
|
+
case 3:
|
|
884
|
+
res = _a.sent();
|
|
885
|
+
(0, bun_test_1.expect)(res.body.message).toBe("Authentication failed");
|
|
886
|
+
return [3 /*break*/, 5];
|
|
887
|
+
case 4:
|
|
888
|
+
process.env.TOKEN_SECRET = savedSecret;
|
|
889
|
+
return [7 /*endfinally*/];
|
|
890
|
+
case 5: return [2 /*return*/];
|
|
891
|
+
}
|
|
892
|
+
});
|
|
893
|
+
}); });
|
|
894
|
+
});
|
|
895
|
+
(0, bun_test_1.describe)("GET /auth/github/link with JWT (fake strategy)", function () {
|
|
896
|
+
var app;
|
|
897
|
+
var agent;
|
|
898
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
899
|
+
function addRoutes(router) {
|
|
900
|
+
router.get("/test", function (_req, res) { return res.json({ ok: true }); });
|
|
901
|
+
}
|
|
902
|
+
return __generator(this, function (_a) {
|
|
903
|
+
switch (_a.label) {
|
|
904
|
+
case 0:
|
|
905
|
+
(0, bun_test_1.setSystemTime)();
|
|
906
|
+
return [4 /*yield*/, connectDb()];
|
|
907
|
+
case 1:
|
|
908
|
+
_a.sent();
|
|
909
|
+
return [4 /*yield*/, GitHubTestUserModel.deleteMany({})];
|
|
910
|
+
case 2:
|
|
911
|
+
_a.sent();
|
|
912
|
+
app = (0, expressServer_1.setupServer)({
|
|
913
|
+
addRoutes: addRoutes,
|
|
914
|
+
githubAuth: {
|
|
915
|
+
allowAccountLinking: true,
|
|
916
|
+
callbackURL: "http://localhost:9000/auth/github/callback",
|
|
917
|
+
clientId: "test-client-id",
|
|
918
|
+
clientSecret: "test-client-secret",
|
|
919
|
+
},
|
|
920
|
+
skipListen: true,
|
|
921
|
+
userModel: GitHubTestUserModel,
|
|
922
|
+
});
|
|
923
|
+
installFakeGithubStrategy();
|
|
924
|
+
agent = supertest_1.default.agent(app);
|
|
925
|
+
return [2 /*return*/];
|
|
926
|
+
}
|
|
927
|
+
});
|
|
928
|
+
}); });
|
|
929
|
+
(0, bun_test_1.afterEach)(function () {
|
|
930
|
+
(0, bun_test_1.setSystemTime)();
|
|
931
|
+
fakeGithubOutcome = { type: "redirect", url: "http://github.com/mock" };
|
|
932
|
+
});
|
|
933
|
+
(0, bun_test_1.it)("GET /auth/github/link forwards to GitHub auth when JWT is valid", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
934
|
+
var user, loginRes, res;
|
|
935
|
+
return __generator(this, function (_a) {
|
|
936
|
+
switch (_a.label) {
|
|
937
|
+
case 0: return [4 /*yield*/, GitHubTestUserModel.create({
|
|
938
|
+
email: "linkjwt@example.com",
|
|
939
|
+
name: "Link JWT User",
|
|
940
|
+
})];
|
|
941
|
+
case 1:
|
|
942
|
+
user = _a.sent();
|
|
943
|
+
return [4 /*yield*/, user.setPassword("password123")];
|
|
944
|
+
case 2:
|
|
945
|
+
_a.sent();
|
|
946
|
+
return [4 /*yield*/, user.save()];
|
|
947
|
+
case 3:
|
|
948
|
+
_a.sent();
|
|
949
|
+
return [4 /*yield*/, agent
|
|
950
|
+
.post("/auth/login")
|
|
951
|
+
.send({ email: "linkjwt@example.com", password: "password123" })
|
|
952
|
+
.expect(200)];
|
|
953
|
+
case 4:
|
|
954
|
+
loginRes = _a.sent();
|
|
955
|
+
fakeGithubOutcome = { type: "redirect", url: "http://github.com/auth" };
|
|
956
|
+
return [4 /*yield*/, agent
|
|
957
|
+
.get("/auth/github/link")
|
|
958
|
+
.set("authorization", "Bearer ".concat(loginRes.body.data.token))
|
|
959
|
+
.expect(302)];
|
|
960
|
+
case 5:
|
|
961
|
+
res = _a.sent();
|
|
962
|
+
(0, bun_test_1.expect)(res.headers.location).toBe("http://github.com/auth");
|
|
963
|
+
return [2 /*return*/];
|
|
964
|
+
}
|
|
965
|
+
});
|
|
966
|
+
}); });
|
|
967
|
+
});
|
|
968
|
+
(0, bun_test_1.describe)("DELETE /auth/github/unlink edge cases", function () {
|
|
969
|
+
var app;
|
|
970
|
+
var agent;
|
|
971
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
972
|
+
function addRoutes(router) {
|
|
973
|
+
router.get("/test", function (_req, res) { return res.json({ ok: true }); });
|
|
974
|
+
}
|
|
975
|
+
return __generator(this, function (_a) {
|
|
976
|
+
switch (_a.label) {
|
|
977
|
+
case 0:
|
|
978
|
+
(0, bun_test_1.setSystemTime)();
|
|
979
|
+
return [4 /*yield*/, connectDb()];
|
|
980
|
+
case 1:
|
|
981
|
+
_a.sent();
|
|
982
|
+
return [4 /*yield*/, GitHubTestUserModel.deleteMany({})];
|
|
983
|
+
case 2:
|
|
984
|
+
_a.sent();
|
|
985
|
+
app = (0, expressServer_1.setupServer)({
|
|
986
|
+
addRoutes: addRoutes,
|
|
987
|
+
githubAuth: {
|
|
988
|
+
allowAccountLinking: true,
|
|
989
|
+
callbackURL: "http://localhost:9000/auth/github/callback",
|
|
990
|
+
clientId: "test-client-id",
|
|
991
|
+
clientSecret: "test-client-secret",
|
|
992
|
+
},
|
|
993
|
+
skipListen: true,
|
|
994
|
+
userModel: GitHubTestUserModel,
|
|
995
|
+
});
|
|
996
|
+
installFakeGithubStrategy();
|
|
997
|
+
agent = supertest_1.default.agent(app);
|
|
998
|
+
return [2 /*return*/];
|
|
999
|
+
}
|
|
1000
|
+
});
|
|
1001
|
+
}); });
|
|
1002
|
+
(0, bun_test_1.afterEach)(function () {
|
|
1003
|
+
(0, bun_test_1.setSystemTime)();
|
|
1004
|
+
});
|
|
1005
|
+
(0, bun_test_1.it)("returns 400 when user has no password (no other auth method)", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1006
|
+
var user, token, res;
|
|
1007
|
+
return __generator(this, function (_a) {
|
|
1008
|
+
switch (_a.label) {
|
|
1009
|
+
case 0: return [4 /*yield*/, GitHubTestUserModel.create({
|
|
1010
|
+
email: "ghonly@example.com",
|
|
1011
|
+
githubId: "ghonly-1",
|
|
1012
|
+
githubUsername: "ghonly",
|
|
1013
|
+
})];
|
|
1014
|
+
case 1:
|
|
1015
|
+
user = _a.sent();
|
|
1016
|
+
return [4 /*yield*/, (0, auth_1.generateTokens)({ _id: user._id })];
|
|
1017
|
+
case 2:
|
|
1018
|
+
token = (_a.sent()).token;
|
|
1019
|
+
return [4 /*yield*/, agent
|
|
1020
|
+
.delete("/auth/github/unlink")
|
|
1021
|
+
.set("authorization", "Bearer ".concat(token))
|
|
1022
|
+
.expect(400)];
|
|
1023
|
+
case 3:
|
|
1024
|
+
res = _a.sent();
|
|
1025
|
+
(0, bun_test_1.expect)(res.body.message).toContain("Cannot unlink GitHub account");
|
|
1026
|
+
return [2 /*return*/];
|
|
1027
|
+
}
|
|
1028
|
+
});
|
|
1029
|
+
}); });
|
|
1030
|
+
(0, bun_test_1.it)("returns 500 when save throws during unlink", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1031
|
+
var user, loginRes, mockableModel, originalFindById, res;
|
|
1032
|
+
return __generator(this, function (_a) {
|
|
1033
|
+
switch (_a.label) {
|
|
1034
|
+
case 0: return [4 /*yield*/, GitHubTestUserModel.create({
|
|
1035
|
+
email: "savefail@example.com",
|
|
1036
|
+
githubId: "savefail-1",
|
|
1037
|
+
})];
|
|
1038
|
+
case 1:
|
|
1039
|
+
user = _a.sent();
|
|
1040
|
+
return [4 /*yield*/, user.setPassword("password123")];
|
|
1041
|
+
case 2:
|
|
1042
|
+
_a.sent();
|
|
1043
|
+
return [4 /*yield*/, user.save()];
|
|
1044
|
+
case 3:
|
|
1045
|
+
_a.sent();
|
|
1046
|
+
return [4 /*yield*/, agent
|
|
1047
|
+
.post("/auth/login")
|
|
1048
|
+
.send({ email: "savefail@example.com", password: "password123" })
|
|
1049
|
+
.expect(200)];
|
|
1050
|
+
case 4:
|
|
1051
|
+
loginRes = _a.sent();
|
|
1052
|
+
mockableModel = GitHubTestUserModel;
|
|
1053
|
+
originalFindById = mockableModel.findById;
|
|
1054
|
+
mockableModel.findById = function () { return ({
|
|
1055
|
+
select: function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1056
|
+
return __generator(this, function (_a) {
|
|
1057
|
+
return [2 /*return*/, ({
|
|
1058
|
+
hash: "x",
|
|
1059
|
+
salt: "y",
|
|
1060
|
+
save: function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1061
|
+
return __generator(this, function (_a) {
|
|
1062
|
+
throw new Error("boom");
|
|
1063
|
+
});
|
|
1064
|
+
}); },
|
|
1065
|
+
setPassword: function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
1066
|
+
return [2 /*return*/, undefined];
|
|
1067
|
+
}); }); },
|
|
1068
|
+
})];
|
|
1069
|
+
});
|
|
1070
|
+
}); },
|
|
1071
|
+
}); };
|
|
1072
|
+
_a.label = 5;
|
|
1073
|
+
case 5:
|
|
1074
|
+
_a.trys.push([5, , 7, 8]);
|
|
1075
|
+
return [4 /*yield*/, agent
|
|
1076
|
+
.delete("/auth/github/unlink")
|
|
1077
|
+
.set("authorization", "Bearer ".concat(loginRes.body.data.token))
|
|
1078
|
+
.expect(500)];
|
|
1079
|
+
case 6:
|
|
1080
|
+
res = _a.sent();
|
|
1081
|
+
(0, bun_test_1.expect)(res.body.message).toBe("Failed to unlink GitHub account");
|
|
1082
|
+
return [3 /*break*/, 8];
|
|
1083
|
+
case 7:
|
|
1084
|
+
mockableModel.findById = originalFindById;
|
|
1085
|
+
return [7 /*endfinally*/];
|
|
1086
|
+
case 8: return [2 /*return*/];
|
|
1087
|
+
}
|
|
1088
|
+
});
|
|
1089
|
+
}); });
|
|
1090
|
+
});
|
|
1091
|
+
(0, bun_test_1.describe)("GitHub strategy verify callback edge cases", function () {
|
|
1092
|
+
var testApp = { get: function () { }, use: function () { } };
|
|
1093
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1094
|
+
return __generator(this, function (_a) {
|
|
1095
|
+
switch (_a.label) {
|
|
1096
|
+
case 0: return [4 /*yield*/, connectDb()];
|
|
1097
|
+
case 1:
|
|
1098
|
+
_a.sent();
|
|
1099
|
+
return [4 /*yield*/, GitHubTestUserModel.deleteMany({})];
|
|
1100
|
+
case 2:
|
|
1101
|
+
_a.sent();
|
|
1102
|
+
return [2 /*return*/];
|
|
1103
|
+
}
|
|
1104
|
+
});
|
|
1105
|
+
}); });
|
|
1106
|
+
(0, bun_test_1.it)("returns 404 when linking a user whose record disappears", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1107
|
+
var existingUser, req, result;
|
|
1108
|
+
var _a;
|
|
1109
|
+
return __generator(this, function (_b) {
|
|
1110
|
+
switch (_b.label) {
|
|
1111
|
+
case 0: return [4 /*yield*/, GitHubTestUserModel.create({
|
|
1112
|
+
email: "gone@example.com",
|
|
1113
|
+
})];
|
|
1114
|
+
case 1:
|
|
1115
|
+
existingUser = _b.sent();
|
|
1116
|
+
(0, githubAuth_1.setupGitHubAuth)(testApp, GitHubTestUserModel, {
|
|
1117
|
+
allowAccountLinking: true,
|
|
1118
|
+
callbackURL: "http://localhost:9000/auth/github/callback",
|
|
1119
|
+
clientId: "id",
|
|
1120
|
+
clientSecret: "secret",
|
|
1121
|
+
});
|
|
1122
|
+
// Delete user before verify runs to hit the 404 path.
|
|
1123
|
+
return [4 /*yield*/, GitHubTestUserModel.deleteOne({ _id: existingUser._id })];
|
|
1124
|
+
case 2:
|
|
1125
|
+
// Delete user before verify runs to hit the 404 path.
|
|
1126
|
+
_b.sent();
|
|
1127
|
+
req = { user: existingUser };
|
|
1128
|
+
return [4 /*yield*/, invokeGitHubVerify(req, "access", "refresh", {
|
|
1129
|
+
id: "gh-missing-1",
|
|
1130
|
+
username: "missing",
|
|
1131
|
+
})];
|
|
1132
|
+
case 3:
|
|
1133
|
+
result = _b.sent();
|
|
1134
|
+
(0, bun_test_1.expect)(result.err).toBeDefined();
|
|
1135
|
+
(0, bun_test_1.expect)((_a = result.err) === null || _a === void 0 ? void 0 : _a.status).toBe(404);
|
|
1136
|
+
return [2 /*return*/];
|
|
1137
|
+
}
|
|
1138
|
+
});
|
|
1139
|
+
}); });
|
|
1140
|
+
});
|
|
@@ -6,15 +6,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.ConsentForm = void 0;
|
|
7
7
|
var mongoose_1 = __importDefault(require("mongoose"));
|
|
8
8
|
var plugins_1 = require("../plugins");
|
|
9
|
-
var
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
var consentFormTypeValues = Object.values(consentFormTypeMap);
|
|
9
|
+
var consentFormTypeValues = [
|
|
10
|
+
"agreement",
|
|
11
|
+
"privacy",
|
|
12
|
+
"hipaa",
|
|
13
|
+
"research",
|
|
14
|
+
"terms",
|
|
15
|
+
"custom",
|
|
16
|
+
];
|
|
18
17
|
var consentFormSchema = new mongoose_1.default.Schema({
|
|
19
18
|
active: {
|
|
20
19
|
default: false,
|
|
@@ -12,6 +12,6 @@ export interface VersionConfigDocument extends mongoose.Document {
|
|
|
12
12
|
updated?: Date;
|
|
13
13
|
}
|
|
14
14
|
export interface VersionConfigModel extends mongoose.Model<VersionConfigDocument> {
|
|
15
|
-
findOneOrNone(query: Record<string,
|
|
15
|
+
findOneOrNone(query: Record<string, unknown>, errorArgs?: Partial<APIErrorConstructor>): Promise<(Document & VersionConfigDocument) | null>;
|
|
16
16
|
}
|
|
17
17
|
export declare const VersionConfig: VersionConfigModel;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export declare
|
|
1
|
+
export declare const sendToSlack: (text: string, { slackChannel, shouldThrow, env, url, }?: {
|
|
2
2
|
slackChannel?: string;
|
|
3
3
|
shouldThrow?: boolean;
|
|
4
4
|
env?: string;
|
|
5
5
|
url?: string;
|
|
6
|
-
})
|
|
6
|
+
}) => Promise<void>;
|
|
@@ -68,11 +68,36 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
68
68
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
69
69
|
}
|
|
70
70
|
};
|
|
71
|
+
var __read = (this && this.__read) || function (o, n) {
|
|
72
|
+
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
73
|
+
if (!m) return o;
|
|
74
|
+
var i = m.call(o), r, ar = [], e;
|
|
75
|
+
try {
|
|
76
|
+
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
77
|
+
}
|
|
78
|
+
catch (error) { e = { error: error }; }
|
|
79
|
+
finally {
|
|
80
|
+
try {
|
|
81
|
+
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
82
|
+
}
|
|
83
|
+
finally { if (e) throw e.error; }
|
|
84
|
+
}
|
|
85
|
+
return ar;
|
|
86
|
+
};
|
|
87
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
88
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
89
|
+
if (ar || !(i in from)) {
|
|
90
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
91
|
+
ar[i] = from[i];
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
95
|
+
};
|
|
71
96
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
72
97
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
73
98
|
};
|
|
74
99
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
75
|
-
exports.sendToSlack =
|
|
100
|
+
exports.sendToSlack = void 0;
|
|
76
101
|
var Sentry = __importStar(require("@sentry/bun"));
|
|
77
102
|
var axios_1 = __importDefault(require("axios"));
|
|
78
103
|
var errors_1 = require("../errors");
|
|
@@ -81,9 +106,13 @@ var logger_1 = require("../logger");
|
|
|
81
106
|
// If `url` is provided, it will be used directly instead of looking up from environment.
|
|
82
107
|
// DEPRECATED: Looking up webhook URLs from the SLACK_WEBHOOKS environment variable by channel name
|
|
83
108
|
// is deprecated and will be removed in a future version. Please pass the `url` parameter directly.
|
|
84
|
-
function
|
|
85
|
-
|
|
86
|
-
|
|
109
|
+
var sendToSlack = function (text_1) {
|
|
110
|
+
var args_1 = [];
|
|
111
|
+
for (var _i = 1; _i < arguments.length; _i++) {
|
|
112
|
+
args_1[_i - 1] = arguments[_i];
|
|
113
|
+
}
|
|
114
|
+
return __awaiter(void 0, __spreadArray([text_1], __read(args_1), false), void 0, function (text, _a) {
|
|
115
|
+
var slackWebhookUrl, slackWebhooksString, slackWebhooks, channel, formattedText, error_1, errorObj;
|
|
87
116
|
var _b, _c, _d;
|
|
88
117
|
var _e = _a === void 0 ? {} : _a, slackChannel = _e.slackChannel, _f = _e.shouldThrow, shouldThrow = _f === void 0 ? false : _f, env = _e.env, url = _e.url;
|
|
89
118
|
return __generator(this, function (_g) {
|
|
@@ -121,12 +150,13 @@ function sendToSlack(text_1) {
|
|
|
121
150
|
return [3 /*break*/, 4];
|
|
122
151
|
case 3:
|
|
123
152
|
error_1 = _g.sent();
|
|
124
|
-
|
|
153
|
+
errorObj = error_1;
|
|
154
|
+
logger_1.logger.error("Error posting to slack: ".concat((_c = errorObj.text) !== null && _c !== void 0 ? _c : errorObj.message));
|
|
125
155
|
Sentry.captureException(error_1);
|
|
126
156
|
if (shouldThrow) {
|
|
127
157
|
throw new errors_1.APIError({
|
|
128
158
|
status: 500,
|
|
129
|
-
title: "Error posting to slack: ".concat((_d =
|
|
159
|
+
title: "Error posting to slack: ".concat((_d = errorObj.text) !== null && _d !== void 0 ? _d : errorObj.message),
|
|
130
160
|
});
|
|
131
161
|
}
|
|
132
162
|
return [3 /*break*/, 4];
|
|
@@ -134,4 +164,5 @@ function sendToSlack(text_1) {
|
|
|
134
164
|
}
|
|
135
165
|
});
|
|
136
166
|
});
|
|
137
|
-
}
|
|
167
|
+
};
|
|
168
|
+
exports.sendToSlack = sendToSlack;
|
package/dist/plugins.d.ts
CHANGED
|
@@ -69,10 +69,10 @@ export interface FindExactlyOnePlugin<T> {
|
|
|
69
69
|
findExactlyOne(query: Record<string, any>, errorArgs?: Partial<APIErrorConstructor>): Promise<Document & T>;
|
|
70
70
|
}
|
|
71
71
|
export declare class DateOnly extends SchemaType {
|
|
72
|
-
constructor(key: string, options: SchemaTypeOptions<
|
|
72
|
+
constructor(key: string, options: SchemaTypeOptions<Date>);
|
|
73
73
|
handleSingle(val: any): Date | undefined;
|
|
74
74
|
$conditionalHandlers: any;
|
|
75
75
|
castForQuery($conditional: any, val: any, context: any): Date | undefined;
|
|
76
|
-
cast(val:
|
|
77
|
-
get(val:
|
|
76
|
+
cast(val: unknown): Date | undefined;
|
|
77
|
+
get(val: unknown): this;
|
|
78
78
|
}
|