* New Docs

CSS update and Dockerfile to include docs folder

flash of unrendered text fix

markdown docs

ignore docs/docs.go

improving the docs generation

github actions for docs generation

go runner version fix

updated docs.yml

update repo action updated

updated actions and dns docs

dns complete

More docs update

Complete docs and updated workflow

Update documentation Tue Aug  6 11:17:42 UTC 2024

Update documentation Thu Aug  8 12:26:57 UTC 2024

clean up

clean up

Dockerfile clean up

Updated workflow

Updated workflow

Update docs.yml

Update docs.yml

* requested changes

* changed ingress gateway to remote access gateway
This commit is contained in:
Sayan Mallick 2024-08-15 11:55:01 +05:30 committed by GitHub
parent 7786c106f7
commit c551c487ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 4916 additions and 4688 deletions

50
.github/workflows/docs.yml vendored Normal file
View file

@ -0,0 +1,50 @@
name: Generate Documentation
on:
workflow_dispatch:
inputs:
branch:
description: 'Branch to run the workflow against'
required: true
default: 'master'
jobs:
generate-docs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
repository: gravitl/netmaker
ref: ${{ github.event.inputs.branch || 'master' }}
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Install Swag
run: go install github.com/swaggo/swag/cmd/swag@latest
- name: Generating Docs
run: |
export PATH=$PATH:$(go env GOPATH)/bin
swag i --md docs/ --parseDependency --parseInternal --outputTypes yaml --parseDepth 1 --output .
- name: Get current timestamp
id: timestamp
run: echo "timestamp=$(date +'%Y-%m-%d %H:%M:%S')" >> $GITHUB_OUTPUT
- name: Create Pull Request
uses: peter-evans/create-pull-request@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "Update documentation ${{ steps.timestamp.outputs.timestamp }}"
title: "Update Swagger documentation ${{ steps.timestamp.outputs.timestamp }}"
body: |
This PR updates the swagger.yml file with the latest documentation changes.
Updated on: ${{ steps.timestamp.outputs.timestamp }}
branch: update-swagger-docs-${{ github.event.inputs.branch }}
base: ${{ github.event.inputs.branch }}
delete-branch: true

View file

@ -35,7 +35,6 @@ var HttpHandlers = []interface{}{
legacyHandlers,
}
// HandleRESTRequests - handles the rest requests
func HandleRESTRequests(wg *sync.WaitGroup, ctx context.Context) {
defer wg.Done()
@ -43,9 +42,19 @@ func HandleRESTRequests(wg *sync.WaitGroup, ctx context.Context) {
// Currently allowed dev origin is all. Should change in prod
// should consider analyzing the allowed methods further
headersOk := handlers.AllowedHeaders([]string{"Access-Control-Allow-Origin", "X-Requested-With", "Content-Type", "authorization", "From-Ui"})
headersOk := handlers.AllowedHeaders(
[]string{
"Access-Control-Allow-Origin",
"X-Requested-With",
"Content-Type",
"authorization",
"From-Ui",
},
)
originsOk := handlers.AllowedOrigins(strings.Split(servercfg.GetAllowedOrigin(), ","))
methodsOk := handlers.AllowedMethods([]string{http.MethodGet, http.MethodPut, http.MethodPost, http.MethodDelete})
methodsOk := handlers.AllowedMethods(
[]string{http.MethodGet, http.MethodPut, http.MethodPost, http.MethodDelete},
)
for _, middleware := range HttpMiddlewares {
r.Use(middleware)
@ -57,7 +66,10 @@ func HandleRESTRequests(wg *sync.WaitGroup, ctx context.Context) {
port := servercfg.GetAPIPort()
srv := &http.Server{Addr: ":" + port, Handler: handlers.CORS(originsOk, headersOk, methodsOk)(r)}
srv := &http.Server{
Addr: ":" + port,
Handler: handlers.CORS(originsOk, headersOk, methodsOk)(r),
}
go func() {
err := srv.ListenAndServe()
if err != nil {

View file

@ -16,25 +16,29 @@ import (
func dnsHandlers(r *mux.Router) {
r.HandleFunc("/api/dns", logic.SecurityCheck(true, http.HandlerFunc(getAllDNS))).Methods(http.MethodGet)
r.HandleFunc("/api/dns/adm/{network}/nodes", logic.SecurityCheck(true, http.HandlerFunc(getNodeDNS))).Methods(http.MethodGet)
r.HandleFunc("/api/dns/adm/{network}/custom", logic.SecurityCheck(true, http.HandlerFunc(getCustomDNS))).Methods(http.MethodGet)
r.HandleFunc("/api/dns/adm/{network}", logic.SecurityCheck(true, http.HandlerFunc(getDNS))).Methods(http.MethodGet)
r.HandleFunc("/api/dns/{network}", logic.SecurityCheck(true, http.HandlerFunc(createDNS))).Methods(http.MethodPost)
r.HandleFunc("/api/dns/adm/pushdns", logic.SecurityCheck(true, http.HandlerFunc(pushDNS))).Methods(http.MethodPost)
r.HandleFunc("/api/dns/{network}/{domain}", logic.SecurityCheck(true, http.HandlerFunc(deleteDNS))).Methods(http.MethodDelete)
r.HandleFunc("/api/dns", logic.SecurityCheck(true, http.HandlerFunc(getAllDNS))).
Methods(http.MethodGet)
r.HandleFunc("/api/dns/adm/{network}/nodes", logic.SecurityCheck(true, http.HandlerFunc(getNodeDNS))).
Methods(http.MethodGet)
r.HandleFunc("/api/dns/adm/{network}/custom", logic.SecurityCheck(true, http.HandlerFunc(getCustomDNS))).
Methods(http.MethodGet)
r.HandleFunc("/api/dns/adm/{network}", logic.SecurityCheck(true, http.HandlerFunc(getDNS))).
Methods(http.MethodGet)
r.HandleFunc("/api/dns/{network}", logic.SecurityCheck(true, http.HandlerFunc(createDNS))).
Methods(http.MethodPost)
r.HandleFunc("/api/dns/adm/pushdns", logic.SecurityCheck(true, http.HandlerFunc(pushDNS))).
Methods(http.MethodPost)
r.HandleFunc("/api/dns/{network}/{domain}", logic.SecurityCheck(true, http.HandlerFunc(deleteDNS))).
Methods(http.MethodDelete)
}
// swagger:route GET /api/dns/adm/{network}/nodes dns getNodeDNS
//
// Gets node DNS entries associated with a network.
//
// Schemes: https
//
// Security:
// oauth
// Responses:
// 200: dnsResponse
// @Summary Gets node DNS entries associated with a network
// @Router /api/dns/{network} [get]
// @Tags DNS
// @Accept json
// @Param network path string true "Network identifier"
// @Success 200 {array} models.DNSEntry
// @Failure 500 {object} models.ErrorResponse
func getNodeDNS(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
@ -53,17 +57,12 @@ func getNodeDNS(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(dns)
}
// swagger:route GET /api/dns dns getAllDNS
//
// Gets all DNS entries.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: dnsResponse
// @Summary Get all DNS entries
// @Router /api/dns [get]
// @Tags DNS
// @Accept json
// @Success 200 {array} models.DNSEntry
// @Failure 500 {object} models.ErrorResponse
func getAllDNS(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
dns, err := logic.GetAllDNS()
@ -77,17 +76,13 @@ func getAllDNS(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(dns)
}
// swagger:route GET /api/dns/adm/{network}/custom dns getCustomDNS
//
// Gets custom DNS entries associated with a network.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: dnsResponse
// @Summary Gets custom DNS entries associated with a network
// @Router /api/dns/adm/{network}/custom [get]
// @Tags DNS
// @Accept json
// @Param network path string true "Network identifier"
// @Success 200 {array} models.DNSEntry
// @Failure 500 {object} models.ErrorResponse
func getCustomDNS(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
@ -97,8 +92,15 @@ func getCustomDNS(w http.ResponseWriter, r *http.Request) {
network := params["network"]
dns, err := logic.GetCustomDNS(network)
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to get custom DNS entries for network [%s]: %v", network, err.Error()))
logger.Log(
0,
r.Header.Get("user"),
fmt.Sprintf(
"failed to get custom DNS entries for network [%s]: %v",
network,
err.Error(),
),
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
@ -106,17 +108,13 @@ func getCustomDNS(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(dns)
}
// swagger:route GET /api/dns/adm/{network} dns getDNS
//
// Gets all DNS entries associated with the network.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: dnsResponse
// @Summary Get all DNS entries associated with the network
// @Router /api/dns/adm/{network} [get]
// @Tags DNS
// @Accept json
// @Param network path string true "Network identifier"
// @Success 200 {array} models.DNSEntry
// @Failure 500 {object} models.ErrorResponse
func getDNS(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
@ -135,17 +133,15 @@ func getDNS(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(dns)
}
// swagger:route POST /api/dns/{network} dns createDNS
//
// Create a DNS entry.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: dnsResponse
// @Summary Create a new DNS entry
// @Router /api/dns/adm/{network} [post]
// @Tags DNS
// @Accept json
// @Param network path string true "Network identifier"
// @Param body body models.DNSEntry true "DNS entry details"
// @Success 200 {object} models.DNSEntry
// @Failure 400 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func createDNS(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
@ -187,18 +183,14 @@ func createDNS(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(entry)
}
// swagger:route DELETE /api/dns/{network}/{domain} dns deleteDNS
//
// Delete a DNS entry.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: stringJSONResponse
// *: stringJSONResponse
// @Summary Delete a DNS entry
// @Router /api/dns/{network}/{domain} [delete]
// @Tags DNS
// @Accept json
// @Param network path string true "Network identifier"
// @Param domain path string true "Domain Name"
// @Success 200 {array} models.DNSEntry
// @Failure 500 {object} models.ErrorResponse
func deleteDNS(w http.ResponseWriter, r *http.Request) {
// Set header
w.Header().Set("Content-Type", "application/json")
@ -243,23 +235,22 @@ func GetDNSEntry(domain string, network string) (models.DNSEntry, error) {
return entry, err
}
// swagger:route POST /api/dns/adm/pushdns dns pushDNS
//
// Push DNS entries to nameserver.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: dnsResponse
// *: dnsResponse
// @Summary Push DNS entries to nameserver
// @Router /api/dns/adm/pushdns [post]
// @Tags DNS
// @Accept json
// @Success 200 {string} string "DNS Pushed to CoreDNS"
// @Failure 400 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func pushDNS(w http.ResponseWriter, r *http.Request) {
// Set header
w.Header().Set("Content-Type", "application/json")
if !servercfg.IsDNSMode() {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("DNS Mode is set to off"), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("DNS Mode is set to off"), "badrequest"),
)
return
}
err := logic.SetDNS()

View file

@ -1,475 +0,0 @@
// Package classification Netmaker
//
// # API Usage
//
// Most actions that can be performed via API can be performed via UI. We recommend managing your networks using the official netmaker-ui project. However, Netmaker can also be run without the UI, and all functions can be achieved via API calls. If your use case requires using Netmaker without the UI or you need to do some troubleshooting/advanced configuration, using the API directly may help.
//
// # Authentication
//
// API calls must be authenticated via a header of the format -H “Authorization: Bearer <YOUR_SECRET_KEY>” There are two methods to obtain YOUR_SECRET_KEY: 1. Using the masterkey. By default, this value is “secret key,” but you should change this on your instance and keep it secure. This value can be set via env var at startup or in a config file (config/environments/< env >.yaml). See the [Netmaker](https://docs.netmaker.org/index.html) documentation for more details. 2. Using a JWT received for a node. This can be retrieved by calling the /api/nodes/<network>/authenticate endpoint, as documented below.
//
// Schemes: https
// BasePath: /
// Version: 0.25.0
// Host: api.demo.netmaker.io
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// Security:
// - oauth
//
// swagger:meta
package controller
import (
"os"
"github.com/gravitl/netmaker/config"
"github.com/gravitl/netmaker/logic/acls"
"github.com/gravitl/netmaker/models"
)
var _ = useUnused() // "use" the function to prevent "unused function" errors
// swagger:parameters getFile
type filenameToGet struct {
// Filename
// in: path
// required: true
Filename string `json:"filename"`
}
// swagger:response hasAdmin
type hasAdmin struct {
// in: body
Admin bool
}
// swagger:response apiHostSliceResponse
type apiHostSliceResponse struct {
// in: body
Host []models.ApiHost
}
// swagger:response apiHostResponse
type apiHostResponse struct {
// in: body
Host models.ApiHost
}
// swagger:parameters getNodeDNS getCustomDNS getDNS
type dnsNetworkPathParam struct {
// Network
// in: path
Network string `json:"network"`
}
// swagger:parameters createDNS
type dnsParams struct {
// Network
// in: path
Network string `json:"network"`
// DNS Entry
// in: body
Body []models.DNSEntry `json:"body"`
}
// Success
// swagger:response dnsResponse
type dnsResponse struct {
// in: body
Body []models.DNSEntry `json:"body"`
}
// swagger:parameters deleteDNS
type dnsDeletePathParams struct {
// Network
// in: path
Network string `json:"network"`
// Domain
// in: path
Domain string `json:"domain"`
}
// swagger:response stringJSONResponse
type stringJSONResponse struct {
// Response
// in: body
Response string `json:"response"`
}
//swagger:response EnrollmentKey
type EnrollmentKey struct {
// in: body
EnrollmentKey models.EnrollmentKey
}
//swagger:response EnrollmentKeys
type EnrollmentKeys struct {
// in: body
EnrollmentKeys []models.EnrollmentKey
}
// swagger:parameters getAllExtClients
type getAllClientsRequest struct {
// Networks
// in:body
Networks []string `json:"networks"`
}
// swagger:response extClientSliceResponse
type extClientSliceResponse struct {
// ExtClients
// in: body
ExtClients []models.ExtClient `json:"ext_clients"`
}
// swagger:response extClientResponse
type extClientResponse struct {
// ExtClient
// in: body
ExtClient models.ExtClient `json:"ext_client"`
}
// swagger:response fileResponse
type fileResponse struct {
// in: body
File os.File
}
// swagger:response successResponse
type successResponse struct {
// Success Response
// in: body
SuccessResponse models.SuccessResponse `json:"success_response"`
}
// swagger:parameters getExtClientConf
type extClientConfParams struct {
// Client ID
// in: path
ClientID string `json:"clientid"`
// Network
// in: path
Network string `json:"network"`
// Type
// in: path
Type string `json:"type"`
}
// swagger:parameters getExtClient getExtClientConf updateExtClient deleteExtClient
type extClientPathParams struct {
// Client ID
// in: path
ClientID string `json:"clientid"`
// Network
// in: path
Network string `json:"network"`
}
// swagger:parameters updateExtClient
type extClientBodyParam struct {
// ExtClient
// in: body
ExtClient models.ExtClient `json:"ext_client"`
}
// swagger:parameters getNetworkExtClients
type extClientNetworkPathParam struct {
// Network
// in: path
Network string `json:"network"`
}
// swagger:parameters createExtClient
type createExtClientPathParams struct {
// Network
// in: path
Network string `json:"network"`
// Node ID
// in: path
NodeID string `json:"nodeid"`
// Custom ExtClient
// in: body
CustomExtClient models.CustomExtClient `json:"custom_ext_client"`
}
// swagger:parameters getNode updateNode deleteNode createRelay deleteRelay createEgressGateway deleteEgressGateway createIngressGateway deleteIngressGateway ingressGatewayUsers
type networkNodePathParams struct {
// in: path
Network string `json:"network"`
// in: path
NodeID string `json:"nodeid"`
}
// swagger:response byteArrayResponse
type byteArrayResponse struct {
// in: body
ByteArray []byte `json:"byte_array"`
}
// swagger:parameters getNetwork deleteNetwork updateNetwork getNetworkACL updateNetworkACL
type NetworkParam struct {
// name: network name
// in: path
Networkname string `json:"networkname"`
}
// swagger:response getNetworksSliceResponse
type getNetworksSliceResponse struct {
// Networks
// in: body
Networks []models.Network `json:"networks"`
}
// swagger:response hostPull
type hostPull struct {
// hostPull
// in: body
HostPull models.HostPull
}
// swagger:parameters createNetwork updateNetwork
type networkBodyParam struct {
// Network
// in: body
Network models.Network `json:"network"`
}
// swagger:parameters updateNetworkNodeLimit keyUpdate createAccessKey getAccessKeys getNetworkNodes
type networkPathParam struct {
// Network
// in: path
Network string `json:"network"`
}
// swagger:response networkBodyResponse
type networkBodyResponse struct {
// Network
// in: body
Network models.Network `json:"network"`
}
// swagger:parameters updateNetworkACL
type aclContainerBodyParam struct {
// ACL Container
// in: body
ACLContainer acls.ACLContainer `json:"acl_container"`
}
// swagger:response aclContainerResponse
type aclContainerResponse struct {
// ACL Container
// in: body
ACLContainer acls.ACLContainer `json:"acl_container"`
}
// swagger:response nodeSliceResponse
type nodeSliceResponse struct {
// Nodes
// in: body
Nodes []models.ApiNode `json:"nodes"`
}
// swagger:response nodeResponse
type nodeResponse struct {
// Node
// in: body
Node models.LegacyNode `json:"node"`
}
// swagger:parameters updateNode deleteNode
type nodeBodyParam struct {
// Node
// in: body
Node models.LegacyNode `json:"node"`
}
//swagger:response okResponse
type okRespone struct{}
// swagger:response RegisterResponse
type RegisterResponse struct {
// in: body
RegisterResponse models.RegisterResponse
}
// swagger:parameters createRelay
type relayRequestBodyParam struct {
// Relay Request
// in: body
RelayRequest models.RelayRequest `json:"relay_request"`
}
// swagger:parameters createEgressGateway
type egressGatewayBodyParam struct {
// Egress Gateway Request
// in: body
EgressGatewayRequest models.EgressGatewayRequest `json:"egress_gateway_request"`
}
// swagger:parameters attachUserToRemoteAccessGateway removeUserFromRemoteAccessGW getUserRemoteAccessGws
type RemoteAccessGatewayUser struct {
// in: path
Username string `json:"username"`
}
// swagger:parameters authenticate
type authParamBodyParam struct {
// network
// in: path
Network string `json:"network"`
// AuthParams
// in: body
AuthParams models.AuthParams `json:"auth_params"`
}
// swagger:response signal
type signal struct {
// in: body
Signal models.Signal
}
// swagger:parameters synchost deleteHost updateHost signalPeer updateKeys
type HostID struct {
// HostID
// in: path
HostID string `json:"hostid"`
}
// swagger:parameters addHostToNetwork deleteHostFromNetwork
type HostFromNetworkParams struct {
// hostid to add or delete from network
// in: path
HostID string `json:"hostid"`
// network
// in: path
Network string `json:"network"`
}
// swagger:parameters createEnrollmentKey
type createEnrollmentKeyParams struct {
// APIEnrollmentKey
// in: body
Body models.APIEnrollmentKey `json:"body"`
}
// swagger:parameters updateEnrollmentKey
type updateEnrollmentKeyParams struct {
// KeyID
// in: path
KeyID string `json:"keyid"`
// APIEnrollmentKey
// in: body
Body models.APIEnrollmentKey `json:"body"`
}
// swagger:parameters deleteEnrollmentKey
type deleteEnrollmentKeyParam struct {
// in: path
KeyID string `json:"keyid"`
}
// swagger:parameters handleHostRegister
type RegisterParams struct {
// in: path
Token string `json:"token"`
// in: body
Host models.Host `json:"host"`
}
// swagger:response serverConfigResponse
type serverConfigResponse struct {
// Server Config
// in: body
// example
//{
//"mqusername": "xxxxxxx"
//}
ServerConfig config.ServerConfig `json:"server_config"`
}
// swagger:parameters createAdmin updateUser updateUserNetworks createUser
type userBodyParam struct {
// User
// in: body
User models.User `json:"user"`
}
// swagger:response userBodyResponse
type userBodyResponse struct {
// User
// in: body
User models.User `json:"user"`
}
// swagger:parameters authenticateUser
type userAuthBodyParam struct {
// User Auth Params
// in: body
UserAuthParams models.UserAuthParams `json:"user_auth_params"`
}
// swagger:parameters updateUser updateUserNetworks updateUserAdm createUser deleteUser getUser
type usernamePathParam struct {
// Username
// in: path
Username string `json:"username"`
}
// prevent issues with integration tests for types just used by Swagger docs.
func useUnused() bool {
_ = dnsParams{}
_ = dnsResponse{}
_ = dnsDeletePathParams{}
_ = stringJSONResponse{}
_ = getAllClientsRequest{}
_ = extClientSliceResponse{}
_ = extClientResponse{}
_ = successResponse{}
_ = extClientPathParams{}
_ = extClientBodyParam{}
_ = extClientNetworkPathParam{}
_ = createExtClientPathParams{}
_ = networkNodePathParams{}
_ = byteArrayResponse{}
_ = getNetworksSliceResponse{}
_ = networkBodyParam{}
_ = networkPathParam{}
_ = networkBodyResponse{}
_ = aclContainerBodyParam{}
_ = aclContainerResponse{}
_ = nodeSliceResponse{}
_ = nodeResponse{}
_ = nodeBodyParam{}
_ = relayRequestBodyParam{}
_ = egressGatewayBodyParam{}
_ = authParamBodyParam{}
_ = serverConfigResponse{}
_ = userBodyParam{}
_ = userBodyResponse{}
_ = userAuthBodyParam{}
_ = usernamePathParam{}
_ = hasAdmin{}
_ = apiHostSliceResponse{}
_ = apiHostResponse{}
_ = fileResponse{}
_ = extClientConfParams{}
_ = hostPull{}
_ = okRespone{}
_ = signal{}
_ = filenameToGet{}
_ = dnsNetworkPathParam{}
_ = createEnrollmentKeyParams{}
_ = updateEnrollmentKeyParams{}
_ = deleteEnrollmentKeyParam{}
return false
}

View file

@ -32,17 +32,12 @@ func enrollmentKeyHandlers(r *mux.Router) {
Methods(http.MethodPut)
}
// swagger:route GET /api/v1/enrollment-keys enrollmentKeys getEnrollmentKeys
//
// Lists all EnrollmentKeys for admins.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: EnrollmentKeys
// @Summary Lists all EnrollmentKeys for admins
// @Router /api/v1/enrollment-keys [get]
// @Tags EnrollmentKeys
// @Security oauth
// @Success 200 {array} models.EnrollmentKey
// @Failure 500 {object} models.ErrorResponse
func getEnrollmentKeys(w http.ResponseWriter, r *http.Request) {
keys, err := logic.GetAllEnrollmentKeys()
if err != nil {
@ -67,17 +62,13 @@ func getEnrollmentKeys(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(ret)
}
// swagger:route DELETE /api/v1/enrollment-keys/{keyid} enrollmentKeys deleteEnrollmentKey
//
// Deletes an EnrollmentKey from Netmaker server.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: okResponse
// @Summary Deletes an EnrollmentKey from Netmaker server
// @Router /api/v1/enrollment-keys/{keyid} [delete]
// @Tags EnrollmentKeys
// @Security oauth
// @Param keyid path string true "Enrollment Key ID"
// @Success 200
// @Failure 500 {object} models.ErrorResponse
func deleteEnrollmentKey(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
keyID := params["keyID"]
@ -91,17 +82,14 @@ func deleteEnrollmentKey(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}
// swagger:route POST /api/v1/enrollment-keys enrollmentKeys createEnrollmentKey
//
// Creates an EnrollmentKey for hosts to use on Netmaker server.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: EnrollmentKey
// @Summary Creates an EnrollmentKey for hosts to register with server and join networks
// @Router /api/v1/enrollment-keys [post]
// @Tags EnrollmentKeys
// @Security oauth
// @Param body body models.APIEnrollmentKey true "Enrollment Key parameters"
// @Success 200 {object} models.EnrollmentKey
// @Failure 400 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func createEnrollmentKey(w http.ResponseWriter, r *http.Request) {
var enrollmentKeyBody models.APIEnrollmentKey
@ -121,7 +109,14 @@ func createEnrollmentKey(w http.ResponseWriter, r *http.Request) {
if err != nil {
logger.Log(0, r.Header.Get("user"), "error validating request body: ",
err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("validation error: name length must be between 3 and 32: %w", err), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
fmt.Errorf("validation error: name length must be between 3 and 32: %w", err),
"badrequest",
),
)
return
}
@ -180,17 +175,15 @@ func createEnrollmentKey(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(newEnrollmentKey)
}
// swagger:route PUT /api/v1/enrollment-keys/{keyid} enrollmentKeys updateEnrollmentKey
//
// Updates an EnrollmentKey for hosts to use on Netmaker server. Updates only the relay to use.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: EnrollmentKey
// @Summary Updates an EnrollmentKey. Updates are only limited to the relay to use
// @Router /api/v1/enrollment-keys/{keyid} [put]
// @Tags EnrollmentKeys
// @Security oauth
// @Param keyid path string true "Enrollment Key ID"
// @Param body body models.APIEnrollmentKey true "Enrollment Key parameters"
// @Success 200 {object} models.EnrollmentKey
// @Failure 400 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func updateEnrollmentKey(w http.ResponseWriter, r *http.Request) {
var enrollmentKeyBody models.APIEnrollmentKey
params := mux.Vars(r)
@ -231,17 +224,15 @@ func updateEnrollmentKey(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(newEnrollmentKey)
}
// swagger:route POST /api/v1/enrollment-keys/{token} enrollmentKeys handleHostRegister
//
// Handles a Netclient registration with server and add nodes accordingly.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: RegisterResponse
// @Summary Handles a Netclient registration with server and add nodes accordingly
// @Router /api/v1/host/register/{token} [post]
// @Tags EnrollmentKeys
// @Security oauth
// @Param token path string true "Enrollment Key Token"
// @Param body body models.Host true "Host registration parameters"
// @Success 200 {object} models.RegisterResponse
// @Failure 400 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func handleHostRegister(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
token := params["token"]

View file

@ -28,13 +28,20 @@ import (
func extClientHandlers(r *mux.Router) {
r.HandleFunc("/api/extclients", logic.SecurityCheck(true, http.HandlerFunc(getAllExtClients))).Methods(http.MethodGet)
r.HandleFunc("/api/extclients/{network}", logic.SecurityCheck(true, http.HandlerFunc(getNetworkExtClients))).Methods(http.MethodGet)
r.HandleFunc("/api/extclients/{network}/{clientid}", logic.SecurityCheck(false, http.HandlerFunc(getExtClient))).Methods(http.MethodGet)
r.HandleFunc("/api/extclients/{network}/{clientid}/{type}", logic.SecurityCheck(false, http.HandlerFunc(getExtClientConf))).Methods(http.MethodGet)
r.HandleFunc("/api/extclients/{network}/{clientid}", logic.SecurityCheck(false, http.HandlerFunc(updateExtClient))).Methods(http.MethodPut)
r.HandleFunc("/api/extclients/{network}/{clientid}", logic.SecurityCheck(false, http.HandlerFunc(deleteExtClient))).Methods(http.MethodDelete)
r.HandleFunc("/api/extclients/{network}/{nodeid}", logic.SecurityCheck(false, checkFreeTierLimits(limitChoiceMachines, http.HandlerFunc(createExtClient)))).Methods(http.MethodPost)
r.HandleFunc("/api/extclients", logic.SecurityCheck(true, http.HandlerFunc(getAllExtClients))).
Methods(http.MethodGet)
r.HandleFunc("/api/extclients/{network}", logic.SecurityCheck(true, http.HandlerFunc(getNetworkExtClients))).
Methods(http.MethodGet)
r.HandleFunc("/api/extclients/{network}/{clientid}", logic.SecurityCheck(false, http.HandlerFunc(getExtClient))).
Methods(http.MethodGet)
r.HandleFunc("/api/extclients/{network}/{clientid}/{type}", logic.SecurityCheck(false, http.HandlerFunc(getExtClientConf))).
Methods(http.MethodGet)
r.HandleFunc("/api/extclients/{network}/{clientid}", logic.SecurityCheck(false, http.HandlerFunc(updateExtClient))).
Methods(http.MethodPut)
r.HandleFunc("/api/extclients/{network}/{clientid}", logic.SecurityCheck(false, http.HandlerFunc(deleteExtClient))).
Methods(http.MethodDelete)
r.HandleFunc("/api/extclients/{network}/{nodeid}", logic.SecurityCheck(false, checkFreeTierLimits(limitChoiceMachines, http.HandlerFunc(createExtClient)))).
Methods(http.MethodPost)
}
func checkIngressExists(nodeID string) bool {
@ -45,18 +52,12 @@ func checkIngressExists(nodeID string) bool {
return node.IsIngressGateway
}
// swagger:route GET /api/extclients/{network} ext_client getNetworkExtClients
//
// Get all extclients associated with network.
// Gets all extclients associated with network, including pending extclients.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: extClientSliceResponse
// @Summary Get all remote access client associated with network
// @Router /api/extclients/{network} [get]
// @Tags Remote Access Client
// @Security oauth2
// @Success 200 {object} models.ExtClient
// @Failure 500 {object} models.ErrorResponse
func getNetworkExtClients(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
@ -77,18 +78,12 @@ func getNetworkExtClients(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(extclients)
}
// swagger:route GET /api/extclients ext_client getAllExtClients
//
// A separate function to get all extclients, not just extclients for a particular network.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: extClientSliceResponse
//
// @Summary Fetches All Remote Access Clients across all networks
// @Router /api/extclients [get]
// @Tags Remote Access Client
// @Security oauth2
// @Success 200 {object} models.ExtClient
// @Failure 500 {object} models.ErrorResponse
// Not quite sure if this is necessary. Probably necessary based on front end but may
// want to review after iteration 1 if it's being used or not
func getAllExtClients(w http.ResponseWriter, r *http.Request) {
@ -107,17 +102,13 @@ func getAllExtClients(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(clients)
}
// swagger:route GET /api/extclients/{network}/{clientid} ext_client getExtClient
//
// Get an individual extclient.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: extClientResponse
// @Summary Get an individual remote access client
// @Router /api/extclients/{network}/{clientid} [get]
// @Tags Remote Access Client
// @Security oauth2
// @Success 200 {object} models.ExtClient
// @Failure 500 {object} models.ErrorResponse
// @Failure 403 {object} models.ErrorResponse
func getExtClient(w http.ResponseWriter, r *http.Request) {
// set header.
w.Header().Set("Content-Type", "application/json")
@ -128,8 +119,12 @@ func getExtClient(w http.ResponseWriter, r *http.Request) {
network := params["network"]
client, err := logic.GetExtClient(clientid, network)
if err != nil {
logger.Log(0, r.Header.Get("user"), fmt.Sprintf("failed to get extclient for [%s] on network [%s]: %v",
clientid, network, err))
logger.Log(
0,
r.Header.Get("user"),
fmt.Sprintf("failed to get extclient for [%s] on network [%s]: %v",
clientid, network, err),
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
@ -137,7 +132,11 @@ func getExtClient(w http.ResponseWriter, r *http.Request) {
// check if user has access to extclient
slog.Error("failed to get extclient", "network", network, "clientID",
clientid, "error", errors.New("access is denied"))
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("access is denied"), "forbidden"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("access is denied"), "forbidden"),
)
return
}
@ -146,17 +145,13 @@ func getExtClient(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(client)
}
// swagger:route GET /api/extclients/{network}/{clientid}/{type} ext_client getExtClientConf
//
// Get an individual extclient.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: extClientResponse
// @Summary Get an individual remote access client
// @Router /api/extclients/{network}/{clientid}/{type} [get]
// @Tags Remote Access Client
// @Security oauth2
// @Success 200 {object} models.ExtClient
// @Failure 500 {object} models.ErrorResponse
// @Failure 403 {object} models.ErrorResponse
func getExtClientConf(w http.ResponseWriter, r *http.Request) {
// set header.
w.Header().Set("Content-Type", "application/json")
@ -166,36 +161,63 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) {
networkid := params["network"]
client, err := logic.GetExtClient(clientid, networkid)
if err != nil {
logger.Log(0, r.Header.Get("user"), fmt.Sprintf("failed to get extclient for [%s] on network [%s]: %v",
clientid, networkid, err))
logger.Log(
0,
r.Header.Get("user"),
fmt.Sprintf("failed to get extclient for [%s] on network [%s]: %v",
clientid, networkid, err),
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
if !logic.IsUserAllowedAccessToExtClient(r.Header.Get("user"), client) {
slog.Error("failed to get extclient", "network", networkid, "clientID",
clientid, "error", errors.New("access is denied"))
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("access is denied"), "forbidden"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("access is denied"), "forbidden"),
)
return
}
gwnode, err := logic.GetNodeByID(client.IngressGatewayID)
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to get ingress gateway node [%s] info: %v", client.IngressGatewayID, err))
logger.Log(
0,
r.Header.Get("user"),
fmt.Sprintf(
"failed to get ingress gateway node [%s] info: %v",
client.IngressGatewayID,
err,
),
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
host, err := logic.GetHost(gwnode.HostID.String())
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to get host for ingress gateway node [%s] info: %v", client.IngressGatewayID, err))
logger.Log(
0,
r.Header.Get("user"),
fmt.Sprintf(
"failed to get host for ingress gateway node [%s] info: %v",
client.IngressGatewayID,
err,
),
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
network, err := logic.GetParentNetwork(client.Network)
if err != nil {
logger.Log(1, r.Header.Get("user"), "Could not retrieve Ingress Gateway Network", client.Network)
logger.Log(
1,
r.Header.Get("user"),
"Could not retrieve Ingress Gateway Network",
client.Network,
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
@ -209,8 +231,19 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) {
allowedPreferredIps = append(allowedPreferredIps, host.EndpointIP.String())
allowedPreferredIps = append(allowedPreferredIps, host.EndpointIPv6.String())
if !slices.Contains(allowedPreferredIps, preferredIp) {
slog.Warn("preferred endpoint ip is not associated with the RAG. proceeding with preferred ip", "preferred ip", preferredIp)
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("preferred endpoint ip is not associated with the RAG"), "badrequest"))
slog.Warn(
"preferred endpoint ip is not associated with the RAG. proceeding with preferred ip",
"preferred ip",
preferredIp,
)
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
errors.New("preferred endpoint ip is not associated with the RAG"),
"badrequest",
),
)
return
}
if net.ParseIP(preferredIp).To4() == nil {
@ -354,16 +387,14 @@ Endpoint = %s
json.NewEncoder(w).Encode(client)
}
// swagger:route POST /api/extclients/{network}/{nodeid} ext_client createExtClient
//
// Create an individual extclient. Must have valid key and be unique.
//
// Schemes: https
//
// Security:
// oauth
// Responses:
// 200: okResponse
// @Summary Create an individual remote access client
// @Router /api/extclients/{network}/{nodeid} [post]
// @Tags Remote Access Client
// @Security oauth2
// @Success 200 {string} string "OK"
// @Failure 500 {object} models.ErrorResponse
// @Failure 400 {object} models.ErrorResponse
// @Failure 403 {object} models.ErrorResponse
func createExtClient(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
@ -466,16 +497,40 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
}
if err = logic.CreateExtClient(&extclient); err != nil {
slog.Error("failed to create extclient", "user", r.Header.Get("user"), "network", node.Network, "error", err)
slog.Error(
"failed to create extclient",
"user",
r.Header.Get("user"),
"network",
node.Network,
"error",
err,
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
slog.Info("created extclient", "user", r.Header.Get("user"), "network", node.Network, "clientid", extclient.ClientID)
slog.Info(
"created extclient",
"user",
r.Header.Get("user"),
"network",
node.Network,
"clientid",
extclient.ClientID,
)
w.WriteHeader(http.StatusOK)
go func() {
if err := logic.SetClientDefaultACLs(&extclient); err != nil {
slog.Error("failed to set default acls for extclient", "user", r.Header.Get("user"), "network", node.Network, "error", err)
slog.Error(
"failed to set default acls for extclient",
"user",
r.Header.Get("user"),
"network",
node.Network,
"error",
err,
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
@ -488,17 +543,14 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
}()
}
// swagger:route PUT /api/extclients/{network}/{clientid} ext_client updateExtClient
//
// Update an individual extclient.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: extClientResponse
// @Summary Update an individual remote access client
// @Router /api/extclients/{network}/{clientid} [put]
// @Tags Remote Access Client
// @Security oauth2
// @Success 200 {object} models.ExtClient
// @Failure 500 {object} models.ErrorResponse
// @Failure 400 {object} models.ErrorResponse
// @Failure 403 {object} models.ErrorResponse
func updateExtClient(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
@ -518,7 +570,15 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
network := params["network"]
oldExtClient, err := logic.GetExtClientByName(clientid)
if err != nil {
slog.Error("failed to retrieve extclient", "user", r.Header.Get("user"), "id", clientid, "error", err)
slog.Error(
"failed to retrieve extclient",
"user",
r.Header.Get("user"),
"id",
clientid,
"error",
err,
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
@ -526,7 +586,11 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
// check if user has access to extclient
slog.Error("failed to get extclient", "network", network, "clientID",
clientid, "error", errors.New("access is denied"))
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("access is denied"), "forbidden"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("access is denied"), "forbidden"),
)
return
}
@ -567,12 +631,32 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
}
newclient := logic.UpdateExtClient(&oldExtClient, &update)
if err := logic.DeleteExtClient(oldExtClient.Network, oldExtClient.ClientID); err != nil {
slog.Error("failed to delete ext client", "user", r.Header.Get("user"), "id", oldExtClient.ClientID, "network", oldExtClient.Network, "error", err)
slog.Error(
"failed to delete ext client",
"user",
r.Header.Get("user"),
"id",
oldExtClient.ClientID,
"network",
oldExtClient.Network,
"error",
err,
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
if err := logic.SaveExtClient(&newclient); err != nil {
slog.Error("failed to save ext client", "user", r.Header.Get("user"), "id", newclient.ClientID, "network", newclient.Network, "error", err)
slog.Error(
"failed to save ext client",
"user",
r.Header.Get("user"),
"id",
newclient.ClientID,
"network",
newclient.Network,
"error",
err,
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
@ -588,13 +672,25 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
ingressNode, err := logic.GetNodeByID(newclient.IngressGatewayID)
if err == nil {
if err = mq.PublishPeerUpdate(false); err != nil {
logger.Log(1, "error setting ext peers on", ingressNode.ID.String(), ":", err.Error())
logger.Log(
1,
"error setting ext peers on",
ingressNode.ID.String(),
":",
err.Error(),
)
}
}
if !update.Enabled {
ingressHost, err := logic.GetHost(ingressNode.HostID.String())
if err != nil {
slog.Error("Failed to get ingress host", "node", ingressNode.ID.String(), "error", err)
slog.Error(
"Failed to get ingress host",
"node",
ingressNode.ID.String(),
"error",
err,
)
return
}
nodes, err := logic.GetAllNodes()
@ -602,7 +698,13 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
slog.Error("Failed to get nodes", "error", err)
return
}
go mq.PublishSingleHostPeerUpdate(ingressHost, nodes, nil, []models.ExtClient{oldExtClient}, false)
go mq.PublishSingleHostPeerUpdate(
ingressHost,
nodes,
nil,
[]models.ExtClient{oldExtClient},
false,
)
}
}
@ -610,17 +712,13 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
}
// swagger:route DELETE /api/extclients/{network}/{clientid} ext_client deleteExtClient
//
// Delete an individual extclient.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: successResponse
// @Summary Delete an individual remote access client
// @Router /api/extclients/{network}/{clientid} [delete]
// @Tags Remote Access Client
// @Security oauth2
// @Success 200
// @Failure 500 {object} models.ErrorResponse
// @Failure 403 {object} models.ErrorResponse
func deleteExtClient(w http.ResponseWriter, r *http.Request) {
// Set header
w.Header().Set("Content-Type", "application/json")
@ -640,13 +738,24 @@ func deleteExtClient(w http.ResponseWriter, r *http.Request) {
if !logic.IsUserAllowedAccessToExtClient(r.Header.Get("user"), extclient) {
slog.Error("user not allowed to delete", "network", network, "clientID",
clientid, "error", errors.New("access is denied"))
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("access is denied"), "forbidden"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("access is denied"), "forbidden"),
)
return
}
ingressnode, err := logic.GetNodeByID(extclient.IngressGatewayID)
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to get ingress gateway node [%s] info: %v", extclient.IngressGatewayID, err))
logger.Log(
0,
r.Header.Get("user"),
fmt.Sprintf(
"failed to get ingress gateway node [%s] info: %v",
extclient.IngressGatewayID,
err,
),
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}

View file

@ -6,16 +6,12 @@ import (
"github.com/gorilla/mux"
)
// @Summary Retrieve a file from the file server
// @Router /meshclient/files/{filename} [get]
// @Tags Meshclient
// @Success 200 {body} file "file"
// @Failure 404 {string} string "404 not found"
func fileHandlers(r *mux.Router) {
// swagger:route GET /meshclient/files/{filename} meshclient getFile
//
// Retrieve a file from the file server.
//
// Schemes: https
//
// Security:
// oauth
// Responses:
// 200: fileResponse
r.PathPrefix("/meshclient/files").Handler(http.StripPrefix("/meshclient/files", http.FileServer(http.Dir("./meshclient/files"))))
r.PathPrefix("/meshclient/files").
Handler(http.StripPrefix("/meshclient/files", http.FileServer(http.Dir("./meshclient/files"))))
}

View file

@ -19,23 +19,43 @@ import (
)
func hostHandlers(r *mux.Router) {
r.HandleFunc("/api/hosts", logic.SecurityCheck(true, http.HandlerFunc(getHosts))).Methods(http.MethodGet)
r.HandleFunc("/api/hosts/keys", logic.SecurityCheck(true, http.HandlerFunc(updateAllKeys))).Methods(http.MethodPut)
r.HandleFunc("/api/hosts/{hostid}/keys", logic.SecurityCheck(true, http.HandlerFunc(updateKeys))).Methods(http.MethodPut)
r.HandleFunc("/api/hosts/{hostid}/sync", logic.SecurityCheck(true, http.HandlerFunc(syncHost))).Methods(http.MethodPost)
r.HandleFunc("/api/hosts/{hostid}", logic.SecurityCheck(true, http.HandlerFunc(updateHost))).Methods(http.MethodPut)
r.HandleFunc("/api/hosts/{hostid}", Authorize(true, false, "all", http.HandlerFunc(deleteHost))).Methods(http.MethodDelete)
r.HandleFunc("/api/hosts/{hostid}/upgrade", logic.SecurityCheck(true, http.HandlerFunc(upgradeHost))).Methods(http.MethodPut)
r.HandleFunc("/api/hosts/{hostid}/networks/{network}", logic.SecurityCheck(true, http.HandlerFunc(addHostToNetwork))).Methods(http.MethodPost)
r.HandleFunc("/api/hosts/{hostid}/networks/{network}", logic.SecurityCheck(true, http.HandlerFunc(deleteHostFromNetwork))).Methods(http.MethodDelete)
r.HandleFunc("/api/hosts", logic.SecurityCheck(true, http.HandlerFunc(getHosts))).
Methods(http.MethodGet)
r.HandleFunc("/api/hosts/keys", logic.SecurityCheck(true, http.HandlerFunc(updateAllKeys))).
Methods(http.MethodPut)
r.HandleFunc("/api/hosts/{hostid}/keys", logic.SecurityCheck(true, http.HandlerFunc(updateKeys))).
Methods(http.MethodPut)
r.HandleFunc("/api/hosts/{hostid}/sync", logic.SecurityCheck(true, http.HandlerFunc(syncHost))).
Methods(http.MethodPost)
r.HandleFunc("/api/hosts/{hostid}", logic.SecurityCheck(true, http.HandlerFunc(updateHost))).
Methods(http.MethodPut)
r.HandleFunc("/api/hosts/{hostid}", Authorize(true, false, "all", http.HandlerFunc(deleteHost))).
Methods(http.MethodDelete)
r.HandleFunc("/api/hosts/{hostid}/upgrade", logic.SecurityCheck(true, http.HandlerFunc(upgradeHost))).
Methods(http.MethodPut)
r.HandleFunc("/api/hosts/{hostid}/networks/{network}", logic.SecurityCheck(true, http.HandlerFunc(addHostToNetwork))).
Methods(http.MethodPost)
r.HandleFunc("/api/hosts/{hostid}/networks/{network}", logic.SecurityCheck(true, http.HandlerFunc(deleteHostFromNetwork))).
Methods(http.MethodDelete)
r.HandleFunc("/api/hosts/adm/authenticate", authenticateHost).Methods(http.MethodPost)
r.HandleFunc("/api/v1/host", Authorize(true, false, "host", http.HandlerFunc(pull))).Methods(http.MethodGet)
r.HandleFunc("/api/v1/host/{hostid}/signalpeer", Authorize(true, false, "host", http.HandlerFunc(signalPeer))).Methods(http.MethodPost)
r.HandleFunc("/api/v1/fallback/host/{hostid}", Authorize(true, false, "host", http.HandlerFunc(hostUpdateFallback))).Methods(http.MethodPut)
r.HandleFunc("/api/emqx/hosts", logic.SecurityCheck(true, http.HandlerFunc(delEmqxHosts))).Methods(http.MethodDelete)
r.HandleFunc("/api/v1/host", Authorize(true, false, "host", http.HandlerFunc(pull))).
Methods(http.MethodGet)
r.HandleFunc("/api/v1/host/{hostid}/signalpeer", Authorize(true, false, "host", http.HandlerFunc(signalPeer))).
Methods(http.MethodPost)
r.HandleFunc("/api/v1/fallback/host/{hostid}", Authorize(true, false, "host", http.HandlerFunc(hostUpdateFallback))).
Methods(http.MethodPut)
r.HandleFunc("/api/emqx/hosts", logic.SecurityCheck(true, http.HandlerFunc(delEmqxHosts))).
Methods(http.MethodDelete)
r.HandleFunc("/api/v1/auth-register/host", socketHandler)
}
// @Summary Upgrade a host
// @Router /api/hosts/{hostid}/upgrade [put]
// @Tags Hosts
// @Security oauth
// @Param hostid path string true "Host ID"
// @Success 200 {string} string "passed message to upgrade host"
// @Failure 500 {object} models.ErrorResponse
// upgrade host is a handler to send upgrade message to a host
func upgradeHost(w http.ResponseWriter, r *http.Request) {
host, err := logic.GetHost(mux.Vars(r)["hostid"])
@ -52,17 +72,12 @@ func upgradeHost(w http.ResponseWriter, r *http.Request) {
logic.ReturnSuccessResponse(w, r, "passed message to upgrade host")
}
// swagger:route GET /api/hosts hosts getHosts
//
// Lists all hosts.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: apiHostSliceResponse
// @Summary List all hosts
// @Router /api/hosts [get]
// @Tags Hosts
// @Security oauth
// @Success 200 {array} models.ApiHost
// @Failure 500 {object} models.ErrorResponse
func getHosts(w http.ResponseWriter, r *http.Request) {
currentHosts, err := logic.GetAllHosts()
if err != nil {
@ -77,23 +92,22 @@ func getHosts(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(apiHosts)
}
// swagger:route GET /api/v1/host hosts pullHost
//
// Used by clients for "pull" command
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: hostPull
// @Summary Used by clients for "pull" command
// @Router /api/v1/host [get]
// @Tags Hosts
// @Security oauth
// @Success 200 {object} models.HostPull
// @Failure 500 {object} models.ErrorResponse
func pull(w http.ResponseWriter, r *http.Request) {
hostID := r.Header.Get(hostIDHeader) // return JSON/API formatted keys
if len(hostID) == 0 {
logger.Log(0, "no host authorized to pull")
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("no host authorized to pull"), "internal"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(fmt.Errorf("no host authorized to pull"), "internal"),
)
return
}
host, err := logic.GetHost(hostID)
@ -153,17 +167,14 @@ func pull(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(&response)
}
// swagger:route PUT /api/hosts/{hostid} hosts updateHost
//
// Updates a Netclient host on Netmaker server.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: apiHostResponse
// @Summary Updates a Netclient host on Netmaker server
// @Router /api/hosts/{hostid} [put]
// @Tags Hosts
// @Security oauth
// @Param hostid path string true "Host ID"
// @Param body body models.ApiHost true "New host data"
// @Success 200 {object} models.ApiHost
// @Failure 500 {object} models.ErrorResponse
func updateHost(w http.ResponseWriter, r *http.Request) {
var newHostData models.ApiHost
err := json.NewDecoder(r.Body).Decode(&newHostData)
@ -194,7 +205,13 @@ func updateHost(w http.ResponseWriter, r *http.Request) {
Action: models.UpdateHost,
Host: *newHost,
}); err != nil {
logger.Log(0, r.Header.Get("user"), "failed to send host update: ", currHost.ID.String(), err.Error())
logger.Log(
0,
r.Header.Get("user"),
"failed to send host update: ",
currHost.ID.String(),
err.Error(),
)
}
go func() {
if err := mq.PublishPeerUpdate(false); err != nil {
@ -213,17 +230,14 @@ func updateHost(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(apiHostData)
}
// swagger:route PUT /api/v1/fallback/host/{hostid} hosts hostUpdateFallback
//
// Updates a Netclient host on Netmaker server.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: apiHostResponse
// @Summary Updates a Netclient host on Netmaker server
// @Router /api/v1/fallback/host/{hostid} [put]
// @Tags Hosts
// @Security oauth
// @Param hostid path string true "Host ID"
// @Param body body models.HostUpdate true "Host update data"
// @Success 200 {string} string "updated host data"
// @Failure 500 {object} models.ErrorResponse
func hostUpdateFallback(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
hostid := params["hostid"]
@ -273,17 +287,14 @@ func hostUpdateFallback(w http.ResponseWriter, r *http.Request) {
logic.ReturnSuccessResponse(w, r, "updated host data")
}
// swagger:route DELETE /api/hosts/{hostid} hosts deleteHost
//
// Deletes a Netclient host from Netmaker server.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: apiHostResponse
// @Summary Deletes a Netclient host from Netmaker server
// @Router /api/hosts/{hostid} [delete]
// @Tags Hosts
// @Security oauth
// @Param hostid path string true "Host ID"
// @Param force query bool false "Force delete"
// @Success 200 {object} models.ApiHost
// @Failure 500 {object} models.ErrorResponse
func deleteHost(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
hostid := params["hostid"]
@ -312,14 +323,26 @@ func deleteHost(w http.ResponseWriter, r *http.Request) {
if servercfg.GetBrokerType() == servercfg.EmqxBrokerType {
// delete EMQX credentials for host
if err := mq.GetEmqxHandler().DeleteEmqxUser(currHost.ID.String()); err != nil {
slog.Error("failed to remove host credentials from EMQX", "id", currHost.ID, "error", err)
slog.Error(
"failed to remove host credentials from EMQX",
"id",
currHost.ID,
"error",
err,
)
}
}
if err = mq.HostUpdate(&models.HostUpdate{
Action: models.DeleteHost,
Host: *currHost,
}); err != nil {
logger.Log(0, r.Header.Get("user"), "failed to send delete host update: ", currHost.ID.String(), err.Error())
logger.Log(
0,
r.Header.Get("user"),
"failed to send delete host update: ",
currHost.ID.String(),
err.Error(),
)
}
if err = logic.RemoveHost(currHost, forceDelete); err != nil {
logger.Log(0, r.Header.Get("user"), "failed to delete a host:", err.Error())
@ -333,23 +356,25 @@ func deleteHost(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(apiHostData)
}
// swagger:route POST /api/hosts/{hostid}/networks/{network} hosts addHostToNetwork
//
// Given a network, a host is added to the network.
//
// Schemes: https
//
// Security:
// oauth
// Responses:
// 200: okResponse
// @Summary To Add Host To Network
// @Router /api/hosts/{hostid}/networks/{network} [post]
// @Tags Hosts
// @Security oauth
// @Param hostid path string true "Host ID"
// @Param network path string true "Network name"
// @Success 200 {string} string "OK"
// @Failure 500 {object} models.ErrorResponse
func addHostToNetwork(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
hostid := params["hostid"]
network := params["network"]
if hostid == "" || network == "" {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("hostid or network cannot be empty"), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("hostid or network cannot be empty"), "badrequest"),
)
return
}
// confirm host exists
@ -362,7 +387,14 @@ func addHostToNetwork(w http.ResponseWriter, r *http.Request) {
newNode, err := logic.UpdateHostNetwork(currHost, network, true)
if err != nil {
logger.Log(0, r.Header.Get("user"), "failed to add host to network:", hostid, network, err.Error())
logger.Log(
0,
r.Header.Get("user"),
"failed to add host to network:",
hostid,
network,
err.Error(),
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
@ -384,21 +416,23 @@ func addHostToNetwork(w http.ResponseWriter, r *http.Request) {
logic.SetDNS()
}
}()
logger.Log(2, r.Header.Get("user"), fmt.Sprintf("added host %s to network %s", currHost.Name, network))
logger.Log(
2,
r.Header.Get("user"),
fmt.Sprintf("added host %s to network %s", currHost.Name, network),
)
w.WriteHeader(http.StatusOK)
}
// swagger:route DELETE /api/hosts/{hostid}/networks/{network} hosts deleteHostFromNetwork
//
// Given a network, a host is removed from the network.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: okResponse
// @Summary To Remove Host from Network
// @Router /api/hosts/{hostid}/networks/{network} [delete]
// @Tags Hosts
// @Security oauth
// @Param hostid path string true "Host ID"
// @Param network path string true "Network name"
// @Param force query bool false "Force delete"
// @Success 200 {string} string "OK"
// @Failure 500 {object} models.ErrorResponse
func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
@ -406,7 +440,11 @@ func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) {
network := params["network"]
forceDelete := r.URL.Query().Get("force") == "true"
if hostid == "" || network == "" {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("hostid or network cannot be empty"), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("hostid or network cannot be empty"), "badrequest"),
)
return
}
// confirm host exists
@ -416,14 +454,29 @@ func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) {
// check if there is any daemon nodes that needs to be deleted
node, err := logic.GetNodeByHostRef(hostid, network)
if err != nil {
slog.Error("couldn't get node for host", "hostid", hostid, "network", network, "error", err)
slog.Error(
"couldn't get node for host",
"hostid",
hostid,
"network",
network,
"error",
err,
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
if err = logic.DeleteNodeByID(&node); err != nil {
slog.Error("failed to force delete daemon node",
"nodeid", node.ID.String(), "hostid", hostid, "network", network, "error", err)
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to force delete daemon node: "+err.Error()), "internal"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
fmt.Errorf("failed to force delete daemon node: "+err.Error()),
"internal",
),
)
return
}
logic.ReturnSuccessResponse(w, r, "force deleted daemon node successfully")
@ -441,20 +494,42 @@ func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) {
// force cleanup the node
node, err := logic.GetNodeByHostRef(hostid, network)
if err != nil {
slog.Error("couldn't get node for host", "hostid", hostid, "network", network, "error", err)
slog.Error(
"couldn't get node for host",
"hostid",
hostid,
"network",
network,
"error",
err,
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
if err = logic.DeleteNodeByID(&node); err != nil {
slog.Error("failed to force delete daemon node",
"nodeid", node.ID.String(), "hostid", hostid, "network", network, "error", err)
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to force delete daemon node: "+err.Error()), "internal"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
fmt.Errorf("failed to force delete daemon node: "+err.Error()),
"internal",
),
)
return
}
logic.ReturnSuccessResponse(w, r, "force deleted daemon node successfully")
return
}
logger.Log(0, r.Header.Get("user"), "failed to remove host from network:", hostid, network, err.Error())
logger.Log(
0,
r.Header.Get("user"),
"failed to remove host from network:",
hostid,
network,
err.Error(),
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
@ -464,7 +539,11 @@ func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) {
}
logger.Log(1, "deleting node", node.ID.String(), "from host", currHost.Name)
if err := logic.DeleteNode(node, forceDelete); err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to delete node"), "internal"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(fmt.Errorf("failed to delete node"), "internal"),
)
return
}
go func() {
@ -473,21 +552,23 @@ func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) {
logic.SetDNS()
}
}()
logger.Log(2, r.Header.Get("user"), fmt.Sprintf("removed host %s from network %s", currHost.Name, network))
logger.Log(
2,
r.Header.Get("user"),
fmt.Sprintf("removed host %s from network %s", currHost.Name, network),
)
w.WriteHeader(http.StatusOK)
}
// swagger:route POST /api/hosts/adm/authenticate authenticate authenticateHost
//
// Host based authentication for making further API calls.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: successResponse
// @Summary To Fetch Auth Token for a Host
// @Router /api/hosts/adm/authenticate [post]
// @Tags Auth
// @Accept json
// @Param body body models.AuthParams true "Authentication parameters"
// @Success 200 {object} models.SuccessResponse
// @Failure 400 {object} models.ErrorResponse
// @Failure 401 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func authenticateHost(response http.ResponseWriter, request *http.Request) {
var authRequest models.AuthParams
var errorResponse = models.ErrorResponse{
@ -579,17 +660,14 @@ func authenticateHost(response http.ResponseWriter, request *http.Request) {
response.Write(successJSONResponse)
}
// swagger:route POST /api/hosts/{hostid}/signalpeer hosts signalPeer
//
// send signal to peer.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: signal
// @Summary Send signal to peer
// @Router /api/v1/host/{hostid}/signalpeer [post]
// @Tags Hosts
// @Security oauth
// @Param hostid path string true "Host ID"
// @Param body body models.Signal true "Signal data"
// @Success 200 {object} models.Signal
// @Failure 400 {object} models.ErrorResponse
func signalPeer(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
hostid := params["hostid"]
@ -617,7 +695,11 @@ func signalPeer(w http.ResponseWriter, r *http.Request) {
signal.IsPro = servercfg.IsPro
peerHost, err := logic.GetHost(signal.ToHostID)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to signal, peer not found"), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("failed to signal, peer not found"), "badrequest"),
)
return
}
err = mq.HostUpdate(&models.HostUpdate{
@ -626,7 +708,14 @@ func signalPeer(w http.ResponseWriter, r *http.Request) {
Signal: signal,
})
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to publish signal to peer: "+err.Error()), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
errors.New("failed to publish signal to peer: "+err.Error()),
"badrequest",
),
)
return
}
@ -634,17 +723,12 @@ func signalPeer(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(signal)
}
// swagger:route POST /api/hosts/keys hosts updateAllKeys
//
// Update keys for a network.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: networkBodyResponse
// @Summary Update keys for all hosts
// @Router /api/hosts/keys [put]
// @Tags Hosts
// @Security oauth
// @Success 200 {string} string "OK"
// @Failure 400 {object} models.ErrorResponse
func updateAllKeys(w http.ResponseWriter, r *http.Request) {
var errorResponse = models.ErrorResponse{}
w.Header().Set("Content-Type", "application/json")
@ -664,7 +748,12 @@ func updateAllKeys(w http.ResponseWriter, r *http.Request) {
hostUpdate.Host = host
logger.Log(2, "updating host", host.ID.String(), " for a key update")
if err = mq.HostUpdate(&hostUpdate); err != nil {
logger.Log(0, "failed to send update to node during a network wide key update", host.ID.String(), err.Error())
logger.Log(
0,
"failed to send update to node during a network wide key update",
host.ID.String(),
err.Error(),
)
}
}
}()
@ -672,17 +761,13 @@ func updateAllKeys(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}
// swagger:route POST /api/hosts/{hostid}keys hosts updateKeys
//
// Update keys for a network.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: networkBodyResponse
// @Summary Update keys for a host
// @Router /api/hosts/{hostid}/keys [put]
// @Tags Hosts
// @Security oauth
// @Param hostid path string true "Host ID"
// @Success 200 {string} string "OK"
// @Failure 400 {object} models.ErrorResponse
func updateKeys(w http.ResponseWriter, r *http.Request) {
var errorResponse = models.ErrorResponse{}
w.Header().Set("Content-Type", "application/json")
@ -711,17 +796,13 @@ func updateKeys(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}
// swagger:route POST /api/hosts/{hostid}/sync hosts synchost
//
// Requests a host to pull.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: networkBodyResponse
// @Summary Requests a host to pull
// @Router /api/hosts/{hostid}/sync [post]
// @Tags Hosts
// @Security oauth
// @Param hostid path string true "Host ID"
// @Success 200 {string} string "OK"
// @Failure 400 {object} models.ErrorResponse
func syncHost(w http.ResponseWriter, r *http.Request) {
hostId := mux.Vars(r)["hostid"]
@ -751,17 +832,12 @@ func syncHost(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}
// swagger:route DELETE /api/emqx/hosts hosts delEmqxHosts
//
// Lists all hosts.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: apiHostResponse
// @Summary Deletes all EMQX hosts
// @Router /api/emqx/hosts [delete]
// @Tags Hosts
// @Security oauth
// @Success 200 {string} string "deleted hosts data on emqx"
// @Failure 500 {object} models.ErrorResponse
func delEmqxHosts(w http.ResponseWriter, r *http.Request) {
currentHosts, err := logic.GetAllHosts()
if err != nil {
@ -777,7 +853,13 @@ func delEmqxHosts(w http.ResponseWriter, r *http.Request) {
}
err = mq.GetEmqxHandler().DeleteEmqxUser(servercfg.GetMqUserName())
if err != nil {
slog.Error("failed to remove server credentials from EMQX", "user", servercfg.GetMqUserName(), "error", err)
slog.Error(
"failed to remove server credentials from EMQX",
"user",
servercfg.GetMqUserName(),
"error",
err,
)
}
logic.ReturnSuccessResponse(w, r, "deleted hosts data on emqx")
}

View file

@ -15,17 +15,12 @@ func ipHandlers(r *mux.Router) {
r.HandleFunc("/api/getip", http.HandlerFunc(getPublicIP)).Methods(http.MethodGet)
}
// swagger:route GET /api/getip ipservice getPublicIP
//
// Get the current public IP address.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: byteArrayResponse
// @Summary Get the current public IP address.
// @Router /api/getip [get]
// @Tags IP Service
// @Security oauth2
// @Success 200 {string} string "The public IP address."
// @Failure 400 {string} string "Invalid IP address or no IP found."
func getPublicIP(w http.ResponseWriter, r *http.Request) {
r.Header.Set("Connection", "close")
ip, err := parseIP(r)

View file

@ -9,20 +9,16 @@ import (
)
func legacyHandlers(r *mux.Router) {
r.HandleFunc("/api/v1/legacy/nodes", logic.SecurityCheck(true, http.HandlerFunc(wipeLegacyNodes))).Methods(http.MethodDelete)
r.HandleFunc("/api/v1/legacy/nodes", logic.SecurityCheck(true, http.HandlerFunc(wipeLegacyNodes))).
Methods(http.MethodDelete)
}
// swagger:route DELETE /api/v1/legacy/nodes nodes wipeLegacyNodes
//
// Delete all legacy nodes from DB.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: successResponse
// @Summary Delete all legacy nodes from DB.
// @Router /api/v1/legacy/nodes [delete]
// @Tags Nodes
// @Security oauth2
// @Success 200 {string} string "Wiped all legacy nodes."
// @Failure 400 {object} models.ErrorResponse
func wipeLegacyNodes(w http.ResponseWriter, r *http.Request) {
// Set header
w.Header().Set("Content-Type", "application/json")

View file

@ -22,28 +22,32 @@ import (
)
func networkHandlers(r *mux.Router) {
r.HandleFunc("/api/networks", logic.SecurityCheck(true, http.HandlerFunc(getNetworks))).Methods(http.MethodGet)
r.HandleFunc("/api/networks", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceNetworks, http.HandlerFunc(createNetwork)))).Methods(http.MethodPost)
r.HandleFunc("/api/networks/{networkname}", logic.SecurityCheck(true, http.HandlerFunc(getNetwork))).Methods(http.MethodGet)
r.HandleFunc("/api/networks/{networkname}", logic.SecurityCheck(true, http.HandlerFunc(deleteNetwork))).Methods(http.MethodDelete)
r.HandleFunc("/api/networks/{networkname}", logic.SecurityCheck(true, http.HandlerFunc(updateNetwork))).Methods(http.MethodPut)
r.HandleFunc("/api/networks", logic.SecurityCheck(true, http.HandlerFunc(getNetworks))).
Methods(http.MethodGet)
r.HandleFunc("/api/networks", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceNetworks, http.HandlerFunc(createNetwork)))).
Methods(http.MethodPost)
r.HandleFunc("/api/networks/{networkname}", logic.SecurityCheck(true, http.HandlerFunc(getNetwork))).
Methods(http.MethodGet)
r.HandleFunc("/api/networks/{networkname}", logic.SecurityCheck(true, http.HandlerFunc(deleteNetwork))).
Methods(http.MethodDelete)
r.HandleFunc("/api/networks/{networkname}", logic.SecurityCheck(true, http.HandlerFunc(updateNetwork))).
Methods(http.MethodPut)
// ACLs
r.HandleFunc("/api/networks/{networkname}/acls", logic.SecurityCheck(true, http.HandlerFunc(updateNetworkACL))).Methods(http.MethodPut)
r.HandleFunc("/api/networks/{networkname}/acls/v2", logic.SecurityCheck(true, http.HandlerFunc(updateNetworkACLv2))).Methods(http.MethodPut)
r.HandleFunc("/api/networks/{networkname}/acls", logic.SecurityCheck(true, http.HandlerFunc(getNetworkACL))).Methods(http.MethodGet)
r.HandleFunc("/api/networks/{networkname}/acls", logic.SecurityCheck(true, http.HandlerFunc(updateNetworkACL))).
Methods(http.MethodPut)
r.HandleFunc("/api/networks/{networkname}/acls/v2", logic.SecurityCheck(true, http.HandlerFunc(updateNetworkACLv2))).
Methods(http.MethodPut)
r.HandleFunc("/api/networks/{networkname}/acls", logic.SecurityCheck(true, http.HandlerFunc(getNetworkACL))).
Methods(http.MethodGet)
}
// swagger:route GET /api/networks networks getNetworks
//
// Lists all networks.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: getNetworksSliceResponse
// @Summary Lists all networks
// @Router /api/networks [get]
// @Tags Networks
// @Security oauth
// @Produce json
// @Success 200 {object} models.Network
// @Failure 500 {object} models.ErrorResponse
func getNetworks(w http.ResponseWriter, r *http.Request) {
var err error
@ -61,17 +65,14 @@ func getNetworks(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(allnetworks)
}
// swagger:route GET /api/networks/{networkname} networks getNetwork
//
// Get a network.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: networkBodyResponse
// @Summary Get a network
// @Router /api/networks/{networkname} [get]
// @Tags Networks
// @Security oauth
// @Param networkname path string true "Network name"
// @Produce json
// @Success 200 {object} models.Network
// @Failure 500 {object} models.ErrorResponse
func getNetwork(w http.ResponseWriter, r *http.Request) {
// set header.
w.Header().Set("Content-Type", "application/json")
@ -90,17 +91,16 @@ func getNetwork(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(network)
}
// swagger:route PUT /api/networks/{networkname}/acls networks updateNetworkACL
//
// Update a network ACL (Access Control List).
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: aclContainerResponse
// @Summary Update a network ACL (Access Control List)
// @Router /api/networks/{networkname}/acls [put]
// @Tags Networks
// @Security oauth
// @Param networkname path string true "Network name"
// @Param body body acls.ACLContainer true "ACL container"
// @Produce json
// @Success 200 {object} acls.ACLContainer
// @Failure 400 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func updateNetworkACL(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
@ -140,17 +140,16 @@ func updateNetworkACL(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(newNetACL)
}
// swagger:route PUT /api/networks/{networkname}/acls/v2 networks updateNetworkACL
//
// Update a network ACL (Access Control List).
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: aclContainerResponse
// @Summary Update a network ACL (Access Control List)
// @Router /api/networks/{networkname}/acls/v2 [put]
// @Tags Networks
// @Security oauth
// @Param networkname path string true "Network name"
// @Param body body acls.ACLContainer true "ACL container"
// @Produce json
// @Success 200 {object} acls.ACLContainer
// @Failure 400 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func updateNetworkACLv2(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
@ -279,13 +278,25 @@ func updateNetworkACLv2(w http.ResponseWriter, r *http.Request) {
client := client
err := logic.DeleteExtClient(client.Network, client.ClientID)
if err != nil {
slog.Error("failed to delete client during update", "client", client.ClientID, "error", err.Error())
slog.Error(
"failed to delete client during update",
"client",
client.ClientID,
"error",
err.Error(),
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
err = logic.SaveExtClient(&client)
if err != nil {
slog.Error("failed to save client during update", "client", client.ClientID, "error", err.Error())
slog.Error(
"failed to save client during update",
"client",
client.ClientID,
"error",
err.Error(),
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
@ -310,7 +321,11 @@ func updateNetworkACLv2(w http.ResponseWriter, r *http.Request) {
// update ingress gateways of associated clients
hosts, err := logic.GetAllHosts()
if err != nil {
slog.Error("failed to fetch hosts after network ACL update. skipping publish extclients ACL", "network", netname)
slog.Error(
"failed to fetch hosts after network ACL update. skipping publish extclients ACL",
"network",
netname,
)
return
}
hostsMap := make(map[uuid.UUID]models.Host)
@ -320,7 +335,13 @@ func updateNetworkACLv2(w http.ResponseWriter, r *http.Request) {
for hostId, clients := range assocClientsToDisconnectPerHost {
if host, ok := hostsMap[hostId]; ok {
if err = mq.PublishSingleHostPeerUpdate(&host, allNodes, nil, clients, false); err != nil {
slog.Error("failed to publish peer update to ingress after ACL update on network", "network", netname, "host", hostId)
slog.Error(
"failed to publish peer update to ingress after ACL update on network",
"network",
netname,
"host",
hostId,
)
}
}
}
@ -330,17 +351,14 @@ func updateNetworkACLv2(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(networkACLChange)
}
// swagger:route GET /api/networks/{networkname}/acls networks getNetworkACL
//
// Get a network ACL (Access Control List).
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: aclContainerResponse
// @Summary Get a network ACL (Access Control List)
// @Router /api/networks/{networkname}/acls [get]
// @Tags Networks
// @Security oauth
// @Param networkname path string true "Network name"
// @Produce json
// @Success 200 {object} acls.ACLContainer
// @Failure 500 {object} models.ErrorResponse
func getNetworkACL(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
@ -364,17 +382,15 @@ func getNetworkACL(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(networkACL)
}
// swagger:route DELETE /api/networks/{networkname} networks deleteNetwork
//
// Delete a network. Will not delete if there are any nodes that belong to the network.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: successResponse
// @Summary Delete a network
// @Router /api/networks/{networkname} [delete]
// @Tags Networks
// @Security oauth
// @Param networkname path string true "Network name"
// @Produce json
// @Success 200 {object} models.SuccessResponse
// @Failure 400 {object} models.ErrorResponse
// @Failure 403 {object} models.ErrorResponse
func deleteNetwork(w http.ResponseWriter, r *http.Request) {
// Set header
w.Header().Set("Content-Type", "application/json")
@ -398,17 +414,14 @@ func deleteNetwork(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode("success")
}
// swagger:route POST /api/networks networks createNetwork
//
// Create a network.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: networkBodyResponse
// @Summary Create a network
// @Router /api/networks [post]
// @Tags Networks
// @Security oauth
// @Param body body models.Network true "Network details"
// @Produce json
// @Success 200 {object} models.Network
// @Failure 400 {object} models.ErrorResponse
func createNetwork(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
@ -473,7 +486,14 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
currHost := &defaultHosts[i]
newNode, err := logic.UpdateHostNetwork(currHost, network.NetID, true)
if err != nil {
logger.Log(0, r.Header.Get("user"), "failed to add host to network:", currHost.ID.String(), network.NetID, err.Error())
logger.Log(
0,
r.Header.Get("user"),
"failed to add host to network:",
currHost.ID.String(),
network.NetID,
err.Error(),
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
@ -483,7 +503,14 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
Host: *currHost,
Node: *newNode,
}); err != nil {
logger.Log(0, r.Header.Get("user"), "failed to add host to network:", currHost.ID.String(), network.NetID, err.Error())
logger.Log(
0,
r.Header.Get("user"),
"failed to add host to network:",
currHost.ID.String(),
network.NetID,
err.Error(),
)
}
// make host failover
logic.CreateFailOver(*newNode)
@ -501,17 +528,15 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(network)
}
// swagger:route PUT /api/networks/{networkname} networks updateNetwork
//
// Update pro settings for a network.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: networkBodyResponse
// @Summary Update network settings
// @Router /api/networks/{networkname} [put]
// @Tags Networks
// @Security oauth
// @Param networkname path string true "Network name"
// @Param body body models.Network true "Network details"
// @Produce json
// @Success 200 {object} models.Network
// @Failure 400 {object} models.ErrorResponse
func updateNetwork(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")

View file

@ -21,30 +21,28 @@ var hostIDHeader = "host-id"
func nodeHandlers(r *mux.Router) {
r.HandleFunc("/api/nodes", Authorize(false, false, "user", http.HandlerFunc(getAllNodes))).Methods(http.MethodGet)
r.HandleFunc("/api/nodes/{network}", Authorize(false, true, "network", http.HandlerFunc(getNetworkNodes))).Methods(http.MethodGet)
r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(getNode))).Methods(http.MethodGet)
r.HandleFunc("/api/nodes/{network}/{nodeid}", logic.SecurityCheck(true, http.HandlerFunc(updateNode))).Methods(http.MethodPut)
r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(deleteNode))).Methods(http.MethodDelete)
r.HandleFunc("/api/nodes/{network}/{nodeid}/creategateway", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceEgress, http.HandlerFunc(createEgressGateway)))).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/deletegateway", logic.SecurityCheck(true, http.HandlerFunc(deleteEgressGateway))).Methods(http.MethodDelete)
r.HandleFunc("/api/nodes/{network}/{nodeid}/createingress", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceIngress, http.HandlerFunc(createIngressGateway)))).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", logic.SecurityCheck(true, http.HandlerFunc(deleteIngressGateway))).Methods(http.MethodDelete)
r.HandleFunc("/api/nodes", Authorize(false, false, "user", http.HandlerFunc(getAllNodes))).
Methods(http.MethodGet)
r.HandleFunc("/api/nodes/{network}", Authorize(false, true, "network", http.HandlerFunc(getNetworkNodes))).
Methods(http.MethodGet)
r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(getNode))).
Methods(http.MethodGet)
r.HandleFunc("/api/nodes/{network}/{nodeid}", logic.SecurityCheck(true, http.HandlerFunc(updateNode))).
Methods(http.MethodPut)
r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(deleteNode))).
Methods(http.MethodDelete)
r.HandleFunc("/api/nodes/{network}/{nodeid}/creategateway", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceEgress, http.HandlerFunc(createEgressGateway)))).
Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/deletegateway", logic.SecurityCheck(true, http.HandlerFunc(deleteEgressGateway))).
Methods(http.MethodDelete)
r.HandleFunc("/api/nodes/{network}/{nodeid}/createingress", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceIngress, http.HandlerFunc(createIngressGateway)))).
Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", logic.SecurityCheck(true, http.HandlerFunc(deleteIngressGateway))).
Methods(http.MethodDelete)
r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods(http.MethodPost)
r.HandleFunc("/api/v1/nodes/migrate", migrate).Methods(http.MethodPost)
}
// swagger:route POST /api/nodes/adm/{network}/authenticate authenticate authenticate
//
// Authenticate to make further API calls related to a network.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: successResponse
func authenticate(response http.ResponseWriter, request *http.Request) {
var authRequest models.AuthParams
@ -149,7 +147,11 @@ func authenticate(response http.ResponseWriter, request *http.Request) {
// even if it's technically ok
// This is kind of a poor man's RBAC. There's probably a better/smarter way.
// TODO: Consider better RBAC implementations
func Authorize(hostAllowed, networkCheck bool, authNetwork string, next http.Handler) http.HandlerFunc {
func Authorize(
hostAllowed, networkCheck bool,
authNetwork string,
next http.Handler,
) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var errorResponse = models.ErrorResponse{
Code: http.StatusForbidden, Message: logic.Forbidden_Msg,
@ -258,17 +260,12 @@ func Authorize(hostAllowed, networkCheck bool, authNetwork string, next http.Han
}
}
// swagger:route GET /api/nodes/{network} nodes getNetworkNodes
//
// Gets all nodes associated with network including pending nodes.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeSliceResponse
// @Summary Gets all nodes associated with network including pending nodes
// @Router /api/nodes/adm/{network} [get]
// @Securitydefinitions.oauth2.application OAuth2Application
// @Tags Nodes
// @Success 200 {array} models.Node
// @Failure 500 {object} models.ErrorResponse
func getNetworkNodes(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
@ -288,18 +285,12 @@ func getNetworkNodes(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(apiNodes)
}
// swagger:route GET /api/nodes nodes getAllNodes
//
// Get all nodes across all networks.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeSliceResponse
//
// @Summary Get all nodes across all networks
// @Router /api/nodes [get]
// @Tags Nodes
// @Securitydefinitions.oauth2.application OAuth2Application
// @Success 200 {array} models.ApiNode
// @Failure 500 {object} models.ErrorResponse
// Not quite sure if this is necessary. Probably necessary based on front end but may want to review after iteration 1 if it's being used or not
func getAllNodes(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
@ -327,17 +318,12 @@ func getAllNodes(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(apiNodes)
}
// swagger:route GET /api/nodes/{network}/{nodeid} nodes getNode
//
// Get an individual node.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
// @Summary Get an individual node
// @Router /api/nodes/{network}/{nodeid} [get]
// @Tags Nodes
// @Security oauth2
// @Success 200 {object} models.NodeGet
// @Failure 500 {object} models.ErrorResponse
func getNode(w http.ResponseWriter, r *http.Request) {
// set header.
w.Header().Set("Content-Type", "application/json")
@ -359,15 +345,29 @@ func getNode(w http.ResponseWriter, r *http.Request) {
}
allNodes, err := logic.GetAllNodes()
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("error fetching wg peers config for host [ %s ]: %v", host.ID.String(), err))
logger.Log(
0,
r.Header.Get("user"),
fmt.Sprintf(
"error fetching wg peers config for host [ %s ]: %v",
host.ID.String(),
err,
),
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
hostPeerUpdate, err := logic.GetPeerUpdateForHost(node.Network, host, allNodes, nil, nil)
if err != nil && !database.IsEmptyRecord(err) {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("error fetching wg peers config for host [ %s ]: %v", host.ID.String(), err))
logger.Log(
0,
r.Header.Get("user"),
fmt.Sprintf(
"error fetching wg peers config for host [ %s ]: %v",
host.ID.String(),
err,
),
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
@ -388,17 +388,12 @@ func getNode(w http.ResponseWriter, r *http.Request) {
// == EGRESS ==
// swagger:route POST /api/nodes/{network}/{nodeid}/creategateway nodes createEgressGateway
//
// Create an egress gateway.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
// @Summary Create an egress gateway
// @Router /api/nodes/{network}/{nodeid}/creategateway [post]
// @Tags Nodes
// @Security oauth2
// @Success 200 {object} models.ApiNode
// @Failure 500 {object} models.ErrorResponse
func createEgressGateway(w http.ResponseWriter, r *http.Request) {
var gateway models.EgressGatewayRequest
var params = mux.Vars(r)
@ -431,7 +426,14 @@ func createEgressGateway(w http.ResponseWriter, r *http.Request) {
}
apiNode := node.ConvertToAPINode()
logger.Log(1, r.Header.Get("user"), "created egress gateway on node", gateway.NodeID, "on network", gateway.NetID)
logger.Log(
1,
r.Header.Get("user"),
"created egress gateway on node",
gateway.NodeID,
"on network",
gateway.NetID,
)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiNode)
go func() {
@ -442,17 +444,12 @@ func createEgressGateway(w http.ResponseWriter, r *http.Request) {
}()
}
// swagger:route DELETE /api/nodes/{network}/{nodeid}/deletegateway nodes deleteEgressGateway
//
// Delete an egress gateway.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
// @Summary Delete an egress gateway
// @Router /api/nodes/{network}/{nodeid}/deletegateway [delete]
// @Tags Nodes
// @Security oauth2
// @Success 200 {object} models.ApiNode
// @Failure 500 {object} models.ErrorResponse
func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
@ -474,7 +471,14 @@ func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
}
apiNode := node.ConvertToAPINode()
logger.Log(1, r.Header.Get("user"), "deleted egress gateway on node", nodeid, "on network", netid)
logger.Log(
1,
r.Header.Get("user"),
"deleted egress gateway on node",
nodeid,
"on network",
netid,
)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiNode)
go func() {
@ -487,17 +491,12 @@ func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
// == INGRESS ==
// swagger:route POST /api/nodes/{network}/{nodeid}/createingress nodes createIngressGateway
//
// Create an ingress gateway.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
// @Summary Create an remote access gateway
// @Router /api/nodes/{network}/{nodeid}/createingress [post]
// @Tags Nodes
// @Security oauth2
// @Success 200 {object} models.ApiNode
// @Failure 500 {object} models.ErrorResponse
func createIngressGateway(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
w.Header().Set("Content-Type", "application/json")
@ -520,7 +519,14 @@ func createIngressGateway(w http.ResponseWriter, r *http.Request) {
}
apiNode := node.ConvertToAPINode()
logger.Log(1, r.Header.Get("user"), "created ingress gateway on node", nodeid, "on network", netid)
logger.Log(
1,
r.Header.Get("user"),
"created ingress gateway on node",
nodeid,
"on network",
netid,
)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiNode)
go func() {
@ -530,17 +536,12 @@ func createIngressGateway(w http.ResponseWriter, r *http.Request) {
}()
}
// swagger:route DELETE /api/nodes/{network}/{nodeid}/deleteingress nodes deleteIngressGateway
//
// Delete an ingress gateway.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
// @Summary Delete an remote access gateway
// @Router /api/nodes/{network}/{nodeid}/deleteingress [delete]
// @Tags Nodes
// @Security oauth2
// @Success 200 {object} models.ApiNode
// @Failure 500 {object} models.ErrorResponse
func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
@ -596,7 +597,13 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
slog.Error("publishSingleHostUpdate", "host", host.Name, "error", err)
}
if err := mq.NodeUpdate(&node); err != nil {
slog.Error("error publishing node update to node", "node", node.ID, "error", err)
slog.Error(
"error publishing node update to node",
"node",
node.ID,
"error",
err,
)
}
if servercfg.IsDNSMode() {
logic.SetDNS()
@ -606,17 +613,12 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
}
}
// swagger:route PUT /api/nodes/{network}/{nodeid} nodes updateNode
//
// Update an individual node.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
// @Summary Update an individual node
// @Router /api/nodes/{network}/{nodeid} [put]
// @Tags Nodes
// @Security oauth2
// @Success 200 {object} models.ApiNode
// @Failure 500 {object} models.ErrorResponse
func updateNode(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
@ -642,7 +644,11 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
}
newNode := newData.ConvertToServerNode(&currentNode)
if newNode == nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("error converting node"), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(fmt.Errorf("error converting node"), "badrequest"),
)
return
}
if newNode.IsInternetGateway != currentNode.IsInternetGateway {
@ -686,7 +692,14 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
}
apiNode := newNode.ConvertToAPINode()
logger.Log(1, r.Header.Get("user"), "updated node", currentNode.ID.String(), "on network", currentNode.Network)
logger.Log(
1,
r.Header.Get("user"),
"updated node",
currentNode.ID.String(),
"on network",
currentNode.Network,
)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiNode)
go func(aclUpdate, relayupdate bool, newNode *models.Node) {
@ -704,17 +717,12 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
}(aclUpdate, relayUpdate, newNode)
}
// swagger:route DELETE /api/nodes/{network}/{nodeid} nodes deleteNode
//
// Delete an individual node.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
// @Summary Delete an individual node
// @Router /api/nodes/{network}/{nodeid} [delete]
// @Tags Nodes
// @Security oauth2
// @Success 200 {string} string "Node deleted."
// @Failure 500 {object} models.ErrorResponse
func deleteNode(w http.ResponseWriter, r *http.Request) {
// Set header
w.Header().Set("Content-Type", "application/json")
@ -735,7 +743,11 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
}
purge := forceDelete || fromNode
if err := logic.DeleteNode(&node, purge); err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to delete node"), "internal"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(fmt.Errorf("failed to delete node"), "internal"),
)
return
}

View file

@ -102,18 +102,12 @@ func getUsage(w http.ResponseWriter, _ *http.Request) {
})
}
// swagger:route GET /api/server/status server getStatus
//
// Get the server configuration.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: serverConfigResponse
// @Summary Get the server status
// @Router /api/server/status [get]
// @Tags Server
// @Security oauth2
func getStatus(w http.ResponseWriter, r *http.Request) {
// @Success 200 {object} status
type status struct {
DB bool `json:"db_connected"`
Broker bool `json:"broker_connected"`
@ -131,7 +125,8 @@ func getStatus(w http.ResponseWriter, r *http.Request) {
var trialEndDate time.Time
var err error
isOnTrial := false
if servercfg.IsPro && (servercfg.GetLicenseKey() == "" || servercfg.GetNetmakerTenantID() == "") {
if servercfg.IsPro &&
(servercfg.GetLicenseKey() == "" || servercfg.GetNetmakerTenantID() == "") {
trialEndDate, err = logic.GetTrialEndDate()
if err != nil {
slog.Error("failed to get trial end date", "error", err)
@ -177,17 +172,11 @@ func allowUsers(next http.Handler) http.HandlerFunc {
}
}
// swagger:route GET /api/server/getserverinfo server getServerInfo
//
// Get the server configuration.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: serverConfigResponse
// @Summary Get the server information
// @Router /api/server/getserverinfo [get]
// @Tags Server
// @Security oauth2
// @Success 200 {object} models.ServerConfig
func getServerInfo(w http.ResponseWriter, r *http.Request) {
// Set header
w.Header().Set("Content-Type", "application/json")
@ -198,17 +187,11 @@ func getServerInfo(w http.ResponseWriter, r *http.Request) {
// w.WriteHeader(http.StatusOK)
}
// swagger:route GET /api/server/getconfig server getConfig
//
// Get the server configuration.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: serverConfigResponse
// @Summary Get the server configuration
// @Router /api/server/getconfig [get]
// @Tags Server
// @Security oauth2
// @Success 200 {object} config.ServerConfig
func getConfig(w http.ResponseWriter, r *http.Request) {
// Set header
w.Header().Set("Content-Type", "application/json")

View file

@ -25,31 +25,39 @@ var (
func userHandlers(r *mux.Router) {
r.HandleFunc("/api/users/adm/hassuperadmin", hasSuperAdmin).Methods(http.MethodGet)
r.HandleFunc("/api/users/adm/createsuperadmin", createSuperAdmin).Methods(http.MethodPost)
r.HandleFunc("/api/users/adm/transfersuperadmin/{username}", logic.SecurityCheck(true, http.HandlerFunc(transferSuperAdmin))).Methods(http.MethodPost)
r.HandleFunc("/api/users/adm/transfersuperadmin/{username}", logic.SecurityCheck(true, http.HandlerFunc(transferSuperAdmin))).
Methods(http.MethodPost)
r.HandleFunc("/api/users/adm/authenticate", authenticateUser).Methods(http.MethodPost)
r.HandleFunc("/api/users/{username}", logic.SecurityCheck(true, http.HandlerFunc(updateUser))).Methods(http.MethodPut)
r.HandleFunc("/api/users/{username}", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceUsers, http.HandlerFunc(createUser)))).Methods(http.MethodPost)
r.HandleFunc("/api/users/{username}", logic.SecurityCheck(true, http.HandlerFunc(deleteUser))).Methods(http.MethodDelete)
r.HandleFunc("/api/users/{username}", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUser)))).Methods(http.MethodGet)
r.HandleFunc("/api/users", logic.SecurityCheck(true, http.HandlerFunc(getUsers))).Methods(http.MethodGet)
r.HandleFunc("/api/users_pending", logic.SecurityCheck(true, http.HandlerFunc(getPendingUsers))).Methods(http.MethodGet)
r.HandleFunc("/api/users_pending", logic.SecurityCheck(true, http.HandlerFunc(deleteAllPendingUsers))).Methods(http.MethodDelete)
r.HandleFunc("/api/users_pending/user/{username}", logic.SecurityCheck(true, http.HandlerFunc(deletePendingUser))).Methods(http.MethodDelete)
r.HandleFunc("/api/users_pending/user/{username}", logic.SecurityCheck(true, http.HandlerFunc(approvePendingUser))).Methods(http.MethodPost)
r.HandleFunc("/api/users/{username}", logic.SecurityCheck(true, http.HandlerFunc(updateUser))).
Methods(http.MethodPut)
r.HandleFunc("/api/users/{username}", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceUsers, http.HandlerFunc(createUser)))).
Methods(http.MethodPost)
r.HandleFunc("/api/users/{username}", logic.SecurityCheck(true, http.HandlerFunc(deleteUser))).
Methods(http.MethodDelete)
r.HandleFunc("/api/users/{username}", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUser)))).
Methods(http.MethodGet)
r.HandleFunc("/api/users", logic.SecurityCheck(true, http.HandlerFunc(getUsers))).
Methods(http.MethodGet)
r.HandleFunc("/api/users_pending", logic.SecurityCheck(true, http.HandlerFunc(getPendingUsers))).
Methods(http.MethodGet)
r.HandleFunc("/api/users_pending", logic.SecurityCheck(true, http.HandlerFunc(deleteAllPendingUsers))).
Methods(http.MethodDelete)
r.HandleFunc("/api/users_pending/user/{username}", logic.SecurityCheck(true, http.HandlerFunc(deletePendingUser))).
Methods(http.MethodDelete)
r.HandleFunc("/api/users_pending/user/{username}", logic.SecurityCheck(true, http.HandlerFunc(approvePendingUser))).
Methods(http.MethodPost)
}
// swagger:route POST /api/users/adm/authenticate authenticate authenticateUser
//
// User authenticates using its password and retrieves a JWT for authorization.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: successResponse
// @Summary Authenticate a user to retrieve an authorization token
// @Router /api/users/adm/authenticate [post]
// @Tags Auth
// @Accept json
// @Param body body models.UserAuthParams true "Authentication parameters"
// @Success 200 {object} models.SuccessResponse
// @Failure 400 {object} models.ErrorResponse
// @Failure 401 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func authenticateUser(response http.ResponseWriter, request *http.Request) {
// Auth request consists of Mac Address and Password (from node that is authorizing
@ -60,7 +68,11 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
}
if !servercfg.IsBasicAuthEnabled() {
logic.ReturnErrorResponse(response, request, logic.FormatError(fmt.Errorf("basic auth is disabled"), "badrequest"))
logic.ReturnErrorResponse(
response,
request,
logic.FormatError(fmt.Errorf("basic auth is disabled"), "badrequest"),
)
return
}
@ -83,7 +95,11 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
return
}
if !(user.IsAdmin || user.IsSuperAdmin) {
logic.ReturnErrorResponse(response, request, logic.FormatError(errors.New("only admins can access dashboard"), "unauthorized"))
logic.ReturnErrorResponse(
response,
request,
logic.FormatError(errors.New("only admins can access dashboard"), "unauthorized"),
)
return
}
}
@ -99,7 +115,11 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
if jwt == "" {
// very unlikely that err is !nil and no jwt returned, but handle it anyways.
logger.Log(0, username, "jwt token is empty")
logic.ReturnErrorResponse(response, request, logic.FormatError(errors.New("no token returned"), "internal"))
logic.ReturnErrorResponse(
response,
request,
logic.FormatError(errors.New("no token returned"), "internal"),
)
return
}
@ -133,9 +153,19 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
}
for _, client := range clients {
if client.OwnerID == username && !client.Enabled {
slog.Info(fmt.Sprintf("enabling ext client %s for user %s due to RAC autodisabling feature", client.ClientID, client.OwnerID))
slog.Info(
fmt.Sprintf(
"enabling ext client %s for user %s due to RAC autodisabling feature",
client.ClientID,
client.OwnerID,
),
)
if newClient, err := logic.ToggleExtClientConnectivity(&client, true); err != nil {
slog.Error("error enabling ext client in RAC autodisable hook", "error", err)
slog.Error(
"error enabling ext client in RAC autodisable hook",
"error",
err,
)
continue // dont return but try for other clients
} else {
// publish peer update to ingress gateway
@ -151,17 +181,11 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
}()
}
// swagger:route GET /api/users/adm/hassuperadmin user hasSuperAdmin
//
// Checks whether the server has an admin.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: hasAdmin
// @Summary Check if the server has a super admin
// @Router /api/users/adm/hassuperadmin [get]
// @Tags Users
// @Success 200 {object} bool
// @Failure 500 {object} models.ErrorResponse
func hasSuperAdmin(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
@ -177,17 +201,12 @@ func hasSuperAdmin(w http.ResponseWriter, r *http.Request) {
}
// swagger:route GET /api/users/{username} user getUser
//
// Get an individual user.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: userBodyResponse
// @Summary Get an individual user
// @Router /api/users/{username} [get]
// @Tags Users
// @Param username path string true "Username of the user to fetch"
// @Success 200 {object} models.User
// @Failure 500 {object} models.ErrorResponse
func getUser(w http.ResponseWriter, r *http.Request) {
// set header.
w.Header().Set("Content-Type", "application/json")
@ -205,17 +224,11 @@ func getUser(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(user)
}
// swagger:route GET /api/users user getUsers
//
// Get all users.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: userBodyResponse
// @Summary Get all users
// @Router /api/users [get]
// @Tags Users
// @Success 200 {array} models.User
// @Failure 500 {object} models.ErrorResponse
func getUsers(w http.ResponseWriter, r *http.Request) {
// set header.
w.Header().Set("Content-Type", "application/json")
@ -233,17 +246,13 @@ func getUsers(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(users)
}
// swagger:route POST /api/users/adm/createsuperadmin user createAdmin
//
// Make a user an admin.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: userBodyResponse
// @Summary Create a super admin
// @Router /api/users/adm/createsuperadmin [post]
// @Tags Users
// @Param body body models.User true "User details"
// @Success 200 {object} models.User
// @Failure 400 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func createSuperAdmin(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
@ -257,7 +266,11 @@ func createSuperAdmin(w http.ResponseWriter, r *http.Request) {
}
if !servercfg.IsBasicAuthEnabled() {
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("basic auth is disabled"), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(fmt.Errorf("basic auth is disabled"), "badrequest"),
)
return
}
@ -271,17 +284,13 @@ func createSuperAdmin(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(logic.ToReturnUser(u))
}
// swagger:route POST /api/users/adm/transfersuperadmin user transferSuperAdmin
//
// Transfers superadmin role to an admin user.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: userBodyResponse
// @Summary Transfer super admin role to another admin user
// @Router /api/users/adm/transfersuperadmin/{username} [post]
// @Tags Users
// @Param username path string true "Username of the user to transfer super admin role"
// @Success 200 {object} models.User
// @Failure 403 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func transferSuperAdmin(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
caller, err := logic.GetUser(r.Header.Get("user"))
@ -289,7 +298,14 @@ func transferSuperAdmin(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
}
if !caller.IsSuperAdmin {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("only superadmin can assign the superadmin role to another user"), "forbidden"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
errors.New("only superadmin can assign the superadmin role to another user"),
"forbidden",
),
)
return
}
var params = mux.Vars(r)
@ -301,11 +317,22 @@ func transferSuperAdmin(w http.ResponseWriter, r *http.Request) {
return
}
if !u.IsAdmin {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("only admins can be promoted to superadmin role"), "forbidden"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
errors.New("only admins can be promoted to superadmin role"),
"forbidden",
),
)
return
}
if !servercfg.IsBasicAuthEnabled() {
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("basic auth is disabled"), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(fmt.Errorf("basic auth is disabled"), "badrequest"),
)
return
}
@ -329,17 +356,15 @@ func transferSuperAdmin(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(logic.ToReturnUser(*u))
}
// swagger:route POST /api/users/{username} user createUser
//
// Create a user.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: userBodyResponse
// @Summary Create a user
// @Router /api/users/{username} [post]
// @Tags Users
// @Param username path string true "Username of the user to create"
// @Param body body models.User true "User details"
// @Success 200 {object} models.User
// @Failure 400 {object} models.ErrorResponse
// @Failure 403 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func createUser(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
caller, err := logic.GetUser(r.Header.Get("user"))
@ -368,7 +393,14 @@ func createUser(w http.ResponseWriter, r *http.Request) {
return
}
if !servercfg.IsPro && !user.IsAdmin {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("non-admins users can only be created on Pro version"), "forbidden"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
errors.New("non-admins users can only be created on Pro version"),
"forbidden",
),
)
return
}
@ -382,17 +414,15 @@ func createUser(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(logic.ToReturnUser(user))
}
// swagger:route PUT /api/users/{username} user updateUser
//
// Update a user.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: userBodyResponse
// @Summary Update a user
// @Router /api/users/{username} [put]
// @Tags Users
// @Param username path string true "Username of the user to update"
// @Param body body models.User true "User details"
// @Success 200 {object} models.User
// @Failure 400 {object} models.ErrorResponse
// @Failure 403 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func updateUser(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
@ -426,7 +456,14 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
return
}
if user.UserName != userchange.UserName {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("user in param and request body not matching"), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
errors.New("user in param and request body not matching"),
"badrequest",
),
)
return
}
selfUpdate := false
@ -436,23 +473,64 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
if !ismaster && !selfUpdate {
if caller.IsAdmin && user.IsSuperAdmin {
slog.Error("non-superadmin user", "caller", caller.UserName, "attempted to update superadmin user", username)
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("cannot update superadmin user"), "forbidden"))
slog.Error(
"non-superadmin user",
"caller",
caller.UserName,
"attempted to update superadmin user",
username,
)
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("cannot update superadmin user"), "forbidden"),
)
return
}
if !caller.IsAdmin && !caller.IsSuperAdmin {
slog.Error("operation not allowed", "caller", caller.UserName, "attempted to update user", username)
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("cannot update superadmin user"), "forbidden"))
slog.Error(
"operation not allowed",
"caller",
caller.UserName,
"attempted to update user",
username,
)
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("cannot update superadmin user"), "forbidden"),
)
return
}
if caller.IsAdmin && user.IsAdmin {
slog.Error("admin user cannot update another admin", "caller", caller.UserName, "attempted to update admin user", username)
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("admin user cannot update another admin"), "forbidden"))
slog.Error(
"admin user cannot update another admin",
"caller",
caller.UserName,
"attempted to update admin user",
username,
)
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
errors.New("admin user cannot update another admin"),
"forbidden",
),
)
return
}
if caller.IsAdmin && userchange.IsAdmin {
err = errors.New("admin user cannot update role of an another user to admin")
slog.Error("failed to update user", "caller", caller.UserName, "attempted to update user", username, "error", err)
slog.Error(
"failed to update user",
"caller",
caller.UserName,
"attempted to update user",
username,
"error",
err,
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
return
}
@ -460,16 +538,39 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
}
if !ismaster && selfUpdate {
if user.IsAdmin != userchange.IsAdmin || user.IsSuperAdmin != userchange.IsSuperAdmin {
slog.Error("user cannot change his own role", "caller", caller.UserName, "attempted to update user role", username)
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("user not allowed to self assign role"), "forbidden"))
slog.Error(
"user cannot change his own role",
"caller",
caller.UserName,
"attempted to update user role",
username,
)
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("user not allowed to self assign role"), "forbidden"),
)
return
}
}
if ismaster {
if !user.IsSuperAdmin && userchange.IsSuperAdmin {
slog.Error("operation not allowed", "caller", logic.MasterUser, "attempted to update user role to superadmin", username)
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("attempted to update user role to superadmin"), "forbidden"))
slog.Error(
"operation not allowed",
"caller",
logic.MasterUser,
"attempted to update user role to superadmin",
username,
)
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
errors.New("attempted to update user role to superadmin"),
"forbidden",
),
)
return
}
}
@ -491,17 +592,12 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(logic.ToReturnUser(*user))
}
// swagger:route DELETE /api/users/{username} user deleteUser
//
// Delete a user.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: userBodyResponse
// @Summary Delete a user
// @Router /api/users/{username} [delete]
// @Tags Users
// @Param username path string true "Username of the user to delete"
// @Success 200 {string} string
// @Failure 500 {object} models.ErrorResponse
func deleteUser(w http.ResponseWriter, r *http.Request) {
// Set header
w.Header().Set("Content-Type", "application/json")
@ -523,14 +619,30 @@ func deleteUser(w http.ResponseWriter, r *http.Request) {
if user.IsSuperAdmin {
slog.Error(
"failed to delete user: ", "user", username, "error", "superadmin cannot be deleted")
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("superadmin cannot be deleted"), "internal"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(fmt.Errorf("superadmin cannot be deleted"), "internal"),
)
return
}
if !caller.IsSuperAdmin {
if caller.IsAdmin && user.IsAdmin {
slog.Error(
"failed to delete user: ", "user", username, "error", "admin cannot delete another admin user, including oneself")
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("admin cannot delete another admin user, including oneself"), "internal"))
"failed to delete user: ",
"user",
username,
"error",
"admin cannot delete another admin user, including oneself",
)
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
fmt.Errorf("admin cannot delete another admin user, including oneself"),
"internal",
),
)
return
}
}
@ -586,17 +698,11 @@ func socketHandler(w http.ResponseWriter, r *http.Request) {
go auth.SessionHandler(conn)
}
// swagger:route GET /api/users_pending user getPendingUsers
//
// Get all pending users.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: userBodyResponse
// @Summary Get all pending users
// @Router /api/users_pending [get]
// @Tags Users
// @Success 200 {array} models.User
// @Failure 500 {object} models.ErrorResponse
func getPendingUsers(w http.ResponseWriter, r *http.Request) {
// set header.
w.Header().Set("Content-Type", "application/json")
@ -613,17 +719,12 @@ func getPendingUsers(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(users)
}
// swagger:route POST /api/users_pending/user/{username} user approvePendingUser
//
// approve pending user.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: userBodyResponse
// @Summary Approve a pending user
// @Router /api/users_pending/user/{username} [post]
// @Tags Users
// @Param username path string true "Username of the pending user to approve"
// @Success 200 {string} string
// @Failure 500 {object} models.ErrorResponse
func approvePendingUser(w http.ResponseWriter, r *http.Request) {
// set header.
w.Header().Set("Content-Type", "application/json")
@ -647,12 +748,23 @@ func approvePendingUser(w http.ResponseWriter, r *http.Request) {
UserName: user.UserName,
Password: newPass,
}); err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to create user: %s", err), "internal"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(fmt.Errorf("failed to create user: %s", err), "internal"),
)
return
}
err = logic.DeletePendingUser(username)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to delete pending user: %s", err), "internal"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
fmt.Errorf("failed to delete pending user: %s", err),
"internal",
),
)
return
}
break
@ -661,17 +773,12 @@ func approvePendingUser(w http.ResponseWriter, r *http.Request) {
logic.ReturnSuccessResponse(w, r, "approved "+username)
}
// swagger:route DELETE /api/users_pending/user/{username} user deletePendingUser
//
// delete pending user.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: userBodyResponse
// @Summary Delete a pending user
// @Router /api/users_pending/user/{username} [delete]
// @Tags Users
// @Param username path string true "Username of the pending user to delete"
// @Success 200 {string} string
// @Failure 500 {object} models.ErrorResponse
func deletePendingUser(w http.ResponseWriter, r *http.Request) {
// set header.
w.Header().Set("Content-Type", "application/json")
@ -688,7 +795,14 @@ func deletePendingUser(w http.ResponseWriter, r *http.Request) {
if user.UserName == username {
err = logic.DeletePendingUser(username)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to delete pending user: %s", err), "internal"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
fmt.Errorf("failed to delete pending user: %s", err),
"internal",
),
)
return
}
break
@ -697,23 +811,24 @@ func deletePendingUser(w http.ResponseWriter, r *http.Request) {
logic.ReturnSuccessResponse(w, r, "deleted pending "+username)
}
// swagger:route DELETE /api/users_pending/{username}/pending user deleteAllPendingUsers
//
// delete all pending users.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: userBodyResponse
// @Summary Delete all pending users
// @Router /api/users_pending [delete]
// @Tags Users
// @Success 200 {string} string
// @Failure 500 {object} models.ErrorResponse
func deleteAllPendingUsers(w http.ResponseWriter, r *http.Request) {
// set header.
w.Header().Set("Content-Type", "application/json")
err := database.DeleteAllRecords(database.PENDING_USERS_TABLE_NAME)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to delete all pending users "+err.Error()), "internal"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
errors.New("failed to delete all pending users "+err.Error()),
"internal",
),
)
return
}
logic.ReturnSuccessResponse(w, r, "cleared all pending users")

1
docs/APIUsage.md Normal file
View file

@ -0,0 +1 @@
Most actions that can be performed via API can be performed via UI. We recommend managing your networks using the official netmaker-ui project. However, Netmaker can also be run without the UI, and all functions can be achieved via API calls. If your use case requires using Netmaker without the UI or you need to do some troubleshooting/advanced configuration, using the API directly may help.

10
docs/Authentication.md Normal file
View file

@ -0,0 +1,10 @@
API calls are primarily authenticated using a user authentication token. This token should be included in the header as follows:
-H "Authorization: Bearer <YOUR_AUTH_TOKEN>"
To obtain YOUR_AUTH_TOKEN:
Call the api/users/adm/authenticate endpoint (see documentation below for details).
Note: While a MasterKey exists (configurable via env var or config file), it should be considered a backup option, used only when server access is lost. By default, this key is "secret key," but it's crucial to change this and keep it secure in your instance.
For more information on configuration and security best practices, refer to the [Netmaker documentation](https://docs.netmaker.org/index.html).

1
docs/Pricing.md Normal file
View file

@ -0,0 +1 @@
Check out our [Pricing](https://www.netmaker.io/pricing). And Feel Free to [Contact Us](https://www.netmaker.io/contact) if you have any questions or need some clarifications.

38
main.go
View file

@ -29,6 +29,17 @@ import (
var version = "v0.25.0"
// @title NetMaker
// @version 0.24.3
// @description NetMaker API Docs
// @tag.name APIUsage
// @tag.description.markdown
// @tag.name Authentication
// @tag.description.markdown
// @tag.name Pricing
// @tag.description.markdown
// @host api.demo.netmaker.io
// Start DB Connection and start API Request Handler
func main() {
absoluteConfigPath := flag.String("c", "", "absolute path to configuration file")
@ -135,7 +146,10 @@ func startControllers(wg *sync.WaitGroup, ctx context.Context) {
}
if !servercfg.IsRestBackend() && !servercfg.IsMessageQueueBackend() {
logger.Log(0, "No Server Mode selected, so nothing is being served! Set Rest mode (REST_BACKEND) or MessageQueue (MESSAGEQUEUE_BACKEND) to 'true'.")
logger.Log(
0,
"No Server Mode selected, so nothing is being served! Set Rest mode (REST_BACKEND) or MessageQueue (MESSAGEQUEUE_BACKEND) to 'true'.",
)
}
wg.Add(1)
@ -167,10 +181,21 @@ func runMessageQueue(wg *sync.WaitGroup, ctx context.Context) {
node.Action = models.NODE_DELETE
node.PendingDelete = true
if err := mq.NodeUpdate(node); err != nil {
logger.Log(0, "failed to send peer update for deleted node: ", node.ID.String(), err.Error())
logger.Log(
0,
"failed to send peer update for deleted node: ",
node.ID.String(),
err.Error(),
)
}
if err := logic.DeleteNode(node, true); err != nil {
slog.Error("error deleting expired node", "nodeid", node.ID.String(), "error", err.Error())
slog.Error(
"error deleting expired node",
"nodeid",
node.ID.String(),
"error",
err.Error(),
)
}
go mq.PublishDeletedNodePeerUpdate(node)
}
@ -189,7 +214,12 @@ func setVerbosity() {
}
return a
}
logger := slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{AddSource: true, ReplaceAttr: replace, Level: logLevel}))
logger := slog.New(
slog.NewJSONHandler(
os.Stderr,
&slog.HandlerOptions{AddSource: true, ReplaceAttr: replace, Level: logLevel},
),
)
slog.SetDefault(logger)
switch verbose {
case 4:

View file

@ -52,111 +52,111 @@ type Iface struct {
// CommonNode - represents a commonn node data elements shared by netmaker and netclient
type CommonNode struct {
ID uuid.UUID `json:"id" yaml:"id"`
HostID uuid.UUID `json:"hostid" yaml:"hostid"`
Network string `json:"network" yaml:"network"`
NetworkRange net.IPNet `json:"networkrange" yaml:"networkrange"`
NetworkRange6 net.IPNet `json:"networkrange6" yaml:"networkrange6"`
Server string `json:"server" yaml:"server"`
Connected bool `json:"connected" yaml:"connected"`
Address net.IPNet `json:"address" yaml:"address"`
Address6 net.IPNet `json:"address6" yaml:"address6"`
Action string `json:"action" yaml:"action"`
LocalAddress net.IPNet `json:"localaddress" yaml:"localaddress"`
IsEgressGateway bool `json:"isegressgateway" yaml:"isegressgateway"`
EgressGatewayRanges []string `json:"egressgatewayranges" bson:"egressgatewayranges" yaml:"egressgatewayranges"`
IsIngressGateway bool `json:"isingressgateway" yaml:"isingressgateway"`
IsRelayed bool `json:"isrelayed" bson:"isrelayed" yaml:"isrelayed"`
RelayedBy string `json:"relayedby" bson:"relayedby" yaml:"relayedby"`
IsRelay bool `json:"isrelay" bson:"isrelay" yaml:"isrelay"`
RelayedNodes []string `json:"relaynodes" yaml:"relayedNodes"`
IngressDNS string `json:"ingressdns" yaml:"ingressdns"`
DNSOn bool `json:"dnson" yaml:"dnson"`
ID uuid.UUID `json:"id" yaml:"id"`
HostID uuid.UUID `json:"hostid" yaml:"hostid"`
Network string `json:"network" yaml:"network"`
NetworkRange net.IPNet `json:"networkrange" yaml:"networkrange" swaggertype:"primitive,integer"`
NetworkRange6 net.IPNet `json:"networkrange6" yaml:"networkrange6" swaggertype:"primitive,number"`
Server string `json:"server" yaml:"server"`
Connected bool `json:"connected" yaml:"connected"`
Address net.IPNet `json:"address" yaml:"address"`
Address6 net.IPNet `json:"address6" yaml:"address6"`
Action string `json:"action" yaml:"action"`
LocalAddress net.IPNet `json:"localaddress" yaml:"localaddress"`
IsEgressGateway bool `json:"isegressgateway" yaml:"isegressgateway"`
EgressGatewayRanges []string `json:"egressgatewayranges" yaml:"egressgatewayranges" bson:"egressgatewayranges"`
IsIngressGateway bool `json:"isingressgateway" yaml:"isingressgateway"`
IsRelayed bool `json:"isrelayed" yaml:"isrelayed" bson:"isrelayed"`
RelayedBy string `json:"relayedby" yaml:"relayedby" bson:"relayedby"`
IsRelay bool `json:"isrelay" yaml:"isrelay" bson:"isrelay"`
RelayedNodes []string `json:"relaynodes" yaml:"relayedNodes"`
IngressDNS string `json:"ingressdns" yaml:"ingressdns"`
DNSOn bool `json:"dnson" yaml:"dnson"`
}
// Node - a model of a network node
type Node struct {
CommonNode
PendingDelete bool `json:"pendingdelete" bson:"pendingdelete" yaml:"pendingdelete"`
LastModified time.Time `json:"lastmodified" bson:"lastmodified" yaml:"lastmodified"`
LastCheckIn time.Time `json:"lastcheckin" bson:"lastcheckin" yaml:"lastcheckin"`
LastPeerUpdate time.Time `json:"lastpeerupdate" bson:"lastpeerupdate" yaml:"lastpeerupdate"`
ExpirationDateTime time.Time `json:"expdatetime" bson:"expdatetime" yaml:"expdatetime"`
PendingDelete bool `json:"pendingdelete" bson:"pendingdelete" yaml:"pendingdelete"`
LastModified time.Time `json:"lastmodified" bson:"lastmodified" yaml:"lastmodified"`
LastCheckIn time.Time `json:"lastcheckin" bson:"lastcheckin" yaml:"lastcheckin"`
LastPeerUpdate time.Time `json:"lastpeerupdate" bson:"lastpeerupdate" yaml:"lastpeerupdate"`
ExpirationDateTime time.Time `json:"expdatetime" bson:"expdatetime" yaml:"expdatetime"`
EgressGatewayNatEnabled bool `json:"egressgatewaynatenabled" bson:"egressgatewaynatenabled" yaml:"egressgatewaynatenabled"`
EgressGatewayRequest EgressGatewayRequest `json:"egressgatewayrequest" bson:"egressgatewayrequest" yaml:"egressgatewayrequest"`
IngressGatewayRange string `json:"ingressgatewayrange" bson:"ingressgatewayrange" yaml:"ingressgatewayrange"`
IngressGatewayRange6 string `json:"ingressgatewayrange6" bson:"ingressgatewayrange6" yaml:"ingressgatewayrange6"`
EgressGatewayRequest EgressGatewayRequest `json:"egressgatewayrequest" bson:"egressgatewayrequest" yaml:"egressgatewayrequest"`
IngressGatewayRange string `json:"ingressgatewayrange" bson:"ingressgatewayrange" yaml:"ingressgatewayrange"`
IngressGatewayRange6 string `json:"ingressgatewayrange6" bson:"ingressgatewayrange6" yaml:"ingressgatewayrange6"`
Metadata string `json:"metadata"`
// == PRO ==
DefaultACL string `json:"defaultacl,omitempty" bson:"defaultacl,omitempty" yaml:"defaultacl,omitempty" validate:"checkyesornoorunset"`
OwnerID string `json:"ownerid,omitempty" bson:"ownerid,omitempty" yaml:"ownerid,omitempty"`
IsFailOver bool `json:"is_fail_over" yaml:"is_fail_over"`
FailOverPeers map[string]struct{} `json:"fail_over_peers" yaml:"fail_over_peers"`
FailedOverBy uuid.UUID `json:"failed_over_by" yaml:"failed_over_by"`
IsInternetGateway bool `json:"isinternetgateway" yaml:"isinternetgateway"`
InetNodeReq InetNodeReq `json:"inet_node_req" yaml:"inet_node_req"`
InternetGwID string `json:"internetgw_node_id" yaml:"internetgw_node_id"`
AdditionalRagIps []net.IP `json:"additional_rag_ips" yaml:"additional_rag_ips"`
DefaultACL string `json:"defaultacl,omitempty" bson:"defaultacl,omitempty" yaml:"defaultacl,omitempty" validate:"checkyesornoorunset"`
OwnerID string `json:"ownerid,omitempty" bson:"ownerid,omitempty" yaml:"ownerid,omitempty"`
IsFailOver bool `json:"is_fail_over" yaml:"is_fail_over"`
FailOverPeers map[string]struct{} `json:"fail_over_peers" yaml:"fail_over_peers"`
FailedOverBy uuid.UUID `json:"failed_over_by" yaml:"failed_over_by"`
IsInternetGateway bool `json:"isinternetgateway" yaml:"isinternetgateway"`
InetNodeReq InetNodeReq `json:"inet_node_req" yaml:"inet_node_req"`
InternetGwID string `json:"internetgw_node_id" yaml:"internetgw_node_id"`
AdditionalRagIps []net.IP `json:"additional_rag_ips" yaml:"additional_rag_ips" swaggertype:"array,number"`
}
// LegacyNode - legacy struct for node model
type LegacyNode struct {
ID string `json:"id,omitempty" bson:"id,omitempty" yaml:"id,omitempty" validate:"required,min=5,id_unique"`
Address string `json:"address" bson:"address" yaml:"address" validate:"omitempty,ipv4"`
Address6 string `json:"address6" bson:"address6" yaml:"address6" validate:"omitempty,ipv6"`
LocalAddress string `json:"localaddress" bson:"localaddress" yaml:"localaddress" validate:"omitempty"`
Interfaces []Iface `json:"interfaces" yaml:"interfaces"`
Name string `json:"name" bson:"name" yaml:"name" validate:"omitempty,max=62,in_charset"`
NetworkSettings Network `json:"networksettings" bson:"networksettings" yaml:"networksettings" validate:"-"`
ListenPort int32 `json:"listenport" bson:"listenport" yaml:"listenport" validate:"omitempty,numeric,min=1024,max=65535"`
LocalListenPort int32 `json:"locallistenport" bson:"locallistenport" yaml:"locallistenport" validate:"numeric,min=0,max=65535"`
PublicKey string `json:"publickey" bson:"publickey" yaml:"publickey" validate:"required,base64"`
Endpoint string `json:"endpoint" bson:"endpoint" yaml:"endpoint" validate:"required,ip"`
AllowedIPs []string `json:"allowedips" bson:"allowedips" yaml:"allowedips"`
PersistentKeepalive int32 `json:"persistentkeepalive" bson:"persistentkeepalive" yaml:"persistentkeepalive" validate:"omitempty,numeric,max=1000"`
IsHub string `json:"ishub" bson:"ishub" yaml:"ishub" validate:"checkyesorno"`
AccessKey string `json:"accesskey" bson:"accesskey" yaml:"accesskey"`
Interface string `json:"interface" bson:"interface" yaml:"interface"`
LastModified int64 `json:"lastmodified" bson:"lastmodified" yaml:"lastmodified"`
ExpirationDateTime int64 `json:"expdatetime" bson:"expdatetime" yaml:"expdatetime"`
LastPeerUpdate int64 `json:"lastpeerupdate" bson:"lastpeerupdate" yaml:"lastpeerupdate"`
LastCheckIn int64 `json:"lastcheckin" bson:"lastcheckin" yaml:"lastcheckin"`
MacAddress string `json:"macaddress" bson:"macaddress" yaml:"macaddress"`
Password string `json:"password" bson:"password" yaml:"password" validate:"required,min=6"`
Network string `json:"network" bson:"network" yaml:"network" validate:"network_exists"`
IsRelayed string `json:"isrelayed" bson:"isrelayed" yaml:"isrelayed"`
IsPending string `json:"ispending" bson:"ispending" yaml:"ispending"`
IsRelay string `json:"isrelay" bson:"isrelay" yaml:"isrelay" validate:"checkyesorno"`
IsDocker string `json:"isdocker" bson:"isdocker" yaml:"isdocker" validate:"checkyesorno"`
IsK8S string `json:"isk8s" bson:"isk8s" yaml:"isk8s" validate:"checkyesorno"`
IsEgressGateway string `json:"isegressgateway" bson:"isegressgateway" yaml:"isegressgateway" validate:"checkyesorno"`
IsIngressGateway string `json:"isingressgateway" bson:"isingressgateway" yaml:"isingressgateway" validate:"checkyesorno"`
EgressGatewayRanges []string `json:"egressgatewayranges" bson:"egressgatewayranges" yaml:"egressgatewayranges"`
ID string `json:"id,omitempty" bson:"id,omitempty" yaml:"id,omitempty" validate:"required,min=5,id_unique"`
Address string `json:"address" bson:"address" yaml:"address" validate:"omitempty,ipv4"`
Address6 string `json:"address6" bson:"address6" yaml:"address6" validate:"omitempty,ipv6"`
LocalAddress string `json:"localaddress" bson:"localaddress" yaml:"localaddress" validate:"omitempty"`
Interfaces []Iface `json:"interfaces" yaml:"interfaces"`
Name string `json:"name" bson:"name" yaml:"name" validate:"omitempty,max=62,in_charset"`
NetworkSettings Network `json:"networksettings" bson:"networksettings" yaml:"networksettings" validate:"-"`
ListenPort int32 `json:"listenport" bson:"listenport" yaml:"listenport" validate:"omitempty,numeric,min=1024,max=65535"`
LocalListenPort int32 `json:"locallistenport" bson:"locallistenport" yaml:"locallistenport" validate:"numeric,min=0,max=65535"`
PublicKey string `json:"publickey" bson:"publickey" yaml:"publickey" validate:"required,base64"`
Endpoint string `json:"endpoint" bson:"endpoint" yaml:"endpoint" validate:"required,ip"`
AllowedIPs []string `json:"allowedips" bson:"allowedips" yaml:"allowedips"`
PersistentKeepalive int32 `json:"persistentkeepalive" bson:"persistentkeepalive" yaml:"persistentkeepalive" validate:"omitempty,numeric,max=1000"`
IsHub string `json:"ishub" bson:"ishub" yaml:"ishub" validate:"checkyesorno"`
AccessKey string `json:"accesskey" bson:"accesskey" yaml:"accesskey"`
Interface string `json:"interface" bson:"interface" yaml:"interface"`
LastModified int64 `json:"lastmodified" bson:"lastmodified" yaml:"lastmodified"`
ExpirationDateTime int64 `json:"expdatetime" bson:"expdatetime" yaml:"expdatetime"`
LastPeerUpdate int64 `json:"lastpeerupdate" bson:"lastpeerupdate" yaml:"lastpeerupdate"`
LastCheckIn int64 `json:"lastcheckin" bson:"lastcheckin" yaml:"lastcheckin"`
MacAddress string `json:"macaddress" bson:"macaddress" yaml:"macaddress"`
Password string `json:"password" bson:"password" yaml:"password" validate:"required,min=6"`
Network string `json:"network" bson:"network" yaml:"network" validate:"network_exists"`
IsRelayed string `json:"isrelayed" bson:"isrelayed" yaml:"isrelayed"`
IsPending string `json:"ispending" bson:"ispending" yaml:"ispending"`
IsRelay string `json:"isrelay" bson:"isrelay" yaml:"isrelay" validate:"checkyesorno"`
IsDocker string `json:"isdocker" bson:"isdocker" yaml:"isdocker" validate:"checkyesorno"`
IsK8S string `json:"isk8s" bson:"isk8s" yaml:"isk8s" validate:"checkyesorno"`
IsEgressGateway string `json:"isegressgateway" bson:"isegressgateway" yaml:"isegressgateway" validate:"checkyesorno"`
IsIngressGateway string `json:"isingressgateway" bson:"isingressgateway" yaml:"isingressgateway" validate:"checkyesorno"`
EgressGatewayRanges []string `json:"egressgatewayranges" bson:"egressgatewayranges" yaml:"egressgatewayranges"`
EgressGatewayNatEnabled string `json:"egressgatewaynatenabled" bson:"egressgatewaynatenabled" yaml:"egressgatewaynatenabled"`
EgressGatewayRequest EgressGatewayRequest `json:"egressgatewayrequest" bson:"egressgatewayrequest" yaml:"egressgatewayrequest"`
RelayAddrs []string `json:"relayaddrs" bson:"relayaddrs" yaml:"relayaddrs"`
FailoverNode string `json:"failovernode" bson:"failovernode" yaml:"failovernode"`
IngressGatewayRange string `json:"ingressgatewayrange" bson:"ingressgatewayrange" yaml:"ingressgatewayrange"`
IngressGatewayRange6 string `json:"ingressgatewayrange6" bson:"ingressgatewayrange6" yaml:"ingressgatewayrange6"`
EgressGatewayRequest EgressGatewayRequest `json:"egressgatewayrequest" bson:"egressgatewayrequest" yaml:"egressgatewayrequest"`
RelayAddrs []string `json:"relayaddrs" bson:"relayaddrs" yaml:"relayaddrs"`
FailoverNode string `json:"failovernode" bson:"failovernode" yaml:"failovernode"`
IngressGatewayRange string `json:"ingressgatewayrange" bson:"ingressgatewayrange" yaml:"ingressgatewayrange"`
IngressGatewayRange6 string `json:"ingressgatewayrange6" bson:"ingressgatewayrange6" yaml:"ingressgatewayrange6"`
// IsStatic - refers to if the Endpoint is set manually or dynamically
IsStatic string `json:"isstatic" bson:"isstatic" yaml:"isstatic" validate:"checkyesorno"`
UDPHolePunch string `json:"udpholepunch" bson:"udpholepunch" yaml:"udpholepunch" validate:"checkyesorno"`
DNSOn string `json:"dnson" bson:"dnson" yaml:"dnson" validate:"checkyesorno"`
IsServer string `json:"isserver" bson:"isserver" yaml:"isserver" validate:"checkyesorno"`
Action string `json:"action" bson:"action" yaml:"action"`
IPForwarding string `json:"ipforwarding" bson:"ipforwarding" yaml:"ipforwarding" validate:"checkyesorno"`
OS string `json:"os" bson:"os" yaml:"os"`
MTU int32 `json:"mtu" bson:"mtu" yaml:"mtu"`
Version string `json:"version" bson:"version" yaml:"version"`
Server string `json:"server" bson:"server" yaml:"server"`
TrafficKeys TrafficKeys `json:"traffickeys" bson:"traffickeys" yaml:"traffickeys"`
FirewallInUse string `json:"firewallinuse" bson:"firewallinuse" yaml:"firewallinuse"`
InternetGateway string `json:"internetgateway" bson:"internetgateway" yaml:"internetgateway"`
Connected string `json:"connected" bson:"connected" yaml:"connected" validate:"checkyesorno"`
IsStatic string `json:"isstatic" bson:"isstatic" yaml:"isstatic" validate:"checkyesorno"`
UDPHolePunch string `json:"udpholepunch" bson:"udpholepunch" yaml:"udpholepunch" validate:"checkyesorno"`
DNSOn string `json:"dnson" bson:"dnson" yaml:"dnson" validate:"checkyesorno"`
IsServer string `json:"isserver" bson:"isserver" yaml:"isserver" validate:"checkyesorno"`
Action string `json:"action" bson:"action" yaml:"action"`
IPForwarding string `json:"ipforwarding" bson:"ipforwarding" yaml:"ipforwarding" validate:"checkyesorno"`
OS string `json:"os" bson:"os" yaml:"os"`
MTU int32 `json:"mtu" bson:"mtu" yaml:"mtu"`
Version string `json:"version" bson:"version" yaml:"version"`
Server string `json:"server" bson:"server" yaml:"server"`
TrafficKeys TrafficKeys `json:"traffickeys" bson:"traffickeys" yaml:"traffickeys"`
FirewallInUse string `json:"firewallinuse" bson:"firewallinuse" yaml:"firewallinuse"`
InternetGateway string `json:"internetgateway" bson:"internetgateway" yaml:"internetgateway"`
Connected string `json:"connected" bson:"connected" yaml:"connected" validate:"checkyesorno"`
// == PRO ==
DefaultACL string `json:"defaultacl,omitempty" bson:"defaultacl,omitempty" yaml:"defaultacl,omitempty" validate:"checkyesornoorunset"`
OwnerID string `json:"ownerid,omitempty" bson:"ownerid,omitempty" yaml:"ownerid,omitempty"`
Failover string `json:"failover" bson:"failover" yaml:"failover" validate:"checkyesorno"`
DefaultACL string `json:"defaultacl,omitempty" bson:"defaultacl,omitempty" yaml:"defaultacl,omitempty" validate:"checkyesornoorunset"`
OwnerID string `json:"ownerid,omitempty" bson:"ownerid,omitempty" yaml:"ownerid,omitempty"`
Failover string `json:"failover" bson:"failover" yaml:"failover" validate:"checkyesorno"`
}
// NodesArray - used for node sorting
@ -375,7 +375,10 @@ func (node *LegacyNode) SetDefaultFailover() {
}
// Node.Fill - fills other node data into calling node data if not set on calling node (skips DNSOn)
func (newNode *Node) Fill(currentNode *Node, isPro bool) { // TODO add new field for nftables present
func (newNode *Node) Fill(
currentNode *Node,
isPro bool,
) { // TODO add new field for nftables present
newNode.ID = currentNode.ID
newNode.HostID = currentNode.HostID
// Revisit the logic for boolean values

View file

@ -19,24 +19,25 @@ import (
// FailOverHandlers - handlers for FailOver
func FailOverHandlers(r *mux.Router) {
r.HandleFunc("/api/v1/node/{nodeid}/failover", http.HandlerFunc(getfailOver)).Methods(http.MethodGet)
r.HandleFunc("/api/v1/node/{nodeid}/failover", logic.SecurityCheck(true, http.HandlerFunc(createfailOver))).Methods(http.MethodPost)
r.HandleFunc("/api/v1/node/{nodeid}/failover", logic.SecurityCheck(true, http.HandlerFunc(deletefailOver))).Methods(http.MethodDelete)
r.HandleFunc("/api/v1/node/{network}/failover/reset", logic.SecurityCheck(true, http.HandlerFunc(resetFailOver))).Methods(http.MethodPost)
r.HandleFunc("/api/v1/node/{nodeid}/failover_me", controller.Authorize(true, false, "host", http.HandlerFunc(failOverME))).Methods(http.MethodPost)
r.HandleFunc("/api/v1/node/{nodeid}/failover", http.HandlerFunc(getfailOver)).
Methods(http.MethodGet)
r.HandleFunc("/api/v1/node/{nodeid}/failover", logic.SecurityCheck(true, http.HandlerFunc(createfailOver))).
Methods(http.MethodPost)
r.HandleFunc("/api/v1/node/{nodeid}/failover", logic.SecurityCheck(true, http.HandlerFunc(deletefailOver))).
Methods(http.MethodDelete)
r.HandleFunc("/api/v1/node/{network}/failover/reset", logic.SecurityCheck(true, http.HandlerFunc(resetFailOver))).
Methods(http.MethodPost)
r.HandleFunc("/api/v1/node/{nodeid}/failover_me", controller.Authorize(true, false, "host", http.HandlerFunc(failOverME))).
Methods(http.MethodPost)
}
// swagger:route GET /api/v1/node/failover node getfailOver
//
// get failover node.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
// @Summary Get failover node
// @Router /api/v1/node/{nodeid}/failover [get]
// @Tags PRO
// @Param nodeid path string true "Node ID"
// @Success 200 {object} models.Node
// @Failure 400 {object} models.ErrorResponse
// @Failure 404 {object} models.ErrorResponse
func getfailOver(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
nodeid := params["nodeid"]
@ -50,24 +51,24 @@ func getfailOver(w http.ResponseWriter, r *http.Request) {
failOverNode, exists := proLogic.FailOverExists(node.Network)
if !exists {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failover node not found"), "notfound"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("failover node not found"), "notfound"),
)
return
}
w.Header().Set("Content-Type", "application/json")
logic.ReturnSuccessResponseWithJson(w, r, failOverNode, "get failover node successfully")
}
// swagger:route POST /api/v1/node/failover node createfailOver
//
// Create a relay.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
// @Summary Create failover node
// @Router /api/v1/node/{nodeid}/failover [post]
// @Tags PRO
// @Param nodeid path string true "Node ID"
// @Success 200 {object} models.Node
// @Failure 400 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func createfailOver(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
nodeid := params["nodeid"]
@ -88,6 +89,12 @@ func createfailOver(w http.ResponseWriter, r *http.Request) {
logic.ReturnSuccessResponseWithJson(w, r, node, "created failover successfully")
}
// @Summary Reset failover for a network
// @Router /api/v1/node/{network}/failover/reset [post]
// @Tags PRO
// @Param network path string true "Network ID"
// @Success 200 {object} models.SuccessResponse
// @Failure 500 {object} models.ErrorResponse
func resetFailOver(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
net := params["network"]
@ -108,17 +115,13 @@ func resetFailOver(w http.ResponseWriter, r *http.Request) {
logic.ReturnSuccessResponse(w, r, "failover has been reset successfully")
}
// swagger:route DELETE /api/v1/node/failover node deletefailOver
//
// Create a relay.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
// @Summary Delete failover node
// @Router /api/v1/node/{nodeid}/failover [delete]
// @Tags PRO
// @Param nodeid path string true "Node ID"
// @Success 200 {object} models.Node
// @Failure 400 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func deletefailOver(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
nodeid := params["nodeid"]
@ -145,17 +148,15 @@ func deletefailOver(w http.ResponseWriter, r *http.Request) {
logic.ReturnSuccessResponseWithJson(w, r, node, "deleted failover successfully")
}
// swagger:route POST /api/node/{nodeid}/failOverME node failOver_me
//
// Create a relay.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
// @Summary Failover me
// @Router /api/v1/node/{nodeid}/failover_me [post]
// @Tags PRO
// @Param nodeid path string true "Node ID"
// @Accept json
// @Param body body models.FailOverMeReq true "Failover request"
// @Success 200 {object} models.SuccessResponse
// @Failure 400 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func failOverME(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
nodeid := params["nodeid"]
@ -174,7 +175,14 @@ func failOverME(w http.ResponseWriter, r *http.Request) {
failOverNode, exists := proLogic.FailOverExists(node.Network)
if !exists {
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("req-from: %s, failover node doesn't exist in the network", host.Name), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
fmt.Errorf("req-from: %s, failover node doesn't exist in the network", host.Name),
"badrequest",
),
)
return
}
var failOverReq models.FailOverMeReq
@ -188,27 +196,57 @@ func failOverME(w http.ResponseWriter, r *http.Request) {
peerNode, err := logic.GetNodeByID(failOverReq.NodeID)
if err != nil {
slog.Error("peer not found: ", "nodeid", failOverReq.NodeID, "error", err)
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("peer not found"), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("peer not found"), "badrequest"),
)
return
}
if node.IsFailOver {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("node is acting as failover"), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("node is acting as failover"), "badrequest"),
)
return
}
if node.IsRelayed && node.RelayedBy == peerNode.ID.String() {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("node is relayed by peer node"), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("node is relayed by peer node"), "badrequest"),
)
return
}
if node.IsRelay && peerNode.RelayedBy == node.ID.String() {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("node acting as relay for the peer node"), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("node acting as relay for the peer node"), "badrequest"),
)
return
}
if node.IsInternetGateway && peerNode.InternetGwID == node.ID.String() {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("node acting as internet gw for the peer node"), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
errors.New("node acting as internet gw for the peer node"),
"badrequest",
),
)
return
}
if node.InternetGwID != "" && node.InternetGwID == peerNode.ID.String() {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("node using a internet gw by the peer node"), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
errors.New("node using a internet gw by the peer node"),
"badrequest",
),
)
return
}
@ -216,10 +254,20 @@ func failOverME(w http.ResponseWriter, r *http.Request) {
if err != nil {
slog.Error("failed to create failover", "id", node.ID.String(),
"network", node.Network, "error", err)
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to create failover: %v", err), "internal"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(fmt.Errorf("failed to create failover: %v", err), "internal"),
)
return
}
slog.Info("[auto-relay] created relay on node", "node", node.ID.String(), "network", node.Network)
slog.Info(
"[auto-relay] created relay on node",
"node",
node.ID.String(),
"network",
node.Network,
)
sendPeerUpdate = true
if sendPeerUpdate {

View file

@ -16,22 +16,24 @@ import (
// InetHandlers - handlers for internet gw
func InetHandlers(r *mux.Router) {
r.HandleFunc("/api/nodes/{network}/{nodeid}/inet_gw", logic.SecurityCheck(true, http.HandlerFunc(createInternetGw))).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/inet_gw", logic.SecurityCheck(true, http.HandlerFunc(updateInternetGw))).Methods(http.MethodPut)
r.HandleFunc("/api/nodes/{network}/{nodeid}/inet_gw", logic.SecurityCheck(true, http.HandlerFunc(deleteInternetGw))).Methods(http.MethodDelete)
r.HandleFunc("/api/nodes/{network}/{nodeid}/inet_gw", logic.SecurityCheck(true, http.HandlerFunc(createInternetGw))).
Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/inet_gw", logic.SecurityCheck(true, http.HandlerFunc(updateInternetGw))).
Methods(http.MethodPut)
r.HandleFunc("/api/nodes/{network}/{nodeid}/inet_gw", logic.SecurityCheck(true, http.HandlerFunc(deleteInternetGw))).
Methods(http.MethodDelete)
}
// swagger:route POST /api/nodes/{network}/{nodeid}/inet_gw nodes createInternetGw
//
// Create an inet node.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
// @Summary Create an internet gateway
// @Router /api/nodes/{network}/{nodeid}/inet_gw [post]
// @Tags PRO
// @Accept json
// @Param network path string true "Network ID"
// @Param nodeid path string true "Node ID"
// @Param body body models.InetNodeReq true "Internet gateway request"
// @Success 200 {object} models.Node
// @Failure 400 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func createInternetGw(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
w.Header().Set("Content-Type", "application/json")
@ -58,7 +60,14 @@ func createInternetGw(w http.ResponseWriter, r *http.Request) {
return
}
if host.OS != models.OS_Types.Linux {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("only linux nodes can be made internet gws"), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
errors.New("only linux nodes can be made internet gws"),
"badrequest",
),
)
return
}
err = proLogic.ValidateInetGwReq(node, request, false)
@ -81,23 +90,29 @@ func createInternetGw(w http.ResponseWriter, r *http.Request) {
return
}
apiNode := node.ConvertToAPINode()
logger.Log(1, r.Header.Get("user"), "created ingress gateway on node", nodeid, "on network", netid)
logger.Log(
1,
r.Header.Get("user"),
"created ingress gateway on node",
nodeid,
"on network",
netid,
)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiNode)
go mq.PublishPeerUpdate(false)
}
// swagger:route PUT /api/nodes/{network}/{nodeid}/inet_gw nodes updateInternetGw
//
// update an inet node.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
// @Summary Update an internet gateway
// @Router /api/nodes/{network}/{nodeid}/inet_gw [put]
// @Tags PRO
// @Accept json
// @Param network path string true "Network ID"
// @Param nodeid path string true "Node ID"
// @Param body body models.InetNodeReq true "Internet gateway request"
// @Success 200 {object} models.Node
// @Failure 400 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func updateInternetGw(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
w.Header().Set("Content-Type", "application/json")
@ -115,7 +130,11 @@ func updateInternetGw(w http.ResponseWriter, r *http.Request) {
return
}
if !node.IsInternetGateway {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("node is not a internet gw"), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("node is not a internet gw"), "badrequest"),
)
return
}
err = proLogic.ValidateInetGwReq(node, request, true)
@ -131,23 +150,27 @@ func updateInternetGw(w http.ResponseWriter, r *http.Request) {
return
}
apiNode := node.ConvertToAPINode()
logger.Log(1, r.Header.Get("user"), "created ingress gateway on node", nodeid, "on network", netid)
logger.Log(
1,
r.Header.Get("user"),
"created ingress gateway on node",
nodeid,
"on network",
netid,
)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiNode)
go mq.PublishPeerUpdate(false)
}
// swagger:route DELETE /api/nodes/{network}/{nodeid}/inet_gw nodes deleteInternetGw
//
// Delete an internet gw.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
// @Summary Delete an internet gateway
// @Router /api/nodes/{network}/{nodeid}/inet_gw [delete]
// @Tags PRO
// @Param network path string true "Network ID"
// @Param nodeid path string true "Node ID"
// @Success 200 {object} models.Node
// @Failure 400 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func deleteInternetGw(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
w.Header().Set("Content-Type", "application/json")
@ -166,7 +189,14 @@ func deleteInternetGw(w http.ResponseWriter, r *http.Request) {
return
}
apiNode := node.ConvertToAPINode()
logger.Log(1, r.Header.Get("user"), "created ingress gateway on node", nodeid, "on network", netid)
logger.Log(
1,
r.Header.Get("user"),
"created ingress gateway on node",
nodeid,
"on network",
netid,
)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiNode)
go mq.PublishPeerUpdate(false)

