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"))
|
||||
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)
|
||||
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"))
|
||||
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)
|
||||
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"))
|
||||
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)
|
||||
logic.ReturnSuccessResponse(w, r, "deleted acl "+acl.Name)
|
||||
}
|
||||
|
|
|
@ -85,6 +85,22 @@ func createEgress(w http.ResponseWriter, r *http.Request) {
|
|||
)
|
||||
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 {
|
||||
// node, err := logic.GetNodeByID(nodeID)
|
||||
// if err != nil {
|
||||
|
@ -174,6 +190,25 @@ func updateEgress(w http.ResponseWriter, r *http.Request) {
|
|||
if req.Status != e.Status {
|
||||
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.Tags = make(datatypes.JSONMap)
|
||||
for nodeID, metric := range req.Nodes {
|
||||
|
@ -211,6 +246,8 @@ func updateEgress(w http.ResponseWriter, r *http.Request) {
|
|||
e.Status = req.Status
|
||||
e.UpdateEgressStatus(db.WithContext(context.TODO()))
|
||||
}
|
||||
event.Diff.New = e
|
||||
logic.LogEvent(event)
|
||||
go mq.PublishPeerUpdate(false)
|
||||
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"))
|
||||
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
|
||||
acls := logic.ListAcls()
|
||||
for _, acl := range acls {
|
||||
|
|
|
@ -72,12 +72,32 @@ func getEnrollmentKeys(w http.ResponseWriter, r *http.Request) {
|
|||
func deleteEnrollmentKey(w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
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 {
|
||||
logger.Log(0, r.Header.Get("user"), "failed to remove enrollment key: ", err.Error())
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||
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)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
@ -173,6 +193,21 @@ func createEnrollmentKey(w http.ResponseWriter, r *http.Request) {
|
|||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||
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")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(newEnrollmentKey)
|
||||
|
@ -208,6 +243,7 @@ func updateEnrollmentKey(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
}
|
||||
currKey, _ := logic.GetEnrollmentKey(keyId)
|
||||
|
||||
newEnrollmentKey, err := logic.UpdateEnrollmentKey(keyId, relayId, enrollmentKeyBody.Groups)
|
||||
if err != nil {
|
||||
|
@ -221,7 +257,25 @@ func updateEnrollmentKey(w http.ResponseWriter, r *http.Request) {
|
|||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||
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)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(newEnrollmentKey)
|
||||
|
@ -355,6 +409,25 @@ func handleHostRegister(w http.ResponseWriter, r *http.Request) {
|
|||
ServerConf: server,
|
||||
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")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(&response)
|
||||
|
|
|
@ -799,6 +799,27 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
|
|||
"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)
|
||||
go func() {
|
||||
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"))
|
||||
return
|
||||
}
|
||||
host, err := logic.GetHost(node.HostID.String())
|
||||
if err != nil {
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||
return
|
||||
}
|
||||
var req models.CreateGwReq
|
||||
err = json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
|
@ -89,7 +94,21 @@ func createGateway(w http.ResponseWriter, r *http.Request) {
|
|||
)
|
||||
logic.GetNodeStatus(&relayNode, false)
|
||||
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)
|
||||
json.NewEncoder(w).Encode(apiNode)
|
||||
go func() {
|
||||
|
@ -138,6 +157,11 @@ func deleteGateway(w http.ResponseWriter, r *http.Request) {
|
|||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||
return
|
||||
}
|
||||
host, err := logic.GetHost(node.HostID.String())
|
||||
if err != nil {
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||
return
|
||||
}
|
||||
node.IsGw = false
|
||||
logic.UpsertNode(&node)
|
||||
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)
|
||||
apiNode := node.ConvertToAPINode()
|
||||
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()
|
||||
logger.Log(2, r.Header.Get("user"), "updated host", newHost.ID.String())
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
@ -420,7 +438,21 @@ func deleteHost(w http.ResponseWriter, r *http.Request) {
|
|||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||
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()
|
||||
logger.Log(2, r.Header.Get("user"), "removed host", currHost.Name)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
@ -492,6 +524,22 @@ func addHostToNetwork(w http.ResponseWriter, r *http.Request) {
|
|||
r.Header.Get("user"),
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -623,6 +671,22 @@ func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) {
|
|||
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(
|
||||
2,
|
||||
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)
|
||||
}
|
||||
}()
|
||||
|
||||
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())
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
|
|
@ -483,9 +483,9 @@ func deleteNetwork(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
err = logic.DeleteNetwork(network, force, doneCh)
|
||||
if err != nil {
|
||||
errtype := "badrequest"
|
||||
errtype := logic.BadReq
|
||||
if strings.Contains(err.Error(), "Node check failed") {
|
||||
errtype = "forbidden"
|
||||
errtype = logic.Forbidden
|
||||
}
|
||||
logger.Log(0, r.Header.Get("user"),
|
||||
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.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)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
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")
|
||||
}
|
||||
}()
|
||||
|
||||
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)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(network)
|
||||
|
|
|
@ -178,7 +178,7 @@ func Authorize(
|
|||
// check if host instead of user
|
||||
if hostAllowed {
|
||||
// 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)
|
||||
// this indicates request is from a node
|
||||
// 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
|
||||
}
|
||||
}
|
||||
_, err = logic.GetHost(newNode.HostID.String())
|
||||
host, err := logic.GetHost(newNode.HostID.String())
|
||||
if err != nil {
|
||||
logger.Log(0, r.Header.Get("user"),
|
||||
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",
|
||||
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)
|
||||
json.NewEncoder(w).Encode(apiNode)
|
||||
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"))
|
||||
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")
|
||||
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.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)
|
||||
|
||||
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"))
|
||||
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)
|
||||
var newID models.TagID
|
||||
if updateTag.NewName != "" {
|
||||
|
@ -198,7 +233,8 @@ func updateTag(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
mq.PublishPeerUpdate(false)
|
||||
}()
|
||||
|
||||
e.Diff.New = updateTag
|
||||
logic.LogEvent(e)
|
||||
var res models.TagListRespNodes = models.TagListRespNodes{
|
||||
Tag: tag,
|
||||
UsedByCnt: len(updateTag.TaggedNodes),
|
||||
|
@ -241,5 +277,21 @@ func deleteTag(w http.ResponseWriter, r *http.Request) {
|
|||
logic.RemoveTagFromEnrollmentKeys(tag.ID)
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -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(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/logout", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(logout)))).Methods(http.MethodPost)
|
||||
}
|
||||
|
||||
// @Summary Authenticate a user to retrieve an authorization token
|
||||
|
@ -64,25 +65,25 @@ func createUserAccessToken(w http.ResponseWriter, r *http.Request) {
|
|||
if err != nil {
|
||||
logger.Log(0, "error decoding request body: ",
|
||||
err.Error())
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, logic.BadReq))
|
||||
return
|
||||
}
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
caller, err := logic.GetUser(r.Header.Get("user"))
|
||||
if err != nil {
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "unauthorized"))
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, logic.UnAuthorized))
|
||||
return
|
||||
}
|
||||
user, err := logic.GetUser(req.UserName)
|
||||
if err != nil {
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "unauthorized"))
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, logic.UnAuthorized))
|
||||
return
|
||||
}
|
||||
if caller.UserName != user.UserName && caller.PlatformRoleID != models.SuperAdminRole {
|
||||
|
@ -106,7 +107,7 @@ func createUserAccessToken(w http.ResponseWriter, r *http.Request) {
|
|||
logic.ReturnErrorResponse(
|
||||
w,
|
||||
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
|
||||
}
|
||||
|
@ -115,10 +116,26 @@ func createUserAccessToken(w http.ResponseWriter, r *http.Request) {
|
|||
logic.ReturnErrorResponse(
|
||||
w,
|
||||
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
|
||||
}
|
||||
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{
|
||||
AuthToken: jwt,
|
||||
UserName: req.UserName,
|
||||
|
@ -197,6 +214,22 @@ func deleteUserAccessTokens(w http.ResponseWriter, r *http.Request) {
|
|||
)
|
||||
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")
|
||||
}
|
||||
|
||||
|
@ -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"))
|
||||
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
|
||||
|
@ -614,6 +679,21 @@ func createUser(w http.ResponseWriter, r *http.Request) {
|
|||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||
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.DeletePendingUser(user.UserName)
|
||||
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) {
|
||||
(&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)
|
||||
if err != nil {
|
||||
logger.Log(0, username,
|
||||
|
@ -759,6 +858,7 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
|
|||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||
return
|
||||
}
|
||||
logic.LogEvent(&e)
|
||||
go mq.PublishPeerUpdate(false)
|
||||
logger.Log(1, username, "was updated")
|
||||
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"))
|
||||
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
|
||||
go func() {
|
||||
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")
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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.
|
||||
// If the context already has a db connection instance,
|
||||
// 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/go-jose/go-jose/v4 v4.0.5 // 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/inconshreveable/mousetrap v1.1.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/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.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/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e h1:XmA6L9IPRdUr28a+SK/oMchGgQy159wvzXA5tJ7l+40=
|
||||
|
|
|
@ -8,20 +8,30 @@ import (
|
|||
"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
|
||||
func FormatError(err error, errType string) models.ErrorResponse {
|
||||
func FormatError(err error, errType ApiErrorType) models.ErrorResponse {
|
||||
|
||||
var status = http.StatusInternalServerError
|
||||
switch errType {
|
||||
case "internal":
|
||||
case Internal:
|
||||
status = http.StatusInternalServerError
|
||||
case "badrequest":
|
||||
case BadReq:
|
||||
status = http.StatusBadRequest
|
||||
case "notfound":
|
||||
case NotFound:
|
||||
status = http.StatusNotFound
|
||||
case "unauthorized":
|
||||
case UnAuthorized:
|
||||
status = http.StatusUnauthorized
|
||||
case "forbidden":
|
||||
case Forbidden:
|
||||
status = http.StatusForbidden
|
||||
default:
|
||||
status = http.StatusInternalServerError
|
||||
|
|
|
@ -125,6 +125,25 @@ func DeleteExtClient(network string, clientid string) error {
|
|||
}
|
||||
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())
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ var (
|
|||
telServerRecord = models.Telemetry{}
|
||||
)
|
||||
|
||||
var LogEvent = func(a *models.Event) {}
|
||||
|
||||
// posthog_pub_key - Key for sending data to PostHog
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
var inviteExists bool
|
||||
// check if invite exists for User
|
||||
in, err := logic.GetUserInvite(content.Email)
|
||||
|
@ -160,6 +161,23 @@ func handleGoogleCallback(w http.ResponseWriter, r *http.Request) {
|
|||
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)
|
||||
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())
|
||||
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)
|
||||
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", 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)
|
||||
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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
|
||||
go func(invite models.UserInvite) {
|
||||
// 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)
|
||||
}
|
||||
|
||||
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"))
|
||||
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")
|
||||
}
|
||||
|
||||
|
@ -463,6 +493,21 @@ func createUserGroup(w http.ResponseWriter, r *http.Request) {
|
|||
user.UserGroups[userGroupReq.Group.ID] = struct{}{}
|
||||
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")
|
||||
}
|
||||
|
||||
|
@ -506,7 +551,25 @@ func updateUserGroup(w http.ResponseWriter, r *http.Request) {
|
|||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||
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
|
||||
go proLogic.UpdatesUserGwAccessOnGrpUpdates(currUserG.NetworkRoles, userGroup.NetworkRoles)
|
||||
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"))
|
||||
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{}))
|
||||
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"))
|
||||
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")
|
||||
}
|
||||
|
||||
|
@ -665,6 +758,25 @@ func updateRole(w http.ResponseWriter, r *http.Request) {
|
|||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||
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
|
||||
go proLogic.UpdatesUserGwAccessOnRoleUpdates(currRole.NetworkLevelAccess, userRole.NetworkLevelAccess, string(userRole.NetworkID))
|
||||
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"))
|
||||
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())
|
||||
logic.ReturnSuccessResponseWithJson(w, r, nil, "deleted user role")
|
||||
}
|
||||
|
@ -1349,6 +1476,21 @@ func approvePendingUser(w http.ResponseWriter, r *http.Request) {
|
|||
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)
|
||||
}
|
||||
|
||||
|
@ -1380,6 +1522,21 @@ func deletePendingUser(w http.ResponseWriter, r *http.Request) {
|
|||
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)
|
||||
}
|
||||
|
||||
|
@ -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"))
|
||||
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")
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ func InitPro() {
|
|||
proControllers.FailOverHandlers,
|
||||
proControllers.InetHandlers,
|
||||
proControllers.RacHandlers,
|
||||
proControllers.EventHandlers,
|
||||
)
|
||||
controller.ListRoles = proControllers.ListRoles
|
||||
logic.EnterpriseCheckFuncs = append(logic.EnterpriseCheckFuncs, func() {
|
||||
|
@ -93,6 +94,7 @@ func InitPro() {
|
|||
proLogic.LoadNodeMetricsToCache()
|
||||
proLogic.InitFailOverCache()
|
||||
email.Init()
|
||||
proLogic.EventWatcher()
|
||||
})
|
||||
logic.ResetFailOver = proLogic.ResetFailOver
|
||||
logic.ResetFailedOverPeer = proLogic.ResetFailedOverPeer
|
||||
|
@ -140,6 +142,7 @@ func InitPro() {
|
|||
logic.GetNodeStatus = proLogic.GetNodeStatus
|
||||
logic.InitializeAuthProvider = auth.InitializeAuthProvider
|
||||
logic.EmailInit = email.Init
|
||||
logic.LogEvent = proLogic.LogEvent
|
||||
}
|
||||
|
||||
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{},
|
||||
&Egress{},
|
||||
&UserAccessToken{},
|
||||
&Event{},
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue