• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

GEWIS / sudosos-backend / 25753937432

12 May 2026 09:17AM UTC coverage: 88.117% (-1.0%) from 89.089%
25753937432

push

github

web-flow
chore(deps): fix missing dependencies for running docs:dev (#911)

3925 of 4574 branches covered (85.81%)

Branch coverage included in aggregate %.

20093 of 22683 relevant lines covered (88.58%)

1125.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.81
/src/controller/member-authentication-secure-controller.ts
1
/**
1✔
2
 *  SudoSOS back-end API service.
3
 *  Copyright (C) 2026 Study association GEWIS
4
 *
5
 *  This program is free software: you can redistribute it and/or modify
6
 *  it under the terms of the GNU Affero General Public License as published
7
 *  by the Free Software Foundation, either version 3 of the License, or
8
 *  (at your option) any later version.
9
 *
10
 *  This program is distributed in the hope that it will be useful,
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 *  GNU Affero General Public License for more details.
14
 *
15
 *  You should have received a copy of the GNU Affero General Public License
16
 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
17
 *
18
 *  @license
19
 */
1✔
20

21
/**
1✔
22
 * This is the module page of the member-authentication-secure-controller.
23
 *
24
 * @module authentication
25
 */
1✔
26

27
import { Response } from 'express';
28
import log4js, { Logger } from 'log4js';
29
import BaseController, { BaseControllerOptions } from './base-controller';
30
import Policy from './policy';
31
import { RequestWithToken } from '../middleware/token-middleware';
32
import TokenHandler from '../authentication/token-handler';
33
import User from '../entity/user/user';
34
import PointOfSale from '../entity/point-of-sale/point-of-sale';
35
import { UserType } from '../entity/user/user';
36
import AuthenticationController from './authentication-controller';
37
import MemberAuthenticationSecurePinRequest from './request/member-authentication-secure-pin-request';
38
import MemberUser from '../entity/user/member-user';
39
import UserService from '../service/user-service';
40

41
/**
1✔
42
 * Handles authenticated-only member authentication endpoints for secure PIN authentication.
43
 * All endpoints require valid JWT tokens and build upon existing authentication.
44
 *
45
 * @promote
46
 */
1✔
47
export default class MemberAuthenticationSecureController extends BaseController {
1✔
48
  private logger: Logger = log4js.getLogger('MemberAuthenticationSecureController');
1✔
49

50
  /**
51
   * Reference to the token handler of the application.
52
   */
53
  protected tokenHandler: TokenHandler;
54

55
  /**
1✔
56
   * Creates a new member authentication secure controller instance.
57
   * @param options - The options passed to the base controller.
58
   * @param tokenHandler - The token handler for creating signed tokens.
59
   */
1✔
60
  public constructor(options: BaseControllerOptions, tokenHandler: TokenHandler) {
1✔
61
    super(options);
2✔
62
    this.configureLogger(this.logger);
2✔
63
    this.tokenHandler = tokenHandler;
2✔
64
  }
2✔
65

66
  /**
1✔
67
   * @inheritdoc
68
   */
1✔
69
  public getPolicy(): Policy {
1✔
70
    return {
2✔
71
      '/member/pin-secure': {
2✔
72
        POST: {
2✔
73
          policy: async () => Promise.resolve(true),
2✔
74
          handler: this.secureMemberPINLogin.bind(this),
2✔
75
          restrictions: { lesser: false },
2✔
76
        },
2✔
77
      },
2✔
78
    };
2✔
79
  }
2✔
80

81
  /**
1✔
82
   * POST /authentication/member/pin-secure
83
   * @summary Secure member PIN authentication that requires POS user authentication
84
   * @operationId secureMemberPINAuthentication
85
   * @tags authenticate - Operations of authentication controller
86
   * @security JWT
87
   * @param {MemberAuthenticationSecurePinRequest} request.body.required - The PIN login request with posId
88
   * @return {AuthenticationResponse} 200 - The created json web token
89
   * @return {string} 403 - Authentication error (invalid POS user or credentials)
90
   * @return {string} 500 - Internal server error
91
   */
1✔
92
  private async secureMemberPINLogin(req: RequestWithToken, res: Response): Promise<void> {
1✔
93
    const body = req.body as MemberAuthenticationSecurePinRequest;
6✔
94
    this.logger.trace('Secure member PIN authentication for memberId', body.memberId, 'by POS user', req.token.user.id);
6✔
95

96
    try {
6✔
97
      // Verify the caller is a POS user
6✔
98
      const tokenUser = await User.findOne(UserService.getOptions({ id: req.token.user.id, allowPos: true }));
6✔
99
      if (!tokenUser || tokenUser.type !== UserType.POINT_OF_SALE) {
6✔
100
        res.status(403).json('Only POS users can use secure member PIN authentication.');
1✔
101
        return;
1✔
102
      }
1✔
103

104
      // Verify the POS user's ID matches the posId in the request
5✔
105
      const pointOfSale = await PointOfSale.findOne({ where: { user: { id: tokenUser.id } } });
5✔
106
      if (!pointOfSale || pointOfSale.id !== body.posId) {
6✔
107
        res.status(403).json('POS user ID does not match the requested posId.');
1✔
108
        return;
1✔
109
      }
1✔
110

111
      // Look up the member user by memberId
4✔
112
      const memberUser = await MemberUser.findOne({
4✔
113
        where: { memberId: body.memberId },
4✔
114
        relations: ['user'],
4✔
115
      });
4✔
116

117
      if (!memberUser) {
6✔
118
        res.status(403).json({
1✔
119
          message: `User ${body.memberId} not registered`,
1✔
120
        });
1✔
121
        return;
1✔
122
      }
1✔
123

124
      // Reuse the PIN login constructor logic
3✔
125
      await (AuthenticationController.PINLoginConstructor(this.roleManager,
3✔
126
        this.tokenHandler, body.pin, memberUser.user.id, body.posId))(req, res);
3✔
127
    } catch (error) {
6!
128
      this.logger.error('Could not authenticate using secure member PIN:', error);
×
129
      res.status(500).json('Internal server error.');
×
130
    }
×
131
  }
6✔
132
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc