Add system activity tracking and event store (#636)

This PR adds system activity tracking. 
The management service records events like 
add/remove peer,  group, rule, route, etc.

The activity events are stored in the SQLite event store
and can be queried by the HTTP API.
This commit is contained in:
Misha Bragin
2023-01-02 15:11:32 +01:00
committed by GitHub
parent 50caacff69
commit 5c0b8a46f0
42 changed files with 1827 additions and 227 deletions

View File

@@ -18,6 +18,8 @@ tags:
description: Interact with and view information about routes.
- name: DNS
description: Interact with and view information about DNS configuration.
- name: Events
description: View information about the account and network events.
components:
schemas:
User:
@@ -45,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:
@@ -96,8 +98,8 @@ components:
description: Peer's hostname
type: string
required:
- id
- name
- id
- name
Peer:
allOf:
- $ref: '#/components/schemas/PeerMinimum'
@@ -140,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:
@@ -197,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:
@@ -253,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'
@@ -267,7 +269,7 @@ components:
items:
$ref: '#/components/schemas/PeerMinimum'
required:
- peers
- peers
PatchMinimum:
type: object
properties:
@@ -311,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
@@ -323,7 +325,7 @@ components:
description: Rule ID
type: string
required:
- id
- id
- $ref: '#/components/schemas/RuleMinimum'
- type: object
properties:
@@ -338,8 +340,8 @@ components:
items:
$ref: '#/components/schemas/GroupMinimum'
required:
- sources
- destinations
- sources
- destinations
RulePatchOperation:
allOf:
- $ref: '#/components/schemas/PatchMinimum'
@@ -428,7 +430,7 @@ components:
ns_type:
description: Nameserver Type
type: string
enum: ["udp"]
enum: [ "udp" ]
port:
description: Nameserver Port
type: integer
@@ -498,32 +500,74 @@ 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:
type: object
properties:
id:
description: Event unique identifier
type: string
timestamp:
description: The date and time when the event occurred
type: string
format: date-time
activity:
description: The activity that occurred during the event
type: string
activity_code:
description: The string code of the activity that occurred during the event
type: string
enum: [ "user.peer.delete", "user.join", "user.invite", "user.peer.add", "user.group.add", "user.group.delete",
"user.role.update",
"setupkey.peer.add", "setupkey.add", "setupkey.update", "setupkey.revoke", "setupkey.overuse",
"setupkey.group.delete", "setupkey.group.add"
"rule.add", "rule.delete", "rule.update",
"group.add", "group.update",
"account.create",
]
initiator_id:
description: The ID of the initiator of the event. E.g., an ID of a user that triggered the event.
type: string
target_id:
description: The ID of the target of the event. E.g., an ID of the peer that a user removed.
type: string
meta:
description: The metadata of the event
type: object
additionalProperties:
type: string
required:
- id
- timestamp
- activity
- activity_code
- initiator_id
- target_id
- meta
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
@@ -535,9 +579,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
@@ -558,7 +602,7 @@ paths:
/api/users/:
post:
summary: Create a User (invite)
tags: [ Users]
tags: [ Users ]
security:
- BearerAuth: [ ]
requestBody:
@@ -585,7 +629,7 @@ paths:
/api/users/{id}:
put:
summary: Update information about a User
tags: [ Users]
tags: [ Users ]
security:
- BearerAuth: [ ]
parameters:
@@ -619,9 +663,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
@@ -642,7 +686,7 @@ paths:
/api/peers/{id}:
get:
summary: Get information about a peer
tags: [Peers]
tags: [ Peers ]
security:
- BearerAuth: [ ]
parameters:
@@ -669,7 +713,7 @@ paths:
"$ref": "#/components/responses/internal_error"
put:
summary: Update information about a peer
tags: [Peers]
tags: [ Peers ]
security:
- BearerAuth: [ ]
parameters:
@@ -710,7 +754,7 @@ paths:
"$ref": "#/components/responses/internal_error"
delete:
summary: Delete a peer
tags: [Peers]
tags: [ Peers ]
security:
- BearerAuth: [ ]
parameters:
@@ -723,7 +767,7 @@ paths:
responses:
'200':
description: Delete status code
content: {}
content: { }
'400':
"$ref": "#/components/responses/bad_request"
'401':
@@ -735,7 +779,7 @@ paths:
/api/setup-keys:
get:
summary: Returns a list of all Setup Keys
tags: [Setup Keys]
tags: [ Setup Keys ]
security:
- BearerAuth: [ ]
responses:
@@ -757,7 +801,7 @@ paths:
"$ref": "#/components/responses/internal_error"
post:
summary: Creates a Setup Key
tags: [Setup Keys]
tags: [ Setup Keys ]
security:
- BearerAuth: [ ]
requestBody:
@@ -784,7 +828,7 @@ paths:
/api/setup-keys/{id}:
get:
summary: Get information about a Setup Key
tags: [Setup Keys]
tags: [ Setup Keys ]
security:
- BearerAuth: [ ]
parameters:
@@ -811,7 +855,7 @@ paths:
"$ref": "#/components/responses/internal_error"
put:
summary: Update information about a Setup Key
tags: [Setup Keys]
tags: [ Setup Keys ]
security:
- BearerAuth: [ ]
parameters:
@@ -844,7 +888,7 @@ paths:
"$ref": "#/components/responses/internal_error"
delete:
summary: Delete a Setup Key
tags: [Setup Keys]
tags: [ Setup Keys ]
security:
- BearerAuth: [ ]
parameters:
@@ -857,7 +901,7 @@ paths:
responses:
'200':
description: Delete status code
content: {}
content: { }
'400':
"$ref": "#/components/responses/bad_request"
'401':
@@ -869,7 +913,7 @@ paths:
/api/groups:
get:
summary: Returns a list of all Groups
tags: [Groups]
tags: [ Groups ]
security:
- BearerAuth: [ ]
responses:
@@ -891,7 +935,7 @@ paths:
"$ref": "#/components/responses/internal_error"
post:
summary: Creates a Group
tags: [Groups]
tags: [ Groups ]
security:
- BearerAuth: [ ]
requestBody:
@@ -927,7 +971,7 @@ paths:
/api/groups/{id}:
get:
summary: Get information about a Group
tags: [Groups]
tags: [ Groups ]
security:
- BearerAuth: [ ]
parameters:
@@ -954,7 +998,7 @@ paths:
"$ref": "#/components/responses/internal_error"
put:
summary: Update/Replace a Group
tags: [Groups]
tags: [ Groups ]
security:
- BearerAuth: [ ]
parameters:
@@ -1029,7 +1073,7 @@ paths:
"$ref": "#/components/responses/internal_error"
delete:
summary: Delete a Group
tags: [Groups]
tags: [ Groups ]
security:
- BearerAuth: [ ]
parameters:
@@ -1042,7 +1086,7 @@ paths:
responses:
'200':
description: Delete status code
content: {}
content: { }
'400':
"$ref": "#/components/responses/bad_request"
'401':
@@ -1054,7 +1098,7 @@ paths:
/api/rules:
get:
summary: Returns a list of all Rules
tags: [Rules]
tags: [ Rules ]
security:
- BearerAuth: [ ]
responses:
@@ -1076,7 +1120,7 @@ paths:
"$ref": "#/components/responses/internal_error"
post:
summary: Creates a Rule
tags: [Rules]
tags: [ Rules ]
security:
- BearerAuth: [ ]
requestBody:
@@ -1106,7 +1150,7 @@ paths:
/api/rules/{id}:
get:
summary: Get information about a Rules
tags: [Rules]
tags: [ Rules ]
security:
- BearerAuth: [ ]
parameters:
@@ -1133,7 +1177,7 @@ paths:
"$ref": "#/components/responses/internal_error"
put:
summary: Update/Replace a Rule
tags: [Rules]
tags: [ Rules ]
security:
- BearerAuth: [ ]
parameters:
@@ -1212,7 +1256,7 @@ paths:
"$ref": "#/components/responses/internal_error"
delete:
summary: Delete a Rule
tags: [Rules]
tags: [ Rules ]
security:
- BearerAuth: [ ]
parameters:
@@ -1225,7 +1269,7 @@ paths:
responses:
'200':
description: Delete status code
content: {}
content: { }
'400':
"$ref": "#/components/responses/bad_request"
'401':
@@ -1573,5 +1617,28 @@ paths:
"$ref": "#/components/responses/requires_authentication"
'403':
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
/api/events:
get:
summary: Returns a list of all events
tags: [ Events ]
security:
- BearerAuth: [ ]
responses:
'200':
description: A JSON Array of Events
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Event'
'400':
"$ref": "#/components/responses/bad_request"
'401':
"$ref": "#/components/responses/requires_authentication"
'403':
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"

View File

@@ -11,6 +11,28 @@ const (
BearerAuthScopes = "BearerAuth.Scopes"
)
// Defines values for EventActivityCode.
const (
EventActivityCodeAccountCreate EventActivityCode = "account.create"
EventActivityCodeGroupAdd EventActivityCode = "group.add"
EventActivityCodeGroupUpdate EventActivityCode = "group.update"
EventActivityCodeRuleAdd EventActivityCode = "rule.add"
EventActivityCodeRuleDelete EventActivityCode = "rule.delete"
EventActivityCodeRuleUpdate EventActivityCode = "rule.update"
EventActivityCodeSetupkeyAdd EventActivityCode = "setupkey.add"
EventActivityCodeSetupkeyOveruse EventActivityCode = "setupkey.overuse"
EventActivityCodeSetupkeyPeerAdd EventActivityCode = "setupkey.peer.add"
EventActivityCodeSetupkeyRevoke EventActivityCode = "setupkey.revoke"
EventActivityCodeSetupkeyUpdate EventActivityCode = "setupkey.update"
EventActivityCodeUserGroupAdd EventActivityCode = "user.group.add"
EventActivityCodeUserGroupDelete EventActivityCode = "user.group.delete"
EventActivityCodeUserInvite EventActivityCode = "user.invite"
EventActivityCodeUserJoin EventActivityCode = "user.join"
EventActivityCodeUserPeerAdd EventActivityCode = "user.peer.add"
EventActivityCodeUserPeerDelete EventActivityCode = "user.peer.delete"
EventActivityCodeUserRoleUpdate EventActivityCode = "user.role.update"
)
// Defines values for GroupPatchOperationOp.
const (
GroupPatchOperationOpAdd GroupPatchOperationOp = "add"
@@ -97,6 +119,33 @@ const (
UserStatusInvited UserStatus = "invited"
)
// 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"`
// Meta The metadata of the event
Meta map[string]string `json:"meta"`
// 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"`
}
// EventActivityCode The string code of the activity that occurred during the event
type EventActivityCode string
// Group defines model for Group.
type Group struct {
// Id Group ID

View File

@@ -0,0 +1,69 @@
package http
import (
"fmt"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
"github.com/netbirdio/netbird/management/server/jwtclaims"
log "github.com/sirupsen/logrus"
"net/http"
)
// Events HTTP handler
type Events struct {
accountManager server.AccountManager
authAudience string
jwtExtractor jwtclaims.ClaimsExtractor
}
// NewEvents creates a new Events HTTP handler
func NewEvents(accountManager server.AccountManager, authAudience string) *Events {
return &Events{
accountManager: accountManager,
authAudience: authAudience,
jwtExtractor: *jwtclaims.NewClaimsExtractor(nil),
}
}
// GetEvents list of the given account
func (h *Events) GetEvents(w http.ResponseWriter, r *http.Request) {
claims := h.jwtExtractor.ExtractClaimsFromRequestContext(r, h.authAudience)
account, user, err := h.accountManager.GetAccountFromToken(claims)
if err != nil {
log.Error(err)
http.Redirect(w, r, "/", http.StatusInternalServerError)
return
}
accountEvents, err := h.accountManager.GetEvents(account.Id, user.Id)
if err != nil {
util.WriteError(err, w)
return
}
events := make([]*api.Event, 0)
for _, e := range accountEvents {
events = append(events, toEventResponse(e))
}
util.WriteJSONObject(w, events)
}
func toEventResponse(event *activity.Event) *api.Event {
meta := make(map[string]string)
if event.Meta != nil {
for s, a := range event.Meta {
meta[s] = fmt.Sprintf("%v", a)
}
}
return &api.Event{
Id: fmt.Sprint(event.ID),
InitiatorId: event.InitiatorID,
Activity: event.Activity.Message(),
ActivityCode: api.EventActivityCode(event.Activity.StringCode()),
TargetId: event.TargetID,
Timestamp: event.Timestamp,
Meta: meta,
}
}

View File

@@ -0,0 +1,250 @@
package http
import (
"encoding/json"
"github.com/gorilla/mux"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/jwtclaims"
"github.com/netbirdio/netbird/management/server/mock_server"
"github.com/stretchr/testify/assert"
"io"
"net/http"
"net/http/httptest"
"strconv"
"testing"
"time"
)
func initEventsTestData(account string, user *server.User, events ...*activity.Event) *Events {
return &Events{
accountManager: &mock_server.MockAccountManager{
GetEventsFunc: func(accountID, userID string) ([]*activity.Event, error) {
if accountID == account {
return events, nil
}
return []*activity.Event{}, nil
},
GetAccountFromTokenFunc: func(claims jwtclaims.AuthorizationClaims) (*server.Account, *server.User, error) {
return &server.Account{
Id: claims.AccountId,
Domain: "hotmail.com",
Users: map[string]*server.User{
user.Id: user,
},
}, user, nil
},
},
authAudience: "",
jwtExtractor: jwtclaims.ClaimsExtractor{
ExtractClaimsFromRequestContext: func(r *http.Request, authAudiance string) jwtclaims.AuthorizationClaims {
return jwtclaims.AuthorizationClaims{
UserId: "test_user",
Domain: "hotmail.com",
AccountId: "test_account",
}
},
},
}
}
func generateEvents(accountID, userID string) []*activity.Event {
ID := uint64(1)
events := make([]*activity.Event, 0)
events = append(events, &activity.Event{
Timestamp: time.Now(),
Activity: activity.PeerAddedByUser,
ID: ID,
InitiatorID: userID,
TargetID: "100.64.0.2",
AccountID: accountID,
Meta: map[string]any{"some": "meta"},
})
ID++
events = append(events, &activity.Event{
Timestamp: time.Now(),
Activity: activity.UserJoined,
ID: ID,
InitiatorID: userID,
TargetID: "",
AccountID: accountID,
Meta: map[string]any{"some": "meta"},
})
ID++
events = append(events, &activity.Event{
Timestamp: time.Now(),
Activity: activity.GroupCreated,
ID: ID,
InitiatorID: userID,
TargetID: "group-id",
AccountID: accountID,
Meta: map[string]any{"some": "meta"},
})
ID++
events = append(events, &activity.Event{
Timestamp: time.Now(),
Activity: activity.SetupKeyUpdated,
ID: ID,
InitiatorID: userID,
TargetID: "setup-key-id",
AccountID: accountID,
Meta: map[string]any{"some": "meta"},
})
ID++
events = append(events, &activity.Event{
Timestamp: time.Now(),
Activity: activity.SetupKeyUpdated,
ID: ID,
InitiatorID: userID,
TargetID: "setup-key-id",
AccountID: accountID,
Meta: map[string]any{"some": "meta"},
})
ID++
events = append(events, &activity.Event{
Timestamp: time.Now(),
Activity: activity.SetupKeyRevoked,
ID: ID,
InitiatorID: userID,
TargetID: "setup-key-id",
AccountID: accountID,
Meta: map[string]any{"some": "meta"},
})
ID++
events = append(events, &activity.Event{
Timestamp: time.Now(),
Activity: activity.SetupKeyOverused,
ID: ID,
InitiatorID: userID,
TargetID: "setup-key-id",
AccountID: accountID,
Meta: map[string]any{"some": "meta"},
})
ID++
events = append(events, &activity.Event{
Timestamp: time.Now(),
Activity: activity.SetupKeyCreated,
ID: ID,
InitiatorID: userID,
TargetID: "setup-key-id",
AccountID: accountID,
Meta: map[string]any{"some": "meta"},
})
ID++
events = append(events, &activity.Event{
Timestamp: time.Now(),
Activity: activity.RuleAdded,
ID: ID,
InitiatorID: userID,
TargetID: "some-id",
AccountID: accountID,
Meta: map[string]any{"some": "meta"},
})
ID++
events = append(events, &activity.Event{
Timestamp: time.Now(),
Activity: activity.RuleRemoved,
ID: ID,
InitiatorID: userID,
TargetID: "some-id",
AccountID: accountID,
Meta: map[string]any{"some": "meta"},
})
ID++
events = append(events, &activity.Event{
Timestamp: time.Now(),
Activity: activity.RuleUpdated,
ID: ID,
InitiatorID: userID,
TargetID: "some-id",
AccountID: accountID,
Meta: map[string]any{"some": "meta"},
})
ID++
events = append(events, &activity.Event{
Timestamp: time.Now(),
Activity: activity.PeerAddedWithSetupKey,
ID: ID,
InitiatorID: userID,
TargetID: "some-id",
AccountID: accountID,
Meta: map[string]any{"some": "meta"},
})
return events
}
func TestEvents_GetEvents(t *testing.T) {
tt := []struct {
name string
expectedStatus int
expectedBody bool
requestType string
requestPath string
requestBody io.Reader
}{
{
name: "GetEvents OK",
expectedBody: true,
requestType: http.MethodGet,
requestPath: "/api/events/",
expectedStatus: http.StatusOK,
},
}
accountID := "test_account"
adminUser := server.NewAdminUser("test_user")
events := generateEvents(accountID, adminUser.Id)
handler := initEventsTestData(accountID, adminUser, events...)
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
recorder := httptest.NewRecorder()
req := httptest.NewRequest(tc.requestType, tc.requestPath, tc.requestBody)
router := mux.NewRouter()
router.HandleFunc("/api/events/", handler.GetEvents).Methods("GET")
router.ServeHTTP(recorder, req)
res := recorder.Result()
defer res.Body.Close()
if status := recorder.Code; status != tc.expectedStatus {
t.Errorf("handler returned wrong status code: got %v want %v",
status, tc.expectedStatus)
return
}
if !tc.expectedBody {
return
}
content, err := io.ReadAll(res.Body)
if err != nil {
t.Fatalf("I don't know what I expected; %v", err)
}
var got []*api.Event
if err = json.Unmarshal(content, &got); err != nil {
t.Fatalf("Sent content is not in correct json format; %v", err)
}
assert.Len(t, got, len(events))
actual := map[string]*api.Event{}
for _, event := range got {
actual[event.Id] = event
}
for _, expected := range events {
event, ok := actual[strconv.FormatUint(expected.ID, 10)]
assert.True(t, ok)
assert.Equal(t, expected.InitiatorID, event.InitiatorId)
assert.Equal(t, expected.TargetID, event.TargetId)
assert.Equal(t, expected.Activity.Message(), event.Activity)
assert.Equal(t, expected.Activity.StringCode(), string(event.ActivityCode))
assert.Equal(t, expected.Meta["some"], event.Meta["some"])
assert.True(t, expected.Timestamp.Equal(event.Timestamp))
}
})
}
}

View File

@@ -51,7 +51,7 @@ func (h *Groups) GetAllGroupsHandler(w http.ResponseWriter, r *http.Request) {
// UpdateGroupHandler handles update to a group identified by a given ID
func (h *Groups) UpdateGroupHandler(w http.ResponseWriter, r *http.Request) {
claims := h.jwtExtractor.ExtractClaimsFromRequestContext(r, h.authAudience)
account, _, err := h.accountManager.GetAccountFromToken(claims)
account, user, err := h.accountManager.GetAccountFromToken(claims)
if err != nil {
util.WriteError(err, w)
return
@@ -102,7 +102,7 @@ func (h *Groups) UpdateGroupHandler(w http.ResponseWriter, r *http.Request) {
Peers: peerIPsToKeys(account, req.Peers),
}
if err := h.accountManager.SaveGroup(account.Id, &group); err != nil {
if err := h.accountManager.SaveGroup(account.Id, user.Id, &group); err != nil {
log.Errorf("failed updating group %s under account %s %v", groupID, account.Id, err)
util.WriteError(err, w)
return
@@ -219,7 +219,7 @@ func (h *Groups) PatchGroupHandler(w http.ResponseWriter, r *http.Request) {
// CreateGroupHandler handles group creation request
func (h *Groups) CreateGroupHandler(w http.ResponseWriter, r *http.Request) {
claims := h.jwtExtractor.ExtractClaimsFromRequestContext(r, h.authAudience)
account, _, err := h.accountManager.GetAccountFromToken(claims)
account, user, err := h.accountManager.GetAccountFromToken(claims)
if err != nil {
util.WriteError(err, w)
return
@@ -243,7 +243,7 @@ func (h *Groups) CreateGroupHandler(w http.ResponseWriter, r *http.Request) {
Peers: peerIPsToKeys(account, req.Peers),
}
err = h.accountManager.SaveGroup(account.Id, &group)
err = h.accountManager.SaveGroup(account.Id, user.Id, &group)
if err != nil {
util.WriteError(err, w)
return

View File

@@ -29,7 +29,7 @@ var TestPeers = map[string]*server.Peer{
func initGroupTestData(user *server.User, groups ...*server.Group) *Groups {
return &Groups{
accountManager: &mock_server.MockAccountManager{
SaveGroupFunc: func(accountID string, group *server.Group) error {
SaveGroupFunc: func(accountID, userID string, group *server.Group) error {
if !strings.HasPrefix(group.ID, "id-") {
group.ID = "id-was-set"
}

View File

@@ -40,6 +40,7 @@ func APIHandler(accountManager s.AccountManager, authIssuer string, authAudience
userHandler := NewUserHandler(accountManager, authAudience)
routesHandler := NewRoutes(accountManager, authAudience)
nameserversHandler := NewNameservers(accountManager, authAudience)
eventsHandler := NewEvents(accountManager, authAudience)
apiHandler.HandleFunc("/peers", peersHandler.GetPeers).Methods("GET", "OPTIONS")
apiHandler.HandleFunc("/peers/{id}", peersHandler.HandlePeer).
@@ -81,6 +82,8 @@ func APIHandler(accountManager s.AccountManager, authIssuer string, authAudience
apiHandler.HandleFunc("/dns/nameservers/{id}", nameserversHandler.GetNameserverGroupHandler).Methods("GET", "OPTIONS")
apiHandler.HandleFunc("/dns/nameservers/{id}", nameserversHandler.DeleteNameserverGroupHandler).Methods("DELETE", "OPTIONS")
apiHandler.HandleFunc("/events", eventsHandler.GetEvents).Methods("GET", "OPTIONS")
err = apiHandler.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
methods, err := route.GetMethods()
if err != nil {

View File

@@ -45,8 +45,8 @@ func (h *Peers) updatePeer(account *server.Account, peer *server.Peer, w http.Re
util.WriteJSONObject(w, toPeerResponse(peer, account, dnsDomain))
}
func (h *Peers) deletePeer(accountId string, peer *server.Peer, w http.ResponseWriter, r *http.Request) {
_, err := h.accountManager.DeletePeer(accountId, peer.Key)
func (h *Peers) deletePeer(accountID, userID string, peer *server.Peer, w http.ResponseWriter, r *http.Request) {
_, err := h.accountManager.DeletePeer(accountID, peer.Key, userID)
if err != nil {
util.WriteError(err, w)
return
@@ -56,7 +56,7 @@ func (h *Peers) deletePeer(accountId string, peer *server.Peer, w http.ResponseW
func (h *Peers) HandlePeer(w http.ResponseWriter, r *http.Request) {
claims := h.jwtExtractor.ExtractClaimsFromRequestContext(r, h.authAudience)
account, _, err := h.accountManager.GetAccountFromToken(claims)
account, user, err := h.accountManager.GetAccountFromToken(claims)
if err != nil {
util.WriteError(err, w)
return
@@ -78,7 +78,7 @@ func (h *Peers) HandlePeer(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodDelete:
h.deletePeer(account.Id, peer, w, r)
h.deletePeer(account.Id, user.Id, peer, w, r)
return
case http.MethodPut:
h.updatePeer(account, peer, w, r)
@@ -143,9 +143,10 @@ func toPeerResponse(peer *server.Peer, account *server.Account, dnsDomain string
}
}
}
fqdn := peer.DNSLabel
if dnsDomain != "" {
fqdn = peer.DNSLabel + "." + dnsDomain
fqdn := peer.FQDN(dnsDomain)
if fqdn == "" {
fqdn = peer.DNSLabel
}
return &api.Peer{
Id: peer.IP.String(),

View File

@@ -52,7 +52,7 @@ func (h *Rules) GetAllRulesHandler(w http.ResponseWriter, r *http.Request) {
// UpdateRuleHandler handles update to a rule identified by a given ID
func (h *Rules) UpdateRuleHandler(w http.ResponseWriter, r *http.Request) {
claims := h.jwtExtractor.ExtractClaimsFromRequestContext(r, h.authAudience)
account, _, err := h.accountManager.GetAccountFromToken(claims)
account, user, err := h.accountManager.GetAccountFromToken(claims)
if err != nil {
util.WriteError(err, w)
return
@@ -109,7 +109,7 @@ func (h *Rules) UpdateRuleHandler(w http.ResponseWriter, r *http.Request) {
return
}
err = h.accountManager.SaveRule(account.Id, &rule)
err = h.accountManager.SaveRule(account.Id, user.Id, &rule)
if err != nil {
util.WriteError(err, w)
return
@@ -267,7 +267,7 @@ func (h *Rules) PatchRuleHandler(w http.ResponseWriter, r *http.Request) {
// CreateRuleHandler handles rule creation request
func (h *Rules) CreateRuleHandler(w http.ResponseWriter, r *http.Request) {
claims := h.jwtExtractor.ExtractClaimsFromRequestContext(r, h.authAudience)
account, _, err := h.accountManager.GetAccountFromToken(claims)
account, user, err := h.accountManager.GetAccountFromToken(claims)
if err != nil {
util.WriteError(err, w)
return
@@ -312,7 +312,7 @@ func (h *Rules) CreateRuleHandler(w http.ResponseWriter, r *http.Request) {
return
}
err = h.accountManager.SaveRule(account.Id, &rule)
err = h.accountManager.SaveRule(account.Id, user.Id, &rule)
if err != nil {
util.WriteError(err, w)
return
@@ -326,7 +326,7 @@ func (h *Rules) CreateRuleHandler(w http.ResponseWriter, r *http.Request) {
// DeleteRuleHandler handles rule deletion request
func (h *Rules) DeleteRuleHandler(w http.ResponseWriter, r *http.Request) {
claims := h.jwtExtractor.ExtractClaimsFromRequestContext(r, h.authAudience)
account, _, err := h.accountManager.GetAccountFromToken(claims)
account, user, err := h.accountManager.GetAccountFromToken(claims)
if err != nil {
util.WriteError(err, w)
return
@@ -339,7 +339,7 @@ func (h *Rules) DeleteRuleHandler(w http.ResponseWriter, r *http.Request) {
return
}
err = h.accountManager.DeleteRule(aID, rID)
err = h.accountManager.DeleteRule(aID, rID, user.Id)
if err != nil {
util.WriteError(err, w)
return

View File

@@ -22,7 +22,7 @@ import (
func initRulesTestData(rules ...*server.Rule) *Rules {
return &Rules{
accountManager: &mock_server.MockAccountManager{
SaveRuleFunc: func(_ string, rule *server.Rule) error {
SaveRuleFunc: func(_, _ string, rule *server.Rule) error {
if !strings.HasPrefix(rule.ID, "id-") {
rule.ID = "id-was-set"
}

View File

@@ -30,7 +30,7 @@ func NewSetupKeysHandler(accountManager server.AccountManager, authAudience stri
// CreateSetupKeyHandler is a POST requests that creates a new SetupKey
func (h *SetupKeys) CreateSetupKeyHandler(w http.ResponseWriter, r *http.Request) {
claims := h.jwtExtractor.ExtractClaimsFromRequestContext(r, h.authAudience)
account, _, err := h.accountManager.GetAccountFromToken(claims)
account, user, err := h.accountManager.GetAccountFromToken(claims)
if err != nil {
util.WriteError(err, w)
return
@@ -61,7 +61,7 @@ func (h *SetupKeys) CreateSetupKeyHandler(w http.ResponseWriter, r *http.Request
}
setupKey, err := h.accountManager.CreateSetupKey(account.Id, req.Name, server.SetupKeyType(req.Type), expiresIn,
req.AutoGroups, req.UsageLimit)
req.AutoGroups, req.UsageLimit, user.Id)
if err != nil {
util.WriteError(err, w)
return
@@ -98,7 +98,7 @@ func (h *SetupKeys) GetSetupKeyHandler(w http.ResponseWriter, r *http.Request) {
// UpdateSetupKeyHandler is a PUT request to update server.SetupKey
func (h *SetupKeys) UpdateSetupKeyHandler(w http.ResponseWriter, r *http.Request) {
claims := h.jwtExtractor.ExtractClaimsFromRequestContext(r, h.authAudience)
account, _, err := h.accountManager.GetAccountFromToken(claims)
account, user, err := h.accountManager.GetAccountFromToken(claims)
if err != nil {
util.WriteError(err, w)
return
@@ -134,7 +134,7 @@ func (h *SetupKeys) UpdateSetupKeyHandler(w http.ResponseWriter, r *http.Request
newKey.Name = req.Name
newKey.Id = keyID
newKey, err = h.accountManager.SaveSetupKey(account.Id, newKey)
newKey, err = h.accountManager.SaveSetupKey(account.Id, newKey, user.Id)
if err != nil {
util.WriteError(err, w)
return

View File

@@ -47,7 +47,7 @@ func initSetupKeysTestMetaData(defaultKey *server.SetupKey, newKey *server.Setup
}, user, nil
},
CreateSetupKeyFunc: func(_ string, keyName string, typ server.SetupKeyType, _ time.Duration, _ []string,
_ int) (*server.SetupKey, error) {
_ int, _ string) (*server.SetupKey, error) {
if keyName == newKey.Name || typ != newKey.Type {
return newKey, nil
}
@@ -64,7 +64,7 @@ func initSetupKeysTestMetaData(defaultKey *server.SetupKey, newKey *server.Setup
}
},
SaveSetupKeyFunc: func(accountID string, key *server.SetupKey) (*server.SetupKey, error) {
SaveSetupKeyFunc: func(accountID string, key *server.SetupKey, _ string) (*server.SetupKey, error) {
if key.Id == updatedSetupKey.Id {
return updatedSetupKey, nil
}

View File

@@ -34,7 +34,7 @@ func (h *UserHandler) UpdateUser(w http.ResponseWriter, r *http.Request) {
}
claims := h.jwtExtractor.ExtractClaimsFromRequestContext(r, h.authAudience)
account, _, err := h.accountManager.GetAccountFromToken(claims)
account, user, err := h.accountManager.GetAccountFromToken(claims)
if err != nil {
util.WriteError(err, w)
return
@@ -60,7 +60,7 @@ func (h *UserHandler) UpdateUser(w http.ResponseWriter, r *http.Request) {
return
}
newUser, err := h.accountManager.SaveUser(account.Id, &server.User{
newUser, err := h.accountManager.SaveUser(account.Id, user.Id, &server.User{
Id: userID,
Role: userRole,
AutoGroups: req.AutoGroups,
@@ -81,7 +81,7 @@ func (h *UserHandler) CreateUserHandler(w http.ResponseWriter, r *http.Request)
}
claims := h.jwtExtractor.ExtractClaimsFromRequestContext(r, h.authAudience)
account, _, err := h.accountManager.GetAccountFromToken(claims)
account, user, err := h.accountManager.GetAccountFromToken(claims)
if err != nil {
util.WriteError(err, w)
return
@@ -99,7 +99,7 @@ func (h *UserHandler) CreateUserHandler(w http.ResponseWriter, r *http.Request)
return
}
newUser, err := h.accountManager.CreateUser(account.Id, &server.UserInfo{
newUser, err := h.accountManager.CreateUser(account.Id, user.Id, &server.UserInfo{
Email: req.Email,
Name: *req.Name,
Role: req.Role,