Finalize events API

This commit is contained in:
braginini
2022-12-12 09:10:55 +01:00
parent cc12aff3a4
commit 5cb2dc676b
9 changed files with 217 additions and 197 deletions

View File

@@ -514,7 +514,18 @@ func (am *DefaultAccountManager) newAccount(userID, domain string) (*Account, er
log.Warnf("an account with ID already exists, retrying...")
continue
} else if statusErr.Type() == status.NotFound {
return newAccountWithId(accountId, userID, domain), nil
newAccount := newAccountWithId(accountId, userID, domain)
_, err = am.eventStore.Save(&activity.Event{
Timestamp: time.Now(),
Activity: activity.AccountCreated,
AccountID: newAccount.Id,
TargetID: newAccount.Id,
InitiatorID: userID,
})
if err != nil {
return nil, err
}
return newAccount, nil
} else {
return nil, err
}
@@ -801,16 +812,15 @@ func (am *DefaultAccountManager) handleNewUserAccount(domainAcc *Account, claims
return nil, err
}
opEvent := &activity.Event{
Timestamp: time.Now(),
Type: activity.ManagementEvent,
OperationCode: activity.UserJoinedOperation,
AccountID: account.Id,
TargetID: claims.UserId,
ModifierID: claims.UserId,
event := &activity.Event{
Timestamp: time.Now(),
Activity: activity.UserJoined,
AccountID: account.Id,
TargetID: claims.UserId,
InitiatorID: claims.UserId,
}
_, err = am.eventStore.Save(opEvent)
_, err = am.eventStore.Save(event)
if err != nil {
return nil, err
}

View File

@@ -3,43 +3,69 @@ package activity
import "time"
const (
// DeviceEvent describes an event that happened of a device (e.g, connected/disconnected)
DeviceEvent Type = "device"
// ManagementEvent describes an event that happened on a Management service (e.g., user added)
ManagementEvent Type = "management"
// PeerAddedByUser indicates that a user added a new peer to the system
PeerAddedByUser Activity = iota
// PeerAddedWithSetupKey indicates that a new peer joined the system using a setup key
PeerAddedWithSetupKey
// UserJoined indicates that a new user joined the account
UserJoined
// UserInvited indicates that a new user was invited to join the account
UserInvited
// AccountCreated indicates that a new account has been created
AccountCreated
)
const (
AddPeerByUserOperation Operation = iota
AddPeerWithKeyOperation
UserJoinedOperation
// PeerAddedByUserMessage is a human-readable text message of the PeerAddedByUser activity
PeerAddedByUserMessage string = "New peer added by a user"
// PeerAddedWithSetupKeyMessage is a human-readable text message of the PeerAddedWithSetupKey activity
PeerAddedWithSetupKeyMessage = "New peer added with a setup key"
//UserJoinedMessage is a human-readable text message of the UserJoined activity
UserJoinedMessage string = "New user joined"
//UserInvitedMessage is a human-readable text message of the UserInvited activity
UserInvitedMessage string = "New user invited"
//AccountCreatedMessage is a human-readable text message of the AccountCreated activity
AccountCreatedMessage string = "Account created"
)
const (
AddPeerByUserOperationMessage string = "Add new peer"
AddPeerWithKeyOperationMessage string = AddPeerByUserOperationMessage
UserJoinedOperationMessage string = "New user joined"
)
// Activity that triggered an Event
type Activity int
// MessageForOperation returns a string message for an Operation
func MessageForOperation(op Operation) string {
switch op {
case AddPeerByUserOperation:
return AddPeerByUserOperationMessage
case AddPeerWithKeyOperation:
return AddPeerWithKeyOperationMessage
case UserJoinedOperation:
return UserJoinedOperationMessage
// Message returns a string representation of an activity
func (a Activity) Message() string {
switch a {
case PeerAddedByUser:
return PeerAddedByUserMessage
case PeerAddedWithSetupKey:
return PeerAddedWithSetupKeyMessage
case UserJoined:
return UserJoinedMessage
case UserInvited:
return UserInvitedMessage
case AccountCreated:
return AccountCreatedMessage
default:
return "UNKNOWN_OPERATION"
return "UNKNOWN_ACTIVITY"
}
}
// Type of the Event
type Type string
// Operation is an action that triggered an Event
type Operation int
// StringCode returns a string code of the activity
func (a Activity) StringCode() string {
switch a {
case PeerAddedByUser:
return "user.peer.add"
case PeerAddedWithSetupKey:
return "setupkey.peer.add"
case UserJoined:
return "user.join"
case UserInvited:
return "user.invite"
case AccountCreated:
return "account.create"
default:
return "UNKNOWN_ACTIVITY"
}
}
// Store provides an interface to store or stream events.
type Store interface {
@@ -55,31 +81,26 @@ type Store interface {
type Event struct {
// Timestamp of the event
Timestamp time.Time
// Operation that was performed during the event
Operation string
// OperationCode that was performed during the event
OperationCode Operation
// Activity that was performed during the event
Activity Activity
// ID of the event (can be empty, meaning that it wasn't yet generated)
ID uint64
// Type of the event
Type Type
// ModifierID is the ID of an object that modifies a Target
ModifierID string
// TargetID is the ID of an object that a Modifier modifies
// InitiatorID is the ID of an object that initiated the event (e.g., a user)
InitiatorID string
// TargetID is the ID of an object that was effected by the event (e.g., a peer)
TargetID string
// AccountID where event happened
// AccountID is the ID of an account where the event happened
AccountID string
}
// Copy the event
func (e *Event) Copy() *Event {
return &Event{
Timestamp: e.Timestamp,
Operation: e.Operation,
ID: e.ID,
Type: e.Type,
ModifierID: e.ModifierID,
TargetID: e.TargetID,
AccountID: e.AccountID,
Timestamp: e.Timestamp,
Activity: e.Activity,
ID: e.ID,
InitiatorID: e.InitiatorID,
TargetID: e.TargetID,
AccountID: e.AccountID,
}
}

View File

@@ -11,12 +11,12 @@ import (
const (
SQLiteEventSinkDB = "events.db"
createTableQuery = "CREATE TABLE IF NOT EXISTS events " +
"(id INTEGER PRIMARY KEY AUTOINCREMENT, account TEXT NOT NULL, " +
"operation INTEGER, " +
"type TEXT, " +
"(id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"activity INTEGER, " +
"timestamp DATETIME, " +
"modifier TEXT," +
" target TEXT);"
"initiator_id TEXT," +
"account_id TEXT," +
" target_id TEXT);"
)
// SQLiteStore is the implementation of the activity.Store interface backed by SQLite
@@ -44,26 +44,23 @@ func processResult(result *sql.Rows) ([]*Event, error) {
events := make([]*Event, 0)
for result.Next() {
var id int64
var operation Operation
var operation Activity
var timestamp time.Time
var modifier string
var initiator string
var target string
var account string
var typ Type
err := result.Scan(&id, &operation, &timestamp, &modifier, &target, &account, &typ)
err := result.Scan(&id, &operation, &timestamp, &initiator, &target, &account)
if err != nil {
return nil, err
}
events = append(events, &Event{
Timestamp: timestamp,
OperationCode: operation,
Operation: MessageForOperation(operation),
ID: uint64(id),
Type: typ,
ModifierID: modifier,
TargetID: target,
AccountID: account,
Timestamp: timestamp,
Activity: operation,
ID: uint64(id),
InitiatorID: initiator,
TargetID: target,
AccountID: account,
})
}
@@ -76,8 +73,8 @@ func (store *SQLiteStore) Get(accountID string, offset, limit int, descending bo
if !descending {
order = "ASC"
}
stmt, err := store.db.Prepare(fmt.Sprintf("SELECT id, operation, timestamp, modifier, target, account, type"+
" FROM events WHERE account = ? ORDER BY timestamp %s LIMIT ? OFFSET ?;", order))
stmt, err := store.db.Prepare(fmt.Sprintf("SELECT id, activity, timestamp, initiator_id, target_id, account_id"+
" FROM events WHERE account_id = ? ORDER BY timestamp %s LIMIT ? OFFSET ?;", order))
if err != nil {
return nil, err
}
@@ -94,12 +91,12 @@ func (store *SQLiteStore) Get(accountID string, offset, limit int, descending bo
// Save an event in the SQLite events table
func (store *SQLiteStore) Save(event *Event) (*Event, error) {
stmt, err := store.db.Prepare("INSERT INTO events(operation, timestamp, modifier, target, account, type) VALUES(?, ?, ?, ?, ?, ?)")
stmt, err := store.db.Prepare("INSERT INTO events(activity, timestamp, initiator_id, target_id, account_id) VALUES(?, ?, ?, ?, ?)")
if err != nil {
return nil, err
}
result, err := stmt.Exec(event.OperationCode, event.Timestamp, event.ModifierID, event.TargetID, event.AccountID, event.Type)
result, err := stmt.Exec(event.Activity, event.Timestamp, event.InitiatorID, event.TargetID, event.AccountID)
if err != nil {
return nil, err
}

View File

@@ -19,12 +19,11 @@ func TestNewSQLiteStore(t *testing.T) {
for i := 0; i < 10; i++ {
_, err = store.Save(&Event{
Timestamp: time.Now(),
OperationCode: AddPeerByUserOperation,
Type: ManagementEvent,
ModifierID: "user_" + fmt.Sprint(i),
TargetID: "peer_" + fmt.Sprint(i),
AccountID: accountID,
Timestamp: time.Now(),
Activity: PeerAddedByUser,
InitiatorID: "user_" + fmt.Sprint(i),
TargetID: "peer_" + fmt.Sprint(i),
AccountID: accountID,
})
if err != nil {
t.Fatal(err)

View File

@@ -47,12 +47,12 @@ components:
items:
type: string
required:
- id
- email
- name
- role
- auto_groups
- status
- id
- email
- name
- role
- auto_groups
- status
UserRequest:
type: object
properties:
@@ -98,8 +98,8 @@ components:
description: Peer's hostname
type: string
required:
- id
- name
- id
- name
Peer:
allOf:
- $ref: '#/components/schemas/PeerMinimum'
@@ -142,15 +142,15 @@ components:
description: Peer's DNS label is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's domain to the peer label. e.g. peer-dns-label.netbird.cloud
type: string
required:
- ip
- connected
- last_seen
- os
- version
- groups
- ssh_enabled
- hostname
- dns_label
- ip
- connected
- last_seen
- os
- version
- groups
- ssh_enabled
- hostname
- dns_label
SetupKey:
type: object
properties:
@@ -199,19 +199,19 @@ components:
description: A number of times this key can be used. The value of 0 indicates the unlimited usage.
type: integer
required:
- id
- key
- name
- expires
- type
- valid
- revoked
- used_times
- last_used
- state
- auto_groups
- updated_at
- usage_limit
- id
- key
- name
- expires
- type
- valid
- revoked
- used_times
- last_used
- state
- auto_groups
- updated_at
- usage_limit
SetupKeyRequest:
type: object
properties:
@@ -255,9 +255,9 @@ components:
description: Count of peers associated to the group
type: integer
required:
- id
- name
- peers_count
- id
- name
- peers_count
Group:
allOf:
- $ref: '#/components/schemas/GroupMinimum'
@@ -269,7 +269,7 @@ components:
items:
$ref: '#/components/schemas/PeerMinimum'
required:
- peers
- peers
PatchMinimum:
type: object
properties:
@@ -313,10 +313,10 @@ components:
description: Rule flow, currently, only "bidirect" for bi-directional traffic is accepted
type: string
required:
- name
- description
- disabled
- flow
- name
- description
- disabled
- flow
Rule:
allOf:
- type: object
@@ -325,7 +325,7 @@ components:
description: Rule ID
type: string
required:
- id
- id
- $ref: '#/components/schemas/RuleMinimum'
- type: object
properties:
@@ -340,8 +340,8 @@ components:
items:
$ref: '#/components/schemas/GroupMinimum'
required:
- sources
- destinations
- sources
- destinations
RulePatchOperation:
allOf:
- $ref: '#/components/schemas/PatchMinimum'
@@ -430,7 +430,7 @@ components:
ns_type:
description: Nameserver Type
type: string
enum: ["udp"]
enum: [ "udp" ]
port:
description: Nameserver Port
type: integer
@@ -500,7 +500,7 @@ components:
path:
description: Nameserver group field to update in form /<field>
type: string
enum: [ "name", "description", "enabled", "groups", "nameservers", "primary", "domains" ]
enum: [ "name", "description", "enabled", "groups", "nameservers", "primary", "domains" ]
required:
- path
Event:
@@ -513,16 +513,13 @@ components:
description: The date and time when the event occurred
type: string
format: date-time
operation:
description: The operation (or action) that occurred during the event
activity:
description: The activity that occurred during the event
type: string
operation_code:
description: The numeric code of the operation (or action) that occurred during the event
type: integer
type:
description: The type of the event that occurred. Indicates whether it was a management or device event
activity_code:
description: The string code of the activity that occurred during the event
type: string
enum: ["device", "management"]
enum: [ "account.create", "user.join", "user.invite", "user.peer.add", "setupkey.peer.add" ]
initiator_id:
description: The ID of the initiator of the event. E.g., an ID of a user that triggered the event.
type: string
@@ -532,33 +529,32 @@ components:
required:
- id
- timestamp
- operation
- operation_code
- type
- activity
- activity_code
- initiator_id
- target_id
responses:
not_found:
description: Resource not found
content: {}
content: { }
validation_failed_simple:
description: Validation failed
content: {}
content: { }
bad_request:
description: Bad Request
content: {}
content: { }
internal_error:
description: Internal Server Error
content: { }
validation_failed:
description: Validation failed
content: {}
content: { }
forbidden:
description: Forbidden
content: {}
content: { }
requires_authentication:
description: Requires authentication
content: {}
content: { }
securitySchemes:
BearerAuth:
type: http
@@ -570,9 +566,9 @@ paths:
/api/users:
get:
summary: Returns a list of all users
tags: [Users]
tags: [ Users ]
security:
- BearerAuth: []
- BearerAuth: [ ]
responses:
'200':
description: A JSON array of Users
@@ -593,7 +589,7 @@ paths:
/api/users/:
post:
summary: Create a User (invite)
tags: [ Users]
tags: [ Users ]
security:
- BearerAuth: [ ]
requestBody:
@@ -620,7 +616,7 @@ paths:
/api/users/{id}:
put:
summary: Update information about a User
tags: [ Users]
tags: [ Users ]
security:
- BearerAuth: [ ]
parameters:
@@ -654,9 +650,9 @@ paths:
/api/peers:
get:
summary: Returns a list of all peers
tags: [Peers]
tags: [ Peers ]
security:
- BearerAuth: []
- BearerAuth: [ ]
responses:
'200':
description: A JSON Array of Peers
@@ -677,7 +673,7 @@ paths:
/api/peers/{id}:
get:
summary: Get information about a peer
tags: [Peers]
tags: [ Peers ]
security:
- BearerAuth: [ ]
parameters:
@@ -704,7 +700,7 @@ paths:
"$ref": "#/components/responses/internal_error"
put:
summary: Update information about a peer
tags: [Peers]
tags: [ Peers ]
security:
- BearerAuth: [ ]
parameters:
@@ -745,7 +741,7 @@ paths:
"$ref": "#/components/responses/internal_error"
delete:
summary: Delete a peer
tags: [Peers]
tags: [ Peers ]
security:
- BearerAuth: [ ]
parameters:
@@ -758,7 +754,7 @@ paths:
responses:
'200':
description: Delete status code
content: {}
content: { }
'400':
"$ref": "#/components/responses/bad_request"
'401':
@@ -770,7 +766,7 @@ paths:
/api/setup-keys:
get:
summary: Returns a list of all Setup Keys
tags: [Setup Keys]
tags: [ Setup Keys ]
security:
- BearerAuth: [ ]
responses:
@@ -792,7 +788,7 @@ paths:
"$ref": "#/components/responses/internal_error"
post:
summary: Creates a Setup Key
tags: [Setup Keys]
tags: [ Setup Keys ]
security:
- BearerAuth: [ ]
requestBody:
@@ -819,7 +815,7 @@ paths:
/api/setup-keys/{id}:
get:
summary: Get information about a Setup Key
tags: [Setup Keys]
tags: [ Setup Keys ]
security:
- BearerAuth: [ ]
parameters:
@@ -846,7 +842,7 @@ paths:
"$ref": "#/components/responses/internal_error"
put:
summary: Update information about a Setup Key
tags: [Setup Keys]
tags: [ Setup Keys ]
security:
- BearerAuth: [ ]
parameters:
@@ -879,7 +875,7 @@ paths:
"$ref": "#/components/responses/internal_error"
delete:
summary: Delete a Setup Key
tags: [Setup Keys]
tags: [ Setup Keys ]
security:
- BearerAuth: [ ]
parameters:
@@ -892,7 +888,7 @@ paths:
responses:
'200':
description: Delete status code
content: {}
content: { }
'400':
"$ref": "#/components/responses/bad_request"
'401':
@@ -904,7 +900,7 @@ paths:
/api/groups:
get:
summary: Returns a list of all Groups
tags: [Groups]
tags: [ Groups ]
security:
- BearerAuth: [ ]
responses:
@@ -926,7 +922,7 @@ paths:
"$ref": "#/components/responses/internal_error"
post:
summary: Creates a Group
tags: [Groups]
tags: [ Groups ]
security:
- BearerAuth: [ ]
requestBody:
@@ -962,7 +958,7 @@ paths:
/api/groups/{id}:
get:
summary: Get information about a Group
tags: [Groups]
tags: [ Groups ]
security:
- BearerAuth: [ ]
parameters:
@@ -989,7 +985,7 @@ paths:
"$ref": "#/components/responses/internal_error"
put:
summary: Update/Replace a Group
tags: [Groups]
tags: [ Groups ]
security:
- BearerAuth: [ ]
parameters:
@@ -1064,7 +1060,7 @@ paths:
"$ref": "#/components/responses/internal_error"
delete:
summary: Delete a Group
tags: [Groups]
tags: [ Groups ]
security:
- BearerAuth: [ ]
parameters:
@@ -1077,7 +1073,7 @@ paths:
responses:
'200':
description: Delete status code
content: {}
content: { }
'400':
"$ref": "#/components/responses/bad_request"
'401':
@@ -1089,7 +1085,7 @@ paths:
/api/rules:
get:
summary: Returns a list of all Rules
tags: [Rules]
tags: [ Rules ]
security:
- BearerAuth: [ ]
responses:
@@ -1111,7 +1107,7 @@ paths:
"$ref": "#/components/responses/internal_error"
post:
summary: Creates a Rule
tags: [Rules]
tags: [ Rules ]
security:
- BearerAuth: [ ]
requestBody:
@@ -1141,7 +1137,7 @@ paths:
/api/rules/{id}:
get:
summary: Get information about a Rules
tags: [Rules]
tags: [ Rules ]
security:
- BearerAuth: [ ]
parameters:
@@ -1168,7 +1164,7 @@ paths:
"$ref": "#/components/responses/internal_error"
put:
summary: Update/Replace a Rule
tags: [Rules]
tags: [ Rules ]
security:
- BearerAuth: [ ]
parameters:
@@ -1247,7 +1243,7 @@ paths:
"$ref": "#/components/responses/internal_error"
delete:
summary: Delete a Rule
tags: [Rules]
tags: [ Rules ]
security:
- BearerAuth: [ ]
parameters:
@@ -1260,7 +1256,7 @@ paths:
responses:
'200':
description: Delete status code
content: {}
content: { }
'400':
"$ref": "#/components/responses/bad_request"
'401':

View File

@@ -11,10 +11,13 @@ const (
BearerAuthScopes = "BearerAuth.Scopes"
)
// Defines values for EventType.
// Defines values for EventActivityCode.
const (
EventTypeDevice EventType = "device"
EventTypeManagement EventType = "management"
EventActivityCodeAccountCreate EventActivityCode = "account.create"
EventActivityCodeSetupkeyPeerAdd EventActivityCode = "setupkey.peer.add"
EventActivityCodeUserInvite EventActivityCode = "user.invite"
EventActivityCodeUserJoin EventActivityCode = "user.join"
EventActivityCodeUserPeerAdd EventActivityCode = "user.peer.add"
)
// Defines values for GroupPatchOperationOp.
@@ -105,30 +108,27 @@ const (
// Event defines model for Event.
type Event struct {
// Activity The activity that occurred during the event
Activity string `json:"activity"`
// ActivityCode The string code of the activity that occurred during the event
ActivityCode EventActivityCode `json:"activity_code"`
// Id Event unique identifier
Id string `json:"id"`
// InitiatorId The ID of the initiator of the event. E.g., an ID of a user that triggered the event.
InitiatorId string `json:"initiator_id"`
// Operation The operation (or action) that occurred during the event
Operation string `json:"operation"`
// OperationCode The numeric code of the operation (or action) that occurred during the event
OperationCode int `json:"operation_code"`
// TargetId The ID of the target of the event. E.g., an ID of the peer that a user removed.
TargetId string `json:"target_id"`
// Timestamp The date and time when the event occurred
Timestamp time.Time `json:"timestamp"`
// Type The type of the event that occurred. Indicates whether it was a management or device event
Type EventType `json:"type"`
}
// EventType The type of the event that occurred. Indicates whether it was a management or device event
type EventType string
// EventActivityCode The string code of the activity that occurred during the event
type EventActivityCode string
// Group defines model for Group.
type Group struct {

View File

@@ -42,7 +42,7 @@ func (h *Events) GetEvents(w http.ResponseWriter, r *http.Request) {
util.WriteError(err, w)
return
}
var events []*api.Event
events := make([]*api.Event, 0)
for _, e := range accountEvents {
events = append(events, toEventResponse(e))
}
@@ -53,12 +53,11 @@ func (h *Events) GetEvents(w http.ResponseWriter, r *http.Request) {
func toEventResponse(event *activity.Event) *api.Event {
return &api.Event{
Id: fmt.Sprint(event.ID),
InitiatorId: event.ModifierID,
Operation: event.Operation,
OperationCode: int(event.OperationCode),
TargetId: event.TargetID,
Timestamp: event.Timestamp,
Type: api.EventType(event.Type),
Id: fmt.Sprint(event.ID),
InitiatorId: event.InitiatorID,
Activity: event.Activity.Message(),
ActivityCode: api.EventActivityCode(event.Activity.StringCode()),
TargetId: event.TargetID,
Timestamp: event.Timestamp,
}
}

View File

@@ -363,7 +363,6 @@ func (am *DefaultAccountManager) AddPeer(setupKey, userID string, peer *Peer) (*
opEvent := &activity.Event{
Timestamp: time.Now(),
Type: activity.ManagementEvent,
AccountID: account.Id,
}
@@ -379,11 +378,11 @@ func (am *DefaultAccountManager) AddPeer(setupKey, userID string, peer *Peer) (*
}
account.SetupKeys[sk.Key] = sk.IncrementUsage()
opEvent.ModifierID = sk.Id
opEvent.OperationCode = activity.AddPeerWithKeyOperation
opEvent.InitiatorID = sk.Id
opEvent.Activity = activity.PeerAddedWithSetupKey
} else {
opEvent.ModifierID = userID
opEvent.OperationCode = activity.AddPeerByUserOperation
opEvent.InitiatorID = userID
opEvent.Activity = activity.PeerAddedByUser
}
takenIps := account.getTakenIPs()

View File

@@ -3,10 +3,9 @@ package server
import (
"fmt"
"github.com/netbirdio/netbird/management/server/idp"
"github.com/netbirdio/netbird/management/server/jwtclaims"
"github.com/netbirdio/netbird/management/server/status"
"strings"
"github.com/netbirdio/netbird/management/server/jwtclaims"
)
const (