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

mendersoftware / mender / 939561117

pending completion
939561117

push

gitlab-ci

web-flow
Merge pull request #1305 from alfrunes/fix-authmgr-deadlock

Make auth manager channels buffered and close response chan on timeout

17 of 33 new or added lines in 2 files covered. (51.52%)

2 existing lines in 1 file now uncovered.

6350 of 10917 relevant lines covered (58.17%)

33.24 hits per line

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

77.52
/app/auth.go
1
// Copyright 2023 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14

15
package app
16

17
import (
18
        "runtime"
19
        "strings"
20
        "sync"
21
        "time"
22

23
        "github.com/pkg/errors"
24
        log "github.com/sirupsen/logrus"
25

26
        "github.com/mendersoftware/mender/app/proxy"
27
        "github.com/mendersoftware/mender/client"
28
        "github.com/mendersoftware/mender/conf"
29
        "github.com/mendersoftware/mender/datastore"
30
        "github.com/mendersoftware/mender/dbus"
31
        "github.com/mendersoftware/mender/device"
32
        "github.com/mendersoftware/mender/store"
33
)
34

35
// Constants for auth manager request actions
36
const (
37
        ActionFetchAuthToken = "FETCH_AUTH_TOKEN"
38
        ActionGetAuthToken   = "GET_AUTH_TOKEN"
39
)
40

41
// Constants for auth manager response events
42
const (
43
        EventFetchAuthToken       = "FETCH_AUTH_TOKEN"
44
        EventGetAuthToken         = "GET_AUTH_TOKEN"
45
        EventAuthTokenStateChange = "AUTH_TOKEN_STATE_CHANGE"
46
)
47

48
// Constants for the auth manager DBus interface
49
const (
50
        AuthManagerDBusPath                      = "/io/mender/AuthenticationManager"
51
        AuthManagerDBusObjectName                = "io.mender.AuthenticationManager"
52
        AuthManagerDBusInterfaceName             = "io.mender.Authentication1"
53
        AuthManagerDBusSignalJwtTokenStateChange = "JwtTokenStateChange"
54
        AuthManagerDBusInterface                 = `<node>
55
        <interface name="io.mender.Authentication1">
56
                <method name="GetJwtToken">
57
                        <arg type="s" name="token" direction="out"/>
58
                        <arg type="s" name="server_url" direction="out"/>
59
                </method>
60
                <method name="FetchJwtToken">
61
                        <arg type="b" name="success" direction="out"/>
62
                </method>
63
                <signal name="JwtTokenStateChange">
64
                        <arg type="s" name="token"/>
65
                        <arg type="s" name="server_url"/>
66
                </signal>
67
        </interface>
68
</node>`
69
)
70

71
const (
72
        noAuthToken                  = client.EmptyAuthToken
73
        authManagerInMessageChanSize = 1024
74

75
        // Keep this at 1 for now. At the time of writing it is only used for
76
        // fetchAuthToken, and it doesn't make sense to have more than one
77
        // request in the queue, since additional requests will just accomplish
78
        // the same thing as one request.
79
        authManagerWorkerQueueSize = 1
80
)
81

82
// AuthManagerRequest stores a request to the Mender authorization manager
83
type AuthManagerRequest struct {
84
        Action          string
85
        ResponseChannel chan<- AuthManagerResponse
86
}
87

88
// AuthManagerResponse stores a response from the Mender authorization manager
89
type AuthManagerResponse struct {
90
        AuthToken client.AuthToken
91
        ServerURL client.ServerURL
92
        Event     string
93
        Error     error
94
}
95

96
// AuthManager is the interface of a Mender authorization manager
97
type AuthManager interface {
98
        Bootstrap() menderError
99
        ForceBootstrap()
100
        GetInMessageChan() chan<- AuthManagerRequest
101
        GetBroadcastMessageChan(name string) <-chan AuthManagerResponse
102
        Start()
103
        Stop()
104
        EnableDBus(api dbus.DBusAPI)
105

106
        // check if device key is available
107
        HasKey() bool
108
        // generate device key (will overwrite an already existing key)
109
        GenerateKey() error
110

111
        client.AuthDataMessenger
112
}
113

114
// MenderAuthManager is the Mender authorization manager
115
type MenderAuthManager struct {
116
        // We use this composition so that we can set a finalizer on the outer
117
        // struct and clean up the go routine which is running using the inner
118
        // struct.
119
        *menderAuthManagerService
120
}
121

122
type menderAuthManagerService struct {
123
        hasStarted bool
124
        inChan     chan AuthManagerRequest
125

126
        broadcastChansMutex sync.Mutex
127
        broadcastChans      map[string]chan AuthManagerResponse
128

129
        workerChan chan AuthManagerRequest
130

131
        quitReq  chan bool
132
        quitResp chan bool
133

134
        authReq client.AuthRequester
135
        api     *client.ApiClient
136

137
        forceBootstrap bool
138
        dbus           dbus.DBusAPI
139
        dbusConn       dbus.Handle
140
        config         *conf.MenderConfig
141
        keyStore       *store.Keystore
142
        idSrc          device.IdentityDataGetter
143
        authToken      client.AuthToken
144
        serverURL      client.ServerURL
145
        tenantToken    client.AuthToken
146

147
        localProxy *proxy.ProxyController
148
}
149

150
// AuthManagerConfig holds the configuration of the auth manager
151
type AuthManagerConfig struct {
152
        Config         *conf.MenderConfig        // mender config struct
153
        AuthDataStore  store.Store               // authorization data store
154
        KeyStore       *store.Keystore           // key storage
155
        IdentitySource device.IdentityDataGetter // provider of identity data
156
        TenantToken    []byte                    // tenant token
157
}
158

159
// NewAuthManager returns a new Mender authorization manager instance
160
func NewAuthManager(conf AuthManagerConfig) *MenderAuthManager {
197✔
161
        if conf.KeyStore == nil || conf.IdentitySource == nil ||
197✔
162
                conf.AuthDataStore == nil {
197✔
163
                return nil
×
164
        }
×
165

166
        httpConfig := client.Config{}
197✔
167
        if conf.Config != nil {
394✔
168
                httpConfig = conf.Config.GetHttpConfig()
197✔
169

197✔
170
        }
197✔
171

172
        api, err := client.NewApiClient(httpConfig)
197✔
173
        if err != nil {
197✔
174
                return nil
×
175
        }
×
176

177
        tenantToken := client.AuthToken(conf.TenantToken)
196✔
178

196✔
179
        wsDialer, err := client.NewWebsocketDialer(httpConfig)
196✔
180
        if err != nil {
196✔
181
                return nil
×
182
        }
×
183

184
        proxy, err := proxy.NewProxyController(api, wsDialer, "", "")
193✔
185
        if err != nil {
193✔
186
                log.Errorf("Error creating local proxy: %s", err.Error())
×
187
        }
×
188

189
        mgr := &MenderAuthManager{
193✔
190
                &menderAuthManagerService{
193✔
191
                        inChan:         make(chan AuthManagerRequest, authManagerInMessageChanSize),
193✔
192
                        broadcastChans: map[string]chan AuthManagerResponse{},
193✔
193
                        quitReq:        make(chan bool),
193✔
194
                        quitResp:       make(chan bool),
193✔
195
                        workerChan:     make(chan AuthManagerRequest, authManagerWorkerQueueSize),
193✔
196
                        api:            api,
193✔
197
                        authReq:        client.NewAuth(),
193✔
198
                        config:         conf.Config,
193✔
199
                        keyStore:       conf.KeyStore,
193✔
200
                        idSrc:          conf.IdentitySource,
193✔
201
                        tenantToken:    tenantToken,
193✔
202
                        localProxy:     proxy,
193✔
203
                },
193✔
204
        }
193✔
205

193✔
206
        if err := mgr.keyStore.Load(); err != nil && !store.IsNoKeys(err) {
193✔
207
                log.Errorf("Failed to load device keys: %v", err)
×
208
                // Otherwise ignore error returned from Load() call. It will
×
209
                // just result in an empty keyStore which in turn will cause
×
210
                // regeneration of keys.
×
211
        }
×
212

213
        // Clean up keys that we don't use anymore. This is safe to do even if
214
        // rolling back, because the old clients will just fetch a new
215
        // AuthToken. Ignore all errors here; this is just cleanup, and it
216
        // doesn't matter if it fails.
217
        _ = conf.AuthDataStore.WriteTransaction(func(txn store.Transaction) error {
386✔
218
                _ = txn.Remove(datastore.AuthTokenName)
193✔
219
                _ = txn.Remove(datastore.AuthTokenCacheInvalidatorName)
193✔
220
                return nil
193✔
221
        })
193✔
222

223
        return mgr
193✔
224
}
225

