Skip to content

Commit 4ec8825

Browse files
committed
allow admin to ban user, log banned user out and prevent log in
1 parent 2a5fae8 commit 4ec8825

16 files changed

Lines changed: 349 additions & 92 deletions

File tree

api/src/controllers/user.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,11 @@ export const login = async (req, res) => {
171171
const user = await User.findOne({ username: req.body.username })
172172

173173
if (user) {
174+
if (user.banned) {
175+
res.status(403).send('User is banned')
176+
return
177+
}
178+
174179
const matches = await bcrypt.compare(req.body.password, user.password)
175180

176181
if (matches) {
@@ -414,6 +419,7 @@ export const fetchUser = async (req, res) => {
414419
role: 1,
415420
...(req.userRole === 'admin' ? { email: 1, invitedBy: 1 } : {}),
416421
remainingInvites: 1,
422+
banned: 1,
417423
},
418424
},
419425
{
@@ -589,3 +595,51 @@ export const verifyUserEmail = async (req, res) => {
589595
res.status(400).send('Request must include token')
590596
}
591597
}
598+
599+
export const banUser = async (req, res) => {
600+
try {
601+
if (req.userRole !== 'admin') {
602+
res.status(401).send('You do not have permission to ban a user')
603+
return
604+
}
605+
606+
const user = await User.findOne({ username: req.params.username })
607+
if (!user) {
608+
res.status(404).send('User does not exist')
609+
return
610+
}
611+
612+
await User.findOneAndUpdate(
613+
{ username: req.params.username },
614+
{ $set: { banned: true } }
615+
)
616+
617+
res.sendStatus(200)
618+
} catch (e) {
619+
res.status(500).send(e.message)
620+
}
621+
}
622+
623+
export const unbanUser = async (req, res) => {
624+
try {
625+
if (req.userRole !== 'admin') {
626+
res.status(401).send('You do not have permission to unban a user')
627+
return
628+
}
629+
630+
const user = await User.findOne({ username: req.params.username })
631+
if (!user) {
632+
res.status(404).send('User does not exist')
633+
return
634+
}
635+
636+
await User.findOneAndUpdate(
637+
{ username: req.params.username },
638+
{ $set: { banned: false } }
639+
)
640+
641+
res.sendStatus(200)
642+
} catch (e) {
643+
res.status(500).send(e.message)
644+
}
645+
}

api/src/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import {
2121
getUserRole,
2222
getUserVerifiedEmailStatus,
2323
verifyUserEmail,
24+
banUser,
25+
unbanUser,
2426
} from './controllers/user'
2527
import {
2628
uploadTorrent,
@@ -153,6 +155,8 @@ app.post('/account/change-password', changePassword)
153155
app.get('/account/get-role', getUserRole)
154156
app.get('/account/get-verified', getUserVerifiedEmailStatus)
155157
app.get('/user/:username', fetchUser)
158+
app.post('/user/ban/:username', banUser)
159+
app.post('/user/unban/:username', unbanUser)
156160

157161
// torrent routes
158162
app.post('/torrent/upload', uploadTorrent)

api/src/middleware/auth.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@ const auth = async (req, res, next) => {
66
const token = req.headers.authorization.replace('Bearer ', '')
77
try {
88
const decoded = jwt.verify(token, process.env.SQ_JWT_SECRET)
9-
109
if (decoded) {
1110
const user = await User.findOne({ _id: decoded.id })
1211
if (user) {
12+
if (user.banned) {
13+
res.status(403).send('User is banned')
14+
return
15+
}
1316
req.userId = user._id
1417
req.userRole = user.role
1518
next()

client/pages/account.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,9 @@ export const getServerSideProps = withAuthServerSideProps(async ({ token }) => {
289289
Authorization: `Bearer ${token}`,
290290
},
291291
})
292+
if (userRes.status === 403 && (await userRes.text()) === 'User is banned') {
293+
throw 'banned'
294+
}
292295
const user = await userRes.json()
293296
const invitesRes = await fetch(`${SQ_API_URL}/account/invites`, {
294297
headers: {
@@ -299,6 +302,7 @@ export const getServerSideProps = withAuthServerSideProps(async ({ token }) => {
299302
const invites = await invitesRes.json()
300303
return { props: { invites, user, userRole: role } }
301304
} catch (e) {
305+
if (e === 'banned') throw 'banned'
302306
return { props: {} }
303307
}
304308
})

client/pages/announcements/[slug]/edit.js

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,28 @@ export const getServerSideProps = withAuthServerSideProps(
107107

108108
const { role } = jwt.verify(token, SQ_JWT_SECRET)
109109

110-
const announcementRes = await fetch(`${SQ_API_URL}/announcements/${slug}`, {
111-
headers: {
112-
'Content-Type': 'application/json',
113-
Authorization: `Bearer ${token}`,
114-
},
115-
})
116-
const announcement = await announcementRes.json()
117-
return { props: { announcement, token, userRole: role } }
110+
try {
111+
const announcementRes = await fetch(
112+
`${SQ_API_URL}/announcements/${slug}`,
113+
{
114+
headers: {
115+
'Content-Type': 'application/json',
116+
Authorization: `Bearer ${token}`,
117+
},
118+
}
119+
)
120+
if (
121+
announcementRes.status === 403 &&
122+
(await announcementRes.text()) === 'User is banned'
123+
) {
124+
throw 'banned'
125+
}
126+
const announcement = await announcementRes.json()
127+
return { props: { announcement, token, userRole: role } }
128+
} catch (e) {
129+
if (e === 'banned') throw 'banned'
130+
return { props: {} }
131+
}
118132
}
119133
)
120134

client/pages/announcements/[slug]/index.js

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -159,14 +159,28 @@ export const getServerSideProps = withAuthServerSideProps(
159159

160160
const { role } = jwt.verify(token, SQ_JWT_SECRET)
161161

162-
const announcementRes = await fetch(`${SQ_API_URL}/announcements/${slug}`, {
163-
headers: {
164-
'Content-Type': 'application/json',
165-
Authorization: `Bearer ${token}`,
166-
},
167-
})
168-
const announcement = await announcementRes.json()
169-
return { props: { announcement, token, userRole: role } }
162+
try {
163+
const announcementRes = await fetch(
164+
`${SQ_API_URL}/announcements/${slug}`,
165+
{
166+
headers: {
167+
'Content-Type': 'application/json',
168+
Authorization: `Bearer ${token}`,
169+
},
170+
}
171+
)
172+
if (
173+
announcementRes.status === 403 &&
174+
(await announcementRes.text()) === 'User is banned'
175+
) {
176+
throw 'banned'
177+
}
178+
const announcement = await announcementRes.json()
179+
return { props: { announcement, token, userRole: role } }
180+
} catch (e) {
181+
if (e === 'banned') throw 'banned'
182+
return { props: {} }
183+
}
170184
}
171185
)
172186

client/pages/announcements/index.js

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -113,27 +113,38 @@ export const getServerSideProps = withAuthServerSideProps(async ({ token }) => {
113113

114114
const { role } = jwt.verify(token, SQ_JWT_SECRET)
115115

116-
const announcementsRes = await fetch(`${SQ_API_URL}/announcements/page/0`, {
117-
headers: {
118-
'Content-Type': 'application/json',
119-
Authorization: `Bearer ${token}`,
120-
},
121-
})
122-
const announcements = await announcementsRes.json()
123-
124-
const pinnedAnnouncementsRes = await fetch(
125-
`${SQ_API_URL}/announcements/pinned`,
126-
{
116+
try {
117+
const announcementsRes = await fetch(`${SQ_API_URL}/announcements/page/0`, {
127118
headers: {
128119
'Content-Type': 'application/json',
129120
Authorization: `Bearer ${token}`,
130121
},
122+
})
123+
if (
124+
announcementsRes.status === 403 &&
125+
(await announcementsRes.text()) === 'User is banned'
126+
) {
127+
throw 'banned'
131128
}
132-
)
133-
const pinnedAnnouncements = await pinnedAnnouncementsRes.json()
129+
const announcements = await announcementsRes.json()
134130

135-
return {
136-
props: { announcements, pinnedAnnouncements, userRole: role || 'user' },
131+
const pinnedAnnouncementsRes = await fetch(
132+
`${SQ_API_URL}/announcements/pinned`,
133+
{
134+
headers: {
135+
'Content-Type': 'application/json',
136+
Authorization: `Bearer ${token}`,
137+
},
138+
}
139+
)
140+
const pinnedAnnouncements = await pinnedAnnouncementsRes.json()
141+
142+
return {
143+
props: { announcements, pinnedAnnouncements, userRole: role || 'user' },
144+
}
145+
} catch (e) {
146+
if (e === 'banned') throw 'banned'
147+
return { props: {} }
137148
}
138149
})
139150

client/pages/categories/[category].js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,16 @@ export const getServerSideProps = withAuthServerSideProps(
6565
},
6666
}
6767
)
68+
if (
69+
searchRes.status === 403 &&
70+
(await searchRes.text()) === 'User is banned'
71+
) {
72+
throw 'banned'
73+
}
6874
const results = await searchRes.json()
6975
return { props: { results } }
7076
} catch (e) {
77+
if (e === 'banned') throw 'banned'
7178
return { props: {} }
7279
}
7380
}

client/pages/index.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,12 @@ export const getServerSideProps = withAuthServerSideProps(async ({ token }) => {
106106
Authorization: `Bearer ${token}`,
107107
},
108108
})
109+
if (
110+
latestRes.status === 403 &&
111+
(await latestRes.text()) === 'User is banned'
112+
) {
113+
throw 'banned'
114+
}
109115
const latest = await latestRes.json()
110116

111117
const verifiedRes = await fetch(`${SQ_API_URL}/account/get-verified`, {
@@ -118,6 +124,7 @@ export const getServerSideProps = withAuthServerSideProps(async ({ token }) => {
118124

119125
return { props: { latest, emailVerified, token } }
120126
} catch (e) {
127+
if (e === 'banned') throw 'banned'
121128
return { props: {} }
122129
}
123130
}, true)

client/pages/reports/[id].js

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,25 @@ export const getServerSideProps = withAuthServerSideProps(
115115

116116
if (role !== 'admin') return { props: { report: null, userRole: role } }
117117

118-
const reportRes = await fetch(`${SQ_API_URL}/reports/${id}`, {
119-
headers: {
120-
'Content-Type': 'application/json',
121-
Authorization: `Bearer ${token}`,
122-
},
123-
})
124-
const report = await reportRes.json()
125-
return { props: { report, token, userRole: role } }
118+
try {
119+
const reportRes = await fetch(`${SQ_API_URL}/reports/${id}`, {
120+
headers: {
121+
'Content-Type': 'application/json',
122+
Authorization: `Bearer ${token}`,
123+
},
124+
})
125+
if (
126+
reportRes.status === 403 &&
127+
(await reportRes.text()) === 'User is banned'
128+
) {
129+
throw 'banned'
130+
}
131+
const report = await reportRes.json()
132+
return { props: { report, token, userRole: role } }
133+
} catch (e) {
134+
if (e === 'banned') throw 'banned'
135+
return { props: {} }
136+
}
126137
}
127138
)
128139

0 commit comments

Comments
 (0)