mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-21 14:19:08 +02:00
Add per-connection user-approval prompts for VNC
This commit is contained in:
@@ -22,6 +22,7 @@ import (
|
||||
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
||||
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
"github.com/netbirdio/netbird/shared/auth"
|
||||
"github.com/netbirdio/netbird/shared/management/http/api"
|
||||
"github.com/netbirdio/netbird/shared/management/http/util"
|
||||
"github.com/netbirdio/netbird/shared/management/status"
|
||||
@@ -525,6 +526,7 @@ func (h *Handler) CreateTemporaryAccess(w http.ResponseWriter, r *http.Request)
|
||||
return
|
||||
}
|
||||
policy.Rules[0].SessionPubKey = pubKey
|
||||
policy.Rules[0].SessionDisplayName = h.displayNameForUser(r.Context(), userAuth)
|
||||
}
|
||||
|
||||
_, err = h.accountManager.SavePolicy(r.Context(), userAuth.AccountId, userAuth.UserId, policy, true)
|
||||
@@ -744,3 +746,30 @@ func peerIPv6String(peer *nbpeer.Peer) *string {
|
||||
s := peer.IPv6.String()
|
||||
return &s
|
||||
}
|
||||
|
||||
// displayNameForUser returns a human-readable label for the requesting
|
||||
// user suitable for a VNC approval prompt. Tries the IdP-resolved
|
||||
// UserInfo first (carries name / email management caches from the
|
||||
// identity provider) and falls through to JWT claims, then user id.
|
||||
// Errors from the lookup don't fail the request; we just degrade.
|
||||
func (h *Handler) displayNameForUser(ctx context.Context, u auth.UserAuth) string {
|
||||
if info, err := h.accountManager.GetCurrentUserInfo(ctx, u); err == nil && info != nil {
|
||||
switch {
|
||||
case info.UserInfo != nil && info.UserInfo.Name != "":
|
||||
return info.UserInfo.Name
|
||||
case info.UserInfo != nil && info.UserInfo.Email != "":
|
||||
return info.UserInfo.Email
|
||||
}
|
||||
} else if err != nil {
|
||||
log.WithContext(ctx).Debugf("display name: GetCurrentUserInfo: %v", err)
|
||||
}
|
||||
switch {
|
||||
case u.PreferredName != "":
|
||||
return u.PreferredName
|
||||
case u.Name != "":
|
||||
return u.Name
|
||||
case u.Email != "":
|
||||
return u.Email
|
||||
}
|
||||
return u.UserId
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/rs/xid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/client/ssh/auth"
|
||||
auth "github.com/netbirdio/netbird/shared/sessionauth"
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
proxydomain "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/domain"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/reverseproxy/service"
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/netbirdio/netbird/client/ssh/auth"
|
||||
auth "github.com/netbirdio/netbird/shared/sessionauth"
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
|
||||
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/client/ssh/auth"
|
||||
auth "github.com/netbirdio/netbird/shared/sessionauth"
|
||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||
)
|
||||
|
||||
@@ -28,6 +28,9 @@ type VNCSessionPubKey struct {
|
||||
PubKey string
|
||||
// UserID is the unhashed user identity the pubkey authenticates as.
|
||||
UserID string
|
||||
// DisplayName is a human-readable label for UserID, used by the host
|
||||
// peer's approval prompt. Empty when not provided.
|
||||
DisplayName string
|
||||
}
|
||||
|
||||
// ruleAuthCallbacks lets Account and NetworkMapComponents share the per-rule
|
||||
@@ -83,8 +86,9 @@ func (cb ruleAuthCallbacks) handleVNCRule(rule *PolicyRule, peerInSources, peerI
|
||||
cb.collectVNCUsers(rule, state.vncAuthorizedUsers)
|
||||
if peerInDestinations && rule.SessionPubKey != "" && rule.AuthorizedUser != "" {
|
||||
state.vncSessionPubKeys = append(state.vncSessionPubKeys, VNCSessionPubKey{
|
||||
PubKey: rule.SessionPubKey,
|
||||
UserID: rule.AuthorizedUser,
|
||||
PubKey: rule.SessionPubKey,
|
||||
UserID: rule.AuthorizedUser,
|
||||
DisplayName: rule.SessionDisplayName,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,13 @@ type PolicyRule struct {
|
||||
// AuthorizedUser when the rule was created via temporary-access for a
|
||||
// VNC scope; empty otherwise.
|
||||
SessionPubKey string
|
||||
|
||||
// SessionDisplayName is a human-readable label for the user the
|
||||
// SessionPubKey was issued to (typically display name, falling back
|
||||
// to email or user id). The daemon surfaces it on the host's
|
||||
// per-connection approval prompt so the user being asked can
|
||||
// recognise who is requesting access.
|
||||
SessionDisplayName string
|
||||
}
|
||||
|
||||
// Copy returns a copy of a policy rule
|
||||
@@ -116,6 +123,7 @@ func (pm *PolicyRule) Copy() *PolicyRule {
|
||||
AuthorizedGroups: make(map[string][]string, len(pm.AuthorizedGroups)),
|
||||
AuthorizedUser: pm.AuthorizedUser,
|
||||
SessionPubKey: pm.SessionPubKey,
|
||||
SessionDisplayName: pm.SessionDisplayName,
|
||||
}
|
||||
copy(rule.Destinations, pm.Destinations)
|
||||
copy(rule.Sources, pm.Sources)
|
||||
@@ -144,7 +152,8 @@ func (pm *PolicyRule) Equal(other *PolicyRule) bool {
|
||||
pm.SourceResource != other.SourceResource ||
|
||||
pm.DestinationResource != other.DestinationResource ||
|
||||
pm.AuthorizedUser != other.AuthorizedUser ||
|
||||
pm.SessionPubKey != other.SessionPubKey {
|
||||
pm.SessionPubKey != other.SessionPubKey ||
|
||||
pm.SessionDisplayName != other.SessionDisplayName {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user