blob: d6cc6b654754c6b719bd0c7ae706249815daa5fe [file] [log] [blame]
Adrien Béraud6ecaa402021-04-06 17:37:25 -04001/*
2 * Copyright (c) 2017-2021 Savoir-faire Linux Inc.
3 *
4 * Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
Adrien Béraud6ecaa402021-04-06 17:37:25 -040020class AuthManager {
21 constructor() {
22 console.log("AuthManager()")
Adrien Béraud6ecaa402021-04-06 17:37:25 -040023 this.authenticating = false
Adrien Béraude74741b2021-04-19 13:22:54 -040024
25 this.state = {
26 initialized: false,
27 authenticated: true,
28 setupComplete: true,
29 error: false
30 }
31
Adrien Béraud6ecaa402021-04-06 17:37:25 -040032 this.tasks = []
33 this.onAuthChanged = undefined
34 }
Adrien Béraud6ecaa402021-04-06 17:37:25 -040035
36 isAuthenticated() {
Adrien Béraude74741b2021-04-19 13:22:54 -040037 return this.state.authenticated
Adrien Béraud6ecaa402021-04-06 17:37:25 -040038 }
39
Adrien Béraude74741b2021-04-19 13:22:54 -040040 getState() {
41 return this.state
42 }
43
44 init(cb) {
45 this.onAuthChanged = cb
46 if (this.state.initialized || this.authenticating)
47 return
48 console.log("Init")
49 this.authenticating = true
50 fetch('/auth')
51 .then(async (response) => {
52 this.authenticating = false
53 this.state.initialized = true
54 console.log("Init ended")
Adrien Béraude74741b2021-04-19 13:22:54 -040055 if (response.status === 200) {
56 const jsonData = await response.json()
57 Object.assign(this.state, {
58 authenticated: true,
59 setupComplete: true,
60 error: false,
61 user: { username: jsonData.username, type: jsonData.type }
62 })
63 } else if (response.status === 401) {
64 const jsonData = await response.json()
65 Object.assign(this.state, {
66 authenticated: false,
67 setupComplete: 'setupComplete' in jsonData ? jsonData.setupComplete : true,
68 error: false
69 })
70 } else {
71 this.state.error = true
72 }
Adrien Béraude74741b2021-04-19 13:22:54 -040073 if (this.onAuthChanged)
74 this.onAuthChanged(this.state)
Adrien Béraud88a52442021-04-26 12:11:41 -040075 }).catch(e => {
76 this.authenticating = false
77 console.log(e)
Adrien Béraude74741b2021-04-19 13:22:54 -040078 })
79 }
80
81 deinit() {
82 console.log("Deinit")
83 this.onAuthChanged = undefined
84 }
85
86 async setup(password) {
87 if (this.authenticating || this.state.setupComplete)
88 return
89 console.log("Starting setup")
90 this.authenticating = true
91 const response = await fetch(`/setup`, {
92 method: 'POST',
93 headers: {
94 'Accept': 'application/json',
95 'Content-Type': 'application/json'
96 },
97 body: JSON.stringify({ password })
98 })
99 console.log(response)
100 if (response.ok) {
101 console.log("Success, going home")
102 //history.replace('/')
103 } else {
104 }
105 this.authenticating = false
106 this.state.setupComplete = true
107 if (this.onAuthChanged)
108 this.onAuthChanged(this.state)
109 return response.ok
110 }
111
112 authenticate(username, password) {
Adrien Béraud6ecaa402021-04-06 17:37:25 -0400113 if (this.authenticating)
114 return
115 console.log("Starting authentication")
116 this.authenticating = true
Adrien Béraude74741b2021-04-19 13:22:54 -0400117 fetch(`/auth/local?username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`, { method:"POST" })
Adrien Béraud6ecaa402021-04-06 17:37:25 -0400118 .then(response => {
119 console.log(response)
120 this.authenticating = false
Adrien Béraude74741b2021-04-19 13:22:54 -0400121 this.state.authenticated = response.ok && response.status === 200
Adrien Béraud6ecaa402021-04-06 17:37:25 -0400122 if (this.onAuthChanged)
Adrien Béraude74741b2021-04-19 13:22:54 -0400123 this.onAuthChanged(this.state)
Adrien Béraud6ecaa402021-04-06 17:37:25 -0400124 while (this.tasks.length !== 0) {
125 const task = this.tasks.shift()
Adrien Béraude74741b2021-04-19 13:22:54 -0400126 if (this.state.authenticated)
Adrien Béraud6ecaa402021-04-06 17:37:25 -0400127 fetch(task.url, task.init).then(res => task.resolve(res))
128 else
129 task.reject(new Error("Authentication failed"))
130 }
Adrien Béraud88a52442021-04-26 12:11:41 -0400131 }).catch(e => {
132 this.authenticating = false
133 console.log(e)
Adrien Béraud6ecaa402021-04-06 17:37:25 -0400134 })
135 }
136
137 disconnect() {
138 console.log("Disconnect")
Adrien Béraude74741b2021-04-19 13:22:54 -0400139 this.state.authenticated = false
Adrien Béraud6ecaa402021-04-06 17:37:25 -0400140 if (this.onAuthChanged)
Adrien Béraude74741b2021-04-19 13:22:54 -0400141 this.onAuthChanged(this.state)
Adrien Béraud6ecaa402021-04-06 17:37:25 -0400142 }
143
144 fetch(url, init) {
Adrien Béraud150b4782021-04-21 19:40:59 -0400145 console.log(`fetch ${url}`)
Adrien Béraude74741b2021-04-19 13:22:54 -0400146 if (!this.state.authenticated) {
Adrien Béraud4e287b92021-04-24 16:15:56 -0400147 if (!init || !init.method || init.method === 'GET') {
148 return new Promise((resolve, reject) => this.tasks.push({url, init, resolve, reject}))
149 } else {
150 return new Promise((resolve, reject) => reject("Not authenticated"))
151 }
Adrien Béraud6ecaa402021-04-06 17:37:25 -0400152 }
153 return fetch(url, init)
154 .then(response => {
Adrien Béraud6ecaa402021-04-06 17:37:25 -0400155 if (response.status === 401) {
156 this.disconnect()
157 return this.fetch(url, init)
158 }
159 return response
160 })
161 }
162}
163
164export default new AuthManager()