diff --git a/management/server/http/handlers/networks/handler.go b/management/server/http/handlers/networks/handler.go index f99eca7941f..b2e0d64fe9b 100644 --- a/management/server/http/handlers/networks/handler.go +++ b/management/server/http/handlers/networks/handler.go @@ -35,7 +35,7 @@ type handler struct { func AddEndpoints(networksManager networks.Manager, resourceManager resources.Manager, routerManager routers.Manager, groupsManager groups.Manager, accountManager account.Manager, router *mux.Router) { addRouterEndpoints(routerManager, router) - addResourceEndpoints(resourceManager, groupsManager, router) + addResourceEndpoints(resourceManager, groupsManager, accountManager, router) networksHandler := newHandler(networksManager, resourceManager, routerManager, groupsManager, accountManager) router.HandleFunc("/networks", networksHandler.getAllNetworks).Methods("GET", "OPTIONS") diff --git a/management/server/http/handlers/networks/resources_handler.go b/management/server/http/handlers/networks/resources_handler.go index c31729a39c7..c63bdf130a3 100644 --- a/management/server/http/handlers/networks/resources_handler.go +++ b/management/server/http/handlers/networks/resources_handler.go @@ -6,33 +6,40 @@ import ( "github.com/gorilla/mux" + "github.com/netbirdio/netbird/management/server/account" nbcontext "github.com/netbirdio/netbird/management/server/context" "github.com/netbirdio/netbird/management/server/groups" "github.com/netbirdio/netbird/management/server/networks/resources" "github.com/netbirdio/netbird/management/server/networks/resources/types" + nbpeer "github.com/netbirdio/netbird/management/server/peer" + nbtypes "github.com/netbirdio/netbird/management/server/types" "github.com/netbirdio/netbird/shared/management/http/api" "github.com/netbirdio/netbird/shared/management/http/util" + "github.com/netbirdio/netbird/shared/management/status" ) type resourceHandler struct { resourceManager resources.Manager groupsManager groups.Manager + accountManager account.Manager } -func addResourceEndpoints(resourcesManager resources.Manager, groupsManager groups.Manager, router *mux.Router) { - resourceHandler := newResourceHandler(resourcesManager, groupsManager) +func addResourceEndpoints(resourcesManager resources.Manager, groupsManager groups.Manager, accountManager account.Manager, router *mux.Router) { + resourceHandler := newResourceHandler(resourcesManager, groupsManager, accountManager) router.HandleFunc("/networks/resources", resourceHandler.getAllResourcesInAccount).Methods("GET", "OPTIONS") router.HandleFunc("/networks/{networkId}/resources", resourceHandler.getAllResourcesInNetwork).Methods("GET", "OPTIONS") router.HandleFunc("/networks/{networkId}/resources", resourceHandler.createResource).Methods("POST", "OPTIONS") router.HandleFunc("/networks/{networkId}/resources/{resourceId}", resourceHandler.getResource).Methods("GET", "OPTIONS") router.HandleFunc("/networks/{networkId}/resources/{resourceId}", resourceHandler.updateResource).Methods("PUT", "OPTIONS") router.HandleFunc("/networks/{networkId}/resources/{resourceId}", resourceHandler.deleteResource).Methods("DELETE", "OPTIONS") + router.HandleFunc("/networks/{networkId}/resources/{resourceId}/temporary-access", resourceHandler.CreateTemporaryAccess).Methods("POST", "OPTIONS") } -func newResourceHandler(resourceManager resources.Manager, groupsManager groups.Manager) *resourceHandler { +func newResourceHandler(resourceManager resources.Manager, groupsManager groups.Manager, accountManager account.Manager) *resourceHandler { return &resourceHandler{ resourceManager: resourceManager, groupsManager: groupsManager, + accountManager: accountManager, } } @@ -218,3 +225,90 @@ func (h *resourceHandler) deleteResource(w http.ResponseWriter, r *http.Request) util.WriteJSONObject(r.Context(), w, util.EmptyObject{}) } + +func (h *resourceHandler) CreateTemporaryAccess(w http.ResponseWriter, r *http.Request) { + userAuth, err := nbcontext.GetUserAuthFromContext(r.Context()) + if err != nil { + util.WriteError(r.Context(), err, w) + return + } + + vars := mux.Vars(r) + networkID := vars["networkId"] + if len(networkID) == 0 { + util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "invalid network ID"), w) + return + } + resourceID := vars["resourceId"] + if len(resourceID) == 0 { + util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "invalid resource ID"), w) + return + } + + var req api.PeerTemporaryAccessRequest + err = json.NewDecoder(r.Body).Decode(&req) + if err != nil { + util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w) + return + } + + newPeer := &nbpeer.Peer{} + newPeer.FromAPITemporaryAccessRequest(&req) + + targetResource, err := h.resourceManager.GetResource(r.Context(), userAuth.AccountId, userAuth.UserId, networkID, resourceID) + if err != nil { + util.WriteError(r.Context(), err, w) + return + } + + peer, _, _, err := h.accountManager.AddPeer(r.Context(), userAuth.AccountId, "", userAuth.UserId, newPeer, true) + if err != nil { + util.WriteError(r.Context(), err, w) + return + } + + for _, rule := range req.Rules { + protocol, portRange, err := nbtypes.ParseRuleString(rule) + if err != nil { + util.WriteError(r.Context(), err, w) + return + } + policy := &nbtypes.Policy{ + AccountID: userAuth.AccountId, + Description: "Temporary access policy for peer " + peer.Name, + Name: "Temporary access policy for peer " + peer.Name, + Enabled: true, + Rules: []*nbtypes.PolicyRule{{ + Name: "Temporary access rule", + Description: "Temporary access rule", + Enabled: true, + Action: nbtypes.PolicyTrafficActionAccept, + SourceResource: nbtypes.Resource{ + Type: nbtypes.ResourceTypePeer, + ID: peer.ID, + }, + DestinationResource: nbtypes.Resource{ + Type: nbtypes.ResourceType(targetResource.Type.String()), + ID: targetResource.ID, + }, + Bidirectional: false, + Protocol: protocol, + PortRanges: []nbtypes.RulePortRange{portRange}, + }}, + } + + _, err = h.accountManager.SavePolicy(r.Context(), userAuth.AccountId, userAuth.UserId, policy, true) + if err != nil { + util.WriteError(r.Context(), err, w) + return + } + } + + resp := &api.PeerTemporaryAccessResponse{ + Id: peer.ID, + Name: peer.Name, + Rules: req.Rules, + } + + util.WriteJSONObject(r.Context(), w, resp) +} diff --git a/shared/management/http/api/openapi.yml b/shared/management/http/api/openapi.yml index 2d063a7b5bd..0e17ea05b83 100644 --- a/shared/management/http/api/openapi.yml +++ b/shared/management/http/api/openapi.yml @@ -4108,6 +4108,48 @@ paths: "$ref": "#/components/responses/forbidden" '500': "$ref": "#/components/responses/internal_error" + /api/networks/{networkId}/resources/{resourceId}/temporary-access: + post: + summary: Create a Temporary Access Peer + description: Creates a temporary access peer that can be used to access this resource and this resource only. The temporary access peer and its access policies will be automatically deleted after it disconnects. + tags: [ Networks ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: networkId + required: true + schema: + type: string + description: The unique identifier of a network + - in: path + name: resourceId + required: true + schema: + type: string + description: The unique identifier of a network resource + requestBody: + description: Temporary Access Peer create request + content: + 'application/json': + schema: + $ref: '#/components/schemas/PeerTemporaryAccessRequest' + responses: + '200': + description: Temporary Access Peer response + content: + application/json: + schema: + $ref: '#/components/schemas/PeerTemporaryAccessResponse' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" /api/networks/{networkId}/routers: get: summary: List all Network Routers diff --git a/shared/management/http/api/types.gen.go b/shared/management/http/api/types.gen.go index d3e4255483c..1854b25a454 100644 --- a/shared/management/http/api/types.gen.go +++ b/shared/management/http/api/types.gen.go @@ -1971,6 +1971,9 @@ type PostApiNetworksNetworkIdResourcesJSONRequestBody = NetworkResourceRequest // PutApiNetworksNetworkIdResourcesResourceIdJSONRequestBody defines body for PutApiNetworksNetworkIdResourcesResourceId for application/json ContentType. type PutApiNetworksNetworkIdResourcesResourceIdJSONRequestBody = NetworkResourceRequest +// PostApiNetworksNetworkIdResourcesResourceIdTemporaryAccessJSONRequestBody defines body for PostApiNetworksNetworkIdResourcesResourceIdTemporaryAccess for application/json ContentType. +type PostApiNetworksNetworkIdResourcesResourceIdTemporaryAccessJSONRequestBody = PeerTemporaryAccessRequest + // PostApiNetworksNetworkIdRoutersJSONRequestBody defines body for PostApiNetworksNetworkIdRouters for application/json ContentType. type PostApiNetworksNetworkIdRoutersJSONRequestBody = NetworkRouterRequest