Skip to content

Commit 20493c5

Browse files
abhishek9686VishalDalwadidependabot[bot]
authored
Release v1.0.0 (#3567)
* increase offline auto delete node time * fix egress HA migration (#3555) * check for node connectivity status * NM-20: Add more refined event logs. (#3552) * feat(go): add more refined event logs; * feat(go): add more refined event logs; * feat(go): add an api to validate user identity. * feat(go): move validate-user-identity under user; * fix(go): prevent disabling basic auth if deployed by operator; (#3561) * NM-38: User Config Fixes (#3559) * Build(deps): bump gorm.io/datatypes from 1.2.5 to 1.2.6 Bumps [gorm.io/datatypes](https://github.com/go-gorm/datatypes) from 1.2.5 to 1.2.6. - [Release notes](https://github.com/go-gorm/datatypes/releases) - [Commits](go-gorm/datatypes@v1.2.5...v1.2.6) --- updated-dependencies: - dependency-name: gorm.io/datatypes dependency-version: 1.2.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> * Build(deps): bump google.golang.org/api from 0.238.0 to 0.240.0 (#3541) Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.238.0 to 0.240.0. - [Release notes](https://github.com/googleapis/google-api-go-client/releases) - [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md) - [Commits](googleapis/google-api-go-client@v0.238.0...v0.240.0) --- updated-dependencies: - dependency-name: google.golang.org/api dependency-version: 0.240.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Build(deps): bump github.com/go-playground/validator/v10 (#3539) Bumps [github.com/go-playground/validator/v10](https://github.com/go-playground/validator) from 10.26.0 to 10.27.0. - [Release notes](https://github.com/go-playground/validator/releases) - [Commits](go-playground/validator@v10.26.0...v10.27.0) --- updated-dependencies: - dependency-name: github.com/go-playground/validator/v10 dependency-version: 10.27.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(go): prevent idp user from being deleted; (#3538) * fix(go): use correct method for pro; fixes: User Config nodes are always reported online. * fix(go): add device id to extclient; * fix(go): try match device id; * fix(go): set device id if not set; * feat(go): return best match offline extclient; * fix(go): match device id with owner and gateway; * fix(go): remove check for rac id; * fix(go): check status on get node status; * fix(go): allow offline or unknown extclient; * feat(go): add count db method; * feat(go): revert change; --------- Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: Abhishek K <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Revert "NM-38: User Config Fixes (#3559)" (#3562) This reverts commit 9d65c62. * fix(go): usage report; (#3563) --------- Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: Vishal Dalwadi <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2 parents 2e47484 + a4d9616 commit 20493c5

File tree

10 files changed

+286
-60
lines changed

10 files changed

+286
-60
lines changed

controllers/server.go

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package controller
33
import (
44
"encoding/json"
55
"errors"
6+
"github.com/google/go-cmp/cmp"
67
"net/http"
78
"os"
89
"strings"
@@ -274,11 +275,11 @@ func updateSettings(w http.ResponseWriter, r *http.Request) {
274275
currSettings := logic.GetServerSettings()
275276
err := logic.UpsertServerSettings(req)
276277
if err != nil {
277-
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to udpate server settings "+err.Error()), "internal"))
278+
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to update server settings "+err.Error()), "internal"))
278279
return
279280
}
280281
logic.LogEvent(&models.Event{
281-
Action: models.Update,
282+
Action: identifySettingsUpdateAction(currSettings, req),
282283
Source: models.Subject{
283284
ID: r.Header.Get("user"),
284285
Name: r.Header.Get("user"),
@@ -323,5 +324,88 @@ func reInit(curr, new models.ServerSettings, force bool) {
323324
}
324325
}
325326
go mq.PublishPeerUpdate(false)
327+
}
328+
329+
func identifySettingsUpdateAction(old, new models.ServerSettings) models.Action {
330+
// TODO: here we are relying on the dashboard to only
331+
// make singular updates, but it's possible that the
332+
// API can be called to make multiple changes to the
333+
// server settings. We should update it to log multiple
334+
// events or create singular update APIs.
335+
if old.MFAEnforced != new.MFAEnforced {
336+
if new.MFAEnforced {
337+
return models.EnforceMFA
338+
} else {
339+
return models.UnenforceMFA
340+
}
341+
}
342+
343+
if old.BasicAuth != new.BasicAuth {
344+
if new.BasicAuth {
345+
return models.EnableBasicAuth
346+
} else {
347+
return models.DisableBasicAuth
348+
}
349+
}
350+
351+
if old.Telemetry != new.Telemetry {
352+
if new.Telemetry == "off" {
353+
return models.DisableTelemetry
354+
} else {
355+
return models.EnableTelemetry
356+
}
357+
}
358+
359+
if old.NetclientAutoUpdate != new.NetclientAutoUpdate ||
360+
old.RacRestrictToSingleNetwork != new.RacRestrictToSingleNetwork ||
361+
old.ManageDNS != new.ManageDNS ||
362+
old.DefaultDomain != new.DefaultDomain ||
363+
old.EndpointDetection != new.EndpointDetection {
364+
return models.UpdateClientSettings
365+
}
366+
367+
if old.AllowedEmailDomains != new.AllowedEmailDomains ||
368+
old.JwtValidityDuration != new.JwtValidityDuration {
369+
return models.UpdateAuthenticationSecuritySettings
370+
}
371+
372+
if old.Verbosity != new.Verbosity ||
373+
old.MetricsPort != new.MetricsPort ||
374+
old.MetricInterval != new.MetricInterval ||
375+
old.AuditLogsRetentionPeriodInDays != new.AuditLogsRetentionPeriodInDays {
376+
return models.UpdateMonitoringAndDebuggingSettings
377+
}
378+
379+
if old.Theme != new.Theme {
380+
return models.UpdateDisplaySettings
381+
}
382+
383+
if old.TextSize != new.TextSize ||
384+
old.ReducedMotion != new.ReducedMotion {
385+
return models.UpdateAccessibilitySettings
386+
}
387+
388+
if old.EmailSenderAddr != new.EmailSenderAddr ||
389+
old.EmailSenderUser != new.EmailSenderUser ||
390+
old.EmailSenderPassword != new.EmailSenderPassword ||
391+
old.SmtpHost != new.SmtpHost ||
392+
old.SmtpPort != new.SmtpPort {
393+
return models.UpdateSMTPSettings
394+
}
395+
396+
if old.AuthProvider != new.AuthProvider ||
397+
old.OIDCIssuer != new.OIDCIssuer ||
398+
old.ClientID != new.ClientID ||
399+
old.ClientSecret != new.ClientSecret ||
400+
old.SyncEnabled != new.SyncEnabled ||
401+
old.IDPSyncInterval != new.IDPSyncInterval ||
402+
old.GoogleAdminEmail != new.GoogleAdminEmail ||
403+
old.GoogleSACredsJson != new.GoogleSACredsJson ||
404+
old.AzureTenant != new.AzureTenant ||
405+
!cmp.Equal(old.GroupFilters, new.GroupFilters) ||
406+
cmp.Equal(old.UserFilters, new.UserFilters) {
407+
return models.UpdateIDPSettings
408+
}
326409

410+
return models.Update
327411
}

controllers/user.go

Lines changed: 124 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"errors"
88
"fmt"
99
"github.com/pquerna/otp"
10+
"golang.org/x/crypto/bcrypt"
1011
"image/png"
1112
"net/http"
1213
"reflect"
@@ -38,6 +39,7 @@ func userHandlers(r *mux.Router) {
3839
r.HandleFunc("/api/users/adm/transfersuperadmin/{username}", logic.SecurityCheck(true, http.HandlerFunc(transferSuperAdmin))).
3940
Methods(http.MethodPost)
4041
r.HandleFunc("/api/users/adm/authenticate", authenticateUser).Methods(http.MethodPost)
42+
r.HandleFunc("/api/users/{username}/validate-identity", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(validateUserIdentity)))).Methods(http.MethodPost)
4143
r.HandleFunc("/api/users/{username}/auth/init-totp", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(initiateTOTPSetup)))).Methods(http.MethodPost)
4244
r.HandleFunc("/api/users/{username}/auth/complete-totp", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(completeTOTPSetup)))).Methods(http.MethodPost)
4345
r.HandleFunc("/api/users/{username}/auth/verify-totp", logic.PreAuthCheck(logic.ContinueIfUserMatch(http.HandlerFunc(verifyTOTP)))).Methods(http.MethodPost)
@@ -308,38 +310,6 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
308310
logic.ReturnErrorResponse(response, request, logic.FormatError(errors.New("access denied to dashboard"), "unauthorized"))
309311
return
310312
}
311-
// log user activity
312-
logic.LogEvent(&models.Event{
313-
Action: models.Login,
314-
Source: models.Subject{
315-
ID: user.UserName,
316-
Name: user.UserName,
317-
Type: models.UserSub,
318-
},
319-
TriggeredBy: user.UserName,
320-
Target: models.Subject{
321-
ID: models.DashboardSub.String(),
322-
Name: models.DashboardSub.String(),
323-
Type: models.DashboardSub,
324-
},
325-
Origin: models.Dashboard,
326-
})
327-
} else {
328-
logic.LogEvent(&models.Event{
329-
Action: models.Login,
330-
Source: models.Subject{
331-
ID: user.UserName,
332-
Name: user.UserName,
333-
Type: models.UserSub,
334-
},
335-
TriggeredBy: user.UserName,
336-
Target: models.Subject{
337-
ID: models.ClientAppSub.String(),
338-
Name: models.ClientAppSub.String(),
339-
Type: models.ClientAppSub,
340-
},
341-
Origin: models.ClientApp,
342-
})
343313
}
344314

345315
username := authRequest.UserName
@@ -393,6 +363,44 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
393363
return
394364
}
395365
logger.Log(2, username, "was authenticated")
366+
367+
// log user activity
368+
if !user.IsMFAEnabled {
369+
if val := request.Header.Get("From-Ui"); val == "true" {
370+
logic.LogEvent(&models.Event{
371+
Action: models.Login,
372+
Source: models.Subject{
373+
ID: user.UserName,
374+
Name: user.UserName,
375+
Type: models.UserSub,
376+
},
377+
TriggeredBy: user.UserName,
378+
Target: models.Subject{
379+
ID: models.DashboardSub.String(),
380+
Name: models.DashboardSub.String(),
381+
Type: models.DashboardSub,
382+
},
383+
Origin: models.Dashboard,
384+
})
385+
} else {
386+
logic.LogEvent(&models.Event{
387+
Action: models.Login,
388+
Source: models.Subject{
389+
ID: user.UserName,
390+
Name: user.UserName,
391+
Type: models.UserSub,
392+
},
393+
TriggeredBy: user.UserName,
394+
Target: models.Subject{
395+
ID: models.ClientAppSub.String(),
396+
Name: models.ClientAppSub.String(),
397+
Type: models.ClientAppSub,
398+
},
399+
Origin: models.ClientApp,
400+
})
401+
}
402+
}
403+
396404
response.Header().Set("Content-Type", "application/json")
397405
response.Write(successJSONResponse)
398406

@@ -434,6 +442,43 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
434442
}()
435443
}
436444

445+
// @Summary Validates a user's identity against it's token. This is used by UI before a user performing a critical operation to validate the user's identity.
446+
// @Router /api/users/{username}/validate-identity [post]
447+
// @Tags Auth
448+
// @Accept json
449+
// @Param body body models.UserIdentityValidationRequest true "User Identity Validation Request"
450+
// @Success 200 {object} models.SuccessResponse
451+
// @Failure 400 {object} models.ErrorResponse
452+
func validateUserIdentity(w http.ResponseWriter, r *http.Request) {
453+
username := r.Header.Get("user")
454+
455+
var req models.UserIdentityValidationRequest
456+
err := json.NewDecoder(r.Body).Decode(&req)
457+
if err != nil {
458+
logger.Log(0, "failed to decode request body: ", err.Error())
459+
err = fmt.Errorf("invalid request body: %v", err)
460+
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
461+
return
462+
}
463+
464+
user, err := logic.GetUser(username)
465+
if err != nil {
466+
logger.Log(0, "failed to get user: ", err.Error())
467+
err = fmt.Errorf("user not found: %v", err)
468+
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
469+
return
470+
}
471+
472+
var resp models.UserIdentityValidationResponse
473+
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password))
474+
if err != nil {
475+
logic.ReturnSuccessResponseWithJson(w, r, resp, "user identity validation failed")
476+
} else {
477+
resp.IdentityValidated = true
478+
logic.ReturnSuccessResponseWithJson(w, r, resp, "user identity validated")
479+
}
480+
}
481+
437482
// @Summary Initiate setting up TOTP 2FA for a user.
438483
// @Router /api/users/auth/init-totp [post]
439484
// @Tags Auth
@@ -557,6 +602,22 @@ func completeTOTPSetup(w http.ResponseWriter, r *http.Request) {
557602
return
558603
}
559604

