mirror of
https://github.com/netbirdio/netbird.git
synced 2024-12-03 13:34:41 +01:00
659110f0d5
With this change, we don't need to update all peers on startup. We will check the existence of an update channel when returning a list or single peer on API. Then after restarting of server consumers of API will see peer not connected status till the creation of an updated channel which indicates peer successful connection.
207 lines
5.9 KiB
Go
207 lines
5.9 KiB
Go
package http
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/gorilla/mux"
|
|
|
|
"github.com/netbirdio/netbird/management/server"
|
|
"github.com/netbirdio/netbird/management/server/http/api"
|
|
"github.com/netbirdio/netbird/management/server/http/util"
|
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
|
"github.com/netbirdio/netbird/management/server/status"
|
|
)
|
|
|
|
// PeersHandler is a handler that returns peers of the account
|
|
type PeersHandler struct {
|
|
accountManager server.AccountManager
|
|
claimsExtractor *jwtclaims.ClaimsExtractor
|
|
}
|
|
|
|
// NewPeersHandler creates a new PeersHandler HTTP handler
|
|
func NewPeersHandler(accountManager server.AccountManager, authCfg AuthCfg) *PeersHandler {
|
|
return &PeersHandler{
|
|
accountManager: accountManager,
|
|
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
|
jwtclaims.WithAudience(authCfg.Audience),
|
|
jwtclaims.WithUserIDClaim(authCfg.UserIDClaim),
|
|
),
|
|
}
|
|
}
|
|
|
|
func (h *PeersHandler) checkPeerStatus(peer *server.Peer) (*server.Peer, error) {
|
|
peerToReturn := peer.Copy()
|
|
if peer.Status.Connected {
|
|
statuses, err := h.accountManager.GetAllConnectedPeers()
|
|
if err != nil {
|
|
return peerToReturn, err
|
|
}
|
|
|
|
// Although we have online status in store we do not yet have an updated channel so have to show it as disconnected
|
|
// This may happen after server restart when not all peers are yet connected
|
|
if _, connected := statuses[peerToReturn.ID]; !connected {
|
|
peerToReturn.Status.Connected = false
|
|
}
|
|
}
|
|
|
|
return peerToReturn, nil
|
|
}
|
|
|
|
func (h *PeersHandler) getPeer(account *server.Account, peerID, userID string, w http.ResponseWriter) {
|
|
peer, err := h.accountManager.GetPeer(account.Id, peerID, userID)
|
|
if err != nil {
|
|
util.WriteError(err, w)
|
|
return
|
|
}
|
|
|
|
peerToReturn, err := h.checkPeerStatus(peer)
|
|
if err != nil {
|
|
util.WriteError(err, w)
|
|
return
|
|
}
|
|
|
|
util.WriteJSONObject(w, toPeerResponse(peerToReturn, account, h.accountManager.GetDNSDomain()))
|
|
}
|
|
|
|
func (h *PeersHandler) updatePeer(account *server.Account, user *server.User, peerID string, w http.ResponseWriter, r *http.Request) {
|
|
req := &api.PeerRequest{}
|
|
err := json.NewDecoder(r.Body).Decode(&req)
|
|
if err != nil {
|
|
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
|
return
|
|
}
|
|
|
|
update := &server.Peer{ID: peerID, SSHEnabled: req.SshEnabled, Name: req.Name,
|
|
LoginExpirationEnabled: req.LoginExpirationEnabled}
|
|
peer, err := h.accountManager.UpdatePeer(account.Id, user.Id, update)
|
|
if err != nil {
|
|
util.WriteError(err, w)
|
|
return
|
|
}
|
|
dnsDomain := h.accountManager.GetDNSDomain()
|
|
util.WriteJSONObject(w, toPeerResponse(peer, account, dnsDomain))
|
|
}
|
|
|
|
func (h *PeersHandler) deletePeer(accountID, userID string, peerID string, w http.ResponseWriter) {
|
|
err := h.accountManager.DeletePeer(accountID, peerID, userID)
|
|
if err != nil {
|
|
util.WriteError(err, w)
|
|
return
|
|
}
|
|
util.WriteJSONObject(w, emptyObject{})
|
|
}
|
|
|
|
// HandlePeer handles all peer requests for GET, PUT and DELETE operations
|
|
func (h *PeersHandler) HandlePeer(w http.ResponseWriter, r *http.Request) {
|
|
claims := h.claimsExtractor.FromRequestContext(r)
|
|
account, user, err := h.accountManager.GetAccountFromToken(claims)
|
|
if err != nil {
|
|
util.WriteError(err, w)
|
|
return
|
|
}
|
|
vars := mux.Vars(r)
|
|
peerID := vars["peerId"]
|
|
if len(peerID) == 0 {
|
|
util.WriteError(status.Errorf(status.InvalidArgument, "invalid peer ID"), w)
|
|
return
|
|
}
|
|
|
|
switch r.Method {
|
|
case http.MethodDelete:
|
|
h.deletePeer(account.Id, user.Id, peerID, w)
|
|
return
|
|
case http.MethodPut:
|
|
h.updatePeer(account, user, peerID, w, r)
|
|
return
|
|
case http.MethodGet:
|
|
h.getPeer(account, peerID, user.Id, w)
|
|
return
|
|
default:
|
|
util.WriteError(status.Errorf(status.NotFound, "unknown METHOD"), w)
|
|
}
|
|
}
|
|
|
|
// GetAllPeers returns a list of all peers associated with a provided account
|
|
func (h *PeersHandler) GetAllPeers(w http.ResponseWriter, r *http.Request) {
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
claims := h.claimsExtractor.FromRequestContext(r)
|
|
account, user, err := h.accountManager.GetAccountFromToken(claims)
|
|
if err != nil {
|
|
util.WriteError(err, w)
|
|
return
|
|
}
|
|
|
|
peers, err := h.accountManager.GetPeers(account.Id, user.Id)
|
|
if err != nil {
|
|
util.WriteError(err, w)
|
|
return
|
|
}
|
|
|
|
dnsDomain := h.accountManager.GetDNSDomain()
|
|
|
|
respBody := []*api.Peer{}
|
|
for _, peer := range peers {
|
|
peerToReturn, err := h.checkPeerStatus(peer)
|
|
if err != nil {
|
|
util.WriteError(err, w)
|
|
return
|
|
}
|
|
respBody = append(respBody, toPeerResponse(peerToReturn, account, dnsDomain))
|
|
}
|
|
util.WriteJSONObject(w, respBody)
|
|
return
|
|
default:
|
|
util.WriteError(status.Errorf(status.NotFound, "unknown METHOD"), w)
|
|
}
|
|
}
|
|
|
|
func toPeerResponse(peer *server.Peer, account *server.Account, dnsDomain string) *api.Peer {
|
|
var groupsInfo []api.GroupMinimum
|
|
groupsChecked := make(map[string]struct{})
|
|
for _, group := range account.Groups {
|
|
_, ok := groupsChecked[group.ID]
|
|
if ok {
|
|
continue
|
|
}
|
|
groupsChecked[group.ID] = struct{}{}
|
|
for _, pk := range group.Peers {
|
|
if pk == peer.ID {
|
|
info := api.GroupMinimum{
|
|
Id: group.ID,
|
|
Name: group.Name,
|
|
PeersCount: len(group.Peers),
|
|
}
|
|
groupsInfo = append(groupsInfo, info)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
fqdn := peer.FQDN(dnsDomain)
|
|
if fqdn == "" {
|
|
fqdn = peer.DNSLabel
|
|
}
|
|
|
|
return &api.Peer{
|
|
Id: peer.ID,
|
|
Name: peer.Name,
|
|
Ip: peer.IP.String(),
|
|
Connected: peer.Status.Connected,
|
|
LastSeen: peer.Status.LastSeen,
|
|
Os: fmt.Sprintf("%s %s", peer.Meta.OS, peer.Meta.Core),
|
|
Version: peer.Meta.WtVersion,
|
|
Groups: groupsInfo,
|
|
SshEnabled: peer.SSHEnabled,
|
|
Hostname: peer.Meta.Hostname,
|
|
UserId: &peer.UserID,
|
|
UiVersion: &peer.Meta.UIVersion,
|
|
DnsLabel: fqdn,
|
|
LoginExpirationEnabled: peer.LoginExpirationEnabled,
|
|
LastLogin: peer.LastLogin,
|
|
LoginExpired: peer.Status.LoginExpired,
|
|
}
|
|
}
|