|
14 | 14 | # You should have received a copy of the GNU General Public License
|
15 | 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16 | 16 |
|
| 17 | +import re |
17 | 18 | import os
|
| 19 | +import json |
18 | 20 |
|
19 | 21 | from passlib.hosts import linux_context
|
20 | 22 | from psutil import users
|
@@ -171,6 +173,15 @@ def verify(login):
|
171 | 173 | if 'key' not in pubkey_options:
|
172 | 174 | raise ConfigError(f'Missing key for public-key "{pubkey}"!')
|
173 | 175 |
|
| 176 | + if 'operator' in user_config: |
| 177 | + op_groups = dict_search('operator.group', user_config) |
| 178 | + if op_groups: |
| 179 | + for og in op_groups: |
| 180 | + if dict_search(f'operator_group.{og}', login) is None: |
| 181 | + raise ConfigError(f'Operator group {og} does not exist') |
| 182 | + else: |
| 183 | + raise ConfigError(f'User {user} is configured as an operator but is not assigned to any operator groups') |
| 184 | + |
174 | 185 | if {'radius', 'tacacs'} <= set(login):
|
175 | 186 | raise ConfigError('Using both RADIUS and TACACS at the same time is not supported!')
|
176 | 187 |
|
@@ -306,6 +317,28 @@ def generate(login):
|
306 | 317 | if os.path.isfile(autologout_file):
|
307 | 318 | os.unlink(autologout_file)
|
308 | 319 |
|
| 320 | + # Operator groups and group membership |
| 321 | + operator_config = {'users': {}, 'groups': {}} |
| 322 | + if 'user' in login: |
| 323 | + for user, user_config in login['user'].items(): |
| 324 | + op_groups = dict_search('operator.group', user_config) |
| 325 | + if op_groups: |
| 326 | + operator_config['users'][user] = op_groups |
| 327 | + |
| 328 | + if 'operator_group' in login: |
| 329 | + operator_config['groups'] = login['operator_group'] |
| 330 | + |
| 331 | + # Convert permissions strings to list |
| 332 | + # so that the operational command runner doesn't have to |
| 333 | + for g in operator_config['groups']: |
| 334 | + policy = dict_search(f'command_policy.allow', operator_config['groups'][g]) |
| 335 | + if policy is not None: |
| 336 | + policy = list(map(lambda s: re.split(r'\s+', s), policy)) |
| 337 | + operator_config['groups'][g]['command_policy']['allow'] = policy |
| 338 | + |
| 339 | + with open('/etc/vyos/operators.json', 'w') as of: |
| 340 | + json.dump(operator_config, of) |
| 341 | + |
309 | 342 | return None
|
310 | 343 |
|
311 | 344 |
|
@@ -335,7 +368,11 @@ def apply(login):
|
335 | 368 | if tmp: command += f" --home '{tmp}'"
|
336 | 369 | else: command += f" --home '/home/{user}'"
|
337 | 370 |
|
338 |
| - command += f' --groups frr,frrvty,vyattacfg,sudo,adm,dip,disk,_kea {user}' |
| 371 | + if 'operator' not in user_config: |
| 372 | + command += f' --groups frr,frrvty,vyattacfg,sudo,adm,dip,disk,_kea' |
| 373 | + |
| 374 | + command += f' {user}' |
| 375 | + |
339 | 376 | try:
|
340 | 377 | cmd(command)
|
341 | 378 | # we should not rely on the value stored in user_config['home_directory'], as a
|
|
0 commit comments