mirror of
https://github.com/gravitl/netmaker.git
synced 2025-09-04 04:04:17 +08:00
NET-2014: Audit Logging (#3455)
* feat: api access tokens
* revoke all user tokens
* redefine access token api routes, add auto egress option to enrollment keys
* add server settings apis, add db table for settigs
* handle server settings updates
* switch to using settings from DB
* fix sever settings migration
* revet force migration for settings
* fix server settings database write
* egress model
* fix revoked tokens to be unauthorized
* update egress model
* remove unused functions
* convert access token to sql schema
* switch access token to sql schema
* fix merge conflicts
* fix server settings types
* bypass basic auth setting for super admin
* add TODO comment
* setup api handlers for egress revamp
* use single DB, fix update nat boolean field
* extend validaiton checks for egress ranges
* add migration to convert to new egress model
* fix panic interface conversion
* publish peer update on settings update
* revoke token generated by an user
* add user token creation restriction by user role
* add forbidden check for access token creation
* revoke user token when group or role is changed
* add default group to admin users on update
* chore(go): import style changes from migration branch;
1. Singular file names for table schema.
2. No table name method.
3. Use .Model instead of .Table.
4. No unnecessary tagging.
* remove nat check on egress gateway request
* Revert "remove nat check on egress gateway request"
This reverts commit 0aff12a189
.
* remove nat check on egress gateway request
* feat(go): add db middleware;
* feat(go): restore method;
* feat(go): add user access token schema;
* add inet gw status to egress model
* fetch node ids in the tag, add inet gw info clients
* add inet gw info to node from egress list
* add migration logic internet gws
* create default acl policies
* add egress info
* add egress TODO
* add egress TODO
* fix user auth api:
* add reference id to acl policy
* add egress response from DB
* publish peer update on egress changes
* re initalise oauth and email config
* set verbosity
* normalise cidr on egress req
* add egress id to acl group
* change acls to use egress id
* resolve merge conflicts
* fix egress reference errors
* move egress model to schema
* add api context to DB
* sync auto update settings with hosts
* sync auto update settings with hosts
* check acl for egress node
* check for egress policy in the acl dst groups
* fix acl rules for egress policies with new models
* add status to egress model
* fix inet node func
* mask secret and convert jwt duration to minutes
* enable egress policies on creation
* convert jwt duration to minutes
* add relevant ranges to inet egress
* skip non active egress routes
* resolve merge conflicts
* fix static check
* notify peers after settings update
* define schema for activity, add api handler to list network activity
* setup event channel and logger
* setup event logger, add event for user login
* change activity model to event
* add api error constants
* add logout event
* log user crud events
* add login events for oauth
* add user related events
* log events for invites and user approvals
* order user activity event by timestamp
* fix logout api
* add user and network events api, add addtional events triggers
* add filters to all events api
* fix events filter
* add diff to event logs
* update user logout api
* log settigns updates
* log events for network and host updates
* check for diff on events
* log host del event
* add user loc info to desktop app connection events
* fix authorize middleware check
* add gateway events
* resolve merge conflicts
---------
Co-authored-by: Vishal Dalwadi <dalwadivishal26@gmail.com>
This commit is contained in:
parent
307a3d1e4b
commit
d7bad9865a
28 changed files with 1203 additions and 31 deletions
|
@ -268,6 +268,22 @@ func createAcl(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Create,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: acl.ID,
|
||||||
|
Name: acl.Name,
|
||||||
|
Type: models.AclSub,
|
||||||
|
},
|
||||||
|
NetworkID: acl.NetworkID,
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
go mq.PublishPeerUpdate(true)
|
go mq.PublishPeerUpdate(true)
|
||||||
logic.ReturnSuccessResponseWithJson(w, r, acl, "created acl successfully")
|
logic.ReturnSuccessResponseWithJson(w, r, acl, "created acl successfully")
|
||||||
}
|
}
|
||||||
|
@ -310,6 +326,26 @@ func updateAcl(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Update,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: acl.ID,
|
||||||
|
Name: acl.Name,
|
||||||
|
Type: models.AclSub,
|
||||||
|
},
|
||||||
|
Diff: models.Diff{
|
||||||
|
Old: acl,
|
||||||
|
New: updateAcl.Acl,
|
||||||
|
},
|
||||||
|
NetworkID: acl.NetworkID,
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
go mq.PublishPeerUpdate(true)
|
go mq.PublishPeerUpdate(true)
|
||||||
logic.ReturnSuccessResponse(w, r, "updated acl "+acl.Name)
|
logic.ReturnSuccessResponse(w, r, "updated acl "+acl.Name)
|
||||||
}
|
}
|
||||||
|
@ -341,6 +377,22 @@ func deleteAcl(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.FormatError(errors.New("cannot delete default policy"), "internal"))
|
logic.FormatError(errors.New("cannot delete default policy"), "internal"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Delete,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: acl.ID,
|
||||||
|
Name: acl.Name,
|
||||||
|
Type: models.AclSub,
|
||||||
|
},
|
||||||
|
NetworkID: acl.NetworkID,
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
go mq.PublishPeerUpdate(true)
|
go mq.PublishPeerUpdate(true)
|
||||||
logic.ReturnSuccessResponse(w, r, "deleted acl "+acl.Name)
|
logic.ReturnSuccessResponse(w, r, "deleted acl "+acl.Name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,6 +85,22 @@ func createEgress(w http.ResponseWriter, r *http.Request) {
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Create,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: e.ID,
|
||||||
|
Name: e.Name,
|
||||||
|
Type: models.EgressSub,
|
||||||
|
},
|
||||||
|
NetworkID: models.NetworkID(e.Network),
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
// for nodeID := range e.Nodes {
|
// for nodeID := range e.Nodes {
|
||||||
// node, err := logic.GetNodeByID(nodeID)
|
// node, err := logic.GetNodeByID(nodeID)
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
|
@ -174,6 +190,25 @@ func updateEgress(w http.ResponseWriter, r *http.Request) {
|
||||||
if req.Status != e.Status {
|
if req.Status != e.Status {
|
||||||
updateStatus = true
|
updateStatus = true
|
||||||
}
|
}
|
||||||
|
event := &models.Event{
|
||||||
|
Action: models.Update,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: e.ID,
|
||||||
|
Name: e.Name,
|
||||||
|
Type: models.EgressSub,
|
||||||
|
},
|
||||||
|
Diff: models.Diff{
|
||||||
|
Old: e,
|
||||||
|
},
|
||||||
|
NetworkID: models.NetworkID(e.Network),
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
}
|
||||||
e.Nodes = make(datatypes.JSONMap)
|
e.Nodes = make(datatypes.JSONMap)
|
||||||
e.Tags = make(datatypes.JSONMap)
|
e.Tags = make(datatypes.JSONMap)
|
||||||
for nodeID, metric := range req.Nodes {
|
for nodeID, metric := range req.Nodes {
|
||||||
|
@ -211,6 +246,8 @@ func updateEgress(w http.ResponseWriter, r *http.Request) {
|
||||||
e.Status = req.Status
|
e.Status = req.Status
|
||||||
e.UpdateEgressStatus(db.WithContext(context.TODO()))
|
e.UpdateEgressStatus(db.WithContext(context.TODO()))
|
||||||
}
|
}
|
||||||
|
event.Diff.New = e
|
||||||
|
logic.LogEvent(event)
|
||||||
go mq.PublishPeerUpdate(false)
|
go mq.PublishPeerUpdate(false)
|
||||||
logic.ReturnSuccessResponseWithJson(w, r, e, "updated egress resource")
|
logic.ReturnSuccessResponseWithJson(w, r, e, "updated egress resource")
|
||||||
}
|
}
|
||||||
|
@ -237,6 +274,22 @@ func deleteEgress(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Delete,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: e.ID,
|
||||||
|
Name: e.Name,
|
||||||
|
Type: models.EgressSub,
|
||||||
|
},
|
||||||
|
NetworkID: models.NetworkID(e.Network),
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
// delete related acl policies
|
// delete related acl policies
|
||||||
acls := logic.ListAcls()
|
acls := logic.ListAcls()
|
||||||
for _, acl := range acls {
|
for _, acl := range acls {
|
||||||
|
|
|
@ -72,12 +72,32 @@ func getEnrollmentKeys(w http.ResponseWriter, r *http.Request) {
|
||||||
func deleteEnrollmentKey(w http.ResponseWriter, r *http.Request) {
|
func deleteEnrollmentKey(w http.ResponseWriter, r *http.Request) {
|
||||||
params := mux.Vars(r)
|
params := mux.Vars(r)
|
||||||
keyID := params["keyID"]
|
keyID := params["keyID"]
|
||||||
err := logic.DeleteEnrollmentKey(keyID, false)
|
key, err := logic.GetEnrollmentKey(keyID)
|
||||||
|
if err != nil {
|
||||||
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = logic.DeleteEnrollmentKey(keyID, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log(0, r.Header.Get("user"), "failed to remove enrollment key: ", err.Error())
|
logger.Log(0, r.Header.Get("user"), "failed to remove enrollment key: ", err.Error())
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Delete,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: keyID,
|
||||||
|
Name: key.Tags[0],
|
||||||
|
Type: models.EnrollmentKeySub,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
logger.Log(2, r.Header.Get("user"), "deleted enrollment key", keyID)
|
logger.Log(2, r.Header.Get("user"), "deleted enrollment key", keyID)
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
@ -173,6 +193,21 @@ func createEnrollmentKey(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Create,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: newEnrollmentKey.Value,
|
||||||
|
Name: newEnrollmentKey.Tags[0],
|
||||||
|
Type: models.EnrollmentKeySub,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
logger.Log(2, r.Header.Get("user"), "created enrollment key")
|
logger.Log(2, r.Header.Get("user"), "created enrollment key")
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
json.NewEncoder(w).Encode(newEnrollmentKey)
|
json.NewEncoder(w).Encode(newEnrollmentKey)
|
||||||
|
@ -208,6 +243,7 @@ func updateEnrollmentKey(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
currKey, _ := logic.GetEnrollmentKey(keyId)
|
||||||
|
|
||||||
newEnrollmentKey, err := logic.UpdateEnrollmentKey(keyId, relayId, enrollmentKeyBody.Groups)
|
newEnrollmentKey, err := logic.UpdateEnrollmentKey(keyId, relayId, enrollmentKeyBody.Groups)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -221,7 +257,25 @@ func updateEnrollmentKey(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Update,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: newEnrollmentKey.Value,
|
||||||
|
Name: newEnrollmentKey.Tags[0],
|
||||||
|
Type: models.EnrollmentKeySub,
|
||||||
|
},
|
||||||
|
Diff: models.Diff{
|
||||||
|
Old: currKey,
|
||||||
|
New: newEnrollmentKey,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
slog.Info("updated enrollment key", "id", keyId)
|
slog.Info("updated enrollment key", "id", keyId)
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
json.NewEncoder(w).Encode(newEnrollmentKey)
|
json.NewEncoder(w).Encode(newEnrollmentKey)
|
||||||
|
@ -355,6 +409,25 @@ func handleHostRegister(w http.ResponseWriter, r *http.Request) {
|
||||||
ServerConf: server,
|
ServerConf: server,
|
||||||
RequestedHost: newHost,
|
RequestedHost: newHost,
|
||||||
}
|
}
|
||||||
|
for _, netID := range enrollmentKey.Networks {
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.JoinHostToNet,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: enrollmentKey.Value,
|
||||||
|
Name: enrollmentKey.Tags[0],
|
||||||
|
Type: models.EnrollmentKeySub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: newHost.ID.String(),
|
||||||
|
Name: newHost.Name,
|
||||||
|
Type: models.DeviceSub,
|
||||||
|
},
|
||||||
|
NetworkID: models.NetworkID(netID),
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
logger.Log(0, newHost.Name, newHost.ID.String(), "registered with Netmaker")
|
logger.Log(0, newHost.Name, newHost.ID.String(), "registered with Netmaker")
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
json.NewEncoder(w).Encode(&response)
|
json.NewEncoder(w).Encode(&response)
|
||||||
|
|
|
@ -799,6 +799,27 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
|
||||||
"clientid",
|
"clientid",
|
||||||
extclient.ClientID,
|
extclient.ClientID,
|
||||||
)
|
)
|
||||||
|
if extclient.RemoteAccessClientID != "" {
|
||||||
|
// if created by user from client app, log event
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Connect,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: userName,
|
||||||
|
Name: userName,
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: userName,
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: extclient.Network,
|
||||||
|
Name: extclient.Network,
|
||||||
|
Type: models.NetworkSub,
|
||||||
|
Info: extclient,
|
||||||
|
},
|
||||||
|
NetworkID: models.NetworkID(extclient.Network),
|
||||||
|
Origin: models.ClientApp,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
go func() {
|
go func() {
|
||||||
if err := logic.SetClientDefaultACLs(&extclient); err != nil {
|
if err := logic.SetClientDefaultACLs(&extclient); err != nil {
|
||||||
|
|
|
@ -39,6 +39,11 @@ func createGateway(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
host, err := logic.GetHost(node.HostID.String())
|
||||||
|
if err != nil {
|
||||||
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||||
|
return
|
||||||
|
}
|
||||||
var req models.CreateGwReq
|
var req models.CreateGwReq
|
||||||
err = json.NewDecoder(r.Body).Decode(&req)
|
err = json.NewDecoder(r.Body).Decode(&req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -89,7 +94,21 @@ func createGateway(w http.ResponseWriter, r *http.Request) {
|
||||||
)
|
)
|
||||||
logic.GetNodeStatus(&relayNode, false)
|
logic.GetNodeStatus(&relayNode, false)
|
||||||
apiNode := relayNode.ConvertToAPINode()
|
apiNode := relayNode.ConvertToAPINode()
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Create,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: node.ID.String(),
|
||||||
|
Name: host.Name,
|
||||||
|
Type: models.GatewaySub,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
json.NewEncoder(w).Encode(apiNode)
|
json.NewEncoder(w).Encode(apiNode)
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -138,6 +157,11 @@ func deleteGateway(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
host, err := logic.GetHost(node.HostID.String())
|
||||||
|
if err != nil {
|
||||||
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||||
|
return
|
||||||
|
}
|
||||||
node.IsGw = false
|
node.IsGw = false
|
||||||
logic.UpsertNode(&node)
|
logic.UpsertNode(&node)
|
||||||
logger.Log(1, r.Header.Get("user"), "deleted gw", nodeid, "on network", netid)
|
logger.Log(1, r.Header.Get("user"), "deleted gw", nodeid, "on network", netid)
|
||||||
|
@ -200,7 +224,21 @@ func deleteGateway(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}()
|
}()
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Delete,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: node.ID.String(),
|
||||||
|
Name: host.Name,
|
||||||
|
Type: models.GatewaySub,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
logic.GetNodeStatus(&node, false)
|
logic.GetNodeStatus(&node, false)
|
||||||
apiNode := node.ConvertToAPINode()
|
apiNode := node.ConvertToAPINode()
|
||||||
logger.Log(1, r.Header.Get("user"), "deleted ingress gateway", nodeid)
|
logger.Log(1, r.Header.Get("user"), "deleted ingress gateway", nodeid)
|
||||||
|
|
|
@ -294,7 +294,25 @@ func updateHost(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Update,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: currHost.ID.String(),
|
||||||
|
Name: newHost.Name,
|
||||||
|
Type: models.DeviceSub,
|
||||||
|
},
|
||||||
|
Diff: models.Diff{
|
||||||
|
Old: currHost,
|
||||||
|
New: newHost,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
apiHostData := newHost.ConvertNMHostToAPI()
|
apiHostData := newHost.ConvertNMHostToAPI()
|
||||||
logger.Log(2, r.Header.Get("user"), "updated host", newHost.ID.String())
|
logger.Log(2, r.Header.Get("user"), "updated host", newHost.ID.String())
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
@ -420,7 +438,21 @@ func deleteHost(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Delete,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: currHost.ID.String(),
|
||||||
|
Name: currHost.Name,
|
||||||
|
Type: models.DeviceSub,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
apiHostData := currHost.ConvertNMHostToAPI()
|
apiHostData := currHost.ConvertNMHostToAPI()
|
||||||
logger.Log(2, r.Header.Get("user"), "removed host", currHost.Name)
|
logger.Log(2, r.Header.Get("user"), "removed host", currHost.Name)
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
@ -492,6 +524,22 @@ func addHostToNetwork(w http.ResponseWriter, r *http.Request) {
|
||||||
r.Header.Get("user"),
|
r.Header.Get("user"),
|
||||||
fmt.Sprintf("added host %s to network %s", currHost.Name, network),
|
fmt.Sprintf("added host %s to network %s", currHost.Name, network),
|
||||||
)
|
)
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.JoinHostToNet,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: currHost.ID.String(),
|
||||||
|
Name: currHost.Name,
|
||||||
|
Type: models.DeviceSub,
|
||||||
|
},
|
||||||
|
NetworkID: models.NetworkID(network),
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -623,6 +671,22 @@ func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.SetDNS()
|
logic.SetDNS()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.RemoveHostFromNet,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: currHost.ID.String(),
|
||||||
|
Name: currHost.Name,
|
||||||
|
Type: models.DeviceSub,
|
||||||
|
},
|
||||||
|
NetworkID: models.NetworkID(network),
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
logger.Log(
|
logger.Log(
|
||||||
2,
|
2,
|
||||||
r.Header.Get("user"),
|
r.Header.Get("user"),
|
||||||
|
@ -937,7 +1001,21 @@ func syncHost(w http.ResponseWriter, r *http.Request) {
|
||||||
slog.Error("failed to send host pull request", "host", host.ID.String(), "error", err)
|
slog.Error("failed to send host pull request", "host", host.ID.String(), "error", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Sync,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: host.ID.String(),
|
||||||
|
Name: host.Name,
|
||||||
|
Type: models.DeviceSub,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
slog.Info("requested host pull", "user", r.Header.Get("user"), "host", host.ID.String())
|
slog.Info("requested host pull", "user", r.Header.Get("user"), "host", host.ID.String())
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
|
@ -483,9 +483,9 @@ func deleteNetwork(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
err = logic.DeleteNetwork(network, force, doneCh)
|
err = logic.DeleteNetwork(network, force, doneCh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errtype := "badrequest"
|
errtype := logic.BadReq
|
||||||
if strings.Contains(err.Error(), "Node check failed") {
|
if strings.Contains(err.Error(), "Node check failed") {
|
||||||
errtype = "forbidden"
|
errtype = logic.Forbidden
|
||||||
}
|
}
|
||||||
logger.Log(0, r.Header.Get("user"),
|
logger.Log(0, r.Header.Get("user"),
|
||||||
fmt.Sprintf("failed to delete network [%s]: %v", network, err))
|
fmt.Sprintf("failed to delete network [%s]: %v", network, err))
|
||||||
|
@ -514,6 +514,21 @@ func deleteNetwork(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.SetDNS()
|
logic.SetDNS()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Delete,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: network,
|
||||||
|
Name: network,
|
||||||
|
Type: models.NetworkSub,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
logger.Log(1, r.Header.Get("user"), "deleted network", network)
|
logger.Log(1, r.Header.Get("user"), "deleted network", network)
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
json.NewEncoder(w).Encode("success")
|
json.NewEncoder(w).Encode("success")
|
||||||
|
@ -636,7 +651,22 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
|
||||||
logger.Log(1, "failed to publish peer update for default hosts after network is added")
|
logger.Log(1, "failed to publish peer update for default hosts after network is added")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Create,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: network.NetID,
|
||||||
|
Name: network.NetID,
|
||||||
|
Type: models.NetworkSub,
|
||||||
|
Info: network,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
logger.Log(1, r.Header.Get("user"), "created network", network.NetID)
|
logger.Log(1, r.Header.Get("user"), "created network", network.NetID)
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
json.NewEncoder(w).Encode(network)
|
json.NewEncoder(w).Encode(network)
|
||||||
|
|
|
@ -178,7 +178,7 @@ func Authorize(
|
||||||
// check if host instead of user
|
// check if host instead of user
|
||||||
if hostAllowed {
|
if hostAllowed {
|
||||||
// TODO --- should ensure that node is only operating on itself
|
// TODO --- should ensure that node is only operating on itself
|
||||||
if hostID, _, _, err := logic.VerifyHostToken(authToken); err == nil {
|
if hostID, macAddr, _, err := logic.VerifyHostToken(authToken); err == nil && macAddr != "" {
|
||||||
r.Header.Set(hostIDHeader, hostID)
|
r.Header.Set(hostIDHeader, hostID)
|
||||||
// this indicates request is from a node
|
// this indicates request is from a node
|
||||||
// used for failover - if a getNode comes from node, this will trigger a metrics wipe
|
// used for failover - if a getNode comes from node, this will trigger a metrics wipe
|
||||||
|
@ -650,7 +650,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, err = logic.GetHost(newNode.HostID.String())
|
host, err := logic.GetHost(newNode.HostID.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log(0, r.Header.Get("user"),
|
logger.Log(0, r.Header.Get("user"),
|
||||||
fmt.Sprintf("failed to get host for node [ %s ] info: %v", nodeid, err))
|
fmt.Sprintf("failed to get host for node [ %s ] info: %v", nodeid, err))
|
||||||
|
@ -682,6 +682,25 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
|
||||||
"on network",
|
"on network",
|
||||||
currentNode.Network,
|
currentNode.Network,
|
||||||
)
|
)
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Update,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: newNode.ID.String(),
|
||||||
|
Name: host.Name,
|
||||||
|
Type: models.NodeSub,
|
||||||
|
},
|
||||||
|
Diff: models.Diff{
|
||||||
|
Old: currentNode,
|
||||||
|
New: newNode,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
json.NewEncoder(w).Encode(apiNode)
|
json.NewEncoder(w).Encode(apiNode)
|
||||||
go func(aclUpdate, relayupdate bool, newNode *models.Node) {
|
go func(aclUpdate, relayupdate bool, newNode *models.Node) {
|
||||||
|
|
|
@ -271,6 +271,25 @@ func updateSettings(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to udpate server settings "+err.Error()), "internal"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to udpate server settings "+err.Error()), "internal"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Update,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: models.SettingSub.String(),
|
||||||
|
Name: models.SettingSub.String(),
|
||||||
|
Type: models.SettingSub,
|
||||||
|
},
|
||||||
|
Diff: models.Diff{
|
||||||
|
Old: currSettings,
|
||||||
|
New: req,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
go reInit(currSettings, req, force == "true")
|
go reInit(currSettings, req, force == "true")
|
||||||
logic.ReturnSuccessResponseWithJson(w, r, req, "updated server settings successfully")
|
logic.ReturnSuccessResponseWithJson(w, r, req, "updated server settings successfully")
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,6 +131,22 @@ func createTag(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.UpsertNode(&node)
|
logic.UpsertNode(&node)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Create,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: tag.ID.String(),
|
||||||
|
Name: tag.TagName,
|
||||||
|
Type: models.TagSub,
|
||||||
|
},
|
||||||
|
NetworkID: tag.Network,
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
go mq.PublishPeerUpdate(false)
|
go mq.PublishPeerUpdate(false)
|
||||||
|
|
||||||
var res models.TagListRespNodes = models.TagListRespNodes{
|
var res models.TagListRespNodes = models.TagListRespNodes{
|
||||||
|
@ -163,6 +179,25 @@ func updateTag(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
e := &models.Event{
|
||||||
|
Action: models.Update,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: tag.ID.String(),
|
||||||
|
Name: tag.TagName,
|
||||||
|
Type: models.TagSub,
|
||||||
|
},
|
||||||
|
Diff: models.Diff{
|
||||||
|
Old: tag,
|
||||||
|
},
|
||||||
|
NetworkID: tag.Network,
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
}
|
||||||
updateTag.NewName = strings.TrimSpace(updateTag.NewName)
|
updateTag.NewName = strings.TrimSpace(updateTag.NewName)
|
||||||
var newID models.TagID
|
var newID models.TagID
|
||||||
if updateTag.NewName != "" {
|
if updateTag.NewName != "" {
|
||||||
|
@ -198,7 +233,8 @@ func updateTag(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
mq.PublishPeerUpdate(false)
|
mq.PublishPeerUpdate(false)
|
||||||
}()
|
}()
|
||||||
|
e.Diff.New = updateTag
|
||||||
|
logic.LogEvent(e)
|
||||||
var res models.TagListRespNodes = models.TagListRespNodes{
|
var res models.TagListRespNodes = models.TagListRespNodes{
|
||||||
Tag: tag,
|
Tag: tag,
|
||||||
UsedByCnt: len(updateTag.TaggedNodes),
|
UsedByCnt: len(updateTag.TaggedNodes),
|
||||||
|
@ -241,5 +277,21 @@ func deleteTag(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.RemoveTagFromEnrollmentKeys(tag.ID)
|
logic.RemoveTagFromEnrollmentKeys(tag.ID)
|
||||||
mq.PublishPeerUpdate(false)
|
mq.PublishPeerUpdate(false)
|
||||||
}()
|
}()
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Delete,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: tag.ID.String(),
|
||||||
|
Name: tag.TagName,
|
||||||
|
Type: models.TagSub,
|
||||||
|
},
|
||||||
|
NetworkID: tag.Network,
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
logic.ReturnSuccessResponse(w, r, "deleted tag "+tagID)
|
logic.ReturnSuccessResponse(w, r, "deleted tag "+tagID)
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ func userHandlers(r *mux.Router) {
|
||||||
r.HandleFunc("/api/v1/users/access_token", logic.SecurityCheck(true, http.HandlerFunc(createUserAccessToken))).Methods(http.MethodPost)
|
r.HandleFunc("/api/v1/users/access_token", logic.SecurityCheck(true, http.HandlerFunc(createUserAccessToken))).Methods(http.MethodPost)
|
||||||
r.HandleFunc("/api/v1/users/access_token", logic.SecurityCheck(true, http.HandlerFunc(getUserAccessTokens))).Methods(http.MethodGet)
|
r.HandleFunc("/api/v1/users/access_token", logic.SecurityCheck(true, http.HandlerFunc(getUserAccessTokens))).Methods(http.MethodGet)
|
||||||
r.HandleFunc("/api/v1/users/access_token", logic.SecurityCheck(true, http.HandlerFunc(deleteUserAccessTokens))).Methods(http.MethodDelete)
|
r.HandleFunc("/api/v1/users/access_token", logic.SecurityCheck(true, http.HandlerFunc(deleteUserAccessTokens))).Methods(http.MethodDelete)
|
||||||
|
r.HandleFunc("/api/v1/users/logout", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(logout)))).Methods(http.MethodPost)
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Summary Authenticate a user to retrieve an authorization token
|
// @Summary Authenticate a user to retrieve an authorization token
|
||||||
|
@ -64,25 +65,25 @@ func createUserAccessToken(w http.ResponseWriter, r *http.Request) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log(0, "error decoding request body: ",
|
logger.Log(0, "error decoding request body: ",
|
||||||
err.Error())
|
err.Error())
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, logic.BadReq))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if req.Name == "" {
|
if req.Name == "" {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("name is required"), "badrequest"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("name is required"), logic.BadReq))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if req.UserName == "" {
|
if req.UserName == "" {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("username is required"), "badrequest"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("username is required"), logic.BadReq))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
caller, err := logic.GetUser(r.Header.Get("user"))
|
caller, err := logic.GetUser(r.Header.Get("user"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "unauthorized"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, logic.UnAuthorized))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
user, err := logic.GetUser(req.UserName)
|
user, err := logic.GetUser(req.UserName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "unauthorized"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, logic.UnAuthorized))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if caller.UserName != user.UserName && caller.PlatformRoleID != models.SuperAdminRole {
|
if caller.UserName != user.UserName && caller.PlatformRoleID != models.SuperAdminRole {
|
||||||
|
@ -106,7 +107,7 @@ func createUserAccessToken(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.ReturnErrorResponse(
|
logic.ReturnErrorResponse(
|
||||||
w,
|
w,
|
||||||
r,
|
r,
|
||||||
logic.FormatError(errors.New("error creating access token "+err.Error()), "internal"),
|
logic.FormatError(errors.New("error creating access token "+err.Error()), logic.Internal),
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -115,10 +116,26 @@ func createUserAccessToken(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.ReturnErrorResponse(
|
logic.ReturnErrorResponse(
|
||||||
w,
|
w,
|
||||||
r,
|
r,
|
||||||
logic.FormatError(errors.New("error creating access token "+err.Error()), "internal"),
|
logic.FormatError(errors.New("error creating access token "+err.Error()), logic.Internal),
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Create,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: caller.UserName,
|
||||||
|
Name: caller.UserName,
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: caller.UserName,
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: req.ID,
|
||||||
|
Name: req.Name,
|
||||||
|
Type: models.UserAccessTokenSub,
|
||||||
|
Info: req,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
logic.ReturnSuccessResponseWithJson(w, r, models.SuccessfulUserLoginResponse{
|
logic.ReturnSuccessResponseWithJson(w, r, models.SuccessfulUserLoginResponse{
|
||||||
AuthToken: jwt,
|
AuthToken: jwt,
|
||||||
UserName: req.UserName,
|
UserName: req.UserName,
|
||||||
|
@ -197,6 +214,22 @@ func deleteUserAccessTokens(w http.ResponseWriter, r *http.Request) {
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Delete,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: caller.UserName,
|
||||||
|
Name: caller.UserName,
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: caller.UserName,
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: a.ID,
|
||||||
|
Name: a.Name,
|
||||||
|
Type: models.UserAccessTokenSub,
|
||||||
|
Info: a,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
logic.ReturnSuccessResponseWithJson(w, r, nil, "revoked access token")
|
logic.ReturnSuccessResponseWithJson(w, r, nil, "revoked access token")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,6 +291,38 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
|
||||||
logic.ReturnErrorResponse(response, request, logic.FormatError(errors.New("access denied to dashboard"), "unauthorized"))
|
logic.ReturnErrorResponse(response, request, logic.FormatError(errors.New("access denied to dashboard"), "unauthorized"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// log user activity
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Login,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: user.UserName,
|
||||||
|
Name: user.UserName,
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: user.UserName,
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: models.DashboardSub.String(),
|
||||||
|
Name: models.DashboardSub.String(),
|
||||||
|
Type: models.DashboardSub,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Login,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: user.UserName,
|
||||||
|
Name: user.UserName,
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: user.UserName,
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: models.ClientAppSub.String(),
|
||||||
|
Name: models.ClientAppSub.String(),
|
||||||
|
Type: models.ClientAppSub,
|
||||||
|
},
|
||||||
|
Origin: models.ClientApp,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
username := authRequest.UserName
|
username := authRequest.UserName
|
||||||
|
@ -614,6 +679,21 @@ func createUser(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Create,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: caller.UserName,
|
||||||
|
Name: caller.UserName,
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: caller.UserName,
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: user.UserName,
|
||||||
|
Name: user.UserName,
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
logic.DeleteUserInvite(user.UserName)
|
logic.DeleteUserInvite(user.UserName)
|
||||||
logic.DeletePendingUser(user.UserName)
|
logic.DeletePendingUser(user.UserName)
|
||||||
go mq.PublishPeerUpdate(false)
|
go mq.PublishPeerUpdate(false)
|
||||||
|
@ -752,6 +832,25 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
|
||||||
if userchange.PlatformRoleID != user.PlatformRoleID || !logic.CompareMaps(user.UserGroups, userchange.UserGroups) {
|
if userchange.PlatformRoleID != user.PlatformRoleID || !logic.CompareMaps(user.UserGroups, userchange.UserGroups) {
|
||||||
(&schema.UserAccessToken{UserName: user.UserName}).DeleteAllUserTokens(r.Context())
|
(&schema.UserAccessToken{UserName: user.UserName}).DeleteAllUserTokens(r.Context())
|
||||||
}
|
}
|
||||||
|
e := models.Event{
|
||||||
|
Action: models.Update,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: caller.UserName,
|
||||||
|
Name: caller.UserName,
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: caller.UserName,
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: user.UserName,
|
||||||
|
Name: user.UserName,
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
Diff: models.Diff{
|
||||||
|
Old: user,
|
||||||
|
New: userchange,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
}
|
||||||
user, err = logic.UpdateUser(&userchange, user)
|
user, err = logic.UpdateUser(&userchange, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log(0, username,
|
logger.Log(0, username,
|
||||||
|
@ -759,6 +858,7 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&e)
|
||||||
go mq.PublishPeerUpdate(false)
|
go mq.PublishPeerUpdate(false)
|
||||||
logger.Log(1, username, "was updated")
|
logger.Log(1, username, "was updated")
|
||||||
json.NewEncoder(w).Encode(logic.ToReturnUser(*user))
|
json.NewEncoder(w).Encode(logic.ToReturnUser(*user))
|
||||||
|
@ -837,6 +937,21 @@ func deleteUser(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Delete,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: caller.UserName,
|
||||||
|
Name: caller.UserName,
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: caller.UserName,
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: user.UserName,
|
||||||
|
Name: user.UserName,
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
// check and delete extclient with this ownerID
|
// check and delete extclient with this ownerID
|
||||||
go func() {
|
go func() {
|
||||||
extclients, err := logic.GetAllExtClients()
|
extclients, err := logic.GetAllExtClients()
|
||||||
|
@ -902,3 +1017,50 @@ func listRoles(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
logic.ReturnSuccessResponseWithJson(w, r, roles, "successfully fetched user roles permission templates")
|
logic.ReturnSuccessResponseWithJson(w, r, roles, "successfully fetched user roles permission templates")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// swagger:route POST /api/v1/user/logout user logout
|
||||||
|
//
|
||||||
|
// LogOut user.
|
||||||
|
//
|
||||||
|
// Schemes: https
|
||||||
|
//
|
||||||
|
// Security:
|
||||||
|
// oauth
|
||||||
|
//
|
||||||
|
// Responses:
|
||||||
|
// 200: userBodyResponse
|
||||||
|
func logout(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// set header.
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
userName := r.URL.Query().Get("username")
|
||||||
|
user, err := logic.GetUser(userName)
|
||||||
|
if err != nil {
|
||||||
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, logic.BadReq))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var target models.SubjectType
|
||||||
|
if val := r.Header.Get("From-Ui"); val == "true" {
|
||||||
|
target = models.DashboardSub
|
||||||
|
} else {
|
||||||
|
target = models.ClientAppSub
|
||||||
|
}
|
||||||
|
if target != "" {
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.LogOut,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: user.UserName,
|
||||||
|
Name: user.UserName,
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: user.UserName,
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: target.String(),
|
||||||
|
Name: target.String(),
|
||||||
|
Type: target,
|
||||||
|
},
|
||||||
|
Origin: models.Origin(target),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
logic.ReturnSuccessResponse(w, r, "user logged out")
|
||||||
|
}
|
||||||
|
|
12
db/db.go
12
db/db.go
|
@ -83,6 +83,18 @@ func FromContext(ctx context.Context) *gorm.DB {
|
||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetPagination(ctx context.Context, page, pageSize int) context.Context {
|
||||||
|
if page < 1 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
if pageSize < 1 || pageSize > 100 {
|
||||||
|
pageSize = 10
|
||||||
|
}
|
||||||
|
db := FromContext(ctx)
|
||||||
|
offset := (page - 1) * pageSize
|
||||||
|
return context.WithValue(ctx, dbCtxKey, db.Offset(offset).Limit(pageSize))
|
||||||
|
}
|
||||||
|
|
||||||
// BeginTx returns a context with a new transaction.
|
// BeginTx returns a context with a new transaction.
|
||||||
// If the context already has a db connection instance,
|
// If the context already has a db connection instance,
|
||||||
// it uses that instance. Otherwise, it uses the
|
// it uses that instance. Otherwise, it uses the
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -59,6 +59,7 @@ require (
|
||||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||||
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||||
|
github.com/google/go-cmp v0.7.0 // indirect
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -40,6 +40,8 @@ github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei
|
||||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e h1:XmA6L9IPRdUr28a+SK/oMchGgQy159wvzXA5tJ7l+40=
|
github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e h1:XmA6L9IPRdUr28a+SK/oMchGgQy159wvzXA5tJ7l+40=
|
||||||
|
|
|
@ -8,20 +8,30 @@ import (
|
||||||
"golang.org/x/exp/slog"
|
"golang.org/x/exp/slog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ApiErrorType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Internal ApiErrorType = "internal"
|
||||||
|
BadReq ApiErrorType = "badrequest"
|
||||||
|
NotFound ApiErrorType = "notfound"
|
||||||
|
UnAuthorized ApiErrorType = "unauthorized"
|
||||||
|
Forbidden ApiErrorType = "forbidden"
|
||||||
|
)
|
||||||
|
|
||||||
// FormatError - takes ErrorResponse and uses correct code
|
// FormatError - takes ErrorResponse and uses correct code
|
||||||
func FormatError(err error, errType string) models.ErrorResponse {
|
func FormatError(err error, errType ApiErrorType) models.ErrorResponse {
|
||||||
|
|
||||||
var status = http.StatusInternalServerError
|
var status = http.StatusInternalServerError
|
||||||
switch errType {
|
switch errType {
|
||||||
case "internal":
|
case Internal:
|
||||||
status = http.StatusInternalServerError
|
status = http.StatusInternalServerError
|
||||||
case "badrequest":
|
case BadReq:
|
||||||
status = http.StatusBadRequest
|
status = http.StatusBadRequest
|
||||||
case "notfound":
|
case NotFound:
|
||||||
status = http.StatusNotFound
|
status = http.StatusNotFound
|
||||||
case "unauthorized":
|
case UnAuthorized:
|
||||||
status = http.StatusUnauthorized
|
status = http.StatusUnauthorized
|
||||||
case "forbidden":
|
case Forbidden:
|
||||||
status = http.StatusForbidden
|
status = http.StatusForbidden
|
||||||
default:
|
default:
|
||||||
status = http.StatusInternalServerError
|
status = http.StatusInternalServerError
|
||||||
|
|
|
@ -125,6 +125,25 @@ func DeleteExtClient(network string, clientid string) error {
|
||||||
}
|
}
|
||||||
deleteExtClientFromCache(key)
|
deleteExtClientFromCache(key)
|
||||||
}
|
}
|
||||||
|
if extClient.RemoteAccessClientID != "" {
|
||||||
|
LogEvent(&models.Event{
|
||||||
|
Action: models.Disconnect,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: extClient.OwnerID,
|
||||||
|
Name: extClient.OwnerID,
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: extClient.OwnerID,
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: extClient.Network,
|
||||||
|
Name: extClient.Network,
|
||||||
|
Type: models.NetworkSub,
|
||||||
|
Info: extClient,
|
||||||
|
},
|
||||||
|
NetworkID: models.NetworkID(extClient.Network),
|
||||||
|
Origin: models.ClientApp,
|
||||||
|
})
|
||||||
|
}
|
||||||
go RemoveNodeFromAclPolicy(extClient.ConvertToStaticNode())
|
go RemoveNodeFromAclPolicy(extClient.ConvertToStaticNode())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ var (
|
||||||
telServerRecord = models.Telemetry{}
|
telServerRecord = models.Telemetry{}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var LogEvent = func(a *models.Event) {}
|
||||||
|
|
||||||
// posthog_pub_key - Key for sending data to PostHog
|
// posthog_pub_key - Key for sending data to PostHog
|
||||||
const posthog_pub_key = "phc_1vEXhPOA1P7HP5jP2dVU9xDTUqXHAelmtravyZ1vvES"
|
const posthog_pub_key = "phc_1vEXhPOA1P7HP5jP2dVU9xDTUqXHAelmtravyZ1vvES"
|
||||||
|
|
||||||
|
|
74
models/events.go
Normal file
74
models/events.go
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
type Action string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Create Action = "CREATE"
|
||||||
|
Update Action = "UPDATE"
|
||||||
|
Delete Action = "DELETE"
|
||||||
|
DeleteAll Action = "DELETE_ALL"
|
||||||
|
Login Action = "LOGIN"
|
||||||
|
LogOut Action = "LOGOUT"
|
||||||
|
Connect Action = "CONNECT"
|
||||||
|
Sync Action = "SYNC"
|
||||||
|
Disconnect Action = "DISCONNECT"
|
||||||
|
JoinHostToNet Action = "JOIN_HOST_TO_NETWORK"
|
||||||
|
RemoveHostFromNet Action = "REMOVE_HOST_FROM_NETWORK"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SubjectType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
UserSub SubjectType = "USER"
|
||||||
|
UserAccessTokenSub SubjectType = "USER_ACCESS_TOKEN"
|
||||||
|
DeviceSub SubjectType = "DEVICE"
|
||||||
|
NodeSub SubjectType = "NODE"
|
||||||
|
GatewaySub SubjectType = "GATEWAY"
|
||||||
|
SettingSub SubjectType = "SETTING"
|
||||||
|
AclSub SubjectType = "ACL"
|
||||||
|
TagSub SubjectType = "TAG"
|
||||||
|
UserRoleSub SubjectType = "USER_ROLE"
|
||||||
|
UserGroupSub SubjectType = "USER_GROUP"
|
||||||
|
UserInviteSub SubjectType = "USER_INVITE"
|
||||||
|
PendingUserSub SubjectType = "PENDING_USER"
|
||||||
|
EgressSub SubjectType = "EGRESS"
|
||||||
|
NetworkSub SubjectType = "NETWORK"
|
||||||
|
DashboardSub SubjectType = "DASHBOARD"
|
||||||
|
EnrollmentKeySub SubjectType = "ENROLLMENT_KEY"
|
||||||
|
ClientAppSub SubjectType = "CLIENT-APP"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (sub SubjectType) String() string {
|
||||||
|
return string(sub)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Origin string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Dashboard Origin = "DASHBOARD"
|
||||||
|
Api Origin = "API"
|
||||||
|
NMCTL Origin = "NMCTL"
|
||||||
|
ClientApp Origin = "CLIENT-APP"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Subject struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type SubjectType `json:"subject_type"`
|
||||||
|
Info interface{} `json:"info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Diff struct {
|
||||||
|
Old interface{}
|
||||||
|
New interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Event struct {
|
||||||
|
Action Action
|
||||||
|
Source Subject
|
||||||
|
Origin Origin
|
||||||
|
Target Subject
|
||||||
|
TriggeredBy string
|
||||||
|
NetworkID NetworkID
|
||||||
|
Diff Diff
|
||||||
|
}
|
|
@ -176,7 +176,22 @@ func handleAzureCallback(w http.ResponseWriter, r *http.Request) {
|
||||||
logger.Log(1, "could not parse jwt for user", authRequest.UserName)
|
logger.Log(1, "could not parse jwt for user", authRequest.UserName)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Login,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: user.UserName,
|
||||||
|
Name: user.UserName,
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: user.UserName,
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: models.DashboardSub.String(),
|
||||||
|
Name: models.DashboardSub.String(),
|
||||||
|
Type: models.DashboardSub,
|
||||||
|
Info: user,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
logger.Log(1, "completed azure OAuth sigin in for", content.Email)
|
logger.Log(1, "completed azure OAuth sigin in for", content.Email)
|
||||||
http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?login="+jwt+"&user="+content.Email, http.StatusPermanentRedirect)
|
http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?login="+jwt+"&user="+content.Email, http.StatusPermanentRedirect)
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,7 +167,22 @@ func handleGithubCallback(w http.ResponseWriter, r *http.Request) {
|
||||||
logger.Log(1, "could not parse jwt for user", authRequest.UserName)
|
logger.Log(1, "could not parse jwt for user", authRequest.UserName)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Login,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: user.UserName,
|
||||||
|
Name: user.UserName,
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: user.UserName,
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: models.DashboardSub.String(),
|
||||||
|
Name: models.DashboardSub.String(),
|
||||||
|
Type: models.DashboardSub,
|
||||||
|
Info: user,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
logger.Log(1, "completed github OAuth sigin in for", content.Email)
|
logger.Log(1, "completed github OAuth sigin in for", content.Email)
|
||||||
http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?login="+jwt+"&user="+content.Email, http.StatusPermanentRedirect)
|
http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?login="+jwt+"&user="+content.Email, http.StatusPermanentRedirect)
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,7 @@ func handleGoogleCallback(w http.ResponseWriter, r *http.Request) {
|
||||||
handleOauthNotConfigured(w)
|
handleOauthNotConfigured(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var inviteExists bool
|
var inviteExists bool
|
||||||
// check if invite exists for User
|
// check if invite exists for User
|
||||||
in, err := logic.GetUserInvite(content.Email)
|
in, err := logic.GetUserInvite(content.Email)
|
||||||
|
@ -160,6 +161,23 @@ func handleGoogleCallback(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Login,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: user.UserName,
|
||||||
|
Name: user.UserName,
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: user.UserName,
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: models.DashboardSub.String(),
|
||||||
|
Name: models.DashboardSub.String(),
|
||||||
|
Type: models.DashboardSub,
|
||||||
|
Info: user,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
|
|
||||||
logger.Log(1, "completed google OAuth sigin in for", content.Email)
|
logger.Log(1, "completed google OAuth sigin in for", content.Email)
|
||||||
http.Redirect(w, r, fmt.Sprintf("%s/login?login=%s&user=%s", servercfg.GetFrontendURL(), jwt, content.Email), http.StatusPermanentRedirect)
|
http.Redirect(w, r, fmt.Sprintf("%s/login?login=%s&user=%s", servercfg.GetFrontendURL(), jwt, content.Email), http.StatusPermanentRedirect)
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,7 +167,22 @@ func handleOIDCCallback(w http.ResponseWriter, r *http.Request) {
|
||||||
logger.Log(1, "could not parse jwt for user", authRequest.UserName, jwtErr.Error())
|
logger.Log(1, "could not parse jwt for user", authRequest.UserName, jwtErr.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Login,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: user.UserName,
|
||||||
|
Name: user.UserName,
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: user.UserName,
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: models.DashboardSub.String(),
|
||||||
|
Name: models.DashboardSub.String(),
|
||||||
|
Type: models.DashboardSub,
|
||||||
|
Info: user,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
logger.Log(1, "completed OIDC OAuth signin in for", content.Email)
|
logger.Log(1, "completed OIDC OAuth signin in for", content.Email)
|
||||||
http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?login="+jwt+"&user="+content.Email, http.StatusPermanentRedirect)
|
http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?login="+jwt+"&user="+content.Email, http.StatusPermanentRedirect)
|
||||||
}
|
}
|
||||||
|
|
114
pro/controllers/events.go
Normal file
114
pro/controllers/events.go
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/gravitl/netmaker/db"
|
||||||
|
"github.com/gravitl/netmaker/logic"
|
||||||
|
"github.com/gravitl/netmaker/models"
|
||||||
|
"github.com/gravitl/netmaker/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func EventHandlers(r *mux.Router) {
|
||||||
|
r.HandleFunc("/api/v1/network/activity", logic.SecurityCheck(true, http.HandlerFunc(listNetworkActivity))).Methods(http.MethodGet)
|
||||||
|
r.HandleFunc("/api/v1/user/activity", logic.SecurityCheck(true, http.HandlerFunc(listUserActivity))).Methods(http.MethodGet)
|
||||||
|
r.HandleFunc("/api/v1/activity", logic.SecurityCheck(true, http.HandlerFunc(listActivity))).Methods(http.MethodGet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary list activity.
|
||||||
|
// @Router /api/v1/activity [get]
|
||||||
|
// @Tags Activity
|
||||||
|
// @Param network_id query string true "network_id required to get the network events"
|
||||||
|
// @Success 200 {object} models.ReturnSuccessResponseWithJson
|
||||||
|
// @Failure 500 {object} models.ErrorResponse
|
||||||
|
func listNetworkActivity(w http.ResponseWriter, r *http.Request) {
|
||||||
|
netID := r.URL.Query().Get("network_id")
|
||||||
|
// Parse query parameters with defaults
|
||||||
|
if netID == "" {
|
||||||
|
logic.ReturnErrorResponse(w, r, models.ErrorResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
Message: "network_id param is missing",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
|
||||||
|
pageSize, _ := strconv.Atoi(r.URL.Query().Get("per_page"))
|
||||||
|
ctx := db.WithContext(r.Context())
|
||||||
|
netActivity, err := (&schema.Event{NetworkID: models.NetworkID(netID)}).ListByNetwork(db.SetPagination(ctx, page, pageSize))
|
||||||
|
if err != nil {
|
||||||
|
logic.ReturnErrorResponse(w, r, models.ErrorResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logic.ReturnSuccessResponseWithJson(w, r, netActivity, "successfully fetched network activity")
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary list activity.
|
||||||
|
// @Router /api/v1/activity [get]
|
||||||
|
// @Tags Activity
|
||||||
|
// @Param network_id query string true "network_id required to get the network events"
|
||||||
|
// @Success 200 {object} models.ReturnSuccessResponseWithJson
|
||||||
|
// @Failure 500 {object} models.ErrorResponse
|
||||||
|
func listUserActivity(w http.ResponseWriter, r *http.Request) {
|
||||||
|
username := r.URL.Query().Get("username")
|
||||||
|
// Parse query parameters with defaults
|
||||||
|
if username == "" {
|
||||||
|
logic.ReturnErrorResponse(w, r, models.ErrorResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
Message: "username param is missing",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
|
||||||
|
pageSize, _ := strconv.Atoi(r.URL.Query().Get("per_page"))
|
||||||
|
ctx := db.WithContext(r.Context())
|
||||||
|
userActivity, err := (&schema.Event{TriggeredBy: username}).ListByUser(db.SetPagination(ctx, page, pageSize))
|
||||||
|
if err != nil {
|
||||||
|
logic.ReturnErrorResponse(w, r, models.ErrorResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logic.ReturnSuccessResponseWithJson(w, r, userActivity, "successfully fetched user activity "+username)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary list activity.
|
||||||
|
// @Router /api/v1/activity [get]
|
||||||
|
// @Tags Activity
|
||||||
|
// @Success 200 {object} models.ReturnSuccessResponseWithJson
|
||||||
|
// @Failure 500 {object} models.ErrorResponse
|
||||||
|
func listActivity(w http.ResponseWriter, r *http.Request) {
|
||||||
|
username := r.URL.Query().Get("username")
|
||||||
|
network := r.URL.Query().Get("network_id")
|
||||||
|
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
|
||||||
|
pageSize, _ := strconv.Atoi(r.URL.Query().Get("per_page"))
|
||||||
|
ctx := db.WithContext(r.Context())
|
||||||
|
var err error
|
||||||
|
var events []schema.Event
|
||||||
|
e := &schema.Event{TriggeredBy: username, NetworkID: models.NetworkID(network)}
|
||||||
|
if username != "" && network != "" {
|
||||||
|
events, err = e.ListByUserAndNetwork(db.SetPagination(ctx, page, pageSize))
|
||||||
|
} else if username != "" && network == "" {
|
||||||
|
events, err = e.ListByUser(db.SetPagination(ctx, page, pageSize))
|
||||||
|
} else if username == "" && network != "" {
|
||||||
|
events, err = e.ListByNetwork(db.SetPagination(ctx, page, pageSize))
|
||||||
|
} else {
|
||||||
|
events, err = e.List(db.SetPagination(ctx, page, pageSize))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
logic.ReturnErrorResponse(w, r, models.ErrorResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logic.ReturnSuccessResponseWithJson(w, r, events, "successfully fetched all events ")
|
||||||
|
}
|
|
@ -62,7 +62,6 @@ func UserHandlers(r *mux.Router) {
|
||||||
r.HandleFunc("/api/users/{username}/remote_access_gw/{remote_access_gateway_id}", logic.SecurityCheck(true, http.HandlerFunc(removeUserFromRemoteAccessGW))).Methods(http.MethodDelete)
|
r.HandleFunc("/api/users/{username}/remote_access_gw/{remote_access_gateway_id}", logic.SecurityCheck(true, http.HandlerFunc(removeUserFromRemoteAccessGW))).Methods(http.MethodDelete)
|
||||||
r.HandleFunc("/api/users/{username}/remote_access_gw", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUserRemoteAccessGwsV1)))).Methods(http.MethodGet)
|
r.HandleFunc("/api/users/{username}/remote_access_gw", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUserRemoteAccessGwsV1)))).Methods(http.MethodGet)
|
||||||
r.HandleFunc("/api/users/ingress/{ingress_id}", logic.SecurityCheck(true, http.HandlerFunc(ingressGatewayUsers))).Methods(http.MethodGet)
|
r.HandleFunc("/api/users/ingress/{ingress_id}", logic.SecurityCheck(true, http.HandlerFunc(ingressGatewayUsers))).Methods(http.MethodGet)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// swagger:route POST /api/v1/users/invite-signup user userInviteSignUp
|
// swagger:route POST /api/v1/users/invite-signup user userInviteSignUp
|
||||||
|
@ -248,6 +247,21 @@ func inviteUsers(w http.ResponseWriter, r *http.Request) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("failed to insert invite for user", "email", invite.Email, "error", err)
|
slog.Error("failed to insert invite for user", "email", invite.Email, "error", err)
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Create,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: callerUserName,
|
||||||
|
Name: callerUserName,
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: callerUserName,
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: inviteeEmail,
|
||||||
|
Name: inviteeEmail,
|
||||||
|
Type: models.UserInviteSub,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
// notify user with magic link
|
// notify user with magic link
|
||||||
go func(invite models.UserInvite) {
|
go func(invite models.UserInvite) {
|
||||||
// Set E-Mail body. You can set plain text or html with text/html
|
// Set E-Mail body. You can set plain text or html with text/html
|
||||||
|
@ -266,6 +280,7 @@ func inviteUsers(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}(invite)
|
}(invite)
|
||||||
}
|
}
|
||||||
|
|
||||||
logic.ReturnSuccessResponse(w, r, "triggered user invites")
|
logic.ReturnSuccessResponse(w, r, "triggered user invites")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,6 +324,21 @@ func deleteUserInvite(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Delete,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: email,
|
||||||
|
Name: email,
|
||||||
|
Type: models.UserInviteSub,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
logic.ReturnSuccessResponse(w, r, "deleted user invite")
|
logic.ReturnSuccessResponse(w, r, "deleted user invite")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,6 +493,21 @@ func createUserGroup(w http.ResponseWriter, r *http.Request) {
|
||||||
user.UserGroups[userGroupReq.Group.ID] = struct{}{}
|
user.UserGroups[userGroupReq.Group.ID] = struct{}{}
|
||||||
logic.UpsertUser(*user)
|
logic.UpsertUser(*user)
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Create,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: userGroupReq.Group.ID.String(),
|
||||||
|
Name: userGroupReq.Group.Name,
|
||||||
|
Type: models.UserGroupSub,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
logic.ReturnSuccessResponseWithJson(w, r, userGroupReq.Group, "created user group")
|
logic.ReturnSuccessResponseWithJson(w, r, userGroupReq.Group, "created user group")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -506,7 +551,25 @@ func updateUserGroup(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Update,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: userGroup.ID.String(),
|
||||||
|
Name: userGroup.Name,
|
||||||
|
Type: models.UserGroupSub,
|
||||||
|
},
|
||||||
|
Diff: models.Diff{
|
||||||
|
Old: currUserG,
|
||||||
|
New: userGroup,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
// reset configs for service user
|
// reset configs for service user
|
||||||
go proLogic.UpdatesUserGwAccessOnGrpUpdates(currUserG.NetworkRoles, userGroup.NetworkRoles)
|
go proLogic.UpdatesUserGwAccessOnGrpUpdates(currUserG.NetworkRoles, userGroup.NetworkRoles)
|
||||||
logic.ReturnSuccessResponseWithJson(w, r, userGroup, "updated user group")
|
logic.ReturnSuccessResponseWithJson(w, r, userGroup, "updated user group")
|
||||||
|
@ -551,6 +614,21 @@ func deleteUserGroup(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Delete,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: userG.ID.String(),
|
||||||
|
Name: userG.Name,
|
||||||
|
Type: models.UserGroupSub,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
go proLogic.UpdatesUserGwAccessOnGrpUpdates(userG.NetworkRoles, make(map[models.NetworkID]map[models.UserRoleID]struct{}))
|
go proLogic.UpdatesUserGwAccessOnGrpUpdates(userG.NetworkRoles, make(map[models.NetworkID]map[models.UserRoleID]struct{}))
|
||||||
logic.ReturnSuccessResponseWithJson(w, r, nil, "deleted user group")
|
logic.ReturnSuccessResponseWithJson(w, r, nil, "deleted user group")
|
||||||
}
|
}
|
||||||
|
@ -631,6 +709,21 @@ func createRole(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Create,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: userRole.ID.String(),
|
||||||
|
Name: userRole.Name,
|
||||||
|
Type: models.UserRoleSub,
|
||||||
|
},
|
||||||
|
Origin: models.ClientApp,
|
||||||
|
})
|
||||||
logic.ReturnSuccessResponseWithJson(w, r, userRole, "created user role")
|
logic.ReturnSuccessResponseWithJson(w, r, userRole, "created user role")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -665,6 +758,25 @@ func updateRole(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Update,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: userRole.ID.String(),
|
||||||
|
Name: userRole.Name,
|
||||||
|
Type: models.UserRoleSub,
|
||||||
|
},
|
||||||
|
Diff: models.Diff{
|
||||||
|
Old: currRole,
|
||||||
|
New: userRole,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
// reset configs for service user
|
// reset configs for service user
|
||||||
go proLogic.UpdatesUserGwAccessOnRoleUpdates(currRole.NetworkLevelAccess, userRole.NetworkLevelAccess, string(userRole.NetworkID))
|
go proLogic.UpdatesUserGwAccessOnRoleUpdates(currRole.NetworkLevelAccess, userRole.NetworkLevelAccess, string(userRole.NetworkID))
|
||||||
logic.ReturnSuccessResponseWithJson(w, r, userRole, "updated user role")
|
logic.ReturnSuccessResponseWithJson(w, r, userRole, "updated user role")
|
||||||
|
@ -693,6 +805,21 @@ func deleteRole(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Delete,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: role.ID.String(),
|
||||||
|
Name: role.Name,
|
||||||
|
Type: models.UserRoleSub,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
go proLogic.UpdatesUserGwAccessOnRoleUpdates(role.NetworkLevelAccess, make(map[models.RsrcType]map[models.RsrcID]models.RsrcPermissionScope), role.NetworkID.String())
|
go proLogic.UpdatesUserGwAccessOnRoleUpdates(role.NetworkLevelAccess, make(map[models.RsrcType]map[models.RsrcID]models.RsrcPermissionScope), role.NetworkID.String())
|
||||||
logic.ReturnSuccessResponseWithJson(w, r, nil, "deleted user role")
|
logic.ReturnSuccessResponseWithJson(w, r, nil, "deleted user role")
|
||||||
}
|
}
|
||||||
|
@ -1349,6 +1476,21 @@ func approvePendingUser(w http.ResponseWriter, r *http.Request) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Create,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: username,
|
||||||
|
Name: username,
|
||||||
|
Type: models.PendingUserSub,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
logic.ReturnSuccessResponse(w, r, "approved "+username)
|
logic.ReturnSuccessResponse(w, r, "approved "+username)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1380,6 +1522,21 @@ func deletePendingUser(w http.ResponseWriter, r *http.Request) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.Delete,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: username,
|
||||||
|
Name: username,
|
||||||
|
Type: models.PendingUserSub,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
logic.ReturnSuccessResponse(w, r, "deleted pending "+username)
|
logic.ReturnSuccessResponse(w, r, "deleted pending "+username)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1395,5 +1552,20 @@ func deleteAllPendingUsers(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to delete all pending users "+err.Error()), "internal"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to delete all pending users "+err.Error()), "internal"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logic.LogEvent(&models.Event{
|
||||||
|
Action: models.DeleteAll,
|
||||||
|
Source: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.UserSub,
|
||||||
|
},
|
||||||
|
TriggeredBy: r.Header.Get("user"),
|
||||||
|
Target: models.Subject{
|
||||||
|
ID: r.Header.Get("user"),
|
||||||
|
Name: r.Header.Get("user"),
|
||||||
|
Type: models.PendingUserSub,
|
||||||
|
},
|
||||||
|
Origin: models.Dashboard,
|
||||||
|
})
|
||||||
logic.ReturnSuccessResponse(w, r, "cleared all pending users")
|
logic.ReturnSuccessResponse(w, r, "cleared all pending users")
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ func InitPro() {
|
||||||
proControllers.FailOverHandlers,
|
proControllers.FailOverHandlers,
|
||||||
proControllers.InetHandlers,
|
proControllers.InetHandlers,
|
||||||
proControllers.RacHandlers,
|
proControllers.RacHandlers,
|
||||||
|
proControllers.EventHandlers,
|
||||||
)
|
)
|
||||||
controller.ListRoles = proControllers.ListRoles
|
controller.ListRoles = proControllers.ListRoles
|
||||||
logic.EnterpriseCheckFuncs = append(logic.EnterpriseCheckFuncs, func() {
|
logic.EnterpriseCheckFuncs = append(logic.EnterpriseCheckFuncs, func() {
|
||||||
|
@ -93,6 +94,7 @@ func InitPro() {
|
||||||
proLogic.LoadNodeMetricsToCache()
|
proLogic.LoadNodeMetricsToCache()
|
||||||
proLogic.InitFailOverCache()
|
proLogic.InitFailOverCache()
|
||||||
email.Init()
|
email.Init()
|
||||||
|
proLogic.EventWatcher()
|
||||||
})
|
})
|
||||||
logic.ResetFailOver = proLogic.ResetFailOver
|
logic.ResetFailOver = proLogic.ResetFailOver
|
||||||
logic.ResetFailedOverPeer = proLogic.ResetFailedOverPeer
|
logic.ResetFailedOverPeer = proLogic.ResetFailedOverPeer
|
||||||
|
@ -140,6 +142,7 @@ func InitPro() {
|
||||||
logic.GetNodeStatus = proLogic.GetNodeStatus
|
logic.GetNodeStatus = proLogic.GetNodeStatus
|
||||||
logic.InitializeAuthProvider = auth.InitializeAuthProvider
|
logic.InitializeAuthProvider = auth.InitializeAuthProvider
|
||||||
logic.EmailInit = email.Init
|
logic.EmailInit = email.Init
|
||||||
|
logic.LogEvent = proLogic.LogEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
func retrieveProLogo() string {
|
func retrieveProLogo() string {
|
||||||
|
|
47
pro/logic/events.go
Normal file
47
pro/logic/events.go
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package logic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/gravitl/netmaker/db"
|
||||||
|
"github.com/gravitl/netmaker/models"
|
||||||
|
"github.com/gravitl/netmaker/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
var EventActivityCh = make(chan models.Event, 100)
|
||||||
|
|
||||||
|
func LogEvent(a *models.Event) {
|
||||||
|
EventActivityCh <- *a
|
||||||
|
}
|
||||||
|
|
||||||
|
func EventWatcher() {
|
||||||
|
|
||||||
|
for e := range EventActivityCh {
|
||||||
|
if e.Action == models.Update {
|
||||||
|
// check if diff
|
||||||
|
if cmp.Equal(e.Diff.Old, e.Diff.New) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sourceJson, _ := json.Marshal(e.Source)
|
||||||
|
dstJson, _ := json.Marshal(e.Target)
|
||||||
|
diff, _ := json.Marshal(e.Diff)
|
||||||
|
a := schema.Event{
|
||||||
|
ID: uuid.New().String(),
|
||||||
|
Action: e.Action,
|
||||||
|
Source: sourceJson,
|
||||||
|
Target: dstJson,
|
||||||
|
Origin: e.Origin,
|
||||||
|
NetworkID: e.NetworkID,
|
||||||
|
TriggeredBy: e.TriggeredBy,
|
||||||
|
Diff: diff,
|
||||||
|
TimeStamp: time.Now().UTC(),
|
||||||
|
}
|
||||||
|
a.Create(db.WithContext(context.TODO()))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
55
schema/event.go
Normal file
55
schema/event.go
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gravitl/netmaker/db"
|
||||||
|
"github.com/gravitl/netmaker/models"
|
||||||
|
"gorm.io/datatypes"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Event struct {
|
||||||
|
ID string `gorm:"primaryKey" json:"id"`
|
||||||
|
Action models.Action `gorm:"action" json:"action"`
|
||||||
|
Source datatypes.JSON `gorm:"source" json:"source"`
|
||||||
|
Origin models.Origin `gorm:"origin" json:"origin"`
|
||||||
|
Target datatypes.JSON `gorm:"target" json:"target"`
|
||||||
|
NetworkID models.NetworkID `gorm:"network_id" json:"network_id"`
|
||||||
|
TriggeredBy string `gorm:"triggered_by" json:"triggered_by"`
|
||||||
|
Diff datatypes.JSON `gorm:"diff" json:"diff"`
|
||||||
|
TimeStamp time.Time `gorm:"time_stamp" json:"time_stamp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Event) Get(ctx context.Context) error {
|
||||||
|
return db.FromContext(ctx).Model(&Event{}).First(&a).Where("id = ?", a.ID).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Event) Update(ctx context.Context) error {
|
||||||
|
return db.FromContext(ctx).Model(&Event{}).Where("id = ?", a.ID).Updates(&a).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Event) Create(ctx context.Context) error {
|
||||||
|
return db.FromContext(ctx).Model(&Event{}).Create(&a).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Event) ListByNetwork(ctx context.Context) (ats []Event, err error) {
|
||||||
|
err = db.FromContext(ctx).Model(&Event{}).Where("network_id = ?", a.NetworkID).Order("time_stamp DESC").Find(&ats).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Event) ListByUser(ctx context.Context) (ats []Event, err error) {
|
||||||
|
err = db.FromContext(ctx).Model(&Event{}).Where("triggered_by = ?", a.TriggeredBy).Order("time_stamp DESC").Find(&ats).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Event) ListByUserAndNetwork(ctx context.Context) (ats []Event, err error) {
|
||||||
|
err = db.FromContext(ctx).Model(&Event{}).Where("network_id = ? AND triggered_by = ?",
|
||||||
|
a.NetworkID, a.TriggeredBy).Order("time_stamp DESC").Find(&ats).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Event) List(ctx context.Context) (ats []Event, err error) {
|
||||||
|
err = db.FromContext(ctx).Model(&Event{}).Order("time_stamp DESC").Find(&ats).Error
|
||||||
|
return
|
||||||
|
}
|
|
@ -6,5 +6,6 @@ func ListModels() []interface{} {
|
||||||
&Job{},
|
&Job{},
|
||||||
&Egress{},
|
&Egress{},
|
||||||
&UserAccessToken{},
|
&UserAccessToken{},
|
||||||
|
&Event{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue