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

mendersoftware / mender / 1641906300

27 Jan 2025 05:31AM UTC coverage: 75.952% (-0.002%) from 75.954%
1641906300

push

gitlab-ci

web-flow
Merge pull request #1734 from mendersoftware/cherry-5.0.x-MEN-7900-client-stuck-after-sync-error

[Cherry 5.0.x]: MEN-7900: Fix Mender client getting stuck after failure in sync state

24 of 40 new or added lines in 3 files covered. (60.0%)

100 existing lines in 4 files now uncovered.

7381 of 9718 relevant lines covered (75.95%)

11129.08 hits per line

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

76.71
/src/api/auth/auth.cpp
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
#include <api/auth.hpp>
16

17
#include <common/log.hpp>
18

19
namespace mender {
20
namespace api {
21
namespace auth {
22

23
namespace mlog = mender::common::log;
24

25
using namespace std;
26

27
const AuthenticatorErrorCategoryClass AuthenticatorErrorCategory;
28

29
const char *AuthenticatorErrorCategoryClass::name() const noexcept {
×
30
        return "AuthenticatorErrorCategory";
×
31
}
32

33
string AuthenticatorErrorCategoryClass::message(int code) const {
×
34
        switch (code) {
×
35
        case NoError:
36
                return "Success";
×
37
        case APIError:
38
                return "API error";
×
39
        case UnauthorizedError:
40
                return "Unauthorized error";
×
41
        case AuthenticationError:
42
                return "Authentication error";
×
43
        default:
44
                return "Unknown";
×
45
        }
46
}
47

48
error::Error MakeError(AuthenticatorErrorCode code, const string &msg) {
3✔
49
        return error::Error(error_condition(code, AuthenticatorErrorCategory), msg);
5✔
50
}
51

52
void Authenticator::ExpireToken() {
28✔
53
        if (!token_fetch_in_progress_) {
28✔
54
                RequestNewToken();
56✔
55
        }
56
}
28✔
57

58
error::Error Authenticator::WithToken(AuthenticatedAction action) {
22✔
59
        pending_actions_.push_back(action);
22✔
60

61
        auto err = StartWatchingTokenSignal();
22✔
62
        if (err != error::NoError) {
22✔
63
                // Should never fail. We rely on the signal heavily so let's fail
64
                // hard if it does.
65
                return err;
1✔
66
        }
67

68
        if (token_fetch_in_progress_) {
21✔
69
                // Already waiting for a new token.
70
                return error::NoError;
6✔
71
        }
72
        // else => should fetch the token, cache it and call all pending actions
73

74
        err = GetJwtToken();
15✔
75
        if (err != error::NoError) {
15✔
76
                // No token and failed to try to get one (should never happen).
77
                ExpectedAuthData ex_auth_data = expected::unexpected(err);
×
78
                PostPendingActions(ex_auth_data);
×
79
                return err;
×
80
        }
81
        // else record that token is already being fetched (by GetJwtToken()).
82
        token_fetch_in_progress_ = true;
15✔
83
        return error::NoError;
15✔
84
}
85

86
void Authenticator::PostPendingActions(const ExpectedAuthData &ex_auth_data) {
43✔
87
        token_fetch_in_progress_ = false;
43✔
88
        for (auto action : pending_actions_) {
64✔
89
                loop_.Post([action, ex_auth_data]() { action(ex_auth_data); });
105✔
90
        }
91
        pending_actions_.clear();
43✔
92
}
43✔
93

94
error::Error Authenticator::RequestNewToken() {
29✔
95
        auto err = FetchJwtToken();
29✔
96
        if (err != error::NoError) {
29✔
97
                mlog::Error("Failed to request new token fetching: " + err.String());
46✔
98
                ExpectedAuthData ex_auth_data = expected::unexpected(err);
23✔
99
                PostPendingActions(ex_auth_data);
23✔
100
                return err;
23✔
101
        }
102

103
        token_fetch_in_progress_ = true;
6✔
104

105
        // Make sure we don't wait for the token forever.
106
        auth_timeout_timer_.AsyncWait(auth_timeout_, [this](error::Error err) {
6✔
107
                if (err.code == make_error_condition(errc::operation_canceled)) {
6✔
108
                        return;
109
                } else if (err == error::NoError) {
2✔
110
                        mlog::Warning("Timed-out waiting for a new token");
4✔
111
                        ExpectedAuthData ex_auth_data = expected::unexpected(
2✔
112
                                MakeError(AuthenticationError, "Timed-out waiting for a new token"));
6✔
113
                        PostPendingActions(ex_auth_data);
2✔
114
                } else {
115
                        // should never happen
116
                        assert(false);
UNCOV
117
                        mlog::Error("Authentication timer error: " + err.String());
×
118

119
                        // In case it did happen, run the stacked up actions and unset the
120
                        // in_progress_ flag or things may got stuck.
UNCOV
121
                        ExpectedAuthData ex_auth_data = expected::unexpected(err);
×
122
                        PostPendingActions(ex_auth_data);
×
123
                }
UNCOV
124
        });
×
125
        return error::NoError;
6✔
126
}
127

128
void Authenticator::HandleReceivedToken(
19✔
129
        common::ExpectedStringPair ex_auth_dbus_data, NoTokenAction no_token) {
130
        auth_timeout_timer_.Cancel();
19✔
131
        ExpectedAuthData ex_auth_data;
132
        if (!ex_auth_dbus_data) {
19✔
133
                mlog::Error("Error receiving the JWT token: " + ex_auth_dbus_data.error().String());
2✔
134
                ex_auth_data = expected::unexpected(ex_auth_dbus_data.error());
2✔
135
        } else {
136
                auto &token = ex_auth_dbus_data.value().first;
18✔
137
                auto &server_url = ex_auth_dbus_data.value().second;
18✔
138

139
                if (server_url.empty()) {
18✔
140
                        mlog::Debug("Got empty server url /token in the response");
4✔
141
                } else {
142
                        mlog::Debug("Got new authentication token for server " + server_url);
32✔
143
                }
144

145
                AuthData auth_data {server_url, token};
35✔
146
                ex_auth_data = ExpectedAuthData(std::move(auth_data));
18✔
147

148
                if (no_token == NoTokenAction::RequestNew and (token == "" or server_url == "")) {
18✔
149
                        RequestNewToken();
1✔
150
                        return;
1✔
151
                }
152
        }
153
        if (no_token == NoTokenAction::Finish && action_ != nullptr) {
18✔
UNCOV
154
                action_();
×
155
        }
156
        PostPendingActions(ex_auth_data);
18✔
157
}
158

159
} // namespace auth
160
} // namespace api
161
} // namespace mender
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