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
6 from string
import punctuation
, ascii_lowercase
, digits
, ascii_uppercase
15 from datetime
import datetime
, timedelta
19 from mgr_module
import CLIReadCommand
, CLIWriteCommand
22 from ..security
import Scope
, Permission
23 from ..settings
import Settings
24 from ..exceptions
import RoleAlreadyExists
, RoleDoesNotExist
, ScopeNotValid
, \
25 PermissionNotValid
, RoleIsAssociatedWithUser
, \
26 UserAlreadyExists
, UserDoesNotExist
, ScopeNotInRole
, \
27 RoleNotInUser
, PasswordPolicyException
, PwdExpirationDateNotValid
30 logger
= logging
.getLogger('access_control')
33 # password hashing algorithm
34 def password_hash(password
, salt_password
=None):
38 salt_password
= bcrypt
.gensalt()
40 salt_password
= salt_password
.encode('utf8')
41 return bcrypt
.hashpw(password
.encode('utf8'), salt_password
).decode('utf8')
44 _P
= Permission
# short alias
47 class PasswordPolicy(object):
48 def __init__(self
, password
, username
=None, old_password
=None):
50 :param password: The new plain password.
52 :param username: The name of the user.
53 :type username: str | None
54 :param old_password: The old plain password.
55 :type old_password: str | None
57 self
.password
= password
58 self
.username
= username
59 self
.old_password
= old_password
60 self
.forbidden_words
= Settings
.PWD_POLICY_EXCLUSION_LIST
.split(',')
61 self
.complexity_credits
= 0
64 def _check_if_contains_word(password
, word
):
65 return re
.compile('(?:{0})'.format(word
),
66 flags
=re
.IGNORECASE
).search(password
)
68 def check_password_complexity(self
):
69 if not Settings
.PWD_POLICY_CHECK_COMPLEXITY_ENABLED
:
70 return Settings
.PWD_POLICY_MIN_COMPLEXITY
72 small_letter_credit
= 1
74 special_character_credit
= 3
75 other_character_credit
= 5
76 self
.complexity_credits
= 0
77 for ch
in self
.password
:
78 if ch
in ascii_uppercase
:
79 self
.complexity_credits
+= big_letter_credit
80 elif ch
in ascii_lowercase
:
81 self
.complexity_credits
+= small_letter_credit
83 self
.complexity_credits
+= digit_credit
84 elif ch
in punctuation
:
85 self
.complexity_credits
+= special_character_credit
87 self
.complexity_credits
+= other_character_credit
88 return self
.complexity_credits
90 def check_is_old_password(self
):
91 if not Settings
.PWD_POLICY_CHECK_OLDPWD_ENABLED
:
93 return self
.old_password
and self
.password
== self
.old_password
95 def check_if_contains_username(self
):
96 if not Settings
.PWD_POLICY_CHECK_USERNAME_ENABLED
:
100 return self
._check
_if
_contains
_word
(self
.password
, self
.username
)
102 def check_if_contains_forbidden_words(self
):
103 if not Settings
.PWD_POLICY_CHECK_EXCLUSION_LIST_ENABLED
:
105 return self
._check
_if
_contains
_word
(self
.password
,
106 '|'.join(self
.forbidden_words
))
108 def check_if_sequential_characters(self
):
109 if not Settings
.PWD_POLICY_CHECK_SEQUENTIAL_CHARS_ENABLED
:
111 for i
in range(1, len(self
.password
) - 1):
112 if ord(self
.password
[i
- 1]) + 1 == ord(self
.password
[i
])\
113 == ord(self
.password
[i
+ 1]) - 1:
117 def check_if_repetitive_characters(self
):
118 if not Settings
.PWD_POLICY_CHECK_REPETITIVE_CHARS_ENABLED
:
120 for i
in range(1, len(self
.password
) - 1):
121 if self
.password
[i
- 1] == self
.password
[i
] == self
.password
[i
+ 1]:
125 def check_password_length(self
):
126 if not Settings
.PWD_POLICY_CHECK_LENGTH_ENABLED
:
128 return len(self
.password
) >= Settings
.PWD_POLICY_MIN_LENGTH
132 Perform all password policy checks.
133 :raise PasswordPolicyException: If a password policy check fails.
135 if not Settings
.PWD_POLICY_ENABLED
:
137 if self
.check_password_complexity() < Settings
.PWD_POLICY_MIN_COMPLEXITY
:
138 raise PasswordPolicyException('Password is too weak.')
139 if not self
.check_password_length():
140 raise PasswordPolicyException('Password is too weak.')
141 if self
.check_is_old_password():
142 raise PasswordPolicyException('Password must not be the same as the previous one.')
143 if self
.check_if_contains_username():
144 raise PasswordPolicyException('Password must not contain username.')
145 result
= self
.check_if_contains_forbidden_words()
147 raise PasswordPolicyException('Password must not contain the keyword "{}".'.format(
149 if self
.check_if_repetitive_characters():
150 raise PasswordPolicyException('Password must not contain repetitive characters.')
151 if self
.check_if_sequential_characters():
152 raise PasswordPolicyException('Password must not contain sequential characters.')
156 def __init__(self
, name
, description
=None, scope_permissions
=None):
158 self
.description
= description
159 if scope_permissions
is None:
160 self
.scopes_permissions
= {}
162 self
.scopes_permissions
= scope_permissions
165 return hash(self
.name
)
167 def __eq__(self
, other
):
168 return self
.name
== other
.name
170 def set_scope_permissions(self
, scope
, permissions
):
171 if not Scope
.valid_scope(scope
):
172 raise ScopeNotValid(scope
)
173 for perm
in permissions
:
174 if not Permission
.valid_permission(perm
):
175 raise PermissionNotValid(perm
)
178 self
.scopes_permissions
[scope
] = permissions
180 def del_scope_permissions(self
, scope
):
181 if scope
not in self
.scopes_permissions
:
182 raise ScopeNotInRole(scope
, self
.name
)
183 del self
.scopes_permissions
[scope
]
185 def reset_scope_permissions(self
):
186 self
.scopes_permissions
= {}
188 def authorize(self
, scope
, permissions
):
189 if scope
in self
.scopes_permissions
:
190 role_perms
= self
.scopes_permissions
[scope
]
191 for perm
in permissions
:
192 if perm
not in role_perms
:
200 'description': self
.description
,
201 'scopes_permissions': self
.scopes_permissions
205 def from_dict(cls
, r_dict
):
206 return Role(r_dict
['name'], r_dict
['description'],
207 r_dict
['scopes_permissions'])
210 # static pre-defined system roles
211 # this roles cannot be deleted nor updated
213 # admin role provides all permissions for all scopes
214 ADMIN_ROLE
= Role('administrator', 'Administrator', {
215 scope_name
: Permission
.all_permissions()
216 for scope_name
in Scope
.all_scopes()
220 # read-only role provides read-only permission for all scopes
221 READ_ONLY_ROLE
= Role('read-only', 'Read-Only', {
222 scope_name
: [_P
.READ
] for scope_name
in Scope
.all_scopes()
223 if scope_name
!= Scope
.DASHBOARD_SETTINGS
227 # block manager role provides all permission for block related scopes
228 BLOCK_MGR_ROLE
= Role('block-manager', 'Block Manager', {
229 Scope
.RBD_IMAGE
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
230 Scope
.POOL
: [_P
.READ
],
231 Scope
.ISCSI
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
232 Scope
.RBD_MIRRORING
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
233 Scope
.GRAFANA
: [_P
.READ
],
237 # RadosGW manager role provides all permissions for block related scopes
238 RGW_MGR_ROLE
= Role('rgw-manager', 'RGW Manager', {
239 Scope
.RGW
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
240 Scope
.GRAFANA
: [_P
.READ
],
244 # Cluster manager role provides all permission for OSDs, Monitors, and
246 CLUSTER_MGR_ROLE
= Role('cluster-manager', 'Cluster Manager', {
247 Scope
.HOSTS
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
248 Scope
.OSD
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
249 Scope
.MONITOR
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
250 Scope
.MANAGER
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
251 Scope
.CONFIG_OPT
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
252 Scope
.LOG
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
253 Scope
.GRAFANA
: [_P
.READ
],
257 # Pool manager role provides all permissions for pool related scopes
258 POOL_MGR_ROLE
= Role('pool-manager', 'Pool Manager', {
259 Scope
.POOL
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
260 Scope
.GRAFANA
: [_P
.READ
],
263 # CephFS manager role provides all permissions for CephFS related scopes
264 CEPHFS_MGR_ROLE
= Role('cephfs-manager', 'CephFS Manager', {
265 Scope
.CEPHFS
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
266 Scope
.GRAFANA
: [_P
.READ
],
269 GANESHA_MGR_ROLE
= Role('ganesha-manager', 'NFS Ganesha Manager', {
270 Scope
.NFS_GANESHA
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
271 Scope
.CEPHFS
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
272 Scope
.RGW
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
273 Scope
.GRAFANA
: [_P
.READ
],
278 ADMIN_ROLE
.name
: ADMIN_ROLE
,
279 READ_ONLY_ROLE
.name
: READ_ONLY_ROLE
,
280 BLOCK_MGR_ROLE
.name
: BLOCK_MGR_ROLE
,
281 RGW_MGR_ROLE
.name
: RGW_MGR_ROLE
,
282 CLUSTER_MGR_ROLE
.name
: CLUSTER_MGR_ROLE
,
283 POOL_MGR_ROLE
.name
: POOL_MGR_ROLE
,
284 CEPHFS_MGR_ROLE
.name
: CEPHFS_MGR_ROLE
,
285 GANESHA_MGR_ROLE
.name
: GANESHA_MGR_ROLE
,
290 def __init__(self
, username
, password
, name
=None, email
=None, roles
=None,
291 last_update
=None, enabled
=True, pwd_expiration_date
=None,
292 pwd_update_required
=False):
293 self
.username
= username
294 self
.password
= password
301 if last_update
is None:
302 self
.refresh_last_update()
304 self
.last_update
= last_update
305 self
._enabled
= enabled
306 self
.pwd_expiration_date
= pwd_expiration_date
307 if self
.pwd_expiration_date
is None:
308 self
.refresh_pwd_expiration_date()
309 self
.pwd_update_required
= pwd_update_required
311 def refresh_last_update(self
):
312 self
.last_update
= int(time
.time())
314 def refresh_pwd_expiration_date(self
):
315 if Settings
.USER_PWD_EXPIRATION_SPAN
> 0:
316 expiration_date
= datetime
.utcnow() + timedelta(
317 days
=Settings
.USER_PWD_EXPIRATION_SPAN
)
318 self
.pwd_expiration_date
= int(time
.mktime(expiration_date
.timetuple()))
320 self
.pwd_expiration_date
= None
327 def enabled(self
, value
):
328 self
._enabled
= value
329 self
.refresh_last_update()
331 def set_password(self
, password
):
332 self
.set_password_hash(password_hash(password
))
334 def set_password_hash(self
, hashed_password
):
335 self
.password
= hashed_password
336 self
.refresh_last_update()
337 self
.refresh_pwd_expiration_date()
338 self
.pwd_update_required
= False
340 def compare_password(self
, password
):
342 Compare the specified password with the user password.
343 :param password: The plain password to check.
345 :return: `True` if the passwords are equal, otherwise `False`.
348 pass_hash
= password_hash(password
, salt_password
=self
.password
)
349 return pass_hash
== self
.password
351 def is_pwd_expired(self
):
352 if self
.pwd_expiration_date
:
353 current_time
= int(time
.mktime(datetime
.utcnow().timetuple()))
354 return self
.pwd_expiration_date
< current_time
357 def set_roles(self
, roles
):
358 self
.roles
= set(roles
)
359 self
.refresh_last_update()
361 def add_roles(self
, roles
):
362 self
.roles
= self
.roles
.union(set(roles
))
363 self
.refresh_last_update()
365 def del_roles(self
, roles
):
367 if role
not in self
.roles
:
368 raise RoleNotInUser(role
.name
, self
.username
)
369 self
.roles
.difference_update(set(roles
))
370 self
.refresh_last_update()
372 def authorize(self
, scope
, permissions
):
373 if self
.pwd_update_required
:
376 for role
in self
.roles
:
377 if role
.authorize(scope
, permissions
):
381 def permissions_dict(self
):
383 perms
= {} # type: dict
384 for role
in self
.roles
:
385 for scope
, perms_list
in role
.scopes_permissions
.items():
387 perms_tmp
= set(perms
[scope
]).union(set(perms_list
))
388 perms
[scope
] = list(perms_tmp
)
390 perms
[scope
] = perms_list
396 'username': self
.username
,
397 'password': self
.password
,
398 'roles': sorted([r
.name
for r
in self
.roles
]),
401 'lastUpdate': self
.last_update
,
402 'enabled': self
.enabled
,
403 'pwdExpirationDate': self
.pwd_expiration_date
,
404 'pwdUpdateRequired': self
.pwd_update_required
408 def from_dict(cls
, u_dict
, roles
):
409 return User(u_dict
['username'], u_dict
['password'], u_dict
['name'],
410 u_dict
['email'], {roles
[r
] for r
in u_dict
['roles']},
411 u_dict
['lastUpdate'], u_dict
['enabled'],
412 u_dict
['pwdExpirationDate'], u_dict
['pwdUpdateRequired'])
415 class AccessControlDB(object):
417 ACDB_CONFIG_KEY
= "accessdb_v"
419 def __init__(self
, version
, users
, roles
):
421 self
.version
= version
423 self
.lock
= threading
.RLock()
425 def create_role(self
, name
, description
=None):
427 if name
in SYSTEM_ROLES
or name
in self
.roles
:
428 raise RoleAlreadyExists(name
)
429 role
= Role(name
, description
)
430 self
.roles
[name
] = role
433 def get_role(self
, name
):
435 if name
not in self
.roles
:
436 raise RoleDoesNotExist(name
)
437 return self
.roles
[name
]
439 def delete_role(self
, name
):
441 if name
not in self
.roles
:
442 raise RoleDoesNotExist(name
)
443 role
= self
.roles
[name
]
445 # check if role is not associated with a user
446 for username
, user
in self
.users
.items():
447 if role
in user
.roles
:
448 raise RoleIsAssociatedWithUser(name
, username
)
452 def create_user(self
, username
, password
, name
, email
, enabled
=True,
453 pwd_expiration_date
=None, pwd_update_required
=False):
454 logger
.debug("creating user: username=%s", username
)
456 if username
in self
.users
:
457 raise UserAlreadyExists(username
)
458 if pwd_expiration_date
and \
459 (pwd_expiration_date
< int(time
.mktime(datetime
.utcnow().timetuple()))):
460 raise PwdExpirationDateNotValid()
461 user
= User(username
, password_hash(password
), name
, email
, enabled
=enabled
,
462 pwd_expiration_date
=pwd_expiration_date
,
463 pwd_update_required
=pwd_update_required
)
464 self
.users
[username
] = user
467 def get_user(self
, username
):
469 if username
not in self
.users
:
470 raise UserDoesNotExist(username
)
471 return self
.users
[username
]
473 def delete_user(self
, username
):
475 if username
not in self
.users
:
476 raise UserDoesNotExist(username
)
477 del self
.users
[username
]
479 def update_users_with_roles(self
, role
):
483 for _
, user
in self
.users
.items():
484 if role
in user
.roles
:
485 user
.refresh_last_update()
490 'users': {un
: u
.to_dict() for un
, u
in self
.users
.items()},
491 'roles': {rn
: r
.to_dict() for rn
, r
in self
.roles
.items()},
492 'version': self
.version
494 mgr
.set_store(self
.accessdb_config_key(), json
.dumps(db
))
497 def accessdb_config_key(cls
, version
=None):
499 version
= cls
.VERSION
500 return "{}{}".format(cls
.ACDB_CONFIG_KEY
, version
)
502 def check_and_update_db(self
):
503 logger
.debug("Checking for previews DB versions")
505 def check_migrate_v0_to_current():
506 # check if there is username/password from previous version
507 username
= mgr
.get_module_option('username', None)
508 password
= mgr
.get_module_option('password', None)
509 if username
and password
:
510 logger
.debug("Found single user credentials: user=%s", username
)
511 # found user credentials
512 user
= self
.create_user(username
, "", None, None)
513 # password is already hashed, so setting manually
514 user
.password
= password
515 user
.add_roles([ADMIN_ROLE
])
518 def check_migrate_v1_to_current():
519 # Check if version 1 exists in the DB and migrate it to current version
520 v1_db
= mgr
.get_store(self
.accessdb_config_key(1))
522 logger
.debug("Found database v1 credentials")
523 v1_db
= json
.loads(v1_db
)
525 for user
, _
in v1_db
['users'].items():
526 v1_db
['users'][user
]['enabled'] = True
527 v1_db
['users'][user
]['pwdExpirationDate'] = None
528 v1_db
['users'][user
]['pwdUpdateRequired'] = False
530 self
.roles
= {rn
: Role
.from_dict(r
) for rn
, r
in v1_db
.get('roles', {}).items()}
531 self
.users
= {un
: User
.from_dict(u
, dict(self
.roles
, **SYSTEM_ROLES
))
532 for un
, u
in v1_db
.get('users', {}).items()}
536 # If version 1 does not exist, check if migration of VERSION "0" needs to be done
537 check_migrate_v0_to_current()
539 check_migrate_v1_to_current()
543 logger
.info("Loading user roles DB version=%s", cls
.VERSION
)
545 json_db
= mgr
.get_store(cls
.accessdb_config_key())
547 logger
.debug("No DB v%s found, creating new...", cls
.VERSION
)
548 db
= cls(cls
.VERSION
, {}, {})
549 # check if we can update from a previous version database
550 db
.check_and_update_db()
553 dict_db
= json
.loads(json_db
)
554 roles
= {rn
: Role
.from_dict(r
)
555 for rn
, r
in dict_db
.get('roles', {}).items()}
556 users
= {un
: User
.from_dict(u
, dict(roles
, **SYSTEM_ROLES
))
557 for un
, u
in dict_db
.get('users', {}).items()}
558 return cls(dict_db
['version'], users
, roles
)
561 def load_access_control_db():
562 mgr
.ACCESS_CTRL_DB
= AccessControlDB
.load()
565 # CLI dashboard access control scope commands
567 @CLIWriteCommand('dashboard set-login-credentials',
568 'name=username,type=CephString '
569 'name=password,type=CephString',
570 'Set the login credentials')
571 def set_login_credentials_cmd(_
, username
, password
):
573 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
574 user
.set_password(password
)
575 except UserDoesNotExist
:
576 user
= mgr
.ACCESS_CTRL_DB
.create_user(username
, password
, None, None)
577 user
.set_roles([ADMIN_ROLE
])
579 mgr
.ACCESS_CTRL_DB
.save()
582 ******************************************************************
583 *** WARNING: this command is deprecated. ***
584 *** Please use the ac-user-* related commands to manage users. ***
585 ******************************************************************
586 Username and password updated''', ''
589 @CLIReadCommand('dashboard ac-role-show',
590 'name=rolename,type=CephString,req=false',
592 def ac_role_show_cmd(_
, rolename
=None):
594 roles
= dict(mgr
.ACCESS_CTRL_DB
.roles
)
595 roles
.update(SYSTEM_ROLES
)
596 roles_list
= [name
for name
, _
in roles
.items()]
597 return 0, json
.dumps(roles_list
), ''
599 role
= mgr
.ACCESS_CTRL_DB
.get_role(rolename
)
600 except RoleDoesNotExist
as ex
:
601 if rolename
not in SYSTEM_ROLES
:
602 return -errno
.ENOENT
, '', str(ex
)
603 role
= SYSTEM_ROLES
[rolename
]
604 return 0, json
.dumps(role
.to_dict()), ''
607 @CLIWriteCommand('dashboard ac-role-create',
608 'name=rolename,type=CephString '
609 'name=description,type=CephString,req=false',
610 'Create a new access control role')
611 def ac_role_create_cmd(_
, rolename
, description
=None):
613 role
= mgr
.ACCESS_CTRL_DB
.create_role(rolename
, description
)
614 mgr
.ACCESS_CTRL_DB
.save()
615 return 0, json
.dumps(role
.to_dict()), ''
616 except RoleAlreadyExists
as ex
:
617 return -errno
.EEXIST
, '', str(ex
)
620 @CLIWriteCommand('dashboard ac-role-delete',
621 'name=rolename,type=CephString',
622 'Delete an access control role')
623 def ac_role_delete_cmd(_
, rolename
):
625 mgr
.ACCESS_CTRL_DB
.delete_role(rolename
)
626 mgr
.ACCESS_CTRL_DB
.save()
627 return 0, "Role '{}' deleted".format(rolename
), ""
628 except RoleDoesNotExist
as ex
:
629 if rolename
in SYSTEM_ROLES
:
630 return -errno
.EPERM
, '', "Cannot delete system role '{}'" \
632 return -errno
.ENOENT
, '', str(ex
)
633 except RoleIsAssociatedWithUser
as ex
:
634 return -errno
.EPERM
, '', str(ex
)
637 @CLIWriteCommand('dashboard ac-role-add-scope-perms',
638 'name=rolename,type=CephString '
639 'name=scopename,type=CephString '
640 'name=permissions,type=CephString,n=N',
641 'Add the scope permissions for a role')
642 def ac_role_add_scope_perms_cmd(_
, rolename
, scopename
, permissions
):
644 role
= mgr
.ACCESS_CTRL_DB
.get_role(rolename
)
645 perms_array
= [perm
.strip() for perm
in permissions
]
646 role
.set_scope_permissions(scopename
, perms_array
)
647 mgr
.ACCESS_CTRL_DB
.update_users_with_roles(role
)
648 mgr
.ACCESS_CTRL_DB
.save()
649 return 0, json
.dumps(role
.to_dict()), ''
650 except RoleDoesNotExist
as ex
:
651 if rolename
in SYSTEM_ROLES
:
652 return -errno
.EPERM
, '', "Cannot update system role '{}'" \
654 return -errno
.ENOENT
, '', str(ex
)
655 except ScopeNotValid
as ex
:
656 return -errno
.EINVAL
, '', str(ex
) + "\n Possible values: {}" \
657 .format(Scope
.all_scopes())
658 except PermissionNotValid
as ex
:
659 return -errno
.EINVAL
, '', str(ex
) + \
660 "\n Possible values: {}" \
661 .format(Permission
.all_permissions())
664 @CLIWriteCommand('dashboard ac-role-del-scope-perms',
665 'name=rolename,type=CephString '
666 'name=scopename,type=CephString',
667 'Delete the scope permissions for a role')
668 def ac_role_del_scope_perms_cmd(_
, rolename
, scopename
):
670 role
= mgr
.ACCESS_CTRL_DB
.get_role(rolename
)
671 role
.del_scope_permissions(scopename
)
672 mgr
.ACCESS_CTRL_DB
.update_users_with_roles(role
)
673 mgr
.ACCESS_CTRL_DB
.save()
674 return 0, json
.dumps(role
.to_dict()), ''
675 except RoleDoesNotExist
as ex
:
676 if rolename
in SYSTEM_ROLES
:
677 return -errno
.EPERM
, '', "Cannot update system role '{}'" \
679 return -errno
.ENOENT
, '', str(ex
)
680 except ScopeNotInRole
as ex
:
681 return -errno
.ENOENT
, '', str(ex
)
684 @CLIReadCommand('dashboard ac-user-show',
685 'name=username,type=CephString,req=false',
687 def ac_user_show_cmd(_
, username
=None):
689 users
= mgr
.ACCESS_CTRL_DB
.users
690 users_list
= [name
for name
, _
in users
.items()]
691 return 0, json
.dumps(users_list
), ''
693 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
694 return 0, json
.dumps(user
.to_dict()), ''
695 except UserDoesNotExist
as ex
:
696 return -errno
.ENOENT
, '', str(ex
)
699 @CLIWriteCommand('dashboard ac-user-create',
700 'name=username,type=CephString '
701 'name=password,type=CephString,req=false '
702 'name=rolename,type=CephString,req=false '
703 'name=name,type=CephString,req=false '
704 'name=email,type=CephString,req=false '
705 'name=enabled,type=CephBool,req=false '
706 'name=force_password,type=CephBool,req=false '
707 'name=pwd_expiration_date,type=CephInt,req=false '
708 'name=pwd_update_required,type=CephBool,req=false',
710 def ac_user_create_cmd(_
, username
, password
=None, rolename
=None, name
=None,
711 email
=None, enabled
=True, force_password
=False,
712 pwd_expiration_date
=None, pwd_update_required
=False):
714 role
= mgr
.ACCESS_CTRL_DB
.get_role(rolename
) if rolename
else None
715 except RoleDoesNotExist
as ex
:
716 if rolename
not in SYSTEM_ROLES
:
717 return -errno
.ENOENT
, '', str(ex
)
718 role
= SYSTEM_ROLES
[rolename
]
721 if not force_password
:
722 pw_check
= PasswordPolicy(password
, username
)
724 user
= mgr
.ACCESS_CTRL_DB
.create_user(username
, password
, name
, email
,
725 enabled
, pwd_expiration_date
,
727 except PasswordPolicyException
as ex
:
728 return -errno
.EINVAL
, '', str(ex
)
729 except UserAlreadyExists
as ex
:
730 return -errno
.EEXIST
, '', str(ex
)
733 user
.set_roles([role
])
734 mgr
.ACCESS_CTRL_DB
.save()
735 return 0, json
.dumps(user
.to_dict()), ''
738 @CLIWriteCommand('dashboard ac-user-enable',
739 'name=username,type=CephString',
741 def ac_user_enable(_
, username
):
743 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
746 mgr
.ACCESS_CTRL_DB
.save()
747 return 0, json
.dumps(user
.to_dict()), ''
748 except UserDoesNotExist
as ex
:
749 return -errno
.ENOENT
, '', str(ex
)
752 @CLIWriteCommand('dashboard ac-user-disable',
753 'name=username,type=CephString',
755 def ac_user_disable(_
, username
):
757 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
760 mgr
.ACCESS_CTRL_DB
.save()
761 return 0, json
.dumps(user
.to_dict()), ''
762 except UserDoesNotExist
as ex
:
763 return -errno
.ENOENT
, '', str(ex
)
766 @CLIWriteCommand('dashboard ac-user-delete',
767 'name=username,type=CephString',
769 def ac_user_delete_cmd(_
, username
):
771 mgr
.ACCESS_CTRL_DB
.delete_user(username
)
772 mgr
.ACCESS_CTRL_DB
.save()
773 return 0, "User '{}' deleted".format(username
), ""
774 except UserDoesNotExist
as ex
:
775 return -errno
.ENOENT
, '', str(ex
)
778 @CLIWriteCommand('dashboard ac-user-set-roles',
779 'name=username,type=CephString '
780 'name=roles,type=CephString,n=N',
782 def ac_user_set_roles_cmd(_
, username
, roles
):
785 for rolename
in rolesname
:
787 roles
.append(mgr
.ACCESS_CTRL_DB
.get_role(rolename
))
788 except RoleDoesNotExist
as ex
:
789 if rolename
not in SYSTEM_ROLES
:
790 return -errno
.ENOENT
, '', str(ex
)
791 roles
.append(SYSTEM_ROLES
[rolename
])
793 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
794 user
.set_roles(roles
)
795 mgr
.ACCESS_CTRL_DB
.save()
796 return 0, json
.dumps(user
.to_dict()), ''
797 except UserDoesNotExist
as ex
:
798 return -errno
.ENOENT
, '', str(ex
)
801 @CLIWriteCommand('dashboard ac-user-add-roles',
802 'name=username,type=CephString '
803 'name=roles,type=CephString,n=N',
805 def ac_user_add_roles_cmd(_
, username
, roles
):
808 for rolename
in rolesname
:
810 roles
.append(mgr
.ACCESS_CTRL_DB
.get_role(rolename
))
811 except RoleDoesNotExist
as ex
:
812 if rolename
not in SYSTEM_ROLES
:
813 return -errno
.ENOENT
, '', str(ex
)
814 roles
.append(SYSTEM_ROLES
[rolename
])
816 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
817 user
.add_roles(roles
)
818 mgr
.ACCESS_CTRL_DB
.save()
819 return 0, json
.dumps(user
.to_dict()), ''
820 except UserDoesNotExist
as ex
:
821 return -errno
.ENOENT
, '', str(ex
)
824 @CLIWriteCommand('dashboard ac-user-del-roles',
825 'name=username,type=CephString '
826 'name=roles,type=CephString,n=N',
827 'Delete roles from user')
828 def ac_user_del_roles_cmd(_
, username
, roles
):
831 for rolename
in rolesname
:
833 roles
.append(mgr
.ACCESS_CTRL_DB
.get_role(rolename
))
834 except RoleDoesNotExist
as ex
:
835 if rolename
not in SYSTEM_ROLES
:
836 return -errno
.ENOENT
, '', str(ex
)
837 roles
.append(SYSTEM_ROLES
[rolename
])
839 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
840 user
.del_roles(roles
)
841 mgr
.ACCESS_CTRL_DB
.save()
842 return 0, json
.dumps(user
.to_dict()), ''
843 except UserDoesNotExist
as ex
:
844 return -errno
.ENOENT
, '', str(ex
)
845 except RoleNotInUser
as ex
:
846 return -errno
.ENOENT
, '', str(ex
)
849 @CLIWriteCommand('dashboard ac-user-set-password',
850 'name=username,type=CephString '
851 'name=password,type=CephString '
852 'name=force_password,type=CephBool,req=false',
854 def ac_user_set_password(_
, username
, password
, force_password
=False):
856 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
857 if not force_password
:
858 pw_check
= PasswordPolicy(password
, user
.name
)
860 user
.set_password(password
)
861 mgr
.ACCESS_CTRL_DB
.save()
862 return 0, json
.dumps(user
.to_dict()), ''
863 except PasswordPolicyException
as ex
:
864 return -errno
.EINVAL
, '', str(ex
)
865 except UserDoesNotExist
as ex
:
866 return -errno
.ENOENT
, '', str(ex
)
869 @CLIWriteCommand('dashboard ac-user-set-password-hash',
870 'name=username,type=CephString '
871 'name=hashed_password,type=CephString',
872 'Set user password bcrypt hash')
873 def ac_user_set_password_hash(_
, username
, hashed_password
):
875 # make sure the hashed_password is actually a bcrypt hash
876 bcrypt
.checkpw(b
'', hashed_password
.encode('utf-8'))
877 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
878 user
.set_password_hash(hashed_password
)
880 mgr
.ACCESS_CTRL_DB
.save()
881 return 0, json
.dumps(user
.to_dict()), ''
883 return -errno
.EINVAL
, '', 'Invalid password hash'
884 except UserDoesNotExist
as ex
:
885 return -errno
.ENOENT
, '', str(ex
)
888 @CLIWriteCommand('dashboard ac-user-set-info',
889 'name=username,type=CephString '
890 'name=name,type=CephString '
891 'name=email,type=CephString',
893 def ac_user_set_info(_
, username
, name
, email
):
895 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
900 mgr
.ACCESS_CTRL_DB
.save()
901 return 0, json
.dumps(user
.to_dict()), ''
902 except UserDoesNotExist
as ex
:
903 return -errno
.ENOENT
, '', str(ex
)
906 class LocalAuthenticator(object):
908 load_access_control_db()
910 def get_user(self
, username
):
911 return mgr
.ACCESS_CTRL_DB
.get_user(username
)
913 def authenticate(self
, username
, password
):
915 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
917 if user
.enabled
and user
.compare_password(password
) \
918 and not user
.is_pwd_expired():
919 return {'permissions': user
.permissions_dict(),
920 'pwdExpirationDate': user
.pwd_expiration_date
,
921 'pwdUpdateRequired': user
.pwd_update_required
}
922 except UserDoesNotExist
:
923 logger
.debug("User '%s' does not exist", username
)
926 def authorize(self
, username
, scope
, permissions
):
927 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
928 return user
.authorize(scope
, permissions
)