• 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

91.6
/src/gewis/controller/gewis-authentication-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 gewis-authentication-controller.
23
 *
24
 * @module GEWIS
25
 */
1✔
26

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

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

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

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

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

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

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

121
    res.json(Config.get().gewis.gewiswebPublicToken);
1✔
122
  }
1✔
123

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

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

156
      let memberUser = await MemberUser.findOne({
3✔
157
        where: { memberId: gewisweb.lidnr },
3✔
158
        relations: UserService.getRelations<MemberUser>(),
3✔
159
      });
3✔
160
      if (!memberUser) {
5!
161
        this.logger.log('User not found in database, creating user');
×
162
        memberUser = await new Gewis().createUserFromWeb(gewisweb);
×
163
      } else {
5✔
164
        //
3✔
165
        const update = webResponseToUpdate(gewisweb);
3✔
166
        await UserService.updateUser(memberUser.user.id, { ...update, active: true });
3✔
167
      }
3✔
168

169
      // If a LOCAL_USER authenticates through GEWIS, implicitly convert the account back to a MEMBER account
3✔
170
      if (memberUser.user.type === UserType.LOCAL_USER) {
5✔
171
        await UserService.updateUserType(memberUser.user, UserType.MEMBER);
1✔
172
      }
1✔
173

174
      const result = await new AuthenticationService().getSaltedToken({
3✔
175
        user: memberUser.user,
3✔
176
        context: { roleManager: this.roleManager, tokenHandler: this.tokenHandler },
3✔
177
        salt: body.nonce,
3✔
178
      });
3✔
179
      res.json(AuthenticationService.asAuthenticationResponse(result.user, result.roles, result.organs, result.token));
3✔
180
    } catch (error) {
5!
181
      this.logger.error('Could not create token:', error);
×
182
      res.status(500).json('Internal server error.');
×
183
    }
×
184
  }
5✔
185

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

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

210
}
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