605+
logic.LogEvent(&models.Event{
606+
Action: models.EnableMFA,
607+
Source: models.Subject{
608+
ID: user.UserName,
609+
Name: user.UserName,
610+
Type: models.UserSub,
611+
},
612+
TriggeredBy: user.UserName,
613+
Target: models.Subject{
614+
ID: user.UserName,
615+
Name: user.UserName,
616+
Type: models.UserSub,
617+
},
618+
Origin: models.Dashboard,
619+
})
620+
560621
logic.ReturnSuccessResponse(w, r, fmt.Sprintf("totp setup complete for user %s", username))
561622
} else {
562623
err = fmt.Errorf("cannot setup totp for user %s: invalid otp", username)
@@ -619,6 +680,22 @@ func verifyTOTP(w http.ResponseWriter, r *http.Request) {
619680
return
620681
}
621682

683+
logic.LogEvent(&models.Event{
684+
Action: models.Login,
685+
Source: models.Subject{
686+
ID: user.UserName,
687+
Name: user.UserName,
688+
Type: models.UserSub,
689+
},
690+
TriggeredBy: user.UserName,
691+
Target: models.Subject{
692+
ID: models.DashboardSub.String(),
693+
Name: models.DashboardSub.String(),
694+
Type: models.DashboardSub,
695+
},
696+
Origin: models.Dashboard,
697+
})
698+
622699
logic.ReturnSuccessResponseWithJson(w, r, models.SuccessfulUserLoginResponse{
623700
UserName: username,
624701
AuthToken: jwt,
@@ -1135,8 +1212,22 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
11351212
UserName: logic.MasterUser,
11361213
}
11371214
}
1215+
action := models.Update
1216+
// TODO: here we are relying on the dashboard to only
1217+
// make singular updates, but it's possible that the
1218+
// API can be called to make multiple changes to the
1219+
// user. We should update it to log multiple events
1220+
// or create singular update APIs.
1221+
if userchange.IsMFAEnabled != user.IsMFAEnabled {
1222+
if userchange.IsMFAEnabled {
1223+
// the update API won't be used to enable MFA.
1224+
action = models.EnableMFA
1225+
} else {
1226+
action = models.DisableMFA
1227+
}
1228+
}
11381229
e := models.Event{
1139-
Action: models.Update,
1230+
Action: action,
11401231
Source: models.Subject{
11411232
ID: caller.UserName,
11421233
Name: caller.UserName,

logic/extpeers.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,7 @@ func GetAllExtClientsWithStatus(status models.NodeStatus) ([]models.ExtClient, e
484484
var validExtClients []models.ExtClient
485485
for _, extClient := range extClients {
486486
node := extClient.ConvertToStaticNode()
487-
GetNodeCheckInStatus(&node, false)
487+
GetNodeStatus(&node, false)
488488

489489
if node.Status == status {
490490
validExtClients = append(validExtClients, extClient)

logic/settings.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ func UpsertServerSettings(s models.ServerSettings) error {
3333
if s.ClientSecret == Mask() {
3434
s.ClientSecret = currSettings.ClientSecret
3535
}
36+
37+
if servercfg.DeployedByOperator() {
38+
s.BasicAuth = true
39+
}
40+
3641
data, err := json.Marshal(s)
3742
if err != nil {
3843
return err
@@ -317,6 +322,10 @@ func GetManageDNS() bool {
317322

318323
// IsBasicAuthEnabled - checks if basic auth has been configured to be turned off
319324
func IsBasicAuthEnabled() bool {
325+
if servercfg.DeployedByOperator() {
326+
return true
327+
}
328+
320329
return GetServerSettings().BasicAuth
321330
}
322331

logic/zombie.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,10 @@ func ManageZombies(ctx context.Context, peerUpdate chan *models.Node) {
139139
if servercfg.IsAutoCleanUpEnabled() {
140140
nodes, _ := GetAllNodes()
141141
for _, node := range nodes {
142-
if time.Since(node.LastCheckIn) > time.Minute*ZOMBIE_DELETE_TIME {
142+
if !node.Connected {
143+
continue
144+
}
145+
if time.Since(node.LastCheckIn) > time.Hour*2 {
143146
if err := DeleteNode(&node, true); err != nil {
144147
continue
145148
}
@@ -168,8 +171,8 @@ func checkPendingRemovalNodes(peerUpdate chan *models.Node) {
168171
peerUpdate <- &node
169172
continue
170173
}
171-
if servercfg.IsAutoCleanUpEnabled() {
172-
if time.Since(node.LastCheckIn) > time.Minute*ZOMBIE_DELETE_TIME {
174+
if servercfg.IsAutoCleanUpEnabled() && node.Connected {
175+
if time.Since(node.LastCheckIn) > time.Hour*2 {
173176
if err := DeleteNode(&node, true); err != nil {
174177
continue
175178
}

0 commit comments

Comments
 (0)