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

GEWIS / sudosos-backend / 18949526732

28 Oct 2025 08:59AM UTC coverage: 89.883% (-0.004%) from 89.887%
18949526732

push

github

web-flow
refactor: remove old deprecated member-authenticator.ts and move to organ membership (#618)

1373 of 1631 branches covered (84.18%)

Branch coverage included in aggregate %.

28 of 29 new or added lines in 6 files covered. (96.55%)

84 existing lines in 11 files now uncovered.

7218 of 7927 relevant lines covered (91.06%)

1112.1 hits per line

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

85.0
/src/gewis/controller/gewis-authentication-controller.ts
1
/**
2
 *  SudoSOS back-end API service.
3
 *  Copyright (C) 2024  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
 */
20

21
/**
22
 * This is the module page of the gewis-authentication-controller.
23
 *
24
 * @module GEWIS
25
 */
26

27
import { Request, Response } from 'express';
28
import * as jwt from 'jsonwebtoken';
2✔
29
import log4js, { Logger } from 'log4js';
2✔
30
import * as util from 'util';
2✔
31
import BaseController, { BaseControllerOptions } from '../../controller/base-controller';
2✔
32
import Policy from '../../controller/policy';
33
import TokenHandler from '../../authentication/token-handler';
34
import GewisUser from '../entity/gewis-user';
2✔
35
import GewiswebToken from '../gewisweb-token';
36
import GewiswebAuthenticationRequest from './request/gewisweb-authentication-request';
37
import AuthenticationService from '../../service/authentication-service';
2✔
38
import GEWISAuthenticationPinRequest from './request/gewis-authentication-pin-request';
39
import AuthenticationLDAPRequest from '../../controller/request/authentication-ldap-request';
40
import AuthenticationController from '../../controller/authentication-controller';
2✔
41
import Gewis from '../gewis';
2✔
42
import UserService from '../../service/user-service';
2✔
43
import { webResponseToUpdate } from '../helpers/gewis-helper';
2✔
44

45
/**
46
  * The GEWIS authentication controller is responsible for:
47
  * - Verifying user authentications.
48
  * - Handing out json web tokens.
49
  */
50
export default class GewisAuthenticationController extends BaseController {
2✔
51
  /**
52
    * Reference to the logger instance.
53
    */
54
  private logger: Logger = log4js.getLogger('GewisAuthenticationController');
2✔
55

56
  /**
57
    * Reference to the token handler of the application.
58
    */
59
  private tokenHandler: TokenHandler;
60

61
  /**
62
   * The secret key shared with gewisweb for JWT HMAC verification.
63
   */
64
  private gewiswebSecret: string;
65

66
  /**
67
    * Creates a new authentication controller instance.
68
    * @param options - The options passed to the base controller.
69
    * @param tokenHandler - The token handler for creating signed tokens.
70
    * @param gewiswebSecret - The shared JWT secret with gewisweb.
71
    */
72
  public constructor(
73
    options: BaseControllerOptions,
74
    tokenHandler: TokenHandler,
75
    gewiswebSecret: string,
76
  ) {
77
    super(options);
2✔
78
    this.logger.level = process.env.LOG_LEVEL;
2✔
79
    this.tokenHandler = tokenHandler;
2✔
80
    this.gewiswebSecret = gewiswebSecret;
2✔
81
  }
82

83
  /**
84
    * @inheritdoc
85
    */
86
  public getPolicy(): Policy {
87
    return {
2✔
88
      '/gewisweb': {
89
        GET: {
90
          policy: async () => true,
1✔
91
          handler: this.getGEWISWebPublic.bind(this),
92
        },
93
        POST: {
94
          body: { modelName: 'GewiswebAuthenticationRequest' },
95
          policy: async () => true,
5✔
96
          handler: this.gewiswebLogin.bind(this),
97
        },
98
      },
99
      '/GEWIS/pin': {
100
        POST: {
101
          body: { modelName: 'GEWISAuthenticationPinRequest' },
102
          policy: async () => true,
3✔
103
          handler: this.gewisPINLogin.bind(this),
104
        },
105
      },
106
      '/GEWIS/LDAP': {
107
        POST: {
108
          body: { modelName: 'AuthenticationLDAPRequest' },
109
          policy: async () => true,
2✔
110
          handler: this.ldapLogin.bind(this),
111
          restrictions: { availableDuringMaintenance: true },
112
        },
113
      },
114
    };
115
  }
116

117
  /**
118
   * GET /authentication/gewisweb
119
   * @summary Get the GEWISWeb public token used by SudoSOS
120
   * @operationId getGEWISWebPublic
121
   * @tags authenticate - Operations of authentication controller
122
   * @returns {string} 200 - Public key
123
   */
124
  public async getGEWISWebPublic(req: Request, res: Response): Promise<void> {
125
    this.logger.trace('Get GEWISWeb public token by IP', req.ip);
1✔
126

127
    res.json(process.env.GEWISWEB_PUBLIC_TOKEN);
1✔
128
  }
129

130
  /**
131
    * POST /authentication/gewisweb
132
    * @summary GEWIS login verification based on gewisweb JWT tokens.
133
    * This method verifies the validity of the gewisweb JWT token, and returns a SudoSOS
134
    * token if the GEWIS token is valid.
135
    * @operationId gewisWebAuthentication
136
    * @tags authenticate - Operations of authentication controller
137
    * @param {GewiswebAuthenticationRequest} request.body.required - The mock login.
138
    * @return {AuthenticationResponse} 200 - The created json web token.
139
    * @return {MessageResponse} 403 - The created json web token.
140
    * @return {string} 400 - Validation error.
141
    */
142
  public async gewiswebLogin(req: Request, res: Response): Promise<void> {
143
    const body = req.body as GewiswebAuthenticationRequest;
5✔
144

145
    try {
5✔
146
      let gewisweb: GewiswebToken;
147
      try {
5✔
148
        gewisweb = await util.promisify(jwt.verify)
5✔
149
          .bind(null, body.token, this.gewiswebSecret, {
150
            algorithms: ['HS512'],
151
            complete: false,
152
          })();
153
      } catch (error) {
154
        // Invalid token supplied.
155
        res.status(403).json({
2✔
156
          message: 'Invalid JWT signature',
157
        });
158
        return;
2✔
159
      }
160
      this.logger.trace('Gewisweb authentication for user with membership id', gewisweb.lidnr);
3✔
161

162
      let gewisUser = await GewisUser.findOne({
3✔
163
        where: { gewisId: gewisweb.lidnr },
164
        relations: ['user'],
165
      });
166
      if (!gewisUser) {
3!
UNCOV
167
        this.logger.log('User not found in database, creating user');
×
168
        gewisUser = await new Gewis().createUserFromWeb(gewisweb);
×
169
      } else {
170
        //
171
        const update = webResponseToUpdate(gewisweb);
3✔
172
        await UserService.updateUser(gewisUser.user.id, { ...update, active: true });
3✔
173
      }
174

175
      const response = await new AuthenticationService().getSaltedToken(
3✔
176
        gewisUser.user,
177
        { roleManager: this.roleManager, tokenHandler: this.tokenHandler },
178
        false,
179
        body.nonce,
180
      );
181
      res.json(response);
3✔
182
    } catch (error) {
183
      this.logger.error('Could not create token:', error);
×
184
      res.status(500).json('Internal server error.');
×
185
    }
186
  }
187

188
  /**
189
   * POST /authentication/GEWIS/LDAP
190
   * @summary LDAP login and hand out token
191
   *    If user has never signed in before this also creates an GEWIS account.
192
   * @operationId gewisLDAPAuthentication
193
   * @tags authenticate - Operations of authentication controller
194
   * @param {AuthenticationLDAPRequest} request.body.required - The LDAP login.
195
   * @return {AuthenticationResponse} 200 - The created json web token.
196
   * @return {string} 400 - Validation error.
197
   * @return {string} 403 - Authentication error.
198
   */
199
  public async ldapLogin(req: Request, res: Response): Promise<void> {
200
    const body = req.body as AuthenticationLDAPRequest;
2✔
201
    this.logger.trace('GEWIS LDAP authentication for user', body.accountName);
2✔
202

203
    try {
2✔
204
      const gewisService = new Gewis();
2✔
205
      await AuthenticationController.LDAPLoginConstructor(this.roleManager, this.tokenHandler, gewisService.findOrCreateGEWISUserAndBind.bind(gewisService))(req, res);
2✔
206
    } catch (error) {
207
      this.logger.error('Could not authenticate using LDAP:', error);
×
208
      res.status(500).json('Internal server error.');
×
209
    }
210
  }
211

212
  /**
213
   * POST /authentication/GEWIS/pin
214
   * @summary PIN login and hand out token.
215
   * @operationId gewisPinAuthentication
216
   * @tags authenticate - Operations of authentication controller
217
   * @param {GEWISAuthenticationPinRequest} request.body.required - The PIN login.
218
   * @return {AuthenticationResponse} 200 - The created json web token.
219
   * @return {string} 400 - Validation error.
220
   * @return {string} 403 - Authentication error.
221
   */
222
  public async gewisPINLogin(req: Request, res: Response): Promise<void> {
223
    const { pin, gewisId } = req.body as GEWISAuthenticationPinRequest;
3✔
224
    this.logger.trace('GEWIS PIN authentication for user', gewisId);
3✔
225

226
    try {
3✔
227
      const gewisUser = await GewisUser.findOne({
3✔
228
        where: { gewisId },
229
        relations: ['user'],
230
      });
231

232
      if (!gewisUser) {
3✔
233
        res.status(403).json({
1✔
234
          message: `User ${gewisId} not registered`,
235
        });
236
        return;
1✔
237
      }
238
      await (AuthenticationController.PINLoginConstructor(this.roleManager, this.tokenHandler,
2✔
239
        pin, gewisUser.user.id))(req, res);
240
    } catch (error) {
241
      this.logger.error('Could not authenticate using PIN:', error);
×
242
      res.status(500).json('Internal server error.');
×
243
    }
244
  }
245
}
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