Skip to content
This repository was archived by the owner on Nov 19, 2021. It is now read-only.

Commit 0305bf4

Browse files
committed
tmp
1 parent e4440bb commit 0305bf4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+3522
-607
lines changed

.env.example

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,9 @@ SENTRY_DSN=https://[email protected]/544
1313
JWT_SECRET=
1414

1515
DB_URL=postgresql://postgres:postgres@meetup-db/meetup
16+
17+
MAILSERVER_ADDRESS=
18+
MAILSERVER_PORT=
19+
MAILSERVER_USERNAME=
20+
MAILSERVER_PASSWORD=
21+

api/helpers/http.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ export class HttpStatus {
421421

422422

423423
export const internalRequest = async (method, url, ...rest) => {
424-
const baseUrl = `http://localhost:${ "development" === process.env.NODE_ENV ? "3000" : process.env.PORT }/api`;
424+
const baseUrl = `http://localhost:${ process.env.PORT }/api`;
425425
const fetchUrl = `${ baseUrl }${ url }`;
426426

427427
try {

api/helpers/middleware.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
} from "./token";
88
import {
99
hasPermission,
10-
RoleNames,
10+
Role,
1111
} from "./permissions";
1212
import {
1313
error,
@@ -49,7 +49,7 @@ export const injectAuthData =
4949
export const requireAuth =
5050
({
5151
fullUserData = false,
52-
role = RoleNames.BASE,
52+
role = Role.base,
5353
} = {}) =>
5454
async (req, res, next) => {
5555
await injectAuthData({ fullUserData })(req);

api/helpers/permissions.ts

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,41 @@ import map from "lodash/fp/map";
55

66
const mapWithIndex = map.convert({ cap: false });
77

8-
export enum RoleNames {
9-
BASE = "nobody",
10-
STUDENT = "student",
11-
ACCOUNT_MANAGER = "account_manager",
12-
MODERATOR = "moderator",
13-
ADMIN = "admin",
8+
// This is just a noop function to force the TS compiler to typecheck the enum
9+
const ensureEnumKeysSameAsValues = <T>(_kv: { [K in keyof T]: K }) => null;
10+
11+
export enum Role {
12+
base = "base",
13+
company = "company",
14+
student = "student",
15+
moderator = "moderator",
16+
admin = "admin",
1417
}
1518

16-
const roleNameToPriority: Record<RoleNames, number> =
19+
ensureEnumKeysSameAsValues(Role);
20+
21+
const roleNameToPriority: Record<Role, number> =
1722
flow(
1823
values,
19-
mapWithIndex((value: RoleNames, i: number) => [ value, i ]),
24+
mapWithIndex((value: Role, i: number) => [ value, i ]),
2025
fromPairs,
21-
)(RoleNames)
26+
)(Role)
2227
;
2328

2429
export const hasPermission =
2530
(
26-
minimumRoleName: RoleNames,
27-
currentRoleName: RoleNames,
31+
minimumRoleName: Role,
32+
currentRoleName: Role,
2833
): boolean =>
2934
roleNameToPriority[minimumRoleName] <= roleNameToPriority[currentRoleName]
3035
;
3136

3237
const isAtLeast =
33-
(role: RoleNames) =>
34-
(roleName: RoleNames) =>
38+
(role: Role) =>
39+
(roleName: Role) =>
3540
hasPermission(role, roleName)
3641
;
3742

38-
export const isAdmin = isAtLeast(RoleNames.ADMIN);
39-
export const isModerator = isAtLeast(RoleNames.MODERATOR);
40-
export const isStudent = isAtLeast(RoleNames.BASE);
43+
export const isAdmin = isAtLeast(Role.admin);
44+
export const isModerator = isAtLeast(Role.moderator);
45+
export const isStudent = isAtLeast(Role.student);

api/helpers/token.js

Lines changed: 16 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,7 @@
11
import {
22
verify,
33
} from "jsonwebtoken";
4-
import {
5-
keysFromSnakeToCamelCase,
6-
} from "../../helpers/object";
7-
import {
8-
basicUserQuery,
9-
currentUserQuery,
10-
} from "../graphql/queries";
11-
import CompanyService from "../services/company-service";
12-
import {
13-
getSetting,
14-
} from "./settings";
15-
import {
16-
graphQlQuery,
17-
} from "./axios";
18-
import {
19-
internalRequest,
20-
} from "./http";
4+
import UserService from "../services/user-service";
215

226
export const extractAuthorizationToken = (req) => {
237
const header = req.get("Authorization");
@@ -54,14 +38,14 @@ export const verifiedJwt =
5438
},
5539
) =>
5640
new Promise(
57-
(resolve, reject) =>
41+
(resolve) =>
5842
verify(
5943
token,
6044
secret,
6145
options,
6246
(err, data) => {
6347
if (err) {
64-
reject(err);
48+
resolve(null);
6549
} else {
6650
resolve(data);
6751
}
@@ -70,7 +54,9 @@ export const verifiedJwt =
7054
)
7155
;
7256

73-
export const fetchAuthenticatedUser = async (reqOrToken, fullUser = false) => {
57+
export const getJwtSecret = () => `jobfair-meetup-secret-${ process.env.JWT_SECRET || "very secret :)" }`;
58+
59+
export const fetchAuthenticatedUser = async (reqOrToken, _fullUser = false) => {
7460
const auth =
7561
"string" === typeof reqOrToken
7662
? reqOrToken
@@ -81,51 +67,18 @@ export const fetchAuthenticatedUser = async (reqOrToken, fullUser = false) => {
8167
return null;
8268
}
8369

84-
try {
85-
// Should be a string of format `jwt $AUTH_TOKEN`
86-
const token = auth.substr("jwt ".length);
87-
const secret = await getSetting("JWT Secret", process.env.JWT_SECRET);
88-
89-
const data = await verifiedJwt({
90-
token,
91-
secret,
92-
});
93-
94-
if (!("role" in data)) {
95-
return null;
96-
}
97-
98-
if (false === fullUser) {
99-
return data;
100-
}
101-
} catch (e) {
102-
console.log("Failed local JWT verification:", e.message);
103-
}
104-
105-
try {
106-
const { current_user: rawUser } = await graphQlQuery(
107-
fullUser ? currentUserQuery() : basicUserQuery(),
108-
auth,
109-
);
110-
111-
const user = keysFromSnakeToCamelCase({
112-
uid: rawUser.resume && rawUser.resume.uid,
113-
...rawUser,
114-
});
115-
116-
delete user.resume;
117-
118-
const { companies = [] } = user;
70+
// Should be a string of format `jwt $AUTH_TOKEN`
71+
const token = auth.substr("jwt ".length);
72+
const secret = getJwtSecret();
11973

120-
if (companies) {
121-
const { data: rawParticipants = [] } = await internalRequest("get", "/companies/participants");
122-
const participantIds = new Set(rawParticipants.map(({ id }) => id));
74+
const jwtData = await verifiedJwt({
75+
token,
76+
secret,
77+
});
12378

124-
user.companies = companies.filter(({ id }) => participantIds.has(id)).map(CompanyService.fixCompany);
125-
}
126-
127-
return user;
128-
} catch (e) {
79+
if (!jwtData?.uid) {
12980
return null;
13081
}
82+
83+
return await UserService.infoBy("uid", jwtData.uid);
13184
};

api/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
error,
2222
ApiError,
2323
} from "./helpers/route";
24+
import EmailService from "./services/email-service";
2425
import MobileNotificationService from "./services/mobile-notification-service";
2526

2627
const fileUploadMiddleware = fileUpload({
@@ -32,6 +33,8 @@ const fileUploadMiddleware = fileUpload({
3233

3334
const app = express();
3435

36+
app.set("trust proxy", true);
37+
3538
Sentry.init({ dsn: process.env.SENTRY_DSN });
3639

3740
// RequestHandler creates a separate execution context using domains, so that every
@@ -133,6 +136,11 @@ query(dbBase)
133136
await client.end();
134137
}
135138
})
139+
.then(async () => {
140+
const result = await EmailService.verifyConnection();
141+
142+
console.log("|> EMAIL SERVICE:", result);
143+
})
136144
;
137145

138146
app.use((err, req, res, _next) => {

api/routes/auth/login.js

Lines changed: 0 additions & 33 deletions
This file was deleted.

api/routes/auth/login.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import jwt from "jsonwebtoken";
2+
import {
3+
HttpStatus,
4+
} from "../../helpers/http";
5+
import {
6+
ApiError,
7+
Router,
8+
} from "../../helpers/route";
9+
import {
10+
getJwtSecret,
11+
} from "../../helpers/token";
12+
import AuthService from "../../services/auth-service";
13+
import UserLogService from "../../services/user-log-service";
14+
15+
const router = new Router();
16+
17+
router.post("/", async ({ body, ip, ips }, res) => {
18+
const { email, password } = body;
19+
20+
if (!email || !password) {
21+
throw new ApiError("no-credentials-provided");
22+
}
23+
24+
const user = await AuthService.login(email, password);
25+
26+
if (!user) {
27+
throw new ApiError(
28+
"invalid-credentials",
29+
HttpStatus.Error.Client.Unauthorized,
30+
null,
31+
);
32+
}
33+
34+
const { uid } = user;
35+
36+
const payload = {
37+
uid,
38+
};
39+
40+
const token = jwt.sign(
41+
payload,
42+
getJwtSecret(),
43+
{
44+
expiresIn: "2 weeks",
45+
},
46+
);
47+
48+
res.cookie(
49+
process.env.JOBFAIR_COOKIE_NAME,
50+
JSON.stringify({
51+
token,
52+
}),
53+
{
54+
domain: `.${ new URL(process.env.BASE_URL || "").hostname }`,
55+
expires: "",
56+
secure: "development" !== process.env.NODE_ENV,
57+
sameSite: "strict",
58+
},
59+
);
60+
61+
await UserLogService.log(
62+
user.uid,
63+
"login",
64+
{
65+
ip,
66+
ips,
67+
},
68+
);
69+
70+
return user;
71+
});
72+
73+
export default router;

0 commit comments

Comments
 (0)