2022-08-20 19:11:54 +02:00
|
|
|
package http
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2023-02-03 21:47:20 +01:00
|
|
|
"net/http"
|
|
|
|
"unicode/utf8"
|
|
|
|
|
2022-08-20 19:11:54 +02:00
|
|
|
"github.com/gorilla/mux"
|
2023-02-28 15:01:24 +01:00
|
|
|
|
2022-08-20 19:11:54 +02:00
|
|
|
"github.com/netbirdio/netbird/management/server"
|
|
|
|
"github.com/netbirdio/netbird/management/server/http/api"
|
2022-11-11 20:36:45 +01:00
|
|
|
"github.com/netbirdio/netbird/management/server/http/util"
|
2022-08-20 19:11:54 +02:00
|
|
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
2022-11-11 20:36:45 +01:00
|
|
|
"github.com/netbirdio/netbird/management/server/status"
|
2022-08-20 19:11:54 +02:00
|
|
|
"github.com/netbirdio/netbird/route"
|
|
|
|
)
|
|
|
|
|
2023-02-28 15:46:08 +01:00
|
|
|
// RoutesHandler is the routes handler of the account
|
|
|
|
type RoutesHandler struct {
|
2023-02-03 21:47:20 +01:00
|
|
|
accountManager server.AccountManager
|
|
|
|
claimsExtractor *jwtclaims.ClaimsExtractor
|
2022-08-20 19:11:54 +02:00
|
|
|
}
|
|
|
|
|
2023-02-28 15:46:08 +01:00
|
|
|
// NewRoutesHandler returns a new instance of RoutesHandler handler
|
|
|
|
func NewRoutesHandler(accountManager server.AccountManager, authCfg AuthCfg) *RoutesHandler {
|
|
|
|
return &RoutesHandler{
|
2022-08-20 19:11:54 +02:00
|
|
|
accountManager: accountManager,
|
2023-02-03 21:47:20 +01:00
|
|
|
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
|
|
|
jwtclaims.WithAudience(authCfg.Audience),
|
|
|
|
jwtclaims.WithUserIDClaim(authCfg.UserIDClaim),
|
|
|
|
),
|
2022-08-20 19:11:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-28 15:01:24 +01:00
|
|
|
// GetAllRoutes returns the list of routes for the account
|
2023-02-28 15:46:08 +01:00
|
|
|
func (h *RoutesHandler) GetAllRoutes(w http.ResponseWriter, r *http.Request) {
|
2023-02-03 21:47:20 +01:00
|
|
|
claims := h.claimsExtractor.FromRequestContext(r)
|
2022-11-11 20:36:45 +01:00
|
|
|
account, user, err := h.accountManager.GetAccountFromToken(claims)
|
2022-08-20 19:11:54 +02:00
|
|
|
if err != nil {
|
2022-11-11 20:36:45 +01:00
|
|
|
util.WriteError(err, w)
|
2022-08-20 19:11:54 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-11-05 10:24:50 +01:00
|
|
|
routes, err := h.accountManager.ListRoutes(account.Id, user.Id)
|
2022-08-20 19:11:54 +02:00
|
|
|
if err != nil {
|
2022-11-11 20:36:45 +01:00
|
|
|
util.WriteError(err, w)
|
2022-08-20 19:11:54 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
apiRoutes := make([]*api.Route, 0)
|
|
|
|
for _, r := range routes {
|
2023-02-03 10:33:28 +01:00
|
|
|
apiRoutes = append(apiRoutes, toRouteResponse(r))
|
2022-08-20 19:11:54 +02:00
|
|
|
}
|
|
|
|
|
2022-11-11 20:36:45 +01:00
|
|
|
util.WriteJSONObject(w, apiRoutes)
|
2022-08-20 19:11:54 +02:00
|
|
|
}
|
|
|
|
|
2023-02-28 15:01:24 +01:00
|
|
|
// CreateRoute handles route creation request
|
2023-02-28 15:46:08 +01:00
|
|
|
func (h *RoutesHandler) CreateRoute(w http.ResponseWriter, r *http.Request) {
|
2023-02-03 21:47:20 +01:00
|
|
|
claims := h.claimsExtractor.FromRequestContext(r)
|
2023-01-25 16:29:59 +01:00
|
|
|
account, user, err := h.accountManager.GetAccountFromToken(claims)
|
2022-08-20 19:11:54 +02:00
|
|
|
if err != nil {
|
2022-11-11 20:36:45 +01:00
|
|
|
util.WriteError(err, w)
|
2022-08-20 19:11:54 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var req api.PostApiRoutesJSONRequestBody
|
2022-11-11 20:36:45 +01:00
|
|
|
err = json.NewDecoder(r.Body).Decode(&req)
|
|
|
|
if err != nil {
|
|
|
|
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
2022-08-20 19:11:54 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-08-22 14:10:24 +02:00
|
|
|
_, newPrefix, err := route.ParseNetwork(req.Network)
|
2022-08-20 19:11:54 +02:00
|
|
|
if err != nil {
|
2022-11-11 20:36:45 +01:00
|
|
|
util.WriteError(err, w)
|
2022-08-20 19:11:54 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-08-22 14:10:24 +02:00
|
|
|
if utf8.RuneCountInString(req.NetworkId) > route.MaxNetIDChar || req.NetworkId == "" {
|
2022-11-11 20:36:45 +01:00
|
|
|
util.WriteError(status.Errorf(status.InvalidArgument, "identifier should be between 1 and %d",
|
|
|
|
route.MaxNetIDChar), w)
|
2022-08-22 14:10:24 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-09-28 14:32:36 +02:00
|
|
|
peerId := ""
|
|
|
|
if req.Peer != nil {
|
|
|
|
peerId = *req.Peer
|
|
|
|
}
|
|
|
|
|
|
|
|
peerGroupIds := []string{}
|
|
|
|
if req.PeerGroups != nil {
|
|
|
|
peerGroupIds = *req.PeerGroups
|
|
|
|
}
|
|
|
|
|
|
|
|
if (peerId != "" && len(peerGroupIds) > 0) || (peerId == "" && len(peerGroupIds) == 0) {
|
|
|
|
util.WriteError(status.Errorf(status.InvalidArgument, "only one peer or peer_groups should be provided"), w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// do not allow non Linux peers
|
|
|
|
if peer := account.GetPeer(peerId); peer != nil {
|
|
|
|
if peer.Meta.GoOS != "linux" {
|
|
|
|
util.WriteError(status.Errorf(status.InvalidArgument, "non-linux peers are non supported as network routes"), w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
newRoute, err := h.accountManager.CreateRoute(
|
|
|
|
account.Id, newPrefix.String(), peerId, peerGroupIds,
|
2024-05-06 14:47:49 +02:00
|
|
|
req.Description, route.NetID(req.NetworkId), req.Masquerade, req.Metric, req.Groups, req.Enabled, user.Id,
|
2023-09-28 14:32:36 +02:00
|
|
|
)
|
2022-08-20 19:11:54 +02:00
|
|
|
if err != nil {
|
2022-11-11 20:36:45 +01:00
|
|
|
util.WriteError(err, w)
|
2022-08-20 19:11:54 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-02-03 10:33:28 +01:00
|
|
|
resp := toRouteResponse(newRoute)
|
2022-08-20 19:11:54 +02:00
|
|
|
|
2022-11-11 20:36:45 +01:00
|
|
|
util.WriteJSONObject(w, &resp)
|
2022-08-20 19:11:54 +02:00
|
|
|
}
|
|
|
|
|
2023-02-28 15:01:24 +01:00
|
|
|
// UpdateRoute handles update to a route identified by a given ID
|
2023-02-28 15:46:08 +01:00
|
|
|
func (h *RoutesHandler) UpdateRoute(w http.ResponseWriter, r *http.Request) {
|
2023-02-03 21:47:20 +01:00
|
|
|
claims := h.claimsExtractor.FromRequestContext(r)
|
2022-11-11 20:36:45 +01:00
|
|
|
account, user, err := h.accountManager.GetAccountFromToken(claims)
|
2022-08-20 19:11:54 +02:00
|
|
|
if err != nil {
|
2022-11-11 20:36:45 +01:00
|
|
|
util.WriteError(err, w)
|
2022-08-20 19:11:54 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
vars := mux.Vars(r)
|
2023-05-03 00:15:25 +02:00
|
|
|
routeID := vars["routeId"]
|
2022-08-20 19:11:54 +02:00
|
|
|
if len(routeID) == 0 {
|
2022-11-11 20:36:45 +01:00
|
|
|
util.WriteError(status.Errorf(status.InvalidArgument, "invalid route ID"), w)
|
2022-08-20 19:11:54 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-05-06 14:47:49 +02:00
|
|
|
_, err = h.accountManager.GetRoute(account.Id, route.ID(routeID), user.Id)
|
2022-08-20 19:11:54 +02:00
|
|
|
if err != nil {
|
2022-11-11 20:36:45 +01:00
|
|
|
util.WriteError(err, w)
|
2022-08-20 19:11:54 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-05-03 00:15:25 +02:00
|
|
|
var req api.PutApiRoutesRouteIdJSONRequestBody
|
2022-11-11 20:36:45 +01:00
|
|
|
err = json.NewDecoder(r.Body).Decode(&req)
|
|
|
|
if err != nil {
|
|
|
|
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
2022-08-20 19:11:54 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-08-22 14:10:24 +02:00
|
|
|
prefixType, newPrefix, err := route.ParseNetwork(req.Network)
|
2022-08-20 19:11:54 +02:00
|
|
|
if err != nil {
|
2022-11-11 20:36:45 +01:00
|
|
|
util.WriteError(status.Errorf(status.InvalidArgument, "couldn't parse update prefix %s for route ID %s",
|
|
|
|
req.Network, routeID), w)
|
2022-08-20 19:11:54 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-08-22 14:10:24 +02:00
|
|
|
if utf8.RuneCountInString(req.NetworkId) > route.MaxNetIDChar || req.NetworkId == "" {
|
2022-11-11 20:36:45 +01:00
|
|
|
util.WriteError(status.Errorf(status.InvalidArgument,
|
|
|
|
"identifier should be between 1 and %d", route.MaxNetIDChar), w)
|
2022-08-22 14:10:24 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-09-28 14:32:36 +02:00
|
|
|
if req.Peer != nil && req.PeerGroups != nil {
|
|
|
|
util.WriteError(status.Errorf(status.InvalidArgument, "only peer or peers_group should be provided"), w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if req.Peer == nil && req.PeerGroups == nil {
|
|
|
|
util.WriteError(status.Errorf(status.InvalidArgument, "either peer or peers_group should be provided"), w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
peerID := ""
|
|
|
|
if req.Peer != nil {
|
|
|
|
peerID = *req.Peer
|
|
|
|
}
|
|
|
|
|
|
|
|
// do not allow non Linux peers
|
|
|
|
if peer := account.GetPeer(peerID); peer != nil {
|
|
|
|
if peer.Meta.GoOS != "linux" {
|
|
|
|
util.WriteError(status.Errorf(status.InvalidArgument, "non-linux peers are non supported as network routes"), w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-20 19:11:54 +02:00
|
|
|
newRoute := &route.Route{
|
2024-05-06 14:47:49 +02:00
|
|
|
ID: route.ID(routeID),
|
2022-08-22 14:10:24 +02:00
|
|
|
Network: newPrefix,
|
2024-05-06 14:47:49 +02:00
|
|
|
NetID: route.NetID(req.NetworkId),
|
2022-08-22 14:10:24 +02:00
|
|
|
NetworkType: prefixType,
|
2022-08-20 19:11:54 +02:00
|
|
|
Masquerade: req.Masquerade,
|
|
|
|
Metric: req.Metric,
|
|
|
|
Description: req.Description,
|
|
|
|
Enabled: req.Enabled,
|
2022-12-06 10:11:57 +01:00
|
|
|
Groups: req.Groups,
|
2022-08-20 19:11:54 +02:00
|
|
|
}
|
|
|
|
|
2023-09-28 14:32:36 +02:00
|
|
|
if req.Peer != nil {
|
|
|
|
newRoute.Peer = peerID
|
|
|
|
}
|
|
|
|
|
|
|
|
if req.PeerGroups != nil {
|
|
|
|
newRoute.PeerGroups = *req.PeerGroups
|
|
|
|
}
|
|
|
|
|
2023-01-25 16:29:59 +01:00
|
|
|
err = h.accountManager.SaveRoute(account.Id, user.Id, newRoute)
|
2022-08-20 19:11:54 +02:00
|
|
|
if err != nil {
|
2022-11-11 20:36:45 +01:00
|
|
|
util.WriteError(err, w)
|
2022-08-20 19:11:54 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-02-03 10:33:28 +01:00
|
|
|
resp := toRouteResponse(newRoute)
|
2022-08-20 19:11:54 +02:00
|
|
|
|
2022-11-11 20:36:45 +01:00
|
|
|
util.WriteJSONObject(w, &resp)
|
2022-08-20 19:11:54 +02:00
|
|
|
}
|
|
|
|
|
2023-02-28 15:01:24 +01:00
|
|
|
// DeleteRoute handles route deletion request
|
2023-02-28 15:46:08 +01:00
|
|
|
func (h *RoutesHandler) DeleteRoute(w http.ResponseWriter, r *http.Request) {
|
2023-02-03 21:47:20 +01:00
|
|
|
claims := h.claimsExtractor.FromRequestContext(r)
|
2023-01-25 16:29:59 +01:00
|
|
|
account, user, err := h.accountManager.GetAccountFromToken(claims)
|
2022-08-20 19:11:54 +02:00
|
|
|
if err != nil {
|
2022-11-11 20:36:45 +01:00
|
|
|
util.WriteError(err, w)
|
2022-08-20 19:11:54 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-05-03 00:15:25 +02:00
|
|
|
routeID := mux.Vars(r)["routeId"]
|
2022-08-20 19:11:54 +02:00
|
|
|
if len(routeID) == 0 {
|
2022-11-11 20:36:45 +01:00
|
|
|
util.WriteError(status.Errorf(status.InvalidArgument, "invalid route ID"), w)
|
2022-08-20 19:11:54 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-05-06 14:47:49 +02:00
|
|
|
err = h.accountManager.DeleteRoute(account.Id, route.ID(routeID), user.Id)
|
2022-08-20 19:11:54 +02:00
|
|
|
if err != nil {
|
2022-11-11 20:36:45 +01:00
|
|
|
util.WriteError(err, w)
|
2022-08-20 19:11:54 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-03-29 18:46:40 +02:00
|
|
|
util.WriteJSONObject(w, emptyObject{})
|
2022-08-20 19:11:54 +02:00
|
|
|
}
|
|
|
|
|
2023-02-28 15:01:24 +01:00
|
|
|
// GetRoute handles a route Get request identified by ID
|
2023-02-28 15:46:08 +01:00
|
|
|
func (h *RoutesHandler) GetRoute(w http.ResponseWriter, r *http.Request) {
|
2023-02-03 21:47:20 +01:00
|
|
|
claims := h.claimsExtractor.FromRequestContext(r)
|
2022-11-11 20:36:45 +01:00
|
|
|
account, user, err := h.accountManager.GetAccountFromToken(claims)
|
2022-08-20 19:11:54 +02:00
|
|
|
if err != nil {
|
2022-11-11 20:36:45 +01:00
|
|
|
util.WriteError(err, w)
|
2022-08-20 19:11:54 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-05-03 00:15:25 +02:00
|
|
|
routeID := mux.Vars(r)["routeId"]
|
2022-08-20 19:11:54 +02:00
|
|
|
if len(routeID) == 0 {
|
2022-11-11 20:36:45 +01:00
|
|
|
util.WriteError(status.Errorf(status.InvalidArgument, "invalid route ID"), w)
|
2022-08-20 19:11:54 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-05-06 14:47:49 +02:00
|
|
|
foundRoute, err := h.accountManager.GetRoute(account.Id, route.ID(routeID), user.Id)
|
2022-08-20 19:11:54 +02:00
|
|
|
if err != nil {
|
2022-11-11 20:36:45 +01:00
|
|
|
util.WriteError(status.Errorf(status.NotFound, "route not found"), w)
|
2022-08-20 19:11:54 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-02-03 10:33:28 +01:00
|
|
|
util.WriteJSONObject(w, toRouteResponse(foundRoute))
|
2022-08-20 19:11:54 +02:00
|
|
|
}
|
|
|
|
|
2023-02-03 10:33:28 +01:00
|
|
|
func toRouteResponse(serverRoute *route.Route) *api.Route {
|
2023-09-28 14:32:36 +02:00
|
|
|
route := &api.Route{
|
2024-05-06 14:47:49 +02:00
|
|
|
Id: string(serverRoute.ID),
|
2022-08-20 19:11:54 +02:00
|
|
|
Description: serverRoute.Description,
|
2024-05-06 14:47:49 +02:00
|
|
|
NetworkId: string(serverRoute.NetID),
|
2022-08-20 19:11:54 +02:00
|
|
|
Enabled: serverRoute.Enabled,
|
2023-09-28 14:32:36 +02:00
|
|
|
Peer: &serverRoute.Peer,
|
2022-08-22 14:10:24 +02:00
|
|
|
Network: serverRoute.Network.String(),
|
|
|
|
NetworkType: serverRoute.NetworkType.String(),
|
2022-08-20 19:11:54 +02:00
|
|
|
Masquerade: serverRoute.Masquerade,
|
|
|
|
Metric: serverRoute.Metric,
|
2022-12-06 10:11:57 +01:00
|
|
|
Groups: serverRoute.Groups,
|
2022-08-20 19:11:54 +02:00
|
|
|
}
|
2023-09-28 14:32:36 +02:00
|
|
|
|
|
|
|
if len(serverRoute.PeerGroups) > 0 {
|
|
|
|
route.PeerGroups = &serverRoute.PeerGroups
|
|
|
|
}
|
|
|
|
return route
|
2022-08-20 19:11:54 +02:00
|
|
|
}
|