View file

@ -19,22 +19,25 @@ import (
// RelayHandlers - handle Pro Relays
func RelayHandlers(r *mux.Router) {
r.HandleFunc("/api/nodes/{network}/{nodeid}/createrelay", controller.Authorize(false, true, "user", http.HandlerFunc(createRelay))).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/deleterelay", controller.Authorize(false, true, "user", http.HandlerFunc(deleteRelay))).Methods(http.MethodDelete)
r.HandleFunc("/api/v1/host/{hostid}/failoverme", controller.Authorize(true, false, "host", http.HandlerFunc(failOverME))).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/createrelay", controller.Authorize(false, true, "user", http.HandlerFunc(createRelay))).
Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/deleterelay", controller.Authorize(false, true, "user", http.HandlerFunc(deleteRelay))).
Methods(http.MethodDelete)
r.HandleFunc("/api/v1/host/{hostid}/failoverme", controller.Authorize(true, false, "host", http.HandlerFunc(failOverME))).
Methods(http.MethodPost)
}
// swagger:route POST /api/nodes/{network}/{nodeid}/createrelay nodes createRelay
//
// Create a relay.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
// @Summary Create a relay
// @Router /api/nodes/{network}/{nodeid}/createrelay [post]
// @Tags PRO
// @Accept json
// @Produce json
// @Param network path string true "Network ID"
// @Param nodeid path string true "Node ID"
// @Param body body models.RelayRequest true "Relay request parameters"
// @Success 200 {object} models.ApiNode
// @Failure 400 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func createRelay(w http.ResponseWriter, r *http.Request) {
var relayRequest models.RelayRequest
var params = mux.Vars(r)
@ -49,8 +52,16 @@ func createRelay(w http.ResponseWriter, r *http.Request) {
relayRequest.NodeID = params["nodeid"]
_, relayNode, err := proLogic.CreateRelay(relayRequest)
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to create relay on node [%s] on network [%s]: %v", relayRequest.NodeID, relayRequest.NetID, err))
logger.Log(
0,
r.Header.Get("user"),
fmt.Sprintf(
"failed to create relay on node [%s] on network [%s]: %v",
relayRequest.NodeID,
relayRequest.NetID,
err,
),
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
@ -64,23 +75,29 @@ func createRelay(w http.ResponseWriter, r *http.Request) {
}
}
go mq.PublishPeerUpdate(false)
logger.Log(1, r.Header.Get("user"), "created relay on node", relayRequest.NodeID, "on network", relayRequest.NetID)
logger.Log(
1,
r.Header.Get("user"),
"created relay on node",
relayRequest.NodeID,
"on network",
relayRequest.NetID,
)
apiNode := relayNode.ConvertToAPINode()
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiNode)
}
// swagger:route DELETE /api/nodes/{network}/{nodeid}/deleterelay nodes deleteRelay
//
// Remove a relay.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
// @Summary Remove a relay
// @Router /api/nodes/{network}/{nodeid}/deleterelay [delete]
// @Tags PRO
// @Accept json
// @Produce json
// @Param network path string true "Network ID"
// @Param nodeid path string true "Node ID"
// @Success 200 {object} models.ApiNode
// @Failure 400 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func deleteRelay(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
@ -97,7 +114,15 @@ func deleteRelay(w http.ResponseWriter, r *http.Request) {
for _, relayedNode := range updateNodes {
err = mq.NodeUpdate(&relayedNode)
if err != nil {
logger.Log(1, "relayed node update ", relayedNode.ID.String(), "on network", relayedNode.Network, ": ", err.Error())
logger.Log(
1,
"relayed node update ",
relayedNode.ID.String(),
"on network",
relayedNode.Network,
": ",
err.Error(),
)
}
h, err := logic.GetHost(relayedNode.HostID.String())
@ -109,14 +134,27 @@ func deleteRelay(w http.ResponseWriter, r *http.Request) {
}
node.IsRelay = true // for iot update to recognise that it has to delete relay peer
if err = mq.PublishSingleHostPeerUpdate(h, nodes, &node, nil, false); err != nil {
logger.Log(1, "failed to publish peer update to host", h.ID.String(), ": ", err.Error())
logger.Log(
1,
"failed to publish peer update to host",
h.ID.String(),
": ",
err.Error(),
)
}
}
}
}
mq.PublishPeerUpdate(false)
}()
logger.Log(1, r.Header.Get("user"), "deleted relay on node", node.ID.String(), "on network", node.Network)
logger.Log(
1,
r.Header.Get("user"),
"deleted relay on node",
node.ID.String(),
"on network",
node.Network,
)
apiNode := node.ConvertToAPINode()
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiNode)

