Compare commits

...

3 Commits

Author SHA1 Message Date
braginini
5d197cd5f9 Return user info in the peer API response 2022-09-23 18:32:25 +02:00
braginini
6bee984b46 Return user info in the peer APi response 2022-09-23 18:24:31 +02:00
braginini
2ee7d69f80 Add UserInfo to PeerInfo 2022-09-23 18:16:24 +02:00
9 changed files with 144 additions and 42 deletions

View File

@@ -47,6 +47,7 @@ type AccountManager interface {
IsUserAdmin(claims jwtclaims.AuthorizationClaims) (bool, error) IsUserAdmin(claims jwtclaims.AuthorizationClaims) (bool, error)
AccountExists(accountId string) (*bool, error) AccountExists(accountId string) (*bool, error)
GetPeer(peerKey string) (*Peer, error) GetPeer(peerKey string) (*Peer, error)
GetPeers(accountID string) ([]*PeerInfo, error)
MarkPeerConnected(peerKey string, connected bool) error MarkPeerConnected(peerKey string, connected bool) error
RenamePeer(accountId string, peerKey string, newName string) (*Peer, error) RenamePeer(accountId string, peerKey string, newName string) (*Peer, error)
DeletePeer(accountId string, peerKey string) (*Peer, error) DeletePeer(accountId string, peerKey string) (*Peer, error)
@@ -57,7 +58,7 @@ type AccountManager interface {
AddPeer(setupKey string, userId string, peer *Peer) (*Peer, error) AddPeer(setupKey string, userId string, peer *Peer) (*Peer, error)
UpdatePeerMeta(peerKey string, meta PeerSystemMeta) error UpdatePeerMeta(peerKey string, meta PeerSystemMeta) error
UpdatePeerSSHKey(peerKey string, sshKey string) error UpdatePeerSSHKey(peerKey string, sshKey string) error
GetUsersFromAccount(accountId string) ([]*UserInfo, error) GetUsers(accountId string) ([]*UserInfo, error)
GetGroup(accountId, groupID string) (*Group, error) GetGroup(accountId, groupID string) (*Group, error)
SaveGroup(accountId string, group *Group) error SaveGroup(accountId string, group *Group) error
UpdateGroup(accountID string, groupID string, operations []GroupUpdateOperation) (*Group, error) UpdateGroup(accountID string, groupID string, operations []GroupUpdateOperation) (*Group, error)

View File

@@ -847,7 +847,7 @@ func TestGetUsersFromAccount(t *testing.T) {
account.Users[user.Id] = user account.Users[user.Id] = user
} }
userInfos, err := manager.GetUsersFromAccount(accountId) userInfos, err := manager.GetUsers(accountId)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@@ -44,6 +44,18 @@ components:
- name - name
- role - role
- auto_groups - auto_groups
UserMinimum:
type: object
properties:
id:
description: User ID
type: string
email:
description: User's email address
type: string
name:
description: User's name from idp provider
type: string
UserRequest: UserRequest:
type: object type: object
properties: properties:
@@ -110,6 +122,11 @@ components:
ssh_enabled: ssh_enabled:
description: Indicates whether SSH server is enabled on this peer description: Indicates whether SSH server is enabled on this peer
type: boolean type: boolean
user:
$ref: '#/components/schemas/UserMinimum'
host_name:
description: Peer's hostname
type: string
required: required:
- ip - ip
- connected - connected

View File

@@ -137,6 +137,9 @@ type Peer struct {
// Groups that the peer belongs to // Groups that the peer belongs to
Groups []GroupMinimum `json:"groups"` Groups []GroupMinimum `json:"groups"`
// Peer's hostname
HostName *string `json:"host_name,omitempty"`
// Peer ID // Peer ID
Id string `json:"id"` Id string `json:"id"`
@@ -154,6 +157,7 @@ type Peer struct {
// Indicates whether SSH server is enabled on this peer // Indicates whether SSH server is enabled on this peer
SshEnabled bool `json:"ssh_enabled"` SshEnabled bool `json:"ssh_enabled"`
User *UserMinimum `json:"user,omitempty"`
// Peer's daemon or cli version // Peer's daemon or cli version
Version string `json:"version"` Version string `json:"version"`
@@ -372,6 +376,18 @@ type User struct {
Role string `json:"role"` Role string `json:"role"`
} }
// UserMinimum defines model for UserMinimum.
type UserMinimum struct {
// User's email address
Email *string `json:"email,omitempty"`
// User ID
Id *string `json:"id,omitempty"`
// User's name from idp provider
Name *string `json:"name,omitempty"`
}
// UserRequest defines model for UserRequest. // UserRequest defines model for UserRequest.
type UserRequest struct { type UserRequest struct {
// Groups to auto-assign to peers registered by this user // Groups to auto-assign to peers registered by this user

View File

@@ -11,7 +11,7 @@ import (
"net/http" "net/http"
) )
//Peers is a handler that returns peers of the account // Peers is a handler that returns peers of the account
type Peers struct { type Peers struct {
accountManager server.AccountManager accountManager server.AccountManager
authAudience string authAudience string
@@ -42,7 +42,7 @@ func (h *Peers) updatePeer(account *server.Account, peer *server.Peer, w http.Re
http.Redirect(w, r, "/", http.StatusInternalServerError) http.Redirect(w, r, "/", http.StatusInternalServerError)
return return
} }
writeJSONObject(w, toPeerResponse(peer, account)) writeJSONObject(w, toPeerResponse(&server.PeerInfo{Peer: peer}, account))
} }
func (h *Peers) deletePeer(accountId string, peer *server.Peer, w http.ResponseWriter, r *http.Request) { func (h *Peers) deletePeer(accountId string, peer *server.Peer, w http.ResponseWriter, r *http.Request) {
@@ -83,7 +83,7 @@ func (h *Peers) HandlePeer(w http.ResponseWriter, r *http.Request) {
h.updatePeer(account, peer, w, r) h.updatePeer(account, peer, w, r)
return return
case http.MethodGet: case http.MethodGet:
writeJSONObject(w, toPeerResponse(peer, account)) writeJSONObject(w, toPeerResponse(&server.PeerInfo{Peer: peer}, account))
return return
default: default:
@@ -93,8 +93,11 @@ func (h *Peers) HandlePeer(w http.ResponseWriter, r *http.Request) {
} }
func (h *Peers) GetPeers(w http.ResponseWriter, r *http.Request) { func (h *Peers) GetPeers(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet: if r.Method != http.MethodGet {
http.Error(w, "", http.StatusNotFound)
}
account, err := getJWTAccount(h.accountManager, h.jwtExtractor, h.authAudience, r) account, err := getJWTAccount(h.accountManager, h.jwtExtractor, h.authAudience, r)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
@@ -102,18 +105,22 @@ func (h *Peers) GetPeers(w http.ResponseWriter, r *http.Request) {
return return
} }
peers, err := h.accountManager.GetPeers(account.Id)
if err != nil {
log.Error(err)
http.Redirect(w, r, "/", http.StatusInternalServerError)
return
}
respBody := []*api.Peer{} respBody := []*api.Peer{}
for _, peer := range account.Peers { for _, peer := range peers {
respBody = append(respBody, toPeerResponse(peer, account)) respBody = append(respBody, toPeerResponse(peer, account))
} }
writeJSONObject(w, respBody) writeJSONObject(w, respBody)
return return
default:
http.Error(w, "", http.StatusNotFound)
}
} }
func toPeerResponse(peer *server.Peer, account *server.Account) *api.Peer { func toPeerResponse(peer *server.PeerInfo, account *server.Account) *api.Peer {
var groupsInfo []api.GroupMinimum var groupsInfo []api.GroupMinimum
groupsChecked := make(map[string]struct{}) groupsChecked := make(map[string]struct{})
for _, group := range account.Groups { for _, group := range account.Groups {
@@ -123,7 +130,7 @@ func toPeerResponse(peer *server.Peer, account *server.Account) *api.Peer {
} }
groupsChecked[group.ID] = struct{}{} groupsChecked[group.ID] = struct{}{}
for _, pk := range group.Peers { for _, pk := range group.Peers {
if pk == peer.Key { if pk == peer.Peer.Key {
info := api.GroupMinimum{ info := api.GroupMinimum{
Id: group.ID, Id: group.ID,
Name: group.Name, Name: group.Name,
@@ -134,15 +141,26 @@ func toPeerResponse(peer *server.Peer, account *server.Account) *api.Peer {
} }
} }
} }
return &api.Peer{ resp := &api.Peer{
Id: peer.IP.String(), Id: peer.Peer.IP.String(),
Name: peer.Name, Name: peer.Peer.Name,
Ip: peer.IP.String(), Ip: peer.Peer.IP.String(),
Connected: peer.Status.Connected, Connected: peer.Peer.Status.Connected,
LastSeen: peer.Status.LastSeen, LastSeen: peer.Peer.Status.LastSeen,
Os: fmt.Sprintf("%s %s", peer.Meta.OS, peer.Meta.Core), Os: fmt.Sprintf("%s %s", peer.Peer.Meta.OS, peer.Peer.Meta.Core),
Version: peer.Meta.WtVersion, Version: peer.Peer.Meta.WtVersion,
Groups: groupsInfo, Groups: groupsInfo,
SshEnabled: peer.SSHEnabled, SshEnabled: peer.Peer.SSHEnabled,
HostName: &peer.Peer.Meta.Hostname,
} }
if peer.UserInfo != nil {
resp.User = &api.UserMinimum{
Email: &peer.UserInfo.Email,
Id: &peer.UserInfo.ID,
Name: &peer.UserInfo.Name,
}
}
return resp
} }

View File

@@ -92,9 +92,11 @@ func (h *UserHandler) GetUsers(w http.ResponseWriter, r *http.Request) {
account, err := getJWTAccount(h.accountManager, h.jwtExtractor, h.authAudience, r) account, err := getJWTAccount(h.accountManager, h.jwtExtractor, h.authAudience, r)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
http.Redirect(w, r, "/", http.StatusInternalServerError)
return
} }
data, err := h.accountManager.GetUsersFromAccount(account.Id) data, err := h.accountManager.GetUsers(account.Id)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
http.Redirect(w, r, "/", http.StatusInternalServerError) http.Redirect(w, r, "/", http.StatusInternalServerError)

View File

@@ -60,7 +60,7 @@ func (am *MockAccountManager) GetUsersFromAccount(accountID string) ([]*server.U
if am.GetUsersFromAccountFunc != nil { if am.GetUsersFromAccountFunc != nil {
return am.GetUsersFromAccountFunc(accountID) return am.GetUsersFromAccountFunc(accountID)
} }
return nil, status.Errorf(codes.Unimplemented, "method GetUsersFromAccount is not implemented") return nil, status.Errorf(codes.Unimplemented, "method GetUsers is not implemented")
} }
// GetOrCreateAccountByUser mock implementation of GetOrCreateAccountByUser from server.AccountManager interface // GetOrCreateAccountByUser mock implementation of GetOrCreateAccountByUser from server.AccountManager interface

View File

@@ -31,6 +31,12 @@ type PeerStatus struct {
Connected bool Connected bool
} }
// PeerInfo is a composition of Peer and additional UserInfo
type PeerInfo struct {
Peer *Peer
UserInfo *UserInfo
}
// Peer represents a machine connected to the network. // Peer represents a machine connected to the network.
// The Peer is a Wireguard peer identified by a public key // The Peer is a Wireguard peer identified by a public key
type Peer struct { type Peer struct {
@@ -68,6 +74,44 @@ func (p *Peer) Copy() *Peer {
} }
} }
// GetPeers returns a list of Peers belonging to the specified account
func (am *DefaultAccountManager) GetPeers(accountID string) ([]*PeerInfo, error) {
am.mux.Lock()
defer am.mux.Unlock()
account, err := am.Store.GetAccount(accountID)
if err != nil {
return nil, status.Errorf(codes.NotFound, "account not found")
}
users, err := am.getUsersInfos(account)
if err != nil {
return nil, err
}
var userMap = make(map[string]*UserInfo)
for _, user := range users {
userMap[user.ID] = user
}
var peerInfos []*PeerInfo
for _, peer := range account.Peers {
if peer.UserID == "" {
peerInfos = append(peerInfos, &PeerInfo{
Peer: peer,
UserInfo: nil,
})
} else {
peerInfos = append(peerInfos, &PeerInfo{
Peer: peer.Copy(),
UserInfo: userMap[peer.UserID],
})
}
}
return peerInfos, nil
}
// GetPeer returns a peer from a Store // GetPeer returns a peer from a Store
func (am *DefaultAccountManager) GetPeer(peerKey string) (*Peer, error) { func (am *DefaultAccountManager) GetPeer(peerKey string) (*Peer, error) {
am.mux.Lock() am.mux.Lock()

View File

@@ -207,16 +207,11 @@ func (am *DefaultAccountManager) IsUserAdmin(claims jwtclaims.AuthorizationClaim
return user.Role == UserRoleAdmin, nil return user.Role == UserRoleAdmin, nil
} }
// GetUsersFromAccount performs a batched request for users from IDP by account ID func (am *DefaultAccountManager) getUsersInfos(account *Account) ([]*UserInfo, error) {
func (am *DefaultAccountManager) GetUsersFromAccount(accountID string) ([]*UserInfo, error) { var err error
account, err := am.GetAccountById(accountID)
if err != nil {
return nil, err
}
queriedUsers := make([]*idp.UserData, 0) queriedUsers := make([]*idp.UserData, 0)
if !isNil(am.idpManager) { if !isNil(am.idpManager) {
queriedUsers, err = am.lookupCache(account.Users, accountID) queriedUsers, err = am.lookupCache(account.Users, account.Id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -249,3 +244,12 @@ func (am *DefaultAccountManager) GetUsersFromAccount(accountID string) ([]*UserI
return userInfos, nil return userInfos, nil
} }
// GetUsers performs a batched request for users from IDP by account ID
func (am *DefaultAccountManager) GetUsers(accountID string) ([]*UserInfo, error) {
account, err := am.GetAccountById(accountID)
if err != nil {
return nil, err
}
return am.getUsersInfos(account)
}