mirror of
https://github.com/netbirdio/netbird.git
synced 2026-07-04 05:40:02 +00:00
Compare commits
6 Commits
v0.22.2
...
fix/iptabl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9452e7b32 | ||
|
|
950d2efed7 | ||
|
|
a9b9b3fa0a | ||
|
|
cdf57275b7 | ||
|
|
e5e69b1f75 | ||
|
|
8eca83f3cb |
@@ -395,6 +395,10 @@ func (i *iptablesManager) insertRoutingRule(keyFormat, table, chain, jump string
|
||||
ipVersion = ipv6
|
||||
}
|
||||
|
||||
if iptablesClient == nil {
|
||||
return fmt.Errorf("unable to insert iptables routing rules. Iptables client is not initialized")
|
||||
}
|
||||
|
||||
ruleKey := genKey(keyFormat, pair.ID)
|
||||
rule := genRuleSpec(jump, ruleKey, pair.source, pair.destination)
|
||||
existingRule, found := i.rules[ipVersion][ruleKey]
|
||||
@@ -459,6 +463,10 @@ func (i *iptablesManager) removeRoutingRule(keyFormat, table, chain string, pair
|
||||
ipVersion = ipv6
|
||||
}
|
||||
|
||||
if iptablesClient == nil {
|
||||
return fmt.Errorf("unable to remove iptables routing rules. Iptables client is not initialized")
|
||||
}
|
||||
|
||||
ruleKey := genKey(keyFormat, pair.ID)
|
||||
existingRule, found := i.rules[ipVersion][ruleKey]
|
||||
if found {
|
||||
|
||||
@@ -54,14 +54,14 @@ type bpfSpecs struct {
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
XdpProgFunc *ebpf.ProgramSpec `ebpf:"xdp_prog_func"`
|
||||
NbWgProxy *ebpf.ProgramSpec `ebpf:"nb_wg_proxy"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
XdpPortMap *ebpf.MapSpec `ebpf:"xdp_port_map"`
|
||||
NbWgProxySettingsMap *ebpf.MapSpec `ebpf:"nb_wg_proxy_settings_map"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
@@ -83,12 +83,12 @@ func (o *bpfObjects) Close() error {
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
XdpPortMap *ebpf.Map `ebpf:"xdp_port_map"`
|
||||
NbWgProxySettingsMap *ebpf.Map `ebpf:"nb_wg_proxy_settings_map"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.XdpPortMap,
|
||||
m.NbWgProxySettingsMap,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -96,12 +96,12 @@ func (m *bpfMaps) Close() error {
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
XdpProgFunc *ebpf.Program `ebpf:"xdp_prog_func"`
|
||||
NbWgProxy *ebpf.Program `ebpf:"nb_wg_proxy"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.XdpProgFunc,
|
||||
p.NbWgProxy,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
@@ -54,14 +54,14 @@ type bpfSpecs struct {
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
XdpProgFunc *ebpf.ProgramSpec `ebpf:"xdp_prog_func"`
|
||||
NbWgProxy *ebpf.ProgramSpec `ebpf:"nb_wg_proxy"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
XdpPortMap *ebpf.MapSpec `ebpf:"xdp_port_map"`
|
||||
NbWgProxySettingsMap *ebpf.MapSpec `ebpf:"nb_wg_proxy_settings_map"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
@@ -83,12 +83,12 @@ func (o *bpfObjects) Close() error {
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
XdpPortMap *ebpf.Map `ebpf:"xdp_port_map"`
|
||||
NbWgProxySettingsMap *ebpf.Map `ebpf:"nb_wg_proxy_settings_map"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.XdpPortMap,
|
||||
m.NbWgProxySettingsMap,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -96,12 +96,12 @@ func (m *bpfMaps) Close() error {
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
XdpProgFunc *ebpf.Program `ebpf:"xdp_prog_func"`
|
||||
NbWgProxy *ebpf.Program `ebpf:"nb_wg_proxy"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.XdpProgFunc,
|
||||
p.NbWgProxy,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
@@ -50,22 +50,22 @@ func (l *EBPF) Load(proxyPort, wgPort int) error {
|
||||
_ = objs.Close()
|
||||
}()
|
||||
|
||||
err = objs.XdpPortMap.Put(mapKeyProxyPort, uint16(proxyPort))
|
||||
err = objs.NbWgProxySettingsMap.Put(mapKeyProxyPort, uint16(proxyPort))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = objs.XdpPortMap.Put(mapKeyWgPort, uint16(wgPort))
|
||||
err = objs.NbWgProxySettingsMap.Put(mapKeyWgPort, uint16(wgPort))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = objs.XdpPortMap.Close()
|
||||
_ = objs.NbWgProxySettingsMap.Close()
|
||||
}()
|
||||
|
||||
l.link, err = link.AttachXDP(link.XDPOptions{
|
||||
Program: objs.XdpProgFunc,
|
||||
Program: objs.NbWgProxy,
|
||||
Interface: ifce.Index,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -75,7 +75,7 @@ func (l *EBPF) Load(proxyPort, wgPort int) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Free free ebpf program
|
||||
// Free ebpf program
|
||||
func (l *EBPF) Free() error {
|
||||
if l.link != nil {
|
||||
return l.link.Close()
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
const __u32 map_key_proxy_port = 0;
|
||||
const __u32 map_key_wg_port = 1;
|
||||
|
||||
struct bpf_map_def SEC("maps") xdp_port_map = {
|
||||
struct bpf_map_def SEC("maps") nb_wg_proxy_settings_map = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.key_size = sizeof(__u32),
|
||||
.value_size = sizeof(__u16),
|
||||
@@ -27,14 +27,14 @@ __u16 wg_port = 0;
|
||||
|
||||
bool read_port_settings() {
|
||||
__u16 *value;
|
||||
value = bpf_map_lookup_elem(&xdp_port_map, &map_key_proxy_port);
|
||||
value = bpf_map_lookup_elem(&nb_wg_proxy_settings_map, &map_key_proxy_port);
|
||||
if(!value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
proxy_port = *value;
|
||||
|
||||
value = bpf_map_lookup_elem(&xdp_port_map, &map_key_wg_port);
|
||||
value = bpf_map_lookup_elem(&nb_wg_proxy_settings_map, &map_key_wg_port);
|
||||
if(!value) {
|
||||
return false;
|
||||
}
|
||||
@@ -44,7 +44,7 @@ bool read_port_settings() {
|
||||
}
|
||||
|
||||
SEC("xdp")
|
||||
int xdp_prog_func(struct xdp_md *ctx) {
|
||||
int nb_wg_proxy(struct xdp_md *ctx) {
|
||||
if(proxy_port == 0 || wg_port == 0) {
|
||||
if(!read_port_settings()){
|
||||
return XDP_PASS;
|
||||
|
||||
@@ -14,7 +14,7 @@ func (w *Factory) GetProxy() Proxy {
|
||||
|
||||
func (w *Factory) Free() error {
|
||||
if w.ebpfProxy != nil {
|
||||
return w.ebpfProxy.CloseConn()
|
||||
return w.ebpfProxy.Free()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -104,6 +104,7 @@ func (p *WGEBPFProxy) CloseConn() error {
|
||||
|
||||
// Free resources
|
||||
func (p *WGEBPFProxy) Free() error {
|
||||
log.Debugf("free up ebpf wg proxy")
|
||||
var err1, err2, err3 error
|
||||
if p.conn != nil {
|
||||
err1 = p.conn.Close()
|
||||
|
||||
@@ -20,6 +20,7 @@ type WGUserSpaceProxy struct {
|
||||
|
||||
// NewWGUserSpaceProxy instantiate a user space WireGuard proxy
|
||||
func NewWGUserSpaceProxy(wgPort int) *WGUserSpaceProxy {
|
||||
log.Debugf("instantiate new userspace proxy")
|
||||
p := &WGUserSpaceProxy{
|
||||
localWGListenPort: wgPort,
|
||||
}
|
||||
|
||||
2
go.mod
2
go.mod
@@ -31,7 +31,7 @@ require (
|
||||
fyne.io/fyne/v2 v2.1.4
|
||||
github.com/c-robinson/iplib v1.0.3
|
||||
github.com/cilium/ebpf v0.10.0
|
||||
github.com/coreos/go-iptables v0.6.0
|
||||
github.com/coreos/go-iptables v0.7.0
|
||||
github.com/creack/pty v1.1.18
|
||||
github.com/eko/gocache/v3 v3.1.1
|
||||
github.com/getlantern/systray v1.2.1
|
||||
|
||||
4
go.sum
4
go.sum
@@ -131,8 +131,8 @@ github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcD
|
||||
github.com/coocood/freecache v1.2.1 h1:/v1CqMq45NFH9mp/Pt142reundeBM0dVUD3osQBeu/U=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk=
|
||||
github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
|
||||
github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8=
|
||||
github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
|
||||
|
||||
@@ -395,7 +395,7 @@ check_nb_domain() {
|
||||
read_nb_domain() {
|
||||
READ_NETBIRD_DOMAIN=""
|
||||
echo -n "Enter the domain you want to use for NetBird (e.g. netbird.my-domain.com): " > /dev/stderr
|
||||
read -r READ_NETBIRD_DOMAIN
|
||||
read -r READ_NETBIRD_DOMAIN < /dev/tty
|
||||
if ! check_nb_domain "$READ_NETBIRD_DOMAIN"; then
|
||||
read_nb_domain
|
||||
fi
|
||||
|
||||
@@ -139,10 +139,14 @@ type DefaultAccountManager struct {
|
||||
type Settings struct {
|
||||
// PeerLoginExpirationEnabled globally enables or disables peer login expiration
|
||||
PeerLoginExpirationEnabled bool
|
||||
|
||||
// PeerLoginExpiration is a setting that indicates when peer login expires.
|
||||
// Applies to all peers that have Peer.LoginExpirationEnabled set to true.
|
||||
PeerLoginExpiration time.Duration
|
||||
|
||||
// GroupsPropagationEnabled allows to propagate auto groups from the user to the peer
|
||||
GroupsPropagationEnabled bool
|
||||
|
||||
// JWTGroupsEnabled allows extract groups from JWT claim, which name defined in the JWTGroupsClaimName
|
||||
// and add it to account groups.
|
||||
JWTGroupsEnabled bool
|
||||
@@ -158,6 +162,7 @@ func (s *Settings) Copy() *Settings {
|
||||
PeerLoginExpiration: s.PeerLoginExpiration,
|
||||
JWTGroupsEnabled: s.JWTGroupsEnabled,
|
||||
JWTGroupsClaimName: s.JWTGroupsClaimName,
|
||||
GroupsPropagationEnabled: s.GroupsPropagationEnabled,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,26 +629,96 @@ func (a *Account) GetPeer(peerID string) *Peer {
|
||||
return a.Peers[peerID]
|
||||
}
|
||||
|
||||
// AddJWTGroups to existed groups if they does not exists
|
||||
func (a *Account) AddJWTGroups(groups []string) (int, error) {
|
||||
existedGroups := make(map[string]*Group)
|
||||
for _, g := range a.Groups {
|
||||
existedGroups[g.Name] = g
|
||||
// AddJWTGroups to account and to user autoassigned groups
|
||||
func (a *Account) AddJWTGroups(userID string, groups []string) bool {
|
||||
user, ok := a.Users[userID]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
var count int
|
||||
existedGroupsByName := make(map[string]*Group)
|
||||
for _, group := range a.Groups {
|
||||
existedGroupsByName[group.Name] = group
|
||||
}
|
||||
|
||||
autoGroups := make(map[string]struct{})
|
||||
for _, groupID := range user.AutoGroups {
|
||||
autoGroups[groupID] = struct{}{}
|
||||
}
|
||||
|
||||
var modified bool
|
||||
for _, name := range groups {
|
||||
if _, ok := existedGroups[name]; !ok {
|
||||
id := xid.New().String()
|
||||
a.Groups[id] = &Group{
|
||||
ID: id,
|
||||
group, ok := existedGroupsByName[name]
|
||||
if !ok {
|
||||
group = &Group{
|
||||
ID: xid.New().String(),
|
||||
Name: name,
|
||||
Issued: GroupIssuedJWT,
|
||||
}
|
||||
count++
|
||||
a.Groups[group.ID] = group
|
||||
modified = true
|
||||
}
|
||||
if _, ok := autoGroups[group.ID]; !ok {
|
||||
if group.Issued == GroupIssuedJWT {
|
||||
user.AutoGroups = append(user.AutoGroups, group.ID)
|
||||
modified = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return count, nil
|
||||
|
||||
return modified
|
||||
}
|
||||
|
||||
// UserGroupsAddToPeers adds groups to all peers of user
|
||||
func (a *Account) UserGroupsAddToPeers(userID string, groups ...string) {
|
||||
userPeers := make(map[string]struct{})
|
||||
for pid, peer := range a.Peers {
|
||||
if peer.UserID == userID {
|
||||
userPeers[pid] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
for _, gid := range groups {
|
||||
group, ok := a.Groups[gid]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
groupPeers := make(map[string]struct{})
|
||||
for _, pid := range group.Peers {
|
||||
groupPeers[pid] = struct{}{}
|
||||
}
|
||||
|
||||
for pid := range userPeers {
|
||||
groupPeers[pid] = struct{}{}
|
||||
}
|
||||
|
||||
group.Peers = group.Peers[:0]
|
||||
for pid := range groupPeers {
|
||||
group.Peers = append(group.Peers, pid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UserGroupsRemoveFromPeers removes groups from all peers of user
|
||||
func (a *Account) UserGroupsRemoveFromPeers(userID string, groups ...string) {
|
||||
for _, gid := range groups {
|
||||
group, ok := a.Groups[gid]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
update := make([]string, 0, len(group.Peers))
|
||||
for _, pid := range group.Peers {
|
||||
peer, ok := a.Peers[pid]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if peer.UserID != userID {
|
||||
update = append(update, pid)
|
||||
}
|
||||
}
|
||||
group.Peers = update
|
||||
}
|
||||
}
|
||||
|
||||
// BuildManager creates a new DefaultAccountManager with a provided Store
|
||||
@@ -1290,11 +1365,13 @@ func (am *DefaultAccountManager) GetAccountFromToken(claims jwtclaims.Authorizat
|
||||
log.Errorf("JWT claim %q is not a string: %v", account.Settings.JWTGroupsClaimName, item)
|
||||
}
|
||||
}
|
||||
n, err := account.AddJWTGroups(groups)
|
||||
if err != nil {
|
||||
log.Errorf("failed to add JWT groups: %v", err)
|
||||
}
|
||||
if n > 0 {
|
||||
// if groups were added or modified, save the account
|
||||
if account.AddJWTGroups(claims.UserId, groups) {
|
||||
if account.Settings.GroupsPropagationEnabled {
|
||||
if user, err := account.FindUser(claims.UserId); err == nil {
|
||||
account.UserGroupsAddToPeers(claims.UserId, append(user.AutoGroups, groups...)...)
|
||||
}
|
||||
}
|
||||
if err := am.Store.SaveAccount(account); err != nil {
|
||||
log.Errorf("failed to save account: %v", err)
|
||||
}
|
||||
|
||||
@@ -216,7 +216,6 @@ func TestAccount_GetPeerNetworkMap(t *testing.T) {
|
||||
assert.Len(t, networkMap.Peers, len(testCase.expectedPeers))
|
||||
assert.Len(t, networkMap.OfflinePeers, len(testCase.expectedOfflinePeers))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestNewAccount(t *testing.T) {
|
||||
@@ -1931,6 +1930,120 @@ func TestAccount_GetNextPeerExpiration(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccount_AddJWTGroups(t *testing.T) {
|
||||
// create a new account
|
||||
account := &Account{
|
||||
Peers: map[string]*Peer{
|
||||
"peer1": {ID: "peer1", Key: "key1", UserID: "user1"},
|
||||
"peer2": {ID: "peer2", Key: "key2", UserID: "user1"},
|
||||
"peer3": {ID: "peer3", Key: "key3", UserID: "user1"},
|
||||
"peer4": {ID: "peer4", Key: "key4", UserID: "user2"},
|
||||
"peer5": {ID: "peer5", Key: "key5", UserID: "user2"},
|
||||
},
|
||||
Groups: map[string]*Group{
|
||||
"group1": {ID: "group1", Name: "group1", Issued: GroupIssuedAPI, Peers: []string{}},
|
||||
},
|
||||
Settings: &Settings{GroupsPropagationEnabled: true},
|
||||
Users: map[string]*User{
|
||||
"user1": {Id: "user1"},
|
||||
"user2": {Id: "user2"},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("api group already exists", func(t *testing.T) {
|
||||
updated := account.AddJWTGroups("user1", []string{"group1"})
|
||||
assert.False(t, updated, "account should not be updated")
|
||||
assert.Empty(t, account.Users["user1"].AutoGroups, "auto groups must be empty")
|
||||
})
|
||||
|
||||
t.Run("add jwt group", func(t *testing.T) {
|
||||
updated := account.AddJWTGroups("user1", []string{"group1", "group2"})
|
||||
assert.True(t, updated, "account should be updated")
|
||||
assert.Len(t, account.Groups, 2, "new group should be added")
|
||||
assert.Len(t, account.Users["user1"].AutoGroups, 1, "new group should be added")
|
||||
assert.Contains(t, account.Groups, account.Users["user1"].AutoGroups[0], "groups must contain group2 from user groups")
|
||||
})
|
||||
|
||||
t.Run("existed group not update", func(t *testing.T) {
|
||||
updated := account.AddJWTGroups("user1", []string{"group2"})
|
||||
assert.False(t, updated, "account should not be updated")
|
||||
assert.Len(t, account.Groups, 2, "groups count should not be changed")
|
||||
})
|
||||
|
||||
t.Run("add new group", func(t *testing.T) {
|
||||
updated := account.AddJWTGroups("user2", []string{"group1", "group3"})
|
||||
assert.True(t, updated, "account should be updated")
|
||||
assert.Len(t, account.Groups, 3, "new group should be added")
|
||||
assert.Len(t, account.Users["user2"].AutoGroups, 1, "new group should be added")
|
||||
assert.Contains(t, account.Groups, account.Users["user2"].AutoGroups[0], "groups must contain group3 from user groups")
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccount_UserGroupsAddToPeers(t *testing.T) {
|
||||
account := &Account{
|
||||
Peers: map[string]*Peer{
|
||||
"peer1": {ID: "peer1", Key: "key1", UserID: "user1"},
|
||||
"peer2": {ID: "peer2", Key: "key2", UserID: "user1"},
|
||||
"peer3": {ID: "peer3", Key: "key3", UserID: "user1"},
|
||||
"peer4": {ID: "peer4", Key: "key4", UserID: "user2"},
|
||||
"peer5": {ID: "peer5", Key: "key5", UserID: "user2"},
|
||||
},
|
||||
Groups: map[string]*Group{
|
||||
"group1": {ID: "group1", Name: "group1", Issued: GroupIssuedAPI, Peers: []string{}},
|
||||
"group2": {ID: "group2", Name: "group2", Issued: GroupIssuedAPI, Peers: []string{}},
|
||||
"group3": {ID: "group3", Name: "group3", Issued: GroupIssuedAPI, Peers: []string{}},
|
||||
},
|
||||
Users: map[string]*User{"user1": {Id: "user1"}, "user2": {Id: "user2"}},
|
||||
}
|
||||
|
||||
t.Run("add groups", func(t *testing.T) {
|
||||
account.UserGroupsAddToPeers("user1", "group1", "group2")
|
||||
assert.ElementsMatch(t, account.Groups["group1"].Peers, []string{"peer1", "peer2", "peer3"}, "group1 contains users peers")
|
||||
assert.ElementsMatch(t, account.Groups["group2"].Peers, []string{"peer1", "peer2", "peer3"}, "group2 contains users peers")
|
||||
})
|
||||
|
||||
t.Run("add same groups", func(t *testing.T) {
|
||||
account.UserGroupsAddToPeers("user1", "group1", "group2")
|
||||
assert.Len(t, account.Groups["group1"].Peers, 3, "peers amount in group1 didn't change")
|
||||
assert.Len(t, account.Groups["group2"].Peers, 3, "peers amount in group2 didn't change")
|
||||
})
|
||||
|
||||
t.Run("add second user peers", func(t *testing.T) {
|
||||
account.UserGroupsAddToPeers("user2", "group2")
|
||||
assert.ElementsMatch(t, account.Groups["group2"].Peers,
|
||||
[]string{"peer1", "peer2", "peer3", "peer4", "peer5"}, "group2 contains first and second user peers")
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccount_UserGroupsRemoveFromPeers(t *testing.T) {
|
||||
account := &Account{
|
||||
Peers: map[string]*Peer{
|
||||
"peer1": {ID: "peer1", Key: "key1", UserID: "user1"},
|
||||
"peer2": {ID: "peer2", Key: "key2", UserID: "user1"},
|
||||
"peer3": {ID: "peer3", Key: "key3", UserID: "user1"},
|
||||
"peer4": {ID: "peer4", Key: "key4", UserID: "user2"},
|
||||
"peer5": {ID: "peer5", Key: "key5", UserID: "user2"},
|
||||
},
|
||||
Groups: map[string]*Group{
|
||||
"group1": {ID: "group1", Name: "group1", Issued: GroupIssuedAPI, Peers: []string{"peer1", "peer2", "peer3"}},
|
||||
"group2": {ID: "group2", Name: "group2", Issued: GroupIssuedAPI, Peers: []string{"peer1", "peer2", "peer3", "peer4", "peer5"}},
|
||||
"group3": {ID: "group3", Name: "group3", Issued: GroupIssuedAPI, Peers: []string{"peer4", "peer5"}},
|
||||
},
|
||||
Users: map[string]*User{"user1": {Id: "user1"}, "user2": {Id: "user2"}},
|
||||
}
|
||||
|
||||
t.Run("remove groups", func(t *testing.T) {
|
||||
account.UserGroupsRemoveFromPeers("user1", "group1", "group2")
|
||||
assert.Empty(t, account.Groups["group1"].Peers, "remove all peers from group1")
|
||||
assert.ElementsMatch(t, account.Groups["group2"].Peers, []string{"peer4", "peer5"}, "group2 contains only second users peers")
|
||||
})
|
||||
|
||||
t.Run("remove group with no peers", func(t *testing.T) {
|
||||
account.UserGroupsRemoveFromPeers("user1", "group3")
|
||||
assert.Len(t, account.Groups["group3"].Peers, 2, "peers amount should not change")
|
||||
})
|
||||
}
|
||||
|
||||
func createManager(t *testing.T) (*DefaultAccountManager, error) {
|
||||
store, err := createStore(t)
|
||||
if err != nil {
|
||||
|
||||
@@ -80,12 +80,14 @@ func (h *AccountsHandler) UpdateAccount(w http.ResponseWriter, r *http.Request)
|
||||
if req.Settings.JwtGroupsEnabled != nil {
|
||||
settings.JWTGroupsEnabled = *req.Settings.JwtGroupsEnabled
|
||||
}
|
||||
if req.Settings.GroupsPropagationEnabled != nil {
|
||||
settings.GroupsPropagationEnabled = *req.Settings.GroupsPropagationEnabled
|
||||
}
|
||||
if req.Settings.JwtGroupsClaimName != nil {
|
||||
settings.JWTGroupsClaimName = *req.Settings.JwtGroupsClaimName
|
||||
}
|
||||
|
||||
updatedAccount, err := h.accountManager.UpdateAccountSettings(accountID, user.Id, settings)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(err, w)
|
||||
return
|
||||
@@ -102,6 +104,7 @@ func toAccountResponse(account *server.Account) *api.Account {
|
||||
Settings: api.AccountSettings{
|
||||
PeerLoginExpiration: int(account.Settings.PeerLoginExpiration.Seconds()),
|
||||
PeerLoginExpirationEnabled: account.Settings.PeerLoginExpirationEnabled,
|
||||
GroupsPropagationEnabled: &account.Settings.GroupsPropagationEnabled,
|
||||
JwtGroupsEnabled: &account.Settings.JWTGroupsEnabled,
|
||||
JwtGroupsClaimName: &account.Settings.JWTGroupsClaimName,
|
||||
},
|
||||
|
||||
@@ -38,7 +38,6 @@ func initAccountsTestData(account *server.Account, admin *server.User) *Accounts
|
||||
accCopy := account.Copy()
|
||||
accCopy.UpdateSettings(newSettings)
|
||||
return accCopy, nil
|
||||
|
||||
},
|
||||
},
|
||||
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||
@@ -54,7 +53,6 @@ func initAccountsTestData(account *server.Account, admin *server.User) *Accounts
|
||||
}
|
||||
|
||||
func TestAccounts_AccountsHandler(t *testing.T) {
|
||||
|
||||
accountID := "test_account"
|
||||
adminUser := server.NewAdminUser("test_user")
|
||||
|
||||
@@ -94,6 +92,7 @@ func TestAccounts_AccountsHandler(t *testing.T) {
|
||||
expectedSettings: api.AccountSettings{
|
||||
PeerLoginExpiration: int(time.Hour.Seconds()),
|
||||
PeerLoginExpirationEnabled: false,
|
||||
GroupsPropagationEnabled: br(false),
|
||||
JwtGroupsClaimName: sr(""),
|
||||
JwtGroupsEnabled: br(false),
|
||||
},
|
||||
@@ -110,6 +109,7 @@ func TestAccounts_AccountsHandler(t *testing.T) {
|
||||
expectedSettings: api.AccountSettings{
|
||||
PeerLoginExpiration: 15552000,
|
||||
PeerLoginExpirationEnabled: true,
|
||||
GroupsPropagationEnabled: br(false),
|
||||
JwtGroupsClaimName: sr(""),
|
||||
JwtGroupsEnabled: br(false),
|
||||
},
|
||||
@@ -126,12 +126,30 @@ func TestAccounts_AccountsHandler(t *testing.T) {
|
||||
expectedSettings: api.AccountSettings{
|
||||
PeerLoginExpiration: 15552000,
|
||||
PeerLoginExpirationEnabled: false,
|
||||
GroupsPropagationEnabled: br(false),
|
||||
JwtGroupsClaimName: sr("roles"),
|
||||
JwtGroupsEnabled: br(true),
|
||||
},
|
||||
expectedArray: false,
|
||||
expectedID: accountID,
|
||||
},
|
||||
{
|
||||
name: "PutAccount OK wiht JWT Propagation",
|
||||
expectedBody: true,
|
||||
requestType: http.MethodPut,
|
||||
requestPath: "/api/accounts/" + accountID,
|
||||
requestBody: bytes.NewBufferString("{\"settings\": {\"peer_login_expiration\": 554400,\"peer_login_expiration_enabled\": true,\"jwt_groups_enabled\":true,\"jwt_groups_claim_name\":\"groups\",\"groups_propagation_enabled\":true}}"),
|
||||
expectedStatus: http.StatusOK,
|
||||
expectedSettings: api.AccountSettings{
|
||||
PeerLoginExpiration: 554400,
|
||||
PeerLoginExpirationEnabled: true,
|
||||
GroupsPropagationEnabled: br(true),
|
||||
JwtGroupsClaimName: sr("groups"),
|
||||
JwtGroupsEnabled: br(true),
|
||||
},
|
||||
expectedArray: false,
|
||||
expectedID: accountID,
|
||||
},
|
||||
{
|
||||
name: "Update account failure with high peer_login_expiration more than 180 days",
|
||||
expectedBody: true,
|
||||
|
||||
@@ -54,6 +54,10 @@ components:
|
||||
description: Period of time after which peer login expires (seconds).
|
||||
type: integer
|
||||
example: 43200
|
||||
groups_propagation_enabled:
|
||||
description: Allows propagate the new user auto groups to peers that belongs to the user
|
||||
type: boolean
|
||||
example: true
|
||||
jwt_groups_enabled:
|
||||
description: Allows extract groups from JWT claim and add it to account groups.
|
||||
type: boolean
|
||||
|
||||
@@ -129,6 +129,9 @@ type AccountRequest struct {
|
||||
|
||||
// AccountSettings defines model for AccountSettings.
|
||||
type AccountSettings struct {
|
||||
// GroupsPropagationEnabled Allows propagate the new user auto groups to peers that belongs to the user
|
||||
GroupsPropagationEnabled *bool `json:"groups_propagation_enabled,omitempty"`
|
||||
|
||||
// JwtGroupsClaimName Name of the claim from which we extract groups names to add it to account groups.
|
||||
JwtGroupsClaimName *string `json:"jwt_groups_claim_name,omitempty"`
|
||||
|
||||
|
||||
@@ -260,7 +260,6 @@ func (am *DefaultAccountManager) inviteNewUser(accountID, userID string, invite
|
||||
am.storeEvent(userID, newUser.Id, accountID, activity.UserInvited, nil)
|
||||
|
||||
return newUser.ToUserInfo(idpUser)
|
||||
|
||||
}
|
||||
|
||||
// GetUser looks up a user by provided authorization claims.
|
||||
@@ -600,6 +599,13 @@ func (am *DefaultAccountManager) SaveUser(accountID, initiatorUserID string, upd
|
||||
}
|
||||
}
|
||||
|
||||
if update.AutoGroups != nil && account.Settings.GroupsPropagationEnabled {
|
||||
removedGroups := difference(oldUser.AutoGroups, update.AutoGroups)
|
||||
// need force update all auto groups in any case they will not be dublicated
|
||||
account.UserGroupsAddToPeers(oldUser.Id, update.AutoGroups...)
|
||||
account.UserGroupsRemoveFromPeers(oldUser.Id, removedGroups...)
|
||||
}
|
||||
|
||||
if err = am.Store.SaveAccount(account); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -640,7 +646,6 @@ func (am *DefaultAccountManager) SaveUser(accountID, initiatorUserID string, upd
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
if !isNil(am.idpManager) && !newUser.IsServiceUser {
|
||||
|
||||
Reference in New Issue
Block a user