mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-22 21:39:08 +02:00
Merge branch 'main' into peers-get-account-refactoring
# Conflicts: # management/server/account.go # management/server/account_test.go # management/server/ephemeral.go # management/server/ephemeral_test.go # management/server/http/handlers/peers/peers_handler.go # management/server/http/handlers/peers/peers_handler_test.go # management/server/integrated_validator.go # management/server/mock_server/account_mock.go # management/server/peer.go # management/server/store/sql_store.go # management/server/store/sql_store_test.go # management/server/store/store.go # management/server/testdata/store_with_expired_peers.sql # management/server/user.go
This commit is contained in:
@@ -84,6 +84,10 @@ components:
|
||||
items:
|
||||
type: string
|
||||
example: Administrators
|
||||
routing_peer_dns_resolution_enabled:
|
||||
description: Enables or disables DNS resolution on the routing peers
|
||||
type: boolean
|
||||
example: true
|
||||
extra:
|
||||
$ref: '#/components/schemas/AccountExtraSettings'
|
||||
required:
|
||||
@@ -668,6 +672,10 @@ components:
|
||||
description: Count of peers associated to the group
|
||||
type: integer
|
||||
example: 2
|
||||
resources_count:
|
||||
description: Count of resources associated to the group
|
||||
type: integer
|
||||
example: 5
|
||||
issued:
|
||||
description: How the group was issued (api, integration, jwt)
|
||||
type: string
|
||||
@@ -677,6 +685,7 @@ components:
|
||||
- id
|
||||
- name
|
||||
- peers_count
|
||||
- resources_count
|
||||
GroupRequest:
|
||||
type: object
|
||||
properties:
|
||||
@@ -690,6 +699,10 @@ components:
|
||||
items:
|
||||
type: string
|
||||
example: "ch8i4ug6lnn4g9hqv7m1"
|
||||
resources:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Resource'
|
||||
required:
|
||||
- name
|
||||
Group:
|
||||
@@ -702,15 +715,16 @@ components:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/PeerMinimum'
|
||||
resources:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Resource'
|
||||
required:
|
||||
- peers
|
||||
- resources
|
||||
PolicyRuleMinimum:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
description: Policy rule ID
|
||||
type: string
|
||||
example: ch8i4ug6lnn4g9hqv7mg
|
||||
name:
|
||||
description: Policy rule name identifier
|
||||
type: string
|
||||
@@ -776,46 +790,80 @@ components:
|
||||
- $ref: '#/components/schemas/PolicyRuleMinimum'
|
||||
- type: object
|
||||
properties:
|
||||
id:
|
||||
description: Policy rule ID
|
||||
type: string
|
||||
example: ch8i4ug6lnn4g9hqv7mg
|
||||
sources:
|
||||
description: Policy rule source group IDs
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: "ch8i4ug6lnn4g9hqv797"
|
||||
sourceResource:
|
||||
description: Policy rule source resource that the rule is applied to
|
||||
$ref: '#/components/schemas/Resource'
|
||||
destinations:
|
||||
description: Policy rule destination group IDs
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: "ch8i4ug6lnn4g9h7v7m0"
|
||||
required:
|
||||
- sources
|
||||
- destinations
|
||||
PolicyRule:
|
||||
destinationResource:
|
||||
description: Policy rule destination resource that the rule is applied to
|
||||
$ref: '#/components/schemas/Resource'
|
||||
|
||||
PolicyRuleCreate:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/PolicyRuleMinimum'
|
||||
- type: object
|
||||
properties:
|
||||
sources:
|
||||
description: Policy rule source group IDs
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: "ch8i4ug6lnn4g9hqv797"
|
||||
sourceResource:
|
||||
description: Policy rule source resource that the rule is applied to
|
||||
$ref: '#/components/schemas/Resource'
|
||||
destinations:
|
||||
description: Policy rule destination group IDs
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: "ch8i4ug6lnn4g9h7v7m0"
|
||||
destinationResource:
|
||||
description: Policy rule destination resource that the rule is applied to
|
||||
$ref: '#/components/schemas/Resource'
|
||||
PolicyRule:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/PolicyRuleMinimum'
|
||||
- type: object
|
||||
properties:
|
||||
id:
|
||||
description: Policy rule ID
|
||||
type: string
|
||||
example: ch8i4ug6lnn4g9hqv7mg
|
||||
sources:
|
||||
description: Policy rule source group IDs
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/GroupMinimum'
|
||||
sourceResource:
|
||||
description: Policy rule source resource that the rule is applied to
|
||||
$ref: '#/components/schemas/Resource'
|
||||
destinations:
|
||||
description: Policy rule destination group IDs
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/GroupMinimum'
|
||||
required:
|
||||
- sources
|
||||
- destinations
|
||||
destinationResource:
|
||||
description: Policy rule destination resource that the rule is applied to
|
||||
$ref: '#/components/schemas/Resource'
|
||||
PolicyMinimum:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
description: Policy ID
|
||||
type: string
|
||||
example: ch8i4ug6lnn4g9hqv7mg
|
||||
name:
|
||||
description: Policy name identifier
|
||||
type: string
|
||||
@@ -830,7 +878,6 @@ components:
|
||||
example: true
|
||||
required:
|
||||
- name
|
||||
- description
|
||||
- enabled
|
||||
PolicyUpdate:
|
||||
allOf:
|
||||
@@ -850,11 +897,33 @@ components:
|
||||
$ref: '#/components/schemas/PolicyRuleUpdate'
|
||||
required:
|
||||
- rules
|
||||
PolicyCreate:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/PolicyMinimum'
|
||||
- type: object
|
||||
properties:
|
||||
source_posture_checks:
|
||||
description: Posture checks ID's applied to policy source groups
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: "chacdk86lnnboviihd70"
|
||||
rules:
|
||||
description: Policy rule object for policy UI editor
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/PolicyRuleUpdate'
|
||||
required:
|
||||
- rules
|
||||
Policy:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/PolicyMinimum'
|
||||
- type: object
|
||||
properties:
|
||||
id:
|
||||
description: Policy ID
|
||||
type: string
|
||||
example: ch8i4ug6lnn4g9hqv7mg
|
||||
source_posture_checks:
|
||||
description: Posture checks ID's applied to policy source groups
|
||||
type: array
|
||||
@@ -1176,6 +1245,181 @@ components:
|
||||
- id
|
||||
- network_type
|
||||
- $ref: '#/components/schemas/RouteRequest'
|
||||
Resource:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
description: ID of the resource
|
||||
type: string
|
||||
example: chacdk86lnnboviihd7g
|
||||
type:
|
||||
description: Type of the resource
|
||||
$ref: '#/components/schemas/ResourceType'
|
||||
required:
|
||||
- id
|
||||
- type
|
||||
ResourceType:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/NetworkResourceType'
|
||||
- type: string
|
||||
example: host
|
||||
NetworkRequest:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
description: Network name
|
||||
type: string
|
||||
example: Remote Network 1
|
||||
description:
|
||||
description: Network description
|
||||
type: string
|
||||
example: A remote network that needs to be accessed
|
||||
required:
|
||||
- name
|
||||
Network:
|
||||
allOf:
|
||||
- type: object
|
||||
properties:
|
||||
id:
|
||||
description: Network ID
|
||||
type: string
|
||||
example: chacdk86lnnboviihd7g
|
||||
routers:
|
||||
description: List of router IDs associated with the network
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: ch8i4ug6lnn4g9hqv7m0
|
||||
routing_peers_count:
|
||||
description: Count of routing peers associated with the network
|
||||
type: integer
|
||||
example: 2
|
||||
resources:
|
||||
description: List of network resource IDs associated with the network
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: ch8i4ug6lnn4g9hqv7m1
|
||||
policies:
|
||||
description: List of policy IDs associated with the network
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: ch8i4ug6lnn4g9hqv7m2
|
||||
required:
|
||||
- id
|
||||
- routers
|
||||
- resources
|
||||
- routing_peers_count
|
||||
- policies
|
||||
- $ref: '#/components/schemas/NetworkRequest'
|
||||
NetworkResourceMinimum:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
description: Network resource name
|
||||
type: string
|
||||
example: Remote Resource 1
|
||||
description:
|
||||
description: Network resource description
|
||||
type: string
|
||||
example: A remote resource inside network 1
|
||||
address:
|
||||
description: Network resource address (either a direct host like 1.1.1.1 or 1.1.1.1/32, or a subnet like 192.168.178.0/24, or domains like example.com and *.example.com)
|
||||
type: string
|
||||
example: "1.1.1.1"
|
||||
enabled:
|
||||
description: Network resource status
|
||||
type: boolean
|
||||
example: true
|
||||
required:
|
||||
- name
|
||||
- address
|
||||
- enabled
|
||||
NetworkResourceRequest:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/NetworkResourceMinimum'
|
||||
- type: object
|
||||
properties:
|
||||
groups:
|
||||
description: Group IDs containing the resource
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: "chacdk86lnnboviihd70"
|
||||
required:
|
||||
- groups
|
||||
- address
|
||||
NetworkResource:
|
||||
allOf:
|
||||
- type: object
|
||||
properties:
|
||||
id:
|
||||
description: Network Resource ID
|
||||
type: string
|
||||
example: chacdk86lnnboviihd7g
|
||||
type:
|
||||
$ref: '#/components/schemas/NetworkResourceType'
|
||||
groups:
|
||||
description: Groups that the resource belongs to
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/GroupMinimum'
|
||||
required:
|
||||
- id
|
||||
- type
|
||||
- groups
|
||||
- $ref: '#/components/schemas/NetworkResourceMinimum'
|
||||
NetworkResourceType:
|
||||
description: Network resource type based of the address
|
||||
type: string
|
||||
enum: [ "host", "subnet", "domain" ]
|
||||
example: host
|
||||
NetworkRouterRequest:
|
||||
type: object
|
||||
properties:
|
||||
peer:
|
||||
description: Peer Identifier associated with route. This property can not be set together with `peer_groups`
|
||||
type: string
|
||||
example: chacbco6lnnbn6cg5s91
|
||||
peer_groups:
|
||||
description: Peers Group Identifier associated with route. This property can not be set together with `peer`
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: chacbco6lnnbn6cg5s91
|
||||
metric:
|
||||
description: Route metric number. Lowest number has higher priority
|
||||
type: integer
|
||||
maximum: 9999
|
||||
minimum: 1
|
||||
example: 9999
|
||||
masquerade:
|
||||
description: Indicate if peer should masquerade traffic to this route's prefix
|
||||
type: boolean
|
||||
example: true
|
||||
enabled:
|
||||
description: Network router status
|
||||
type: boolean
|
||||
example: true
|
||||
required:
|
||||
# Only one property has to be set
|
||||
#- peer
|
||||
#- peer_groups
|
||||
- metric
|
||||
- masquerade
|
||||
- enabled
|
||||
NetworkRouter:
|
||||
allOf:
|
||||
- type: object
|
||||
properties:
|
||||
id:
|
||||
description: Network Router Id
|
||||
type: string
|
||||
example: chacdk86lnnboviihd7g
|
||||
required:
|
||||
- id
|
||||
- $ref: '#/components/schemas/NetworkRouterRequest'
|
||||
Nameserver:
|
||||
type: object
|
||||
properties:
|
||||
@@ -2274,7 +2518,7 @@ paths:
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
$ref: '#/components/schemas/PolicyUpdate'
|
||||
$ref: '#/components/schemas/PolicyCreate'
|
||||
responses:
|
||||
'200':
|
||||
description: A Policy object
|
||||
@@ -2460,6 +2704,502 @@ paths:
|
||||
"$ref": "#/components/responses/forbidden"
|
||||
'500':
|
||||
"$ref": "#/components/responses/internal_error"
|
||||
/api/networks:
|
||||
get:
|
||||
summary: List all Networks
|
||||
description: Returns a list of all networks
|
||||
tags: [ Networks ]
|
||||
security:
|
||||
- BearerAuth: [ ]
|
||||
- TokenAuth: [ ]
|
||||
responses:
|
||||
'200':
|
||||
description: A JSON Array of Networks
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Network'
|
||||
'400':
|
||||
"$ref": "#/components/responses/bad_request"
|
||||
'401':
|
||||
"$ref": "#/components/responses/requires_authentication"
|
||||
'403':
|
||||
"$ref": "#/components/responses/forbidden"
|
||||
'500':
|
||||
"$ref": "#/components/responses/internal_error"
|
||||
post:
|
||||
summary: Create a Network
|
||||
description: Creates a Network
|
||||
tags: [ Networks ]
|
||||
security:
|
||||
- BearerAuth: [ ]
|
||||
- TokenAuth: [ ]
|
||||
requestBody:
|
||||
description: New Network request
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
$ref: '#/components/schemas/NetworkRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: A Network Object
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Network'
|
||||
'400':
|
||||
"$ref": "#/components/responses/bad_request"
|
||||
'401':
|
||||
"$ref": "#/components/responses/requires_authentication"
|
||||
'403':
|
||||
"$ref": "#/components/responses/forbidden"
|
||||
'500':
|
||||
"$ref": "#/components/responses/internal_error"
|
||||
/api/networks/{networkId}:
|
||||
get:
|
||||
summary: Retrieve a Network
|
||||
description: Get information about a Network
|
||||
tags: [ Networks ]
|
||||
security:
|
||||
- BearerAuth: [ ]
|
||||
- TokenAuth: [ ]
|
||||
parameters:
|
||||
- in: path
|
||||
name: networkId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: The unique identifier of a network
|
||||
responses:
|
||||
'200':
|
||||
description: A Network object
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Network'
|
||||
'400':
|
||||
"$ref": "#/components/responses/bad_request"
|
||||
'401':
|
||||
"$ref": "#/components/responses/requires_authentication"
|
||||
'403':
|
||||
"$ref": "#/components/responses/forbidden"
|
||||
'500':
|
||||
"$ref": "#/components/responses/internal_error"
|
||||
put:
|
||||
summary: Update a Network
|
||||
description: Update/Replace a Network
|
||||
tags: [ Networks ]
|
||||
security:
|
||||
- BearerAuth: [ ]
|
||||
- TokenAuth: [ ]
|
||||
parameters:
|
||||
- in: path
|
||||
name: networkId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: The unique identifier of a network
|
||||
requestBody:
|
||||
description: Update Network request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/NetworkRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: A Network object
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Network'
|
||||
'400':
|
||||
"$ref": "#/components/responses/bad_request"
|
||||
'401':
|
||||
"$ref": "#/components/responses/requires_authentication"
|
||||
'403':
|
||||
"$ref": "#/components/responses/forbidden"
|
||||
'500':
|
||||
"$ref": "#/components/responses/internal_error"
|
||||
delete:
|
||||
summary: Delete a Network
|
||||
description: Delete a network
|
||||
tags: [ Networks ]
|
||||
security:
|
||||
- BearerAuth: [ ]
|
||||
- TokenAuth: [ ]
|
||||
parameters:
|
||||
- in: path
|
||||
name: networkId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: The unique identifier of a network
|
||||
responses:
|
||||
'200':
|
||||
description: Delete status code
|
||||
content: { }
|
||||
'400':
|
||||
"$ref": "#/components/responses/bad_request"
|
||||
'401':
|
||||
"$ref": "#/components/responses/requires_authentication"
|
||||
'403':
|
||||
"$ref": "#/components/responses/forbidden"
|
||||
'500':
|
||||
"$ref": "#/components/responses/internal_error"
|
||||
/api/networks/{networkId}/resources:
|
||||
get:
|
||||
summary: List all Network Resources
|
||||
description: Returns a list of all resources in a network
|
||||
tags: [ Networks ]
|
||||
security:
|
||||
- BearerAuth: [ ]
|
||||
- TokenAuth: [ ]
|
||||
parameters:
|
||||
- in: path
|
||||
name: networkId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: The unique identifier of a network
|
||||
responses:
|
||||
'200':
|
||||
description: A JSON Array of Resources
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/NetworkResource'
|
||||
'400':
|
||||
"$ref": "#/components/responses/bad_request"
|
||||
'401':
|
||||
"$ref": "#/components/responses/requires_authentication"
|
||||
'403':
|
||||
"$ref": "#/components/responses/forbidden"
|
||||
'500':
|
||||
"$ref": "#/components/responses/internal_error"
|
||||
post:
|
||||
summary: Create a Network Resource
|
||||
description: Creates a Network Resource
|
||||
tags: [ Networks ]
|
||||
security:
|
||||
- BearerAuth: [ ]
|
||||
- TokenAuth: [ ]
|
||||
parameters:
|
||||
- in: path
|
||||
name: networkId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: The unique identifier of a network
|
||||
requestBody:
|
||||
description: New Network Resource request
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
$ref: '#/components/schemas/NetworkResourceRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: A Network Resource Object
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/NetworkResource'
|
||||
'400':
|
||||
"$ref": "#/components/responses/bad_request"
|
||||
'401':
|
||||
"$ref": "#/components/responses/requires_authentication"
|
||||
'403':
|
||||
"$ref": "#/components/responses/forbidden"
|
||||
'500':
|
||||
"$ref": "#/components/responses/internal_error"
|
||||
/api/networks/{networkId}/resources/{resourceId}:
|
||||
get:
|
||||
summary: Retrieve a Network Resource
|
||||
description: Get information about a Network Resource
|
||||
tags: [ Networks ]
|
||||
security:
|
||||
- BearerAuth: [ ]
|
||||
- TokenAuth: [ ]
|
||||
parameters:
|
||||
- in: path
|
||||
name: networkId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: The unique identifier of a network
|
||||
- in: path
|
||||
name: resourceId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: The unique identifier of a network resource
|
||||
responses:
|
||||
'200':
|
||||
description: A Network Resource object
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/NetworkResource'
|
||||
'400':
|
||||
"$ref": "#/components/responses/bad_request"
|
||||
'401':
|
||||
"$ref": "#/components/responses/requires_authentication"
|
||||
'403':
|
||||
"$ref": "#/components/responses/forbidden"
|
||||
'500':
|
||||
"$ref": "#/components/responses/internal_error"
|
||||
put:
|
||||
summary: Update a Network Resource
|
||||
description: Update a Network Resource
|
||||
tags: [ Networks ]
|
||||
security:
|
||||
- BearerAuth: [ ]
|
||||
- TokenAuth: [ ]
|
||||
parameters:
|
||||
- in: path
|
||||
name: networkId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: The unique identifier of a network
|
||||
- in: path
|
||||
name: resourceId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: The unique identifier of a resource
|
||||
requestBody:
|
||||
description: Update Network Resource request
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
$ref: '#/components/schemas/NetworkResourceRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: A Network Resource object
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/NetworkResource'
|
||||
'400':
|
||||
"$ref": "#/components/responses/bad_request"
|
||||
'401':
|
||||
"$ref": "#/components/responses/requires_authentication"
|
||||
'403':
|
||||
"$ref": "#/components/responses/forbidden"
|
||||
'500':
|
||||
"$ref": "#/components/responses/internal_error"
|
||||
delete:
|
||||
summary: Delete a Network Resource
|
||||
description: Delete a network resource
|
||||
tags: [ Networks ]
|
||||
security:
|
||||
- BearerAuth: [ ]
|
||||
- TokenAuth: [ ]
|
||||
parameters:
|
||||
- in: path
|
||||
name: networkId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: The unique identifier of a network
|
||||
- in: path
|
||||
name: resourceId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: The unique identifier of a network resource
|
||||
responses:
|
||||
'200':
|
||||
description: Delete status code
|
||||
content: { }
|
||||
'400':
|
||||
"$ref": "#/components/responses/bad_request"
|
||||
'401':
|
||||
"$ref": "#/components/responses/requires_authentication"
|
||||
'403':
|
||||
"$ref": "#/components/responses/forbidden"
|
||||
'500':
|
||||
"$ref": "#/components/responses/internal_error"
|
||||
/api/networks/{networkId}/routers:
|
||||
get:
|
||||
summary: List all Network Routers
|
||||
description: Returns a list of all routers in a network
|
||||
tags: [ Networks ]
|
||||
security:
|
||||
- BearerAuth: [ ]
|
||||
- TokenAuth: [ ]
|
||||
parameters:
|
||||
- in: path
|
||||
name: networkId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: The unique identifier of a network
|
||||
responses:
|
||||
'200':
|
||||
description: A JSON Array of Routers
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/NetworkRouter'
|
||||
'400':
|
||||
"$ref": "#/components/responses/bad_request"
|
||||
'401':
|
||||
"$ref": "#/components/responses/requires_authentication"
|
||||
'403':
|
||||
"$ref": "#/components/responses/forbidden"
|
||||
'500':
|
||||
"$ref": "#/components/responses/internal_error"
|
||||
post:
|
||||
summary: Create a Network Router
|
||||
description: Creates a Network Router
|
||||
tags: [ Networks ]
|
||||
security:
|
||||
- BearerAuth: [ ]
|
||||
- TokenAuth: [ ]
|
||||
parameters:
|
||||
- in: path
|
||||
name: networkId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: The unique identifier of a network
|
||||
requestBody:
|
||||
description: New Network Router request
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
$ref: '#/components/schemas/NetworkRouterRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: A Router Object
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/NetworkRouter'
|
||||
'400':
|
||||
"$ref": "#/components/responses/bad_request"
|
||||
'401':
|
||||
"$ref": "#/components/responses/requires_authentication"
|
||||
'403':
|
||||
"$ref": "#/components/responses/forbidden"
|
||||
'500':
|
||||
"$ref": "#/components/responses/internal_error"
|
||||
/api/networks/{networkId}/routers/{routerId}:
|
||||
get:
|
||||
summary: Retrieve a Network Router
|
||||
description: Get information about a Network Router
|
||||
tags: [ Networks ]
|
||||
security:
|
||||
- BearerAuth: [ ]
|
||||
- TokenAuth: [ ]
|
||||
parameters:
|
||||
- in: path
|
||||
name: networkId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: The unique identifier of a network
|
||||
- in: path
|
||||
name: routerId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: The unique identifier of a router
|
||||
responses:
|
||||
'200':
|
||||
description: A Router object
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/NetworkRouter'
|
||||
'400':
|
||||
"$ref": "#/components/responses/bad_request"
|
||||
'401':
|
||||
"$ref": "#/components/responses/requires_authentication"
|
||||
'403':
|
||||
"$ref": "#/components/responses/forbidden"
|
||||
'500':
|
||||
"$ref": "#/components/responses/internal_error"
|
||||
put:
|
||||
summary: Update a Network Router
|
||||
description: Update a Network Router
|
||||
tags: [ Networks ]
|
||||
security:
|
||||
- BearerAuth: [ ]
|
||||
- TokenAuth: [ ]
|
||||
parameters:
|
||||
- in: path
|
||||
name: networkId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: The unique identifier of a network
|
||||
- in: path
|
||||
name: routerId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: The unique identifier of a router
|
||||
requestBody:
|
||||
description: Update Network Router request
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
$ref: '#/components/schemas/NetworkRouterRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: A Router object
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/NetworkRouter'
|
||||
'400':
|
||||
"$ref": "#/components/responses/bad_request"
|
||||
'401':
|
||||
"$ref": "#/components/responses/requires_authentication"
|
||||
'403':
|
||||
"$ref": "#/components/responses/forbidden"
|
||||
'500':
|
||||
"$ref": "#/components/responses/internal_error"
|
||||
delete:
|
||||
summary: Delete a Network Router
|
||||
description: Delete a network router
|
||||
tags: [ Networks ]
|
||||
security:
|
||||
- BearerAuth: [ ]
|
||||
- TokenAuth: [ ]
|
||||
parameters:
|
||||
- in: path
|
||||
name: networkId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: The unique identifier of a network
|
||||
- in: path
|
||||
name: routerId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: The unique identifier of a router
|
||||
responses:
|
||||
'200':
|
||||
description: Delete status code
|
||||
content: { }
|
||||
'400':
|
||||
"$ref": "#/components/responses/bad_request"
|
||||
'401':
|
||||
"$ref": "#/components/responses/requires_authentication"
|
||||
'403':
|
||||
"$ref": "#/components/responses/forbidden"
|
||||
'500':
|
||||
"$ref": "#/components/responses/internal_error"
|
||||
/api/dns/nameservers:
|
||||
get:
|
||||
summary: List all Nameserver Groups
|
||||
|
||||
@@ -88,6 +88,13 @@ const (
|
||||
NameserverNsTypeUdp NameserverNsType = "udp"
|
||||
)
|
||||
|
||||
// Defines values for NetworkResourceType.
|
||||
const (
|
||||
NetworkResourceTypeDomain NetworkResourceType = "domain"
|
||||
NetworkResourceTypeHost NetworkResourceType = "host"
|
||||
NetworkResourceTypeSubnet NetworkResourceType = "subnet"
|
||||
)
|
||||
|
||||
// Defines values for PeerNetworkRangeCheckAction.
|
||||
const (
|
||||
PeerNetworkRangeCheckActionAllow PeerNetworkRangeCheckAction = "allow"
|
||||
@@ -136,6 +143,13 @@ const (
|
||||
PolicyRuleUpdateProtocolUdp PolicyRuleUpdateProtocol = "udp"
|
||||
)
|
||||
|
||||
// Defines values for ResourceType.
|
||||
const (
|
||||
ResourceTypeDomain ResourceType = "domain"
|
||||
ResourceTypeHost ResourceType = "host"
|
||||
ResourceTypeSubnet ResourceType = "subnet"
|
||||
)
|
||||
|
||||
// Defines values for UserStatus.
|
||||
const (
|
||||
UserStatusActive UserStatus = "active"
|
||||
@@ -234,6 +248,9 @@ type AccountSettings struct {
|
||||
|
||||
// RegularUsersViewBlocked Allows blocking regular users from viewing parts of the system.
|
||||
RegularUsersViewBlocked bool `json:"regular_users_view_blocked"`
|
||||
|
||||
// RoutingPeerDnsResolutionEnabled Enables or disables DNS resolution on the routing peers
|
||||
RoutingPeerDnsResolutionEnabled *bool `json:"routing_peer_dns_resolution_enabled,omitempty"`
|
||||
}
|
||||
|
||||
// Checks List of objects that perform the actual checks
|
||||
@@ -365,7 +382,11 @@ type Group struct {
|
||||
Peers []PeerMinimum `json:"peers"`
|
||||
|
||||
// PeersCount Count of peers associated to the group
|
||||
PeersCount int `json:"peers_count"`
|
||||
PeersCount int `json:"peers_count"`
|
||||
Resources []Resource `json:"resources"`
|
||||
|
||||
// ResourcesCount Count of resources associated to the group
|
||||
ResourcesCount int `json:"resources_count"`
|
||||
}
|
||||
|
||||
// GroupIssued How the group was issued (api, integration, jwt)
|
||||
@@ -384,6 +405,9 @@ type GroupMinimum struct {
|
||||
|
||||
// PeersCount Count of peers associated to the group
|
||||
PeersCount int `json:"peers_count"`
|
||||
|
||||
// ResourcesCount Count of resources associated to the group
|
||||
ResourcesCount int `json:"resources_count"`
|
||||
}
|
||||
|
||||
// GroupMinimumIssued How the group was issued (api, integration, jwt)
|
||||
@@ -395,7 +419,8 @@ type GroupRequest struct {
|
||||
Name string `json:"name"`
|
||||
|
||||
// Peers List of peers ids
|
||||
Peers *[]string `json:"peers,omitempty"`
|
||||
Peers *[]string `json:"peers,omitempty"`
|
||||
Resources *[]Resource `json:"resources,omitempty"`
|
||||
}
|
||||
|
||||
// Location Describe geographical location information
|
||||
@@ -494,6 +519,138 @@ type NameserverGroupRequest struct {
|
||||
SearchDomainsEnabled bool `json:"search_domains_enabled"`
|
||||
}
|
||||
|
||||
// Network defines model for Network.
|
||||
type Network struct {
|
||||
// Description Network description
|
||||
Description *string `json:"description,omitempty"`
|
||||
|
||||
// Id Network ID
|
||||
Id string `json:"id"`
|
||||
|
||||
// Name Network name
|
||||
Name string `json:"name"`
|
||||
|
||||
// Policies List of policy IDs associated with the network
|
||||
Policies []string `json:"policies"`
|
||||
|
||||
// Resources List of network resource IDs associated with the network
|
||||
Resources []string `json:"resources"`
|
||||
|
||||
// Routers List of router IDs associated with the network
|
||||
Routers []string `json:"routers"`
|
||||
|
||||
// RoutingPeersCount Count of routing peers associated with the network
|
||||
RoutingPeersCount int `json:"routing_peers_count"`
|
||||
}
|
||||
|
||||
// NetworkRequest defines model for NetworkRequest.
|
||||
type NetworkRequest struct {
|
||||
// Description Network description
|
||||
Description *string `json:"description,omitempty"`
|
||||
|
||||
// Name Network name
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// NetworkResource defines model for NetworkResource.
|
||||
type NetworkResource struct {
|
||||
// Address Network resource address (either a direct host like 1.1.1.1 or 1.1.1.1/32, or a subnet like 192.168.178.0/24, or domains like example.com and *.example.com)
|
||||
Address string `json:"address"`
|
||||
|
||||
// Description Network resource description
|
||||
Description *string `json:"description,omitempty"`
|
||||
|
||||
// Enabled Network resource status
|
||||
Enabled bool `json:"enabled"`
|
||||
|
||||
// Groups Groups that the resource belongs to
|
||||
Groups []GroupMinimum `json:"groups"`
|
||||
|
||||
// Id Network Resource ID
|
||||
Id string `json:"id"`
|
||||
|
||||
// Name Network resource name
|
||||
Name string `json:"name"`
|
||||
|
||||
// Type Network resource type based of the address
|
||||
Type NetworkResourceType `json:"type"`
|
||||
}
|
||||
|
||||
// NetworkResourceMinimum defines model for NetworkResourceMinimum.
|
||||
type NetworkResourceMinimum struct {
|
||||
// Address Network resource address (either a direct host like 1.1.1.1 or 1.1.1.1/32, or a subnet like 192.168.178.0/24, or domains like example.com and *.example.com)
|
||||
Address string `json:"address"`
|
||||
|
||||
// Description Network resource description
|
||||
Description *string `json:"description,omitempty"`
|
||||
|
||||
// Enabled Network resource status
|
||||
Enabled bool `json:"enabled"`
|
||||
|
||||
// Name Network resource name
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// NetworkResourceRequest defines model for NetworkResourceRequest.
|
||||
type NetworkResourceRequest struct {
|
||||
// Address Network resource address (either a direct host like 1.1.1.1 or 1.1.1.1/32, or a subnet like 192.168.178.0/24, or domains like example.com and *.example.com)
|
||||
Address string `json:"address"`
|
||||
|
||||
// Description Network resource description
|
||||
Description *string `json:"description,omitempty"`
|
||||
|
||||
// Enabled Network resource status
|
||||
Enabled bool `json:"enabled"`
|
||||
|
||||
// Groups Group IDs containing the resource
|
||||
Groups []string `json:"groups"`
|
||||
|
||||
// Name Network resource name
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// NetworkResourceType Network resource type based of the address
|
||||
type NetworkResourceType string
|
||||
|
||||
// NetworkRouter defines model for NetworkRouter.
|
||||
type NetworkRouter struct {
|
||||
// Enabled Network router status
|
||||
Enabled bool `json:"enabled"`
|
||||
|
||||
// Id Network Router Id
|
||||
Id string `json:"id"`
|
||||
|
||||
// Masquerade Indicate if peer should masquerade traffic to this route's prefix
|
||||
Masquerade bool `json:"masquerade"`
|
||||
|
||||
// Metric Route metric number. Lowest number has higher priority
|
||||
Metric int `json:"metric"`
|
||||
|
||||
// Peer Peer Identifier associated with route. This property can not be set together with `peer_groups`
|
||||
Peer *string `json:"peer,omitempty"`
|
||||
|
||||
// PeerGroups Peers Group Identifier associated with route. This property can not be set together with `peer`
|
||||
PeerGroups *[]string `json:"peer_groups,omitempty"`
|
||||
}
|
||||
|
||||
// NetworkRouterRequest defines model for NetworkRouterRequest.
|
||||
type NetworkRouterRequest struct {
|
||||
// Enabled Network router status
|
||||
Enabled bool `json:"enabled"`
|
||||
|
||||
// Masquerade Indicate if peer should masquerade traffic to this route's prefix
|
||||
Masquerade bool `json:"masquerade"`
|
||||
|
||||
// Metric Route metric number. Lowest number has higher priority
|
||||
Metric int `json:"metric"`
|
||||
|
||||
// Peer Peer Identifier associated with route. This property can not be set together with `peer_groups`
|
||||
Peer *string `json:"peer,omitempty"`
|
||||
|
||||
// PeerGroups Peers Group Identifier associated with route. This property can not be set together with `peer`
|
||||
PeerGroups *[]string `json:"peer_groups,omitempty"`
|
||||
}
|
||||
|
||||
// OSVersionCheck Posture check for the version of operating system
|
||||
type OSVersionCheck struct {
|
||||
// Android Posture check for the version of operating system
|
||||
@@ -737,7 +894,7 @@ type PersonalAccessTokenRequest struct {
|
||||
// Policy defines model for Policy.
|
||||
type Policy struct {
|
||||
// Description Policy friendly description
|
||||
Description string `json:"description"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
|
||||
// Enabled Policy status
|
||||
Enabled bool `json:"enabled"`
|
||||
@@ -755,16 +912,31 @@ type Policy struct {
|
||||
SourcePostureChecks []string `json:"source_posture_checks"`
|
||||
}
|
||||
|
||||
// PolicyMinimum defines model for PolicyMinimum.
|
||||
type PolicyMinimum struct {
|
||||
// PolicyCreate defines model for PolicyCreate.
|
||||
type PolicyCreate struct {
|
||||
// Description Policy friendly description
|
||||
Description string `json:"description"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
|
||||
// Enabled Policy status
|
||||
Enabled bool `json:"enabled"`
|
||||
|
||||
// Id Policy ID
|
||||
Id *string `json:"id,omitempty"`
|
||||
// Name Policy name identifier
|
||||
Name string `json:"name"`
|
||||
|
||||
// Rules Policy rule object for policy UI editor
|
||||
Rules []PolicyRuleUpdate `json:"rules"`
|
||||
|
||||
// SourcePostureChecks Posture checks ID's applied to policy source groups
|
||||
SourcePostureChecks *[]string `json:"source_posture_checks,omitempty"`
|
||||
}
|
||||
|
||||
// PolicyMinimum defines model for PolicyMinimum.
|
||||
type PolicyMinimum struct {
|
||||
// Description Policy friendly description
|
||||
Description *string `json:"description,omitempty"`
|
||||
|
||||
// Enabled Policy status
|
||||
Enabled bool `json:"enabled"`
|
||||
|
||||
// Name Policy name identifier
|
||||
Name string `json:"name"`
|
||||
@@ -779,10 +951,11 @@ type PolicyRule struct {
|
||||
Bidirectional bool `json:"bidirectional"`
|
||||
|
||||
// Description Policy rule friendly description
|
||||
Description *string `json:"description,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
DestinationResource *Resource `json:"destinationResource,omitempty"`
|
||||
|
||||
// Destinations Policy rule destination group IDs
|
||||
Destinations []GroupMinimum `json:"destinations"`
|
||||
Destinations *[]GroupMinimum `json:"destinations,omitempty"`
|
||||
|
||||
// Enabled Policy rule status
|
||||
Enabled bool `json:"enabled"`
|
||||
@@ -800,10 +973,11 @@ type PolicyRule struct {
|
||||
Ports *[]string `json:"ports,omitempty"`
|
||||
|
||||
// Protocol Policy rule type of the traffic
|
||||
Protocol PolicyRuleProtocol `json:"protocol"`
|
||||
Protocol PolicyRuleProtocol `json:"protocol"`
|
||||
SourceResource *Resource `json:"sourceResource,omitempty"`
|
||||
|
||||
// Sources Policy rule source group IDs
|
||||
Sources []GroupMinimum `json:"sources"`
|
||||
Sources *[]GroupMinimum `json:"sources,omitempty"`
|
||||
}
|
||||
|
||||
// PolicyRuleAction Policy rule accept or drops packets
|
||||
@@ -826,9 +1000,6 @@ type PolicyRuleMinimum struct {
|
||||
// Enabled Policy rule status
|
||||
Enabled bool `json:"enabled"`
|
||||
|
||||
// Id Policy rule ID
|
||||
Id *string `json:"id,omitempty"`
|
||||
|
||||
// Name Policy rule name identifier
|
||||
Name string `json:"name"`
|
||||
|
||||
@@ -857,10 +1028,11 @@ type PolicyRuleUpdate struct {
|
||||
Bidirectional bool `json:"bidirectional"`
|
||||
|
||||
// Description Policy rule friendly description
|
||||
Description *string `json:"description,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
DestinationResource *Resource `json:"destinationResource,omitempty"`
|
||||
|
||||
// Destinations Policy rule destination group IDs
|
||||
Destinations []string `json:"destinations"`
|
||||
Destinations *[]string `json:"destinations,omitempty"`
|
||||
|
||||
// Enabled Policy rule status
|
||||
Enabled bool `json:"enabled"`
|
||||
@@ -878,10 +1050,11 @@ type PolicyRuleUpdate struct {
|
||||
Ports *[]string `json:"ports,omitempty"`
|
||||
|
||||
// Protocol Policy rule type of the traffic
|
||||
Protocol PolicyRuleUpdateProtocol `json:"protocol"`
|
||||
Protocol PolicyRuleUpdateProtocol `json:"protocol"`
|
||||
SourceResource *Resource `json:"sourceResource,omitempty"`
|
||||
|
||||
// Sources Policy rule source group IDs
|
||||
Sources []string `json:"sources"`
|
||||
Sources *[]string `json:"sources,omitempty"`
|
||||
}
|
||||
|
||||
// PolicyRuleUpdateAction Policy rule accept or drops packets
|
||||
@@ -893,14 +1066,11 @@ type PolicyRuleUpdateProtocol string
|
||||
// PolicyUpdate defines model for PolicyUpdate.
|
||||
type PolicyUpdate struct {
|
||||
// Description Policy friendly description
|
||||
Description string `json:"description"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
|
||||
// Enabled Policy status
|
||||
Enabled bool `json:"enabled"`
|
||||
|
||||
// Id Policy ID
|
||||
Id *string `json:"id,omitempty"`
|
||||
|
||||
// Name Policy name identifier
|
||||
Name string `json:"name"`
|
||||
|
||||
@@ -955,6 +1125,16 @@ type ProcessCheck struct {
|
||||
Processes []Process `json:"processes"`
|
||||
}
|
||||
|
||||
// Resource defines model for Resource.
|
||||
type Resource struct {
|
||||
// Id ID of the resource
|
||||
Id string `json:"id"`
|
||||
Type ResourceType `json:"type"`
|
||||
}
|
||||
|
||||
// ResourceType defines model for ResourceType.
|
||||
type ResourceType string
|
||||
|
||||
// Route defines model for Route.
|
||||
type Route struct {
|
||||
// AccessControlGroups Access control group identifier associated with route.
|
||||
@@ -1292,6 +1472,24 @@ type PostApiGroupsJSONRequestBody = GroupRequest
|
||||
// PutApiGroupsGroupIdJSONRequestBody defines body for PutApiGroupsGroupId for application/json ContentType.
|
||||
type PutApiGroupsGroupIdJSONRequestBody = GroupRequest
|
||||
|
||||
// PostApiNetworksJSONRequestBody defines body for PostApiNetworks for application/json ContentType.
|
||||
type PostApiNetworksJSONRequestBody = NetworkRequest
|
||||
|
||||
// PutApiNetworksNetworkIdJSONRequestBody defines body for PutApiNetworksNetworkId for application/json ContentType.
|
||||
type PutApiNetworksNetworkIdJSONRequestBody = NetworkRequest
|
||||
|
||||
// PostApiNetworksNetworkIdResourcesJSONRequestBody defines body for PostApiNetworksNetworkIdResources for application/json ContentType.
|
||||
type PostApiNetworksNetworkIdResourcesJSONRequestBody = NetworkResourceRequest
|
||||
|
||||
// PutApiNetworksNetworkIdResourcesResourceIdJSONRequestBody defines body for PutApiNetworksNetworkIdResourcesResourceId for application/json ContentType.
|
||||
type PutApiNetworksNetworkIdResourcesResourceIdJSONRequestBody = NetworkResourceRequest
|
||||
|
||||
// PostApiNetworksNetworkIdRoutersJSONRequestBody defines body for PostApiNetworksNetworkIdRouters for application/json ContentType.
|
||||
type PostApiNetworksNetworkIdRoutersJSONRequestBody = NetworkRouterRequest
|
||||
|
||||
// PutApiNetworksNetworkIdRoutersRouterIdJSONRequestBody defines body for PutApiNetworksNetworkIdRoutersRouterId for application/json ContentType.
|
||||
type PutApiNetworksNetworkIdRoutersRouterIdJSONRequestBody = NetworkRouterRequest
|
||||
|
||||
// PutApiPeersPeerIdJSONRequestBody defines body for PutApiPeersPeerId for application/json ContentType.
|
||||
type PutApiPeersPeerIdJSONRequestBody = PeerRequest
|
||||
|
||||
@@ -1299,7 +1497,7 @@ type PutApiPeersPeerIdJSONRequestBody = PeerRequest
|
||||
type PostApiPoliciesJSONRequestBody = PolicyUpdate
|
||||
|
||||
// PutApiPoliciesPolicyIdJSONRequestBody defines body for PutApiPoliciesPolicyId for application/json ContentType.
|
||||
type PutApiPoliciesPolicyIdJSONRequestBody = PolicyUpdate
|
||||
type PutApiPoliciesPolicyIdJSONRequestBody = PolicyCreate
|
||||
|
||||
// PostApiPostureChecksJSONRequestBody defines body for PostApiPostureChecks for application/json ContentType.
|
||||
type PostApiPostureChecksJSONRequestBody = PostureCheckUpdate
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package configs
|
||||
|
||||
// AuthCfg contains parameters for authentication middleware
|
||||
type AuthCfg struct {
|
||||
Issuer string
|
||||
Audience string
|
||||
UserIDClaim string
|
||||
KeysLocation string
|
||||
}
|
||||
@@ -12,35 +12,31 @@ import (
|
||||
|
||||
s "github.com/netbirdio/netbird/management/server"
|
||||
"github.com/netbirdio/netbird/management/server/geolocation"
|
||||
nbgroups "github.com/netbirdio/netbird/management/server/groups"
|
||||
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||
"github.com/netbirdio/netbird/management/server/http/handlers/accounts"
|
||||
"github.com/netbirdio/netbird/management/server/http/handlers/dns"
|
||||
"github.com/netbirdio/netbird/management/server/http/handlers/events"
|
||||
"github.com/netbirdio/netbird/management/server/http/handlers/groups"
|
||||
"github.com/netbirdio/netbird/management/server/http/handlers/networks"
|
||||
"github.com/netbirdio/netbird/management/server/http/handlers/peers"
|
||||
"github.com/netbirdio/netbird/management/server/http/handlers/policies"
|
||||
"github.com/netbirdio/netbird/management/server/http/handlers/routes"
|
||||
"github.com/netbirdio/netbird/management/server/http/handlers/setup_keys"
|
||||
"github.com/netbirdio/netbird/management/server/http/handlers/users"
|
||||
"github.com/netbirdio/netbird/management/server/http/middleware"
|
||||
"github.com/netbirdio/netbird/management/server/integrated_validator"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
nbnetworks "github.com/netbirdio/netbird/management/server/networks"
|
||||
"github.com/netbirdio/netbird/management/server/networks/resources"
|
||||
"github.com/netbirdio/netbird/management/server/networks/routers"
|
||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||
)
|
||||
|
||||
const apiPrefix = "/api"
|
||||
|
||||
// AuthCfg contains parameters for authentication middleware
|
||||
type AuthCfg struct {
|
||||
Issuer string
|
||||
Audience string
|
||||
UserIDClaim string
|
||||
KeysLocation string
|
||||
}
|
||||
|
||||
type apiHandler struct {
|
||||
Router *mux.Router
|
||||
AccountManager s.AccountManager
|
||||
geolocationManager *geolocation.Geolocation
|
||||
AuthCfg AuthCfg
|
||||
}
|
||||
|
||||
// EmptyObject is an empty struct used to return empty JSON object
|
||||
type emptyObject struct {
|
||||
}
|
||||
|
||||
// APIHandler creates the Management service HTTP API handler registering all the available endpoints.
|
||||
func APIHandler(ctx context.Context, accountManager s.AccountManager, LocationManager *geolocation.Geolocation, jwtValidator jwtclaims.JWTValidator, appMetrics telemetry.AppMetrics, authCfg AuthCfg, integratedValidator integrated_validator.IntegratedValidator) (http.Handler, error) {
|
||||
// NewAPIHandler creates the Management service HTTP API handler registering all the available endpoints.
|
||||
func NewAPIHandler(ctx context.Context, accountManager s.AccountManager, networksManager nbnetworks.Manager, resourceManager resources.Manager, routerManager routers.Manager, groupsManager nbgroups.Manager, LocationManager geolocation.Geolocation, jwtValidator jwtclaims.JWTValidator, appMetrics telemetry.AppMetrics, authCfg configs.AuthCfg, integratedValidator integrated_validator.IntegratedValidator) (http.Handler, error) {
|
||||
claimsExtractor := jwtclaims.NewClaimsExtractor(
|
||||
jwtclaims.WithAudience(authCfg.Audience),
|
||||
jwtclaims.WithUserIDClaim(authCfg.UserIDClaim),
|
||||
@@ -75,133 +71,20 @@ func APIHandler(ctx context.Context, accountManager s.AccountManager, LocationMa
|
||||
router := rootRouter.PathPrefix(prefix).Subrouter()
|
||||
router.Use(metricsMiddleware.Handler, corsMiddleware.Handler, authMiddleware.Handler, acMiddleware.Handler)
|
||||
|
||||
api := apiHandler{
|
||||
Router: router,
|
||||
AccountManager: accountManager,
|
||||
geolocationManager: LocationManager,
|
||||
AuthCfg: authCfg,
|
||||
}
|
||||
|
||||
if _, err := integrations.RegisterHandlers(ctx, prefix, api.Router, accountManager, claimsExtractor, integratedValidator, appMetrics.GetMeter()); err != nil {
|
||||
if _, err := integrations.RegisterHandlers(ctx, prefix, router, accountManager, claimsExtractor, integratedValidator, appMetrics.GetMeter()); err != nil {
|
||||
return nil, fmt.Errorf("register integrations endpoints: %w", err)
|
||||
}
|
||||
|
||||
api.addAccountsEndpoint()
|
||||
api.addPeersEndpoint()
|
||||
api.addUsersEndpoint()
|
||||
api.addUsersTokensEndpoint()
|
||||
api.addSetupKeysEndpoint()
|
||||
api.addPoliciesEndpoint()
|
||||
api.addGroupsEndpoint()
|
||||
api.addRoutesEndpoint()
|
||||
api.addDNSNameserversEndpoint()
|
||||
api.addDNSSettingEndpoint()
|
||||
api.addEventsEndpoint()
|
||||
api.addPostureCheckEndpoint()
|
||||
api.addLocationsEndpoint()
|
||||
accounts.AddEndpoints(accountManager, authCfg, router)
|
||||
peers.AddEndpoints(accountManager, authCfg, router)
|
||||
users.AddEndpoints(accountManager, authCfg, router)
|
||||
setup_keys.AddEndpoints(accountManager, authCfg, router)
|
||||
policies.AddEndpoints(accountManager, LocationManager, authCfg, router)
|
||||
groups.AddEndpoints(accountManager, authCfg, router)
|
||||
routes.AddEndpoints(accountManager, authCfg, router)
|
||||
dns.AddEndpoints(accountManager, authCfg, router)
|
||||
events.AddEndpoints(accountManager, authCfg, router)
|
||||
networks.AddEndpoints(networksManager, resourceManager, routerManager, groupsManager, accountManager, accountManager.GetAccountIDFromToken, authCfg, router)
|
||||
|
||||
return rootRouter, nil
|
||||
}
|
||||
|
||||
func (apiHandler *apiHandler) addAccountsEndpoint() {
|
||||
accountsHandler := NewAccountsHandler(apiHandler.AccountManager, apiHandler.AuthCfg)
|
||||
apiHandler.Router.HandleFunc("/accounts/{accountId}", accountsHandler.UpdateAccount).Methods("PUT", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/accounts/{accountId}", accountsHandler.DeleteAccount).Methods("DELETE", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/accounts", accountsHandler.GetAllAccounts).Methods("GET", "OPTIONS")
|
||||
}
|
||||
|
||||
func (apiHandler *apiHandler) addPeersEndpoint() {
|
||||
peersHandler := NewPeersHandler(apiHandler.AccountManager, apiHandler.AuthCfg)
|
||||
apiHandler.Router.HandleFunc("/peers", peersHandler.GetAllPeers).Methods("GET", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/peers/{peerId}", peersHandler.HandlePeer).
|
||||
Methods("GET", "PUT", "DELETE", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/peers/{peerId}/accessible-peers", peersHandler.GetAccessiblePeers).Methods("GET", "OPTIONS")
|
||||
}
|
||||
|
||||
func (apiHandler *apiHandler) addUsersEndpoint() {
|
||||
userHandler := NewUsersHandler(apiHandler.AccountManager, apiHandler.AuthCfg)
|
||||
apiHandler.Router.HandleFunc("/users", userHandler.GetAllUsers).Methods("GET", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/users/{userId}", userHandler.UpdateUser).Methods("PUT", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/users/{userId}", userHandler.DeleteUser).Methods("DELETE", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/users", userHandler.CreateUser).Methods("POST", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/users/{userId}/invite", userHandler.InviteUser).Methods("POST", "OPTIONS")
|
||||
}
|
||||
|
||||
func (apiHandler *apiHandler) addUsersTokensEndpoint() {
|
||||
tokenHandler := NewPATsHandler(apiHandler.AccountManager, apiHandler.AuthCfg)
|
||||
apiHandler.Router.HandleFunc("/users/{userId}/tokens", tokenHandler.GetAllTokens).Methods("GET", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/users/{userId}/tokens", tokenHandler.CreateToken).Methods("POST", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/users/{userId}/tokens/{tokenId}", tokenHandler.GetToken).Methods("GET", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/users/{userId}/tokens/{tokenId}", tokenHandler.DeleteToken).Methods("DELETE", "OPTIONS")
|
||||
}
|
||||
|
||||
func (apiHandler *apiHandler) addSetupKeysEndpoint() {
|
||||
keysHandler := NewSetupKeysHandler(apiHandler.AccountManager, apiHandler.AuthCfg)
|
||||
apiHandler.Router.HandleFunc("/setup-keys", keysHandler.GetAllSetupKeys).Methods("GET", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/setup-keys", keysHandler.CreateSetupKey).Methods("POST", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/setup-keys/{keyId}", keysHandler.GetSetupKey).Methods("GET", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/setup-keys/{keyId}", keysHandler.UpdateSetupKey).Methods("PUT", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/setup-keys/{keyId}", keysHandler.DeleteSetupKey).Methods("DELETE", "OPTIONS")
|
||||
}
|
||||
|
||||
func (apiHandler *apiHandler) addPoliciesEndpoint() {
|
||||
policiesHandler := NewPoliciesHandler(apiHandler.AccountManager, apiHandler.AuthCfg)
|
||||
apiHandler.Router.HandleFunc("/policies", policiesHandler.GetAllPolicies).Methods("GET", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/policies", policiesHandler.CreatePolicy).Methods("POST", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/policies/{policyId}", policiesHandler.UpdatePolicy).Methods("PUT", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/policies/{policyId}", policiesHandler.GetPolicy).Methods("GET", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/policies/{policyId}", policiesHandler.DeletePolicy).Methods("DELETE", "OPTIONS")
|
||||
}
|
||||
|
||||
func (apiHandler *apiHandler) addGroupsEndpoint() {
|
||||
groupsHandler := NewGroupsHandler(apiHandler.AccountManager, apiHandler.AuthCfg)
|
||||
apiHandler.Router.HandleFunc("/groups", groupsHandler.GetAllGroups).Methods("GET", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/groups", groupsHandler.CreateGroup).Methods("POST", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/groups/{groupId}", groupsHandler.UpdateGroup).Methods("PUT", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/groups/{groupId}", groupsHandler.GetGroup).Methods("GET", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/groups/{groupId}", groupsHandler.DeleteGroup).Methods("DELETE", "OPTIONS")
|
||||
}
|
||||
|
||||
func (apiHandler *apiHandler) addRoutesEndpoint() {
|
||||
routesHandler := NewRoutesHandler(apiHandler.AccountManager, apiHandler.AuthCfg)
|
||||
apiHandler.Router.HandleFunc("/routes", routesHandler.GetAllRoutes).Methods("GET", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/routes", routesHandler.CreateRoute).Methods("POST", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/routes/{routeId}", routesHandler.UpdateRoute).Methods("PUT", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/routes/{routeId}", routesHandler.GetRoute).Methods("GET", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/routes/{routeId}", routesHandler.DeleteRoute).Methods("DELETE", "OPTIONS")
|
||||
}
|
||||
|
||||
func (apiHandler *apiHandler) addDNSNameserversEndpoint() {
|
||||
nameserversHandler := NewNameserversHandler(apiHandler.AccountManager, apiHandler.AuthCfg)
|
||||
apiHandler.Router.HandleFunc("/dns/nameservers", nameserversHandler.GetAllNameservers).Methods("GET", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/dns/nameservers", nameserversHandler.CreateNameserverGroup).Methods("POST", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/dns/nameservers/{nsgroupId}", nameserversHandler.UpdateNameserverGroup).Methods("PUT", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/dns/nameservers/{nsgroupId}", nameserversHandler.GetNameserverGroup).Methods("GET", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/dns/nameservers/{nsgroupId}", nameserversHandler.DeleteNameserverGroup).Methods("DELETE", "OPTIONS")
|
||||
}
|
||||
|
||||
func (apiHandler *apiHandler) addDNSSettingEndpoint() {
|
||||
dnsSettingsHandler := NewDNSSettingsHandler(apiHandler.AccountManager, apiHandler.AuthCfg)
|
||||
apiHandler.Router.HandleFunc("/dns/settings", dnsSettingsHandler.GetDNSSettings).Methods("GET", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/dns/settings", dnsSettingsHandler.UpdateDNSSettings).Methods("PUT", "OPTIONS")
|
||||
}
|
||||
|
||||
func (apiHandler *apiHandler) addEventsEndpoint() {
|
||||
eventsHandler := NewEventsHandler(apiHandler.AccountManager, apiHandler.AuthCfg)
|
||||
apiHandler.Router.HandleFunc("/events", eventsHandler.GetAllEvents).Methods("GET", "OPTIONS")
|
||||
}
|
||||
|
||||
func (apiHandler *apiHandler) addPostureCheckEndpoint() {
|
||||
postureCheckHandler := NewPostureChecksHandler(apiHandler.AccountManager, apiHandler.geolocationManager, apiHandler.AuthCfg)
|
||||
apiHandler.Router.HandleFunc("/posture-checks", postureCheckHandler.GetAllPostureChecks).Methods("GET", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/posture-checks", postureCheckHandler.CreatePostureCheck).Methods("POST", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/posture-checks/{postureCheckId}", postureCheckHandler.UpdatePostureCheck).Methods("PUT", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/posture-checks/{postureCheckId}", postureCheckHandler.GetPostureCheck).Methods("GET", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/posture-checks/{postureCheckId}", postureCheckHandler.DeletePostureCheck).Methods("DELETE", "OPTIONS")
|
||||
}
|
||||
|
||||
func (apiHandler *apiHandler) addLocationsEndpoint() {
|
||||
locationHandler := NewGeolocationsHandlerHandler(apiHandler.AccountManager, apiHandler.geolocationManager, apiHandler.AuthCfg)
|
||||
apiHandler.Router.HandleFunc("/locations/countries", locationHandler.GetAllCountries).Methods("GET", "OPTIONS")
|
||||
apiHandler.Router.HandleFunc("/locations/countries/{country}/cities", locationHandler.GetCitiesByCountry).Methods("GET", "OPTIONS")
|
||||
}
|
||||
|
||||
+28
-15
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -10,20 +10,29 @@ import (
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"github.com/netbirdio/netbird/management/server/account"
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||
"github.com/netbirdio/netbird/management/server/http/util"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
// AccountsHandler is a handler that handles the server.Account HTTP endpoints
|
||||
type AccountsHandler struct {
|
||||
// handler is a handler that handles the server.Account HTTP endpoints
|
||||
type handler struct {
|
||||
accountManager server.AccountManager
|
||||
claimsExtractor *jwtclaims.ClaimsExtractor
|
||||
}
|
||||
|
||||
// NewAccountsHandler creates a new AccountsHandler HTTP handler
|
||||
func NewAccountsHandler(accountManager server.AccountManager, authCfg AuthCfg) *AccountsHandler {
|
||||
return &AccountsHandler{
|
||||
func AddEndpoints(accountManager server.AccountManager, authCfg configs.AuthCfg, router *mux.Router) {
|
||||
accountsHandler := newHandler(accountManager, authCfg)
|
||||
router.HandleFunc("/accounts/{accountId}", accountsHandler.updateAccount).Methods("PUT", "OPTIONS")
|
||||
router.HandleFunc("/accounts/{accountId}", accountsHandler.deleteAccount).Methods("DELETE", "OPTIONS")
|
||||
router.HandleFunc("/accounts", accountsHandler.getAllAccounts).Methods("GET", "OPTIONS")
|
||||
}
|
||||
|
||||
// newHandler creates a new handler HTTP handler
|
||||
func newHandler(accountManager server.AccountManager, authCfg configs.AuthCfg) *handler {
|
||||
return &handler{
|
||||
accountManager: accountManager,
|
||||
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||
jwtclaims.WithAudience(authCfg.Audience),
|
||||
@@ -32,8 +41,8 @@ func NewAccountsHandler(accountManager server.AccountManager, authCfg AuthCfg) *
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllAccounts is HTTP GET handler that returns a list of accounts. Effectively returns just a single account.
|
||||
func (h *AccountsHandler) GetAllAccounts(w http.ResponseWriter, r *http.Request) {
|
||||
// getAllAccounts is HTTP GET handler that returns a list of accounts. Effectively returns just a single account.
|
||||
func (h *handler) getAllAccounts(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -51,8 +60,8 @@ func (h *AccountsHandler) GetAllAccounts(w http.ResponseWriter, r *http.Request)
|
||||
util.WriteJSONObject(r.Context(), w, []*api.Account{resp})
|
||||
}
|
||||
|
||||
// UpdateAccount is HTTP PUT handler that updates the provided account. Updates only account settings (server.Settings)
|
||||
func (h *AccountsHandler) UpdateAccount(w http.ResponseWriter, r *http.Request) {
|
||||
// updateAccount is HTTP PUT handler that updates the provided account. Updates only account settings (server.Settings)
|
||||
func (h *handler) updateAccount(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
_, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -74,7 +83,7 @@ func (h *AccountsHandler) UpdateAccount(w http.ResponseWriter, r *http.Request)
|
||||
return
|
||||
}
|
||||
|
||||
settings := &server.Settings{
|
||||
settings := &types.Settings{
|
||||
PeerLoginExpirationEnabled: req.Settings.PeerLoginExpirationEnabled,
|
||||
PeerLoginExpiration: time.Duration(float64(time.Second.Nanoseconds()) * float64(req.Settings.PeerLoginExpiration)),
|
||||
RegularUsersViewBlocked: req.Settings.RegularUsersViewBlocked,
|
||||
@@ -99,6 +108,9 @@ func (h *AccountsHandler) UpdateAccount(w http.ResponseWriter, r *http.Request)
|
||||
if req.Settings.JwtAllowGroups != nil {
|
||||
settings.JWTAllowGroups = *req.Settings.JwtAllowGroups
|
||||
}
|
||||
if req.Settings.RoutingPeerDnsResolutionEnabled != nil {
|
||||
settings.RoutingPeerDNSResolutionEnabled = *req.Settings.RoutingPeerDnsResolutionEnabled
|
||||
}
|
||||
|
||||
updatedAccount, err := h.accountManager.UpdateAccountSettings(r.Context(), accountID, userID, settings)
|
||||
if err != nil {
|
||||
@@ -111,8 +123,8 @@ func (h *AccountsHandler) UpdateAccount(w http.ResponseWriter, r *http.Request)
|
||||
util.WriteJSONObject(r.Context(), w, &resp)
|
||||
}
|
||||
|
||||
// DeleteAccount is a HTTP DELETE handler to delete an account
|
||||
func (h *AccountsHandler) DeleteAccount(w http.ResponseWriter, r *http.Request) {
|
||||
// deleteAccount is a HTTP DELETE handler to delete an account
|
||||
func (h *handler) deleteAccount(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
vars := mux.Vars(r)
|
||||
targetAccountID := vars["accountId"]
|
||||
@@ -127,10 +139,10 @@ func (h *AccountsHandler) DeleteAccount(w http.ResponseWriter, r *http.Request)
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, emptyObject{})
|
||||
util.WriteJSONObject(r.Context(), w, util.EmptyObject{})
|
||||
}
|
||||
|
||||
func toAccountResponse(accountID string, settings *server.Settings) *api.Account {
|
||||
func toAccountResponse(accountID string, settings *types.Settings) *api.Account {
|
||||
jwtAllowGroups := settings.JWTAllowGroups
|
||||
if jwtAllowGroups == nil {
|
||||
jwtAllowGroups = []string{}
|
||||
@@ -146,6 +158,7 @@ func toAccountResponse(accountID string, settings *server.Settings) *api.Account
|
||||
JwtGroupsClaimName: &settings.JWTGroupsClaimName,
|
||||
JwtAllowGroups: &jwtAllowGroups,
|
||||
RegularUsersViewBlocked: settings.RegularUsersViewBlocked,
|
||||
RoutingPeerDnsResolutionEnabled: &settings.RoutingPeerDNSResolutionEnabled,
|
||||
}
|
||||
|
||||
if settings.Extra != nil {
|
||||
+46
-42
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -13,23 +13,23 @@ import (
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"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/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
func initAccountsTestData(account *server.Account, admin *server.User) *AccountsHandler {
|
||||
return &AccountsHandler{
|
||||
func initAccountsTestData(account *types.Account, admin *types.User) *handler {
|
||||
return &handler{
|
||||
accountManager: &mock_server.MockAccountManager{
|
||||
GetAccountIDFromTokenFunc: func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error) {
|
||||
return account.Id, admin.Id, nil
|
||||
},
|
||||
GetAccountSettingsFunc: func(ctx context.Context, accountID string, userID string) (*server.Settings, error) {
|
||||
GetAccountSettingsFunc: func(ctx context.Context, accountID string, userID string) (*types.Settings, error) {
|
||||
return account.Settings, nil
|
||||
},
|
||||
UpdateAccountSettingsFunc: func(ctx context.Context, accountID, userID string, newSettings *server.Settings) (*server.Account, error) {
|
||||
UpdateAccountSettingsFunc: func(ctx context.Context, accountID, userID string, newSettings *types.Settings) (*types.Account, error) {
|
||||
halfYearLimit := 180 * 24 * time.Hour
|
||||
if newSettings.PeerLoginExpiration > halfYearLimit {
|
||||
return nil, status.Errorf(status.InvalidArgument, "peer login expiration can't be larger than 180 days")
|
||||
@@ -58,19 +58,19 @@ func initAccountsTestData(account *server.Account, admin *server.User) *Accounts
|
||||
|
||||
func TestAccounts_AccountsHandler(t *testing.T) {
|
||||
accountID := "test_account"
|
||||
adminUser := server.NewAdminUser("test_user")
|
||||
adminUser := types.NewAdminUser("test_user")
|
||||
|
||||
sr := func(v string) *string { return &v }
|
||||
br := func(v bool) *bool { return &v }
|
||||
|
||||
handler := initAccountsTestData(&server.Account{
|
||||
handler := initAccountsTestData(&types.Account{
|
||||
Id: accountID,
|
||||
Domain: "hotmail.com",
|
||||
Network: server.NewNetwork(),
|
||||
Users: map[string]*server.User{
|
||||
Network: types.NewNetwork(),
|
||||
Users: map[string]*types.User{
|
||||
adminUser.Id: adminUser,
|
||||
},
|
||||
Settings: &server.Settings{
|
||||
Settings: &types.Settings{
|
||||
PeerLoginExpirationEnabled: false,
|
||||
PeerLoginExpiration: time.Hour,
|
||||
RegularUsersViewBlocked: true,
|
||||
@@ -89,19 +89,20 @@ func TestAccounts_AccountsHandler(t *testing.T) {
|
||||
requestBody io.Reader
|
||||
}{
|
||||
{
|
||||
name: "GetAllAccounts OK",
|
||||
name: "getAllAccounts OK",
|
||||
expectedBody: true,
|
||||
requestType: http.MethodGet,
|
||||
requestPath: "/api/accounts",
|
||||
expectedStatus: http.StatusOK,
|
||||
expectedSettings: api.AccountSettings{
|
||||
PeerLoginExpiration: int(time.Hour.Seconds()),
|
||||
PeerLoginExpirationEnabled: false,
|
||||
GroupsPropagationEnabled: br(false),
|
||||
JwtGroupsClaimName: sr(""),
|
||||
JwtGroupsEnabled: br(false),
|
||||
JwtAllowGroups: &[]string{},
|
||||
RegularUsersViewBlocked: true,
|
||||
PeerLoginExpiration: int(time.Hour.Seconds()),
|
||||
PeerLoginExpirationEnabled: false,
|
||||
GroupsPropagationEnabled: br(false),
|
||||
JwtGroupsClaimName: sr(""),
|
||||
JwtGroupsEnabled: br(false),
|
||||
JwtAllowGroups: &[]string{},
|
||||
RegularUsersViewBlocked: true,
|
||||
RoutingPeerDnsResolutionEnabled: br(false),
|
||||
},
|
||||
expectedArray: true,
|
||||
expectedID: accountID,
|
||||
@@ -114,13 +115,14 @@ func TestAccounts_AccountsHandler(t *testing.T) {
|
||||
requestBody: bytes.NewBufferString("{\"settings\": {\"peer_login_expiration\": 15552000,\"peer_login_expiration_enabled\": true}}"),
|
||||
expectedStatus: http.StatusOK,
|
||||
expectedSettings: api.AccountSettings{
|
||||
PeerLoginExpiration: 15552000,
|
||||
PeerLoginExpirationEnabled: true,
|
||||
GroupsPropagationEnabled: br(false),
|
||||
JwtGroupsClaimName: sr(""),
|
||||
JwtGroupsEnabled: br(false),
|
||||
JwtAllowGroups: &[]string{},
|
||||
RegularUsersViewBlocked: false,
|
||||
PeerLoginExpiration: 15552000,
|
||||
PeerLoginExpirationEnabled: true,
|
||||
GroupsPropagationEnabled: br(false),
|
||||
JwtGroupsClaimName: sr(""),
|
||||
JwtGroupsEnabled: br(false),
|
||||
JwtAllowGroups: &[]string{},
|
||||
RegularUsersViewBlocked: false,
|
||||
RoutingPeerDnsResolutionEnabled: br(false),
|
||||
},
|
||||
expectedArray: false,
|
||||
expectedID: accountID,
|
||||
@@ -133,13 +135,14 @@ func TestAccounts_AccountsHandler(t *testing.T) {
|
||||
requestBody: bytes.NewBufferString("{\"settings\": {\"peer_login_expiration\": 15552000,\"peer_login_expiration_enabled\": false,\"jwt_groups_enabled\":true,\"jwt_groups_claim_name\":\"roles\",\"jwt_allow_groups\":[\"test\"],\"regular_users_view_blocked\":true}}"),
|
||||
expectedStatus: http.StatusOK,
|
||||
expectedSettings: api.AccountSettings{
|
||||
PeerLoginExpiration: 15552000,
|
||||
PeerLoginExpirationEnabled: false,
|
||||
GroupsPropagationEnabled: br(false),
|
||||
JwtGroupsClaimName: sr("roles"),
|
||||
JwtGroupsEnabled: br(true),
|
||||
JwtAllowGroups: &[]string{"test"},
|
||||
RegularUsersViewBlocked: true,
|
||||
PeerLoginExpiration: 15552000,
|
||||
PeerLoginExpirationEnabled: false,
|
||||
GroupsPropagationEnabled: br(false),
|
||||
JwtGroupsClaimName: sr("roles"),
|
||||
JwtGroupsEnabled: br(true),
|
||||
JwtAllowGroups: &[]string{"test"},
|
||||
RegularUsersViewBlocked: true,
|
||||
RoutingPeerDnsResolutionEnabled: br(false),
|
||||
},
|
||||
expectedArray: false,
|
||||
expectedID: accountID,
|
||||
@@ -152,13 +155,14 @@ func TestAccounts_AccountsHandler(t *testing.T) {
|
||||
requestBody: bytes.NewBufferString("{\"settings\": {\"peer_login_expiration\": 554400,\"peer_login_expiration_enabled\": true,\"jwt_groups_enabled\":true,\"jwt_groups_claim_name\":\"groups\",\"groups_propagation_enabled\":true,\"regular_users_view_blocked\":true}}"),
|
||||
expectedStatus: http.StatusOK,
|
||||
expectedSettings: api.AccountSettings{
|
||||
PeerLoginExpiration: 554400,
|
||||
PeerLoginExpirationEnabled: true,
|
||||
GroupsPropagationEnabled: br(true),
|
||||
JwtGroupsClaimName: sr("groups"),
|
||||
JwtGroupsEnabled: br(true),
|
||||
JwtAllowGroups: &[]string{},
|
||||
RegularUsersViewBlocked: true,
|
||||
PeerLoginExpiration: 554400,
|
||||
PeerLoginExpirationEnabled: true,
|
||||
GroupsPropagationEnabled: br(true),
|
||||
JwtGroupsClaimName: sr("groups"),
|
||||
JwtGroupsEnabled: br(true),
|
||||
JwtAllowGroups: &[]string{},
|
||||
RegularUsersViewBlocked: true,
|
||||
RoutingPeerDnsResolutionEnabled: br(false),
|
||||
},
|
||||
expectedArray: false,
|
||||
expectedID: accountID,
|
||||
@@ -189,8 +193,8 @@ func TestAccounts_AccountsHandler(t *testing.T) {
|
||||
req := httptest.NewRequest(tc.requestType, tc.requestPath, tc.requestBody)
|
||||
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/api/accounts", handler.GetAllAccounts).Methods("GET")
|
||||
router.HandleFunc("/api/accounts/{accountId}", handler.UpdateAccount).Methods("PUT")
|
||||
router.HandleFunc("/api/accounts", handler.getAllAccounts).Methods("GET")
|
||||
router.HandleFunc("/api/accounts/{accountId}", handler.updateAccount).Methods("PUT")
|
||||
router.ServeHTTP(recorder, req)
|
||||
|
||||
res := recorder.Result()
|
||||
+25
-11
@@ -1,26 +1,40 @@
|
||||
package http
|
||||
package dns
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||
"github.com/netbirdio/netbird/management/server/http/util"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
// DNSSettingsHandler is a handler that returns the DNS settings of the account
|
||||
type DNSSettingsHandler struct {
|
||||
// dnsSettingsHandler is a handler that returns the DNS settings of the account
|
||||
type dnsSettingsHandler struct {
|
||||
accountManager server.AccountManager
|
||||
claimsExtractor *jwtclaims.ClaimsExtractor
|
||||
}
|
||||
|
||||
// NewDNSSettingsHandler returns a new instance of DNSSettingsHandler handler
|
||||
func NewDNSSettingsHandler(accountManager server.AccountManager, authCfg AuthCfg) *DNSSettingsHandler {
|
||||
return &DNSSettingsHandler{
|
||||
func AddEndpoints(accountManager server.AccountManager, authCfg configs.AuthCfg, router *mux.Router) {
|
||||
addDNSSettingEndpoint(accountManager, authCfg, router)
|
||||
addDNSNameserversEndpoint(accountManager, authCfg, router)
|
||||
}
|
||||
|
||||
func addDNSSettingEndpoint(accountManager server.AccountManager, authCfg configs.AuthCfg, router *mux.Router) {
|
||||
dnsSettingsHandler := newDNSSettingsHandler(accountManager, authCfg)
|
||||
router.HandleFunc("/dns/settings", dnsSettingsHandler.getDNSSettings).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/dns/settings", dnsSettingsHandler.updateDNSSettings).Methods("PUT", "OPTIONS")
|
||||
}
|
||||
|
||||
// newDNSSettingsHandler returns a new instance of dnsSettingsHandler handler
|
||||
func newDNSSettingsHandler(accountManager server.AccountManager, authCfg configs.AuthCfg) *dnsSettingsHandler {
|
||||
return &dnsSettingsHandler{
|
||||
accountManager: accountManager,
|
||||
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||
jwtclaims.WithAudience(authCfg.Audience),
|
||||
@@ -29,8 +43,8 @@ func NewDNSSettingsHandler(accountManager server.AccountManager, authCfg AuthCfg
|
||||
}
|
||||
}
|
||||
|
||||
// GetDNSSettings returns the DNS settings for the account
|
||||
func (h *DNSSettingsHandler) GetDNSSettings(w http.ResponseWriter, r *http.Request) {
|
||||
// getDNSSettings returns the DNS settings for the account
|
||||
func (h *dnsSettingsHandler) getDNSSettings(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -52,8 +66,8 @@ func (h *DNSSettingsHandler) GetDNSSettings(w http.ResponseWriter, r *http.Reque
|
||||
util.WriteJSONObject(r.Context(), w, apiDNSSettings)
|
||||
}
|
||||
|
||||
// UpdateDNSSettings handles update to DNS settings of an account
|
||||
func (h *DNSSettingsHandler) UpdateDNSSettings(w http.ResponseWriter, r *http.Request) {
|
||||
// updateDNSSettings handles update to DNS settings of an account
|
||||
func (h *dnsSettingsHandler) updateDNSSettings(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -68,7 +82,7 @@ func (h *DNSSettingsHandler) UpdateDNSSettings(w http.ResponseWriter, r *http.Re
|
||||
return
|
||||
}
|
||||
|
||||
updateDNSSettings := &server.DNSSettings{
|
||||
updateDNSSettings := &types.DNSSettings{
|
||||
DisabledManagementGroups: req.DisabledManagementGroups,
|
||||
}
|
||||
|
||||
+12
-12
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package dns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -13,10 +13,10 @@ import (
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
"github.com/netbirdio/netbird/management/server/mock_server"
|
||||
)
|
||||
@@ -27,26 +27,26 @@ const (
|
||||
testDNSSettingsUserID = "test_user"
|
||||
)
|
||||
|
||||
var baseExistingDNSSettings = server.DNSSettings{
|
||||
var baseExistingDNSSettings = types.DNSSettings{
|
||||
DisabledManagementGroups: []string{testDNSSettingsExistingGroup},
|
||||
}
|
||||
|
||||
var testingDNSSettingsAccount = &server.Account{
|
||||
var testingDNSSettingsAccount = &types.Account{
|
||||
Id: testDNSSettingsAccountID,
|
||||
Domain: "hotmail.com",
|
||||
Users: map[string]*server.User{
|
||||
testDNSSettingsUserID: server.NewAdminUser("test_user"),
|
||||
Users: map[string]*types.User{
|
||||
testDNSSettingsUserID: types.NewAdminUser("test_user"),
|
||||
},
|
||||
DNSSettings: baseExistingDNSSettings,
|
||||
}
|
||||
|
||||
func initDNSSettingsTestData() *DNSSettingsHandler {
|
||||
return &DNSSettingsHandler{
|
||||
func initDNSSettingsTestData() *dnsSettingsHandler {
|
||||
return &dnsSettingsHandler{
|
||||
accountManager: &mock_server.MockAccountManager{
|
||||
GetDNSSettingsFunc: func(ctx context.Context, accountID string, userID string) (*server.DNSSettings, error) {
|
||||
GetDNSSettingsFunc: func(ctx context.Context, accountID string, userID string) (*types.DNSSettings, error) {
|
||||
return &testingDNSSettingsAccount.DNSSettings, nil
|
||||
},
|
||||
SaveDNSSettingsFunc: func(ctx context.Context, accountID string, userID string, dnsSettingsToSave *server.DNSSettings) error {
|
||||
SaveDNSSettingsFunc: func(ctx context.Context, accountID string, userID string, dnsSettingsToSave *types.DNSSettings) error {
|
||||
if dnsSettingsToSave != nil {
|
||||
return nil
|
||||
}
|
||||
@@ -120,8 +120,8 @@ func TestDNSSettingsHandlers(t *testing.T) {
|
||||
req := httptest.NewRequest(tc.requestType, tc.requestPath, tc.requestBody)
|
||||
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/api/dns/settings", p.GetDNSSettings).Methods("GET")
|
||||
router.HandleFunc("/api/dns/settings", p.UpdateDNSSettings).Methods("PUT")
|
||||
router.HandleFunc("/api/dns/settings", p.getDNSSettings).Methods("GET")
|
||||
router.HandleFunc("/api/dns/settings", p.updateDNSSettings).Methods("PUT")
|
||||
router.ServeHTTP(recorder, req)
|
||||
|
||||
res := recorder.Result()
|
||||
+27
-17
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package dns
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -11,20 +11,30 @@ import (
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||
"github.com/netbirdio/netbird/management/server/http/util"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
)
|
||||
|
||||
// NameserversHandler is the nameserver group handler of the account
|
||||
type NameserversHandler struct {
|
||||
// nameserversHandler is the nameserver group handler of the account
|
||||
type nameserversHandler struct {
|
||||
accountManager server.AccountManager
|
||||
claimsExtractor *jwtclaims.ClaimsExtractor
|
||||
}
|
||||
|
||||
// NewNameserversHandler returns a new instance of NameserversHandler handler
|
||||
func NewNameserversHandler(accountManager server.AccountManager, authCfg AuthCfg) *NameserversHandler {
|
||||
return &NameserversHandler{
|
||||
func addDNSNameserversEndpoint(accountManager server.AccountManager, authCfg configs.AuthCfg, router *mux.Router) {
|
||||
nameserversHandler := newNameserversHandler(accountManager, authCfg)
|
||||
router.HandleFunc("/dns/nameservers", nameserversHandler.getAllNameservers).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/dns/nameservers", nameserversHandler.createNameserverGroup).Methods("POST", "OPTIONS")
|
||||
router.HandleFunc("/dns/nameservers/{nsgroupId}", nameserversHandler.updateNameserverGroup).Methods("PUT", "OPTIONS")
|
||||
router.HandleFunc("/dns/nameservers/{nsgroupId}", nameserversHandler.getNameserverGroup).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/dns/nameservers/{nsgroupId}", nameserversHandler.deleteNameserverGroup).Methods("DELETE", "OPTIONS")
|
||||
}
|
||||
|
||||
// newNameserversHandler returns a new instance of nameserversHandler handler
|
||||
func newNameserversHandler(accountManager server.AccountManager, authCfg configs.AuthCfg) *nameserversHandler {
|
||||
return &nameserversHandler{
|
||||
accountManager: accountManager,
|
||||
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||
jwtclaims.WithAudience(authCfg.Audience),
|
||||
@@ -33,8 +43,8 @@ func NewNameserversHandler(accountManager server.AccountManager, authCfg AuthCfg
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllNameservers returns the list of nameserver groups for the account
|
||||
func (h *NameserversHandler) GetAllNameservers(w http.ResponseWriter, r *http.Request) {
|
||||
// getAllNameservers returns the list of nameserver groups for the account
|
||||
func (h *nameserversHandler) getAllNameservers(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -57,8 +67,8 @@ func (h *NameserversHandler) GetAllNameservers(w http.ResponseWriter, r *http.Re
|
||||
util.WriteJSONObject(r.Context(), w, apiNameservers)
|
||||
}
|
||||
|
||||
// CreateNameserverGroup handles nameserver group creation request
|
||||
func (h *NameserversHandler) CreateNameserverGroup(w http.ResponseWriter, r *http.Request) {
|
||||
// createNameserverGroup handles nameserver group creation request
|
||||
func (h *nameserversHandler) createNameserverGroup(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -90,8 +100,8 @@ func (h *NameserversHandler) CreateNameserverGroup(w http.ResponseWriter, r *htt
|
||||
util.WriteJSONObject(r.Context(), w, &resp)
|
||||
}
|
||||
|
||||
// UpdateNameserverGroup handles update to a nameserver group identified by a given ID
|
||||
func (h *NameserversHandler) UpdateNameserverGroup(w http.ResponseWriter, r *http.Request) {
|
||||
// updateNameserverGroup handles update to a nameserver group identified by a given ID
|
||||
func (h *nameserversHandler) updateNameserverGroup(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -141,8 +151,8 @@ func (h *NameserversHandler) UpdateNameserverGroup(w http.ResponseWriter, r *htt
|
||||
util.WriteJSONObject(r.Context(), w, &resp)
|
||||
}
|
||||
|
||||
// DeleteNameserverGroup handles nameserver group deletion request
|
||||
func (h *NameserversHandler) DeleteNameserverGroup(w http.ResponseWriter, r *http.Request) {
|
||||
// deleteNameserverGroup handles nameserver group deletion request
|
||||
func (h *nameserversHandler) deleteNameserverGroup(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -162,11 +172,11 @@ func (h *NameserversHandler) DeleteNameserverGroup(w http.ResponseWriter, r *htt
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, emptyObject{})
|
||||
util.WriteJSONObject(r.Context(), w, util.EmptyObject{})
|
||||
}
|
||||
|
||||
// GetNameserverGroup handles a nameserver group Get request identified by ID
|
||||
func (h *NameserversHandler) GetNameserverGroup(w http.ResponseWriter, r *http.Request) {
|
||||
// getNameserverGroup handles a nameserver group Get request identified by ID
|
||||
func (h *nameserversHandler) getNameserverGroup(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
+7
-7
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package dns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -50,8 +50,8 @@ var baseExistingNSGroup = &nbdns.NameServerGroup{
|
||||
Enabled: true,
|
||||
}
|
||||
|
||||
func initNameserversTestData() *NameserversHandler {
|
||||
return &NameserversHandler{
|
||||
func initNameserversTestData() *nameserversHandler {
|
||||
return &nameserversHandler{
|
||||
accountManager: &mock_server.MockAccountManager{
|
||||
GetNameServerGroupFunc: func(_ context.Context, accountID, userID, nsGroupID string) (*nbdns.NameServerGroup, error) {
|
||||
if nsGroupID == existingNSGroupID {
|
||||
@@ -206,10 +206,10 @@ func TestNameserversHandlers(t *testing.T) {
|
||||
req := httptest.NewRequest(tc.requestType, tc.requestPath, tc.requestBody)
|
||||
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/api/dns/nameservers/{nsgroupId}", p.GetNameserverGroup).Methods("GET")
|
||||
router.HandleFunc("/api/dns/nameservers", p.CreateNameserverGroup).Methods("POST")
|
||||
router.HandleFunc("/api/dns/nameservers/{nsgroupId}", p.DeleteNameserverGroup).Methods("DELETE")
|
||||
router.HandleFunc("/api/dns/nameservers/{nsgroupId}", p.UpdateNameserverGroup).Methods("PUT")
|
||||
router.HandleFunc("/api/dns/nameservers/{nsgroupId}", p.getNameserverGroup).Methods("GET")
|
||||
router.HandleFunc("/api/dns/nameservers", p.createNameserverGroup).Methods("POST")
|
||||
router.HandleFunc("/api/dns/nameservers/{nsgroupId}", p.deleteNameserverGroup).Methods("DELETE")
|
||||
router.HandleFunc("/api/dns/nameservers/{nsgroupId}", p.updateNameserverGroup).Methods("PUT")
|
||||
router.ServeHTTP(recorder, req)
|
||||
|
||||
res := recorder.Result()
|
||||
+16
-9
@@ -1,28 +1,35 @@
|
||||
package http
|
||||
package events
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"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/configs"
|
||||
"github.com/netbirdio/netbird/management/server/http/util"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
)
|
||||
|
||||
// EventsHandler HTTP handler
|
||||
type EventsHandler struct {
|
||||
// handler HTTP handler
|
||||
type handler struct {
|
||||
accountManager server.AccountManager
|
||||
claimsExtractor *jwtclaims.ClaimsExtractor
|
||||
}
|
||||
|
||||
// NewEventsHandler creates a new EventsHandler HTTP handler
|
||||
func NewEventsHandler(accountManager server.AccountManager, authCfg AuthCfg) *EventsHandler {
|
||||
return &EventsHandler{
|
||||
func AddEndpoints(accountManager server.AccountManager, authCfg configs.AuthCfg, router *mux.Router) {
|
||||
eventsHandler := newHandler(accountManager, authCfg)
|
||||
router.HandleFunc("/events", eventsHandler.getAllEvents).Methods("GET", "OPTIONS")
|
||||
}
|
||||
|
||||
// newHandler creates a new events handler
|
||||
func newHandler(accountManager server.AccountManager, authCfg configs.AuthCfg) *handler {
|
||||
return &handler{
|
||||
accountManager: accountManager,
|
||||
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||
jwtclaims.WithAudience(authCfg.Audience),
|
||||
@@ -31,8 +38,8 @@ func NewEventsHandler(accountManager server.AccountManager, authCfg AuthCfg) *Ev
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllEvents list of the given account
|
||||
func (h *EventsHandler) GetAllEvents(w http.ResponseWriter, r *http.Request) {
|
||||
// getAllEvents list of the given account
|
||||
func (h *handler) getAllEvents(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -60,7 +67,7 @@ func (h *EventsHandler) GetAllEvents(w http.ResponseWriter, r *http.Request) {
|
||||
util.WriteJSONObject(r.Context(), w, events)
|
||||
}
|
||||
|
||||
func (h *EventsHandler) fillEventsWithUserInfo(ctx context.Context, events []*api.Event, accountId, userId string) error {
|
||||
func (h *handler) fillEventsWithUserInfo(ctx context.Context, events []*api.Event, accountId, userId string) error {
|
||||
// build email, name maps based on users
|
||||
userInfos, err := h.accountManager.GetUsersFromAccount(ctx, accountId, userId)
|
||||
if err != nil {
|
||||
+9
-9
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package events
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -13,15 +13,15 @@ import (
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"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/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
func initEventsTestData(account string, events ...*activity.Event) *EventsHandler {
|
||||
return &EventsHandler{
|
||||
func initEventsTestData(account string, events ...*activity.Event) *handler {
|
||||
return &handler{
|
||||
accountManager: &mock_server.MockAccountManager{
|
||||
GetEventsFunc: func(_ context.Context, accountID, userID string) ([]*activity.Event, error) {
|
||||
if accountID == account {
|
||||
@@ -32,8 +32,8 @@ func initEventsTestData(account string, events ...*activity.Event) *EventsHandle
|
||||
GetAccountIDFromTokenFunc: func(_ context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error) {
|
||||
return claims.AccountId, claims.UserId, nil
|
||||
},
|
||||
GetUsersFromAccountFunc: func(_ context.Context, accountID, userID string) ([]*server.UserInfo, error) {
|
||||
return make([]*server.UserInfo, 0), nil
|
||||
GetUsersFromAccountFunc: func(_ context.Context, accountID, userID string) ([]*types.UserInfo, error) {
|
||||
return make([]*types.UserInfo, 0), nil
|
||||
},
|
||||
},
|
||||
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||
@@ -183,7 +183,7 @@ func TestEvents_GetEvents(t *testing.T) {
|
||||
requestBody io.Reader
|
||||
}{
|
||||
{
|
||||
name: "GetAllEvents OK",
|
||||
name: "getAllEvents OK",
|
||||
expectedBody: true,
|
||||
requestType: http.MethodGet,
|
||||
requestPath: "/api/events/",
|
||||
@@ -191,7 +191,7 @@ func TestEvents_GetEvents(t *testing.T) {
|
||||
},
|
||||
}
|
||||
accountID := "test_account"
|
||||
adminUser := server.NewAdminUser("test_user")
|
||||
adminUser := types.NewAdminUser("test_user")
|
||||
events := generateEvents(accountID, adminUser.Id)
|
||||
handler := initEventsTestData(accountID, events...)
|
||||
|
||||
@@ -201,7 +201,7 @@ func TestEvents_GetEvents(t *testing.T) {
|
||||
req := httptest.NewRequest(tc.requestType, tc.requestPath, tc.requestBody)
|
||||
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/api/events/", handler.GetAllEvents).Methods("GET")
|
||||
router.HandleFunc("/api/events/", handler.getAllEvents).Methods("GET")
|
||||
router.ServeHTTP(recorder, req)
|
||||
|
||||
res := recorder.Result()
|
||||
+68
-28
@@ -1,30 +1,41 @@
|
||||
package http
|
||||
package groups
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
nbgroup "github.com/netbirdio/netbird/management/server/group"
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/http/util"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
)
|
||||
|
||||
// GroupsHandler is a handler that returns groups of the account
|
||||
type GroupsHandler struct {
|
||||
// handler is a handler that returns groups of the account
|
||||
type handler struct {
|
||||
accountManager server.AccountManager
|
||||
claimsExtractor *jwtclaims.ClaimsExtractor
|
||||
}
|
||||
|
||||
// NewGroupsHandler creates a new GroupsHandler HTTP handler
|
||||
func NewGroupsHandler(accountManager server.AccountManager, authCfg AuthCfg) *GroupsHandler {
|
||||
return &GroupsHandler{
|
||||
func AddEndpoints(accountManager server.AccountManager, authCfg configs.AuthCfg, router *mux.Router) {
|
||||
groupsHandler := newHandler(accountManager, authCfg)
|
||||
router.HandleFunc("/groups", groupsHandler.getAllGroups).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/groups", groupsHandler.createGroup).Methods("POST", "OPTIONS")
|
||||
router.HandleFunc("/groups/{groupId}", groupsHandler.updateGroup).Methods("PUT", "OPTIONS")
|
||||
router.HandleFunc("/groups/{groupId}", groupsHandler.getGroup).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/groups/{groupId}", groupsHandler.deleteGroup).Methods("DELETE", "OPTIONS")
|
||||
}
|
||||
|
||||
// newHandler creates a new groups handler
|
||||
func newHandler(accountManager server.AccountManager, authCfg configs.AuthCfg) *handler {
|
||||
return &handler{
|
||||
accountManager: accountManager,
|
||||
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||
jwtclaims.WithAudience(authCfg.Audience),
|
||||
@@ -33,8 +44,8 @@ func NewGroupsHandler(accountManager server.AccountManager, authCfg AuthCfg) *Gr
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllGroups list for the account
|
||||
func (h *GroupsHandler) GetAllGroups(w http.ResponseWriter, r *http.Request) {
|
||||
// getAllGroups list for the account
|
||||
func (h *handler) getAllGroups(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -63,8 +74,8 @@ func (h *GroupsHandler) GetAllGroups(w http.ResponseWriter, r *http.Request) {
|
||||
util.WriteJSONObject(r.Context(), w, groupsResponse)
|
||||
}
|
||||
|
||||
// UpdateGroup handles update to a group identified by a given ID
|
||||
func (h *GroupsHandler) UpdateGroup(w http.ResponseWriter, r *http.Request) {
|
||||
// updateGroup handles update to a group identified by a given ID
|
||||
func (h *handler) updateGroup(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -118,10 +129,21 @@ func (h *GroupsHandler) UpdateGroup(w http.ResponseWriter, r *http.Request) {
|
||||
} else {
|
||||
peers = *req.Peers
|
||||
}
|
||||
group := nbgroup.Group{
|
||||
|
||||
resources := make([]types.Resource, 0)
|
||||
if req.Resources != nil {
|
||||
for _, res := range *req.Resources {
|
||||
resource := types.Resource{}
|
||||
resource.FromAPIRequest(&res)
|
||||
resources = append(resources, resource)
|
||||
}
|
||||
}
|
||||
|
||||
group := types.Group{
|
||||
ID: groupID,
|
||||
Name: req.Name,
|
||||
Peers: peers,
|
||||
Resources: resources,
|
||||
Issued: existingGroup.Issued,
|
||||
IntegrationReference: existingGroup.IntegrationReference,
|
||||
}
|
||||
@@ -141,8 +163,8 @@ func (h *GroupsHandler) UpdateGroup(w http.ResponseWriter, r *http.Request) {
|
||||
util.WriteJSONObject(r.Context(), w, toGroupResponse(accountPeers, &group))
|
||||
}
|
||||
|
||||
// CreateGroup handles group creation request
|
||||
func (h *GroupsHandler) CreateGroup(w http.ResponseWriter, r *http.Request) {
|
||||
// createGroup handles group creation request
|
||||
func (h *handler) createGroup(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -168,10 +190,21 @@ func (h *GroupsHandler) CreateGroup(w http.ResponseWriter, r *http.Request) {
|
||||
} else {
|
||||
peers = *req.Peers
|
||||
}
|
||||
group := nbgroup.Group{
|
||||
Name: req.Name,
|
||||
Peers: peers,
|
||||
Issued: nbgroup.GroupIssuedAPI,
|
||||
|
||||
resources := make([]types.Resource, 0)
|
||||
if req.Resources != nil {
|
||||
for _, res := range *req.Resources {
|
||||
resource := types.Resource{}
|
||||
resource.FromAPIRequest(&res)
|
||||
resources = append(resources, resource)
|
||||
}
|
||||
}
|
||||
|
||||
group := types.Group{
|
||||
Name: req.Name,
|
||||
Peers: peers,
|
||||
Resources: resources,
|
||||
Issued: types.GroupIssuedAPI,
|
||||
}
|
||||
|
||||
err = h.accountManager.SaveGroup(r.Context(), accountID, userID, &group)
|
||||
@@ -189,8 +222,8 @@ func (h *GroupsHandler) CreateGroup(w http.ResponseWriter, r *http.Request) {
|
||||
util.WriteJSONObject(r.Context(), w, toGroupResponse(accountPeers, &group))
|
||||
}
|
||||
|
||||
// DeleteGroup handles group deletion request
|
||||
func (h *GroupsHandler) DeleteGroup(w http.ResponseWriter, r *http.Request) {
|
||||
// deleteGroup handles group deletion request
|
||||
func (h *handler) deleteGroup(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -215,11 +248,11 @@ func (h *GroupsHandler) DeleteGroup(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, emptyObject{})
|
||||
util.WriteJSONObject(r.Context(), w, util.EmptyObject{})
|
||||
}
|
||||
|
||||
// GetGroup returns a group
|
||||
func (h *GroupsHandler) GetGroup(w http.ResponseWriter, r *http.Request) {
|
||||
// getGroup returns a group
|
||||
func (h *handler) getGroup(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -248,13 +281,13 @@ func (h *GroupsHandler) GetGroup(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
}
|
||||
|
||||
func toGroupResponse(peers []*nbpeer.Peer, group *nbgroup.Group) *api.Group {
|
||||
func toGroupResponse(peers []*nbpeer.Peer, group *types.Group) *api.Group {
|
||||
peersMap := make(map[string]*nbpeer.Peer, len(peers))
|
||||
for _, peer := range peers {
|
||||
peersMap[peer.ID] = peer
|
||||
}
|
||||
|
||||
cache := make(map[string]api.PeerMinimum)
|
||||
peerCache := make(map[string]api.PeerMinimum)
|
||||
gr := api.Group{
|
||||
Id: group.ID,
|
||||
Name: group.Name,
|
||||
@@ -262,7 +295,7 @@ func toGroupResponse(peers []*nbpeer.Peer, group *nbgroup.Group) *api.Group {
|
||||
}
|
||||
|
||||
for _, pid := range group.Peers {
|
||||
_, ok := cache[pid]
|
||||
_, ok := peerCache[pid]
|
||||
if !ok {
|
||||
peer, ok := peersMap[pid]
|
||||
if !ok {
|
||||
@@ -272,12 +305,19 @@ func toGroupResponse(peers []*nbpeer.Peer, group *nbgroup.Group) *api.Group {
|
||||
Id: peer.ID,
|
||||
Name: peer.Name,
|
||||
}
|
||||
cache[pid] = peerResp
|
||||
peerCache[pid] = peerResp
|
||||
gr.Peers = append(gr.Peers, peerResp)
|
||||
}
|
||||
}
|
||||
|
||||
gr.PeersCount = len(gr.Peers)
|
||||
|
||||
for _, res := range group.Resources {
|
||||
resResp := res.ToAPIResponse()
|
||||
gr.Resources = append(gr.Resources, *resResp)
|
||||
}
|
||||
|
||||
gr.ResourcesCount = len(gr.Resources)
|
||||
|
||||
return &gr
|
||||
}
|
||||
+20
-20
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package groups
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -17,13 +17,13 @@ import (
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
nbgroup "github.com/netbirdio/netbird/management/server/group"
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/http/util"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
"github.com/netbirdio/netbird/management/server/mock_server"
|
||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
var TestPeers = map[string]*nbpeer.Peer{
|
||||
@@ -31,20 +31,20 @@ var TestPeers = map[string]*nbpeer.Peer{
|
||||
"B": {Key: "B", ID: "peer-B-ID", IP: net.ParseIP("200.200.200.200")},
|
||||
}
|
||||
|
||||
func initGroupTestData(initGroups ...*nbgroup.Group) *GroupsHandler {
|
||||
return &GroupsHandler{
|
||||
func initGroupTestData(initGroups ...*types.Group) *handler {
|
||||
return &handler{
|
||||
accountManager: &mock_server.MockAccountManager{
|
||||
SaveGroupFunc: func(_ context.Context, accountID, userID string, group *nbgroup.Group) error {
|
||||
SaveGroupFunc: func(_ context.Context, accountID, userID string, group *types.Group) error {
|
||||
if !strings.HasPrefix(group.ID, "id-") {
|
||||
group.ID = "id-was-set"
|
||||
}
|
||||
return nil
|
||||
},
|
||||
GetGroupFunc: func(_ context.Context, _, groupID, _ string) (*nbgroup.Group, error) {
|
||||
groups := map[string]*nbgroup.Group{
|
||||
"id-jwt-group": {ID: "id-jwt-group", Name: "From JWT", Issued: nbgroup.GroupIssuedJWT},
|
||||
"id-existed": {ID: "id-existed", Peers: []string{"A", "B"}, Issued: nbgroup.GroupIssuedAPI},
|
||||
"id-all": {ID: "id-all", Name: "All", Issued: nbgroup.GroupIssuedAPI},
|
||||
GetGroupFunc: func(_ context.Context, _, groupID, _ string) (*types.Group, error) {
|
||||
groups := map[string]*types.Group{
|
||||
"id-jwt-group": {ID: "id-jwt-group", Name: "From JWT", Issued: types.GroupIssuedJWT},
|
||||
"id-existed": {ID: "id-existed", Peers: []string{"A", "B"}, Issued: types.GroupIssuedAPI},
|
||||
"id-all": {ID: "id-all", Name: "All", Issued: types.GroupIssuedAPI},
|
||||
}
|
||||
|
||||
for _, group := range initGroups {
|
||||
@@ -61,9 +61,9 @@ func initGroupTestData(initGroups ...*nbgroup.Group) *GroupsHandler {
|
||||
GetAccountIDFromTokenFunc: func(_ context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error) {
|
||||
return claims.AccountId, claims.UserId, nil
|
||||
},
|
||||
GetGroupByNameFunc: func(ctx context.Context, groupName, _ string) (*nbgroup.Group, error) {
|
||||
GetGroupByNameFunc: func(ctx context.Context, groupName, _ string) (*types.Group, error) {
|
||||
if groupName == "All" {
|
||||
return &nbgroup.Group{ID: "id-all", Name: "All", Issued: nbgroup.GroupIssuedAPI}, nil
|
||||
return &types.Group{ID: "id-all", Name: "All", Issued: types.GroupIssuedAPI}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown group name")
|
||||
@@ -106,21 +106,21 @@ func TestGetGroup(t *testing.T) {
|
||||
requestBody io.Reader
|
||||
}{
|
||||
{
|
||||
name: "GetGroup OK",
|
||||
name: "getGroup OK",
|
||||
expectedBody: true,
|
||||
requestType: http.MethodGet,
|
||||
requestPath: "/api/groups/idofthegroup",
|
||||
expectedStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "GetGroup not found",
|
||||
name: "getGroup not found",
|
||||
requestType: http.MethodGet,
|
||||
requestPath: "/api/groups/notexists",
|
||||
expectedStatus: http.StatusNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
group := &nbgroup.Group{
|
||||
group := &types.Group{
|
||||
ID: "idofthegroup",
|
||||
Name: "Group",
|
||||
}
|
||||
@@ -133,7 +133,7 @@ func TestGetGroup(t *testing.T) {
|
||||
req := httptest.NewRequest(tc.requestType, tc.requestPath, tc.requestBody)
|
||||
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/api/groups/{groupId}", p.GetGroup).Methods("GET")
|
||||
router.HandleFunc("/api/groups/{groupId}", p.getGroup).Methods("GET")
|
||||
router.ServeHTTP(recorder, req)
|
||||
|
||||
res := recorder.Result()
|
||||
@@ -154,7 +154,7 @@ func TestGetGroup(t *testing.T) {
|
||||
t.Fatalf("I don't know what I expected; %v", err)
|
||||
}
|
||||
|
||||
got := &nbgroup.Group{}
|
||||
got := &types.Group{}
|
||||
if err = json.Unmarshal(content, &got); err != nil {
|
||||
t.Fatalf("Sent content is not in correct json format; %v", err)
|
||||
}
|
||||
@@ -254,8 +254,8 @@ func TestWriteGroup(t *testing.T) {
|
||||
req := httptest.NewRequest(tc.requestType, tc.requestPath, tc.requestBody)
|
||||
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/api/groups", p.CreateGroup).Methods("POST")
|
||||
router.HandleFunc("/api/groups/{groupId}", p.UpdateGroup).Methods("PUT")
|
||||
router.HandleFunc("/api/groups", p.createGroup).Methods("POST")
|
||||
router.HandleFunc("/api/groups/{groupId}", p.updateGroup).Methods("PUT")
|
||||
router.ServeHTTP(recorder, req)
|
||||
|
||||
res := recorder.Result()
|
||||
@@ -331,7 +331,7 @@ func TestDeleteGroup(t *testing.T) {
|
||||
req := httptest.NewRequest(tc.requestType, tc.requestPath, nil)
|
||||
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/api/groups/{groupId}", p.DeleteGroup).Methods("DELETE")
|
||||
router.HandleFunc("/api/groups/{groupId}", p.deleteGroup).Methods("DELETE")
|
||||
router.ServeHTTP(recorder, req)
|
||||
|
||||
res := recorder.Result()
|
||||
@@ -0,0 +1,321 @@
|
||||
package networks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
s "github.com/netbirdio/netbird/management/server"
|
||||
"github.com/netbirdio/netbird/management/server/groups"
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||
"github.com/netbirdio/netbird/management/server/http/util"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
"github.com/netbirdio/netbird/management/server/networks"
|
||||
"github.com/netbirdio/netbird/management/server/networks/resources"
|
||||
"github.com/netbirdio/netbird/management/server/networks/routers"
|
||||
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
|
||||
"github.com/netbirdio/netbird/management/server/networks/types"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
nbtypes "github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
// handler is a handler that returns networks of the account
|
||||
type handler struct {
|
||||
networksManager networks.Manager
|
||||
resourceManager resources.Manager
|
||||
routerManager routers.Manager
|
||||
accountManager s.AccountManager
|
||||
|
||||
groupsManager groups.Manager
|
||||
extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error)
|
||||
claimsExtractor *jwtclaims.ClaimsExtractor
|
||||
}
|
||||
|
||||
func AddEndpoints(networksManager networks.Manager, resourceManager resources.Manager, routerManager routers.Manager, groupsManager groups.Manager, accountManager s.AccountManager, extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error), authCfg configs.AuthCfg, router *mux.Router) {
|
||||
addRouterEndpoints(routerManager, extractFromToken, authCfg, router)
|
||||
addResourceEndpoints(resourceManager, groupsManager, extractFromToken, authCfg, router)
|
||||
|
||||
networksHandler := newHandler(networksManager, resourceManager, routerManager, groupsManager, accountManager, extractFromToken, authCfg)
|
||||
router.HandleFunc("/networks", networksHandler.getAllNetworks).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/networks", networksHandler.createNetwork).Methods("POST", "OPTIONS")
|
||||
router.HandleFunc("/networks/{networkId}", networksHandler.getNetwork).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/networks/{networkId}", networksHandler.updateNetwork).Methods("PUT", "OPTIONS")
|
||||
router.HandleFunc("/networks/{networkId}", networksHandler.deleteNetwork).Methods("DELETE", "OPTIONS")
|
||||
}
|
||||
|
||||
func newHandler(networksManager networks.Manager, resourceManager resources.Manager, routerManager routers.Manager, groupsManager groups.Manager, accountManager s.AccountManager, extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error), authCfg configs.AuthCfg) *handler {
|
||||
return &handler{
|
||||
networksManager: networksManager,
|
||||
resourceManager: resourceManager,
|
||||
routerManager: routerManager,
|
||||
groupsManager: groupsManager,
|
||||
accountManager: accountManager,
|
||||
extractFromToken: extractFromToken,
|
||||
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||
jwtclaims.WithAudience(authCfg.Audience),
|
||||
jwtclaims.WithUserIDClaim(authCfg.UserIDClaim),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) getAllNetworks(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
networks, err := h.networksManager.GetAllNetworks(r.Context(), accountID, userID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
resourceIDs, err := h.resourceManager.GetAllResourceIDsInAccount(r.Context(), accountID, userID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
groups, err := h.groupsManager.GetAllGroups(r.Context(), accountID, userID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
routers, err := h.routerManager.GetAllRoutersInAccount(r.Context(), accountID, userID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
account, err := h.accountManager.GetAccount(r.Context(), accountID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, h.generateNetworkResponse(networks, routers, resourceIDs, groups, account))
|
||||
}
|
||||
|
||||
func (h *handler) createNetwork(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
var req api.NetworkRequest
|
||||
err = json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
||||
return
|
||||
}
|
||||
|
||||
network := &types.Network{}
|
||||
network.FromAPIRequest(&req)
|
||||
|
||||
network.AccountID = accountID
|
||||
network, err = h.networksManager.CreateNetwork(r.Context(), userID, network)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
account, err := h.accountManager.GetAccount(r.Context(), accountID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
policyIDs := account.GetPoliciesAppliedInNetwork(network.ID)
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, network.ToAPIResponse([]string{}, []string{}, 0, policyIDs))
|
||||
}
|
||||
|
||||
func (h *handler) getNetwork(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
networkID := vars["networkId"]
|
||||
if len(networkID) == 0 {
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "invalid network ID"), w)
|
||||
return
|
||||
}
|
||||
|
||||
network, err := h.networksManager.GetNetwork(r.Context(), accountID, userID, networkID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
routerIDs, resourceIDs, peerCount, err := h.collectIDsInNetwork(r.Context(), accountID, userID, networkID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
account, err := h.accountManager.GetAccount(r.Context(), accountID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
policyIDs := account.GetPoliciesAppliedInNetwork(networkID)
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, network.ToAPIResponse(routerIDs, resourceIDs, peerCount, policyIDs))
|
||||
}
|
||||
|
||||
func (h *handler) updateNetwork(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
networkID := vars["networkId"]
|
||||
if len(networkID) == 0 {
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "invalid network ID"), w)
|
||||
return
|
||||
}
|
||||
|
||||
var req api.NetworkRequest
|
||||
err = json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
||||
return
|
||||
}
|
||||
|
||||
network := &types.Network{}
|
||||
network.FromAPIRequest(&req)
|
||||
|
||||
network.ID = networkID
|
||||
network.AccountID = accountID
|
||||
network, err = h.networksManager.UpdateNetwork(r.Context(), userID, network)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
routerIDs, resourceIDs, peerCount, err := h.collectIDsInNetwork(r.Context(), accountID, userID, networkID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
account, err := h.accountManager.GetAccount(r.Context(), accountID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
policyIDs := account.GetPoliciesAppliedInNetwork(networkID)
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, network.ToAPIResponse(routerIDs, resourceIDs, peerCount, policyIDs))
|
||||
}
|
||||
|
||||
func (h *handler) deleteNetwork(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
networkID := vars["networkId"]
|
||||
if len(networkID) == 0 {
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "invalid network ID"), w)
|
||||
return
|
||||
}
|
||||
|
||||
err = h.networksManager.DeleteNetwork(r.Context(), accountID, userID, networkID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, util.EmptyObject{})
|
||||
}
|
||||
|
||||
func (h *handler) collectIDsInNetwork(ctx context.Context, accountID, userID, networkID string) ([]string, []string, int, error) {
|
||||
resources, err := h.resourceManager.GetAllResourcesInNetwork(ctx, accountID, userID, networkID)
|
||||
if err != nil {
|
||||
return nil, nil, 0, fmt.Errorf("failed to get resources in network: %w", err)
|
||||
}
|
||||
|
||||
var resourceIDs []string
|
||||
for _, resource := range resources {
|
||||
resourceIDs = append(resourceIDs, resource.ID)
|
||||
}
|
||||
|
||||
routers, err := h.routerManager.GetAllRoutersInNetwork(ctx, accountID, userID, networkID)
|
||||
if err != nil {
|
||||
return nil, nil, 0, fmt.Errorf("failed to get routers in network: %w", err)
|
||||
}
|
||||
|
||||
groups, err := h.groupsManager.GetAllGroups(ctx, accountID, userID)
|
||||
if err != nil {
|
||||
return nil, nil, 0, fmt.Errorf("failed to get groups: %w", err)
|
||||
}
|
||||
|
||||
peerCounter := 0
|
||||
var routerIDs []string
|
||||
for _, router := range routers {
|
||||
routerIDs = append(routerIDs, router.ID)
|
||||
if router.Peer != "" {
|
||||
peerCounter++
|
||||
}
|
||||
if len(router.PeerGroups) > 0 {
|
||||
for _, groupID := range router.PeerGroups {
|
||||
peerCounter += len(groups[groupID].Peers)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return routerIDs, resourceIDs, peerCounter, nil
|
||||
}
|
||||
|
||||
func (h *handler) generateNetworkResponse(networks []*types.Network, routers map[string][]*routerTypes.NetworkRouter, resourceIDs map[string][]string, groups map[string]*nbtypes.Group, account *nbtypes.Account) []*api.Network {
|
||||
var networkResponse []*api.Network
|
||||
for _, network := range networks {
|
||||
routerIDs, peerCounter := getRouterIDs(network, routers, groups)
|
||||
policyIDs := account.GetPoliciesAppliedInNetwork(network.ID)
|
||||
networkResponse = append(networkResponse, network.ToAPIResponse(routerIDs, resourceIDs[network.ID], peerCounter, policyIDs))
|
||||
}
|
||||
return networkResponse
|
||||
}
|
||||
|
||||
func getRouterIDs(network *types.Network, routers map[string][]*routerTypes.NetworkRouter, groups map[string]*nbtypes.Group) ([]string, int) {
|
||||
routerIDs := []string{}
|
||||
peerCounter := 0
|
||||
for _, router := range routers[network.ID] {
|
||||
routerIDs = append(routerIDs, router.ID)
|
||||
if router.Peer != "" {
|
||||
peerCounter++
|
||||
}
|
||||
if len(router.PeerGroups) > 0 {
|
||||
for _, groupID := range router.PeerGroups {
|
||||
group, ok := groups[groupID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
peerCounter += len(group.Peers)
|
||||
}
|
||||
}
|
||||
}
|
||||
return routerIDs, peerCounter
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
package networks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/groups"
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||
"github.com/netbirdio/netbird/management/server/http/util"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
"github.com/netbirdio/netbird/management/server/networks/resources"
|
||||
"github.com/netbirdio/netbird/management/server/networks/resources/types"
|
||||
)
|
||||
|
||||
type resourceHandler struct {
|
||||
resourceManager resources.Manager
|
||||
groupsManager groups.Manager
|
||||
extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error)
|
||||
claimsExtractor *jwtclaims.ClaimsExtractor
|
||||
}
|
||||
|
||||
func addResourceEndpoints(resourcesManager resources.Manager, groupsManager groups.Manager, extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error), authCfg configs.AuthCfg, router *mux.Router) {
|
||||
resourceHandler := newResourceHandler(resourcesManager, groupsManager, extractFromToken, authCfg)
|
||||
router.HandleFunc("/networks/resources", resourceHandler.getAllResourcesInAccount).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/networks/{networkId}/resources", resourceHandler.getAllResourcesInNetwork).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/networks/{networkId}/resources", resourceHandler.createResource).Methods("POST", "OPTIONS")
|
||||
router.HandleFunc("/networks/{networkId}/resources/{resourceId}", resourceHandler.getResource).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/networks/{networkId}/resources/{resourceId}", resourceHandler.updateResource).Methods("PUT", "OPTIONS")
|
||||
router.HandleFunc("/networks/{networkId}/resources/{resourceId}", resourceHandler.deleteResource).Methods("DELETE", "OPTIONS")
|
||||
}
|
||||
|
||||
func newResourceHandler(resourceManager resources.Manager, groupsManager groups.Manager, extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error), authCfg configs.AuthCfg) *resourceHandler {
|
||||
return &resourceHandler{
|
||||
resourceManager: resourceManager,
|
||||
groupsManager: groupsManager,
|
||||
extractFromToken: extractFromToken,
|
||||
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||
jwtclaims.WithAudience(authCfg.Audience),
|
||||
jwtclaims.WithUserIDClaim(authCfg.UserIDClaim),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *resourceHandler) getAllResourcesInNetwork(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
networkID := mux.Vars(r)["networkId"]
|
||||
resources, err := h.resourceManager.GetAllResourcesInNetwork(r.Context(), accountID, userID, networkID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
grps, err := h.groupsManager.GetAllGroups(r.Context(), accountID, userID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
var resourcesResponse []*api.NetworkResource
|
||||
for _, resource := range resources {
|
||||
groupMinimumInfo := groups.ToGroupsInfo(grps, resource.ID)
|
||||
resourcesResponse = append(resourcesResponse, resource.ToAPIResponse(groupMinimumInfo))
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, resourcesResponse)
|
||||
}
|
||||
func (h *resourceHandler) getAllResourcesInAccount(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
resources, err := h.resourceManager.GetAllResourcesInAccount(r.Context(), accountID, userID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
grps, err := h.groupsManager.GetAllGroups(r.Context(), accountID, userID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
var resourcesResponse []*api.NetworkResource
|
||||
for _, resource := range resources {
|
||||
groupMinimumInfo := groups.ToGroupsInfo(grps, resource.ID)
|
||||
resourcesResponse = append(resourcesResponse, resource.ToAPIResponse(groupMinimumInfo))
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, resourcesResponse)
|
||||
}
|
||||
|
||||
func (h *resourceHandler) createResource(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
var req api.NetworkResourceRequest
|
||||
err = json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
||||
return
|
||||
}
|
||||
|
||||
resource := &types.NetworkResource{}
|
||||
resource.FromAPIRequest(&req)
|
||||
|
||||
resource.NetworkID = mux.Vars(r)["networkId"]
|
||||
resource.AccountID = accountID
|
||||
resource, err = h.resourceManager.CreateResource(r.Context(), userID, resource)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
grps, err := h.groupsManager.GetAllGroups(r.Context(), accountID, userID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
groupMinimumInfo := groups.ToGroupsInfo(grps, resource.ID)
|
||||
util.WriteJSONObject(r.Context(), w, resource.ToAPIResponse(groupMinimumInfo))
|
||||
}
|
||||
|
||||
func (h *resourceHandler) getResource(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
networkID := mux.Vars(r)["networkId"]
|
||||
resourceID := mux.Vars(r)["resourceId"]
|
||||
resource, err := h.resourceManager.GetResource(r.Context(), accountID, userID, networkID, resourceID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
grps, err := h.groupsManager.GetAllGroups(r.Context(), accountID, userID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
groupMinimumInfo := groups.ToGroupsInfo(grps, resource.ID)
|
||||
util.WriteJSONObject(r.Context(), w, resource.ToAPIResponse(groupMinimumInfo))
|
||||
}
|
||||
|
||||
func (h *resourceHandler) updateResource(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
var req api.NetworkResourceRequest
|
||||
err = json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
||||
return
|
||||
}
|
||||
|
||||
resource := &types.NetworkResource{}
|
||||
resource.FromAPIRequest(&req)
|
||||
|
||||
resource.ID = mux.Vars(r)["resourceId"]
|
||||
resource.NetworkID = mux.Vars(r)["networkId"]
|
||||
resource.AccountID = accountID
|
||||
resource, err = h.resourceManager.UpdateResource(r.Context(), userID, resource)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
grps, err := h.groupsManager.GetAllGroups(r.Context(), accountID, userID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
groupMinimumInfo := groups.ToGroupsInfo(grps, resource.ID)
|
||||
util.WriteJSONObject(r.Context(), w, resource.ToAPIResponse(groupMinimumInfo))
|
||||
}
|
||||
|
||||
func (h *resourceHandler) deleteResource(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
networkID := mux.Vars(r)["networkId"]
|
||||
resourceID := mux.Vars(r)["resourceId"]
|
||||
err = h.resourceManager.DeleteResource(r.Context(), accountID, userID, networkID, resourceID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, util.EmptyObject{})
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
package networks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||
"github.com/netbirdio/netbird/management/server/http/util"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
"github.com/netbirdio/netbird/management/server/networks/routers"
|
||||
"github.com/netbirdio/netbird/management/server/networks/routers/types"
|
||||
)
|
||||
|
||||
type routersHandler struct {
|
||||
routersManager routers.Manager
|
||||
extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error)
|
||||
claimsExtractor *jwtclaims.ClaimsExtractor
|
||||
}
|
||||
|
||||
func addRouterEndpoints(routersManager routers.Manager, extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error), authCfg configs.AuthCfg, router *mux.Router) {
|
||||
routersHandler := newRoutersHandler(routersManager, extractFromToken, authCfg)
|
||||
router.HandleFunc("/networks/{networkId}/routers", routersHandler.getAllRouters).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/networks/{networkId}/routers", routersHandler.createRouter).Methods("POST", "OPTIONS")
|
||||
router.HandleFunc("/networks/{networkId}/routers/{routerId}", routersHandler.getRouter).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/networks/{networkId}/routers/{routerId}", routersHandler.updateRouter).Methods("PUT", "OPTIONS")
|
||||
router.HandleFunc("/networks/{networkId}/routers/{routerId}", routersHandler.deleteRouter).Methods("DELETE", "OPTIONS")
|
||||
}
|
||||
|
||||
func newRoutersHandler(routersManager routers.Manager, extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error), authCfg configs.AuthCfg) *routersHandler {
|
||||
return &routersHandler{
|
||||
routersManager: routersManager,
|
||||
extractFromToken: extractFromToken,
|
||||
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||
jwtclaims.WithAudience(authCfg.Audience),
|
||||
jwtclaims.WithUserIDClaim(authCfg.UserIDClaim),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *routersHandler) getAllRouters(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
networkID := mux.Vars(r)["networkId"]
|
||||
routers, err := h.routersManager.GetAllRoutersInNetwork(r.Context(), accountID, userID, networkID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
var routersResponse []*api.NetworkRouter
|
||||
for _, router := range routers {
|
||||
routersResponse = append(routersResponse, router.ToAPIResponse())
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, routersResponse)
|
||||
}
|
||||
|
||||
func (h *routersHandler) createRouter(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
networkID := mux.Vars(r)["networkId"]
|
||||
var req api.NetworkRouterRequest
|
||||
err = json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
||||
return
|
||||
}
|
||||
|
||||
router := &types.NetworkRouter{}
|
||||
router.FromAPIRequest(&req)
|
||||
|
||||
router.NetworkID = networkID
|
||||
router.AccountID = accountID
|
||||
|
||||
router, err = h.routersManager.CreateRouter(r.Context(), userID, router)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, router.ToAPIResponse())
|
||||
}
|
||||
|
||||
func (h *routersHandler) getRouter(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
routerID := mux.Vars(r)["routerId"]
|
||||
networkID := mux.Vars(r)["networkId"]
|
||||
router, err := h.routersManager.GetRouter(r.Context(), accountID, userID, networkID, routerID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, router.ToAPIResponse())
|
||||
}
|
||||
|
||||
func (h *routersHandler) updateRouter(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
var req api.NetworkRouterRequest
|
||||
err = json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
||||
return
|
||||
}
|
||||
|
||||
router := &types.NetworkRouter{}
|
||||
router.FromAPIRequest(&req)
|
||||
|
||||
router.NetworkID = mux.Vars(r)["networkId"]
|
||||
router.ID = mux.Vars(r)["routerId"]
|
||||
router.AccountID = accountID
|
||||
|
||||
router, err = h.routersManager.UpdateRouter(r.Context(), userID, router)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, router.ToAPIResponse())
|
||||
}
|
||||
|
||||
func (h *routersHandler) deleteRouter(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
routerID := mux.Vars(r)["routerId"]
|
||||
networkID := mux.Vars(r)["networkId"]
|
||||
err = h.routersManager.DeleteRouter(r.Context(), accountID, userID, networkID, routerID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, struct{}{})
|
||||
}
|
||||
+35
-26
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package peers
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -10,23 +10,33 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
nbgroup "github.com/netbirdio/netbird/management/server/group"
|
||||
"github.com/netbirdio/netbird/management/server/groups"
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||
"github.com/netbirdio/netbird/management/server/http/util"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
// PeersHandler is a handler that returns peers of the account
|
||||
type PeersHandler struct {
|
||||
// Handler is a handler that returns peers of the account
|
||||
type Handler struct {
|
||||
accountManager server.AccountManager
|
||||
claimsExtractor *jwtclaims.ClaimsExtractor
|
||||
}
|
||||
|
||||
// NewPeersHandler creates a new PeersHandler HTTP handler
|
||||
func NewPeersHandler(accountManager server.AccountManager, authCfg AuthCfg) *PeersHandler {
|
||||
return &PeersHandler{
|
||||
func AddEndpoints(accountManager server.AccountManager, authCfg configs.AuthCfg, router *mux.Router) {
|
||||
peersHandler := NewHandler(accountManager, authCfg)
|
||||
router.HandleFunc("/peers", peersHandler.GetAllPeers).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/peers/{peerId}", peersHandler.HandlePeer).
|
||||
Methods("GET", "PUT", "DELETE", "OPTIONS")
|
||||
router.HandleFunc("/peers/{peerId}/accessible-peers", peersHandler.GetAccessiblePeers).Methods("GET", "OPTIONS")
|
||||
}
|
||||
|
||||
// NewHandler creates a new peers Handler
|
||||
func NewHandler(accountManager server.AccountManager, authCfg configs.AuthCfg) *Handler {
|
||||
return &Handler{
|
||||
accountManager: accountManager,
|
||||
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||
jwtclaims.WithAudience(authCfg.Audience),
|
||||
@@ -35,7 +45,7 @@ func NewPeersHandler(accountManager server.AccountManager, authCfg AuthCfg) *Pee
|
||||
}
|
||||
}
|
||||
|
||||
func (h *PeersHandler) checkPeerStatus(peer *nbpeer.Peer) (*nbpeer.Peer, error) {
|
||||
func (h *Handler) checkPeerStatus(peer *nbpeer.Peer) (*nbpeer.Peer, error) {
|
||||
peerToReturn := peer.Copy()
|
||||
if peer.Status.Connected {
|
||||
// Although we have online status in store we do not yet have an updated channel so have to show it as disconnected
|
||||
@@ -48,7 +58,7 @@ func (h *PeersHandler) checkPeerStatus(peer *nbpeer.Peer) (*nbpeer.Peer, error)
|
||||
return peerToReturn, nil
|
||||
}
|
||||
|
||||
func (h *PeersHandler) getPeer(ctx context.Context, accountID, peerID, userID string, w http.ResponseWriter) {
|
||||
func (h *Handler) getPeer(ctx context.Context, accountID, peerID, userID string, w http.ResponseWriter) {
|
||||
peer, err := h.accountManager.GetPeer(ctx, accountID, peerID, userID)
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, w)
|
||||
@@ -62,8 +72,8 @@ func (h *PeersHandler) getPeer(ctx context.Context, accountID, peerID, userID st
|
||||
}
|
||||
dnsDomain := h.accountManager.GetDNSDomain()
|
||||
|
||||
groups, _ := h.accountManager.GetAllGroups(ctx, accountID, userID)
|
||||
groupsInfo := toGroupsInfo(groups, peerID)
|
||||
allGroups, _ := h.accountManager.GetAllGroups(ctx, accountID, userID)
|
||||
groupsInfo := groups.ToGroupsInfo(allGroups, peerID)
|
||||
|
||||
validPeers, err := h.accountManager.GetValidatedPeers(ctx, accountID)
|
||||
if err != nil {
|
||||
@@ -76,7 +86,7 @@ func (h *PeersHandler) getPeer(ctx context.Context, accountID, peerID, userID st
|
||||
util.WriteJSONObject(ctx, w, toSinglePeerResponse(peerToReturn, groupsInfo, dnsDomain, valid))
|
||||
}
|
||||
|
||||
func (h *PeersHandler) updatePeer(ctx context.Context, accountID, userID, peerID string, w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handler) updatePeer(ctx context.Context, accountID, userID, peerID string, w http.ResponseWriter, r *http.Request) {
|
||||
req := &api.PeerRequest{}
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
@@ -112,7 +122,7 @@ func (h *PeersHandler) updatePeer(ctx context.Context, accountID, userID, peerID
|
||||
util.WriteError(ctx, err, w)
|
||||
return
|
||||
}
|
||||
groupMinimumInfo := toGroupsInfo(peerGroups, peer.ID)
|
||||
groupMinimumInfo := groups.ToGroupsInfo(peerGroups, peer.ID)
|
||||
|
||||
validPeers, err := h.accountManager.GetValidatedPeers(ctx, accountID)
|
||||
if err != nil {
|
||||
@@ -126,18 +136,18 @@ func (h *PeersHandler) updatePeer(ctx context.Context, accountID, userID, peerID
|
||||
util.WriteJSONObject(r.Context(), w, toSinglePeerResponse(peer, groupMinimumInfo, dnsDomain, valid))
|
||||
}
|
||||
|
||||
func (h *PeersHandler) deletePeer(ctx context.Context, accountID, userID string, peerID string, w http.ResponseWriter) {
|
||||
func (h *Handler) deletePeer(ctx context.Context, accountID, userID string, peerID string, w http.ResponseWriter) {
|
||||
err := h.accountManager.DeletePeer(ctx, accountID, peerID, userID)
|
||||
if err != nil {
|
||||
log.WithContext(ctx).Errorf("failed to delete peer: %v", err)
|
||||
util.WriteError(ctx, err, w)
|
||||
return
|
||||
}
|
||||
util.WriteJSONObject(ctx, w, emptyObject{})
|
||||
util.WriteJSONObject(ctx, w, util.EmptyObject{})
|
||||
}
|
||||
|
||||
// HandlePeer handles all peer requests for GET, PUT and DELETE operations
|
||||
func (h *PeersHandler) HandlePeer(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handler) HandlePeer(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -167,7 +177,7 @@ func (h *PeersHandler) HandlePeer(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// GetAllPeers returns a list of all peers associated with a provided account
|
||||
func (h *PeersHandler) GetAllPeers(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handler) GetAllPeers(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -183,7 +193,7 @@ func (h *PeersHandler) GetAllPeers(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
dnsDomain := h.accountManager.GetDNSDomain()
|
||||
|
||||
groups, _ := h.accountManager.GetAllGroups(r.Context(), accountID, userID)
|
||||
allGroups, _ := h.accountManager.GetAllGroups(r.Context(), accountID, userID)
|
||||
|
||||
respBody := make([]*api.PeerBatch, 0, len(peers))
|
||||
for _, peer := range peers {
|
||||
@@ -192,8 +202,7 @@ func (h *PeersHandler) GetAllPeers(w http.ResponseWriter, r *http.Request) {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
groupMinimumInfo := toGroupsInfo(groups, peer.ID)
|
||||
groupMinimumInfo := groups.ToGroupsInfo(allGroups, peer.ID)
|
||||
|
||||
respBody = append(respBody, toPeerListItemResponse(peerToReturn, groupMinimumInfo, dnsDomain, 0))
|
||||
}
|
||||
@@ -209,7 +218,7 @@ func (h *PeersHandler) GetAllPeers(w http.ResponseWriter, r *http.Request) {
|
||||
util.WriteJSONObject(r.Context(), w, respBody)
|
||||
}
|
||||
|
||||
func (h *PeersHandler) setApprovalRequiredFlag(respBody []*api.PeerBatch, approvedPeersMap map[string]struct{}) {
|
||||
func (h *Handler) setApprovalRequiredFlag(respBody []*api.PeerBatch, approvedPeersMap map[string]struct{}) {
|
||||
for _, peer := range respBody {
|
||||
_, ok := approvedPeersMap[peer.Id]
|
||||
if !ok {
|
||||
@@ -219,7 +228,7 @@ func (h *PeersHandler) setApprovalRequiredFlag(respBody []*api.PeerBatch, approv
|
||||
}
|
||||
|
||||
// GetAccessiblePeers returns a list of all peers that the specified peer can connect to within the network.
|
||||
func (h *PeersHandler) GetAccessiblePeers(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handler) GetAccessiblePeers(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -271,12 +280,12 @@ func (h *PeersHandler) GetAccessiblePeers(w http.ResponseWriter, r *http.Request
|
||||
dnsDomain := h.accountManager.GetDNSDomain()
|
||||
|
||||
customZone := account.GetPeersCustomZone(r.Context(), dnsDomain)
|
||||
netMap := account.GetPeerNetworkMap(r.Context(), peerID, customZone, validPeers, nil)
|
||||
netMap := account.GetPeerNetworkMap(r.Context(), peerID, customZone, validPeers, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), nil)
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, toAccessiblePeers(netMap, dnsDomain))
|
||||
}
|
||||
|
||||
func toAccessiblePeers(netMap *server.NetworkMap, dnsDomain string) []api.AccessiblePeer {
|
||||
func toAccessiblePeers(netMap *types.NetworkMap, dnsDomain string) []api.AccessiblePeer {
|
||||
accessiblePeers := make([]api.AccessiblePeer, 0, len(netMap.Peers)+len(netMap.OfflinePeers))
|
||||
for _, p := range netMap.Peers {
|
||||
accessiblePeers = append(accessiblePeers, peerToAccessiblePeer(p, dnsDomain))
|
||||
@@ -355,7 +364,7 @@ func toSinglePeerResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dnsD
|
||||
UiVersion: peer.Meta.UIVersion,
|
||||
DnsLabel: fqdn(peer, dnsDomain),
|
||||
LoginExpirationEnabled: peer.LoginExpirationEnabled,
|
||||
LastLogin: peer.LastLogin,
|
||||
LastLogin: peer.GetLastLogin(),
|
||||
LoginExpired: peer.Status.LoginExpired,
|
||||
ApprovalRequired: !approved,
|
||||
CountryCode: peer.Location.CountryCode,
|
||||
@@ -389,7 +398,7 @@ func toPeerListItemResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dn
|
||||
UiVersion: peer.Meta.UIVersion,
|
||||
DnsLabel: fqdn(peer, dnsDomain),
|
||||
LoginExpirationEnabled: peer.LoginExpirationEnabled,
|
||||
LastLogin: peer.LastLogin,
|
||||
LastLogin: peer.GetLastLogin(),
|
||||
LoginExpired: peer.Status.LoginExpired,
|
||||
AccessiblePeersCount: accessiblePeersCount,
|
||||
CountryCode: peer.Location.CountryCode,
|
||||
+15
-16
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package peers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -15,11 +15,10 @@ import (
|
||||
"github.com/gorilla/mux"
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
nbgroup "github.com/netbirdio/netbird/management/server/group"
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -38,19 +37,19 @@ const (
|
||||
userIDKey ctxKey = "user_id"
|
||||
)
|
||||
|
||||
func initTestMetaData(peers ...*nbpeer.Peer) *PeersHandler {
|
||||
func initTestMetaData(peers ...*nbpeer.Peer) *Handler {
|
||||
|
||||
peersMap := make(map[string]*nbpeer.Peer)
|
||||
for _, peer := range peers {
|
||||
peersMap[peer.ID] = peer.Copy()
|
||||
}
|
||||
|
||||
policy := &server.Policy{
|
||||
policy := &types.Policy{
|
||||
ID: "policy",
|
||||
AccountID: "test_id",
|
||||
Name: "policy",
|
||||
Enabled: true,
|
||||
Rules: []*server.PolicyRule{
|
||||
Rules: []*types.PolicyRule{
|
||||
{
|
||||
ID: "rule",
|
||||
Name: "rule",
|
||||
@@ -65,19 +64,19 @@ func initTestMetaData(peers ...*nbpeer.Peer) *PeersHandler {
|
||||
},
|
||||
}
|
||||
|
||||
srvUser := server.NewRegularUser(serviceUser)
|
||||
srvUser := types.NewRegularUser(serviceUser)
|
||||
srvUser.IsServiceUser = true
|
||||
|
||||
account := &server.Account{
|
||||
account := &types.Account{
|
||||
Id: "test_id",
|
||||
Domain: "hotmail.com",
|
||||
Peers: peersMap,
|
||||
Users: map[string]*server.User{
|
||||
adminUser: server.NewAdminUser(adminUser),
|
||||
regularUser: server.NewRegularUser(regularUser),
|
||||
Users: map[string]*types.User{
|
||||
adminUser: types.NewAdminUser(adminUser),
|
||||
regularUser: types.NewRegularUser(regularUser),
|
||||
serviceUser: srvUser,
|
||||
},
|
||||
Groups: map[string]*nbgroup.Group{
|
||||
Groups: map[string]*types.Group{
|
||||
"group1": {
|
||||
ID: "group1",
|
||||
AccountID: "test_id",
|
||||
@@ -86,12 +85,12 @@ func initTestMetaData(peers ...*nbpeer.Peer) *PeersHandler {
|
||||
Peers: maps.Keys(peersMap),
|
||||
},
|
||||
},
|
||||
Settings: &server.Settings{
|
||||
Settings: &types.Settings{
|
||||
PeerLoginExpirationEnabled: true,
|
||||
PeerLoginExpiration: time.Hour,
|
||||
},
|
||||
Policies: []*server.Policy{policy},
|
||||
Network: &server.Network{
|
||||
Policies: []*types.Policy{policy},
|
||||
Network: &types.Network{
|
||||
Identifier: "ciclqisab2ss43jdn8q0",
|
||||
Net: net.IPNet{
|
||||
IP: net.ParseIP("100.67.0.0"),
|
||||
@@ -101,7 +100,7 @@ func initTestMetaData(peers ...*nbpeer.Peer) *PeersHandler {
|
||||
},
|
||||
}
|
||||
|
||||
return &PeersHandler{
|
||||
return &Handler{
|
||||
accountManager: &mock_server.MockAccountManager{
|
||||
UpdatePeerFunc: func(_ context.Context, accountID, userID string, update *nbpeer.Peer) (*nbpeer.Peer, error) {
|
||||
var p *nbpeer.Peer
|
||||
+10
-10
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package policies
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -11,22 +11,22 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/geolocation"
|
||||
"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/netbirdio/netbird/management/server/types"
|
||||
"github.com/netbirdio/netbird/util"
|
||||
)
|
||||
|
||||
func initGeolocationTestData(t *testing.T) *GeolocationsHandler {
|
||||
func initGeolocationTestData(t *testing.T) *geolocationsHandler {
|
||||
t.Helper()
|
||||
|
||||
var (
|
||||
mmdbPath = "../testdata/GeoLite2-City_20240305.mmdb"
|
||||
geonamesdbPath = "../testdata/geonames_20240305.db"
|
||||
mmdbPath = "../../../testdata/GeoLite2-City_20240305.mmdb"
|
||||
geonamesdbPath = "../../../testdata/geonames_20240305.db"
|
||||
)
|
||||
|
||||
tempDir := t.TempDir()
|
||||
@@ -41,13 +41,13 @@ func initGeolocationTestData(t *testing.T) *GeolocationsHandler {
|
||||
assert.NoError(t, err)
|
||||
t.Cleanup(func() { _ = geo.Stop() })
|
||||
|
||||
return &GeolocationsHandler{
|
||||
return &geolocationsHandler{
|
||||
accountManager: &mock_server.MockAccountManager{
|
||||
GetAccountIDFromTokenFunc: func(_ context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error) {
|
||||
return claims.AccountId, claims.UserId, nil
|
||||
},
|
||||
GetUserByIDFunc: func(ctx context.Context, id string) (*server.User, error) {
|
||||
return server.NewAdminUser(id), nil
|
||||
GetUserByIDFunc: func(ctx context.Context, id string) (*types.User, error) {
|
||||
return types.NewAdminUser(id), nil
|
||||
},
|
||||
},
|
||||
geolocationManager: geo,
|
||||
@@ -114,7 +114,7 @@ func TestGetCitiesByCountry(t *testing.T) {
|
||||
req := httptest.NewRequest(tc.requestType, tc.requestPath, nil)
|
||||
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/api/locations/countries/{country}/cities", geolocationHandler.GetCitiesByCountry).Methods("GET")
|
||||
router.HandleFunc("/api/locations/countries/{country}/cities", geolocationHandler.getCitiesByCountry).Methods("GET")
|
||||
router.ServeHTTP(recorder, req)
|
||||
|
||||
res := recorder.Result()
|
||||
@@ -202,7 +202,7 @@ func TestGetAllCountries(t *testing.T) {
|
||||
req := httptest.NewRequest(tc.requestType, tc.requestPath, nil)
|
||||
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/api/locations/countries", geolocationHandler.GetAllCountries).Methods("GET")
|
||||
router.HandleFunc("/api/locations/countries", geolocationHandler.getAllCountries).Methods("GET")
|
||||
router.ServeHTTP(recorder, req)
|
||||
|
||||
res := recorder.Result()
|
||||
+19
-12
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package policies
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"github.com/netbirdio/netbird/management/server/geolocation"
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||
"github.com/netbirdio/netbird/management/server/http/util"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
@@ -18,16 +19,22 @@ var (
|
||||
countryCodeRegex = regexp.MustCompile("^[a-zA-Z]{2}$")
|
||||
)
|
||||
|
||||
// GeolocationsHandler is a handler that returns locations.
|
||||
type GeolocationsHandler struct {
|
||||
// geolocationsHandler is a handler that returns locations.
|
||||
type geolocationsHandler struct {
|
||||
accountManager server.AccountManager
|
||||
geolocationManager *geolocation.Geolocation
|
||||
geolocationManager geolocation.Geolocation
|
||||
claimsExtractor *jwtclaims.ClaimsExtractor
|
||||
}
|
||||
|
||||
// NewGeolocationsHandlerHandler creates a new Geolocations handler
|
||||
func NewGeolocationsHandlerHandler(accountManager server.AccountManager, geolocationManager *geolocation.Geolocation, authCfg AuthCfg) *GeolocationsHandler {
|
||||
return &GeolocationsHandler{
|
||||
func addLocationsEndpoint(accountManager server.AccountManager, locationManager geolocation.Geolocation, authCfg configs.AuthCfg, router *mux.Router) {
|
||||
locationHandler := newGeolocationsHandlerHandler(accountManager, locationManager, authCfg)
|
||||
router.HandleFunc("/locations/countries", locationHandler.getAllCountries).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/locations/countries/{country}/cities", locationHandler.getCitiesByCountry).Methods("GET", "OPTIONS")
|
||||
}
|
||||
|
||||
// newGeolocationsHandlerHandler creates a new Geolocations handler
|
||||
func newGeolocationsHandlerHandler(accountManager server.AccountManager, geolocationManager geolocation.Geolocation, authCfg configs.AuthCfg) *geolocationsHandler {
|
||||
return &geolocationsHandler{
|
||||
accountManager: accountManager,
|
||||
geolocationManager: geolocationManager,
|
||||
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||
@@ -37,8 +44,8 @@ func NewGeolocationsHandlerHandler(accountManager server.AccountManager, geoloca
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllCountries retrieves a list of all countries
|
||||
func (l *GeolocationsHandler) GetAllCountries(w http.ResponseWriter, r *http.Request) {
|
||||
// getAllCountries retrieves a list of all countries
|
||||
func (l *geolocationsHandler) getAllCountries(w http.ResponseWriter, r *http.Request) {
|
||||
if err := l.authenticateUser(r); err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
@@ -63,8 +70,8 @@ func (l *GeolocationsHandler) GetAllCountries(w http.ResponseWriter, r *http.Req
|
||||
util.WriteJSONObject(r.Context(), w, countries)
|
||||
}
|
||||
|
||||
// GetCitiesByCountry retrieves a list of cities based on the given country code
|
||||
func (l *GeolocationsHandler) GetCitiesByCountry(w http.ResponseWriter, r *http.Request) {
|
||||
// getCitiesByCountry retrieves a list of cities based on the given country code
|
||||
func (l *geolocationsHandler) getCitiesByCountry(w http.ResponseWriter, r *http.Request) {
|
||||
if err := l.authenticateUser(r); err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
@@ -96,7 +103,7 @@ func (l *GeolocationsHandler) GetCitiesByCountry(w http.ResponseWriter, r *http.
|
||||
util.WriteJSONObject(r.Context(), w, cities)
|
||||
}
|
||||
|
||||
func (l *GeolocationsHandler) authenticateUser(r *http.Request) error {
|
||||
func (l *geolocationsHandler) authenticateUser(r *http.Request) error {
|
||||
claims := l.claimsExtractor.FromRequestContext(r)
|
||||
_, userID, err := l.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
+114
-47
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package policies
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -6,23 +6,36 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
nbgroup "github.com/netbirdio/netbird/management/server/group"
|
||||
"github.com/netbirdio/netbird/management/server/geolocation"
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||
"github.com/netbirdio/netbird/management/server/http/util"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
// Policies is a handler that returns policy of the account
|
||||
type Policies struct {
|
||||
// handler is a handler that returns policy of the account
|
||||
type handler struct {
|
||||
accountManager server.AccountManager
|
||||
claimsExtractor *jwtclaims.ClaimsExtractor
|
||||
}
|
||||
|
||||
// NewPoliciesHandler creates a new Policies handler
|
||||
func NewPoliciesHandler(accountManager server.AccountManager, authCfg AuthCfg) *Policies {
|
||||
return &Policies{
|
||||
func AddEndpoints(accountManager server.AccountManager, locationManager geolocation.Geolocation, authCfg configs.AuthCfg, router *mux.Router) {
|
||||
policiesHandler := newHandler(accountManager, authCfg)
|
||||
router.HandleFunc("/policies", policiesHandler.getAllPolicies).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/policies", policiesHandler.createPolicy).Methods("POST", "OPTIONS")
|
||||
router.HandleFunc("/policies/{policyId}", policiesHandler.updatePolicy).Methods("PUT", "OPTIONS")
|
||||
router.HandleFunc("/policies/{policyId}", policiesHandler.getPolicy).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/policies/{policyId}", policiesHandler.deletePolicy).Methods("DELETE", "OPTIONS")
|
||||
addPostureCheckEndpoint(accountManager, locationManager, authCfg, router)
|
||||
}
|
||||
|
||||
// newHandler creates a new policies handler
|
||||
func newHandler(accountManager server.AccountManager, authCfg configs.AuthCfg) *handler {
|
||||
return &handler{
|
||||
accountManager: accountManager,
|
||||
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||
jwtclaims.WithAudience(authCfg.Audience),
|
||||
@@ -31,8 +44,8 @@ func NewPoliciesHandler(accountManager server.AccountManager, authCfg AuthCfg) *
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllPolicies list for the account
|
||||
func (h *Policies) GetAllPolicies(w http.ResponseWriter, r *http.Request) {
|
||||
// getAllPolicies list for the account
|
||||
func (h *handler) getAllPolicies(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -65,8 +78,8 @@ func (h *Policies) GetAllPolicies(w http.ResponseWriter, r *http.Request) {
|
||||
util.WriteJSONObject(r.Context(), w, policies)
|
||||
}
|
||||
|
||||
// UpdatePolicy handles update to a policy identified by a given ID
|
||||
func (h *Policies) UpdatePolicy(w http.ResponseWriter, r *http.Request) {
|
||||
// updatePolicy handles update to a policy identified by a given ID
|
||||
func (h *handler) updatePolicy(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -90,8 +103,8 @@ func (h *Policies) UpdatePolicy(w http.ResponseWriter, r *http.Request) {
|
||||
h.savePolicy(w, r, accountID, userID, policyID)
|
||||
}
|
||||
|
||||
// CreatePolicy handles policy creation request
|
||||
func (h *Policies) CreatePolicy(w http.ResponseWriter, r *http.Request) {
|
||||
// createPolicy handles policy creation request
|
||||
func (h *handler) createPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -103,7 +116,7 @@ func (h *Policies) CreatePolicy(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// savePolicy handles policy creation and update
|
||||
func (h *Policies) savePolicy(w http.ResponseWriter, r *http.Request, accountID string, userID string, policyID string) {
|
||||
func (h *handler) savePolicy(w http.ResponseWriter, r *http.Request, accountID string, userID string, policyID string) {
|
||||
var req api.PutApiPoliciesPolicyIdJSONRequestBody
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
||||
@@ -120,28 +133,74 @@ func (h *Policies) savePolicy(w http.ResponseWriter, r *http.Request, accountID
|
||||
return
|
||||
}
|
||||
|
||||
policy := &server.Policy{
|
||||
description := ""
|
||||
if req.Description != nil {
|
||||
description = *req.Description
|
||||
}
|
||||
|
||||
policy := &types.Policy{
|
||||
ID: policyID,
|
||||
AccountID: accountID,
|
||||
Name: req.Name,
|
||||
Enabled: req.Enabled,
|
||||
Description: req.Description,
|
||||
Description: description,
|
||||
}
|
||||
for _, rule := range req.Rules {
|
||||
var ruleID string
|
||||
if rule.Id != nil {
|
||||
if rule.Id != nil && policyID != "" {
|
||||
ruleID = *rule.Id
|
||||
}
|
||||
|
||||
pr := server.PolicyRule{
|
||||
hasSources := rule.Sources != nil
|
||||
hasSourceResource := rule.SourceResource != nil
|
||||
|
||||
hasDestinations := rule.Destinations != nil
|
||||
hasDestinationResource := rule.DestinationResource != nil
|
||||
|
||||
if hasSources && hasSourceResource {
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "specify either sources or source resources, not both"), w)
|
||||
return
|
||||
}
|
||||
|
||||
if hasDestinations && hasDestinationResource {
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "specify either destinations or destination resources, not both"), w)
|
||||
return
|
||||
}
|
||||
|
||||
if !(hasSources || hasSourceResource) || !(hasDestinations || hasDestinationResource) {
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "specify either sources or source resources and destinations or destination resources"), w)
|
||||
return
|
||||
}
|
||||
|
||||
pr := types.PolicyRule{
|
||||
ID: ruleID,
|
||||
PolicyID: policyID,
|
||||
Name: rule.Name,
|
||||
Destinations: rule.Destinations,
|
||||
Sources: rule.Sources,
|
||||
Bidirectional: rule.Bidirectional,
|
||||
}
|
||||
|
||||
if hasSources {
|
||||
pr.Sources = *rule.Sources
|
||||
}
|
||||
|
||||
if hasSourceResource {
|
||||
// TODO: validate the resource id and type
|
||||
sourceResource := &types.Resource{}
|
||||
sourceResource.FromAPIRequest(rule.SourceResource)
|
||||
pr.SourceResource = *sourceResource
|
||||
}
|
||||
|
||||
if hasDestinations {
|
||||
pr.Destinations = *rule.Destinations
|
||||
}
|
||||
|
||||
if hasDestinationResource {
|
||||
// TODO: validate the resource id and type
|
||||
destinationResource := &types.Resource{}
|
||||
destinationResource.FromAPIRequest(rule.DestinationResource)
|
||||
pr.DestinationResource = *destinationResource
|
||||
}
|
||||
|
||||
pr.Enabled = rule.Enabled
|
||||
if rule.Description != nil {
|
||||
pr.Description = *rule.Description
|
||||
@@ -149,9 +208,9 @@ func (h *Policies) savePolicy(w http.ResponseWriter, r *http.Request, accountID
|
||||
|
||||
switch rule.Action {
|
||||
case api.PolicyRuleUpdateActionAccept:
|
||||
pr.Action = server.PolicyTrafficActionAccept
|
||||
pr.Action = types.PolicyTrafficActionAccept
|
||||
case api.PolicyRuleUpdateActionDrop:
|
||||
pr.Action = server.PolicyTrafficActionDrop
|
||||
pr.Action = types.PolicyTrafficActionDrop
|
||||
default:
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "unknown action type"), w)
|
||||
return
|
||||
@@ -159,13 +218,13 @@ func (h *Policies) savePolicy(w http.ResponseWriter, r *http.Request, accountID
|
||||
|
||||
switch rule.Protocol {
|
||||
case api.PolicyRuleUpdateProtocolAll:
|
||||
pr.Protocol = server.PolicyRuleProtocolALL
|
||||
pr.Protocol = types.PolicyRuleProtocolALL
|
||||
case api.PolicyRuleUpdateProtocolTcp:
|
||||
pr.Protocol = server.PolicyRuleProtocolTCP
|
||||
pr.Protocol = types.PolicyRuleProtocolTCP
|
||||
case api.PolicyRuleUpdateProtocolUdp:
|
||||
pr.Protocol = server.PolicyRuleProtocolUDP
|
||||
pr.Protocol = types.PolicyRuleProtocolUDP
|
||||
case api.PolicyRuleUpdateProtocolIcmp:
|
||||
pr.Protocol = server.PolicyRuleProtocolICMP
|
||||
pr.Protocol = types.PolicyRuleProtocolICMP
|
||||
default:
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "unknown protocol type: %v", rule.Protocol), w)
|
||||
return
|
||||
@@ -192,7 +251,7 @@ func (h *Policies) savePolicy(w http.ResponseWriter, r *http.Request, accountID
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "valid port value is in 1..65535 range"), w)
|
||||
return
|
||||
}
|
||||
pr.PortRanges = append(pr.PortRanges, server.RulePortRange{
|
||||
pr.PortRanges = append(pr.PortRanges, types.RulePortRange{
|
||||
Start: uint16(portRange.Start),
|
||||
End: uint16(portRange.End),
|
||||
})
|
||||
@@ -201,7 +260,7 @@ func (h *Policies) savePolicy(w http.ResponseWriter, r *http.Request, accountID
|
||||
|
||||
// validate policy object
|
||||
switch pr.Protocol {
|
||||
case server.PolicyRuleProtocolALL, server.PolicyRuleProtocolICMP:
|
||||
case types.PolicyRuleProtocolALL, types.PolicyRuleProtocolICMP:
|
||||
if len(pr.Ports) != 0 || len(pr.PortRanges) != 0 {
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "for ALL or ICMP protocol ports is not allowed"), w)
|
||||
return
|
||||
@@ -210,7 +269,7 @@ func (h *Policies) savePolicy(w http.ResponseWriter, r *http.Request, accountID
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "for ALL or ICMP protocol type flow can be only bi-directional"), w)
|
||||
return
|
||||
}
|
||||
case server.PolicyRuleProtocolTCP, server.PolicyRuleProtocolUDP:
|
||||
case types.PolicyRuleProtocolTCP, types.PolicyRuleProtocolUDP:
|
||||
if !pr.Bidirectional && (len(pr.Ports) == 0 || len(pr.PortRanges) != 0) {
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "for ALL or ICMP protocol type flow can be only bi-directional"), w)
|
||||
return
|
||||
@@ -245,8 +304,8 @@ func (h *Policies) savePolicy(w http.ResponseWriter, r *http.Request, accountID
|
||||
util.WriteJSONObject(r.Context(), w, resp)
|
||||
}
|
||||
|
||||
// DeletePolicy handles policy deletion request
|
||||
func (h *Policies) DeletePolicy(w http.ResponseWriter, r *http.Request) {
|
||||
// deletePolicy handles policy deletion request
|
||||
func (h *handler) deletePolicy(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -266,11 +325,11 @@ func (h *Policies) DeletePolicy(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, emptyObject{})
|
||||
util.WriteJSONObject(r.Context(), w, util.EmptyObject{})
|
||||
}
|
||||
|
||||
// GetPolicy handles a group Get request identified by ID
|
||||
func (h *Policies) GetPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
// getPolicy handles a group Get request identified by ID
|
||||
func (h *handler) getPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -306,8 +365,8 @@ func (h *Policies) GetPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
util.WriteJSONObject(r.Context(), w, resp)
|
||||
}
|
||||
|
||||
func toPolicyResponse(groups []*nbgroup.Group, policy *server.Policy) *api.Policy {
|
||||
groupsMap := make(map[string]*nbgroup.Group)
|
||||
func toPolicyResponse(groups []*types.Group, policy *types.Policy) *api.Policy {
|
||||
groupsMap := make(map[string]*types.Group)
|
||||
for _, group := range groups {
|
||||
groupsMap[group.ID] = group
|
||||
}
|
||||
@@ -316,7 +375,7 @@ func toPolicyResponse(groups []*nbgroup.Group, policy *server.Policy) *api.Polic
|
||||
ap := &api.Policy{
|
||||
Id: &policy.ID,
|
||||
Name: policy.Name,
|
||||
Description: policy.Description,
|
||||
Description: &policy.Description,
|
||||
Enabled: policy.Enabled,
|
||||
SourcePostureChecks: policy.SourcePostureChecks,
|
||||
}
|
||||
@@ -324,13 +383,15 @@ func toPolicyResponse(groups []*nbgroup.Group, policy *server.Policy) *api.Polic
|
||||
rID := r.ID
|
||||
rDescription := r.Description
|
||||
rule := api.PolicyRule{
|
||||
Id: &rID,
|
||||
Name: r.Name,
|
||||
Enabled: r.Enabled,
|
||||
Description: &rDescription,
|
||||
Bidirectional: r.Bidirectional,
|
||||
Protocol: api.PolicyRuleProtocol(r.Protocol),
|
||||
Action: api.PolicyRuleAction(r.Action),
|
||||
Id: &rID,
|
||||
Name: r.Name,
|
||||
Enabled: r.Enabled,
|
||||
Description: &rDescription,
|
||||
Bidirectional: r.Bidirectional,
|
||||
Protocol: api.PolicyRuleProtocol(r.Protocol),
|
||||
Action: api.PolicyRuleAction(r.Action),
|
||||
SourceResource: r.SourceResource.ToAPIResponse(),
|
||||
DestinationResource: r.DestinationResource.ToAPIResponse(),
|
||||
}
|
||||
|
||||
if len(r.Ports) != 0 {
|
||||
@@ -349,26 +410,30 @@ func toPolicyResponse(groups []*nbgroup.Group, policy *server.Policy) *api.Polic
|
||||
rule.PortRanges = &portRanges
|
||||
}
|
||||
|
||||
var sources []api.GroupMinimum
|
||||
for _, gid := range r.Sources {
|
||||
_, ok := cache[gid]
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if group, ok := groupsMap[gid]; ok {
|
||||
minimum := api.GroupMinimum{
|
||||
Id: group.ID,
|
||||
Name: group.Name,
|
||||
PeersCount: len(group.Peers),
|
||||
}
|
||||
rule.Sources = append(rule.Sources, minimum)
|
||||
sources = append(sources, minimum)
|
||||
cache[gid] = minimum
|
||||
}
|
||||
}
|
||||
rule.Sources = &sources
|
||||
|
||||
var destinations []api.GroupMinimum
|
||||
for _, gid := range r.Destinations {
|
||||
cachedMinimum, ok := cache[gid]
|
||||
if ok {
|
||||
rule.Destinations = append(rule.Destinations, cachedMinimum)
|
||||
destinations = append(destinations, cachedMinimum)
|
||||
continue
|
||||
}
|
||||
if group, ok := groupsMap[gid]; ok {
|
||||
@@ -377,10 +442,12 @@ func toPolicyResponse(groups []*nbgroup.Group, policy *server.Policy) *api.Polic
|
||||
Name: group.Name,
|
||||
PeersCount: len(group.Peers),
|
||||
}
|
||||
rule.Destinations = append(rule.Destinations, minimum)
|
||||
destinations = append(destinations, minimum)
|
||||
cache[gid] = minimum
|
||||
}
|
||||
}
|
||||
rule.Destinations = &destinations
|
||||
|
||||
ap.Rules = append(ap.Rules, rule)
|
||||
}
|
||||
return ap
|
||||
+41
-31
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package policies
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
nbgroup "github.com/netbirdio/netbird/management/server/group"
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
@@ -20,50 +20,49 @@ import (
|
||||
|
||||
"github.com/magiconair/properties/assert"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"github.com/netbirdio/netbird/management/server/mock_server"
|
||||
)
|
||||
|
||||
func initPoliciesTestData(policies ...*server.Policy) *Policies {
|
||||
testPolicies := make(map[string]*server.Policy, len(policies))
|
||||
func initPoliciesTestData(policies ...*types.Policy) *handler {
|
||||
testPolicies := make(map[string]*types.Policy, len(policies))
|
||||
for _, policy := range policies {
|
||||
testPolicies[policy.ID] = policy
|
||||
}
|
||||
return &Policies{
|
||||
return &handler{
|
||||
accountManager: &mock_server.MockAccountManager{
|
||||
GetPolicyFunc: func(_ context.Context, _, policyID, _ string) (*server.Policy, error) {
|
||||
GetPolicyFunc: func(_ context.Context, _, policyID, _ string) (*types.Policy, error) {
|
||||
policy, ok := testPolicies[policyID]
|
||||
if !ok {
|
||||
return nil, status.Errorf(status.NotFound, "policy not found")
|
||||
}
|
||||
return policy, nil
|
||||
},
|
||||
SavePolicyFunc: func(_ context.Context, _, _ string, policy *server.Policy) (*server.Policy, error) {
|
||||
SavePolicyFunc: func(_ context.Context, _, _ string, policy *types.Policy) (*types.Policy, error) {
|
||||
if !strings.HasPrefix(policy.ID, "id-") {
|
||||
policy.ID = "id-was-set"
|
||||
policy.Rules[0].ID = "id-was-set"
|
||||
}
|
||||
return policy, nil
|
||||
},
|
||||
GetAllGroupsFunc: func(ctx context.Context, accountID, userID string) ([]*nbgroup.Group, error) {
|
||||
return []*nbgroup.Group{{ID: "F"}, {ID: "G"}}, nil
|
||||
GetAllGroupsFunc: func(ctx context.Context, accountID, userID string) ([]*types.Group, error) {
|
||||
return []*types.Group{{ID: "F"}, {ID: "G"}}, nil
|
||||
},
|
||||
GetAccountIDFromTokenFunc: func(_ context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error) {
|
||||
return claims.AccountId, claims.UserId, nil
|
||||
},
|
||||
GetAccountByIDFunc: func(ctx context.Context, accountID string, userID string) (*server.Account, error) {
|
||||
user := server.NewAdminUser(userID)
|
||||
return &server.Account{
|
||||
GetAccountByIDFunc: func(ctx context.Context, accountID string, userID string) (*types.Account, error) {
|
||||
user := types.NewAdminUser(userID)
|
||||
return &types.Account{
|
||||
Id: accountID,
|
||||
Domain: "hotmail.com",
|
||||
Policies: []*server.Policy{
|
||||
Policies: []*types.Policy{
|
||||
{ID: "id-existed"},
|
||||
},
|
||||
Groups: map[string]*nbgroup.Group{
|
||||
Groups: map[string]*types.Group{
|
||||
"F": {ID: "F"},
|
||||
"G": {ID: "G"},
|
||||
},
|
||||
Users: map[string]*server.User{
|
||||
Users: map[string]*types.User{
|
||||
"test_user": user,
|
||||
},
|
||||
}, nil
|
||||
@@ -91,24 +90,24 @@ func TestPoliciesGetPolicy(t *testing.T) {
|
||||
requestBody io.Reader
|
||||
}{
|
||||
{
|
||||
name: "GetPolicy OK",
|
||||
name: "getPolicy OK",
|
||||
expectedBody: true,
|
||||
requestType: http.MethodGet,
|
||||
requestPath: "/api/policies/idofthepolicy",
|
||||
expectedStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "GetPolicy not found",
|
||||
name: "getPolicy not found",
|
||||
requestType: http.MethodGet,
|
||||
requestPath: "/api/policies/notexists",
|
||||
expectedStatus: http.StatusNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
policy := &server.Policy{
|
||||
policy := &types.Policy{
|
||||
ID: "idofthepolicy",
|
||||
Name: "Rule",
|
||||
Rules: []*server.PolicyRule{
|
||||
Rules: []*types.PolicyRule{
|
||||
{ID: "idoftherule", Name: "Rule"},
|
||||
},
|
||||
}
|
||||
@@ -121,7 +120,7 @@ func TestPoliciesGetPolicy(t *testing.T) {
|
||||
req := httptest.NewRequest(tc.requestType, tc.requestPath, tc.requestBody)
|
||||
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/api/policies/{policyId}", p.GetPolicy).Methods("GET")
|
||||
router.HandleFunc("/api/policies/{policyId}", p.getPolicy).Methods("GET")
|
||||
router.ServeHTTP(recorder, req)
|
||||
|
||||
res := recorder.Result()
|
||||
@@ -155,6 +154,7 @@ func TestPoliciesGetPolicy(t *testing.T) {
|
||||
|
||||
func TestPoliciesWritePolicy(t *testing.T) {
|
||||
str := func(s string) *string { return &s }
|
||||
emptyString := ""
|
||||
tt := []struct {
|
||||
name string
|
||||
expectedStatus int
|
||||
@@ -177,14 +177,17 @@ func TestPoliciesWritePolicy(t *testing.T) {
|
||||
"Description": "Description",
|
||||
"Protocol": "tcp",
|
||||
"Action": "accept",
|
||||
"Bidirectional":true
|
||||
"Bidirectional":true,
|
||||
"Sources": ["F"],
|
||||
"Destinations": ["G"]
|
||||
}
|
||||
]}`)),
|
||||
expectedStatus: http.StatusOK,
|
||||
expectedBody: true,
|
||||
expectedPolicy: &api.Policy{
|
||||
Id: str("id-was-set"),
|
||||
Name: "Default POSTed Policy",
|
||||
Id: str("id-was-set"),
|
||||
Name: "Default POSTed Policy",
|
||||
Description: &emptyString,
|
||||
Rules: []api.PolicyRule{
|
||||
{
|
||||
Id: str("id-was-set"),
|
||||
@@ -193,6 +196,8 @@ func TestPoliciesWritePolicy(t *testing.T) {
|
||||
Protocol: "tcp",
|
||||
Action: "accept",
|
||||
Bidirectional: true,
|
||||
Sources: &[]api.GroupMinimum{{Id: "F"}},
|
||||
Destinations: &[]api.GroupMinimum{{Id: "G"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -221,14 +226,17 @@ func TestPoliciesWritePolicy(t *testing.T) {
|
||||
"Description": "Description",
|
||||
"Protocol": "tcp",
|
||||
"Action": "accept",
|
||||
"Bidirectional":true
|
||||
"Bidirectional":true,
|
||||
"Sources": ["F"],
|
||||
"Destinations": ["F"]
|
||||
}
|
||||
]}`)),
|
||||
expectedStatus: http.StatusOK,
|
||||
expectedBody: true,
|
||||
expectedPolicy: &api.Policy{
|
||||
Id: str("id-existed"),
|
||||
Name: "Default POSTed Policy",
|
||||
Id: str("id-existed"),
|
||||
Name: "Default POSTed Policy",
|
||||
Description: &emptyString,
|
||||
Rules: []api.PolicyRule{
|
||||
{
|
||||
Id: str("id-existed"),
|
||||
@@ -237,6 +245,8 @@ func TestPoliciesWritePolicy(t *testing.T) {
|
||||
Protocol: "tcp",
|
||||
Action: "accept",
|
||||
Bidirectional: true,
|
||||
Sources: &[]api.GroupMinimum{{Id: "F"}},
|
||||
Destinations: &[]api.GroupMinimum{{Id: "F"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -251,10 +261,10 @@ func TestPoliciesWritePolicy(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
p := initPoliciesTestData(&server.Policy{
|
||||
p := initPoliciesTestData(&types.Policy{
|
||||
ID: "id-existed",
|
||||
Name: "Default POSTed Rule",
|
||||
Rules: []*server.PolicyRule{
|
||||
Rules: []*types.PolicyRule{
|
||||
{
|
||||
ID: "id-existed",
|
||||
Name: "Default POSTed Rule",
|
||||
@@ -269,8 +279,8 @@ func TestPoliciesWritePolicy(t *testing.T) {
|
||||
req := httptest.NewRequest(tc.requestType, tc.requestPath, tc.requestBody)
|
||||
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/api/policies", p.CreatePolicy).Methods("POST")
|
||||
router.HandleFunc("/api/policies/{policyId}", p.UpdatePolicy).Methods("PUT")
|
||||
router.HandleFunc("/api/policies", p.createPolicy).Methods("POST")
|
||||
router.HandleFunc("/api/policies/{policyId}", p.updatePolicy).Methods("PUT")
|
||||
router.ServeHTTP(recorder, req)
|
||||
|
||||
res := recorder.Result()
|
||||
+30
-19
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package policies
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -9,22 +9,33 @@ import (
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"github.com/netbirdio/netbird/management/server/geolocation"
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||
"github.com/netbirdio/netbird/management/server/http/util"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
"github.com/netbirdio/netbird/management/server/posture"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
)
|
||||
|
||||
// PostureChecksHandler is a handler that returns posture checks of the account.
|
||||
type PostureChecksHandler struct {
|
||||
// postureChecksHandler is a handler that returns posture checks of the account.
|
||||
type postureChecksHandler struct {
|
||||
accountManager server.AccountManager
|
||||
geolocationManager *geolocation.Geolocation
|
||||
geolocationManager geolocation.Geolocation
|
||||
claimsExtractor *jwtclaims.ClaimsExtractor
|
||||
}
|
||||
|
||||
// NewPostureChecksHandler creates a new PostureChecks handler
|
||||
func NewPostureChecksHandler(accountManager server.AccountManager, geolocationManager *geolocation.Geolocation, authCfg AuthCfg) *PostureChecksHandler {
|
||||
return &PostureChecksHandler{
|
||||
func addPostureCheckEndpoint(accountManager server.AccountManager, locationManager geolocation.Geolocation, authCfg configs.AuthCfg, router *mux.Router) {
|
||||
postureCheckHandler := newPostureChecksHandler(accountManager, locationManager, authCfg)
|
||||
router.HandleFunc("/posture-checks", postureCheckHandler.getAllPostureChecks).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/posture-checks", postureCheckHandler.createPostureCheck).Methods("POST", "OPTIONS")
|
||||
router.HandleFunc("/posture-checks/{postureCheckId}", postureCheckHandler.updatePostureCheck).Methods("PUT", "OPTIONS")
|
||||
router.HandleFunc("/posture-checks/{postureCheckId}", postureCheckHandler.getPostureCheck).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/posture-checks/{postureCheckId}", postureCheckHandler.deletePostureCheck).Methods("DELETE", "OPTIONS")
|
||||
addLocationsEndpoint(accountManager, locationManager, authCfg, router)
|
||||
}
|
||||
|
||||
// newPostureChecksHandler creates a new PostureChecks handler
|
||||
func newPostureChecksHandler(accountManager server.AccountManager, geolocationManager geolocation.Geolocation, authCfg configs.AuthCfg) *postureChecksHandler {
|
||||
return &postureChecksHandler{
|
||||
accountManager: accountManager,
|
||||
geolocationManager: geolocationManager,
|
||||
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||
@@ -34,8 +45,8 @@ func NewPostureChecksHandler(accountManager server.AccountManager, geolocationMa
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllPostureChecks list for the account
|
||||
func (p *PostureChecksHandler) GetAllPostureChecks(w http.ResponseWriter, r *http.Request) {
|
||||
// getAllPostureChecks list for the account
|
||||
func (p *postureChecksHandler) getAllPostureChecks(w http.ResponseWriter, r *http.Request) {
|
||||
claims := p.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := p.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -57,8 +68,8 @@ func (p *PostureChecksHandler) GetAllPostureChecks(w http.ResponseWriter, r *htt
|
||||
util.WriteJSONObject(r.Context(), w, postureChecks)
|
||||
}
|
||||
|
||||
// UpdatePostureCheck handles update to a posture check identified by a given ID
|
||||
func (p *PostureChecksHandler) UpdatePostureCheck(w http.ResponseWriter, r *http.Request) {
|
||||
// updatePostureCheck handles update to a posture check identified by a given ID
|
||||
func (p *postureChecksHandler) updatePostureCheck(w http.ResponseWriter, r *http.Request) {
|
||||
claims := p.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := p.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -82,8 +93,8 @@ func (p *PostureChecksHandler) UpdatePostureCheck(w http.ResponseWriter, r *http
|
||||
p.savePostureChecks(w, r, accountID, userID, postureChecksID)
|
||||
}
|
||||
|
||||
// CreatePostureCheck handles posture check creation request
|
||||
func (p *PostureChecksHandler) CreatePostureCheck(w http.ResponseWriter, r *http.Request) {
|
||||
// createPostureCheck handles posture check creation request
|
||||
func (p *postureChecksHandler) createPostureCheck(w http.ResponseWriter, r *http.Request) {
|
||||
claims := p.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := p.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -94,8 +105,8 @@ func (p *PostureChecksHandler) CreatePostureCheck(w http.ResponseWriter, r *http
|
||||
p.savePostureChecks(w, r, accountID, userID, "")
|
||||
}
|
||||
|
||||
// GetPostureCheck handles a posture check Get request identified by ID
|
||||
func (p *PostureChecksHandler) GetPostureCheck(w http.ResponseWriter, r *http.Request) {
|
||||
// getPostureCheck handles a posture check Get request identified by ID
|
||||
func (p *postureChecksHandler) getPostureCheck(w http.ResponseWriter, r *http.Request) {
|
||||
claims := p.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := p.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -119,8 +130,8 @@ func (p *PostureChecksHandler) GetPostureCheck(w http.ResponseWriter, r *http.Re
|
||||
util.WriteJSONObject(r.Context(), w, postureChecks.ToAPIResponse())
|
||||
}
|
||||
|
||||
// DeletePostureCheck handles posture check deletion request
|
||||
func (p *PostureChecksHandler) DeletePostureCheck(w http.ResponseWriter, r *http.Request) {
|
||||
// deletePostureCheck handles posture check deletion request
|
||||
func (p *postureChecksHandler) deletePostureCheck(w http.ResponseWriter, r *http.Request) {
|
||||
claims := p.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := p.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -140,11 +151,11 @@ func (p *PostureChecksHandler) DeletePostureCheck(w http.ResponseWriter, r *http
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, emptyObject{})
|
||||
util.WriteJSONObject(r.Context(), w, util.EmptyObject{})
|
||||
}
|
||||
|
||||
// savePostureChecks handles posture checks create and update
|
||||
func (p *PostureChecksHandler) savePostureChecks(w http.ResponseWriter, r *http.Request, accountID, userID, postureChecksID string) {
|
||||
func (p *postureChecksHandler) savePostureChecks(w http.ResponseWriter, r *http.Request, accountID, userID, postureChecksID string) {
|
||||
var (
|
||||
err error
|
||||
req api.PostureCheckUpdate
|
||||
+17
-17
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package policies
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -25,13 +25,13 @@ import (
|
||||
var berlin = "Berlin"
|
||||
var losAngeles = "Los Angeles"
|
||||
|
||||
func initPostureChecksTestData(postureChecks ...*posture.Checks) *PostureChecksHandler {
|
||||
func initPostureChecksTestData(postureChecks ...*posture.Checks) *postureChecksHandler {
|
||||
testPostureChecks := make(map[string]*posture.Checks, len(postureChecks))
|
||||
for _, postureCheck := range postureChecks {
|
||||
testPostureChecks[postureCheck.ID] = postureCheck
|
||||
}
|
||||
|
||||
return &PostureChecksHandler{
|
||||
return &postureChecksHandler{
|
||||
accountManager: &mock_server.MockAccountManager{
|
||||
GetPostureChecksFunc: func(_ context.Context, accountID, postureChecksID, userID string) (*posture.Checks, error) {
|
||||
p, ok := testPostureChecks[postureChecksID]
|
||||
@@ -70,7 +70,7 @@ func initPostureChecksTestData(postureChecks ...*posture.Checks) *PostureChecksH
|
||||
return claims.AccountId, claims.UserId, nil
|
||||
},
|
||||
},
|
||||
geolocationManager: &geolocation.Geolocation{},
|
||||
geolocationManager: &geolocation.Mock{},
|
||||
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||
jwtclaims.WithFromRequestContext(func(r *http.Request) jwtclaims.AuthorizationClaims {
|
||||
return jwtclaims.AuthorizationClaims{
|
||||
@@ -147,35 +147,35 @@ func TestGetPostureCheck(t *testing.T) {
|
||||
requestBody io.Reader
|
||||
}{
|
||||
{
|
||||
name: "GetPostureCheck NBVersion OK",
|
||||
name: "getPostureCheck NBVersion OK",
|
||||
expectedBody: true,
|
||||
id: postureCheck.ID,
|
||||
checkName: postureCheck.Name,
|
||||
expectedStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "GetPostureCheck OSVersion OK",
|
||||
name: "getPostureCheck OSVersion OK",
|
||||
expectedBody: true,
|
||||
id: osPostureCheck.ID,
|
||||
checkName: osPostureCheck.Name,
|
||||
expectedStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "GetPostureCheck GeoLocation OK",
|
||||
name: "getPostureCheck GeoLocation OK",
|
||||
expectedBody: true,
|
||||
id: geoPostureCheck.ID,
|
||||
checkName: geoPostureCheck.Name,
|
||||
expectedStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "GetPostureCheck PrivateNetwork OK",
|
||||
name: "getPostureCheck PrivateNetwork OK",
|
||||
expectedBody: true,
|
||||
id: privateNetworkCheck.ID,
|
||||
checkName: privateNetworkCheck.Name,
|
||||
expectedStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "GetPostureCheck Not Found",
|
||||
name: "getPostureCheck Not Found",
|
||||
id: "not-exists",
|
||||
expectedStatus: http.StatusNotFound,
|
||||
},
|
||||
@@ -189,7 +189,7 @@ func TestGetPostureCheck(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/posture-checks/"+tc.id, tc.requestBody)
|
||||
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/api/posture-checks/{postureCheckId}", p.GetPostureCheck).Methods("GET")
|
||||
router.HandleFunc("/api/posture-checks/{postureCheckId}", p.getPostureCheck).Methods("GET")
|
||||
router.ServeHTTP(recorder, req)
|
||||
|
||||
res := recorder.Result()
|
||||
@@ -231,7 +231,7 @@ func TestPostureCheckUpdate(t *testing.T) {
|
||||
requestType string
|
||||
requestPath string
|
||||
requestBody io.Reader
|
||||
setupHandlerFunc func(handler *PostureChecksHandler)
|
||||
setupHandlerFunc func(handler *postureChecksHandler)
|
||||
}{
|
||||
{
|
||||
name: "Create Posture Checks NB version",
|
||||
@@ -286,7 +286,7 @@ func TestPostureCheckUpdate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
setupHandlerFunc: func(handler *PostureChecksHandler) {
|
||||
setupHandlerFunc: func(handler *postureChecksHandler) {
|
||||
handler.geolocationManager = nil
|
||||
},
|
||||
},
|
||||
@@ -427,7 +427,7 @@ func TestPostureCheckUpdate(t *testing.T) {
|
||||
}`)),
|
||||
expectedStatus: http.StatusPreconditionFailed,
|
||||
expectedBody: false,
|
||||
setupHandlerFunc: func(handler *PostureChecksHandler) {
|
||||
setupHandlerFunc: func(handler *postureChecksHandler) {
|
||||
handler.geolocationManager = nil
|
||||
},
|
||||
},
|
||||
@@ -614,7 +614,7 @@ func TestPostureCheckUpdate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
setupHandlerFunc: func(handler *PostureChecksHandler) {
|
||||
setupHandlerFunc: func(handler *postureChecksHandler) {
|
||||
handler.geolocationManager = nil
|
||||
},
|
||||
},
|
||||
@@ -677,7 +677,7 @@ func TestPostureCheckUpdate(t *testing.T) {
|
||||
}`)),
|
||||
expectedStatus: http.StatusPreconditionFailed,
|
||||
expectedBody: false,
|
||||
setupHandlerFunc: func(handler *PostureChecksHandler) {
|
||||
setupHandlerFunc: func(handler *postureChecksHandler) {
|
||||
handler.geolocationManager = nil
|
||||
},
|
||||
},
|
||||
@@ -842,8 +842,8 @@ func TestPostureCheckUpdate(t *testing.T) {
|
||||
}
|
||||
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/api/posture-checks", defaultHandler.CreatePostureCheck).Methods("POST")
|
||||
router.HandleFunc("/api/posture-checks/{postureCheckId}", defaultHandler.UpdatePostureCheck).Methods("PUT")
|
||||
router.HandleFunc("/api/posture-checks", defaultHandler.createPostureCheck).Methods("POST")
|
||||
router.HandleFunc("/api/posture-checks/{postureCheckId}", defaultHandler.updatePostureCheck).Methods("PUT")
|
||||
router.ServeHTTP(recorder, req)
|
||||
|
||||
res := recorder.Result()
|
||||
+29
-19
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package routes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/netbirdio/netbird/management/domain"
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||
"github.com/netbirdio/netbird/management/server/http/util"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
@@ -23,15 +24,24 @@ import (
|
||||
const maxDomains = 32
|
||||
const failedToConvertRoute = "failed to convert route to response: %v"
|
||||
|
||||
// RoutesHandler is the routes handler of the account
|
||||
type RoutesHandler struct {
|
||||
// handler is the routes handler of the account
|
||||
type handler struct {
|
||||
accountManager server.AccountManager
|
||||
claimsExtractor *jwtclaims.ClaimsExtractor
|
||||
}
|
||||
|
||||
// NewRoutesHandler returns a new instance of RoutesHandler handler
|
||||
func NewRoutesHandler(accountManager server.AccountManager, authCfg AuthCfg) *RoutesHandler {
|
||||
return &RoutesHandler{
|
||||
func AddEndpoints(accountManager server.AccountManager, authCfg configs.AuthCfg, router *mux.Router) {
|
||||
routesHandler := newHandler(accountManager, authCfg)
|
||||
router.HandleFunc("/routes", routesHandler.getAllRoutes).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/routes", routesHandler.createRoute).Methods("POST", "OPTIONS")
|
||||
router.HandleFunc("/routes/{routeId}", routesHandler.updateRoute).Methods("PUT", "OPTIONS")
|
||||
router.HandleFunc("/routes/{routeId}", routesHandler.getRoute).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/routes/{routeId}", routesHandler.deleteRoute).Methods("DELETE", "OPTIONS")
|
||||
}
|
||||
|
||||
// newHandler returns a new instance of routes handler
|
||||
func newHandler(accountManager server.AccountManager, authCfg configs.AuthCfg) *handler {
|
||||
return &handler{
|
||||
accountManager: accountManager,
|
||||
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||
jwtclaims.WithAudience(authCfg.Audience),
|
||||
@@ -40,8 +50,8 @@ func NewRoutesHandler(accountManager server.AccountManager, authCfg AuthCfg) *Ro
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllRoutes returns the list of routes for the account
|
||||
func (h *RoutesHandler) GetAllRoutes(w http.ResponseWriter, r *http.Request) {
|
||||
// getAllRoutes returns the list of routes for the account
|
||||
func (h *handler) getAllRoutes(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -67,8 +77,8 @@ func (h *RoutesHandler) GetAllRoutes(w http.ResponseWriter, r *http.Request) {
|
||||
util.WriteJSONObject(r.Context(), w, apiRoutes)
|
||||
}
|
||||
|
||||
// CreateRoute handles route creation request
|
||||
func (h *RoutesHandler) CreateRoute(w http.ResponseWriter, r *http.Request) {
|
||||
// createRoute handles route creation request
|
||||
func (h *handler) createRoute(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -139,7 +149,7 @@ func (h *RoutesHandler) CreateRoute(w http.ResponseWriter, r *http.Request) {
|
||||
util.WriteJSONObject(r.Context(), w, routes)
|
||||
}
|
||||
|
||||
func (h *RoutesHandler) validateRoute(req api.PostApiRoutesJSONRequestBody) error {
|
||||
func (h *handler) validateRoute(req api.PostApiRoutesJSONRequestBody) error {
|
||||
if req.Network != nil && req.Domains != nil {
|
||||
return status.Errorf(status.InvalidArgument, "only one of 'network' or 'domains' should be provided")
|
||||
}
|
||||
@@ -164,8 +174,8 @@ func (h *RoutesHandler) validateRoute(req api.PostApiRoutesJSONRequestBody) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateRoute handles update to a route identified by a given ID
|
||||
func (h *RoutesHandler) UpdateRoute(w http.ResponseWriter, r *http.Request) {
|
||||
// updateRoute handles update to a route identified by a given ID
|
||||
func (h *handler) updateRoute(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -257,8 +267,8 @@ func (h *RoutesHandler) UpdateRoute(w http.ResponseWriter, r *http.Request) {
|
||||
util.WriteJSONObject(r.Context(), w, routes)
|
||||
}
|
||||
|
||||
// DeleteRoute handles route deletion request
|
||||
func (h *RoutesHandler) DeleteRoute(w http.ResponseWriter, r *http.Request) {
|
||||
// deleteRoute handles route deletion request
|
||||
func (h *handler) deleteRoute(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -278,11 +288,11 @@ func (h *RoutesHandler) DeleteRoute(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, emptyObject{})
|
||||
util.WriteJSONObject(r.Context(), w, util.EmptyObject{})
|
||||
}
|
||||
|
||||
// GetRoute handles a route Get request identified by ID
|
||||
func (h *RoutesHandler) GetRoute(w http.ResponseWriter, r *http.Request) {
|
||||
// getRoute handles a route Get request identified by ID
|
||||
func (h *handler) getRoute(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -350,7 +360,7 @@ func validateDomains(domains []string) (domain.List, error) {
|
||||
return nil, fmt.Errorf("domains list exceeds maximum allowed domains: %d", maxDomains)
|
||||
}
|
||||
|
||||
domainRegex := regexp.MustCompile(`^(?:(?:xn--)?[a-zA-Z0-9_](?:[a-zA-Z0-9-_]{0,61}[a-zA-Z0-9])?\.)*(?:xn--)?[a-zA-Z0-9](?:[a-zA-Z0-9-_]{0,61}[a-zA-Z0-9])?$`)
|
||||
domainRegex := regexp.MustCompile(`^(?:\*\.)?(?:(?:xn--)?[a-zA-Z0-9_](?:[a-zA-Z0-9-_]{0,61}[a-zA-Z0-9])?\.)*(?:xn--)?[a-zA-Z0-9](?:[a-zA-Z0-9-_]{0,61}[a-zA-Z0-9])?$`)
|
||||
|
||||
var domainList domain.List
|
||||
|
||||
+51
-22
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package routes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -11,18 +11,19 @@ import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/util"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
"github.com/netbirdio/netbird/route"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/magiconair/properties/assert"
|
||||
|
||||
"github.com/netbirdio/netbird/management/domain"
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
"github.com/netbirdio/netbird/management/server/mock_server"
|
||||
)
|
||||
@@ -61,7 +62,7 @@ var baseExistingRoute = &route.Route{
|
||||
Groups: []string{existingGroupID},
|
||||
}
|
||||
|
||||
var testingAccount = &server.Account{
|
||||
var testingAccount = &types.Account{
|
||||
Id: testAccountID,
|
||||
Domain: "hotmail.com",
|
||||
Peers: map[string]*nbpeer.Peer{
|
||||
@@ -82,13 +83,13 @@ var testingAccount = &server.Account{
|
||||
},
|
||||
},
|
||||
},
|
||||
Users: map[string]*server.User{
|
||||
"test_user": server.NewAdminUser("test_user"),
|
||||
Users: map[string]*types.User{
|
||||
"test_user": types.NewAdminUser("test_user"),
|
||||
},
|
||||
}
|
||||
|
||||
func initRoutesTestData() *RoutesHandler {
|
||||
return &RoutesHandler{
|
||||
func initRoutesTestData() *handler {
|
||||
return &handler{
|
||||
accountManager: &mock_server.MockAccountManager{
|
||||
GetRouteFunc: func(_ context.Context, _ string, routeID route.ID, _ string) (*route.Route, error) {
|
||||
if routeID == existingRouteID {
|
||||
@@ -152,7 +153,7 @@ func initRoutesTestData() *RoutesHandler {
|
||||
return nil
|
||||
},
|
||||
GetAccountIDFromTokenFunc: func(_ context.Context, _ jwtclaims.AuthorizationClaims) (string, string, error) {
|
||||
//return testingAccount, testingAccount.Users["test_user"], nil
|
||||
// return testingAccount, testingAccount.Users["test_user"], nil
|
||||
return testingAccount.Id, testingAccount.Users["test_user"].Id, nil
|
||||
},
|
||||
},
|
||||
@@ -239,7 +240,7 @@ func TestRoutesHandlers(t *testing.T) {
|
||||
Id: existingRouteID,
|
||||
Description: "Post",
|
||||
NetworkId: "awesomeNet",
|
||||
Network: toPtr("192.168.0.0/16"),
|
||||
Network: util.ToPtr("192.168.0.0/16"),
|
||||
Peer: &existingPeerID,
|
||||
NetworkType: route.IPv4NetworkString,
|
||||
Masquerade: false,
|
||||
@@ -259,7 +260,7 @@ func TestRoutesHandlers(t *testing.T) {
|
||||
Id: existingRouteID,
|
||||
Description: "Post",
|
||||
NetworkId: "domainNet",
|
||||
Network: toPtr("invalid Prefix"),
|
||||
Network: util.ToPtr("invalid Prefix"),
|
||||
KeepRoute: true,
|
||||
Domains: &[]string{existingDomain},
|
||||
Peer: &existingPeerID,
|
||||
@@ -281,7 +282,7 @@ func TestRoutesHandlers(t *testing.T) {
|
||||
Id: existingRouteID,
|
||||
Description: "Post",
|
||||
NetworkId: "awesomeNet",
|
||||
Network: toPtr("192.168.0.0/16"),
|
||||
Network: util.ToPtr("192.168.0.0/16"),
|
||||
Peer: &existingPeerID,
|
||||
NetworkType: route.IPv4NetworkString,
|
||||
Masquerade: false,
|
||||
@@ -330,6 +331,14 @@ func TestRoutesHandlers(t *testing.T) {
|
||||
expectedStatus: http.StatusUnprocessableEntity,
|
||||
expectedBody: false,
|
||||
},
|
||||
{
|
||||
name: "POST Wildcard Domain",
|
||||
requestType: http.MethodPost,
|
||||
requestPath: "/api/routes",
|
||||
requestBody: bytes.NewBufferString(fmt.Sprintf(`{"Description":"Post","domains":["*.example.com"],"network_id":"awesomeNet","Peer":"%s","groups":["%s"]}`, existingPeerID, existingGroupID)),
|
||||
expectedStatus: http.StatusOK,
|
||||
expectedBody: false,
|
||||
},
|
||||
{
|
||||
name: "POST UnprocessableEntity when both network and domains are provided",
|
||||
requestType: http.MethodPost,
|
||||
@@ -377,7 +386,7 @@ func TestRoutesHandlers(t *testing.T) {
|
||||
Id: existingRouteID,
|
||||
Description: "Post",
|
||||
NetworkId: "awesomeNet",
|
||||
Network: toPtr("192.168.0.0/16"),
|
||||
Network: util.ToPtr("192.168.0.0/16"),
|
||||
Peer: &existingPeerID,
|
||||
NetworkType: route.IPv4NetworkString,
|
||||
Masquerade: false,
|
||||
@@ -396,7 +405,7 @@ func TestRoutesHandlers(t *testing.T) {
|
||||
Id: existingRouteID,
|
||||
Description: "Post",
|
||||
NetworkId: "awesomeNet",
|
||||
Network: toPtr("invalid Prefix"),
|
||||
Network: util.ToPtr("invalid Prefix"),
|
||||
Domains: &[]string{existingDomain},
|
||||
Peer: &existingPeerID,
|
||||
NetworkType: route.DomainNetworkString,
|
||||
@@ -417,7 +426,7 @@ func TestRoutesHandlers(t *testing.T) {
|
||||
Id: existingRouteID,
|
||||
Description: "Post",
|
||||
NetworkId: "awesomeNet",
|
||||
Network: toPtr("192.168.0.0/16"),
|
||||
Network: util.ToPtr("192.168.0.0/16"),
|
||||
Peer: &emptyString,
|
||||
PeerGroups: &[]string{existingGroupID},
|
||||
NetworkType: route.IPv4NetworkString,
|
||||
@@ -521,10 +530,10 @@ func TestRoutesHandlers(t *testing.T) {
|
||||
req := httptest.NewRequest(tc.requestType, tc.requestPath, tc.requestBody)
|
||||
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/api/routes/{routeId}", p.GetRoute).Methods("GET")
|
||||
router.HandleFunc("/api/routes/{routeId}", p.DeleteRoute).Methods("DELETE")
|
||||
router.HandleFunc("/api/routes", p.CreateRoute).Methods("POST")
|
||||
router.HandleFunc("/api/routes/{routeId}", p.UpdateRoute).Methods("PUT")
|
||||
router.HandleFunc("/api/routes/{routeId}", p.getRoute).Methods("GET")
|
||||
router.HandleFunc("/api/routes/{routeId}", p.deleteRoute).Methods("DELETE")
|
||||
router.HandleFunc("/api/routes", p.createRoute).Methods("POST")
|
||||
router.HandleFunc("/api/routes/{routeId}", p.updateRoute).Methods("PUT")
|
||||
router.ServeHTTP(recorder, req)
|
||||
|
||||
res := recorder.Result()
|
||||
@@ -609,6 +618,30 @@ func TestValidateDomains(t *testing.T) {
|
||||
expected: domain.List{"google.com"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Valid wildcard domain",
|
||||
domains: []string{"*.example.com"},
|
||||
expected: domain.List{"*.example.com"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Wildcard with dot domain",
|
||||
domains: []string{".*.example.com"},
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Wildcard with dot domain",
|
||||
domains: []string{".*.example.com"},
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid wildcard domain",
|
||||
domains: []string{"a.*.example.com"},
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -631,7 +664,3 @@ func toApiRoute(t *testing.T, r *route.Route) *api.Route {
|
||||
require.NoError(t, err, "Failed to convert route")
|
||||
return apiRoute
|
||||
}
|
||||
|
||||
func toPtr[T any](v T) *T {
|
||||
return &v
|
||||
}
|
||||
+38
-27
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package setup_keys
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -10,20 +10,31 @@ import (
|
||||
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||
"github.com/netbirdio/netbird/management/server/http/util"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
// SetupKeysHandler is a handler that returns a list of setup keys of the account
|
||||
type SetupKeysHandler struct {
|
||||
// handler is a handler that returns a list of setup keys of the account
|
||||
type handler struct {
|
||||
accountManager server.AccountManager
|
||||
claimsExtractor *jwtclaims.ClaimsExtractor
|
||||
}
|
||||
|
||||
// NewSetupKeysHandler creates a new SetupKeysHandler HTTP handler
|
||||
func NewSetupKeysHandler(accountManager server.AccountManager, authCfg AuthCfg) *SetupKeysHandler {
|
||||
return &SetupKeysHandler{
|
||||
func AddEndpoints(accountManager server.AccountManager, authCfg configs.AuthCfg, router *mux.Router) {
|
||||
keysHandler := newHandler(accountManager, authCfg)
|
||||
router.HandleFunc("/setup-keys", keysHandler.getAllSetupKeys).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/setup-keys", keysHandler.createSetupKey).Methods("POST", "OPTIONS")
|
||||
router.HandleFunc("/setup-keys/{keyId}", keysHandler.getSetupKey).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/setup-keys/{keyId}", keysHandler.updateSetupKey).Methods("PUT", "OPTIONS")
|
||||
router.HandleFunc("/setup-keys/{keyId}", keysHandler.deleteSetupKey).Methods("DELETE", "OPTIONS")
|
||||
}
|
||||
|
||||
// newHandler creates a new setup key handler
|
||||
func newHandler(accountManager server.AccountManager, authCfg configs.AuthCfg) *handler {
|
||||
return &handler{
|
||||
accountManager: accountManager,
|
||||
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||
jwtclaims.WithAudience(authCfg.Audience),
|
||||
@@ -32,8 +43,8 @@ func NewSetupKeysHandler(accountManager server.AccountManager, authCfg AuthCfg)
|
||||
}
|
||||
}
|
||||
|
||||
// CreateSetupKey is a POST requests that creates a new SetupKey
|
||||
func (h *SetupKeysHandler) CreateSetupKey(w http.ResponseWriter, r *http.Request) {
|
||||
// createSetupKey is a POST requests that creates a new SetupKey
|
||||
func (h *handler) createSetupKey(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -53,8 +64,8 @@ func (h *SetupKeysHandler) CreateSetupKey(w http.ResponseWriter, r *http.Request
|
||||
return
|
||||
}
|
||||
|
||||
if !(server.SetupKeyType(req.Type) == server.SetupKeyReusable ||
|
||||
server.SetupKeyType(req.Type) == server.SetupKeyOneOff) {
|
||||
if !(types.SetupKeyType(req.Type) == types.SetupKeyReusable ||
|
||||
types.SetupKeyType(req.Type) == types.SetupKeyOneOff) {
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "unknown setup key type %s", req.Type), w)
|
||||
return
|
||||
}
|
||||
@@ -75,22 +86,22 @@ func (h *SetupKeysHandler) CreateSetupKey(w http.ResponseWriter, r *http.Request
|
||||
ephemeral = *req.Ephemeral
|
||||
}
|
||||
|
||||
setupKey, err := h.accountManager.CreateSetupKey(r.Context(), accountID, req.Name, server.SetupKeyType(req.Type), expiresIn,
|
||||
setupKey, err := h.accountManager.CreateSetupKey(r.Context(), accountID, req.Name, types.SetupKeyType(req.Type), expiresIn,
|
||||
req.AutoGroups, req.UsageLimit, userID, ephemeral)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
apiSetupKeys := toResponseBody(setupKey)
|
||||
apiSetupKeys := ToResponseBody(setupKey)
|
||||
// for the creation we need to send the plain key
|
||||
apiSetupKeys.Key = setupKey.Key
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, apiSetupKeys)
|
||||
}
|
||||
|
||||
// GetSetupKey is a GET request to get a SetupKey by ID
|
||||
func (h *SetupKeysHandler) GetSetupKey(w http.ResponseWriter, r *http.Request) {
|
||||
// getSetupKey is a GET request to get a SetupKey by ID
|
||||
func (h *handler) getSetupKey(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -114,8 +125,8 @@ func (h *SetupKeysHandler) GetSetupKey(w http.ResponseWriter, r *http.Request) {
|
||||
writeSuccess(r.Context(), w, key)
|
||||
}
|
||||
|
||||
// UpdateSetupKey is a PUT request to update server.SetupKey
|
||||
func (h *SetupKeysHandler) UpdateSetupKey(w http.ResponseWriter, r *http.Request) {
|
||||
// updateSetupKey is a PUT request to update server.SetupKey
|
||||
func (h *handler) updateSetupKey(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -142,7 +153,7 @@ func (h *SetupKeysHandler) UpdateSetupKey(w http.ResponseWriter, r *http.Request
|
||||
return
|
||||
}
|
||||
|
||||
newKey := &server.SetupKey{}
|
||||
newKey := &types.SetupKey{}
|
||||
newKey.AutoGroups = req.AutoGroups
|
||||
newKey.Revoked = req.Revoked
|
||||
newKey.Id = keyID
|
||||
@@ -155,8 +166,8 @@ func (h *SetupKeysHandler) UpdateSetupKey(w http.ResponseWriter, r *http.Request
|
||||
writeSuccess(r.Context(), w, newKey)
|
||||
}
|
||||
|
||||
// GetAllSetupKeys is a GET request that returns a list of SetupKey
|
||||
func (h *SetupKeysHandler) GetAllSetupKeys(w http.ResponseWriter, r *http.Request) {
|
||||
// getAllSetupKeys is a GET request that returns a list of SetupKey
|
||||
func (h *handler) getAllSetupKeys(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -172,13 +183,13 @@ func (h *SetupKeysHandler) GetAllSetupKeys(w http.ResponseWriter, r *http.Reques
|
||||
|
||||
apiSetupKeys := make([]*api.SetupKey, 0)
|
||||
for _, key := range setupKeys {
|
||||
apiSetupKeys = append(apiSetupKeys, toResponseBody(key))
|
||||
apiSetupKeys = append(apiSetupKeys, ToResponseBody(key))
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, apiSetupKeys)
|
||||
}
|
||||
|
||||
func (h *SetupKeysHandler) DeleteSetupKey(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *handler) deleteSetupKey(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -199,20 +210,20 @@ func (h *SetupKeysHandler) DeleteSetupKey(w http.ResponseWriter, r *http.Request
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, emptyObject{})
|
||||
util.WriteJSONObject(r.Context(), w, util.EmptyObject{})
|
||||
}
|
||||
|
||||
func writeSuccess(ctx context.Context, w http.ResponseWriter, key *server.SetupKey) {
|
||||
func writeSuccess(ctx context.Context, w http.ResponseWriter, key *types.SetupKey) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(200)
|
||||
err := json.NewEncoder(w).Encode(toResponseBody(key))
|
||||
err := json.NewEncoder(w).Encode(ToResponseBody(key))
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, w)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func toResponseBody(key *server.SetupKey) *api.SetupKey {
|
||||
func ToResponseBody(key *types.SetupKey) *api.SetupKey {
|
||||
var state string
|
||||
switch {
|
||||
case key.IsExpired():
|
||||
@@ -229,12 +240,12 @@ func toResponseBody(key *server.SetupKey) *api.SetupKey {
|
||||
Id: key.Id,
|
||||
Key: key.KeySecret,
|
||||
Name: key.Name,
|
||||
Expires: key.ExpiresAt,
|
||||
Expires: key.GetExpiresAt(),
|
||||
Type: string(key.Type),
|
||||
Valid: key.IsValid(),
|
||||
Revoked: key.Revoked,
|
||||
UsedTimes: key.UsedTimes,
|
||||
LastUsed: key.LastUsed,
|
||||
LastUsed: key.GetLastUsed(),
|
||||
State: state,
|
||||
AutoGroups: key.AutoGroups,
|
||||
UpdatedAt: key.UpdatedAt,
|
||||
+27
-27
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package setup_keys
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -14,11 +14,11 @@ import (
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"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/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -28,17 +28,17 @@ const (
|
||||
notFoundSetupKeyID = "notFoundSetupKeyID"
|
||||
)
|
||||
|
||||
func initSetupKeysTestMetaData(defaultKey *server.SetupKey, newKey *server.SetupKey, updatedSetupKey *server.SetupKey,
|
||||
user *server.User,
|
||||
) *SetupKeysHandler {
|
||||
return &SetupKeysHandler{
|
||||
func initSetupKeysTestMetaData(defaultKey *types.SetupKey, newKey *types.SetupKey, updatedSetupKey *types.SetupKey,
|
||||
user *types.User,
|
||||
) *handler {
|
||||
return &handler{
|
||||
accountManager: &mock_server.MockAccountManager{
|
||||
GetAccountIDFromTokenFunc: func(_ context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error) {
|
||||
return claims.AccountId, claims.UserId, nil
|
||||
},
|
||||
CreateSetupKeyFunc: func(_ context.Context, _ string, keyName string, typ server.SetupKeyType, _ time.Duration, _ []string,
|
||||
CreateSetupKeyFunc: func(_ context.Context, _ string, keyName string, typ types.SetupKeyType, _ time.Duration, _ []string,
|
||||
_ int, _ string, ephemeral bool,
|
||||
) (*server.SetupKey, error) {
|
||||
) (*types.SetupKey, error) {
|
||||
if keyName == newKey.Name || typ != newKey.Type {
|
||||
nk := newKey.Copy()
|
||||
nk.Ephemeral = ephemeral
|
||||
@@ -46,7 +46,7 @@ func initSetupKeysTestMetaData(defaultKey *server.SetupKey, newKey *server.Setup
|
||||
}
|
||||
return nil, fmt.Errorf("failed creating setup key")
|
||||
},
|
||||
GetSetupKeyFunc: func(_ context.Context, accountID, userID, keyID string) (*server.SetupKey, error) {
|
||||
GetSetupKeyFunc: func(_ context.Context, accountID, userID, keyID string) (*types.SetupKey, error) {
|
||||
switch keyID {
|
||||
case defaultKey.Id:
|
||||
return defaultKey, nil
|
||||
@@ -57,15 +57,15 @@ func initSetupKeysTestMetaData(defaultKey *server.SetupKey, newKey *server.Setup
|
||||
}
|
||||
},
|
||||
|
||||
SaveSetupKeyFunc: func(_ context.Context, accountID string, key *server.SetupKey, _ string) (*server.SetupKey, error) {
|
||||
SaveSetupKeyFunc: func(_ context.Context, accountID string, key *types.SetupKey, _ string) (*types.SetupKey, error) {
|
||||
if key.Id == updatedSetupKey.Id {
|
||||
return updatedSetupKey, nil
|
||||
}
|
||||
return nil, status.Errorf(status.NotFound, "key %s not found", key.Id)
|
||||
},
|
||||
|
||||
ListSetupKeysFunc: func(_ context.Context, accountID, userID string) ([]*server.SetupKey, error) {
|
||||
return []*server.SetupKey{defaultKey}, nil
|
||||
ListSetupKeysFunc: func(_ context.Context, accountID, userID string) ([]*types.SetupKey, error) {
|
||||
return []*types.SetupKey{defaultKey}, nil
|
||||
},
|
||||
|
||||
DeleteSetupKeyFunc: func(_ context.Context, accountID, userID, keyID string) error {
|
||||
@@ -80,7 +80,7 @@ func initSetupKeysTestMetaData(defaultKey *server.SetupKey, newKey *server.Setup
|
||||
return jwtclaims.AuthorizationClaims{
|
||||
UserId: user.Id,
|
||||
Domain: "hotmail.com",
|
||||
AccountId: testAccountID,
|
||||
AccountId: "testAccountId",
|
||||
}
|
||||
}),
|
||||
),
|
||||
@@ -88,20 +88,20 @@ func initSetupKeysTestMetaData(defaultKey *server.SetupKey, newKey *server.Setup
|
||||
}
|
||||
|
||||
func TestSetupKeysHandlers(t *testing.T) {
|
||||
defaultSetupKey, _ := server.GenerateDefaultSetupKey()
|
||||
defaultSetupKey, _ := types.GenerateDefaultSetupKey()
|
||||
defaultSetupKey.Id = existingSetupKeyID
|
||||
|
||||
adminUser := server.NewAdminUser("test_user")
|
||||
adminUser := types.NewAdminUser("test_user")
|
||||
|
||||
newSetupKey, plainKey := server.GenerateSetupKey(newSetupKeyName, server.SetupKeyReusable, 0, []string{"group-1"},
|
||||
server.SetupKeyUnlimitedUsage, true)
|
||||
newSetupKey, plainKey := types.GenerateSetupKey(newSetupKeyName, types.SetupKeyReusable, 0, []string{"group-1"},
|
||||
types.SetupKeyUnlimitedUsage, true)
|
||||
newSetupKey.Key = plainKey
|
||||
updatedDefaultSetupKey := defaultSetupKey.Copy()
|
||||
updatedDefaultSetupKey.AutoGroups = []string{"group-1"}
|
||||
updatedDefaultSetupKey.Name = updatedSetupKeyName
|
||||
updatedDefaultSetupKey.Revoked = true
|
||||
|
||||
expectedNewKey := toResponseBody(newSetupKey)
|
||||
expectedNewKey := ToResponseBody(newSetupKey)
|
||||
expectedNewKey.Key = plainKey
|
||||
tt := []struct {
|
||||
name string
|
||||
@@ -119,7 +119,7 @@ func TestSetupKeysHandlers(t *testing.T) {
|
||||
requestPath: "/api/setup-keys",
|
||||
expectedStatus: http.StatusOK,
|
||||
expectedBody: true,
|
||||
expectedSetupKeys: []*api.SetupKey{toResponseBody(defaultSetupKey)},
|
||||
expectedSetupKeys: []*api.SetupKey{ToResponseBody(defaultSetupKey)},
|
||||
},
|
||||
{
|
||||
name: "Get Existing Setup Key",
|
||||
@@ -127,7 +127,7 @@ func TestSetupKeysHandlers(t *testing.T) {
|
||||
requestPath: "/api/setup-keys/" + existingSetupKeyID,
|
||||
expectedStatus: http.StatusOK,
|
||||
expectedBody: true,
|
||||
expectedSetupKey: toResponseBody(defaultSetupKey),
|
||||
expectedSetupKey: ToResponseBody(defaultSetupKey),
|
||||
},
|
||||
{
|
||||
name: "Get Not Existing Setup Key",
|
||||
@@ -158,7 +158,7 @@ func TestSetupKeysHandlers(t *testing.T) {
|
||||
))),
|
||||
expectedStatus: http.StatusOK,
|
||||
expectedBody: true,
|
||||
expectedSetupKey: toResponseBody(updatedDefaultSetupKey),
|
||||
expectedSetupKey: ToResponseBody(updatedDefaultSetupKey),
|
||||
},
|
||||
{
|
||||
name: "Delete Setup Key",
|
||||
@@ -178,11 +178,11 @@ func TestSetupKeysHandlers(t *testing.T) {
|
||||
req := httptest.NewRequest(tc.requestType, tc.requestPath, tc.requestBody)
|
||||
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/api/setup-keys", handler.GetAllSetupKeys).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/api/setup-keys", handler.CreateSetupKey).Methods("POST", "OPTIONS")
|
||||
router.HandleFunc("/api/setup-keys/{keyId}", handler.GetSetupKey).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/api/setup-keys/{keyId}", handler.UpdateSetupKey).Methods("PUT", "OPTIONS")
|
||||
router.HandleFunc("/api/setup-keys/{keyId}", handler.DeleteSetupKey).Methods("DELETE", "OPTIONS")
|
||||
router.HandleFunc("/api/setup-keys", handler.getAllSetupKeys).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/api/setup-keys", handler.createSetupKey).Methods("POST", "OPTIONS")
|
||||
router.HandleFunc("/api/setup-keys/{keyId}", handler.getSetupKey).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/api/setup-keys/{keyId}", handler.updateSetupKey).Methods("PUT", "OPTIONS")
|
||||
router.HandleFunc("/api/setup-keys/{keyId}", handler.deleteSetupKey).Methods("DELETE", "OPTIONS")
|
||||
router.ServeHTTP(recorder, req)
|
||||
|
||||
res := recorder.Result()
|
||||
@@ -227,7 +227,7 @@ func TestSetupKeysHandlers(t *testing.T) {
|
||||
func assertKeys(t *testing.T, got *api.SetupKey, expected *api.SetupKey) {
|
||||
t.Helper()
|
||||
// this comparison is done manually because when converting to JSON dates formatted differently
|
||||
// assert.Equal(t, got.UpdatedAt, tc.expectedSetupKey.UpdatedAt) //doesn't work
|
||||
// assert.Equal(t, got.UpdatedAt, tc.expectedResponse.UpdatedAt) //doesn't work
|
||||
assert.WithinDurationf(t, got.UpdatedAt, expected.UpdatedAt, 0, "")
|
||||
assert.WithinDurationf(t, got.Expires, expected.Expires, 0, "")
|
||||
assert.Equal(t, got.Name, expected.Name)
|
||||
+29
-24
@@ -1,28 +1,37 @@
|
||||
package http
|
||||
package users
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||
"github.com/netbirdio/netbird/management/server/http/util"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
// PATHandler is the nameserver group handler of the account
|
||||
type PATHandler struct {
|
||||
// patHandler is the nameserver group handler of the account
|
||||
type patHandler struct {
|
||||
accountManager server.AccountManager
|
||||
claimsExtractor *jwtclaims.ClaimsExtractor
|
||||
}
|
||||
|
||||
// NewPATsHandler creates a new PATHandler HTTP handler
|
||||
func NewPATsHandler(accountManager server.AccountManager, authCfg AuthCfg) *PATHandler {
|
||||
return &PATHandler{
|
||||
func addUsersTokensEndpoint(accountManager server.AccountManager, authCfg configs.AuthCfg, router *mux.Router) {
|
||||
tokenHandler := newPATsHandler(accountManager, authCfg)
|
||||
router.HandleFunc("/users/{userId}/tokens", tokenHandler.getAllTokens).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/users/{userId}/tokens", tokenHandler.createToken).Methods("POST", "OPTIONS")
|
||||
router.HandleFunc("/users/{userId}/tokens/{tokenId}", tokenHandler.getToken).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/users/{userId}/tokens/{tokenId}", tokenHandler.deleteToken).Methods("DELETE", "OPTIONS")
|
||||
}
|
||||
|
||||
// newPATsHandler creates a new patHandler HTTP handler
|
||||
func newPATsHandler(accountManager server.AccountManager, authCfg configs.AuthCfg) *patHandler {
|
||||
return &patHandler{
|
||||
accountManager: accountManager,
|
||||
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||
jwtclaims.WithAudience(authCfg.Audience),
|
||||
@@ -31,8 +40,8 @@ func NewPATsHandler(accountManager server.AccountManager, authCfg AuthCfg) *PATH
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllTokens is HTTP GET handler that returns a list of all personal access tokens for the given user
|
||||
func (h *PATHandler) GetAllTokens(w http.ResponseWriter, r *http.Request) {
|
||||
// getAllTokens is HTTP GET handler that returns a list of all personal access tokens for the given user
|
||||
func (h *patHandler) getAllTokens(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -61,8 +70,8 @@ func (h *PATHandler) GetAllTokens(w http.ResponseWriter, r *http.Request) {
|
||||
util.WriteJSONObject(r.Context(), w, patResponse)
|
||||
}
|
||||
|
||||
// GetToken is HTTP GET handler that returns a personal access token for the given user
|
||||
func (h *PATHandler) GetToken(w http.ResponseWriter, r *http.Request) {
|
||||
// getToken is HTTP GET handler that returns a personal access token for the given user
|
||||
func (h *patHandler) getToken(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -92,8 +101,8 @@ func (h *PATHandler) GetToken(w http.ResponseWriter, r *http.Request) {
|
||||
util.WriteJSONObject(r.Context(), w, toPATResponse(pat))
|
||||
}
|
||||
|
||||
// CreateToken is HTTP POST handler that creates a personal access token for the given user
|
||||
func (h *PATHandler) CreateToken(w http.ResponseWriter, r *http.Request) {
|
||||
// createToken is HTTP POST handler that creates a personal access token for the given user
|
||||
func (h *patHandler) createToken(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -124,8 +133,8 @@ func (h *PATHandler) CreateToken(w http.ResponseWriter, r *http.Request) {
|
||||
util.WriteJSONObject(r.Context(), w, toPATGeneratedResponse(pat))
|
||||
}
|
||||
|
||||
// DeleteToken is HTTP DELETE handler that deletes a personal access token for the given user
|
||||
func (h *PATHandler) DeleteToken(w http.ResponseWriter, r *http.Request) {
|
||||
// deleteToken is HTTP DELETE handler that deletes a personal access token for the given user
|
||||
func (h *patHandler) deleteToken(w http.ResponseWriter, r *http.Request) {
|
||||
claims := h.claimsExtractor.FromRequestContext(r)
|
||||
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
||||
if err != nil {
|
||||
@@ -152,25 +161,21 @@ func (h *PATHandler) DeleteToken(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, emptyObject{})
|
||||
util.WriteJSONObject(r.Context(), w, util.EmptyObject{})
|
||||
}
|
||||
|
||||
func toPATResponse(pat *server.PersonalAccessToken) *api.PersonalAccessToken {
|
||||
var lastUsed *time.Time
|
||||
if !pat.LastUsed.IsZero() {
|
||||
lastUsed = &pat.LastUsed
|
||||
}
|
||||
func toPATResponse(pat *types.PersonalAccessToken) *api.PersonalAccessToken {
|
||||
return &api.PersonalAccessToken{
|
||||
CreatedAt: pat.CreatedAt,
|
||||
CreatedBy: pat.CreatedBy,
|
||||
Name: pat.Name,
|
||||
ExpirationDate: pat.ExpirationDate,
|
||||
ExpirationDate: pat.GetExpirationDate(),
|
||||
Id: pat.ID,
|
||||
LastUsed: lastUsed,
|
||||
LastUsed: pat.LastUsed,
|
||||
}
|
||||
}
|
||||
|
||||
func toPATGeneratedResponse(pat *server.PersonalAccessTokenGenerated) *api.PersonalAccessTokenGenerated {
|
||||
func toPATGeneratedResponse(pat *types.PersonalAccessTokenGenerated) *api.PersonalAccessTokenGenerated {
|
||||
return &api.PersonalAccessTokenGenerated{
|
||||
PlainToken: pat.PlainToken,
|
||||
PersonalAccessToken: *toPATResponse(&pat.PersonalAccessToken),
|
||||
+26
-25
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package users
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -12,13 +12,14 @@ import (
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/netbirdio/netbird/management/server/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"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/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -31,49 +32,49 @@ const (
|
||||
testDomain = "hotmail.com"
|
||||
)
|
||||
|
||||
var testAccount = &server.Account{
|
||||
var testAccount = &types.Account{
|
||||
Id: existingAccountID,
|
||||
Domain: testDomain,
|
||||
Users: map[string]*server.User{
|
||||
Users: map[string]*types.User{
|
||||
existingUserID: {
|
||||
Id: existingUserID,
|
||||
PATs: map[string]*server.PersonalAccessToken{
|
||||
PATs: map[string]*types.PersonalAccessToken{
|
||||
existingTokenID: {
|
||||
ID: existingTokenID,
|
||||
Name: "My first token",
|
||||
HashedToken: "someHash",
|
||||
ExpirationDate: time.Now().UTC().AddDate(0, 0, 7),
|
||||
ExpirationDate: util.ToPtr(time.Now().UTC().AddDate(0, 0, 7)),
|
||||
CreatedBy: existingUserID,
|
||||
CreatedAt: time.Now().UTC(),
|
||||
LastUsed: time.Now().UTC(),
|
||||
LastUsed: util.ToPtr(time.Now().UTC()),
|
||||
},
|
||||
"token2": {
|
||||
ID: "token2",
|
||||
Name: "My second token",
|
||||
HashedToken: "someOtherHash",
|
||||
ExpirationDate: time.Now().UTC().AddDate(0, 0, 7),
|
||||
ExpirationDate: util.ToPtr(time.Now().UTC().AddDate(0, 0, 7)),
|
||||
CreatedBy: existingUserID,
|
||||
CreatedAt: time.Now().UTC(),
|
||||
LastUsed: time.Now().UTC(),
|
||||
LastUsed: util.ToPtr(time.Now().UTC()),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func initPATTestData() *PATHandler {
|
||||
return &PATHandler{
|
||||
func initPATTestData() *patHandler {
|
||||
return &patHandler{
|
||||
accountManager: &mock_server.MockAccountManager{
|
||||
CreatePATFunc: func(_ context.Context, accountID string, initiatorUserID string, targetUserID string, tokenName string, expiresIn int) (*server.PersonalAccessTokenGenerated, error) {
|
||||
CreatePATFunc: func(_ context.Context, accountID string, initiatorUserID string, targetUserID string, tokenName string, expiresIn int) (*types.PersonalAccessTokenGenerated, error) {
|
||||
if accountID != existingAccountID {
|
||||
return nil, status.Errorf(status.NotFound, "account with ID %s not found", accountID)
|
||||
}
|
||||
if targetUserID != existingUserID {
|
||||
return nil, status.Errorf(status.NotFound, "user with ID %s not found", targetUserID)
|
||||
}
|
||||
return &server.PersonalAccessTokenGenerated{
|
||||
return &types.PersonalAccessTokenGenerated{
|
||||
PlainToken: "nbp_z1pvsg2wP3EzmEou4S679KyTNhov632eyrXe",
|
||||
PersonalAccessToken: server.PersonalAccessToken{},
|
||||
PersonalAccessToken: types.PersonalAccessToken{},
|
||||
}, nil
|
||||
},
|
||||
|
||||
@@ -92,7 +93,7 @@ func initPATTestData() *PATHandler {
|
||||
}
|
||||
return nil
|
||||
},
|
||||
GetPATFunc: func(_ context.Context, accountID string, initiatorUserID string, targetUserID string, tokenID string) (*server.PersonalAccessToken, error) {
|
||||
GetPATFunc: func(_ context.Context, accountID string, initiatorUserID string, targetUserID string, tokenID string) (*types.PersonalAccessToken, error) {
|
||||
if accountID != existingAccountID {
|
||||
return nil, status.Errorf(status.NotFound, "account with ID %s not found", accountID)
|
||||
}
|
||||
@@ -104,14 +105,14 @@ func initPATTestData() *PATHandler {
|
||||
}
|
||||
return testAccount.Users[existingUserID].PATs[existingTokenID], nil
|
||||
},
|
||||
GetAllPATsFunc: func(_ context.Context, accountID string, initiatorUserID string, targetUserID string) ([]*server.PersonalAccessToken, error) {
|
||||
GetAllPATsFunc: func(_ context.Context, accountID string, initiatorUserID string, targetUserID string) ([]*types.PersonalAccessToken, error) {
|
||||
if accountID != existingAccountID {
|
||||
return nil, status.Errorf(status.NotFound, "account with ID %s not found", accountID)
|
||||
}
|
||||
if targetUserID != existingUserID {
|
||||
return nil, status.Errorf(status.NotFound, "user with ID %s not found", targetUserID)
|
||||
}
|
||||
return []*server.PersonalAccessToken{testAccount.Users[existingUserID].PATs[existingTokenID], testAccount.Users[existingUserID].PATs["token2"]}, nil
|
||||
return []*types.PersonalAccessToken{testAccount.Users[existingUserID].PATs[existingTokenID], testAccount.Users[existingUserID].PATs["token2"]}, nil
|
||||
},
|
||||
},
|
||||
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||
@@ -186,10 +187,10 @@ func TestTokenHandlers(t *testing.T) {
|
||||
req := httptest.NewRequest(tc.requestType, tc.requestPath, tc.requestBody)
|
||||
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/api/users/{userId}/tokens", p.GetAllTokens).Methods("GET")
|
||||
router.HandleFunc("/api/users/{userId}/tokens/{tokenId}", p.GetToken).Methods("GET")
|
||||
router.HandleFunc("/api/users/{userId}/tokens", p.CreateToken).Methods("POST")
|
||||
router.HandleFunc("/api/users/{userId}/tokens/{tokenId}", p.DeleteToken).Methods("DELETE")
|
||||
router.HandleFunc("/api/users/{userId}/tokens", p.getAllTokens).Methods("GET")
|
||||
router.HandleFunc("/api/users/{userId}/tokens/{tokenId}", p.getToken).Methods("GET")
|
||||
router.HandleFunc("/api/users/{userId}/tokens", p.createToken).Methods("POST")
|
||||
router.HandleFunc("/api/users/{userId}/tokens/{tokenId}", p.deleteToken).Methods("DELETE")
|
||||
router.ServeHTTP(recorder, req)
|
||||
|
||||
res := recorder.Result()
|
||||
@@ -217,7 +218,7 @@ func TestTokenHandlers(t *testing.T) {
|
||||
t.Fatalf("Sent content is not in correct json format; %v", err)
|
||||
}
|
||||
assert.NotEmpty(t, got.PlainToken)
|
||||
assert.Equal(t, server.PATLength, len(got.PlainToken))
|
||||
assert.Equal(t, types.PATLength, len(got.PlainToken))
|
||||
case "Get All Tokens":
|
||||
expectedTokens := []api.PersonalAccessToken{
|
||||
toTokenResponse(*testAccount.Users[existingUserID].PATs[existingTokenID]),
|
||||
@@ -243,13 +244,13 @@ func TestTokenHandlers(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func toTokenResponse(serverToken server.PersonalAccessToken) api.PersonalAccessToken {
|
||||
func toTokenResponse(serverToken types.PersonalAccessToken) api.PersonalAccessToken {
|
||||
return api.PersonalAccessToken{
|
||||
Id: serverToken.ID,
|
||||
Name: serverToken.Name,
|
||||
CreatedAt: serverToken.CreatedAt,
|
||||
LastUsed: &serverToken.LastUsed,
|
||||
LastUsed: serverToken.LastUsed,
|
||||
CreatedBy: serverToken.CreatedBy,
|
||||
ExpirationDate: serverToken.ExpirationDate,
|
||||
ExpirationDate: serverToken.GetExpirationDate(),
|
||||
}
|
||||
}
|
||||
+37
-25
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package users
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -9,22 +9,34 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||
"github.com/netbirdio/netbird/management/server/http/util"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
)
|
||||
|
||||
// UsersHandler is a handler that returns users of the account
|
||||
type UsersHandler struct {
|
||||
// handler is a handler that returns users of the account
|
||||
type handler struct {
|
||||
accountManager server.AccountManager
|
||||
claimsExtractor *jwtclaims.ClaimsExtractor
|
||||
}
|
||||
|
||||
// NewUsersHandler creates a new UsersHandler HTTP handler
|
||||
func NewUsersHandler(accountManager server.AccountManager, authCfg AuthCfg) *UsersHandler {
|
||||
return &UsersHandler{
|
||||
func AddEndpoints(accountManager server.AccountManager, authCfg configs.AuthCfg, router *mux.Router) {
|
||||
userHandler := newHandler(accountManager, authCfg)
|
||||
router.HandleFunc("/users", userHandler.getAllUsers).Methods("GET", "OPTIONS")
|
||||
router.HandleFunc("/users/{userId}", userHandler.updateUser).Methods("PUT", "OPTIONS")
|
||||
router.HandleFunc("/users/{userId}", userHandler.deleteUser).Methods("DELETE", "OPTIONS")
|
||||
router.HandleFunc("/users", userHandler.createUser).Methods("POST", "OPTIONS")
|
||||
router.HandleFunc("/users/{userId}/invite", userHandler.inviteUser).Methods("POST", "OPTIONS")
|
||||
addUsersTokensEndpoint(accountManager, authCfg, router)
|
||||
}
|
||||
|
||||
// newHandler creates a new UsersHandler HTTP handler
|
||||
func newHandler(accountManager server.AccountManager, authCfg configs.AuthCfg) *handler {
|
||||
return &handler{
|
||||
accountManager: accountManager,
|
||||
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||
jwtclaims.WithAudience(authCfg.Audience),
|
||||
@@ -33,8 +45,8 @@ func NewUsersHandler(accountManager server.AccountManager, authCfg AuthCfg) *Use
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateUser is a PUT requests to update User data
|
||||
func (h *UsersHandler) UpdateUser(w http.ResponseWriter, r *http.Request) {
|
||||
// updateUser is a PUT requests to update User data
|
||||
func (h *handler) updateUser(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPut {
|
||||
util.WriteErrorResponse("wrong HTTP method", http.StatusMethodNotAllowed, w)
|
||||
return
|
||||
@@ -72,13 +84,13 @@ func (h *UsersHandler) UpdateUser(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
userRole := server.StrRoleToUserRole(req.Role)
|
||||
if userRole == server.UserRoleUnknown {
|
||||
userRole := types.StrRoleToUserRole(req.Role)
|
||||
if userRole == types.UserRoleUnknown {
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "invalid user role"), w)
|
||||
return
|
||||
}
|
||||
|
||||
newUser, err := h.accountManager.SaveUser(r.Context(), accountID, userID, &server.User{
|
||||
newUser, err := h.accountManager.SaveUser(r.Context(), accountID, userID, &types.User{
|
||||
Id: targetUserID,
|
||||
Role: userRole,
|
||||
AutoGroups: req.AutoGroups,
|
||||
@@ -94,8 +106,8 @@ func (h *UsersHandler) UpdateUser(w http.ResponseWriter, r *http.Request) {
|
||||
util.WriteJSONObject(r.Context(), w, toUserResponse(newUser, claims.UserId))
|
||||
}
|
||||
|
||||
// DeleteUser is a DELETE request to delete a user
|
||||
func (h *UsersHandler) DeleteUser(w http.ResponseWriter, r *http.Request) {
|
||||
// deleteUser is a DELETE request to delete a user
|
||||
func (h *handler) deleteUser(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodDelete {
|
||||
util.WriteErrorResponse("wrong HTTP method", http.StatusMethodNotAllowed, w)
|
||||
return
|
||||
@@ -121,11 +133,11 @@ func (h *UsersHandler) DeleteUser(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, emptyObject{})
|
||||
util.WriteJSONObject(r.Context(), w, util.EmptyObject{})
|
||||
}
|
||||
|
||||
// CreateUser creates a User in the system with a status "invited" (effectively this is a user invite).
|
||||
func (h *UsersHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
|
||||
// createUser creates a User in the system with a status "invited" (effectively this is a user invite).
|
||||
func (h *handler) createUser(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
util.WriteErrorResponse("wrong HTTP method", http.StatusMethodNotAllowed, w)
|
||||
return
|
||||
@@ -145,7 +157,7 @@ func (h *UsersHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if server.StrRoleToUserRole(req.Role) == server.UserRoleUnknown {
|
||||
if types.StrRoleToUserRole(req.Role) == types.UserRoleUnknown {
|
||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "unknown user role %s", req.Role), w)
|
||||
return
|
||||
}
|
||||
@@ -160,13 +172,13 @@ func (h *UsersHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
|
||||
name = *req.Name
|
||||
}
|
||||
|
||||
newUser, err := h.accountManager.CreateUser(r.Context(), accountID, userID, &server.UserInfo{
|
||||
newUser, err := h.accountManager.CreateUser(r.Context(), accountID, userID, &types.UserInfo{
|
||||
Email: email,
|
||||
Name: name,
|
||||
Role: req.Role,
|
||||
AutoGroups: req.AutoGroups,
|
||||
IsServiceUser: req.IsServiceUser,
|
||||
Issued: server.UserIssuedAPI,
|
||||
Issued: types.UserIssuedAPI,
|
||||
})
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
@@ -175,9 +187,9 @@ func (h *UsersHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
|
||||
util.WriteJSONObject(r.Context(), w, toUserResponse(newUser, claims.UserId))
|
||||
}
|
||||
|
||||
// GetAllUsers returns a list of users of the account this user belongs to.
|
||||
// getAllUsers returns a list of users of the account this user belongs to.
|
||||
// It also gathers additional user data (like email and name) from the IDP manager.
|
||||
func (h *UsersHandler) GetAllUsers(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *handler) getAllUsers(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
util.WriteErrorResponse("wrong HTTP method", http.StatusMethodNotAllowed, w)
|
||||
return
|
||||
@@ -222,9 +234,9 @@ func (h *UsersHandler) GetAllUsers(w http.ResponseWriter, r *http.Request) {
|
||||
util.WriteJSONObject(r.Context(), w, users)
|
||||
}
|
||||
|
||||
// InviteUser resend invitations to users who haven't activated their accounts,
|
||||
// inviteUser resend invitations to users who haven't activated their accounts,
|
||||
// prior to the expiration period.
|
||||
func (h *UsersHandler) InviteUser(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *handler) inviteUser(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
util.WriteErrorResponse("wrong HTTP method", http.StatusMethodNotAllowed, w)
|
||||
return
|
||||
@@ -250,10 +262,10 @@ func (h *UsersHandler) InviteUser(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, emptyObject{})
|
||||
util.WriteJSONObject(r.Context(), w, util.EmptyObject{})
|
||||
}
|
||||
|
||||
func toUserResponse(user *server.UserInfo, currenUserID string) *api.User {
|
||||
func toUserResponse(user *types.UserInfo, currenUserID string) *api.User {
|
||||
autoGroups := user.AutoGroups
|
||||
if autoGroups == nil {
|
||||
autoGroups = []string{}
|
||||
+25
-25
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package users
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -13,11 +13,11 @@ import (
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"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/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -26,54 +26,54 @@ const (
|
||||
regularUserID = "regularUserID"
|
||||
)
|
||||
|
||||
var usersTestAccount = &server.Account{
|
||||
var usersTestAccount = &types.Account{
|
||||
Id: existingAccountID,
|
||||
Domain: testDomain,
|
||||
Users: map[string]*server.User{
|
||||
Users: map[string]*types.User{
|
||||
existingUserID: {
|
||||
Id: existingUserID,
|
||||
Role: "admin",
|
||||
IsServiceUser: false,
|
||||
AutoGroups: []string{"group_1"},
|
||||
Issued: server.UserIssuedAPI,
|
||||
Issued: types.UserIssuedAPI,
|
||||
},
|
||||
regularUserID: {
|
||||
Id: regularUserID,
|
||||
Role: "user",
|
||||
IsServiceUser: false,
|
||||
AutoGroups: []string{"group_1"},
|
||||
Issued: server.UserIssuedAPI,
|
||||
Issued: types.UserIssuedAPI,
|
||||
},
|
||||
serviceUserID: {
|
||||
Id: serviceUserID,
|
||||
Role: "user",
|
||||
IsServiceUser: true,
|
||||
AutoGroups: []string{"group_1"},
|
||||
Issued: server.UserIssuedAPI,
|
||||
Issued: types.UserIssuedAPI,
|
||||
},
|
||||
nonDeletableServiceUserID: {
|
||||
Id: serviceUserID,
|
||||
Role: "admin",
|
||||
IsServiceUser: true,
|
||||
NonDeletable: true,
|
||||
Issued: server.UserIssuedIntegration,
|
||||
Issued: types.UserIssuedIntegration,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func initUsersTestData() *UsersHandler {
|
||||
return &UsersHandler{
|
||||
func initUsersTestData() *handler {
|
||||
return &handler{
|
||||
accountManager: &mock_server.MockAccountManager{
|
||||
GetAccountIDFromTokenFunc: func(_ context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error) {
|
||||
return usersTestAccount.Id, claims.UserId, nil
|
||||
},
|
||||
GetUserByIDFunc: func(ctx context.Context, id string) (*server.User, error) {
|
||||
GetUserByIDFunc: func(ctx context.Context, id string) (*types.User, error) {
|
||||
return usersTestAccount.Users[id], nil
|
||||
},
|
||||
GetUsersFromAccountFunc: func(_ context.Context, accountID, userID string) ([]*server.UserInfo, error) {
|
||||
users := make([]*server.UserInfo, 0)
|
||||
GetUsersFromAccountFunc: func(_ context.Context, accountID, userID string) ([]*types.UserInfo, error) {
|
||||
users := make([]*types.UserInfo, 0)
|
||||
for _, v := range usersTestAccount.Users {
|
||||
users = append(users, &server.UserInfo{
|
||||
users = append(users, &types.UserInfo{
|
||||
ID: v.Id,
|
||||
Role: string(v.Role),
|
||||
Name: "",
|
||||
@@ -85,7 +85,7 @@ func initUsersTestData() *UsersHandler {
|
||||
}
|
||||
return users, nil
|
||||
},
|
||||
CreateUserFunc: func(_ context.Context, accountID, userID string, key *server.UserInfo) (*server.UserInfo, error) {
|
||||
CreateUserFunc: func(_ context.Context, accountID, userID string, key *types.UserInfo) (*types.UserInfo, error) {
|
||||
if userID != existingUserID {
|
||||
return nil, status.Errorf(status.NotFound, "user with ID %s does not exists", userID)
|
||||
}
|
||||
@@ -100,7 +100,7 @@ func initUsersTestData() *UsersHandler {
|
||||
}
|
||||
return nil
|
||||
},
|
||||
SaveUserFunc: func(_ context.Context, accountID, userID string, update *server.User) (*server.UserInfo, error) {
|
||||
SaveUserFunc: func(_ context.Context, accountID, userID string, update *types.User) (*types.UserInfo, error) {
|
||||
if update.Id == notFoundUserID {
|
||||
return nil, status.Errorf(status.NotFound, "user with ID %s does not exists", update.Id)
|
||||
}
|
||||
@@ -109,7 +109,7 @@ func initUsersTestData() *UsersHandler {
|
||||
return nil, status.Errorf(status.NotFound, "user with ID %s does not exists", userID)
|
||||
}
|
||||
|
||||
info, err := update.Copy().ToUserInfo(nil, &server.Settings{RegularUsersViewBlocked: false})
|
||||
info, err := update.Copy().ToUserInfo(nil, &types.Settings{RegularUsersViewBlocked: false})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -147,7 +147,7 @@ func TestGetUsers(t *testing.T) {
|
||||
requestPath string
|
||||
expectedUserIDs []string
|
||||
}{
|
||||
{name: "GetAllUsers", requestType: http.MethodGet, requestPath: "/api/users", expectedStatus: http.StatusOK, expectedUserIDs: []string{existingUserID, regularUserID, serviceUserID}},
|
||||
{name: "getAllUsers", requestType: http.MethodGet, requestPath: "/api/users", expectedStatus: http.StatusOK, expectedUserIDs: []string{existingUserID, regularUserID, serviceUserID}},
|
||||
{name: "GetOnlyServiceUsers", requestType: http.MethodGet, requestPath: "/api/users?service_user=true", expectedStatus: http.StatusOK, expectedUserIDs: []string{serviceUserID}},
|
||||
{name: "GetOnlyRegularUsers", requestType: http.MethodGet, requestPath: "/api/users?service_user=false", expectedStatus: http.StatusOK, expectedUserIDs: []string{existingUserID, regularUserID}},
|
||||
}
|
||||
@@ -159,7 +159,7 @@ func TestGetUsers(t *testing.T) {
|
||||
recorder := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(tc.requestType, tc.requestPath, nil)
|
||||
|
||||
userHandler.GetAllUsers(recorder, req)
|
||||
userHandler.getAllUsers(recorder, req)
|
||||
|
||||
res := recorder.Result()
|
||||
defer res.Body.Close()
|
||||
@@ -175,7 +175,7 @@ func TestGetUsers(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
respBody := []*server.UserInfo{}
|
||||
respBody := []*types.UserInfo{}
|
||||
err = json.Unmarshal(content, &respBody)
|
||||
if err != nil {
|
||||
t.Fatalf("Sent content is not in correct json format; %v", err)
|
||||
@@ -265,7 +265,7 @@ func TestUpdateUser(t *testing.T) {
|
||||
req := httptest.NewRequest(tc.requestType, tc.requestPath, tc.requestBody)
|
||||
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/api/users/{userId}", userHandler.UpdateUser).Methods("PUT")
|
||||
router.HandleFunc("/api/users/{userId}", userHandler.updateUser).Methods("PUT")
|
||||
router.ServeHTTP(recorder, req)
|
||||
|
||||
res := recorder.Result()
|
||||
@@ -342,7 +342,7 @@ func TestCreateUser(t *testing.T) {
|
||||
requestType string
|
||||
requestPath string
|
||||
requestBody io.Reader
|
||||
expectedResult []*server.User
|
||||
expectedResult []*types.User
|
||||
}{
|
||||
{name: "CreateServiceUser", requestType: http.MethodPost, requestPath: "/api/users", expectedStatus: http.StatusOK, requestBody: bytes.NewBuffer(serviceUserString)},
|
||||
// right now creation is blocked in AC middleware, will be refactored in the future
|
||||
@@ -356,7 +356,7 @@ func TestCreateUser(t *testing.T) {
|
||||
req := httptest.NewRequest(tc.requestType, tc.requestPath, tc.requestBody)
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
userHandler.CreateUser(rr, req)
|
||||
userHandler.createUser(rr, req)
|
||||
|
||||
res := rr.Result()
|
||||
defer res.Body.Close()
|
||||
@@ -401,7 +401,7 @@ func TestInviteUser(t *testing.T) {
|
||||
req = mux.SetURLVars(req, tc.requestVars)
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
userHandler.InviteUser(rr, req)
|
||||
userHandler.inviteUser(rr, req)
|
||||
|
||||
res := rr.Result()
|
||||
defer res.Body.Close()
|
||||
@@ -454,7 +454,7 @@ func TestDeleteUser(t *testing.T) {
|
||||
req = mux.SetURLVars(req, tc.requestVars)
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
userHandler.DeleteUser(rr, req)
|
||||
userHandler.deleteUser(rr, req)
|
||||
|
||||
res := rr.Result()
|
||||
defer res.Body.Close()
|
||||
@@ -7,16 +7,16 @@ import (
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"github.com/netbirdio/netbird/management/server/http/middleware/bypass"
|
||||
"github.com/netbirdio/netbird/management/server/http/util"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
)
|
||||
|
||||
// GetUser function defines a function to fetch user from Account by jwtclaims.AuthorizationClaims
|
||||
type GetUser func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (*server.User, error)
|
||||
type GetUser func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (*types.User, error)
|
||||
|
||||
// AccessControl middleware to restrict to make POST/PUT/DELETE requests by admin only
|
||||
type AccessControl struct {
|
||||
|
||||
@@ -11,16 +11,16 @@ import (
|
||||
"github.com/golang-jwt/jwt"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
nbContext "github.com/netbirdio/netbird/management/server/context"
|
||||
"github.com/netbirdio/netbird/management/server/http/middleware/bypass"
|
||||
"github.com/netbirdio/netbird/management/server/http/util"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
// GetAccountFromPATFunc function
|
||||
type GetAccountFromPATFunc func(ctx context.Context, token string) (*server.Account, *server.User, *server.PersonalAccessToken, error)
|
||||
type GetAccountFromPATFunc func(ctx context.Context, token string) (*types.Account, *types.User, *types.PersonalAccessToken, error)
|
||||
|
||||
// ValidateAndParseTokenFunc function
|
||||
type ValidateAndParseTokenFunc func(ctx context.Context, token string) (*jwt.Token, error)
|
||||
@@ -161,7 +161,7 @@ func (m *AuthMiddleware) checkPATFromRequest(w http.ResponseWriter, r *http.Requ
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid Token: %w", err)
|
||||
}
|
||||
if time.Now().After(pat.ExpirationDate) {
|
||||
if time.Now().After(pat.GetExpirationDate()) {
|
||||
return fmt.Errorf("token expired")
|
||||
}
|
||||
|
||||
@@ -175,6 +175,7 @@ func (m *AuthMiddleware) checkPATFromRequest(w http.ResponseWriter, r *http.Requ
|
||||
claimMaps[m.audience+jwtclaims.AccountIDSuffix] = account.Id
|
||||
claimMaps[m.audience+jwtclaims.DomainIDSuffix] = account.Domain
|
||||
claimMaps[m.audience+jwtclaims.DomainCategorySuffix] = account.DomainCategory
|
||||
claimMaps[jwtclaims.IsToken] = true
|
||||
jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claimMaps)
|
||||
newRequest := r.WithContext(context.WithValue(r.Context(), jwtclaims.TokenUserProperty, jwtToken)) //nolint
|
||||
// Update the current request with the new context information.
|
||||
|
||||
@@ -9,10 +9,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/netbirdio/netbird/management/server/util"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"github.com/netbirdio/netbird/management/server/http/middleware/bypass"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -28,28 +29,28 @@ const (
|
||||
wrongToken = "wrongToken"
|
||||
)
|
||||
|
||||
var testAccount = &server.Account{
|
||||
var testAccount = &types.Account{
|
||||
Id: accountID,
|
||||
Domain: domain,
|
||||
Users: map[string]*server.User{
|
||||
Users: map[string]*types.User{
|
||||
userID: {
|
||||
Id: userID,
|
||||
PATs: map[string]*server.PersonalAccessToken{
|
||||
PATs: map[string]*types.PersonalAccessToken{
|
||||
tokenID: {
|
||||
ID: tokenID,
|
||||
Name: "My first token",
|
||||
HashedToken: "someHash",
|
||||
ExpirationDate: time.Now().UTC().AddDate(0, 0, 7),
|
||||
ExpirationDate: util.ToPtr(time.Now().UTC().AddDate(0, 0, 7)),
|
||||
CreatedBy: userID,
|
||||
CreatedAt: time.Now().UTC(),
|
||||
LastUsed: time.Now().UTC(),
|
||||
LastUsed: util.ToPtr(time.Now().UTC()),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func mockGetAccountFromPAT(_ context.Context, token string) (*server.Account, *server.User, *server.PersonalAccessToken, error) {
|
||||
func mockGetAccountFromPAT(_ context.Context, token string) (*types.Account, *types.User, *types.PersonalAccessToken, error) {
|
||||
if token == PAT {
|
||||
return testAccount, testAccount.Users[userID], testAccount.Users[userID].PATs[tokenID], nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
//go:build benchmark
|
||||
// +build benchmark
|
||||
|
||||
package benchmarks
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/http/testing/testing_tools"
|
||||
)
|
||||
|
||||
// Map to store peers, groups, users, and setupKeys by name
|
||||
var benchCasesPeers = map[string]testing_tools.BenchmarkCase{
|
||||
"Peers - XS": {Peers: 5, Groups: 10000, Users: 10000, SetupKeys: 10000},
|
||||
"Peers - S": {Peers: 100, Groups: 5, Users: 5, SetupKeys: 5},
|
||||
"Peers - M": {Peers: 1000, Groups: 20, Users: 20, SetupKeys: 100},
|
||||
"Peers - L": {Peers: 5000, Groups: 5, Users: 5, SetupKeys: 5},
|
||||
"Groups - L": {Peers: 5000, Groups: 10000, Users: 5, SetupKeys: 5},
|
||||
"Users - L": {Peers: 5000, Groups: 5, Users: 10000, SetupKeys: 5},
|
||||
"Setup Keys - L": {Peers: 5000, Groups: 5, Users: 5, SetupKeys: 10000},
|
||||
"Peers - XL": {Peers: 25000, Groups: 50, Users: 100, SetupKeys: 500},
|
||||
}
|
||||
|
||||
func BenchmarkUpdatePeer(b *testing.B) {
|
||||
var expectedMetrics = map[string]testing_tools.PerformanceMetrics{
|
||||
"Peers - XS": {MinMsPerOpLocal: 1300, MaxMsPerOpLocal: 1700, MinMsPerOpCICD: 2200, MaxMsPerOpCICD: 13000},
|
||||
"Peers - S": {MinMsPerOpLocal: 100, MaxMsPerOpLocal: 130, MinMsPerOpCICD: 80, MaxMsPerOpCICD: 200},
|
||||
"Peers - M": {MinMsPerOpLocal: 160, MaxMsPerOpLocal: 190, MinMsPerOpCICD: 100, MaxMsPerOpCICD: 500},
|
||||
"Peers - L": {MinMsPerOpLocal: 400, MaxMsPerOpLocal: 430, MinMsPerOpCICD: 450, MaxMsPerOpCICD: 1400},
|
||||
"Groups - L": {MinMsPerOpLocal: 1200, MaxMsPerOpLocal: 1500, MinMsPerOpCICD: 1900, MaxMsPerOpCICD: 13000},
|
||||
"Users - L": {MinMsPerOpLocal: 600, MaxMsPerOpLocal: 800, MinMsPerOpCICD: 800, MaxMsPerOpCICD: 2800},
|
||||
"Setup Keys - L": {MinMsPerOpLocal: 400, MaxMsPerOpLocal: 700, MinMsPerOpCICD: 600, MaxMsPerOpCICD: 1300},
|
||||
"Peers - XL": {MinMsPerOpLocal: 1400, MaxMsPerOpLocal: 1900, MinMsPerOpCICD: 2200, MaxMsPerOpCICD: 5000},
|
||||
}
|
||||
|
||||
log.SetOutput(io.Discard)
|
||||
defer log.SetOutput(os.Stderr)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
for name, bc := range benchCasesPeers {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
apiHandler, am, _ := testing_tools.BuildApiBlackBoxWithDBState(b, "../testdata/peers.sql", nil, false)
|
||||
testing_tools.PopulateTestData(b, am.(*server.DefaultAccountManager), bc.Peers, bc.Groups, bc.Users, bc.SetupKeys)
|
||||
|
||||
b.ResetTimer()
|
||||
start := time.Now()
|
||||
for i := 0; i < b.N; i++ {
|
||||
requestBody := api.PeerRequest{
|
||||
Name: "peer" + strconv.Itoa(i),
|
||||
}
|
||||
|
||||
// the time marshal will be recorded as well but for our use case that is ok
|
||||
body, err := json.Marshal(requestBody)
|
||||
assert.NoError(b, err)
|
||||
|
||||
req := testing_tools.BuildRequest(b, body, http.MethodPut, "/api/peers/"+testing_tools.TestPeerId, testing_tools.TestAdminId)
|
||||
apiHandler.ServeHTTP(recorder, req)
|
||||
}
|
||||
|
||||
testing_tools.EvaluateBenchmarkResults(b, name, time.Since(start), expectedMetrics[name], recorder)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetOnePeer(b *testing.B) {
|
||||
var expectedMetrics = map[string]testing_tools.PerformanceMetrics{
|
||||
"Peers - XS": {MinMsPerOpLocal: 600, MaxMsPerOpLocal: 900, MinMsPerOpCICD: 1100, MaxMsPerOpCICD: 7000},
|
||||
"Peers - S": {MinMsPerOpLocal: 3, MaxMsPerOpLocal: 7, MinMsPerOpCICD: 2, MaxMsPerOpCICD: 30},
|
||||
"Peers - M": {MinMsPerOpLocal: 20, MaxMsPerOpLocal: 40, MinMsPerOpCICD: 35, MaxMsPerOpCICD: 80},
|
||||
"Peers - L": {MinMsPerOpLocal: 120, MaxMsPerOpLocal: 160, MinMsPerOpCICD: 100, MaxMsPerOpCICD: 300},
|
||||
"Groups - L": {MinMsPerOpLocal: 500, MaxMsPerOpLocal: 750, MinMsPerOpCICD: 900, MaxMsPerOpCICD: 6500},
|
||||
"Users - L": {MinMsPerOpLocal: 200, MaxMsPerOpLocal: 300, MinMsPerOpCICD: 200, MaxMsPerOpCICD: 600},
|
||||
"Setup Keys - L": {MinMsPerOpLocal: 200, MaxMsPerOpLocal: 300, MinMsPerOpCICD: 200, MaxMsPerOpCICD: 600},
|
||||
"Peers - XL": {MinMsPerOpLocal: 600, MaxMsPerOpLocal: 800, MinMsPerOpCICD: 600, MaxMsPerOpCICD: 1500},
|
||||
}
|
||||
|
||||
log.SetOutput(io.Discard)
|
||||
defer log.SetOutput(os.Stderr)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
for name, bc := range benchCasesPeers {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
apiHandler, am, _ := testing_tools.BuildApiBlackBoxWithDBState(b, "../testdata/peers.sql", nil, false)
|
||||
testing_tools.PopulateTestData(b, am.(*server.DefaultAccountManager), bc.Peers, bc.Groups, bc.Users, bc.SetupKeys)
|
||||
|
||||
b.ResetTimer()
|
||||
start := time.Now()
|
||||
for i := 0; i < b.N; i++ {
|
||||
req := testing_tools.BuildRequest(b, nil, http.MethodGet, "/api/peers/"+testing_tools.TestPeerId, testing_tools.TestAdminId)
|
||||
apiHandler.ServeHTTP(recorder, req)
|
||||
}
|
||||
|
||||
testing_tools.EvaluateBenchmarkResults(b, name, time.Since(start), expectedMetrics[name], recorder)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetAllPeers(b *testing.B) {
|
||||
var expectedMetrics = map[string]testing_tools.PerformanceMetrics{
|
||||
"Peers - XS": {MinMsPerOpLocal: 600, MaxMsPerOpLocal: 900, MinMsPerOpCICD: 1100, MaxMsPerOpCICD: 6000},
|
||||
"Peers - S": {MinMsPerOpLocal: 4, MaxMsPerOpLocal: 10, MinMsPerOpCICD: 7, MaxMsPerOpCICD: 30},
|
||||
"Peers - M": {MinMsPerOpLocal: 20, MaxMsPerOpLocal: 50, MinMsPerOpCICD: 40, MaxMsPerOpCICD: 90},
|
||||
"Peers - L": {MinMsPerOpLocal: 130, MaxMsPerOpLocal: 170, MinMsPerOpCICD: 150, MaxMsPerOpCICD: 350},
|
||||
"Groups - L": {MinMsPerOpLocal: 5000, MaxMsPerOpLocal: 5500, MinMsPerOpCICD: 7000, MaxMsPerOpCICD: 15000},
|
||||
"Users - L": {MinMsPerOpLocal: 250, MaxMsPerOpLocal: 300, MinMsPerOpCICD: 250, MaxMsPerOpCICD: 700},
|
||||
"Setup Keys - L": {MinMsPerOpLocal: 250, MaxMsPerOpLocal: 350, MinMsPerOpCICD: 250, MaxMsPerOpCICD: 700},
|
||||
"Peers - XL": {MinMsPerOpLocal: 900, MaxMsPerOpLocal: 1300, MinMsPerOpCICD: 1100, MaxMsPerOpCICD: 2200},
|
||||
}
|
||||
|
||||
log.SetOutput(io.Discard)
|
||||
defer log.SetOutput(os.Stderr)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
for name, bc := range benchCasesPeers {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
apiHandler, am, _ := testing_tools.BuildApiBlackBoxWithDBState(b, "../testdata/peers.sql", nil, false)
|
||||
testing_tools.PopulateTestData(b, am.(*server.DefaultAccountManager), bc.Peers, bc.Groups, bc.Users, bc.SetupKeys)
|
||||
|
||||
b.ResetTimer()
|
||||
start := time.Now()
|
||||
for i := 0; i < b.N; i++ {
|
||||
req := testing_tools.BuildRequest(b, nil, http.MethodGet, "/api/peers", testing_tools.TestAdminId)
|
||||
apiHandler.ServeHTTP(recorder, req)
|
||||
}
|
||||
|
||||
testing_tools.EvaluateBenchmarkResults(b, name, time.Since(start), expectedMetrics[name], recorder)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDeletePeer(b *testing.B) {
|
||||
var expectedMetrics = map[string]testing_tools.PerformanceMetrics{
|
||||
"Peers - XS": {MinMsPerOpLocal: 600, MaxMsPerOpLocal: 800, MinMsPerOpCICD: 1100, MaxMsPerOpCICD: 7000},
|
||||
"Peers - S": {MinMsPerOpLocal: 20, MaxMsPerOpLocal: 50, MinMsPerOpCICD: 50, MaxMsPerOpCICD: 210},
|
||||
"Peers - M": {MinMsPerOpLocal: 20, MaxMsPerOpLocal: 50, MinMsPerOpCICD: 50, MaxMsPerOpCICD: 230},
|
||||
"Peers - L": {MinMsPerOpLocal: 20, MaxMsPerOpLocal: 50, MinMsPerOpCICD: 50, MaxMsPerOpCICD: 210},
|
||||
"Groups - L": {MinMsPerOpLocal: 400, MaxMsPerOpLocal: 550, MinMsPerOpCICD: 700, MaxMsPerOpCICD: 5500},
|
||||
"Users - L": {MinMsPerOpLocal: 170, MaxMsPerOpLocal: 210, MinMsPerOpCICD: 290, MaxMsPerOpCICD: 1700},
|
||||
"Setup Keys - L": {MinMsPerOpLocal: 30, MaxMsPerOpLocal: 125, MinMsPerOpCICD: 55, MaxMsPerOpCICD: 280},
|
||||
"Peers - XL": {MinMsPerOpLocal: 30, MaxMsPerOpLocal: 50, MinMsPerOpCICD: 60, MaxMsPerOpCICD: 250},
|
||||
}
|
||||
|
||||
log.SetOutput(io.Discard)
|
||||
defer log.SetOutput(os.Stderr)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
for name, bc := range benchCasesPeers {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
apiHandler, am, _ := testing_tools.BuildApiBlackBoxWithDBState(b, "../testdata/peers.sql", nil, false)
|
||||
testing_tools.PopulateTestData(b, am.(*server.DefaultAccountManager), 1000, bc.Groups, bc.Users, bc.SetupKeys)
|
||||
|
||||
b.ResetTimer()
|
||||
start := time.Now()
|
||||
for i := 0; i < b.N; i++ {
|
||||
req := testing_tools.BuildRequest(b, nil, http.MethodDelete, "/api/peers/"+"oldpeer-"+strconv.Itoa(i), testing_tools.TestAdminId)
|
||||
apiHandler.ServeHTTP(recorder, req)
|
||||
}
|
||||
|
||||
testing_tools.EvaluateBenchmarkResults(b, name, time.Since(start), expectedMetrics[name], recorder)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
//go:build benchmark
|
||||
// +build benchmark
|
||||
|
||||
package benchmarks
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/http/testing/testing_tools"
|
||||
)
|
||||
|
||||
// Map to store peers, groups, users, and setupKeys by name
|
||||
var benchCasesSetupKeys = map[string]testing_tools.BenchmarkCase{
|
||||
"Setup Keys - XS": {Peers: 10000, Groups: 10000, Users: 10000, SetupKeys: 5},
|
||||
"Setup Keys - S": {Peers: 5, Groups: 5, Users: 5, SetupKeys: 100},
|
||||
"Setup Keys - M": {Peers: 100, Groups: 20, Users: 20, SetupKeys: 1000},
|
||||
"Setup Keys - L": {Peers: 5, Groups: 5, Users: 5, SetupKeys: 5000},
|
||||
"Peers - L": {Peers: 10000, Groups: 5, Users: 5, SetupKeys: 5000},
|
||||
"Groups - L": {Peers: 5, Groups: 10000, Users: 5, SetupKeys: 5000},
|
||||
"Users - L": {Peers: 5, Groups: 5, Users: 10000, SetupKeys: 5000},
|
||||
"Setup Keys - XL": {Peers: 500, Groups: 50, Users: 100, SetupKeys: 25000},
|
||||
}
|
||||
|
||||
func BenchmarkCreateSetupKey(b *testing.B) {
|
||||
var expectedMetrics = map[string]testing_tools.PerformanceMetrics{
|
||||
"Setup Keys - XS": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 17},
|
||||
"Setup Keys - S": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 17},
|
||||
"Setup Keys - M": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 17},
|
||||
"Setup Keys - L": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 17},
|
||||
"Peers - L": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 17},
|
||||
"Groups - L": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 17},
|
||||
"Users - L": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 17},
|
||||
"Setup Keys - XL": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 17},
|
||||
}
|
||||
|
||||
log.SetOutput(io.Discard)
|
||||
defer log.SetOutput(os.Stderr)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
for name, bc := range benchCasesSetupKeys {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
apiHandler, am, _ := testing_tools.BuildApiBlackBoxWithDBState(b, "../testdata/setup_keys.sql", nil, false)
|
||||
testing_tools.PopulateTestData(b, am.(*server.DefaultAccountManager), bc.Peers, bc.Groups, bc.Users, bc.SetupKeys)
|
||||
|
||||
b.ResetTimer()
|
||||
start := time.Now()
|
||||
for i := 0; i < b.N; i++ {
|
||||
requestBody := api.CreateSetupKeyRequest{
|
||||
AutoGroups: []string{testing_tools.TestGroupId},
|
||||
ExpiresIn: testing_tools.ExpiresIn,
|
||||
Name: testing_tools.NewKeyName + strconv.Itoa(i),
|
||||
Type: "reusable",
|
||||
UsageLimit: 0,
|
||||
}
|
||||
|
||||
// the time marshal will be recorded as well but for our use case that is ok
|
||||
body, err := json.Marshal(requestBody)
|
||||
assert.NoError(b, err)
|
||||
|
||||
req := testing_tools.BuildRequest(b, body, http.MethodPost, "/api/setup-keys", testing_tools.TestAdminId)
|
||||
apiHandler.ServeHTTP(recorder, req)
|
||||
}
|
||||
|
||||
testing_tools.EvaluateBenchmarkResults(b, name, time.Since(start), expectedMetrics[name], recorder)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUpdateSetupKey(b *testing.B) {
|
||||
var expectedMetrics = map[string]testing_tools.PerformanceMetrics{
|
||||
"Setup Keys - XS": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 3, MinMsPerOpCICD: 2, MaxMsPerOpCICD: 19},
|
||||
"Setup Keys - S": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 3, MinMsPerOpCICD: 2, MaxMsPerOpCICD: 19},
|
||||
"Setup Keys - M": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 3, MinMsPerOpCICD: 2, MaxMsPerOpCICD: 19},
|
||||
"Setup Keys - L": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 3, MinMsPerOpCICD: 2, MaxMsPerOpCICD: 19},
|
||||
"Peers - L": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 3, MinMsPerOpCICD: 2, MaxMsPerOpCICD: 19},
|
||||
"Groups - L": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 3, MinMsPerOpCICD: 2, MaxMsPerOpCICD: 19},
|
||||
"Users - L": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 3, MinMsPerOpCICD: 2, MaxMsPerOpCICD: 19},
|
||||
"Setup Keys - XL": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 3, MinMsPerOpCICD: 2, MaxMsPerOpCICD: 19},
|
||||
}
|
||||
|
||||
log.SetOutput(io.Discard)
|
||||
defer log.SetOutput(os.Stderr)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
for name, bc := range benchCasesSetupKeys {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
apiHandler, am, _ := testing_tools.BuildApiBlackBoxWithDBState(b, "../testdata/setup_keys.sql", nil, false)
|
||||
testing_tools.PopulateTestData(b, am.(*server.DefaultAccountManager), bc.Peers, bc.Groups, bc.Users, bc.SetupKeys)
|
||||
|
||||
b.ResetTimer()
|
||||
start := time.Now()
|
||||
for i := 0; i < b.N; i++ {
|
||||
groupId := testing_tools.TestGroupId
|
||||
if i%2 == 0 {
|
||||
groupId = testing_tools.NewGroupId
|
||||
}
|
||||
requestBody := api.SetupKeyRequest{
|
||||
AutoGroups: []string{groupId},
|
||||
Revoked: false,
|
||||
}
|
||||
|
||||
// the time marshal will be recorded as well but for our use case that is ok
|
||||
body, err := json.Marshal(requestBody)
|
||||
assert.NoError(b, err)
|
||||
|
||||
req := testing_tools.BuildRequest(b, body, http.MethodPut, "/api/setup-keys/"+testing_tools.TestKeyId, testing_tools.TestAdminId)
|
||||
apiHandler.ServeHTTP(recorder, req)
|
||||
}
|
||||
|
||||
testing_tools.EvaluateBenchmarkResults(b, name, time.Since(start), expectedMetrics[name], recorder)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetOneSetupKey(b *testing.B) {
|
||||
var expectedMetrics = map[string]testing_tools.PerformanceMetrics{
|
||||
"Setup Keys - XS": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 16},
|
||||
"Setup Keys - S": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 16},
|
||||
"Setup Keys - M": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 16},
|
||||
"Setup Keys - L": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 16},
|
||||
"Peers - L": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 16},
|
||||
"Groups - L": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 16},
|
||||
"Users - L": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 16},
|
||||
"Setup Keys - XL": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 16},
|
||||
}
|
||||
|
||||
log.SetOutput(io.Discard)
|
||||
defer log.SetOutput(os.Stderr)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
for name, bc := range benchCasesSetupKeys {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
apiHandler, am, _ := testing_tools.BuildApiBlackBoxWithDBState(b, "../testdata/setup_keys.sql", nil, false)
|
||||
testing_tools.PopulateTestData(b, am.(*server.DefaultAccountManager), bc.Peers, bc.Groups, bc.Users, bc.SetupKeys)
|
||||
|
||||
b.ResetTimer()
|
||||
start := time.Now()
|
||||
for i := 0; i < b.N; i++ {
|
||||
req := testing_tools.BuildRequest(b, nil, http.MethodGet, "/api/setup-keys/"+testing_tools.TestKeyId, testing_tools.TestAdminId)
|
||||
apiHandler.ServeHTTP(recorder, req)
|
||||
}
|
||||
|
||||
testing_tools.EvaluateBenchmarkResults(b, name, time.Since(start), expectedMetrics[name], recorder)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetAllSetupKeys(b *testing.B) {
|
||||
var expectedMetrics = map[string]testing_tools.PerformanceMetrics{
|
||||
"Setup Keys - XS": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 12},
|
||||
"Setup Keys - S": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 15},
|
||||
"Setup Keys - M": {MinMsPerOpLocal: 5, MaxMsPerOpLocal: 10, MinMsPerOpCICD: 5, MaxMsPerOpCICD: 40},
|
||||
"Setup Keys - L": {MinMsPerOpLocal: 30, MaxMsPerOpLocal: 50, MinMsPerOpCICD: 30, MaxMsPerOpCICD: 150},
|
||||
"Peers - L": {MinMsPerOpLocal: 30, MaxMsPerOpLocal: 50, MinMsPerOpCICD: 30, MaxMsPerOpCICD: 150},
|
||||
"Groups - L": {MinMsPerOpLocal: 30, MaxMsPerOpLocal: 50, MinMsPerOpCICD: 30, MaxMsPerOpCICD: 150},
|
||||
"Users - L": {MinMsPerOpLocal: 30, MaxMsPerOpLocal: 50, MinMsPerOpCICD: 30, MaxMsPerOpCICD: 150},
|
||||
"Setup Keys - XL": {MinMsPerOpLocal: 140, MaxMsPerOpLocal: 220, MinMsPerOpCICD: 150, MaxMsPerOpCICD: 500},
|
||||
}
|
||||
|
||||
log.SetOutput(io.Discard)
|
||||
defer log.SetOutput(os.Stderr)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
for name, bc := range benchCasesSetupKeys {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
apiHandler, am, _ := testing_tools.BuildApiBlackBoxWithDBState(b, "../testdata/setup_keys.sql", nil, false)
|
||||
testing_tools.PopulateTestData(b, am.(*server.DefaultAccountManager), bc.Peers, bc.Groups, bc.Users, bc.SetupKeys)
|
||||
|
||||
b.ResetTimer()
|
||||
start := time.Now()
|
||||
for i := 0; i < b.N; i++ {
|
||||
req := testing_tools.BuildRequest(b, nil, http.MethodGet, "/api/setup-keys", testing_tools.TestAdminId)
|
||||
apiHandler.ServeHTTP(recorder, req)
|
||||
}
|
||||
|
||||
testing_tools.EvaluateBenchmarkResults(b, name, time.Since(start), expectedMetrics[name], recorder)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDeleteSetupKey(b *testing.B) {
|
||||
var expectedMetrics = map[string]testing_tools.PerformanceMetrics{
|
||||
"Setup Keys - XS": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 16},
|
||||
"Setup Keys - S": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 16},
|
||||
"Setup Keys - M": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 16},
|
||||
"Setup Keys - L": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 16},
|
||||
"Peers - L": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 16},
|
||||
"Groups - L": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 16},
|
||||
"Users - L": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 16},
|
||||
"Setup Keys - XL": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 16},
|
||||
}
|
||||
|
||||
log.SetOutput(io.Discard)
|
||||
defer log.SetOutput(os.Stderr)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
for name, bc := range benchCasesSetupKeys {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
apiHandler, am, _ := testing_tools.BuildApiBlackBoxWithDBState(b, "../testdata/setup_keys.sql", nil, false)
|
||||
testing_tools.PopulateTestData(b, am.(*server.DefaultAccountManager), bc.Peers, bc.Groups, bc.Users, 1000)
|
||||
|
||||
b.ResetTimer()
|
||||
start := time.Now()
|
||||
for i := 0; i < b.N; i++ {
|
||||
req := testing_tools.BuildRequest(b, nil, http.MethodDelete, "/api/setup-keys/"+"oldkey-"+strconv.Itoa(i), testing_tools.TestAdminId)
|
||||
apiHandler.ServeHTTP(recorder, req)
|
||||
}
|
||||
|
||||
testing_tools.EvaluateBenchmarkResults(b, name, time.Since(start), expectedMetrics[name], recorder)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
//go:build benchmark
|
||||
// +build benchmark
|
||||
|
||||
package benchmarks
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
"github.com/netbirdio/netbird/management/server/http/testing/testing_tools"
|
||||
)
|
||||
|
||||
// Map to store peers, groups, users, and setupKeys by name
|
||||
var benchCasesUsers = map[string]testing_tools.BenchmarkCase{
|
||||
"Users - XS": {Peers: 10000, Groups: 10000, Users: 5, SetupKeys: 10000},
|
||||
"Users - S": {Peers: 5, Groups: 5, Users: 10, SetupKeys: 5},
|
||||
"Users - M": {Peers: 100, Groups: 20, Users: 1000, SetupKeys: 1000},
|
||||
"Users - L": {Peers: 5, Groups: 5, Users: 5000, SetupKeys: 5},
|
||||
"Peers - L": {Peers: 10000, Groups: 5, Users: 5000, SetupKeys: 5},
|
||||
"Groups - L": {Peers: 5, Groups: 10000, Users: 5000, SetupKeys: 5},
|
||||
"Setup Keys - L": {Peers: 5, Groups: 5, Users: 5000, SetupKeys: 10000},
|
||||
"Users - XL": {Peers: 500, Groups: 50, Users: 25000, SetupKeys: 3000},
|
||||
}
|
||||
|
||||
func BenchmarkUpdateUser(b *testing.B) {
|
||||
var expectedMetrics = map[string]testing_tools.PerformanceMetrics{
|
||||
"Users - XS": {MinMsPerOpLocal: 700, MaxMsPerOpLocal: 1000, MinMsPerOpCICD: 1300, MaxMsPerOpCICD: 7000},
|
||||
"Users - S": {MinMsPerOpLocal: 1, MaxMsPerOpLocal: 5, MinMsPerOpCICD: 6, MaxMsPerOpCICD: 40},
|
||||
"Users - M": {MinMsPerOpLocal: 20, MaxMsPerOpLocal: 40, MinMsPerOpCICD: 30, MaxMsPerOpCICD: 200},
|
||||
"Users - L": {MinMsPerOpLocal: 60, MaxMsPerOpLocal: 100, MinMsPerOpCICD: 130, MaxMsPerOpCICD: 700},
|
||||
"Peers - L": {MinMsPerOpLocal: 300, MaxMsPerOpLocal: 500, MinMsPerOpCICD: 550, MaxMsPerOpCICD: 2000},
|
||||
"Groups - L": {MinMsPerOpLocal: 400, MaxMsPerOpLocal: 600, MinMsPerOpCICD: 750, MaxMsPerOpCICD: 5000},
|
||||
"Setup Keys - L": {MinMsPerOpLocal: 50, MaxMsPerOpLocal: 200, MinMsPerOpCICD: 150, MaxMsPerOpCICD: 1000},
|
||||
"Users - XL": {MinMsPerOpLocal: 350, MaxMsPerOpLocal: 550, MinMsPerOpCICD: 700, MaxMsPerOpCICD: 3500},
|
||||
}
|
||||
|
||||
log.SetOutput(io.Discard)
|
||||
defer log.SetOutput(os.Stderr)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
for name, bc := range benchCasesUsers {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
apiHandler, am, _ := testing_tools.BuildApiBlackBoxWithDBState(b, "../testdata/users.sql", nil, false)
|
||||
testing_tools.PopulateTestData(b, am.(*server.DefaultAccountManager), bc.Peers, bc.Groups, bc.Users, bc.SetupKeys)
|
||||
|
||||
b.ResetTimer()
|
||||
start := time.Now()
|
||||
for i := 0; i < b.N; i++ {
|
||||
groupId := testing_tools.TestGroupId
|
||||
if i%2 == 0 {
|
||||
groupId = testing_tools.NewGroupId
|
||||
}
|
||||
requestBody := api.UserRequest{
|
||||
AutoGroups: []string{groupId},
|
||||
IsBlocked: false,
|
||||
Role: "admin",
|
||||
}
|
||||
|
||||
// the time marshal will be recorded as well but for our use case that is ok
|
||||
body, err := json.Marshal(requestBody)
|
||||
assert.NoError(b, err)
|
||||
|
||||
req := testing_tools.BuildRequest(b, body, http.MethodPut, "/api/users/"+testing_tools.TestUserId, testing_tools.TestAdminId)
|
||||
apiHandler.ServeHTTP(recorder, req)
|
||||
}
|
||||
|
||||
testing_tools.EvaluateBenchmarkResults(b, name, time.Since(start), expectedMetrics[name], recorder)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetOneUser(b *testing.B) {
|
||||
b.Skip("Skipping benchmark as endpoint is missing")
|
||||
var expectedMetrics = map[string]testing_tools.PerformanceMetrics{
|
||||
"Users - XS": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 12},
|
||||
"Users - S": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 12},
|
||||
"Users - M": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 12},
|
||||
"Users - L": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 12},
|
||||
"Peers - L": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 12},
|
||||
"Groups - L": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 12},
|
||||
"Setup Keys - L": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 12},
|
||||
"Users - XL": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 12},
|
||||
}
|
||||
|
||||
log.SetOutput(io.Discard)
|
||||
defer log.SetOutput(os.Stderr)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
for name, bc := range benchCasesUsers {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
apiHandler, am, _ := testing_tools.BuildApiBlackBoxWithDBState(b, "../testdata/users.sql", nil, false)
|
||||
testing_tools.PopulateTestData(b, am.(*server.DefaultAccountManager), bc.Peers, bc.Groups, bc.Users, bc.SetupKeys)
|
||||
|
||||
b.ResetTimer()
|
||||
start := time.Now()
|
||||
for i := 0; i < b.N; i++ {
|
||||
req := testing_tools.BuildRequest(b, nil, http.MethodGet, "/api/users/"+testing_tools.TestUserId, testing_tools.TestAdminId)
|
||||
apiHandler.ServeHTTP(recorder, req)
|
||||
}
|
||||
|
||||
testing_tools.EvaluateBenchmarkResults(b, name, time.Since(start), expectedMetrics[name], recorder)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetAllUsers(b *testing.B) {
|
||||
var expectedMetrics = map[string]testing_tools.PerformanceMetrics{
|
||||
"Users - XS": {MinMsPerOpLocal: 50, MaxMsPerOpLocal: 90, MinMsPerOpCICD: 60, MaxMsPerOpCICD: 180},
|
||||
"Users - S": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 30},
|
||||
"Users - M": {MinMsPerOpLocal: 5, MaxMsPerOpLocal: 12, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 30},
|
||||
"Users - L": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 30},
|
||||
"Peers - L": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 30},
|
||||
"Groups - L": {MinMsPerOpLocal: 0.5, MaxMsPerOpLocal: 2, MinMsPerOpCICD: 1, MaxMsPerOpCICD: 30},
|
||||
"Setup Keys - L": {MinMsPerOpLocal: 40, MaxMsPerOpLocal: 140, MinMsPerOpCICD: 60, MaxMsPerOpCICD: 200},
|
||||
"Users - XL": {MinMsPerOpLocal: 15, MaxMsPerOpLocal: 40, MinMsPerOpCICD: 20, MaxMsPerOpCICD: 90},
|
||||
}
|
||||
|
||||
log.SetOutput(io.Discard)
|
||||
defer log.SetOutput(os.Stderr)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
for name, bc := range benchCasesUsers {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
apiHandler, am, _ := testing_tools.BuildApiBlackBoxWithDBState(b, "../testdata/users.sql", nil, false)
|
||||
testing_tools.PopulateTestData(b, am.(*server.DefaultAccountManager), bc.Peers, bc.Groups, bc.Users, bc.SetupKeys)
|
||||
|
||||
b.ResetTimer()
|
||||
start := time.Now()
|
||||
for i := 0; i < b.N; i++ {
|
||||
req := testing_tools.BuildRequest(b, nil, http.MethodGet, "/api/setup-keys", testing_tools.TestAdminId)
|
||||
apiHandler.ServeHTTP(recorder, req)
|
||||
}
|
||||
|
||||
testing_tools.EvaluateBenchmarkResults(b, name, time.Since(start), expectedMetrics[name], recorder)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDeleteUsers(b *testing.B) {
|
||||
var expectedMetrics = map[string]testing_tools.PerformanceMetrics{
|
||||
"Users - XS": {MinMsPerOpLocal: 1000, MaxMsPerOpLocal: 1600, MinMsPerOpCICD: 1900, MaxMsPerOpCICD: 10000},
|
||||
"Users - S": {MinMsPerOpLocal: 15, MaxMsPerOpLocal: 40, MinMsPerOpCICD: 30, MaxMsPerOpCICD: 200},
|
||||
"Users - M": {MinMsPerOpLocal: 15, MaxMsPerOpLocal: 70, MinMsPerOpCICD: 15, MaxMsPerOpCICD: 230},
|
||||
"Users - L": {MinMsPerOpLocal: 15, MaxMsPerOpLocal: 45, MinMsPerOpCICD: 30, MaxMsPerOpCICD: 190},
|
||||
"Peers - L": {MinMsPerOpLocal: 400, MaxMsPerOpLocal: 600, MinMsPerOpCICD: 650, MaxMsPerOpCICD: 1800},
|
||||
"Groups - L": {MinMsPerOpLocal: 600, MaxMsPerOpLocal: 800, MinMsPerOpCICD: 1200, MaxMsPerOpCICD: 7500},
|
||||
"Setup Keys - L": {MinMsPerOpLocal: 20, MaxMsPerOpLocal: 200, MinMsPerOpCICD: 55, MaxMsPerOpCICD: 600},
|
||||
"Users - XL": {MinMsPerOpLocal: 50, MaxMsPerOpLocal: 150, MinMsPerOpCICD: 80, MaxMsPerOpCICD: 400},
|
||||
}
|
||||
|
||||
log.SetOutput(io.Discard)
|
||||
defer log.SetOutput(os.Stderr)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
for name, bc := range benchCasesUsers {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
apiHandler, am, _ := testing_tools.BuildApiBlackBoxWithDBState(b, "../testdata/users.sql", nil, false)
|
||||
testing_tools.PopulateTestData(b, am.(*server.DefaultAccountManager), bc.Peers, bc.Groups, 1000, bc.SetupKeys)
|
||||
|
||||
b.ResetTimer()
|
||||
start := time.Now()
|
||||
for i := 0; i < b.N; i++ {
|
||||
req := testing_tools.BuildRequest(b, nil, http.MethodDelete, "/api/users/"+"olduser-"+strconv.Itoa(i), testing_tools.TestAdminId)
|
||||
apiHandler.ServeHTTP(recorder, req)
|
||||
}
|
||||
|
||||
testing_tools.EvaluateBenchmarkResults(b, name, time.Since(start), expectedMetrics[name], recorder)
|
||||
})
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
CREATE TABLE `accounts` (`id` text,`created_by` text,`created_at` datetime,`domain` text,`domain_category` text,`is_domain_primary_account` numeric,`network_identifier` text,`network_net` text,`network_dns` text,`network_serial` integer,`dns_settings_disabled_management_groups` text,`settings_peer_login_expiration_enabled` numeric,`settings_peer_login_expiration` integer,`settings_regular_users_view_blocked` numeric,`settings_groups_propagation_enabled` numeric,`settings_jwt_groups_enabled` numeric,`settings_jwt_groups_claim_name` text,`settings_jwt_allow_groups` text,`settings_extra_peer_approval_enabled` numeric,`settings_extra_integrated_validator_groups` text,PRIMARY KEY (`id`));
|
||||
CREATE TABLE `users` (`id` text,`account_id` text,`role` text,`is_service_user` numeric,`non_deletable` numeric,`service_user_name` text,`auto_groups` text,`blocked` numeric,`last_login` datetime DEFAULT NULL,`created_at` datetime,`issued` text DEFAULT "api",`integration_ref_id` integer,`integration_ref_integration_type` text,PRIMARY KEY (`id`),CONSTRAINT `fk_accounts_users_g` FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`));
|
||||
CREATE TABLE `groups` (`id` text,`account_id` text,`name` text,`issued` text,`peers` text,`integration_ref_id` integer,`integration_ref_integration_type` text,PRIMARY KEY (`id`),CONSTRAINT `fk_accounts_groups_g` FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`));
|
||||
CREATE TABLE `setup_keys` (`id` text,`account_id` text,`key` text,`key_secret` text,`name` text,`type` text,`created_at` datetime,`expires_at` datetime,`updated_at` datetime,`revoked` numeric,`used_times` integer,`last_used` datetime DEFAULT NULL,`auto_groups` text,`usage_limit` integer,`ephemeral` numeric,PRIMARY KEY (`id`),CONSTRAINT `fk_accounts_setup_keys_g` FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`));
|
||||
|
||||
INSERT INTO accounts VALUES('testAccountId','','2024-10-02 16:01:38.000000000+00:00','test.com','private',1,'testNetworkIdentifier','{"IP":"100.64.0.0","Mask":"//8AAA=="}','',0,'[]',0,86400000000000,0,0,0,'',NULL,NULL,NULL);
|
||||
INSERT INTO users VALUES('testUserId','testAccountId','user',0,0,'','[]',0,NULL,'2024-10-02 16:01:38.000000000+00:00','api',0,'');
|
||||
INSERT INTO users VALUES('testAdminId','testAccountId','admin',0,0,'','[]',0,NULL,'2024-10-02 16:01:38.000000000+00:00','api',0,'');
|
||||
INSERT INTO users VALUES('testOwnerId','testAccountId','owner',0,0,'','[]',0,NULL,'2024-10-02 16:01:38.000000000+00:00','api',0,'');
|
||||
INSERT INTO users VALUES('testServiceUserId','testAccountId','user',1,0,'','[]',0,NULL,'2024-10-02 16:01:38.000000000+00:00','api',0,'');
|
||||
INSERT INTO users VALUES('testServiceAdminId','testAccountId','admin',1,0,'','[]',0,NULL,'2024-10-02 16:01:38.000000000+00:00','api',0,'');
|
||||
INSERT INTO users VALUES('blockedUserId','testAccountId','admin',0,0,'','[]',1,NULL,'2024-10-02 16:01:38.000000000+00:00','api',0,'');
|
||||
INSERT INTO users VALUES('otherUserId','otherAccountId','admin',0,0,'','[]',0,NULL,'2024-10-02 16:01:38.000000000+00:00','api',0,'');
|
||||
INSERT INTO "groups" VALUES('testGroupId','testAccountId','testGroupName','api','[]',0,'');
|
||||
INSERT INTO "groups" VALUES('newGroupId','testAccountId','newGroupName','api','[]',0,'');
|
||||
INSERT INTO setup_keys VALUES('testKeyId','testAccountId','testKey','testK****','existingKey','one-off','2021-08-19 20:46:20.000000000+00:00','2321-09-18 20:46:20.000000000+00:00','2021-08-19 20:46:20.000000000+00:000',0,0,NULL,'["testGroupId"]',1,0);
|
||||
INSERT INTO setup_keys VALUES('revokedKeyId','testAccountId','revokedKey','testK****','existingKey','reusable','2021-08-19 20:46:20.000000000+00:00','2321-09-18 20:46:20.000000000+00:00','2021-08-19 20:46:20.000000000+00:00',1,0,NULL,'["testGroupId"]',3,0);
|
||||
INSERT INTO setup_keys VALUES('expiredKeyId','testAccountId','expiredKey','testK****','existingKey','reusable','2021-08-19 20:46:20.000000000+00:00','1921-09-18 20:46:20.000000000+00:00','2021-08-19 20:46:20.000000000+00:00',0,1,NULL,'["testGroupId"]',5,1);
|
||||
|
||||
CREATE TABLE `peers` (`id` text,`account_id` text,`key` text,`setup_key` text,`ip` text,`meta_hostname` text,`meta_go_os` text,`meta_kernel` text,`meta_core` text,`meta_platform` text,`meta_os` text,`meta_os_version` text,`meta_wt_version` text,`meta_ui_version` text,`meta_kernel_version` text,`meta_network_addresses` text,`meta_system_serial_number` text,`meta_system_product_name` text,`meta_system_manufacturer` text,`meta_environment` text,`meta_files` text,`name` text,`dns_label` text,`peer_status_last_seen` datetime,`peer_status_connected` numeric,`peer_status_login_expired` numeric,`peer_status_requires_approval` numeric,`user_id` text,`ssh_key` text,`ssh_enabled` numeric,`login_expiration_enabled` numeric,`last_login` datetime,`created_at` datetime,`ephemeral` numeric,`location_connection_ip` text,`location_country_code` text,`location_city_name` text,`location_geo_name_id` integer,PRIMARY KEY (`id`),CONSTRAINT `fk_accounts_peers_g` FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`));
|
||||
|
||||
INSERT INTO peers VALUES('testPeerId','testAccountId','5rvhvriKJZ3S9oxYToVj5TzDM9u9y8cxg7htIMWlYAg=','72546A29-6BC8-4311-BCFC-9CDBF33F1A48','"100.64.114.31"','f2a34f6a4731','linux','Linux','11','unknown','Debian GNU/Linux','','0.12.0','','',NULL,'','','','{"Cloud":"","Platform":""}',NULL,'f2a34f6a4731','f2a34f6a4731','2023-03-02 09:21:02.189035775+01:00',0,0,0,'','ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILzUUSYG/LGnV8zarb2SGN+tib/PZ+M7cL4WtTzUrTpk',0,0,'2023-03-01 19:48:19.817799698+01:00','2024-10-02 17:00:32.527947+02:00',0,'""','','',0);
|
||||
@@ -0,0 +1,24 @@
|
||||
CREATE TABLE `accounts` (`id` text,`created_by` text,`created_at` datetime,`domain` text,`domain_category` text,`is_domain_primary_account` numeric,`network_identifier` text,`network_net` text,`network_dns` text,`network_serial` integer,`dns_settings_disabled_management_groups` text,`settings_peer_login_expiration_enabled` numeric,`settings_peer_login_expiration` integer,`settings_regular_users_view_blocked` numeric,`settings_groups_propagation_enabled` numeric,`settings_jwt_groups_enabled` numeric,`settings_jwt_groups_claim_name` text,`settings_jwt_allow_groups` text,`settings_extra_peer_approval_enabled` numeric,`settings_extra_integrated_validator_groups` text,PRIMARY KEY (`id`));
|
||||
CREATE TABLE `users` (`id` text,`account_id` text,`role` text,`is_service_user` numeric,`non_deletable` numeric,`service_user_name` text,`auto_groups` text,`blocked` numeric,`last_login` datetime DEFAULT NULL,`created_at` datetime,`issued` text DEFAULT "api",`integration_ref_id` integer,`integration_ref_integration_type` text,PRIMARY KEY (`id`),CONSTRAINT `fk_accounts_users_g` FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`));
|
||||
CREATE TABLE `peers` (`id` text,`account_id` text,`key` text,`setup_key` text,`ip` text,`meta_hostname` text,`meta_go_os` text,`meta_kernel` text,`meta_core` text,`meta_platform` text,`meta_os` text,`meta_os_version` text,`meta_wt_version` text,`meta_ui_version` text,`meta_kernel_version` text,`meta_network_addresses` text,`meta_system_serial_number` text,`meta_system_product_name` text,`meta_system_manufacturer` text,`meta_environment` text,`meta_files` text,`name` text,`dns_label` text,`peer_status_last_seen` datetime DEFAULT NULL,`peer_status_connected` numeric,`peer_status_login_expired` numeric,`peer_status_requires_approval` numeric,`user_id` text,`ssh_key` text,`ssh_enabled` numeric,`login_expiration_enabled` numeric,`last_login` datetime DEFAULT NULL,`created_at` datetime,`ephemeral` numeric,`location_connection_ip` text,`location_country_code` text,`location_city_name` text,`location_geo_name_id` integer,PRIMARY KEY (`id`),CONSTRAINT `fk_accounts_peers_g` FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`));
|
||||
CREATE TABLE `groups` (`id` text,`account_id` text,`name` text,`issued` text,`peers` text,`integration_ref_id` integer,`integration_ref_integration_type` text,PRIMARY KEY (`id`),CONSTRAINT `fk_accounts_groups_g` FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`));
|
||||
|
||||
INSERT INTO accounts VALUES('testAccountId','','2024-10-02 16:01:38.000000000+00:00','test.com','private',1,'testNetworkIdentifier','{"IP":"100.64.0.0","Mask":"//8AAA=="}','',0,'[]',0,86400000000000,0,0,0,'',NULL,NULL,NULL);
|
||||
INSERT INTO users VALUES('testUserId','testAccountId','user',0,0,'','[]',0,NULL,'2024-10-02 16:01:38.000000000+00:00','api',0,'');
|
||||
INSERT INTO users VALUES('testAdminId','testAccountId','admin',0,0,'','[]',0,NULL,'2024-10-02 16:01:38.000000000+00:00','api',0,'');
|
||||
INSERT INTO users VALUES('testOwnerId','testAccountId','owner',0,0,'','[]',0,NULL,'2024-10-02 16:01:38.000000000+00:00','api',0,'');
|
||||
INSERT INTO users VALUES('testServiceUserId','testAccountId','user',1,0,'','[]',0,NULL,'2024-10-02 16:01:38.000000000+00:00','api',0,'');
|
||||
INSERT INTO users VALUES('testServiceAdminId','testAccountId','admin',1,0,'','[]',0,NULL,'2024-10-02 16:01:38.000000000+00:00','api',0,'');
|
||||
INSERT INTO users VALUES('blockedUserId','testAccountId','admin',0,0,'','[]',1,NULL,'2024-10-02 16:01:38.000000000+00:00','api',0,'');
|
||||
INSERT INTO users VALUES('otherUserId','otherAccountId','admin',0,0,'','[]',0,NULL,'2024-10-02 16:01:38.000000000+00:00','api',0,'');
|
||||
INSERT INTO peers VALUES('testPeerId','testAccountId','5rvhvriKJZ3S9oxYToVj5TzDM9u9y8cxg7htIMWlYAg=','72546A29-6BC8-4311-BCFC-9CDBF33F1A48','"100.64.114.31"','f2a34f6a4731','linux','Linux','11','unknown','Debian GNU/Linux','','0.12.0','','',NULL,'','','','{"Cloud":"","Platform":""}',NULL,'f2a34f6a4731','f2a34f6a4731','2023-03-02 09:21:02.189035775+01:00',0,0,0,'','ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILzUUSYG/LGnV8zarb2SGN+tib/PZ+M7cL4WtTzUrTpk',0,1,'2023-03-01 19:48:19.817799698+01:00','2024-10-02 17:00:32.527947+02:00',0,'""','','',0);
|
||||
INSERT INTO "groups" VALUES('testGroupId','testAccountId','testGroupName','api','[]',0,'');
|
||||
INSERT INTO "groups" VALUES('newGroupId','testAccountId','newGroupName','api','[]',0,'');
|
||||
|
||||
|
||||
CREATE TABLE `setup_keys` (`id` text,`account_id` text,`key` text,`key_secret` text,`name` text,`type` text,`created_at` datetime,`expires_at` datetime,`updated_at` datetime DEFAULT NULL,`revoked` numeric,`used_times` integer,`last_used` datetime DEFAULT NULL,`auto_groups` text,`usage_limit` integer,`ephemeral` numeric,PRIMARY KEY (`id`),CONSTRAINT `fk_accounts_setup_keys_g` FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`));
|
||||
|
||||
INSERT INTO setup_keys VALUES('testKeyId','testAccountId','testKey','testK****','existingKey','one-off','2021-08-19 20:46:20.000000000+00:00','2321-09-18 20:46:20.000000000+00:00','2021-08-19 20:46:20.000000000+00:000',0,0,NULL,'["testGroupId"]',1,0);
|
||||
INSERT INTO setup_keys VALUES('revokedKeyId','testAccountId','revokedKey','testK****','existingKey','reusable','2021-08-19 20:46:20.000000000+00:00','2321-09-18 20:46:20.000000000+00:00','2021-08-19 20:46:20.000000000+00:00',1,0,NULL,'["testGroupId"]',3,0);
|
||||
INSERT INTO setup_keys VALUES('expiredKeyId','testAccountId','expiredKey','testK****','existingKey','reusable','2021-08-19 20:46:20.000000000+00:00','1921-09-18 20:46:20.000000000+00:00','2021-08-19 20:46:20.000000000+00:00',0,1,NULL,'["testGroupId"]',5,1);
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
CREATE TABLE `accounts` (`id` text,`created_by` text,`created_at` datetime,`domain` text,`domain_category` text,`is_domain_primary_account` numeric,`network_identifier` text,`network_net` text,`network_dns` text,`network_serial` integer,`dns_settings_disabled_management_groups` text,`settings_peer_login_expiration_enabled` numeric,`settings_peer_login_expiration` integer,`settings_regular_users_view_blocked` numeric,`settings_groups_propagation_enabled` numeric,`settings_jwt_groups_enabled` numeric,`settings_jwt_groups_claim_name` text,`settings_jwt_allow_groups` text,`settings_extra_peer_approval_enabled` numeric,`settings_extra_integrated_validator_groups` text,PRIMARY KEY (`id`));
|
||||
CREATE TABLE `groups` (`id` text,`account_id` text,`name` text,`issued` text,`peers` text,`integration_ref_id` integer,`integration_ref_integration_type` text,PRIMARY KEY (`id`),CONSTRAINT `fk_accounts_groups_g` FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`));
|
||||
CREATE TABLE `setup_keys` (`id` text,`account_id` text,`key` text,`key_secret` text,`name` text,`type` text,`created_at` datetime,`expires_at` datetime,`updated_at` datetime,`revoked` numeric,`used_times` integer,`last_used` datetime DEFAULT NULL,`auto_groups` text,`usage_limit` integer,`ephemeral` numeric,PRIMARY KEY (`id`),CONSTRAINT `fk_accounts_setup_keys_g` FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`));
|
||||
CREATE TABLE `peers` (`id` text,`account_id` text,`key` text,`setup_key` text,`ip` text,`meta_hostname` text,`meta_go_os` text,`meta_kernel` text,`meta_core` text,`meta_platform` text,`meta_os` text,`meta_os_version` text,`meta_wt_version` text,`meta_ui_version` text,`meta_kernel_version` text,`meta_network_addresses` text,`meta_system_serial_number` text,`meta_system_product_name` text,`meta_system_manufacturer` text,`meta_environment` text,`meta_files` text,`name` text,`dns_label` text,`peer_status_last_seen` datetime,`peer_status_connected` numeric,`peer_status_login_expired` numeric,`peer_status_requires_approval` numeric,`user_id` text,`ssh_key` text,`ssh_enabled` numeric,`login_expiration_enabled` numeric,`last_login` datetime,`created_at` datetime,`ephemeral` numeric,`location_connection_ip` text,`location_country_code` text,`location_city_name` text,`location_geo_name_id` integer,PRIMARY KEY (`id`),CONSTRAINT `fk_accounts_peers_g` FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`));
|
||||
|
||||
INSERT INTO accounts VALUES('testAccountId','','2024-10-02 16:01:38.000000000+00:00','test.com','private',1,'testNetworkIdentifier','{"IP":"100.64.0.0","Mask":"//8AAA=="}','',0,'[]',0,86400000000000,0,0,0,'',NULL,NULL,NULL);
|
||||
INSERT INTO "groups" VALUES('testGroupId','testAccountId','testGroupName','api','[]',0,'');
|
||||
INSERT INTO "groups" VALUES('newGroupId','testAccountId','newGroupName','api','[]',0,'');
|
||||
INSERT INTO setup_keys VALUES('testKeyId','testAccountId','testKey','testK****','existingKey','one-off','2021-08-19 20:46:20.000000000+00:00','2321-09-18 20:46:20.000000000+00:00','2021-08-19 20:46:20.000000000+00:000',0,0,NULL,'["testGroupId"]',1,0);
|
||||
INSERT INTO setup_keys VALUES('revokedKeyId','testAccountId','revokedKey','testK****','existingKey','reusable','2021-08-19 20:46:20.000000000+00:00','2321-09-18 20:46:20.000000000+00:00','2021-08-19 20:46:20.000000000+00:00',1,0,NULL,'["testGroupId"]',3,0);
|
||||
INSERT INTO setup_keys VALUES('expiredKeyId','testAccountId','expiredKey','testK****','existingKey','reusable','2021-08-19 20:46:20.000000000+00:00','1921-09-18 20:46:20.000000000+00:00','2021-08-19 20:46:20.000000000+00:00',0,1,NULL,'["testGroupId"]',5,1);
|
||||
INSERT INTO peers VALUES('testPeerId','testAccountId','5rvhvriKJZ3S9oxYToVj5TzDM9u9y8cxg7htIMWlYAg=','72546A29-6BC8-4311-BCFC-9CDBF33F1A48','"100.64.114.31"','f2a34f6a4731','linux','Linux','11','unknown','Debian GNU/Linux','','0.12.0','','',NULL,'','','','{"Cloud":"","Platform":""}',NULL,'f2a34f6a4731','f2a34f6a4731','2023-03-02 09:21:02.189035775+01:00',0,0,0,'','ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILzUUSYG/LGnV8zarb2SGN+tib/PZ+M7cL4WtTzUrTpk',0,1,'2023-03-01 19:48:19.817799698+01:00','2024-10-02 17:00:32.527947+02:00',0,'""','','',0);
|
||||
|
||||
|
||||
CREATE TABLE `users` (`id` text,`account_id` text,`role` text,`is_service_user` numeric,`non_deletable` numeric,`service_user_name` text,`auto_groups` text,`blocked` numeric,`last_login` datetime DEFAULT NULL,`created_at` datetime,`issued` text DEFAULT "api",`integration_ref_id` integer,`integration_ref_integration_type` text,PRIMARY KEY (`id`),CONSTRAINT `fk_accounts_users_g` FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`));
|
||||
|
||||
INSERT INTO users VALUES('testUserId','testAccountId','user',0,0,'','[]',0,NULL,'2024-10-02 16:01:38.000000000+00:00','api',0,'');
|
||||
INSERT INTO users VALUES('testAdminId','testAccountId','admin',0,0,'','[]',0,NULL,'2024-10-02 16:01:38.000000000+00:00','api',0,'');
|
||||
INSERT INTO users VALUES('testOwnerId','testAccountId','owner',0,0,'','[]',0,NULL,'2024-10-02 16:01:38.000000000+00:00','api',0,'');
|
||||
INSERT INTO users VALUES('testServiceUserId','testAccountId','user',1,0,'','[]',0,NULL,'2024-10-02 16:01:38.000000000+00:00','api',0,'');
|
||||
INSERT INTO users VALUES('testServiceAdminId','testAccountId','admin',1,0,'','[]',0,NULL,'2024-10-02 16:01:38.000000000+00:00','api',0,'');
|
||||
INSERT INTO users VALUES('blockedUserId','testAccountId','admin',0,0,'','[]',1,NULL,'2024-10-02 16:01:38.000000000+00:00','api',0,'');
|
||||
INSERT INTO users VALUES('otherUserId','otherAccountId','admin',0,0,'','[]',0,NULL,'2024-10-02 16:01:38.000000000+00:00','api',0,'');
|
||||
@@ -0,0 +1,311 @@
|
||||
package testing_tools
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"github.com/netbirdio/netbird/management/server/activity"
|
||||
"github.com/netbirdio/netbird/management/server/geolocation"
|
||||
"github.com/netbirdio/netbird/management/server/groups"
|
||||
nbhttp "github.com/netbirdio/netbird/management/server/http"
|
||||
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
"github.com/netbirdio/netbird/management/server/networks"
|
||||
"github.com/netbirdio/netbird/management/server/networks/resources"
|
||||
"github.com/netbirdio/netbird/management/server/networks/routers"
|
||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||
"github.com/netbirdio/netbird/management/server/posture"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
const (
|
||||
TestAccountId = "testAccountId"
|
||||
TestPeerId = "testPeerId"
|
||||
TestGroupId = "testGroupId"
|
||||
TestKeyId = "testKeyId"
|
||||
|
||||
TestUserId = "testUserId"
|
||||
TestAdminId = "testAdminId"
|
||||
TestOwnerId = "testOwnerId"
|
||||
TestServiceUserId = "testServiceUserId"
|
||||
TestServiceAdminId = "testServiceAdminId"
|
||||
BlockedUserId = "blockedUserId"
|
||||
OtherUserId = "otherUserId"
|
||||
InvalidToken = "invalidToken"
|
||||
|
||||
NewKeyName = "newKey"
|
||||
NewGroupId = "newGroupId"
|
||||
ExpiresIn = 3600
|
||||
RevokedKeyId = "revokedKeyId"
|
||||
ExpiredKeyId = "expiredKeyId"
|
||||
|
||||
ExistingKeyName = "existingKey"
|
||||
)
|
||||
|
||||
type TB interface {
|
||||
Cleanup(func())
|
||||
Helper()
|
||||
Errorf(format string, args ...any)
|
||||
Fatalf(format string, args ...any)
|
||||
TempDir() string
|
||||
}
|
||||
|
||||
// BenchmarkCase defines a single benchmark test case
|
||||
type BenchmarkCase struct {
|
||||
Peers int
|
||||
Groups int
|
||||
Users int
|
||||
SetupKeys int
|
||||
}
|
||||
|
||||
// PerformanceMetrics holds the performance expectations
|
||||
type PerformanceMetrics struct {
|
||||
MinMsPerOpLocal float64
|
||||
MaxMsPerOpLocal float64
|
||||
MinMsPerOpCICD float64
|
||||
MaxMsPerOpCICD float64
|
||||
}
|
||||
|
||||
func BuildApiBlackBoxWithDBState(t TB, sqlFile string, expectedPeerUpdate *server.UpdateMessage, validateUpdate bool) (http.Handler, server.AccountManager, chan struct{}) {
|
||||
store, cleanup, err := store.NewTestStoreFromSQL(context.Background(), sqlFile, t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test store: %v", err)
|
||||
}
|
||||
t.Cleanup(cleanup)
|
||||
|
||||
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create metrics: %v", err)
|
||||
}
|
||||
|
||||
peersUpdateManager := server.NewPeersUpdateManager(nil)
|
||||
updMsg := peersUpdateManager.CreateChannel(context.Background(), TestPeerId)
|
||||
done := make(chan struct{})
|
||||
if validateUpdate {
|
||||
go func() {
|
||||
if expectedPeerUpdate != nil {
|
||||
peerShouldReceiveUpdate(t, updMsg, expectedPeerUpdate)
|
||||
} else {
|
||||
peerShouldNotReceiveUpdate(t, updMsg)
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
}
|
||||
|
||||
geoMock := &geolocation.Mock{}
|
||||
validatorMock := server.MocIntegratedValidator{}
|
||||
am, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "", &activity.InMemoryEventStore{}, geoMock, false, validatorMock, metrics)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create manager: %v", err)
|
||||
}
|
||||
|
||||
networksManagerMock := networks.NewManagerMock()
|
||||
resourcesManagerMock := resources.NewManagerMock()
|
||||
routersManagerMock := routers.NewManagerMock()
|
||||
groupsManagerMock := groups.NewManagerMock()
|
||||
apiHandler, err := nbhttp.NewAPIHandler(context.Background(), am, networksManagerMock, resourcesManagerMock, routersManagerMock, groupsManagerMock, geoMock, &jwtclaims.JwtValidatorMock{}, metrics, configs.AuthCfg{}, validatorMock)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create API handler: %v", err)
|
||||
}
|
||||
|
||||
return apiHandler, am, done
|
||||
}
|
||||
|
||||
func peerShouldNotReceiveUpdate(t TB, updateMessage <-chan *server.UpdateMessage) {
|
||||
t.Helper()
|
||||
select {
|
||||
case msg := <-updateMessage:
|
||||
t.Errorf("Unexpected message received: %+v", msg)
|
||||
case <-time.After(500 * time.Millisecond):
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func peerShouldReceiveUpdate(t TB, updateMessage <-chan *server.UpdateMessage, expected *server.UpdateMessage) {
|
||||
t.Helper()
|
||||
|
||||
select {
|
||||
case msg := <-updateMessage:
|
||||
if msg == nil {
|
||||
t.Errorf("Received nil update message, expected valid message")
|
||||
}
|
||||
assert.Equal(t, expected, msg)
|
||||
case <-time.After(500 * time.Millisecond):
|
||||
t.Errorf("Timed out waiting for update message")
|
||||
}
|
||||
}
|
||||
|
||||
func BuildRequest(t TB, requestBody []byte, requestType, requestPath, user string) *http.Request {
|
||||
t.Helper()
|
||||
|
||||
req := httptest.NewRequest(requestType, requestPath, bytes.NewBuffer(requestBody))
|
||||
req.Header.Set("Authorization", "Bearer "+user)
|
||||
|
||||
return req
|
||||
}
|
||||
|
||||
func ReadResponse(t *testing.T, recorder *httptest.ResponseRecorder, expectedStatus int, expectResponse bool) ([]byte, bool) {
|
||||
t.Helper()
|
||||
|
||||
res := recorder.Result()
|
||||
defer res.Body.Close()
|
||||
|
||||
content, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read response body: %v", err)
|
||||
}
|
||||
|
||||
if !expectResponse {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if status := recorder.Code; status != expectedStatus {
|
||||
t.Fatalf("handler returned wrong status code: got %v want %v, content: %s",
|
||||
status, expectedStatus, string(content))
|
||||
}
|
||||
|
||||
return content, expectedStatus == http.StatusOK
|
||||
}
|
||||
|
||||
func PopulateTestData(b *testing.B, am *server.DefaultAccountManager, peers, groups, users, setupKeys int) {
|
||||
b.Helper()
|
||||
|
||||
ctx := context.Background()
|
||||
account, err := am.GetAccount(ctx, TestAccountId)
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to get account: %v", err)
|
||||
}
|
||||
|
||||
// Create peers
|
||||
for i := 0; i < peers; i++ {
|
||||
peerKey, _ := wgtypes.GeneratePrivateKey()
|
||||
peer := &nbpeer.Peer{
|
||||
ID: fmt.Sprintf("oldpeer-%d", i),
|
||||
DNSLabel: fmt.Sprintf("oldpeer-%d", i),
|
||||
Key: peerKey.PublicKey().String(),
|
||||
IP: net.ParseIP(fmt.Sprintf("100.64.%d.%d", i/256, i%256)),
|
||||
Status: &nbpeer.PeerStatus{LastSeen: time.Now().UTC(), Connected: true},
|
||||
UserID: TestUserId,
|
||||
}
|
||||
account.Peers[peer.ID] = peer
|
||||
}
|
||||
|
||||
// Create users
|
||||
for i := 0; i < users; i++ {
|
||||
user := &types.User{
|
||||
Id: fmt.Sprintf("olduser-%d", i),
|
||||
AccountID: account.Id,
|
||||
Role: types.UserRoleUser,
|
||||
}
|
||||
account.Users[user.Id] = user
|
||||
}
|
||||
|
||||
for i := 0; i < setupKeys; i++ {
|
||||
key := &types.SetupKey{
|
||||
Id: fmt.Sprintf("oldkey-%d", i),
|
||||
AccountID: account.Id,
|
||||
AutoGroups: []string{"someGroupID"},
|
||||
UpdatedAt: time.Now().UTC(),
|
||||
ExpiresAt: util.ToPtr(time.Now().Add(ExpiresIn * time.Second)),
|
||||
Name: NewKeyName + strconv.Itoa(i),
|
||||
Type: "reusable",
|
||||
UsageLimit: 0,
|
||||
}
|
||||
account.SetupKeys[key.Id] = key
|
||||
}
|
||||
|
||||
// Create groups and policies
|
||||
account.Policies = make([]*types.Policy, 0, groups)
|
||||
for i := 0; i < groups; i++ {
|
||||
groupID := fmt.Sprintf("group-%d", i)
|
||||
group := &types.Group{
|
||||
ID: groupID,
|
||||
Name: fmt.Sprintf("Group %d", i),
|
||||
}
|
||||
for j := 0; j < peers/groups; j++ {
|
||||
peerIndex := i*(peers/groups) + j
|
||||
group.Peers = append(group.Peers, fmt.Sprintf("peer-%d", peerIndex))
|
||||
}
|
||||
account.Groups[groupID] = group
|
||||
|
||||
// Create a policy for this group
|
||||
policy := &types.Policy{
|
||||
ID: fmt.Sprintf("policy-%d", i),
|
||||
Name: fmt.Sprintf("Policy for Group %d", i),
|
||||
Enabled: true,
|
||||
Rules: []*types.PolicyRule{
|
||||
{
|
||||
ID: fmt.Sprintf("rule-%d", i),
|
||||
Name: fmt.Sprintf("Rule for Group %d", i),
|
||||
Enabled: true,
|
||||
Sources: []string{groupID},
|
||||
Destinations: []string{groupID},
|
||||
Bidirectional: true,
|
||||
Protocol: types.PolicyRuleProtocolALL,
|
||||
Action: types.PolicyTrafficActionAccept,
|
||||
},
|
||||
},
|
||||
}
|
||||
account.Policies = append(account.Policies, policy)
|
||||
}
|
||||
|
||||
account.PostureChecks = []*posture.Checks{
|
||||
{
|
||||
ID: "PostureChecksAll",
|
||||
Name: "All",
|
||||
Checks: posture.ChecksDefinition{
|
||||
NBVersionCheck: &posture.NBVersionCheck{
|
||||
MinVersion: "0.0.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = am.Store.SaveAccount(context.Background(), account)
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to save account: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func EvaluateBenchmarkResults(b *testing.B, name string, duration time.Duration, perfMetrics PerformanceMetrics, recorder *httptest.ResponseRecorder) {
|
||||
b.Helper()
|
||||
|
||||
if recorder.Code != http.StatusOK {
|
||||
b.Fatalf("Benchmark %s failed: unexpected status code %d", name, recorder.Code)
|
||||
}
|
||||
|
||||
msPerOp := float64(duration.Nanoseconds()) / float64(b.N) / 1e6
|
||||
b.ReportMetric(msPerOp, "ms/op")
|
||||
|
||||
minExpected := perfMetrics.MinMsPerOpLocal
|
||||
maxExpected := perfMetrics.MaxMsPerOpLocal
|
||||
if os.Getenv("CI") == "true" {
|
||||
minExpected = perfMetrics.MinMsPerOpCICD
|
||||
maxExpected = perfMetrics.MaxMsPerOpCICD
|
||||
}
|
||||
|
||||
if msPerOp < minExpected {
|
||||
b.Fatalf("Benchmark %s failed: too fast (%.2f ms/op, minimum %.2f ms/op)", name, msPerOp, minExpected)
|
||||
}
|
||||
|
||||
if msPerOp > maxExpected {
|
||||
b.Fatalf("Benchmark %s failed: too slow (%.2f ms/op, maximum %.2f ms/op)", name, msPerOp, maxExpected)
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,10 @@ import (
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
)
|
||||
|
||||
// EmptyObject is an empty struct used to return empty JSON object
|
||||
type EmptyObject struct {
|
||||
}
|
||||
|
||||
type ErrorResponse struct {
|
||||
Message string `json:"message"`
|
||||
Code int `json:"code"`
|
||||
|
||||
Reference in New Issue
Block a user