Skip to content

Commit

Permalink
feat: Permanently locked accounts are no longer allowed to log in
Browse files Browse the repository at this point in the history
  • Loading branch information
spuxx1701 committed Apr 28, 2024
1 parent 577d83b commit d62a24b
Show file tree
Hide file tree
Showing 11 changed files with 330 additions and 6 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/).

## [3.1.0] - 2024-04-28

### Changed

- User accounts that have been locked permanently are no longer allowed to log into potber-api. User accounts that have been locked temporarily are not affected.

## [3.0.0] - 2024-04-05

### Changed
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "potber-api",
"version": "3.0.0",
"version": "3.1.0",
"description": "A modern JSON API for forum.mods.de",
"author": "https://spuxx.dev",
"private": true,
Expand Down
5 changes: 4 additions & 1 deletion src/auth/config/auth.exceptions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { UnauthorizedException } from '@nestjs/common';
import { ForbiddenException, UnauthorizedException } from '@nestjs/common';
import { appExceptions } from 'src/config/app.exceptions';

export const authExceptions = {
Expand All @@ -8,6 +8,9 @@ export const authExceptions = {
wrongCredentials: new UnauthorizedException(
'Login failed (possibly due to wrong credentials).',
),
lockedPermanently: new ForbiddenException(
'The account has been locked permanently. potber-api does not support permenently locked accounts logging in.',
),
},
validate: {
invalidSession: new UnauthorizedException(
Expand Down
29 changes: 29 additions & 0 deletions src/auth/controllers/auth.controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,35 @@ describe('Auth | AuthController', () => {
statusCode: 401,
});
});

it('should fail with 403 if the user account has been locked permanently', async () => {
container.mockServer.use(...authHandlers.login.lockedPermanently);
const request = fakeRequest(container.app, 'POST', '/auth/login');
const response = await request.send({
username: 'Foo',
password: 'Bar',
lifetime: 3600,
});
expect(response.statusCode).toBe(403);
expect(response.body).toStrictEqual({
error: 'Forbidden',
message:
'The account has been locked permanently. potber-api does not support permenently locked accounts logging in.',
statusCode: 403,
});
});

it('should succeed if the user account has been locked termporarily', async () => {
container.mockServer.use(...authHandlers.login.lockedTemporarily);
const request = fakeRequest(container.app, 'POST', '/auth/login');
const response = await request.send({
username: 'Foo',
password: 'Bar',
lifetime: 3600,
});
expect(response.statusCode).toBe(200);
expect(response.body.access_token).toBeDefined();
});
});

describe('/auth/session', () => {
Expand Down
17 changes: 15 additions & 2 deletions src/auth/services/auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { Injectable, Logger, UnauthorizedException } from '@nestjs/common';
import {
ForbiddenException,
Injectable,
Logger,
UnauthorizedException,
} from '@nestjs/common';
import { forumConfig } from 'src/config/forum.config';
import { authExceptions } from '../config/auth.exceptions';
import { LoginResource } from '../resources/login.resource';
Expand Down Expand Up @@ -114,7 +119,14 @@ export class AuthService {
async createSession(cookie: string): Promise<SessionResource> {
try {
const userId = await this.getUserId(cookie);
const { name, avatarUrl } = await this.usersService.findById(userId);
const { name, avatarUrl, locked, status } =
await this.usersService.findById(userId);
// If the use account has been locked permanently, the login should fail.
// We check both locked and status since locked will also be true if the account
// has been locked temporarily.
if (locked && status === 'gesperrt') {
throw authExceptions.login.lockedPermanently;
}
const session: Partial<SessionResource> = {
userId,
username: name,
Expand All @@ -123,6 +135,7 @@ export class AuthService {
};
return session as SessionResource;
} catch (error) {
if (error instanceof ForbiddenException) throw error;
throw new UnauthorizedException(error.message);
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/users/services/users.service.spec.includes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export const userProfileMockData: UserProfileMockDatEntry[] = [
activity: 'online',
status: 'aktiv',
age: '15.10.2007 19:44 (5691 Tage)',
locked: false,
},
},
{
Expand Down Expand Up @@ -250,6 +251,7 @@ export const userProfileMockData: UserProfileMockDatEntry[] = [
activity: 'war heute online',
status: 'aktiv',
age: '17.02.2002 00:00 (7822 Tage)',
locked: false,
},
},
{
Expand Down Expand Up @@ -363,6 +365,7 @@ export const userProfileMockData: UserProfileMockDatEntry[] = [
activity: 'offline seit über sieben Monaten',
status: 'aktiv',
age: '30.08.2004 19:58 (6896 Tage)',
locked: false,
},
},
];
2 changes: 2 additions & 0 deletions src/users/services/users.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export class UsersService {
const ageMatches = html.match(
/Dabei\sseit:<\/td>(?:\s*)<td class="attrv">(.*)<\/td>/,
);
const locked = html.includes('<td class="attrv">gesperrt');
const age = ageMatches[1];
const user: UserResource = {
id,
Expand All @@ -77,6 +78,7 @@ export class UsersService {
status,
avatarUrl,
age,
locked,
};
return user;
}
Expand Down
70 changes: 70 additions & 0 deletions test/msw/handlers/auth/auth.handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,76 @@ export const authHandlers = {
return res(ctx.status(200), ctx.text(xml));
}),
],
lockedPermanently: [
rest.post(`${forumConfig.LOGIN_URL}`, async (req, res, ctx) => {
const xml = readHandlerMockFile(
'auth/login/success/login.response.html',
);
return res(ctx.status(200), ctx.text(xml));
}),
rest.get(`${forumConfig.SSO_URL}`, async (req, res, ctx) => {
return res(
ctx.status(200),
ctx.cookie('MDESID', 'foo', {
secure: true,
sameSite: 'none',
httpOnly: true,
}),
ctx.cookie('MDESID', 'bar', {
secure: true,
sameSite: 'none',
httpOnly: true,
}),
);
}),
rest.get(`${forumConfig.API_URL}boards.php`, (req, res, ctx) => {
const xml = readHandlerMockFile(
'auth/login/success/boards.response.xml',
);
return res(ctx.status(200), ctx.text(xml));
}),
rest.get(`${forumConfig.USER_PAGE_URL}1342456`, (req, res, ctx) => {
const xml = readHandlerMockFile(
'auth/login/locked-permanently/user.response.html',
);
return res(ctx.status(200), ctx.text(xml));
}),
],
lockedTemporarily: [
rest.post(`${forumConfig.LOGIN_URL}`, async (req, res, ctx) => {
const xml = readHandlerMockFile(
'auth/login/success/login.response.html',
);
return res(ctx.status(200), ctx.text(xml));
}),
rest.get(`${forumConfig.SSO_URL}`, async (req, res, ctx) => {
return res(
ctx.status(200),
ctx.cookie('MDESID', 'foo', {
secure: true,
sameSite: 'none',
httpOnly: true,
}),
ctx.cookie('MDESID', 'bar', {
secure: true,
sameSite: 'none',
httpOnly: true,
}),
);
}),
rest.get(`${forumConfig.API_URL}boards.php`, (req, res, ctx) => {
const xml = readHandlerMockFile(
'auth/login/success/boards.response.xml',
);
return res(ctx.status(200), ctx.text(xml));
}),
rest.get(`${forumConfig.USER_PAGE_URL}1342456`, (req, res, ctx) => {
const xml = readHandlerMockFile(
'auth/login/locked-temporarily/user.response.html',
);
return res(ctx.status(200), ctx.text(xml));
}),
],
},

session: {
Expand Down
99 changes: 99 additions & 0 deletions test/msw/handlers/auth/login/locked-permanently/user.response.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<!-- UID 42060 -->
<!-- P 30 -->
<!-- L 0 -->
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-15" />
<title>mods.de Profil: Rattlesnake</title>
<link href="/p/lib/css/infobar.css?20200406" rel="stylesheet" type="text/css" />
<link href="/p/lib/css/profile.css?20210401" rel="stylesheet" type="text/css" />
<script src="/p/lib/js/profile.js?20200406" type="text/javascript"></script>
<script src="/p/lib/js/async.js?20200406" type="text/javascript"></script>
<!-- </head>
<body> -->

<div class="infobar" id="infobar_notloggedin">
<form method="post" action="//login.mods.de/?&redirect=https%3A%2F%2Fmy.mods.de%2F42060%3FUID%3D42060" name="loginform">
<strong>Du bist nicht eingeloggt!</strong>
Möglicherweise kannst du deswegen nicht alles sehen.
<br/>
<table><tr><td>
<span class="nobr">
<input name="login_username" placeholder="Benutzername" />
<input name="login_password" placeholder="Passwort" type="password" />
<select name="login_lifetime">
<!-- <option value="-1">tempor&auml;r</option> -->
<option value="3600">f&uuml;r eine Stunde</option>
<option value="86400">f&uuml;r einen Tag</option>
<option value="604800">f&uuml;r eine Woche</option>
<option value="31536000" selected>f&uuml;r ein Jahr</option>
</select>
<input type="submit" value="login &raquo;" class="button" />
</span>
</td><td>
&nbsp;&nbsp;(<a href="//forum.mods.de/bb//register.php?rd=%2F%2Fmy.mods.de%2F42060">Noch kein mods.de-Account?</a>
/
<a href="//forum.mods.de/bb//register.php?view=newpw&rd=%2F%2Fmy.mods.de%2F42060">Passwort vergessen?</a>)
</td></tr></table>
</form>
</div>
<div id="content">
<script type="text/javascript"><!--
var uid = 42060;
var myuid = 0;
// --></script>
<table>

<tr class="color2 bar"><td></td><td></td><td></td></tr>

<tr class="color1">
<td rowspan="5" class="c vam avatar">
<img src="//forum.mods.de/bb/./avatare/rattlesnake.gif" class="avatar" alt="Avatar" /><br/>
<img alt="*" src="//forum.mods.de/bb/img/rank/links.gif"/><img alt="*" src="//forum.mods.de/bb/img/rank/gruen.gif"/><img alt="*" src="//forum.mods.de/bb/img/rank/gruen.gif"/><img alt="*" src="//forum.mods.de/bb/img/rank/gruen.gif"/><img alt="*" src="//forum.mods.de/bb/img/rank/gruen.gif"/><img alt="*" src="//forum.mods.de/bb/img/rank/gruen.gif"/><img alt="*" src="//forum.mods.de/bb/img/rank/gruen.gif"/><img alt="*" src="//forum.mods.de/bb/img/rank/gruen.gif"/><img alt="*" src="//forum.mods.de/bb/img/rank/gruen.gif"/><img alt="*" src="//forum.mods.de/bb/img/rank/gruen.gif"/><img alt="*" src="//forum.mods.de/bb/img/rank/rechts.gif"/><br/>
<span class="rang">Spamk&ouml;nig</span><br/>
</td>
<td class="attrn">Benutzername:</td>
<td class="attrv">
<div class="backlink"><a href="//my.mods.de/Rattlesnake">my.mods.de/Rattlesnake</a></div>
Rattlesnake </td>
</tr><tr class="color2">
<td class="attrn">Dabei seit:</td>
<td class="attrv">30.09.2001 00:00 (8246 Tage)</td>
</tr><tr class="color1">
<td class="attrn">Zuletzt im Board:</td>
<td class="attrv"><em>privat</em></td>
</tr><tr class="color2">
<td class="attrn">Status:</td>
<td class="attrv">offline seit ewigen Zeiten</td>
</tr><tr class="color1">
<td class="attrn">Accountstatus:</td>
<td class="attrv">gesperrt</td>
</tr>

<tr><td colspan="3" style="padding: 0 0 0 0">

<div class="color2 p">

&raquo; <a href="//forum.mods.de/bb/pm/?a=5&amp;rcpt=42060">PM schreiben</a><br/>
&raquo; <a href="javascript:buddyadd(42060, '046549bd265e69b4b1a5072f05ce7de1');" onclick='return buddyadd_("Rattlesnake");'>In Freundesliste aufnehmen</a><br/>
&raquo; <a href="//forum.mods.de/bb/search.php?search_UID=42060">Beiträge im Forum suchen</a><br/>
</div>

<div class="color3 p linklist"> </div>
<div class="color2 bar"></div>


</td></tr>
</table>
<script type="text/javascript">/*<![CDATA[*/
init(); /*]]>*/</script>

</div>
<div id="authornote">
&lt; <script type='text/javascript'><!--
l365879='de';l1145159='e';l8740391='mod';l267267='nos';l2791247='s';document.write('<'+'a hr'+'ef=\'ma'+'ilt'+'o:'+l1145159+l267267+'@'+l8740391+l2791247+'.'+l365879+'?subject=Erweitertes%20Profil%20'+'\'>'+l1145159+l267267+'@'+l8740391+l2791247+'.'+l365879+'</a>');
// --></script> 2007-2023 &gt;
</div>
</body>
</html>
Loading

0 comments on commit d62a24b

Please sign in to comment.