Skip to content

Commit 671e9a0

Browse files
Merge pull request #1061 from NethServer/bug-7836
feat(api-server): support assigning multiple roles
2 parents 7934e7d + c0985e9 commit 671e9a0

File tree

7 files changed

+61
-77
lines changed

7 files changed

+61
-77
lines changed

core/api-server/methods/auth.go

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -115,25 +115,33 @@ func RedisAuthorization(username string, c *gin.Context) (models.UserAuthorizati
115115
pathScan = "module/" + c.Param("module_id") + "/roles/"
116116
}
117117

118-
// get roles of current user: HGET roles/<username>.<entity> -> <role>
119-
role, errRedisRoleGet := redisConnection.HGet(ctx, "roles/"+username, pathGet).Result()
118+
// get roles of current user: HGET roles/<username> -> <role(s)>
119+
roles, errRedisRoleGet := redisConnection.HGet(ctx, "roles/"+username, pathGet).Result()
120120

121121
// handle redis error
122122
if errRedisRoleGet != nil {
123123
return userAuthorizationsRedis, errRedisRoleGet
124124
}
125125

126-
// get action for current role and entity: SMEMBERS <entity>/<reference>/roles/<role>
127-
actions, errRedisRoleScan := redisConnection.SMembers(ctx, pathScan+role).Result()
128-
129-
// handle redis error
130-
if errRedisRoleScan != nil {
131-
return userAuthorizationsRedis, errRedisRoleScan
126+
// get actions for each role and entity: SMEMBERS <entity>/<reference>/roles/<role>
127+
var actions []string
128+
roleList := strings.Split(roles, ",")
129+
for _, r := range roleList {
130+
r = strings.TrimSpace(r)
131+
if r == "" {
132+
continue
133+
}
134+
a, errRedisRoleScan := redisConnection.SMembers(ctx, pathScan+r).Result()
135+
if errRedisRoleScan != nil {
136+
return userAuthorizationsRedis, errRedisRoleScan
137+
}
138+
// duplicated values are allowed, since Authorizator checks only existence
139+
actions = append(actions, a...)
132140
}
133141

134142
// compose user authorizations
135143
userAuthorizationsRedis.Username = username
136-
userAuthorizationsRedis.Role = role
144+
userAuthorizationsRedis.Role = roles // keep original string of raw role list
137145
userAuthorizationsRedis.Actions = actions
138146

139147
// close redis connection

core/imageroot/usr/local/agent/pypkg/cluster/grants.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -84,19 +84,29 @@ def alter_user(rdb, user, revoke, role, on_clause):
8484
"""
8585
Grant/Revoke the ROLE ON some module to USER
8686
87-
The module can be cluster, node/X, module/modY etc.
88-
The ROLE must be already defined in the given module.
87+
The module matcher, specified by ON_CLAUSE, can be cluster, node/X,
88+
module/modY etc. Wildcard match is allowed.
89+
90+
ROLE is a comma-separated list of role names without spaces. Each role
91+
name must be already defined in the matching module(s).
8992
"""
9093

94+
targets = set()
95+
for xrole in role.split(","):
96+
# Make sure there is no white space around the role name:
97+
xrole = xrole.strip()
98+
if not xrole:
99+
continue
100+
for key in rdb.scan_iter(f'{on_clause}/roles/{xrole}'):
101+
agent_id = key.removesuffix(f'/roles/{xrole}')
102+
targets.add(agent_id)
103+
91104
pipe = rdb.pipeline()
92-
for key in rdb.scan_iter(f'{on_clause}/roles/{role}'):
93-
pos = key.find(f'/roles/{role}')
94-
agent_id = key[:pos]
105+
for agent_id in targets:
95106
if revoke:
96-
pipe.hdel(f'roles/{user}', agent_id, role)
107+
pipe.hdel(f'roles/{user}', agent_id)
97108
else:
98109
pipe.hset(f'roles/{user}', agent_id, role)
99-
100110
pipe.execute()
101111

102112
def refresh_permissions(rdb):
@@ -110,7 +120,8 @@ def refresh_permissions(rdb):
110120
def add_module_permissions(rdb, module_id, authorizations, node_id):
111121
"""Parse authorizations and grant permissions to module_id"""
112122
for authz in authorizations:
113-
xagent, xrole = authz.split(':') # e.g. traefik@node:routeadm
123+
# authz examples: "traefik@node:routeadm", "node:fwadm,portsadm"
124+
xagent, xrole = authz.split(':')
114125

115126
if xagent.endswith("@any"):
116127
agent_selector = 'module/' + xagent.removesuffix("@any") + '*' # wildcard allowed in on_clause
@@ -119,6 +130,10 @@ def add_module_permissions(rdb, module_id, authorizations, node_id):
119130
else:
120131
agent_selector = agent.resolve_agent_id(xagent, node_id=node_id)
121132

133+
# If the agent_selector resolves multiple times to the same value,
134+
# the last alter_user() call wins: roles are not accumulated or
135+
# merged across calls. Still it is allowed to pass a
136+
# comma-separated list of roles as xrole value.
122137
alter_user(rdb,
123138
user=f'module/{module_id}',
124139
revoke=False,

core/imageroot/var/lib/nethserver/cluster/actions/add-node/50update

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -186,32 +186,16 @@ cluster.grants.grant(rdb, "add-custom-zone", f'node/{node_id}', "fwadm")
186186
cluster.grants.grant(rdb, "remove-custom-zone", f'node/{node_id}', "fwadm")
187187
cluster.grants.grant(rdb, "add-rich-rules", f'node/{node_id}', "fwadm")
188188
cluster.grants.grant(rdb, "remove-rich-rules", f'node/{node_id}', "fwadm")
189-
189+
cluster.grants.grant(rdb, "add-tun", f'node/{node_id}', "tunadm")
190+
cluster.grants.grant(rdb, "remove-tun", f'node/{node_id}', "tunadm")
190191
cluster.grants.grant(rdb, "add-public-service", f'node/{node_id}', "tunadm")
191192
cluster.grants.grant(rdb, "remove-public-service", f'node/{node_id}', "tunadm")
192193
cluster.grants.grant(rdb, "add-custom-zone", f'node/{node_id}', "tunadm")
193194
cluster.grants.grant(rdb, "remove-custom-zone", f'node/{node_id}', "tunadm")
194-
cluster.grants.grant(rdb, "add-tun", f'node/{node_id}', "tunadm")
195-
cluster.grants.grant(rdb, "remove-tun", f'node/{node_id}', "tunadm")
196-
195+
cluster.grants.grant(rdb, "add-rich-rules", f'node/{node_id}', "tunadm")
196+
cluster.grants.grant(rdb, "remove-rich-rules", f'node/{node_id}', "tunadm")
197197
cluster.grants.grant(rdb, "allocate-ports", f'node/{node_id}', "portsadm")
198198
cluster.grants.grant(rdb, "deallocate-ports", f'node/{node_id}', "portsadm")
199-
cluster.grants.grant(rdb, "allocate-ports", f'node/{node_id}', "fwadm,portsadm")
200-
cluster.grants.grant(rdb, "deallocate-ports", f'node/{node_id}', "fwadm,portsadm")
201-
cluster.grants.grant(rdb, "add-public-service", f'node/{node_id}', "fwadm,portsadm")
202-
cluster.grants.grant(rdb, "remove-public-service", f'node/{node_id}', "fwadm,portsadm")
203-
cluster.grants.grant(rdb, "add-custom-zone", f'node/{node_id}', "fwadm,portsadm")
204-
cluster.grants.grant(rdb, "remove-custom-zone", f'node/{node_id}', "fwadm,portsadm")
205-
cluster.grants.grant(rdb, "add-rich-rules", f'node/{node_id}', "fwadm,portsadm")
206-
cluster.grants.grant(rdb, "remove-rich-rules", f'node/{node_id}', "fwadm,portsadm")
207-
cluster.grants.grant(rdb, "allocate-ports", f'node/{node_id}', "tunadm,portsadm")
208-
cluster.grants.grant(rdb, "deallocate-ports", f'node/{node_id}', "tunadm,portsadm")
209-
cluster.grants.grant(rdb, "add-tun", f'node/{node_id}', "tunadm,portsadm")
210-
cluster.grants.grant(rdb, "remove-tun", f'node/{node_id}', "tunadm,portsadm")
211-
cluster.grants.grant(rdb, "add-public-service", f'node/{node_id}', "tunadm,portsadm")
212-
cluster.grants.grant(rdb, "remove-public-service", f'node/{node_id}', "tunadm,portsadm")
213-
cluster.grants.grant(rdb, "add-custom-zone", f'node/{node_id}', "tunadm,portsadm")
214-
cluster.grants.grant(rdb, "remove-custom-zone", f'node/{node_id}', "tunadm,portsadm")
215199

216200
# Grant on cascade the owner role on the new node, to users with the owner
217201
# role on cluster

core/imageroot/var/lib/nethserver/cluster/update-core-pre-modules.d/50update_grants

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,20 @@ cluster.grants.grant(rdb, action_clause="bind-user-domains", to_clause="account
1919
cluster.grants.grant(rdb, action_clause="bind-user-domains", to_clause="accountprovider", on_clause='cluster')
2020
cluster.grants.grant(rdb, action_clause="list-modules", to_clause="accountprovider", on_clause='cluster')
2121

22-
#
23-
# Reuse and reallocate TCP/UDP port range #6974
24-
#
2522
for node_id in set(rdb.hvals('cluster/module_node')):
23+
# Reuse and reallocate TCP/UDP port range #6974:
2624
cluster.grants.grant(rdb, "allocate-ports", f'node/{node_id}', "portsadm")
2725
cluster.grants.grant(rdb, "deallocate-ports", f'node/{node_id}', "portsadm")
28-
cluster.grants.grant(rdb, "allocate-ports", f'node/{node_id}', "fwadm,portsadm")
29-
cluster.grants.grant(rdb, "deallocate-ports", f'node/{node_id}', "fwadm,portsadm")
30-
cluster.grants.grant(rdb, "add-public-service", f'node/{node_id}', "fwadm,portsadm")
31-
cluster.grants.grant(rdb, "remove-public-service", f'node/{node_id}', "fwadm,portsadm")
32-
cluster.grants.grant(rdb, "add-custom-zone", f'node/{node_id}', "fwadm,portsadm")
33-
cluster.grants.grant(rdb, "remove-custom-zone", f'node/{node_id}', "fwadm,portsadm")
34-
cluster.grants.grant(rdb, "allocate-ports", f'node/{node_id}', "tunadm,portsadm")
35-
cluster.grants.grant(rdb, "deallocate-ports", f'node/{node_id}', "tunadm,portsadm")
36-
cluster.grants.grant(rdb, "add-tun", f'node/{node_id}', "tunadm,portsadm")
37-
cluster.grants.grant(rdb, "remove-tun", f'node/{node_id}', "tunadm,portsadm")
38-
cluster.grants.grant(rdb, "add-public-service", f'node/{node_id}', "tunadm,portsadm")
39-
cluster.grants.grant(rdb, "remove-public-service", f'node/{node_id}', "tunadm,portsadm")
40-
cluster.grants.grant(rdb, "add-custom-zone", f'node/{node_id}', "tunadm,portsadm")
41-
cluster.grants.grant(rdb, "remove-custom-zone", f'node/{node_id}', "tunadm,portsadm")
26+
# Fix rich rules management #7836:
27+
cluster.grants.grant(rdb, "add-rich-rules", f'node/{node_id}', "fwadm")
28+
cluster.grants.grant(rdb, "remove-rich-rules", f'node/{node_id}', "fwadm")
29+
cluster.grants.grant(rdb, "add-rich-rules", f'node/{node_id}', "tunadm")
30+
cluster.grants.grant(rdb, "remove-rich-rules", f'node/{node_id}', "tunadm")
31+
rdb.delete(
32+
f'node/{node_id}/roles/fwadm,portsadm',
33+
f'node/{node_id}/roles/tunadm,portsadm',
34+
)
35+
4236
#
4337
# END of grant updates
4438
#

core/imageroot/var/lib/nethserver/node/install-finalize.sh

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -121,33 +121,18 @@ cluster.grants.grant(rdb, action_clause="add-public-service", to_clause="fwadm"
121121
cluster.grants.grant(rdb, action_clause="remove-public-service", to_clause="fwadm", on_clause='node/1')
122122
cluster.grants.grant(rdb, action_clause="add-custom-zone", to_clause="fwadm", on_clause='node/1')
123123
cluster.grants.grant(rdb, action_clause="remove-custom-zone", to_clause="fwadm", on_clause='node/1')
124-
124+
cluster.grants.grant(rdb, action_clause="add-rich-rules", to_clause="fwadm", on_clause='node/1')
125+
cluster.grants.grant(rdb, action_clause="remove-rich-rules", to_clause="fwadm", on_clause='node/1')
125126
cluster.grants.grant(rdb, action_clause="add-tun", to_clause="tunadm", on_clause='node/1')
126127
cluster.grants.grant(rdb, action_clause="remove-tun", to_clause="tunadm", on_clause='node/1')
127128
cluster.grants.grant(rdb, action_clause="add-public-service", to_clause="tunadm", on_clause='node/1')
128129
cluster.grants.grant(rdb, action_clause="remove-public-service", to_clause="tunadm", on_clause='node/1')
129130
cluster.grants.grant(rdb, action_clause="add-custom-zone", to_clause="tunadm", on_clause='node/1')
130131
cluster.grants.grant(rdb, action_clause="remove-custom-zone", to_clause="tunadm", on_clause='node/1')
131-
132+
cluster.grants.grant(rdb, action_clause="add-rich-rules", to_clause="tunadm", on_clause='node/1')
133+
cluster.grants.grant(rdb, action_clause="remove-rich-rules", to_clause="tunadm", on_clause='node/1')
132134
cluster.grants.grant(rdb, action_clause="allocate-ports", to_clause="portsadm", on_clause='node/1')
133135
cluster.grants.grant(rdb, action_clause="deallocate-ports", to_clause="portsadm", on_clause='node/1')
134-
cluster.grants.grant(rdb, action_clause="allocate-ports", to_clause="fwadm,portsadm", on_clause='node/1')
135-
cluster.grants.grant(rdb, action_clause="deallocate-ports", to_clause="fwadm,portsadm", on_clause='node/1')
136-
cluster.grants.grant(rdb, action_clause="add-public-service", to_clause="fwadm,portsadm", on_clause='node/1')
137-
cluster.grants.grant(rdb, action_clause="remove-public-service", to_clause="fwadm,portsadm", on_clause='node/1')
138-
cluster.grants.grant(rdb, action_clause="add-custom-zone", to_clause="fwadm,portsadm", on_clause='node/1')
139-
cluster.grants.grant(rdb, action_clause="remove-custom-zone", to_clause="fwadm,portsadm", on_clause='node/1')
140-
cluster.grants.grant(rdb, action_clause="allocate-ports", to_clause="tunadm,portsadm", on_clause='node/1')
141-
cluster.grants.grant(rdb, action_clause="deallocate-ports", to_clause="tunadm,portsadm", on_clause='node/1')
142-
cluster.grants.grant(rdb, action_clause="add-tun", to_clause="tunadm,portsadm", on_clause='node/1')
143-
cluster.grants.grant(rdb, action_clause="remove-tun", to_clause="tunadm,portsadm", on_clause='node/1')
144-
cluster.grants.grant(rdb, action_clause="add-public-service", to_clause="tunadm,portsadm", on_clause='node/1')
145-
cluster.grants.grant(rdb, action_clause="remove-public-service", to_clause="tunadm,portsadm", on_clause='node/1')
146-
cluster.grants.grant(rdb, action_clause="add-custom-zone", to_clause="tunadm,portsadm", on_clause='node/1')
147-
cluster.grants.grant(rdb, action_clause="remove-custom-zone", to_clause="tunadm,portsadm", on_clause='node/1')
148-
cluster.grants.grant(rdb, action_clause="add-rich-rules", to_clause="fwadm,portsadm", on_clause='node/1')
149-
cluster.grants.grant(rdb, action_clause="remove-rich-rules", to_clause="fwadm,portsadm", on_clause='node/1')
150-
151136
cluster.grants.grant(rdb, action_clause="update-routes", to_clause="accountprovider", on_clause='cluster')
152137
cluster.grants.grant(rdb, action_clause="bind-user-domains", to_clause="accountconsumer", on_clause='cluster')
153138
cluster.grants.grant(rdb, action_clause="bind-user-domains", to_clause="accountprovider", on_clause='cluster')

docs/core/tun.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ be authorized to use it, by adding `node:tunadm` to the module image label
1818

1919
org.nethserver.authorizations=node:tunadm
2020

21-
Please note that the _tunadm_ authorization includes also the [_fwadm_](../firewall) one.
21+
Please note that for backward compatibility with core 3.16 the _tunadm_
22+
authorization includes also the [_fwadm_](../firewall) one.
2223

2324
Then the module actions must use the `agent`
2425
Python package to add/remove the tun device needed by the

docs/modules/port_allocation.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,6 @@ with other existing node-related roles:
4040
- `org.nethserver.authorizations = node:fwadm,portsadm`
4141
- `org.nethserver.authorizations = node:tunadm,portsadm`
4242

43-
Note that the value must be exactly one of the above. Other combinations
44-
like `node:portsadm,fwadm` are not valid.
45-
4643
The module will be granted execution permissions for the following actions
4744
on the local node:
4845
- `allocate-ports`

0 commit comments

Comments
 (0)