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

vocdoni / saas-backend / 18683461346

21 Oct 2025 12:14PM UTC coverage: 60.245% (-0.008%) from 60.253%
18683461346

push

github

altergui
api: fix missing returns after errors

* api: updatePendingUserInvitationHandler
* api: oauthLoginHandler
* objectstorage: processFile

1 of 3 new or added lines in 3 files covered. (33.33%)

2 existing lines in 2 files now uncovered.

6157 of 10220 relevant lines covered (60.24%)

36.5 hits per line

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

59.69
/api/auth.go
1
package api
2

3
import (
4
        "encoding/json"
5
        "fmt"
6
        "net/http"
7

8
        "github.com/ethereum/go-ethereum/common"
9
        "github.com/vocdoni/saas-backend/account"
10
        "github.com/vocdoni/saas-backend/api/apicommon"
11
        "github.com/vocdoni/saas-backend/db"
12
        "github.com/vocdoni/saas-backend/errors"
13
        "github.com/vocdoni/saas-backend/internal"
14
)
15

16
// refreshTokenHandler godoc
17
//
18
//        @Summary                Refresh JWT token
19
//        @Description        Refresh the JWT token for an authenticated user
20
//        @Tags                        auth
21
//        @Accept                        json
22
//        @Produce                json
23
//        @Security                BearerAuth
24
//        @Success                200        {object}        apicommon.LoginResponse
25
//        @Failure                401        {object}        errors.Error
26
//        @Router                        /auth/refresh [post]
27
func (a *API) refreshTokenHandler(w http.ResponseWriter, r *http.Request) {
×
28
        // get the user from the request context
×
29
        user, ok := apicommon.UserFromContext(r.Context())
×
30
        if !ok {
×
31
                errors.ErrUnauthorized.Write(w)
×
32
                return
×
33
        }
×
34
        // generate a new token with the user name as the subject
35
        res, err := a.buildLoginResponse(user.Email)
×
36
        if err != nil {
×
37
                errors.ErrGenericInternalServerError.Write(w)
×
38
                return
×
39
        }
×
40
        // send the token back to the user
41
        apicommon.HTTPWriteJSON(w, res)
×
42
}
43

44
// authLoginHandler godoc
45
//
46
//        @Summary                Login to get a JWT token
47
//        @Description        Authenticate a user and get a JWT token
48
//        @Tags                        auth
49
//        @Accept                        json
50
//        @Produce                json
51
//        @Param                        request        body                apicommon.UserInfo        true        "Login credentials"
52
//        @Success                200                {object}        apicommon.LoginResponse
53
//        @Failure                400                {object}        errors.Error
54
//        @Failure                401                {object}        errors.Error
55
//        @Router                        /auth/login [post]
56
func (a *API) authLoginHandler(w http.ResponseWriter, r *http.Request) {
45✔
57
        // het the user info from the request body
45✔
58
        loginInfo := &apicommon.UserInfo{}
45✔
59
        if err := json.NewDecoder(r.Body).Decode(loginInfo); err != nil {
45✔
60
                errors.ErrMalformedBody.Write(w)
×
61
                return
×
62
        }
×
63
        // get the user information from the database by email
64
        user, err := a.db.UserByEmail(loginInfo.Email)
45✔
65
        if err != nil {
45✔
66
                if err == db.ErrNotFound {
×
67
                        errors.ErrUnauthorized.Write(w)
×
68
                        return
×
69
                }
×
70
                errors.ErrGenericInternalServerError.Write(w)
×
71
                return
×
72
        }
73
        // check the password
74
        if pass := internal.HexHashPassword(passwordSalt, loginInfo.Password); pass != user.Password {
46✔
75
                errors.ErrUnauthorized.Write(w)
1✔
76
                return
1✔
77
        }
1✔
78
        // check if the user is verified
79
        if !user.Verified {
44✔
80
                errors.ErrUserNoVerified.Write(w)
×
81
                return
×
82
        }
×
83
        // generate a new token with the user name as the subject
84
        res, err := a.buildLoginResponse(loginInfo.Email)
44✔
85
        if err != nil {
44✔
86
                errors.ErrGenericInternalServerError.Write(w)
×
87
                return
×
88
        }
×
89
        // send the token back to the user
90
        apicommon.HTTPWriteJSON(w, res)
44✔
91
}
92

93
// writableOrganizationAddressesHandler godoc
94
//
95
//        @Summary                Get writable organization addresses
96
//        @Description        Get the list of organization addresses where the user has write access
97
//        @Tags                        auth
98
//        @Accept                        json
99
//        @Produce                json
100
//        @Security                BearerAuth
101
//        @Success                200        {object}        apicommon.OrganizationAddresses
102
//        @Failure                401        {object}        errors.Error
103
//        @Failure                404        {object}        errors.Error        "No organizations found"
104
//        @Router                        /auth/addresses [get]
105
//
106
// writableOrganizationAddressesHandler returns the list of addresses of the
107
// organizations where the user has write access.
108
func (*API) writableOrganizationAddressesHandler(w http.ResponseWriter, r *http.Request) {
1✔
109
        // get the user from the request context
1✔
110
        user, ok := apicommon.UserFromContext(r.Context())
1✔
111
        if !ok {
1✔
112
                errors.ErrUnauthorized.Write(w)
×
113
                return
×
114
        }
×
115
        // check if the user has organizations
116
        if len(user.Organizations) == 0 {
1✔
117
                errors.ErrNoOrganizations.Write(w)
×
118
                return
×
119
        }
×
120
        // get the user organizations information from the database if any
121
        userAddresses := &apicommon.OrganizationAddresses{
1✔
122
                Addresses: []common.Address{},
1✔
123
        }
1✔
124
        // get the addresses of the organizations where the user has write access
1✔
125
        for _, org := range user.Organizations {
2✔
126
                // check if the user has organization write permission to the organization based on the
1✔
127
                // role of the user in the organization
1✔
128
                if db.HasOrganizationWritePermission(org.Role) {
2✔
129
                        userAddresses.Addresses = append(userAddresses.Addresses, org.Address)
1✔
130
                }
1✔
131
        }
132
        // write the response back to the user
133
        apicommon.HTTPWriteJSON(w, userAddresses)
1✔
134
}
135

136
// oauthLoginHandler godoc
137
//
138
//        @Summary                Login using OAuth service
139
//        @Description        Register/Authenticate a user and get a JWT token
140
//        @Tags                        auth
141
//        @Accept                        json
142
//        @Produce                json
143
//        @Param                        request        body                apicommon.UserInfo        true        "Login credentials"
144
//        @Success                200                {object}        apicommon.OAuthLoginResponse
145
//        @Failure                400                {object}        errors.Error
146
//        @Failure                401                {object}        errors.Error
147
//        @Failure                500                {object}        errors.Error
148
//        @Router                        /oauth/login [post]
149
func (a *API) oauthLoginHandler(w http.ResponseWriter, r *http.Request) {
6✔
150
        // get the user info from the request body
6✔
151
        loginInfo := &apicommon.OAuthLoginRequest{}
6✔
152
        if err := json.NewDecoder(r.Body).Decode(loginInfo); err != nil {
7✔
153
                errors.ErrMalformedBody.Write(w)
1✔
154
                return
1✔
155
        }
1✔
156
        // get the user information from the database by email
157
        user, err := a.db.UserByEmail(loginInfo.Email)
5✔
158
        if err != nil && err != db.ErrNotFound {
5✔
159
                errors.ErrGenericInternalServerError.Write(w)
×
160
                return
×
161
        }
×
162
        res := &apicommon.OAuthLoginResponse{}
5✔
163
        // if the user doesn't exist, do oauth verification and on success create the new user
5✔
164
        if err == db.ErrNotFound {
8✔
165
                // Register the user
3✔
166
                // extract from the external signature the user pubkey and verify matches the provided one
3✔
167
                if err := account.VerifySignature(loginInfo.OAuthSignature, loginInfo.UserOAuthSignature, loginInfo.Address); err != nil {
4✔
168
                        errors.ErrUnauthorized.WithErr(err).Write(w)
1✔
169
                        return
1✔
170
                }
1✔
171
                // fetch oauth service pubkey or address and verify the internal signature
172
                resp, err := http.Get(fmt.Sprintf("%s/api/info/getAddress", a.oauthServiceURL))
2✔
173
                defer func() {
4✔
174
                        if err := resp.Body.Close(); err != nil {
2✔
175
                                // handle the error, for example log it
×
176
                                fmt.Println("Error closing response body:", err)
×
177
                        }
×
178
                }()
179
                if err != nil {
2✔
180
                        errors.ErrOAuthServerConnectionFailed.WithErr(err).Write(w)
×
181
                        return
×
182
                }
×
183
                var result apicommon.OAuthServiceAddressResponse
2✔
184
                if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
2✔
185
                        errors.ErrGenericInternalServerError.WithErr(err).Write(w)
×
186
                        return
×
187
                }
×
188

189
                // verify the signature of the oauth service
190
                if err := account.VerifySignature(loginInfo.Email, loginInfo.OAuthSignature, result.Address); err != nil {
3✔
191
                        errors.ErrUnauthorized.WithErr(err).Write(w)
1✔
192
                        return
1✔
193
                }
1✔
194

195
                // genareate the new user and password and store it in the database
196
                user = &db.User{
1✔
197
                        Email:     loginInfo.Email,
1✔
198
                        FirstName: loginInfo.FirstName,
1✔
199
                        LastName:  loginInfo.LastName,
1✔
200
                        Password:  internal.HexHashPassword(passwordSalt, loginInfo.UserOAuthSignature),
1✔
201
                        Verified:  true,
1✔
202
                }
1✔
203
                if _, err := a.db.SetUser(user); err != nil {
1✔
204
                        errors.ErrGenericInternalServerError.WithErr(err).Write(w)
×
NEW
205
                        return
×
UNCOV
206
                }
×
207
                res.Registered = true
1✔
208
        }
209
        // Login
210
        // check that the address generated password matches the one in the database
211
        if pass := internal.HexHashPassword(passwordSalt, loginInfo.UserOAuthSignature); pass != user.Password {
4✔
212
                errors.ErrUnauthorized.Write(w)
1✔
213
                return
1✔
214
        }
1✔
215
        // generate a new token with the user name as the subject
216
        login, err := a.buildLoginResponse(loginInfo.Email)
2✔
217
        if err != nil {
2✔
218
                errors.ErrGenericInternalServerError.WithErr(err).Write(w)
×
219
                return
×
220
        }
×
221
        res.Token = login.Token
2✔
222
        res.Expirity = login.Expirity
2✔
223
        // send the token back to the user
2✔
224
        apicommon.HTTPWriteJSON(w, res)
2✔
225
}
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