mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-19 00:36:38 +00:00
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:
@@ -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"
|
||||
@@ -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
|
||||
|
||||
69
management/server/http/events.go
Normal file
69
management/server/http/events.go
Normal 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,
|
||||
}
|
||||
}
|
||||
250
management/server/http/events_test.go
Normal file
250
management/server/http/events_test.go
Normal 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))
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user