Skip to content

Commit 36d4467

Browse files
Merge pull request #706 from freeCodeCamp/main
Create a new pull request by comparing changes across two branches
2 parents de83c40 + f32cb78 commit 36d4467

File tree

161 files changed

+18551
-280
lines changed

Some content is hidden

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

161 files changed

+18551
-280
lines changed

api-server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
"lodash": "4.17.21",
5555
"loopback": "3.28.0",
5656
"loopback-boot": "2.28.0",
57-
"loopback-connector-mongodb": "4.2.0",
57+
"loopback-connector-mongodb": "5.6.0",
5858
"method-override": "3.0.0",
5959
"moment": "2.29.3",
6060
"moment-timezone": "0.5.33",

api-server/src/server/datasources.production.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module.exports = {
77
connectionTimeout: 10000,
88
url: secrets.db,
99
useNewUrlParser: true,
10+
useUnifiedTopology: true,
1011
allowExtendedOperators: true
1112
},
1213
mail: {

api/src/app.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -186,10 +186,10 @@ export const build = async (
186186
void fastify.register(auth);
187187
void fastify.register(notFound);
188188
void fastify.register(prismaPlugin);
189+
void fastify.register(bouncer);
189190

190191
// Routes requiring authentication:
191192
void fastify.register(async function (fastify, _opts) {
192-
await fastify.register(bouncer);
193193
fastify.addHook('onRequest', fastify.authorize);
194194
// CSRF protection enabled:
195195
await fastify.register(async function (fastify, _opts) {
@@ -222,14 +222,19 @@ export const build = async (
222222
await fastify.register(settingRedirectRoutes);
223223
});
224224
});
225-
// Routes not requiring authentication:
226-
void fastify.register(mobileAuth0Routes);
227-
// TODO: consolidate with LOCAL_MOCK_AUTH
228-
if (FCC_ENABLE_DEV_LOGIN_MODE) {
229-
void fastify.register(devAuthRoutes);
230-
} else {
231-
void fastify.register(authRoutes);
232-
}
225+
// Routes for signed out users:
226+
void fastify.register(async function (fastify) {
227+
fastify.addHook('onRequest', fastify.authorize);
228+
// TODO(Post-MVP): add the redirectIfSignedIn hook here, rather than in the
229+
// mobileAuth0Routes and authRoutes plugins.
230+
await fastify.register(mobileAuth0Routes);
231+
// TODO: consolidate with LOCAL_MOCK_AUTH
232+
if (FCC_ENABLE_DEV_LOGIN_MODE) {
233+
await fastify.register(devAuthRoutes);
234+
} else {
235+
await fastify.register(authRoutes);
236+
}
237+
});
233238
void fastify.register(chargeStripeRoute);
234239
void fastify.register(signoutRoute);
235240
void fastify.register(emailSubscribtionRoutes);

api/src/plugins/auth0.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import cookies, { sign, unsign } from './cookies';
88
import { auth0Client } from './auth0';
99
import redirectWithMessage, { formatMessage } from './redirect-with-message';
1010
import auth from './auth';
11+
import bouncer from './bouncer';
1112

1213
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
1314
jest.mock('../utils/env', () => ({
@@ -24,6 +25,7 @@ describe('auth0 plugin', () => {
2425
await fastify.register(cookies);
2526
await fastify.register(redirectWithMessage);
2627
await fastify.register(auth);
28+
await fastify.register(bouncer);
2729
await fastify.register(auth0Client);
2830
await fastify.register(prismaPlugin);
2931
});

api/src/plugins/auth0.ts

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,21 +53,28 @@ export const auth0Client: FastifyPluginCallbackTypebox = fp(
5353
}
5454
});
5555

56-
fastify.get('/signin', async function (request, reply) {
57-
const returnTo = request.headers.referer ?? `${HOME_LOCATION}/learn`;
58-
void reply.setCookie('login-returnto', returnTo, {
59-
domain: COOKIE_DOMAIN,
60-
httpOnly: true,
61-
secure: true,
62-
signed: true,
63-
sameSite: 'lax'
64-
});
56+
void fastify.register(function (fastify, _options, done) {
57+
// TODO(Post-MVP): move this into the app, so that we add this hook once for
58+
// all auth routes.
59+
fastify.addHook('onRequest', fastify.redirectIfSignedIn);
6560

66-
const redirectUrl = await this.auth0OAuth.generateAuthorizationUri(
67-
request,
68-
reply
69-
);
70-
void reply.redirect(redirectUrl);
61+
fastify.get('/signin', async function (request, reply) {
62+
const returnTo = request.headers.referer ?? `${HOME_LOCATION}/learn`;
63+
void reply.setCookie('login-returnto', returnTo, {
64+
domain: COOKIE_DOMAIN,
65+
httpOnly: true,
66+
secure: true,
67+
signed: true,
68+
sameSite: 'lax'
69+
});
70+
71+
const redirectUrl = await this.auth0OAuth.generateAuthorizationUri(
72+
request,
73+
reply
74+
);
75+
void reply.redirect(redirectUrl);
76+
});
77+
done();
7178
});
7279

7380
// TODO: use a schema to validate the query params.
@@ -151,5 +158,7 @@ export const auth0Client: FastifyPluginCallbackTypebox = fp(
151158

152159
done();
153160
},
154-
{ dependencies: ['redirect-with-message'] }
161+
// TODO(Post-MVP): remove bouncer dependency when moving redirectIfSignedIn
162+
// out of this plugin.
163+
{ dependencies: ['redirect-with-message', 'bouncer'] }
155164
);

api/src/plugins/bouncer.test.ts

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ describe('bouncer', () => {
4040
fastify.addHook('onRequest', fastify.send401IfNoUser);
4141
});
4242

43-
it('should return 401 if no user is present', async () => {
43+
it('should return 401 if NO user is present', async () => {
4444
const message = {
4545
type: 'danger',
4646
content: 'Something undesirable occurred'
@@ -83,7 +83,10 @@ describe('bouncer', () => {
8383
});
8484
const redirectLocation = `${HOME_LOCATION}?${formatMessage({ type: 'info', content: 'Only authenticated users can access this route. Please sign in and try again.' })}`;
8585

86-
it('should redirect to HOME_LOCATION if no user is present', async () => {
86+
// TODO(Post-MVP): make the redirects consistent between redirectIfNoUser
87+
// and redirectIfSignedIn. Either both should redirect to the referer or
88+
// both should redirect to HOME_LOCATION.
89+
it('should redirect to HOME_LOCATION if NO user is present', async () => {
8790
const message = {
8891
type: 'danger',
8992
content: 'At the moment, content is ignored'
@@ -117,36 +120,35 @@ describe('bouncer', () => {
117120
});
118121
});
119122

120-
describe('fallback hook', () => {
121-
it('should reject unauthed requests when no other reject hooks are added', async () => {
122-
const message = {
123-
type: 'danger',
124-
content: 'Something undesirable occurred'
125-
};
123+
describe('redirectIfSignedIn', () => {
124+
beforeEach(() => {
125+
fastify.addHook('onRequest', fastify.redirectIfSignedIn);
126+
});
127+
128+
it('should redirect to the referer if a user is present', async () => {
126129
authorizeSpy.mockImplementationOnce((req, _reply, done) => {
127-
req.accessDeniedMessage = message;
130+
req.user = { id: '123' };
128131
done();
129132
});
130133
const res = await fastify.inject({
131134
method: 'GET',
132-
url: '/'
135+
url: '/',
136+
headers: {
137+
referer: 'https://www.freecodecamp.org/some/other/path'
138+
}
133139
});
134140

135-
expect(res.json()).toStrictEqual({
136-
type: message.type,
137-
message: message.content
138-
});
141+
expect(res.headers.location).toBe(
142+
'https://www.freecodecamp.org/some/other/path'
143+
);
144+
expect(res.statusCode).toEqual(302);
139145
});
140146

141-
it('should not be called if another reject hook is added', async () => {
142-
const redirectLocation = `${HOME_LOCATION}?${formatMessage({ type: 'info', content: 'Only authenticated users can access this route. Please sign in and try again.' })}`;
147+
it('should not alter the response if NO user is present', async () => {
143148
const message = {
144149
type: 'danger',
145-
content: 'Something undesirable occurred'
150+
content: 'At the moment, content is ignored'
146151
};
147-
// using redirectIfNoUser as the reject hook since then it's obvious that
148-
// the fallback hook is not called.
149-
fastify.addHook('onRequest', fastify.redirectIfNoUser);
150152
authorizeSpy.mockImplementationOnce((req, _reply, done) => {
151153
req.accessDeniedMessage = message;
152154
done();
@@ -156,8 +158,8 @@ describe('bouncer', () => {
156158
url: '/'
157159
});
158160

159-
expect(res.headers.location).toBe(redirectLocation);
160-
expect(res.statusCode).toEqual(302);
161+
expect(res.json()).toEqual({ foo: 'bar' });
162+
expect(res.statusCode).toEqual(200);
161163
});
162164
});
163165
});

api/src/plugins/bouncer.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ declare module 'fastify' {
1010
interface FastifyInstance {
1111
send401IfNoUser: (req: FastifyRequest, reply: FastifyReply) => void;
1212
redirectIfNoUser: (req: FastifyRequest, reply: FastifyReply) => void;
13+
redirectIfSignedIn: (req: FastifyRequest, reply: FastifyReply) => void;
1314
}
1415
}
1516

@@ -40,9 +41,20 @@ const plugin: FastifyPluginCallback = (fastify, _options, done) => {
4041
}
4142
);
4243

43-
fastify.addHook('preParsing', fastify.send401IfNoUser);
44+
fastify.decorate(
45+
'redirectIfSignedIn',
46+
async function (req: FastifyRequest, reply: FastifyReply) {
47+
if (req.user) {
48+
const { returnTo } = getRedirectParams(req);
49+
await reply.redirect(returnTo);
50+
}
51+
}
52+
);
4453

4554
done();
4655
};
4756

48-
export default fp(plugin, { dependencies: ['auth', 'redirect-with-message'] });
57+
export default fp(plugin, {
58+
dependencies: ['auth', 'redirect-with-message'],
59+
name: 'bouncer'
60+
});

api/src/routes/auth.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ export const mobileAuth0Routes: FastifyPluginCallback = (
5656
})
5757
);
5858

59+
// TODO(Post-MVP): move this into the app, so that we add this hook once for
60+
// all auth routes.
61+
fastify.addHook('onRequest', fastify.redirectIfSignedIn);
62+
5963
fastify.get('/mobile-login', async req => {
6064
const email = await getEmailFromAuth0(req);
6165

client/gatsby-config.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,14 @@ module.exports = {
3030
}
3131
},
3232
'gatsby-plugin-react-helmet',
33-
'gatsby-plugin-postcss',
33+
{
34+
resolve: 'gatsby-plugin-postcss',
35+
options: {
36+
postcssOptions: {
37+
config: path.resolve(__dirname, 'postcss.config.js')
38+
}
39+
}
40+
},
3441
{
3542
resolve: 'gatsby-plugin-create-client-paths',
3643
options: {

client/gatsby-node.js

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,7 @@ exports.onCreateNode = function onCreateNode({ node, actions, getNode }) {
2323
if (node.internal.type === 'MarkdownRemark') {
2424
const slug = createFilePath({ node, getNode });
2525
if (!slug.includes('LICENSE')) {
26-
const {
27-
frontmatter: { component = '' }
28-
} = node;
2926
createNodeField({ node, name: 'slug', value: slug });
30-
createNodeField({ node, name: 'component', value: component });
3127
}
3228
}
3329
};
@@ -74,12 +70,14 @@ exports.createPages = async function createPages({
7470
) {
7571
edges {
7672
node {
73+
id
7774
challenge {
7875
block
7976
blockType
8077
certification
8178
challengeType
8279
dashedName
80+
demoType
8381
disableLoopProtectTests
8482
disableLoopProtectPreview
8583
fields {
@@ -121,17 +119,14 @@ exports.createPages = async function createPages({
121119
fields {
122120
slug
123121
nodeIdentity
124-
component
125122
}
126123
frontmatter {
127124
certification
128125
block
129126
superBlock
130127
title
131128
}
132-
htmlAst
133129
id
134-
excerpt
135130
}
136131
}
137132
}

0 commit comments

Comments
 (0)