226
func (m *MenderAuthManager) EnableDBus(api dbus.DBusAPI) {
193✔
227
        if m.hasStarted {
193✔
228
                panic("Calling WithDBus() after the service has started is a programming mistake.")
×
229
        }
230
        m.dbus = api
193✔
231
}
232

233
// GetInMessageChan returns the channel to send requests to the auth manager
234
func (m *MenderAuthManager) GetInMessageChan() chan<- AuthManagerRequest {
31✔
235
        // Auto-start the service if it hasn't been started already.
31✔
236
        m.Start()
31✔
237
        return m.inChan
31✔
238
}
31✔
239

240
// GetBroadcastMessageChan returns the channel to get responses from the auth manager
241
func (m *MenderAuthManager) GetBroadcastMessageChan(name string) <-chan AuthManagerResponse {
31✔
242
        // Auto-start the service if it hasn't been started already.
31✔
243
        m.Start()
31✔
244

31✔
245
        m.broadcastChansMutex.Lock()
31✔
246
        defer m.broadcastChansMutex.Unlock()
31✔
247

31✔
248
        if m.broadcastChans[name] == nil {
62✔
249
                m.broadcastChans[name] = make(chan AuthManagerResponse, 1)
31✔
250
        }
31✔
251
        return m.broadcastChans[name]
31✔
252
}
253

254
func (m *menderAuthManagerService) registerDBusCallbacks() (unregisterFunc func()) {
31✔
255
        // GetJwtToken
31✔
256
        m.dbus.RegisterMethodCallCallback(
31✔
257
                AuthManagerDBusPath,
31✔
258
                AuthManagerDBusInterfaceName,
31✔
259
                "GetJwtToken",
31✔
260
                func(objectPath, interfaceName, methodName string, parameters string) (interface{}, error) {
59✔
261
                        respChan := make(chan AuthManagerResponse, 1)
28✔
262
                        m.inChan <- AuthManagerRequest{
28✔
263
                                Action:          ActionGetAuthToken,
28✔
264
                                ResponseChannel: respChan,
28✔
265
                        }
28✔
266
                        timeout := timers.Get(time.Second * 5)
28✔
267
                        select {
28✔
268
                        case message, ok := <-respChan:
28✔
269
                                if !ok {
28✔
NEW
270
                                        // (race): AuthManagerService timed out.
×
NEW
271
                                        break
×
272
                                }
273
                                tokenAndServerURL := dbus.TokenAndServerURL{
28✔
274
                                        Token:     string(message.AuthToken),
28✔
275
                                        ServerURL: m.localProxy.GetServerUrl(),
28✔
276
                                }
28✔
277
                                return tokenAndServerURL, message.Error
28✔
NEW
278
                        case <-timeout.C:
×
NEW
279
                                timers.Put(timeout)
×
280
                        }
281
                        return string(noAuthToken), errors.New("timeout when calling GetJwtToken")
×
282
                },
283
        )
284
        // FetchJwtToken
285
        m.dbus.RegisterMethodCallCallback(
31✔
286
                AuthManagerDBusPath,
31✔
287
                AuthManagerDBusInterfaceName,
31✔
288
                "FetchJwtToken",
31✔
289
                func(objectPath, interfaceName, methodName string, parameters string) (interface{}, error) {
32✔
290
                        respChan := make(chan AuthManagerResponse, 1)
1✔
291
                        m.inChan <- AuthManagerRequest{
1✔
292
                                Action:          ActionFetchAuthToken,
1✔
293
                                ResponseChannel: respChan,
1✔
294
                        }
1✔
295
                        timeout := timers.Get(time.Second * 5)
1✔
296
                        select {
1✔
297
                        case message, ok := <-respChan:
1✔
298
                                if !ok {
1✔
NEW
299
                                        // (race): AuthManagerService timed out.
×
NEW
300
                                        break
×
301
                                }
302
                                return message.Event == EventFetchAuthToken, message.Error
1✔
NEW
303
                        case <-timeout.C:
×
NEW
304
                                timers.Put(timeout)
×
305
                        }
306
                        return false, errors.New("timeout when calling FetchJwtToken")
×
307
                },
308
        )
309

310
        return func() {
34✔
311
                m.dbus.UnregisterMethodCallCallback(
3✔
312
                        AuthManagerDBusPath,
3✔
313
                        AuthManagerDBusInterfaceName,
3✔
314
                        "FetchJwtToken",
3✔
315
                )
3✔
316
                m.dbus.UnregisterMethodCallCallback(
3✔
317
                        AuthManagerDBusPath,
3✔
318
                        AuthManagerDBusInterfaceName,
3✔
319
                        "GetJwtToken",
3✔
320
                )
3✔
321
        }
3✔
322
}
323

324
// This is idempotent, the service will only start once.
325
func (m *MenderAuthManager) Start() {
31✔
326
        if m.menderAuthManagerService.hasStarted {
62✔
327
                return
31✔
328
        }
31✔
329

330
        m.menderAuthManagerService.hasStarted = true
31✔
331

31✔
332
        initDone := make(chan struct{}, 1)
31✔
333
        go m.menderAuthManagerService.run(initDone)
31✔
334

31✔
335
        // Wait for initialization to finish.
31✔
336
        <-initDone
31✔
337

31✔
338
        runtime.SetFinalizer(m, func(m *MenderAuthManager) {
31✔
339
                m.Stop()
×
340
        })
×
341
}
342

343
// Run is the main routine of the Mender authorization manager
344
func (m *menderAuthManagerService) run(initDone chan struct{}) {
31✔
345
        // When we are being stopped, make sure they know that this happened.
31✔
346
        defer func() {
34✔
347
                // Checking for panic here is just to avoid deadlocking if we
3✔
348
                // get an unexpected panic: Let it propagate instead of blocking
3✔
349
                // on the channel. If the program is correct, this should never
3✔
350
                // be non-nil.
3✔
351
                if recover() == nil {
6✔
352
                        m.quitResp <- true
3✔
353
                }
3✔
354
        }()
355

356
        // run the DBus interface, if available
357
        if m.dbus != nil {
62✔
358
                if dbusConn, err := m.dbus.BusGet(dbus.GBusTypeSystem); err == nil {
62✔
359
                        m.dbusConn = dbusConn
31✔
360

31✔
361
                        nameGid, err := m.dbus.BusOwnNameOnConnection(dbusConn, AuthManagerDBusObjectName,
31✔
362
                                dbus.DBusNameOwnerFlagsAllowReplacement|dbus.DBusNameOwnerFlagsReplace)
31✔
363
                        if err != nil {
31✔
364
                                log.Errorf(
×
365
                                        "Could not own DBus name '%s': %s",
×
366
                                        AuthManagerDBusObjectName,
×
367
                                        err.Error(),
×
368
                                )
×
369
                                goto mainloop
×
370
                        }
371
                        defer m.dbus.BusUnownName(nameGid)
31✔
372

31✔
373
                        intGid, err := m.dbus.BusRegisterInterface(
31✔
374
                                dbusConn,
31✔
375
                                AuthManagerDBusPath,
31✔
376
                                AuthManagerDBusInterface,
31✔
377
                        )
31✔
378
                        if err != nil {
31✔
379
                                log.Errorf("Could register DBus interface name '%s' at path '%s': %s",
×
380
                                        AuthManagerDBusInterface, AuthManagerDBusPath, err.Error())
×
381
                                goto mainloop
×
382
                        }
383
                        defer m.dbus.BusUnregisterInterface(dbusConn, intGid)
31✔
384

31✔
385
                        unregisterFunc := m.registerDBusCallbacks()
31✔
386
                        defer unregisterFunc()
31✔
387

31✔
388
                        dbusLoop := m.dbus.MainLoopNew()
31✔
389
                        go m.dbus.MainLoopRun(dbusLoop)
31✔
390
                        defer m.dbus.MainLoopQuit(dbusLoop)
31✔
391
                }
392
        }
393

394
mainloop:
395
        initDone <- struct{}{}
31✔
396

31✔
397
        go m.longRunningWorkerLoop()
31✔
398

31✔
399
        // run the auth manager main loop
31✔
400
        running := true
31✔
401
        for running {
62✔
402
                select {
31✔
403
                case msg := <-m.inChan:
31✔
404
                        switch msg.Action {
31✔
405
                        case ActionGetAuthToken:
28✔
406
                                log.Debug("received the GET_AUTH_TOKEN action")
28✔
407
                                m.getAuthToken(msg.ResponseChannel)
28✔
408
                        case ActionFetchAuthToken:
31✔
409
                                log.Debug("received the FETCH_AUTH_TOKEN action")
31✔
410

31✔
411
                                // notify the sender we'll fetch the token
31✔
412
                                resp := AuthManagerResponse{Event: EventFetchAuthToken}
31✔
413
                                timeout := timers.Get(time.Second * 5)
31✔
414
                                select {
31✔
415
                                case msg.ResponseChannel <- resp:
31✔
416

NEW
417
                                case <-timeout.C:
×
NEW
418
                                        timers.Put(timeout)
×
NEW
419
                                        close(msg.ResponseChannel)
×
420
                                }
421

422
                                // Potentially long running operation, use worker.
423
                                select {
31✔
424
                                case m.workerChan <- msg:
31✔
425
                                default:
×
426
                                        // Already a request in the queue, nothing to do.
427
                                }
428
                        }
429
                case <-m.quitReq:
3✔
430
                        running = false
3✔
431
                        m.workerChan <- AuthManagerRequest{}
3✔
432
                }
433
        }
434
}
435

436
// This is a helper to the main loop, for tasks that may take a long time. It's
437
// running in a separate Go routine.
438
func (m *menderAuthManagerService) longRunningWorkerLoop() {
31✔
439
        for msg := range m.workerChan {
62✔
440
                switch msg.Action {
31✔
441
                case ActionFetchAuthToken:
31✔
442
                        m.fetchAuthToken()
31✔
443
                case "":
3✔
444
                        // Quit loop.
3✔
445
                        return
3✔
446

447
                }
448
        }
449
}
450

451
// Stop the running MenderAuthManager. Must not be called in the same go
452
// routine as run(). This is idempotent, it is safe to call on a stopped
453
// service.
454
func (m *MenderAuthManager) Stop() {
3✔
455
        if !m.menderAuthManagerService.hasStarted {
3✔
456
                return
×
457
        }
×
458

459
        m.menderAuthManagerService.quitReq <- true
3✔
460
        <-m.menderAuthManagerService.quitResp
3✔
461
        m.menderAuthManagerService.hasStarted = false
3✔
462

3✔
463
        m.localProxy.Stop()
3✔
464

3✔
465
        runtime.SetFinalizer(m, nil)
3✔
466
}
467

468
// getAuthToken returns the cached auth token
469
func (m *menderAuthManagerService) getAuthToken(responseChannel chan<- AuthManagerResponse) {
28✔
470
        msg := AuthManagerResponse{
28✔
471
                AuthToken: m.authToken,
28✔
472
                ServerURL: m.serverURL,
28✔
473
                Event:     EventGetAuthToken,
28✔
474
        }
28✔
475

28✔
476
        timeout := timers.Get(time.Second * 5)
28✔
477
        select {
28✔
478
        case responseChannel <- msg:
28✔
479

NEW
480
        case <-timeout.C:
×
NEW
481
                timers.Put(timeout)
×
NEW
482
                close(responseChannel)
×
483
        }
484
}
485

486
// broadcast broadcasts the notification to all the subscribers
487
func (m *menderAuthManagerService) broadcast(message AuthManagerResponse) {
31✔
488
        m.broadcastChansMutex.Lock()
31✔
489
        for name, broadcastChan := range m.broadcastChans {
62✔
490
                select {
31✔
491
                case broadcastChan <- message:
31✔
492
                default:
×
NEW
493
                        close(broadcastChan)
×
NEW
494
                        delete(m.broadcastChans, name)
×
495
                }
496
        }
497
        m.broadcastChansMutex.Unlock()
31✔
498

31✔
499
        // emit signal on dbus, if available
31✔
500
        if m.dbus != nil {
62✔
501
                tokenAndServerURL := dbus.TokenAndServerURL{
31✔
502
                        Token:     string(message.AuthToken),
31✔
503
                        ServerURL: string(message.ServerURL),
31✔
504
                }
31✔
505
                _ = m.dbus.EmitSignal(m.dbusConn, "", AuthManagerDBusPath,
31✔
506
                        AuthManagerDBusInterfaceName, AuthManagerDBusSignalJwtTokenStateChange,
31✔
507
                        tokenAndServerURL)
31✔
508
        }
31✔
509
}
510

511
// broadcastAuthTokenStateChange broadcasts the notification to all the subscribers
512
func (m *menderAuthManagerService) broadcastAuthTokenStateChange() {
31✔
513
        m.localProxy.Stop()
31✔
514
        if m.authToken != "" {
60✔
515
                // reconfigure proxy
29✔
516
                err := m.localProxy.Reconfigure(string(m.serverURL), string(m.authToken))
29✔
517
                if err != nil {
29✔
518
                        log.Errorf(
×
519
                                "Could not reconfigure proxy with URL %q and token '%s...'"+
×
520
                                        " Other applications running on the device won't be able"+
×
521
                                        " to reach the Mender server. Error: %s",
×
522
                                m.serverURL,
×
523
                                string(m.authToken)[:7],
×
524
                                err.Error(),
×
525
                        )
×
526
                } else {
29✔
527
                        m.localProxy.Start()
29✔
528

29✔
529
                }
29✔
530
        }
531

532
        m.broadcast(AuthManagerResponse{
31✔
533
                Event:     EventAuthTokenStateChange,
31✔
534
                AuthToken: m.authToken,
31✔
535
                ServerURL: client.ServerURL(m.localProxy.GetServerUrl()),
31✔
536
        })
31✔
537
}
538

539
// fetchAuthToken authenticates with the server and retrieve a new auth token, if needed
540
func (m *menderAuthManagerService) fetchAuthToken() {
31✔
541
        var rsp []byte
31✔
542
        var err error
31✔
543
        var server *client.MenderServer
31✔
544
        resp := AuthManagerResponse{Event: EventFetchAuthToken}
31✔
545

31✔
546
        defer func() {
62✔
547
                m.broadcastAuthTokenStateChange()
31✔
548
        }()
31✔
549

550
        if err := m.Bootstrap(); err != nil {
31✔
551
                log.Errorf("Bootstrap failed: %s", err)
×
552
                resp.Error = err
×
553
                return
×
554
        }
×
555

556
        // Cycle through servers and attempt to authorize.
557
        serverIterator := nextServerIterator(*m.config)
31✔
558
        if serverIterator == nil {
31✔
559
                log.Debug("empty server list in mender.conf, serverIterator is nil")
×
560
                err := NewFatalError(errors.New("empty server list in mender.conf"))
×
561
                resp.Error = err
×
562
                return
×
563
        }
×
564

565
        if server = serverIterator(); server == nil {
31✔
566
                log.Debug("empty server list in mender.conf, server is nil")
×
567
                err := NewFatalError(errors.New("empty server list in mender.conf"))
×
568
                resp.Error = err
×
569
                return
×
570
        }
×
571

572
        var serverURL string
31✔
573
        for {
62✔
574
                serverURL = server.ServerURL
31✔
575
                rsp, err = m.authReq.Request(m.api, serverURL, m)
31✔
576

31✔
577
                if err == nil {
60✔
578
                        // SUCCESS!
29✔
579
                        break
29✔
580
                }
581
                log.Errorf("Failed to authorize with %q: %s",
2✔
582
                        server.ServerURL, err.Error())
2✔
583
                server = serverIterator()
2✔
584
                if server == nil {
4✔
585
                        break
2✔
586
                }
587
                log.Infof("Attempting to authorize with %q.",
×
588
                        server.ServerURL)
×
589
        }
590
        if err != nil {
33✔
591
                // Generate and report error.
2✔
592
                errCause := errors.Cause(err)
2✔
593
                if errCause == client.AuthErrorUnauthorized {
2✔
594
                        m.authToken = ""
×
595
                        m.serverURL = ""
×
596
                }
×
597
                err := NewTransientError(errors.Wrap(err, "authorization request failed"))
2✔
598
                resp.Error = err
2✔
599
                return
2✔
600
        }
601

602
        if len(rsp) == 0 {
29✔
603
                resp.Error = errors.New("empty auth response data")
×
604
                return
×
605
        }
×
606

607
        m.authToken = client.AuthToken(rsp)
29✔
608
        m.serverURL = client.ServerURL(serverURL)
29✔
609

29✔
610
        log.Infof("successfully received new authorization data from server %s", m.serverURL)
29✔
611
}
612

613
// ForceBootstrap forces the bootstrap
614
func (m *menderAuthManagerService) ForceBootstrap() {
2✔
615
        m.forceBootstrap = true
2✔
616
}
2✔
617

618
// Bootstrap performs the bootstrap, if needed or forced
619
func (m *menderAuthManagerService) Bootstrap() menderError {
31✔
620
        if !m.needsBootstrap() {
52✔
621
                return nil
21✔
622
        }
21✔
623

624
        return m.doBootstrap()
24✔
625
}
626

627
func (m *menderAuthManagerService) needsBootstrap() bool {
31✔
628
        if m.forceBootstrap {
33✔
629
                return true
2✔
630
        }
2✔
631

632
        if !m.HasKey() {
53✔
633
                log.Debugf("Needs keys")
22✔
634
                return true
22✔
635
        }
22✔
636

637
        return false
21✔
638
}
639

640
func (m *menderAuthManagerService) doBootstrap() menderError {
24✔
641
        if !m.HasKey() || m.forceBootstrap {
48✔
642
                log.Infof("Device keys not present or bootstrap forced, generating")
24✔
643

24✔
644
                err := m.GenerateKey()
24✔
645
                if err != nil {
24✔
646
                        if store.IsStaticKey(err) {
×
647
                                log.Error("Device key is static, refusing to regenerate.")
×
648
                        } else {
×
649
                                return NewFatalError(err)
×
650
                        }
×
651
                }
652
        }
653

654
        m.forceBootstrap = false
24✔
655
        return nil
24✔
656
}
657

658
// MakeAuthRequest makes an auth request
659
func (m *menderAuthManagerService) MakeAuthRequest() (*client.AuthRequest, error) {
31✔
660

31✔
661
        var err error
31✔
662
        authd := client.AuthReqData{}
31✔
663

31✔
664
        idata, err := m.idSrc.Get()
31✔
665
        if err != nil {
31✔
666
                return nil, errors.Wrapf(err, "failed to obtain identity data")
×
667
        }
×
668

669
        authd.IdData = idata
31✔
670

31✔
671
        // fill device public key
31✔
672
        authd.Pubkey, err = m.keyStore.PublicPEM()
31✔
673
        if err != nil {
31✔
674
                return nil, errors.Wrapf(err, "failed to obtain device public key")
×
675
        }
×
676

677
        tentok := strings.TrimSpace(string(m.tenantToken))
31✔
678

31✔
679
        log.Debugf("Tenant token: %s", tentok)
31✔
680

31✔
681
        // fill tenant token
31✔
682
        authd.TenantToken = string(tentok)
31✔
683

31✔
684
        log.Debugf("Authorization data: %v", authd)
31✔
685

31✔
686
        reqdata, err := authd.ToBytes()
31✔
687
        if err != nil {
31✔
688
                return nil, errors.Wrapf(err, "failed to convert auth request data")
×
689
        }
×
690

691
        // generate signature
692
        sig, err := m.keyStore.Sign(reqdata)
31✔
693
        if err != nil {
31✔
694
                return nil, errors.Wrapf(err, "failed to sign auth request")
×
695
        }
×
696

697
        return &client.AuthRequest{
31✔
698
                Data:      reqdata,
31✔
699
                Token:     client.AuthToken(tentok),
31✔
700
                Signature: sig,
31✔
701
        }, nil
31✔
702
}
703

704
// HasKey check if device key is available
705
func (m *menderAuthManagerService) HasKey() bool {
31✔
706
        return m.keyStore.Private() != nil
31✔
707
}
31✔
708

709
// GenerateKey generate device key (will overwrite an already existing key)
710
func (m *menderAuthManagerService) GenerateKey() error {
24✔
711
        if err := m.keyStore.Generate(); err != nil {
24✔
712
                if store.IsStaticKey(err) {
×
713
                        return err
×
714
                }
×
715
                log.Errorf("Failed to generate device key: %v", err)
×
716
                return errors.Wrapf(err, "failed to generate device key")
×
717
        }
718

719
        if err := m.keyStore.Save(); err != nil {
24✔
720
                log.Errorf("Failed to save device key: %s", err)
×
721
                return NewFatalError(err)
×
722
        }
×
723
        return nil
24✔
724
}
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

© 2025 Coveralls, Inc