1 # -*- coding: utf-8 -*-
2 # pylint: disable=too-many-arguments,too-many-return-statements
3 # pylint: disable=too-many-branches, too-many-locals, too-many-statements
4 from __future__
import absolute_import
12 from datetime
import datetime
, timedelta
13 from string
import ascii_lowercase
, ascii_uppercase
, digits
, punctuation
14 from typing
import List
, Optional
, Sequence
17 from mgr_module
import CLICheckNonemptyFileInput
, CLIReadCommand
, CLIWriteCommand
20 from ..exceptions
import PasswordPolicyException
, PermissionNotValid
, \
21 PwdExpirationDateNotValid
, RoleAlreadyExists
, RoleDoesNotExist
, \
22 RoleIsAssociatedWithUser
, RoleNotInUser
, ScopeNotInRole
, ScopeNotValid
, \
23 UserAlreadyExists
, UserDoesNotExist
24 from ..security
import Permission
, Scope
25 from ..settings
import Settings
27 logger
= logging
.getLogger('access_control')
30 # password hashing algorithm
31 def password_hash(password
, salt_password
=None):
35 salt_password
= bcrypt
.gensalt()
37 salt_password
= salt_password
.encode('utf8')
38 return bcrypt
.hashpw(password
.encode('utf8'), salt_password
).decode('utf8')
41 _P
= Permission
# short alias
44 class PasswordPolicy(object):
45 def __init__(self
, password
, username
=None, old_password
=None):
47 :param password: The new plain password.
49 :param username: The name of the user.
50 :type username: str | None
51 :param old_password: The old plain password.
52 :type old_password: str | None
54 self
.password
= password
55 self
.username
= username
56 self
.old_password
= old_password
57 self
.forbidden_words
= Settings
.PWD_POLICY_EXCLUSION_LIST
.split(',')
58 self
.complexity_credits
= 0
61 def _check_if_contains_word(password
, word
):
62 return re
.compile('(?:{0})'.format(word
),
63 flags
=re
.IGNORECASE
).search(password
)
65 def check_password_complexity(self
):
66 if not Settings
.PWD_POLICY_CHECK_COMPLEXITY_ENABLED
:
67 return Settings
.PWD_POLICY_MIN_COMPLEXITY
69 small_letter_credit
= 1
71 special_character_credit
= 3
72 other_character_credit
= 5
73 self
.complexity_credits
= 0
74 for ch
in self
.password
:
75 if ch
in ascii_uppercase
:
76 self
.complexity_credits
+= big_letter_credit
77 elif ch
in ascii_lowercase
:
78 self
.complexity_credits
+= small_letter_credit
80 self
.complexity_credits
+= digit_credit
81 elif ch
in punctuation
:
82 self
.complexity_credits
+= special_character_credit
84 self
.complexity_credits
+= other_character_credit
85 return self
.complexity_credits
87 def check_is_old_password(self
):
88 if not Settings
.PWD_POLICY_CHECK_OLDPWD_ENABLED
:
90 return self
.old_password
and self
.password
== self
.old_password
92 def check_if_contains_username(self
):
93 if not Settings
.PWD_POLICY_CHECK_USERNAME_ENABLED
:
97 return self
._check
_if
_contains
_word
(self
.password
, self
.username
)
99 def check_if_contains_forbidden_words(self
):
100 if not Settings
.PWD_POLICY_CHECK_EXCLUSION_LIST_ENABLED
:
102 return self
._check
_if
_contains
_word
(self
.password
,
103 '|'.join(self
.forbidden_words
))
105 def check_if_sequential_characters(self
):
106 if not Settings
.PWD_POLICY_CHECK_SEQUENTIAL_CHARS_ENABLED
:
108 for i
in range(1, len(self
.password
) - 1):
109 if ord(self
.password
[i
- 1]) + 1 == ord(self
.password
[i
])\
110 == ord(self
.password
[i
+ 1]) - 1:
114 def check_if_repetitive_characters(self
):
115 if not Settings
.PWD_POLICY_CHECK_REPETITIVE_CHARS_ENABLED
:
117 for i
in range(1, len(self
.password
) - 1):
118 if self
.password
[i
- 1] == self
.password
[i
] == self
.password
[i
+ 1]:
122 def check_password_length(self
):
123 if not Settings
.PWD_POLICY_CHECK_LENGTH_ENABLED
:
125 return len(self
.password
) >= Settings
.PWD_POLICY_MIN_LENGTH
129 Perform all password policy checks.
130 :raise PasswordPolicyException: If a password policy check fails.
132 if not Settings
.PWD_POLICY_ENABLED
:
134 if self
.check_password_complexity() < Settings
.PWD_POLICY_MIN_COMPLEXITY
:
135 raise PasswordPolicyException('Password is too weak.')
136 if not self
.check_password_length():
137 raise PasswordPolicyException('Password is too weak.')
138 if self
.check_is_old_password():
139 raise PasswordPolicyException('Password must not be the same as the previous one.')
140 if self
.check_if_contains_username():
141 raise PasswordPolicyException('Password must not contain username.')
142 result
= self
.check_if_contains_forbidden_words()
144 raise PasswordPolicyException('Password must not contain the keyword "{}".'.format(
146 if self
.check_if_repetitive_characters():
147 raise PasswordPolicyException('Password must not contain repetitive characters.')
148 if self
.check_if_sequential_characters():
149 raise PasswordPolicyException('Password must not contain sequential characters.')
153 def __init__(self
, name
, description
=None, scope_permissions
=None):
155 self
.description
= description
156 if scope_permissions
is None:
157 self
.scopes_permissions
= {}
159 self
.scopes_permissions
= scope_permissions
162 return hash(self
.name
)
164 def __eq__(self
, other
):
165 return self
.name
== other
.name
167 def set_scope_permissions(self
, scope
, permissions
):
168 if not Scope
.valid_scope(scope
):
169 raise ScopeNotValid(scope
)
170 for perm
in permissions
:
171 if not Permission
.valid_permission(perm
):
172 raise PermissionNotValid(perm
)
175 self
.scopes_permissions
[scope
] = permissions
177 def del_scope_permissions(self
, scope
):
178 if scope
not in self
.scopes_permissions
:
179 raise ScopeNotInRole(scope
, self
.name
)
180 del self
.scopes_permissions
[scope
]
182 def reset_scope_permissions(self
):
183 self
.scopes_permissions
= {}
185 def authorize(self
, scope
, permissions
):
186 if scope
in self
.scopes_permissions
:
187 role_perms
= self
.scopes_permissions
[scope
]
188 for perm
in permissions
:
189 if perm
not in role_perms
:
197 'description': self
.description
,
198 'scopes_permissions': self
.scopes_permissions
202 def from_dict(cls
, r_dict
):
203 return Role(r_dict
['name'], r_dict
['description'],
204 r_dict
['scopes_permissions'])
207 # static pre-defined system roles
208 # this roles cannot be deleted nor updated
210 # admin role provides all permissions for all scopes
212 'administrator', 'allows full permissions for all security scopes', {
213 scope_name
: Permission
.all_permissions()
214 for scope_name
in Scope
.all_scopes()
218 # read-only role provides read-only permission for all scopes
219 READ_ONLY_ROLE
= Role(
221 'allows read permission for all security scope except dashboard settings and config-opt', {
222 scope_name
: [_P
.READ
] for scope_name
in Scope
.all_scopes()
223 if scope_name
not in (Scope
.DASHBOARD_SETTINGS
, Scope
.CONFIG_OPT
)
227 # block manager role provides all permission for block related scopes
228 BLOCK_MGR_ROLE
= Role(
229 'block-manager', 'allows full permissions for rbd-image, rbd-mirroring, and iscsi scopes', {
230 Scope
.RBD_IMAGE
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
231 Scope
.POOL
: [_P
.READ
],
232 Scope
.ISCSI
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
233 Scope
.RBD_MIRRORING
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
234 Scope
.GRAFANA
: [_P
.READ
],
238 # RadosGW manager role provides all permissions for block related scopes
240 'rgw-manager', 'allows full permissions for the rgw scope', {
241 Scope
.RGW
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
242 Scope
.GRAFANA
: [_P
.READ
],
246 # Cluster manager role provides all permission for OSDs, Monitors, and
248 CLUSTER_MGR_ROLE
= Role(
249 'cluster-manager', """allows full permissions for the hosts, osd, mon, mgr,
250 and config-opt scopes""", {
251 Scope
.HOSTS
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
252 Scope
.OSD
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
253 Scope
.MONITOR
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
254 Scope
.MANAGER
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
255 Scope
.CONFIG_OPT
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
256 Scope
.LOG
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
257 Scope
.GRAFANA
: [_P
.READ
],
261 # Pool manager role provides all permissions for pool related scopes
262 POOL_MGR_ROLE
= Role(
263 'pool-manager', 'allows full permissions for the pool scope', {
264 Scope
.POOL
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
265 Scope
.GRAFANA
: [_P
.READ
],
268 # CephFS manager role provides all permissions for CephFS related scopes
269 CEPHFS_MGR_ROLE
= Role(
270 'cephfs-manager', 'allows full permissions for the cephfs scope', {
271 Scope
.CEPHFS
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
272 Scope
.GRAFANA
: [_P
.READ
],
275 GANESHA_MGR_ROLE
= Role(
276 'ganesha-manager', 'allows full permissions for the nfs-ganesha scope', {
277 Scope
.NFS_GANESHA
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
278 Scope
.CEPHFS
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
279 Scope
.RGW
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
280 Scope
.GRAFANA
: [_P
.READ
],
285 ADMIN_ROLE
.name
: ADMIN_ROLE
,
286 READ_ONLY_ROLE
.name
: READ_ONLY_ROLE
,
287 BLOCK_MGR_ROLE
.name
: BLOCK_MGR_ROLE
,
288 RGW_MGR_ROLE
.name
: RGW_MGR_ROLE
,
289 CLUSTER_MGR_ROLE
.name
: CLUSTER_MGR_ROLE
,
290 POOL_MGR_ROLE
.name
: POOL_MGR_ROLE
,
291 CEPHFS_MGR_ROLE
.name
: CEPHFS_MGR_ROLE
,
292 GANESHA_MGR_ROLE
.name
: GANESHA_MGR_ROLE
,
297 def __init__(self
, username
, password
, name
=None, email
=None, roles
=None,
298 last_update
=None, enabled
=True, pwd_expiration_date
=None,
299 pwd_update_required
=False):
300 self
.username
= username
301 self
.password
= password
304 self
.invalid_auth_attempt
= 0
309 if last_update
is None:
310 self
.refresh_last_update()
312 self
.last_update
= last_update
313 self
._enabled
= enabled
314 self
.pwd_expiration_date
= pwd_expiration_date
315 if self
.pwd_expiration_date
is None:
316 self
.refresh_pwd_expiration_date()
317 self
.pwd_update_required
= pwd_update_required
319 def refresh_last_update(self
):
320 self
.last_update
= int(time
.time())
322 def refresh_pwd_expiration_date(self
):
323 if Settings
.USER_PWD_EXPIRATION_SPAN
> 0:
324 expiration_date
= datetime
.utcnow() + timedelta(
325 days
=Settings
.USER_PWD_EXPIRATION_SPAN
)
326 self
.pwd_expiration_date
= int(time
.mktime(expiration_date
.timetuple()))
328 self
.pwd_expiration_date
= None
335 def enabled(self
, value
):
336 self
._enabled
= value
337 self
.refresh_last_update()
339 def set_password(self
, password
):
340 self
.set_password_hash(password_hash(password
))
342 def set_password_hash(self
, hashed_password
):
343 self
.invalid_auth_attempt
= 0
344 self
.password
= hashed_password
345 self
.refresh_last_update()
346 self
.refresh_pwd_expiration_date()
347 self
.pwd_update_required
= False
349 def compare_password(self
, password
):
351 Compare the specified password with the user password.
352 :param password: The plain password to check.
354 :return: `True` if the passwords are equal, otherwise `False`.
357 pass_hash
= password_hash(password
, salt_password
=self
.password
)
358 return pass_hash
== self
.password
360 def is_pwd_expired(self
):
361 if self
.pwd_expiration_date
:
362 current_time
= int(time
.mktime(datetime
.utcnow().timetuple()))
363 return self
.pwd_expiration_date
< current_time
366 def set_roles(self
, roles
):
367 self
.roles
= set(roles
)
368 self
.refresh_last_update()
370 def add_roles(self
, roles
):
371 self
.roles
= self
.roles
.union(set(roles
))
372 self
.refresh_last_update()
374 def del_roles(self
, roles
):
376 if role
not in self
.roles
:
377 raise RoleNotInUser(role
.name
, self
.username
)
378 self
.roles
.difference_update(set(roles
))
379 self
.refresh_last_update()
381 def authorize(self
, scope
, permissions
):
382 if self
.pwd_update_required
:
385 for role
in self
.roles
:
386 if role
.authorize(scope
, permissions
):
390 def permissions_dict(self
):
392 perms
= {} # type: dict
393 for role
in self
.roles
:
394 for scope
, perms_list
in role
.scopes_permissions
.items():
396 perms_tmp
= set(perms
[scope
]).union(set(perms_list
))
397 perms
[scope
] = list(perms_tmp
)
399 perms
[scope
] = perms_list
405 'username': self
.username
,
406 'password': self
.password
,
407 'roles': sorted([r
.name
for r
in self
.roles
]),
410 'lastUpdate': self
.last_update
,
411 'enabled': self
.enabled
,
412 'pwdExpirationDate': self
.pwd_expiration_date
,
413 'pwdUpdateRequired': self
.pwd_update_required
417 def from_dict(cls
, u_dict
, roles
):
418 return User(u_dict
['username'], u_dict
['password'], u_dict
['name'],
419 u_dict
['email'], {roles
[r
] for r
in u_dict
['roles']},
420 u_dict
['lastUpdate'], u_dict
['enabled'],
421 u_dict
['pwdExpirationDate'], u_dict
['pwdUpdateRequired'])
424 class AccessControlDB(object):
426 ACDB_CONFIG_KEY
= "accessdb_v"
428 def __init__(self
, version
, users
, roles
):
430 self
.version
= version
432 self
.lock
= threading
.RLock()
434 def create_role(self
, name
, description
=None):
436 if name
in SYSTEM_ROLES
or name
in self
.roles
:
437 raise RoleAlreadyExists(name
)
438 role
= Role(name
, description
)
439 self
.roles
[name
] = role
442 def get_role(self
, name
):
444 if name
not in self
.roles
:
445 raise RoleDoesNotExist(name
)
446 return self
.roles
[name
]
448 def increment_attempt(self
, username
):
450 if username
in self
.users
:
451 self
.users
[username
].invalid_auth_attempt
+= 1
453 def reset_attempt(self
, username
):
455 if username
in self
.users
:
456 self
.users
[username
].invalid_auth_attempt
= 0
458 def get_attempt(self
, username
):
461 return self
.users
[username
].invalid_auth_attempt
465 def delete_role(self
, name
):
467 if name
not in self
.roles
:
468 raise RoleDoesNotExist(name
)
469 role
= self
.roles
[name
]
471 # check if role is not associated with a user
472 for username
, user
in self
.users
.items():
473 if role
in user
.roles
:
474 raise RoleIsAssociatedWithUser(name
, username
)
478 def create_user(self
, username
, password
, name
, email
, enabled
=True,
479 pwd_expiration_date
=None, pwd_update_required
=False):
480 logger
.debug("creating user: username=%s", username
)
482 if username
in self
.users
:
483 raise UserAlreadyExists(username
)
484 if pwd_expiration_date
and \
485 (pwd_expiration_date
< int(time
.mktime(datetime
.utcnow().timetuple()))):
486 raise PwdExpirationDateNotValid()
487 user
= User(username
, password_hash(password
), name
, email
, enabled
=enabled
,
488 pwd_expiration_date
=pwd_expiration_date
,
489 pwd_update_required
=pwd_update_required
)
490 self
.users
[username
] = user
493 def get_user(self
, username
):
495 if username
not in self
.users
:
496 raise UserDoesNotExist(username
)
497 return self
.users
[username
]
499 def delete_user(self
, username
):
501 if username
not in self
.users
:
502 raise UserDoesNotExist(username
)
503 del self
.users
[username
]
505 def update_users_with_roles(self
, role
):
509 for _
, user
in self
.users
.items():
510 if role
in user
.roles
:
511 user
.refresh_last_update()
516 'users': {un
: u
.to_dict() for un
, u
in self
.users
.items()},
517 'roles': {rn
: r
.to_dict() for rn
, r
in self
.roles
.items()},
518 'version': self
.version
520 mgr
.set_store(self
.accessdb_config_key(), json
.dumps(db
))
523 def accessdb_config_key(cls
, version
=None):
525 version
= cls
.VERSION
526 return "{}{}".format(cls
.ACDB_CONFIG_KEY
, version
)
528 def check_and_update_db(self
):
529 logger
.debug("Checking for previous DB versions")
531 def check_migrate_v1_to_current():
532 # Check if version 1 exists in the DB and migrate it to current version
533 v1_db
= mgr
.get_store(self
.accessdb_config_key(1))
535 logger
.debug("Found database v1 credentials")
536 v1_db
= json
.loads(v1_db
)
538 for user
, _
in v1_db
['users'].items():
539 v1_db
['users'][user
]['enabled'] = True
540 v1_db
['users'][user
]['pwdExpirationDate'] = None
541 v1_db
['users'][user
]['pwdUpdateRequired'] = False
543 self
.roles
= {rn
: Role
.from_dict(r
) for rn
, r
in v1_db
.get('roles', {}).items()}
544 self
.users
= {un
: User
.from_dict(u
, dict(self
.roles
, **SYSTEM_ROLES
))
545 for un
, u
in v1_db
.get('users', {}).items()}
549 check_migrate_v1_to_current()
553 logger
.info("Loading user roles DB version=%s", cls
.VERSION
)
555 json_db
= mgr
.get_store(cls
.accessdb_config_key())
557 logger
.debug("No DB v%s found, creating new...", cls
.VERSION
)
558 db
= cls(cls
.VERSION
, {}, {})
559 # check if we can update from a previous version database
560 db
.check_and_update_db()
563 dict_db
= json
.loads(json_db
)
564 roles
= {rn
: Role
.from_dict(r
)
565 for rn
, r
in dict_db
.get('roles', {}).items()}
566 users
= {un
: User
.from_dict(u
, dict(roles
, **SYSTEM_ROLES
))
567 for un
, u
in dict_db
.get('users', {}).items()}
568 return cls(dict_db
['version'], users
, roles
)
571 def load_access_control_db():
572 mgr
.ACCESS_CTRL_DB
= AccessControlDB
.load()
575 # CLI dashboard access control scope commands
577 @CLIWriteCommand('dashboard set-login-credentials')
578 @CLICheckNonemptyFileInput
579 def set_login_credentials_cmd(_
, username
: str, inbuf
: str):
581 Set the login credentials. Password read from -i <file>
585 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
586 user
.set_password(password
)
587 except UserDoesNotExist
:
588 user
= mgr
.ACCESS_CTRL_DB
.create_user(username
, password
, None, None)
589 user
.set_roles([ADMIN_ROLE
])
591 mgr
.ACCESS_CTRL_DB
.save()
594 ******************************************************************
595 *** WARNING: this command is deprecated. ***
596 *** Please use the ac-user-* related commands to manage users. ***
597 ******************************************************************
598 Username and password updated''', ''
601 @CLIReadCommand('dashboard ac-role-show')
602 def ac_role_show_cmd(_
, rolename
: Optional
[str] = None):
607 roles
= dict(mgr
.ACCESS_CTRL_DB
.roles
)
608 roles
.update(SYSTEM_ROLES
)
609 roles_list
= [name
for name
, _
in roles
.items()]
610 return 0, json
.dumps(roles_list
), ''
612 role
= mgr
.ACCESS_CTRL_DB
.get_role(rolename
)
613 except RoleDoesNotExist
as ex
:
614 if rolename
not in SYSTEM_ROLES
:
615 return -errno
.ENOENT
, '', str(ex
)
616 role
= SYSTEM_ROLES
[rolename
]
617 return 0, json
.dumps(role
.to_dict()), ''
620 @CLIWriteCommand('dashboard ac-role-create')
621 def ac_role_create_cmd(_
, rolename
: str, description
: Optional
[str] = None):
623 Create a new access control role
626 role
= mgr
.ACCESS_CTRL_DB
.create_role(rolename
, description
)
627 mgr
.ACCESS_CTRL_DB
.save()
628 return 0, json
.dumps(role
.to_dict()), ''
629 except RoleAlreadyExists
as ex
:
630 return -errno
.EEXIST
, '', str(ex
)
633 @CLIWriteCommand('dashboard ac-role-delete')
634 def ac_role_delete_cmd(_
, rolename
: str):
636 Delete an access control role
639 mgr
.ACCESS_CTRL_DB
.delete_role(rolename
)
640 mgr
.ACCESS_CTRL_DB
.save()
641 return 0, "Role '{}' deleted".format(rolename
), ""
642 except RoleDoesNotExist
as ex
:
643 if rolename
in SYSTEM_ROLES
:
644 return -errno
.EPERM
, '', "Cannot delete system role '{}'" \
646 return -errno
.ENOENT
, '', str(ex
)
647 except RoleIsAssociatedWithUser
as ex
:
648 return -errno
.EPERM
, '', str(ex
)
651 @CLIWriteCommand('dashboard ac-role-add-scope-perms')
652 def ac_role_add_scope_perms_cmd(_
,
655 permissions
: Sequence
[str]):
657 Add the scope permissions for a role
660 role
= mgr
.ACCESS_CTRL_DB
.get_role(rolename
)
661 perms_array
= [perm
.strip() for perm
in permissions
]
662 role
.set_scope_permissions(scopename
, perms_array
)
663 mgr
.ACCESS_CTRL_DB
.update_users_with_roles(role
)
664 mgr
.ACCESS_CTRL_DB
.save()
665 return 0, json
.dumps(role
.to_dict()), ''
666 except RoleDoesNotExist
as ex
:
667 if rolename
in SYSTEM_ROLES
:
668 return -errno
.EPERM
, '', "Cannot update system role '{}'" \
670 return -errno
.ENOENT
, '', str(ex
)
671 except ScopeNotValid
as ex
:
672 return -errno
.EINVAL
, '', str(ex
) + "\n Possible values: {}" \
673 .format(Scope
.all_scopes())
674 except PermissionNotValid
as ex
:
675 return -errno
.EINVAL
, '', str(ex
) + \
676 "\n Possible values: {}" \
677 .format(Permission
.all_permissions())
680 @CLIWriteCommand('dashboard ac-role-del-scope-perms')
681 def ac_role_del_scope_perms_cmd(_
, rolename
: str, scopename
: str):
683 Delete the scope permissions for a role
686 role
= mgr
.ACCESS_CTRL_DB
.get_role(rolename
)
687 role
.del_scope_permissions(scopename
)
688 mgr
.ACCESS_CTRL_DB
.update_users_with_roles(role
)
689 mgr
.ACCESS_CTRL_DB
.save()
690 return 0, json
.dumps(role
.to_dict()), ''
691 except RoleDoesNotExist
as ex
:
692 if rolename
in SYSTEM_ROLES
:
693 return -errno
.EPERM
, '', "Cannot update system role '{}'" \
695 return -errno
.ENOENT
, '', str(ex
)
696 except ScopeNotInRole
as ex
:
697 return -errno
.ENOENT
, '', str(ex
)
700 @CLIReadCommand('dashboard ac-user-show')
701 def ac_user_show_cmd(_
, username
: Optional
[str] = None):
706 users
= mgr
.ACCESS_CTRL_DB
.users
707 users_list
= [name
for name
, _
in users
.items()]
708 return 0, json
.dumps(users_list
), ''
710 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
711 return 0, json
.dumps(user
.to_dict()), ''
712 except UserDoesNotExist
as ex
:
713 return -errno
.ENOENT
, '', str(ex
)
716 @CLIWriteCommand('dashboard ac-user-create')
717 @CLICheckNonemptyFileInput
718 def ac_user_create_cmd(_
, username
: str, inbuf
: str,
719 rolename
: Optional
[str] = None,
720 name
: Optional
[str] = None,
721 email
: Optional
[str] = None,
722 enabled
: bool = True,
723 force_password
: bool = False,
724 pwd_expiration_date
: Optional
[int] = None,
725 pwd_update_required
: bool = False):
727 Create a user. Password read from -i <file>
731 role
= mgr
.ACCESS_CTRL_DB
.get_role(rolename
) if rolename
else None
732 except RoleDoesNotExist
as ex
:
733 if rolename
not in SYSTEM_ROLES
:
734 return -errno
.ENOENT
, '', str(ex
)
735 role
= SYSTEM_ROLES
[rolename
]
738 if not force_password
:
739 pw_check
= PasswordPolicy(password
, username
)
741 user
= mgr
.ACCESS_CTRL_DB
.create_user(username
, password
, name
, email
,
742 enabled
, pwd_expiration_date
,
744 except PasswordPolicyException
as ex
:
745 return -errno
.EINVAL
, '', str(ex
)
746 except UserAlreadyExists
as ex
:
747 return 0, str(ex
), ''
750 user
.set_roles([role
])
751 mgr
.ACCESS_CTRL_DB
.save()
752 return 0, json
.dumps(user
.to_dict()), ''
755 @CLIWriteCommand('dashboard ac-user-enable')
756 def ac_user_enable(_
, username
: str):
761 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
763 mgr
.ACCESS_CTRL_DB
.reset_attempt(username
)
765 mgr
.ACCESS_CTRL_DB
.save()
766 return 0, json
.dumps(user
.to_dict()), ''
767 except UserDoesNotExist
as ex
:
768 return -errno
.ENOENT
, '', str(ex
)
771 @CLIWriteCommand('dashboard ac-user-disable')
772 def ac_user_disable(_
, username
: str):
777 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
780 mgr
.ACCESS_CTRL_DB
.save()
781 return 0, json
.dumps(user
.to_dict()), ''
782 except UserDoesNotExist
as ex
:
783 return -errno
.ENOENT
, '', str(ex
)
786 @CLIWriteCommand('dashboard ac-user-delete')
787 def ac_user_delete_cmd(_
, username
: str):
792 mgr
.ACCESS_CTRL_DB
.delete_user(username
)
793 mgr
.ACCESS_CTRL_DB
.save()
794 return 0, "User '{}' deleted".format(username
), ""
795 except UserDoesNotExist
as ex
:
796 return -errno
.ENOENT
, '', str(ex
)
799 @CLIWriteCommand('dashboard ac-user-set-roles')
800 def ac_user_set_roles_cmd(_
, username
: str, roles
: Sequence
[str]):
805 roles
: List
[Role
] = []
806 for rolename
in rolesname
:
808 roles
.append(mgr
.ACCESS_CTRL_DB
.get_role(rolename
))
809 except RoleDoesNotExist
as ex
:
810 if rolename
not in SYSTEM_ROLES
:
811 return -errno
.ENOENT
, '', str(ex
)
812 roles
.append(SYSTEM_ROLES
[rolename
])
814 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
815 user
.set_roles(roles
)
816 mgr
.ACCESS_CTRL_DB
.save()
817 return 0, json
.dumps(user
.to_dict()), ''
818 except UserDoesNotExist
as ex
:
819 return -errno
.ENOENT
, '', str(ex
)
822 @CLIWriteCommand('dashboard ac-user-add-roles')
823 def ac_user_add_roles_cmd(_
, username
: str, roles
: Sequence
[str]):
828 roles
: List
[Role
] = []
829 for rolename
in rolesname
:
831 roles
.append(mgr
.ACCESS_CTRL_DB
.get_role(rolename
))
832 except RoleDoesNotExist
as ex
:
833 if rolename
not in SYSTEM_ROLES
:
834 return -errno
.ENOENT
, '', str(ex
)
835 roles
.append(SYSTEM_ROLES
[rolename
])
837 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
838 user
.add_roles(roles
)
839 mgr
.ACCESS_CTRL_DB
.save()
840 return 0, json
.dumps(user
.to_dict()), ''
841 except UserDoesNotExist
as ex
:
842 return -errno
.ENOENT
, '', str(ex
)
845 @CLIWriteCommand('dashboard ac-user-del-roles')
846 def ac_user_del_roles_cmd(_
, username
: str, roles
: Sequence
[str]):
848 Delete roles from user
851 roles
: List
[Role
] = []
852 for rolename
in rolesname
:
854 roles
.append(mgr
.ACCESS_CTRL_DB
.get_role(rolename
))
855 except RoleDoesNotExist
as ex
:
856 if rolename
not in SYSTEM_ROLES
:
857 return -errno
.ENOENT
, '', str(ex
)
858 roles
.append(SYSTEM_ROLES
[rolename
])
860 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
861 user
.del_roles(roles
)
862 mgr
.ACCESS_CTRL_DB
.save()
863 return 0, json
.dumps(user
.to_dict()), ''
864 except UserDoesNotExist
as ex
:
865 return -errno
.ENOENT
, '', str(ex
)
866 except RoleNotInUser
as ex
:
867 return -errno
.ENOENT
, '', str(ex
)
870 @CLIWriteCommand('dashboard ac-user-set-password')
871 @CLICheckNonemptyFileInput
872 def ac_user_set_password(_
, username
: str, inbuf
: str,
873 force_password
: bool = False):
875 Set user password from -i <file>
879 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
880 if not force_password
:
881 pw_check
= PasswordPolicy(password
, user
.name
)
883 user
.set_password(password
)
884 mgr
.ACCESS_CTRL_DB
.save()
885 return 0, json
.dumps(user
.to_dict()), ''
886 except PasswordPolicyException
as ex
:
887 return -errno
.EINVAL
, '', str(ex
)
888 except UserDoesNotExist
as ex
:
889 return -errno
.ENOENT
, '', str(ex
)
892 @CLIWriteCommand('dashboard ac-user-set-password-hash')
893 @CLICheckNonemptyFileInput
894 def ac_user_set_password_hash(_
, username
: str, inbuf
: str):
896 Set user password bcrypt hash from -i <file>
898 hashed_password
= inbuf
900 # make sure the hashed_password is actually a bcrypt hash
901 bcrypt
.checkpw(b
'', hashed_password
.encode('utf-8'))
902 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
903 user
.set_password_hash(hashed_password
)
905 mgr
.ACCESS_CTRL_DB
.save()
906 return 0, json
.dumps(user
.to_dict()), ''
908 return -errno
.EINVAL
, '', 'Invalid password hash'
909 except UserDoesNotExist
as ex
:
910 return -errno
.ENOENT
, '', str(ex
)
913 @CLIWriteCommand('dashboard ac-user-set-info')
914 def ac_user_set_info(_
, username
: str, name
: str, email
: str):
919 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
924 mgr
.ACCESS_CTRL_DB
.save()
925 return 0, json
.dumps(user
.to_dict()), ''
926 except UserDoesNotExist
as ex
:
927 return -errno
.ENOENT
, '', str(ex
)
930 class LocalAuthenticator(object):
932 load_access_control_db()
934 def get_user(self
, username
):
935 return mgr
.ACCESS_CTRL_DB
.get_user(username
)
937 def authenticate(self
, username
, password
):
939 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
941 if user
.enabled
and user
.compare_password(password
) \
942 and not user
.is_pwd_expired():
943 return {'permissions': user
.permissions_dict(),
944 'pwdExpirationDate': user
.pwd_expiration_date
,
945 'pwdUpdateRequired': user
.pwd_update_required
}
946 except UserDoesNotExist
:
947 logger
.debug("User '%s' does not exist", username
)
950 def authorize(self
, username
, scope
, permissions
):
951 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
952 return user
.authorize(scope
, permissions
)