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

mendersoftware / useradm / 1290251377

14 May 2024 01:33PM UTC coverage: 85.226% (-1.6%) from 86.874%
1290251377

Pull #423

gitlab-ci

alfrunes
test(acceptance): Expose Mongo URL as pytest argument

Signed-off-by: Alf-Rune Siqveland <alf.rune@northern.tech>
Pull Request #423: Acceptance tests fixup

2792 of 3276 relevant lines covered (85.23%)

49.04 hits per line

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

63.11
/server.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
package main
15

16
import (
17
        "net/http"
18
        "os"
19
        "path"
20
        "path/filepath"
21
        "regexp"
22

23
        "github.com/mendersoftware/go-lib-micro/config"
24
        "github.com/mendersoftware/go-lib-micro/log"
25
        "github.com/pkg/errors"
26

27
        api_http "github.com/mendersoftware/useradm/api/http"
28
        "github.com/mendersoftware/useradm/client/tenant"
29
        "github.com/mendersoftware/useradm/common"
30
        . "github.com/mendersoftware/useradm/config"
31
        "github.com/mendersoftware/useradm/jwt"
32
        "github.com/mendersoftware/useradm/store/mongo"
33
        useradm "github.com/mendersoftware/useradm/user"
34
)
35

36
func RunServer(c config.Reader) error {
1✔
37

1✔
38
        l := log.New(log.Ctx{})
1✔
39

1✔
40
        authorizer := &SimpleAuthz{}
1✔
41

1✔
42
        // let's now go through all the existing keys and load them
1✔
43
        jwtHandlers, err := addPrivateKeys(
1✔
44
                l,
1✔
45
                filepath.Dir(c.GetString(SettingServerPrivKeyPath)),
1✔
46
                c.GetString(SettingServerPrivKeyFileNamePattern),
1✔
47
        )
1✔
48
        if err != nil {
1✔
49
                return err
×
50
        }
×
51

52
        // the handler for keyId equal 0 is the one associated with the
53
        // SettingServerPrivKeyPathDefault key. it is the one serving all the previously
54
        // issued tokens (before the kid introduction in the JWTs)
55
        defaultHandler, err := jwt.NewJWTHandler(
1✔
56
                SettingServerPrivKeyPathDefault,
1✔
57
                c.GetString(SettingServerPrivKeyFileNamePattern),
1✔
58
        )
1✔
59
        if err == nil && defaultHandler != nil {
2✔
60
                // the key with id 0 is by default the default one. this allows
1✔
61
                // to support tokens without "kid" in the header
1✔
62
                // it is possible, that you rotated the default key, in which case you have to
1✔
63
                // set USERADM_SERVER_PRIV_KEY_PATH=/etc/useradm/rsa/private.id.2048.pem
1✔
64
                // where private.id.2048.pem is the new key, with new id. the new one will by default
1✔
65
                // be used to issue new tokens, while any other token which has id that we have
1✔
66
                // will be authorized against its matching key (by id from "kid" in JWT header)
1✔
67
                // or which does not have "kid" will be authorized against the key with id 0.
1✔
68
                // in other words: the key with id 0 (if not present as private.id.0.pem)
1✔
69
                // is the default one, and all the JWT with no "kid" in headers are being
1✔
70
                // checked against it.
1✔
71
                jwtHandlers[common.KeyIdZero] = defaultHandler
1✔
72
        }
1✔
73

74
        // if the default path is different from the currently set key path
75
        // we still have not loaded this key. this happens when the key rotation took place,
76
        // someone exported USERADM_SERVER_PRIV_KEY_PATH=path-to-a-new-key and this key
77
        // now will serve all. if we do not have this, the Login will fall back to the keyId
78
        // from the filename and either use the KeyIdZero key or fail to find the key to issue a
79
        // token if the one set in USERADM_SERVER_PRIV_KEY_PATH does have id in the filename
80
        // (but does not exist because we have not loaded it)
81
        // this also means that careless setting of USERADM_SERVER_PRIV_KEY_PATH to a key that does
82
        // not match the SettingServerPrivKeyFileNamePattern will result in
83
        // KeyIdZero handler overwrite and lack of back support for tokens signed by it.
84
        if c.GetString(SettingServerPrivKeyPath) != SettingServerPrivKeyPathDefault {
1✔
85
                defaultHandler, err = jwt.NewJWTHandler(
×
86
                        c.GetString(SettingServerPrivKeyPath),
×
87
                        c.GetString(SettingServerPrivKeyFileNamePattern),
×
88
                )
×
89
                if err == nil && defaultHandler != nil {
×
90
                        keyId := common.KeyIdFromPath(
×
91
                                c.GetString(SettingServerPrivKeyPath),
×
92
                                c.GetString(SettingServerPrivKeyFileNamePattern),
×
93
                        )
×
94
                        if keyId == common.KeyIdZero {
×
95
                                l.Warnf(
×
96
                                        "currently set private key %s either does not match %s pattern"+
×
97
                                                " or has explicitly set id=0. we are overridding the default"+
×
98
                                                " private key handler with id=0",
×
99
                                        c.GetString(SettingServerPrivKeyPath),
×
100
                                        c.GetString(SettingServerPrivKeyFileNamePattern),
×
101
                                )
×
102
                        }
×
103
                        jwtHandlers[keyId] = defaultHandler
×
104
                }
105
        }
106

107
        var jwtFallbackHandler jwt.Handler
1✔
108
        fallback := c.GetString(SettingServerFallbackPrivKeyPath)
1✔
109
        if err == nil && fallback != "" {
1✔
110
                jwtFallbackHandler, err = jwt.NewJWTHandler(
×
111
                        fallback,
×
112
                        c.GetString(SettingServerPrivKeyFileNamePattern),
×
113
                )
×
114
        }
×
115
        if err != nil {
1✔
116
                return err
×
117
        }
×
118

119
        db, err := mongo.GetDataStoreMongo(dataStoreMongoConfigFromAppConfig(c))
1✔
120
        if err != nil {
1✔
121
                return errors.Wrap(err, "database connection failed")
×
122
        }
×
123

124
        ua := useradm.NewUserAdm(jwtHandlers, db,
1✔
125
                useradm.Config{
1✔
126
                        Issuer:                         c.GetString(SettingJWTIssuer),
1✔
127
                        ExpirationTimeSeconds:          int64(c.GetInt(SettingJWTExpirationTimeout)),
1✔
128
                        LimitSessionsPerUser:           c.GetInt(SettingLimitSessionsPerUser),
1✔
129
                        LimitTokensPerUser:             c.GetInt(SettingLimitTokensPerUser),
1✔
130
                        TokenLastUsedUpdateFreqMinutes: c.GetInt(SettingTokenLastUsedUpdateFreqMinutes),
1✔
131
                        PrivateKeyPath:                 c.GetString(SettingServerPrivKeyPath),
1✔
132
                        PrivateKeyFileNamePattern:      c.GetString(SettingServerPrivKeyFileNamePattern),
1✔
133
                })
1✔
134

1✔
135
        if tadmAddr := c.GetString(SettingTenantAdmAddr); tadmAddr != "" {
1✔
136
                l.Infof("settting up tenant verification")
×
137

×
138
                tc := tenant.NewClient(tenant.Config{
×
139
                        TenantAdmAddr: tadmAddr,
×
140
                })
×
141

×
142
                ua = ua.WithTenantVerification(tc)
×
143
        }
×
144

145
        useradmapi := api_http.NewUserAdmApiHandlers(ua, db, jwtHandlers,
1✔
146
                api_http.Config{
1✔
147
                        TokenMaxExpSeconds: c.GetInt(SettingTokenMaxExpirationSeconds),
1✔
148
                        JWTFallback:        jwtFallbackHandler,
1✔
149
                })
1✔
150

1✔
151
        handler, err := useradmapi.Build(authorizer)
1✔
152
        if err != nil {
1✔
153
                return errors.Wrap(err, "useradm API handlers setup failed")
×
154
        }
×
155

156
        addr := c.GetString(SettingListen)
1✔
157
        l.Printf("listening on %s", addr)
1✔
158

1✔
159
        return http.ListenAndServe(addr, handler)
1✔
160
}
161