View file

@ -17,27 +17,30 @@ import (
)
func UserHandlers(r *mux.Router) {
r.HandleFunc("/api/users/{username}/remote_access_gw/{remote_access_gateway_id}", logic.SecurityCheck(true, http.HandlerFunc(attachUserToRemoteAccessGw))).Methods(http.MethodPost)
r.HandleFunc("/api/users/{username}/remote_access_gw/{remote_access_gateway_id}", logic.SecurityCheck(true, http.HandlerFunc(removeUserFromRemoteAccessGW))).Methods(http.MethodDelete)
r.HandleFunc("/api/users/{username}/remote_access_gw", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUserRemoteAccessGws)))).Methods(http.MethodGet)
r.HandleFunc("/api/users/ingress/{ingress_id}", logic.SecurityCheck(true, http.HandlerFunc(ingressGatewayUsers))).Methods(http.MethodGet)
r.HandleFunc("/api/users/{username}/remote_access_gw/{remote_access_gateway_id}", logic.SecurityCheck(true, http.HandlerFunc(attachUserToRemoteAccessGw))).
Methods(http.MethodPost)
r.HandleFunc("/api/users/{username}/remote_access_gw/{remote_access_gateway_id}", logic.SecurityCheck(true, http.HandlerFunc(removeUserFromRemoteAccessGW))).
Methods(http.MethodDelete)
r.HandleFunc("/api/users/{username}/remote_access_gw", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUserRemoteAccessGws)))).
Methods(http.MethodGet)
r.HandleFunc("/api/users/ingress/{ingress_id}", logic.SecurityCheck(true, http.HandlerFunc(ingressGatewayUsers))).
Methods(http.MethodGet)
r.HandleFunc("/api/oauth/login", auth.HandleAuthLogin).Methods(http.MethodGet)
r.HandleFunc("/api/oauth/callback", auth.HandleAuthCallback).Methods(http.MethodGet)
r.HandleFunc("/api/oauth/headless", auth.HandleHeadlessSSO)
r.HandleFunc("/api/oauth/register/{regKey}", auth.RegisterHostSSO).Methods(http.MethodGet)
}
// swagger:route POST /api/users/{username}/remote_access_gw user attachUserToRemoteAccessGateway
//
// Attach User to a remote access gateway.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: userBodyResponse
// @Summary Attach user to a remote access gateway
// @Router /api/users/{username}/remote_access_gw/{remote_access_gateway_id} [post]
// @Tags PRO
// @Accept json
// @Produce json
// @Param username path string true "Username"
// @Param remote_access_gateway_id path string true "Remote Access Gateway ID"
// @Success 200 {object} models.ReturnUser
// @Failure 400 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func attachUserToRemoteAccessGw(w http.ResponseWriter, r *http.Request) {
// set header.
w.Header().Set("Content-Type", "application/json")
@ -46,27 +49,59 @@ func attachUserToRemoteAccessGw(w http.ResponseWriter, r *http.Request) {
username := params["username"]
remoteGwID := params["remote_access_gateway_id"]
if username == "" || remoteGwID == "" {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("required params `username` and `remote_access_gateway_id`"), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
errors.New("required params `username` and `remote_access_gateway_id`"),
"badrequest",
),
)
return
}
user, err := logic.GetUser(username)
if err != nil {
slog.Error("failed to fetch user: ", "username", username, "error", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch user %s, error: %v", username, err), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
fmt.Errorf("failed to fetch user %s, error: %v", username, err),
"badrequest",
),
)
return
}
if user.IsAdmin || user.IsSuperAdmin {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("superadmins/admins have access to all gateways"), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
errors.New("superadmins/admins have access to all gateways"),
"badrequest",
),
)
return
}
node, err := logic.GetNodeByID(remoteGwID)
if err != nil {
slog.Error("failed to fetch gateway node", "nodeID", remoteGwID, "error", err)
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch remote access gateway node, error: %v", err), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
fmt.Errorf("failed to fetch remote access gateway node, error: %v", err),
"badrequest",
),
)
return
}
if !node.IsIngressGateway {
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("node is not a remote access gateway"), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(fmt.Errorf("node is not a remote access gateway"), "badrequest"),
)
return
}
if user.RemoteGwIDs == nil {
@ -76,24 +111,30 @@ func attachUserToRemoteAccessGw(w http.ResponseWriter, r *http.Request) {
err = logic.UpsertUser(*user)
if err != nil {
slog.Error("failed to update user's gateways", "user", username, "error", err)
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch remote access gateway node,error: %v", err), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
fmt.Errorf("failed to fetch remote access gateway node,error: %v", err),
"badrequest",
),
)
return
}
json.NewEncoder(w).Encode(logic.ToReturnUser(*user))
}
// swagger:route DELETE /api/users/{username}/remote_access_gw user removeUserFromRemoteAccessGW
//
// Delete User from a remote access gateway.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: userBodyResponse
// @Summary Remove user from a remote access gateway
// @Router /api/users/{username}/remote_access_gw/{remote_access_gateway_id} [delete]
// @Tags PRO
// @Accept json
// @Produce json
// @Param username path string true "Username"
// @Param remote_access_gateway_id path string true "Remote Access Gateway ID"
// @Success 200 {object} models.ReturnUser
// @Failure 400 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func removeUserFromRemoteAccessGW(w http.ResponseWriter, r *http.Request) {
// set header.
w.Header().Set("Content-Type", "application/json")
@ -102,13 +143,27 @@ func removeUserFromRemoteAccessGW(w http.ResponseWriter, r *http.Request) {
username := params["username"]
remoteGwID := params["remote_access_gateway_id"]
if username == "" || remoteGwID == "" {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("required params `username` and `remote_access_gateway_id`"), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
errors.New("required params `username` and `remote_access_gateway_id`"),
"badrequest",
),
)
return
}
user, err := logic.GetUser(username)
if err != nil {
logger.Log(0, username, "failed to fetch user: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch user %s, error: %v", username, err), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
fmt.Errorf("failed to fetch user %s, error: %v", username, err),
"badrequest",
),
)
return
}
delete(user.RemoteGwIDs, remoteGwID)
@ -139,23 +194,30 @@ func removeUserFromRemoteAccessGW(w http.ResponseWriter, r *http.Request) {
err = logic.UpsertUser(*user)
if err != nil {
slog.Error("failed to update user gateways", "user", username, "error", err)
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to fetch remote access gaetway node "+err.Error()), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
errors.New("failed to fetch remote access gaetway node "+err.Error()),
"badrequest",
),
)
return
}
json.NewEncoder(w).Encode(logic.ToReturnUser(*user))
}
// swagger:route GET "/api/users/{username}/remote_access_gw" nodes getUserRemoteAccessGws
//
// Get an individual node.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
// @Summary Get user's remote access gateways
// @Router /api/users/{username}/remote_access_gw [get]
// @Tags PRO
// @Accept json
// @Produce json
// @Param username path string true "Username"
// @Param remote_access_clientid query string false "Remote Access Client ID"
// @Param from_mobile query boolean false "Request from mobile"
// @Success 200 {array} models.UserRemoteGws
// @Failure 400 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) {
// set header.
w.Header().Set("Content-Type", "application/json")
@ -163,7 +225,11 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
username := params["username"]
if username == "" {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("required params username"), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("required params username"), "badrequest"),
)
return
}
remoteAccessClientID := r.URL.Query().Get("remote_access_clientid")
@ -178,7 +244,11 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) {
}
reqFromMobile := r.URL.Query().Get("from_mobile") == "true"
if req.RemoteAccessClientID == "" && remoteAccessClientID == "" {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("remote access client id cannot be empty"), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("remote access client id cannot be empty"), "badrequest"),
)
return
}
if req.RemoteAccessClientID == "" {
@ -188,7 +258,14 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) {
user, err := logic.GetUser(username)
if err != nil {
logger.Log(0, username, "failed to fetch user: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch user %s, error: %v", username, err), "badrequest"))
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
fmt.Errorf("failed to fetch user %s, error: %v", username, err),
"badrequest",
),
)
return
}
allextClients, err := logic.GetAllExtClients()
@ -199,7 +276,8 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) {
processedAdminNodeIds := make(map[string]struct{})
for _, extClient := range allextClients {
if extClient.RemoteAccessClientID == req.RemoteAccessClientID && extClient.OwnerID == username {
if extClient.RemoteAccessClientID == req.RemoteAccessClientID &&
extClient.OwnerID == username {
node, err := logic.GetNodeByID(extClient.IngressGatewayID)
if err != nil {
continue
@ -219,7 +297,8 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) {
slog.Error("failed to get node network", "error", err)
}
if _, ok := user.RemoteGwIDs[node.ID.String()]; (!user.IsAdmin && !user.IsSuperAdmin) && ok {
if _, ok := user.RemoteGwIDs[node.ID.String()]; (!user.IsAdmin && !user.IsSuperAdmin) &&
ok {
gws := userGws[node.Network]
extClient.AllowedIPs = logic.GetExtclientAllowedIPs(extClient)
gws = append(gws, models.UserRemoteGws{
@ -345,17 +424,15 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(userGws)
}
// swagger:route GET /api/nodes/{network}/{nodeid}/ingress/users users ingressGatewayUsers
//
// Lists all the users attached to an ingress gateway.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
// @Summary List users attached to an remote access gateway
// @Router /api/nodes/{network}/{nodeid}/ingress/users [get]
// @Tags PRO
// @Accept json
// @Produce json
// @Param ingress_id path string true "Ingress Gateway ID"
// @Success 200 {array} models.IngressGwUsers
// @Failure 400 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func ingressGatewayUsers(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
@ -368,8 +445,17 @@ func ingressGatewayUsers(w http.ResponseWriter, r *http.Request) {
}
gwUsers, err := logic.GetIngressGwUsers(node)
if err != nil {
slog.Error("failed to get users on ingress gateway", "nodeid", ingressID, "network", node.Network, "user", r.Header.Get("user"),
"error", err)
slog.Error(
"failed to get users on ingress gateway",
"nodeid",
ingressID,
"network",
node.Network,
"user",
r.Header.Get("user"),
"error",
err,
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}

3105
swagger.yaml Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff