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
18 from mgr_module
import CLICheckNonemptyFileInput
, CLIReadCommand
, CLIWriteCommand
21 from ..security
import Scope
, Permission
22 from ..settings
import Settings
23 from ..exceptions
import RoleAlreadyExists
, RoleDoesNotExist
, ScopeNotValid
, \
24 PermissionNotValid
, RoleIsAssociatedWithUser
, \
25 UserAlreadyExists
, UserDoesNotExist
, ScopeNotInRole
, \
26 RoleNotInUser
, PasswordPolicyException
, PwdExpirationDateNotValid
29 logger
= logging
.getLogger('access_control')
32 # password hashing algorithm
33 def password_hash(password
, salt_password
=None):
37 salt_password
= bcrypt
.gensalt()
39 salt_password
= salt_password
.encode('utf8')
40 return bcrypt
.hashpw(password
.encode('utf8'), salt_password
).decode('utf8')
43 _P
= Permission
# short alias
46 class PasswordPolicy(object):
47 def __init__(self
, password
, username
=None, old_password
=None):
49 :param password: The new plain password.
51 :param username: The name of the user.
52 :type username: str | None
53 :param old_password: The old plain password.
54 :type old_password: str | None
56 self
.password
= password
57 self
.username
= username
58 self
.old_password
= old_password
59 self
.forbidden_words
= Settings
.PWD_POLICY_EXCLUSION_LIST
.split(',')
60 self
.complexity_credits
= 0
63 def _check_if_contains_word(password
, word
):
64 return re
.compile('(?:{0})'.format(word
),
65 flags
=re
.IGNORECASE
).search(password
)
67 def check_password_complexity(self
):
68 if not Settings
.PWD_POLICY_CHECK_COMPLEXITY_ENABLED
:
69 return Settings
.PWD_POLICY_MIN_COMPLEXITY
71 small_letter_credit
= 1
73 special_character_credit
= 3
74 other_character_credit
= 5
75 self
.complexity_credits
= 0
76 for ch
in self
.password
:
77 if ch
in ascii_uppercase
:
78 self
.complexity_credits
+= big_letter_credit
79 elif ch
in ascii_lowercase
:
80 self
.complexity_credits
+= small_letter_credit
82 self
.complexity_credits
+= digit_credit
83 elif ch
in punctuation
:
84 self
.complexity_credits
+= special_character_credit
86 self
.complexity_credits
+= other_character_credit
87 return self
.complexity_credits
89 def check_is_old_password(self
):
90 if not Settings
.PWD_POLICY_CHECK_OLDPWD_ENABLED
:
92 return self
.old_password
and self
.password
== self
.old_password
94 def check_if_contains_username(self
):
95 if not Settings
.PWD_POLICY_CHECK_USERNAME_ENABLED
:
99 return self
._check
_if
_contains
_word
(self
.password
, self
.username
)
101 def check_if_contains_forbidden_words(self
):
102 if not Settings
.PWD_POLICY_CHECK_EXCLUSION_LIST_ENABLED
:
104 return self
._check
_if
_contains
_word
(self
.password
,
105 '|'.join(self
.forbidden_words
))
107 def check_if_sequential_characters(self
):
108 if not Settings
.PWD_POLICY_CHECK_SEQUENTIAL_CHARS_ENABLED
:
110 for i
in range(1, len(self
.password
) - 1):
111 if ord(self
.password
[i
- 1]) + 1 == ord(self
.password
[i
])\
112 == ord(self
.password
[i
+ 1]) - 1:
116 def check_if_repetitive_characters(self
):
117 if not Settings
.PWD_POLICY_CHECK_REPETITIVE_CHARS_ENABLED
:
119 for i
in range(1, len(self
.password
) - 1):
120 if self
.password
[i
- 1] == self
.password
[i
] == self
.password
[i
+ 1]:
124 def check_password_length(self
):
125 if not Settings
.PWD_POLICY_CHECK_LENGTH_ENABLED
:
127 return len(self
.password
) >= Settings
.PWD_POLICY_MIN_LENGTH
131 Perform all password policy checks.
132 :raise PasswordPolicyException: If a password policy check fails.
134 if not Settings
.PWD_POLICY_ENABLED
:
136 if self
.check_password_complexity() < Settings
.PWD_POLICY_MIN_COMPLEXITY
:
137 raise PasswordPolicyException('Password is too weak.')
138 if not self
.check_password_length():
139 raise PasswordPolicyException('Password is too weak.')
140 if self
.check_is_old_password():
141 raise PasswordPolicyException('Password must not be the same as the previous one.')
142 if self
.check_if_contains_username():
143 raise PasswordPolicyException('Password must not contain username.')
144 result
= self
.check_if_contains_forbidden_words()
146 raise PasswordPolicyException('Password must not contain the keyword "{}".'.format(
148 if self
.check_if_repetitive_characters():
149 raise PasswordPolicyException('Password must not contain repetitive characters.')
150 if self
.check_if_sequential_characters():
151 raise PasswordPolicyException('Password must not contain sequential characters.')
155 def __init__(self
, name
, description
=None, scope_permissions
=None):
157 self
.description
= description
158 if scope_permissions
is None:
159 self
.scopes_permissions
= {}
161 self
.scopes_permissions
= scope_permissions
164 return hash(self
.name
)
166 def __eq__(self
, other
):
167 return self
.name
== other
.name
169 def set_scope_permissions(self
, scope
, permissions
):
170 if not Scope
.valid_scope(scope
):
171 raise ScopeNotValid(scope
)
172 for perm
in permissions
:
173 if not Permission
.valid_permission(perm
):
174 raise PermissionNotValid(perm
)
177 self
.scopes_permissions
[scope
] = permissions
179 def del_scope_permissions(self
, scope
):
180 if scope
not in self
.scopes_permissions
:
181 raise ScopeNotInRole(scope
, self
.name
)
182 del self
.scopes_permissions
[scope
]
184 def reset_scope_permissions(self
):
185 self
.scopes_permissions
= {}
187 def authorize(self
, scope
, permissions
):
188 if scope
in self
.scopes_permissions
:
189 role_perms
= self
.scopes_permissions
[scope
]
190 for perm
in permissions
:
191 if perm
not in role_perms
:
199 'description': self
.description
,
200 'scopes_permissions': self
.scopes_permissions
204 def from_dict(cls
, r_dict
):
205 return Role(r_dict
['name'], r_dict
['description'],
206 r_dict
['scopes_permissions'])
209 # static pre-defined system roles
210 # this roles cannot be deleted nor updated
212 # admin role provides all permissions for all scopes
213 ADMIN_ROLE
= Role('administrator', 'Administrator', {
214 scope_name
: Permission
.all_permissions()
215 for scope_name
in Scope
.all_scopes()
219 # read-only role provides read-only permission for all scopes
220 READ_ONLY_ROLE
= Role('read-only', 'Read-Only', {
221 scope_name
: [_P
.READ
] for scope_name
in Scope
.all_scopes()
222 if scope_name
!= Scope
.DASHBOARD_SETTINGS
226 # block manager role provides all permission for block related scopes
227 BLOCK_MGR_ROLE
= Role('block-manager', 'Block Manager', {
228 Scope
.RBD_IMAGE
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
229 Scope
.POOL
: [_P
.READ
],
230 Scope
.ISCSI
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
231 Scope
.RBD_MIRRORING
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
232 Scope
.GRAFANA
: [_P
.READ
],
236 # RadosGW manager role provides all permissions for block related scopes
237 RGW_MGR_ROLE
= Role('rgw-manager', 'RGW Manager', {
238 Scope
.RGW
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
239 Scope
.GRAFANA
: [_P
.READ
],
243 # Cluster manager role provides all permission for OSDs, Monitors, and
245 CLUSTER_MGR_ROLE
= Role('cluster-manager', 'Cluster Manager', {
246 Scope
.HOSTS
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
247 Scope
.OSD
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
248 Scope
.MONITOR
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
249 Scope
.MANAGER
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
250 Scope
.CONFIG_OPT
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
251 Scope
.LOG
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
252 Scope
.GRAFANA
: [_P
.READ
],
256 # Pool manager role provides all permissions for pool related scopes
257 POOL_MGR_ROLE
= Role('pool-manager', 'Pool Manager', {
258 Scope
.POOL
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
259 Scope
.GRAFANA
: [_P
.READ
],
262 # CephFS manager role provides all permissions for CephFS related scopes
263 CEPHFS_MGR_ROLE
= Role('cephfs-manager', 'CephFS Manager', {
264 Scope
.CEPHFS
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
265 Scope
.GRAFANA
: [_P
.READ
],
268 GANESHA_MGR_ROLE
= Role('ganesha-manager', 'NFS Ganesha Manager', {
269 Scope
.NFS_GANESHA
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
270 Scope
.CEPHFS
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
271 Scope
.RGW
: [_P
.READ
, _P
.CREATE
, _P
.UPDATE
, _P
.DELETE
],
272 Scope
.GRAFANA
: [_P
.READ
],
277 ADMIN_ROLE
.name
: ADMIN_ROLE
,
278 READ_ONLY_ROLE
.name
: READ_ONLY_ROLE
,
279 BLOCK_MGR_ROLE
.name
: BLOCK_MGR_ROLE
,
280 RGW_MGR_ROLE
.name
: RGW_MGR_ROLE
,
281 CLUSTER_MGR_ROLE
.name
: CLUSTER_MGR_ROLE
,
282 POOL_MGR_ROLE
.name
: POOL_MGR_ROLE
,
283 CEPHFS_MGR_ROLE
.name
: CEPHFS_MGR_ROLE
,
284 GANESHA_MGR_ROLE
.name
: GANESHA_MGR_ROLE
,
289 def __init__(self
, username
, password
, name
=None, email
=None, roles
=None,
290 last_update
=None, enabled
=True, pwd_expiration_date
=None,
291 pwd_update_required
=False):
292 self
.username
= username
293 self
.password
= password
296 self
.invalid_auth_attempt
= 0
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
.invalid_auth_attempt
= 0
336 self
.password
= hashed_password
337 self
.refresh_last_update()
338 self
.refresh_pwd_expiration_date()
339 self
.pwd_update_required
= False
341 def compare_password(self
, password
):
343 Compare the specified password with the user password.
344 :param password: The plain password to check.
346 :return: `True` if the passwords are equal, otherwise `False`.
349 pass_hash
= password_hash(password
, salt_password
=self
.password
)
350 return pass_hash
== self
.password
352 def is_pwd_expired(self
):
353 if self
.pwd_expiration_date
:
354 current_time
= int(time
.mktime(datetime
.utcnow().timetuple()))
355 return self
.pwd_expiration_date
< current_time
358 def set_roles(self
, roles
):
359 self
.roles
= set(roles
)
360 self
.refresh_last_update()
362 def add_roles(self
, roles
):
363 self
.roles
= self
.roles
.union(set(roles
))
364 self
.refresh_last_update()
366 def del_roles(self
, roles
):
368 if role
not in self
.roles
:
369 raise RoleNotInUser(role
.name
, self
.username
)
370 self
.roles
.difference_update(set(roles
))
371 self
.refresh_last_update()
373 def authorize(self
, scope
, permissions
):
374 if self
.pwd_update_required
:
377 for role
in self
.roles
:
378 if role
.authorize(scope
, permissions
):
382 def permissions_dict(self
):
384 perms
= {} # type: dict
385 for role
in self
.roles
:
386 for scope
, perms_list
in role
.scopes_permissions
.items():
388 perms_tmp
= set(perms
[scope
]).union(set(perms_list
))
389 perms
[scope
] = list(perms_tmp
)
391 perms
[scope
] = perms_list
397 'username': self
.username
,
398 'password': self
.password
,
399 'roles': sorted([r
.name
for r
in self
.roles
]),
402 'lastUpdate': self
.last_update
,
403 'enabled': self
.enabled
,
404 'pwdExpirationDate': self
.pwd_expiration_date
,
405 'pwdUpdateRequired': self
.pwd_update_required
409 def from_dict(cls
, u_dict
, roles
):
410 return User(u_dict
['username'], u_dict
['password'], u_dict
['name'],
411 u_dict
['email'], {roles
[r
] for r
in u_dict
['roles']},
412 u_dict
['lastUpdate'], u_dict
['enabled'],
413 u_dict
['pwdExpirationDate'], u_dict
['pwdUpdateRequired'])
416 class AccessControlDB(object):
418 ACDB_CONFIG_KEY
= "accessdb_v"
420 def __init__(self
, version
, users
, roles
):
422 self
.version
= version
424 self
.lock
= threading
.RLock()
426 def create_role(self
, name
, description
=None):
428 if name
in SYSTEM_ROLES
or name
in self
.roles
:
429 raise RoleAlreadyExists(name
)
430 role
= Role(name
, description
)
431 self
.roles
[name
] = role
434 def get_role(self
, name
):
436 if name
not in self
.roles
:
437 raise RoleDoesNotExist(name
)
438 return self
.roles
[name
]
440 def increment_attempt(self
, username
):
442 if username
in self
.users
:
443 self
.users
[username
].invalid_auth_attempt
+= 1
445 def reset_attempt(self
, username
):
447 if username
in self
.users
:
448 self
.users
[username
].invalid_auth_attempt
= 0
450 def get_attempt(self
, username
):
453 return self
.users
[username
].invalid_auth_attempt
457 def delete_role(self
, name
):
459 if name
not in self
.roles
:
460 raise RoleDoesNotExist(name
)
461 role
= self
.roles
[name
]
463 # check if role is not associated with a user
464 for username
, user
in self
.users
.items():
465 if role
in user
.roles
:
466 raise RoleIsAssociatedWithUser(name
, username
)
470 def create_user(self
, username
, password
, name
, email
, enabled
=True,
471 pwd_expiration_date
=None, pwd_update_required
=False):
472 logger
.debug("creating user: username=%s", username
)
474 if username
in self
.users
:
475 raise UserAlreadyExists(username
)
476 if pwd_expiration_date
and \
477 (pwd_expiration_date
< int(time
.mktime(datetime
.utcnow().timetuple()))):
478 raise PwdExpirationDateNotValid()
479 user
= User(username
, password_hash(password
), name
, email
, enabled
=enabled
,
480 pwd_expiration_date
=pwd_expiration_date
,
481 pwd_update_required
=pwd_update_required
)
482 self
.users
[username
] = user
485 def get_user(self
, username
):
487 if username
not in self
.users
:
488 raise UserDoesNotExist(username
)
489 return self
.users
[username
]
491 def delete_user(self
, username
):
493 if username
not in self
.users
:
494 raise UserDoesNotExist(username
)
495 del self
.users
[username
]
497 def update_users_with_roles(self
, role
):
501 for _
, user
in self
.users
.items():
502 if role
in user
.roles
:
503 user
.refresh_last_update()
508 'users': {un
: u
.to_dict() for un
, u
in self
.users
.items()},
509 'roles': {rn
: r
.to_dict() for rn
, r
in self
.roles
.items()},
510 'version': self
.version
512 mgr
.set_store(self
.accessdb_config_key(), json
.dumps(db
))
515 def accessdb_config_key(cls
, version
=None):
517 version
= cls
.VERSION
518 return "{}{}".format(cls
.ACDB_CONFIG_KEY
, version
)
520 def check_and_update_db(self
):
521 logger
.debug("Checking for previews DB versions")
523 def check_migrate_v0_to_current():
524 # check if there is username/password from previous version
525 username
= mgr
.get_module_option('username', None)
526 password
= mgr
.get_module_option('password', None)
527 if username
and password
:
528 logger
.debug("Found single user credentials: user=%s", username
)
529 # found user credentials
530 user
= self
.create_user(username
, "", None, None)
531 # password is already hashed, so setting manually
532 user
.password
= password
533 user
.add_roles([ADMIN_ROLE
])
536 def check_migrate_v1_to_current():
537 # Check if version 1 exists in the DB and migrate it to current version
538 v1_db
= mgr
.get_store(self
.accessdb_config_key(1))
540 logger
.debug("Found database v1 credentials")
541 v1_db
= json
.loads(v1_db
)
543 for user
, _
in v1_db
['users'].items():
544 v1_db
['users'][user
]['enabled'] = True
545 v1_db
['users'][user
]['pwdExpirationDate'] = None
546 v1_db
['users'][user
]['pwdUpdateRequired'] = False
548 self
.roles
= {rn
: Role
.from_dict(r
) for rn
, r
in v1_db
.get('roles', {}).items()}
549 self
.users
= {un
: User
.from_dict(u
, dict(self
.roles
, **SYSTEM_ROLES
))
550 for un
, u
in v1_db
.get('users', {}).items()}
554 # If version 1 does not exist, check if migration of VERSION "0" needs to be done
555 check_migrate_v0_to_current()
557 check_migrate_v1_to_current()
561 logger
.info("Loading user roles DB version=%s", cls
.VERSION
)
563 json_db
= mgr
.get_store(cls
.accessdb_config_key())
565 logger
.debug("No DB v%s found, creating new...", cls
.VERSION
)
566 db
= cls(cls
.VERSION
, {}, {})
567 # check if we can update from a previous version database
568 db
.check_and_update_db()
571 dict_db
= json
.loads(json_db
)
572 roles
= {rn
: Role
.from_dict(r
)
573 for rn
, r
in dict_db
.get('roles', {}).items()}
574 users
= {un
: User
.from_dict(u
, dict(roles
, **SYSTEM_ROLES
))
575 for un
, u
in dict_db
.get('users', {}).items()}
576 return cls(dict_db
['version'], users
, roles
)
579 def load_access_control_db():
580 mgr
.ACCESS_CTRL_DB
= AccessControlDB
.load()
583 # CLI dashboard access control scope commands
585 @CLIWriteCommand('dashboard set-login-credentials',
586 'name=username,type=CephString',
587 'Set the login credentials. Password read from -i <file>')
588 @CLICheckNonemptyFileInput
589 def set_login_credentials_cmd(_
, username
, inbuf
):
592 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
593 user
.set_password(password
)
594 except UserDoesNotExist
:
595 user
= mgr
.ACCESS_CTRL_DB
.create_user(username
, password
, None, None)
596 user
.set_roles([ADMIN_ROLE
])
598 mgr
.ACCESS_CTRL_DB
.save()
601 ******************************************************************
602 *** WARNING: this command is deprecated. ***
603 *** Please use the ac-user-* related commands to manage users. ***
604 ******************************************************************
605 Username and password updated''', ''
608 @CLIReadCommand('dashboard ac-role-show',
609 'name=rolename,type=CephString,req=false',
611 def ac_role_show_cmd(_
, rolename
=None):
613 roles
= dict(mgr
.ACCESS_CTRL_DB
.roles
)
614 roles
.update(SYSTEM_ROLES
)
615 roles_list
= [name
for name
, _
in roles
.items()]
616 return 0, json
.dumps(roles_list
), ''
618 role
= mgr
.ACCESS_CTRL_DB
.get_role(rolename
)
619 except RoleDoesNotExist
as ex
:
620 if rolename
not in SYSTEM_ROLES
:
621 return -errno
.ENOENT
, '', str(ex
)
622 role
= SYSTEM_ROLES
[rolename
]
623 return 0, json
.dumps(role
.to_dict()), ''
626 @CLIWriteCommand('dashboard ac-role-create',
627 'name=rolename,type=CephString '
628 'name=description,type=CephString,req=false',
629 'Create a new access control role')
630 def ac_role_create_cmd(_
, rolename
, description
=None):
632 role
= mgr
.ACCESS_CTRL_DB
.create_role(rolename
, description
)
633 mgr
.ACCESS_CTRL_DB
.save()
634 return 0, json
.dumps(role
.to_dict()), ''
635 except RoleAlreadyExists
as ex
:
636 return -errno
.EEXIST
, '', str(ex
)
639 @CLIWriteCommand('dashboard ac-role-delete',
640 'name=rolename,type=CephString',
641 'Delete an access control role')
642 def ac_role_delete_cmd(_
, rolename
):
644 mgr
.ACCESS_CTRL_DB
.delete_role(rolename
)
645 mgr
.ACCESS_CTRL_DB
.save()
646 return 0, "Role '{}' deleted".format(rolename
), ""
647 except RoleDoesNotExist
as ex
:
648 if rolename
in SYSTEM_ROLES
:
649 return -errno
.EPERM
, '', "Cannot delete system role '{}'" \
651 return -errno
.ENOENT
, '', str(ex
)
652 except RoleIsAssociatedWithUser
as ex
:
653 return -errno
.EPERM
, '', str(ex
)
656 @CLIWriteCommand('dashboard ac-role-add-scope-perms',
657 'name=rolename,type=CephString '
658 'name=scopename,type=CephString '
659 'name=permissions,type=CephString,n=N',
660 'Add the scope permissions for a role')
661 def ac_role_add_scope_perms_cmd(_
, rolename
, scopename
, permissions
):
663 role
= mgr
.ACCESS_CTRL_DB
.get_role(rolename
)
664 perms_array
= [perm
.strip() for perm
in permissions
]
665 role
.set_scope_permissions(scopename
, perms_array
)
666 mgr
.ACCESS_CTRL_DB
.update_users_with_roles(role
)
667 mgr
.ACCESS_CTRL_DB
.save()
668 return 0, json
.dumps(role
.to_dict()), ''
669 except RoleDoesNotExist
as ex
:
670 if rolename
in SYSTEM_ROLES
:
671 return -errno
.EPERM
, '', "Cannot update system role '{}'" \
673 return -errno
.ENOENT
, '', str(ex
)
674 except ScopeNotValid
as ex
:
675 return -errno
.EINVAL
, '', str(ex
) + "\n Possible values: {}" \
676 .format(Scope
.all_scopes())
677 except PermissionNotValid
as ex
:
678 return -errno
.EINVAL
, '', str(ex
) + \
679 "\n Possible values: {}" \
680 .format(Permission
.all_permissions())
683 @CLIWriteCommand('dashboard ac-role-del-scope-perms',
684 'name=rolename,type=CephString '
685 'name=scopename,type=CephString',
686 'Delete the scope permissions for a role')
687 def ac_role_del_scope_perms_cmd(_
, rolename
, scopename
):
689 role
= mgr
.ACCESS_CTRL_DB
.get_role(rolename
)
690 role
.del_scope_permissions(scopename
)
691 mgr
.ACCESS_CTRL_DB
.update_users_with_roles(role
)
692 mgr
.ACCESS_CTRL_DB
.save()
693 return 0, json
.dumps(role
.to_dict()), ''
694 except RoleDoesNotExist
as ex
:
695 if rolename
in SYSTEM_ROLES
:
696 return -errno
.EPERM
, '', "Cannot update system role '{}'" \
698 return -errno
.ENOENT
, '', str(ex
)
699 except ScopeNotInRole
as ex
:
700 return -errno
.ENOENT
, '', str(ex
)
703 @CLIReadCommand('dashboard ac-user-show',
704 'name=username,type=CephString,req=false',
706 def ac_user_show_cmd(_
, username
=None):
708 users
= mgr
.ACCESS_CTRL_DB
.users
709 users_list
= [name
for name
, _
in users
.items()]
710 return 0, json
.dumps(users_list
), ''
712 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
713 return 0, json
.dumps(user
.to_dict()), ''
714 except UserDoesNotExist
as ex
:
715 return -errno
.ENOENT
, '', str(ex
)
718 @CLIWriteCommand('dashboard ac-user-create',
719 'name=username,type=CephString '
720 'name=rolename,type=CephString,req=false '
721 'name=name,type=CephString,req=false '
722 'name=email,type=CephString,req=false '
723 'name=enabled,type=CephBool,req=false '
724 'name=force_password,type=CephBool,req=false '
725 'name=pwd_expiration_date,type=CephInt,req=false '
726 'name=pwd_update_required,type=CephBool,req=false',
727 'Create a user. Password read from -i <file>')
728 @CLICheckNonemptyFileInput
729 def ac_user_create_cmd(_
, username
, inbuf
, rolename
=None, name
=None,
730 email
=None, enabled
=True, force_password
=False,
731 pwd_expiration_date
=None, pwd_update_required
=False):
734 role
= mgr
.ACCESS_CTRL_DB
.get_role(rolename
) if rolename
else None
735 except RoleDoesNotExist
as ex
:
736 if rolename
not in SYSTEM_ROLES
:
737 return -errno
.ENOENT
, '', str(ex
)
738 role
= SYSTEM_ROLES
[rolename
]
741 if not force_password
:
742 pw_check
= PasswordPolicy(password
, username
)
744 user
= mgr
.ACCESS_CTRL_DB
.create_user(username
, password
, name
, email
,
745 enabled
, pwd_expiration_date
,
747 except PasswordPolicyException
as ex
:
748 return -errno
.EINVAL
, '', str(ex
)
749 except UserAlreadyExists
as ex
:
750 return 0, str(ex
), ''
753 user
.set_roles([role
])
754 mgr
.ACCESS_CTRL_DB
.save()
755 return 0, json
.dumps(user
.to_dict()), ''
758 @CLIWriteCommand('dashboard ac-user-enable',
759 'name=username,type=CephString',
761 def ac_user_enable(_
, username
):
763 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
765 mgr
.ACCESS_CTRL_DB
.reset_attempt(username
)
767 mgr
.ACCESS_CTRL_DB
.save()
768 return 0, json
.dumps(user
.to_dict()), ''
769 except UserDoesNotExist
as ex
:
770 return -errno
.ENOENT
, '', str(ex
)
773 @CLIWriteCommand('dashboard ac-user-disable',
774 'name=username,type=CephString',
776 def ac_user_disable(_
, username
):
778 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
781 mgr
.ACCESS_CTRL_DB
.save()
782 return 0, json
.dumps(user
.to_dict()), ''
783 except UserDoesNotExist
as ex
:
784 return -errno
.ENOENT
, '', str(ex
)
787 @CLIWriteCommand('dashboard ac-user-delete',
788 'name=username,type=CephString',
790 def ac_user_delete_cmd(_
, username
):
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 'name=username,type=CephString '
801 'name=roles,type=CephString,n=N',
803 def ac_user_set_roles_cmd(_
, username
, roles
):
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 'name=username,type=CephString '
824 'name=roles,type=CephString,n=N',
826 def ac_user_add_roles_cmd(_
, username
, roles
):
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 'name=username,type=CephString '
847 'name=roles,type=CephString,n=N',
848 'Delete roles from user')
849 def ac_user_del_roles_cmd(_
, username
, roles
):
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 'name=username,type=CephString '
872 'name=force_password,type=CephBool,req=false',
873 'Set user password from -i <file>')
874 @CLICheckNonemptyFileInput
875 def ac_user_set_password(_
, username
, inbuf
, force_password
=False):
878 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
879 if not force_password
:
880 pw_check
= PasswordPolicy(password
, user
.name
)
882 user
.set_password(password
)
883 mgr
.ACCESS_CTRL_DB
.save()
884 return 0, json
.dumps(user
.to_dict()), ''
885 except PasswordPolicyException
as ex
:
886 return -errno
.EINVAL
, '', str(ex
)
887 except UserDoesNotExist
as ex
:
888 return -errno
.ENOENT
, '', str(ex
)
891 @CLIWriteCommand('dashboard ac-user-set-password-hash',
892 'name=username,type=CephString',
893 'Set user password bcrypt hash from -i <file>')
894 @CLICheckNonemptyFileInput
895 def ac_user_set_password_hash(_
, username
, inbuf
):
896 hashed_password
= inbuf
898 # make sure the hashed_password is actually a bcrypt hash
899 bcrypt
.checkpw(b
'', hashed_password
.encode('utf-8'))
900 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
901 user
.set_password_hash(hashed_password
)
903 mgr
.ACCESS_CTRL_DB
.save()
904 return 0, json
.dumps(user
.to_dict()), ''
906 return -errno
.EINVAL
, '', 'Invalid password hash'
907 except UserDoesNotExist
as ex
:
908 return -errno
.ENOENT
, '', str(ex
)
911 @CLIWriteCommand('dashboard ac-user-set-info',
912 'name=username,type=CephString '
913 'name=name,type=CephString '
914 'name=email,type=CephString',
916 def ac_user_set_info(_
, username
, name
, email
):
918 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
923 mgr
.ACCESS_CTRL_DB
.save()
924 return 0, json
.dumps(user
.to_dict()), ''
925 except UserDoesNotExist
as ex
:
926 return -errno
.ENOENT
, '', str(ex
)
929 class LocalAuthenticator(object):
931 load_access_control_db()
933 def get_user(self
, username
):
934 return mgr
.ACCESS_CTRL_DB
.get_user(username
)
936 def authenticate(self
, username
, password
):
938 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
940 if user
.enabled
and user
.compare_password(password
) \
941 and not user
.is_pwd_expired():
942 return {'permissions': user
.permissions_dict(),
943 'pwdExpirationDate': user
.pwd_expiration_date
,
944 'pwdUpdateRequired': user
.pwd_update_required
}
945 except UserDoesNotExist
:
946 logger
.debug("User '%s' does not exist", username
)
949 def authorize(self
, username
, scope
, permissions
):
950 user
= mgr
.ACCESS_CTRL_DB
.get_user(username
)
951 return user
.authorize(scope
, permissions
)