162
func addPrivateKeys(
163
        l *log.Logger,
164
        privateKeysDirectory string,
165
        privateKeyPattern string,
166
) (handlers map[int]jwt.Handler, err error) {
2✔
167
        files, err := os.ReadDir(privateKeysDirectory)
2✔
168
        if err != nil {
2✔
169
                return
×
170
        }
×
171

172
        r, err := regexp.Compile(privateKeyPattern)
2✔
173
        if err != nil {
2✔
174
                return
×
175
        }
×
176

177
        handlers = make(map[int]jwt.Handler, len(files))
2✔
178
        for _, fileEntry := range files {
16✔
179
                if r.MatchString(fileEntry.Name()) {
24✔
180
                        keyPath := path.Join(privateKeysDirectory, fileEntry.Name())
10✔
181
                        handler, err := jwt.NewJWTHandler(keyPath, privateKeyPattern)
10✔
182
                        if err != nil {
10✔
183
                                continue
×
184
                        }
185
                        keyId := common.KeyIdFromPath(keyPath, privateKeyPattern)
10✔
186
                        l.Infof("loaded private key id=%d from %s", keyId, keyPath)
10✔
187
                        handlers[keyId] = handler
10✔
188
                }
189
        }
190
        return handlers, nil
2✔
191
}
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