blob: 77d194517e50a4988cdb2dab4ef2cb239f004af1 [file] [log] [blame]
simon80b7b3b2022-09-28 17:50:10 -04001/* eslint-disable no-undef */
2// TODO: This hides eslint errors for this file. This should be removed once this file is cleaned up.
3
simon218d3d12022-10-01 17:27:01 -04004import { PromiseExecutor } from '../../model/util';
5
Adrien Béraud6ecaa402021-04-06 17:37:25 -04006/*
7 * Copyright (c) 2017-2021 Savoir-faire Linux Inc.
8 *
9 * Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <https://www.gnu.org/licenses/>.
23 */
24
simon218d3d12022-10-01 17:27:01 -040025interface AuthManagerState {
26 initialized: boolean;
27 authenticated: boolean;
28 setupComplete: boolean;
29 error: boolean;
30}
31
32interface AuthManagerTask extends PromiseExecutor<Response> {
33 url: string;
34 init?: RequestInit;
35}
36
37interface InitData {
38 loggedin?: true;
39 username?: string;
40 type?: string;
41 setupComplete?: boolean;
42}
43
44type OnAuthChanged = (auth: AuthManagerState) => void;
45
Adrien Béraud6ecaa402021-04-06 17:37:25 -040046class AuthManager {
simon218d3d12022-10-01 17:27:01 -040047 private authenticating: boolean;
48 private readonly _state: AuthManagerState;
49 private tasks: AuthManagerTask[];
50 private onAuthChanged: OnAuthChanged | undefined;
51
simond47ef9e2022-09-28 22:24:28 -040052 constructor() {
53 console.log('AuthManager()');
54 this.authenticating = false;
Adrien Béraude74741b2021-04-19 13:22:54 -040055
simon218d3d12022-10-01 17:27:01 -040056 this._state = {
simond47ef9e2022-09-28 22:24:28 -040057 initialized: false,
58 authenticated: true,
59 setupComplete: true,
60 error: false,
61 };
Adrien Béraude74741b2021-04-19 13:22:54 -040062
simond47ef9e2022-09-28 22:24:28 -040063 this.tasks = [];
64 this.onAuthChanged = undefined;
Adrien Béraude5cad982021-06-07 10:05:50 -040065
simon218d3d12022-10-01 17:27:01 -040066 // @ts-ignore
simond47ef9e2022-09-28 22:24:28 -040067 if (initData) {
68 console.log('Using static initData');
simon218d3d12022-10-01 17:27:01 -040069 // @ts-ignore
simond47ef9e2022-09-28 22:24:28 -040070 this.setInitData(initData);
71 return;
Adrien Béraud6ecaa402021-04-06 17:37:25 -040072 }
simond47ef9e2022-09-28 22:24:28 -040073 }
Adrien Béraud6ecaa402021-04-06 17:37:25 -040074
simond47ef9e2022-09-28 22:24:28 -040075 isAuthenticated() {
simon218d3d12022-10-01 17:27:01 -040076 return this._state.authenticated;
simond47ef9e2022-09-28 22:24:28 -040077 }
78
79 getState() {
simon218d3d12022-10-01 17:27:01 -040080 return this._state;
simond47ef9e2022-09-28 22:24:28 -040081 }
82
simon218d3d12022-10-01 17:27:01 -040083 setInitData(data: InitData) {
simond47ef9e2022-09-28 22:24:28 -040084 this.authenticating = false;
simon218d3d12022-10-01 17:27:01 -040085 this._state.initialized = true;
simond47ef9e2022-09-28 22:24:28 -040086 if (data.username) {
simon218d3d12022-10-01 17:27:01 -040087 Object.assign(this._state, {
simond47ef9e2022-09-28 22:24:28 -040088 authenticated: true,
89 setupComplete: true,
90 error: false,
91 user: { username: data.username, type: data.type },
92 });
93 } else {
simon218d3d12022-10-01 17:27:01 -040094 Object.assign(this._state, {
simond47ef9e2022-09-28 22:24:28 -040095 authenticated: false,
simon218d3d12022-10-01 17:27:01 -040096 setupComplete: data.setupComplete ?? true,
simond47ef9e2022-09-28 22:24:28 -040097 error: false,
98 });
Adrien Béraud6ecaa402021-04-06 17:37:25 -040099 }
simond47ef9e2022-09-28 22:24:28 -0400100 console.log('Init ended');
101 /*if (this.onAuthChanged)
simon218d3d12022-10-01 17:27:01 -0400102 this.onAuthChanged(this._state)*/
simond47ef9e2022-09-28 22:24:28 -0400103 }
Adrien Béraude5cad982021-06-07 10:05:50 -0400104
simon218d3d12022-10-01 17:27:01 -0400105 init(cb: OnAuthChanged) {
simond47ef9e2022-09-28 22:24:28 -0400106 this.onAuthChanged = cb;
simon218d3d12022-10-01 17:27:01 -0400107 if (this._state.initialized || this.authenticating) return;
simond47ef9e2022-09-28 22:24:28 -0400108 /*if (initData) {
Adrien Béraude5cad982021-06-07 10:05:50 -0400109 console.log("Using static initData")
110 this.setInitData(initData)
111 return
112 }*/
simond47ef9e2022-09-28 22:24:28 -0400113 this.authenticating = true;
114 fetch('/auth')
115 .then(async (response) => {
116 this.authenticating = false;
simon218d3d12022-10-01 17:27:01 -0400117 this._state.initialized = true;
simond47ef9e2022-09-28 22:24:28 -0400118 if (response.status === 200) {
119 this.setInitData(await response.json());
120 } else if (response.status === 401) {
121 this.setInitData(await response.json());
Adrien Béraude74741b2021-04-19 13:22:54 -0400122 } else {
simon218d3d12022-10-01 17:27:01 -0400123 this._state.error = true;
124 if (this.onAuthChanged) this.onAuthChanged(this._state);
Adrien Béraude74741b2021-04-19 13:22:54 -0400125 }
simond47ef9e2022-09-28 22:24:28 -0400126 })
127 .catch((e) => {
128 this.authenticating = false;
129 console.log(e);
130 });
131 }
Adrien Béraude74741b2021-04-19 13:22:54 -0400132
simond47ef9e2022-09-28 22:24:28 -0400133 deinit() {
134 console.log('Deinit');
135 this.onAuthChanged = undefined;
136 }
Adrien Béraud6ecaa402021-04-06 17:37:25 -0400137
simon218d3d12022-10-01 17:27:01 -0400138 async setup(password: string) {
139 if (this.authenticating || this._state.setupComplete) return;
simond47ef9e2022-09-28 22:24:28 -0400140 console.log('Starting setup');
141 this.authenticating = true;
142 const response = await fetch(`/setup`, {
143 method: 'POST',
144 headers: {
145 Accept: 'application/json',
146 'Content-Type': 'application/json',
147 },
148 body: JSON.stringify({ password }),
149 });
150 console.log(response);
151 if (response.ok) {
152 console.log('Success, going home');
153 //navigate('/')
Adrien Béraud6ecaa402021-04-06 17:37:25 -0400154 }
simon80b7b3b2022-09-28 17:50:10 -0400155
simond47ef9e2022-09-28 22:24:28 -0400156 this.authenticating = false;
simon218d3d12022-10-01 17:27:01 -0400157 this._state.setupComplete = true;
158 if (this.onAuthChanged) this.onAuthChanged(this._state);
simond47ef9e2022-09-28 22:24:28 -0400159 return response.ok;
160 }
Adrien Béraud6ecaa402021-04-06 17:37:25 -0400161
simon218d3d12022-10-01 17:27:01 -0400162 authenticate(username: string, password: string) {
simond47ef9e2022-09-28 22:24:28 -0400163 if (this.authenticating) return;
164 console.log('Starting authentication');
165 this.authenticating = true;
166 fetch(`/auth/local?username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`, {
167 method: 'POST',
168 })
169 .then((response) => {
170 console.log(response);
171 this.authenticating = false;
simon218d3d12022-10-01 17:27:01 -0400172 this._state.authenticated = response.ok && response.status === 200;
173 if (this.onAuthChanged) this.onAuthChanged(this._state);
174 while (true) {
simond47ef9e2022-09-28 22:24:28 -0400175 const task = this.tasks.shift();
simon218d3d12022-10-01 17:27:01 -0400176 if (!task) {
177 break;
178 }
179 if (this._state.authenticated) {
simond47ef9e2022-09-28 22:24:28 -0400180 fetch(task.url, task.init)
181 .then((res) => task.resolve(res))
182 .catch((e) => console.log('Error executing pending task: ' + e));
simon218d3d12022-10-01 17:27:01 -0400183 } else {
184 task.reject(new Error('Authentication failed'));
185 }
Adrien Béraud6ecaa402021-04-06 17:37:25 -0400186 }
simond47ef9e2022-09-28 22:24:28 -0400187 })
188 .catch((e) => {
189 this.authenticating = false;
190 console.log(e);
191 });
192 }
193
194 disconnect() {
195 console.log('Disconnect');
simon218d3d12022-10-01 17:27:01 -0400196 this._state.authenticated = false;
197 if (this.onAuthChanged) this.onAuthChanged(this._state);
simond47ef9e2022-09-28 22:24:28 -0400198 }
199
simon218d3d12022-10-01 17:27:01 -0400200 fetch(url: string, init?: RequestInit): Promise<Response> {
simond47ef9e2022-09-28 22:24:28 -0400201 console.log(`fetch ${url}`);
simon218d3d12022-10-01 17:27:01 -0400202 if (!this._state.authenticated) {
simond47ef9e2022-09-28 22:24:28 -0400203 if (!init || !init.method || init.method === 'GET') {
simon218d3d12022-10-01 17:27:01 -0400204 return new Promise<Response>((resolve, reject) => this.tasks.push({ url, init, resolve, reject }));
simond47ef9e2022-09-28 22:24:28 -0400205 } else {
simon218d3d12022-10-01 17:27:01 -0400206 return new Promise<Response>((resolve, reject) => reject('Not authenticated'));
simond47ef9e2022-09-28 22:24:28 -0400207 }
Adrien Béraud6ecaa402021-04-06 17:37:25 -0400208 }
simond47ef9e2022-09-28 22:24:28 -0400209 return fetch(url, init).then((response) => {
210 if (response.status === 401) {
211 this.disconnect();
212 return this.fetch(url, init);
213 }
214 return response;
215 });
216 }
Adrien Béraud6ecaa402021-04-06 17:37:25 -0400217}
218
simond47ef9e2022-09-28 22:24:28 -0400219export default new AuthManager();