]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/dashboard/controllers/user.py
import ceph 16.2.7
[ceph.git] / ceph / src / pybind / mgr / dashboard / controllers / user.py
CommitLineData
11fdf7f2
TL
1# -*- coding: utf-8 -*-
2from __future__ import absolute_import
3
9f95a23c 4import time
f67539c2 5from datetime import datetime
9f95a23c 6
11fdf7f2 7import cherrypy
f67539c2 8from ceph_argparse import CephString
11fdf7f2 9
11fdf7f2 10from .. import mgr
f67539c2
TL
11from ..exceptions import DashboardException, PasswordPolicyException, \
12 PwdExpirationDateNotValid, UserAlreadyExists, UserDoesNotExist
11fdf7f2 13from ..security import Scope
9f95a23c 14from ..services.access_control import SYSTEM_ROLES, PasswordPolicy
11fdf7f2 15from ..services.auth import JwtManager
a4b75251
TL
16from . import APIDoc, APIRouter, BaseController, Endpoint, EndpointDoc, \
17 RESTController, allow_empty_body, validate_ceph_type
f67539c2
TL
18
19USER_SCHEMA = ([{
20 "username": (str, 'Username of the user'),
21 "roles": ([str], 'User Roles'),
22 "name": (str, 'User Name'),
23 "email": (str, 'User email address'),
24 "lastUpdate": (int, 'Details last updated'),
25 "enabled": (bool, 'Is the user enabled?'),
26 "pwdExpirationDate": (str, 'Password Expiration date'),
27 "pwdUpdateRequired": (bool, 'Is Password Update Required?')
28}], '')
11fdf7f2
TL
29
30
9f95a23c
TL
31def validate_password_policy(password, username=None, old_password=None):
32 """
33 :param password: The password to validate.
34 :param username: The name of the user (optional).
35 :param old_password: The old password (optional).
36 :return: Returns the password complexity credits.
37 :rtype: int
38 :raises DashboardException: If a password policy fails.
39 """
40 pw_policy = PasswordPolicy(password, username, old_password)
41 try:
42 pw_policy.check_all()
43 return pw_policy.complexity_credits
44 except PasswordPolicyException as ex:
45 raise DashboardException(msg=str(ex),
46 code='password_policy_validation_failed',
47 component='user')
48
49
a4b75251
TL
50@APIRouter('/user', Scope.USER)
51@APIDoc("Display User Details", "User")
11fdf7f2 52class User(RESTController):
9f95a23c 53
11fdf7f2
TL
54 @staticmethod
55 def _user_to_dict(user):
56 result = user.to_dict()
57 del result['password']
58 return result
59
60 @staticmethod
61 def _get_user_roles(roles):
62 all_roles = dict(mgr.ACCESS_CTRL_DB.roles)
63 all_roles.update(SYSTEM_ROLES)
64 try:
65 return [all_roles[rolename] for rolename in roles]
66 except KeyError:
67 raise DashboardException(msg='Role does not exist',
68 code='role_does_not_exist',
69 component='user')
70
f67539c2
TL
71 @EndpointDoc("Get List Of Users",
72 responses={200: USER_SCHEMA})
11fdf7f2
TL
73 def list(self):
74 users = mgr.ACCESS_CTRL_DB.users
75 result = [User._user_to_dict(u) for _, u in users.items()]
76 return result
77
78 def get(self, username):
79 try:
80 user = mgr.ACCESS_CTRL_DB.get_user(username)
81 except UserDoesNotExist:
82 raise cherrypy.HTTPError(404)
83 return User._user_to_dict(user)
84
f67539c2 85 @validate_ceph_type([('username', CephString())], 'user')
9f95a23c
TL
86 def create(self, username=None, password=None, name=None, email=None,
87 roles=None, enabled=True, pwdExpirationDate=None, pwdUpdateRequired=True):
11fdf7f2
TL
88 if not username:
89 raise DashboardException(msg='Username is required',
90 code='username_required',
91 component='user')
92 user_roles = None
93 if roles:
94 user_roles = User._get_user_roles(roles)
9f95a23c
TL
95 if password:
96 validate_password_policy(password, username)
11fdf7f2 97 try:
9f95a23c
TL
98 user = mgr.ACCESS_CTRL_DB.create_user(username, password, name,
99 email, enabled, pwdExpirationDate,
100 pwdUpdateRequired)
11fdf7f2
TL
101 except UserAlreadyExists:
102 raise DashboardException(msg='Username already exists',
103 code='username_already_exists',
104 component='user')
9f95a23c
TL
105 except PwdExpirationDateNotValid:
106 raise DashboardException(msg='Password expiration date must not be in '
107 'the past',
108 code='pwd_past_expiration_date',
109 component='user')
110
11fdf7f2
TL
111 if user_roles:
112 user.set_roles(user_roles)
113 mgr.ACCESS_CTRL_DB.save()
114 return User._user_to_dict(user)
115
116 def delete(self, username):
117 session_username = JwtManager.get_username()
118 if session_username == username:
119 raise DashboardException(msg='Cannot delete current user',
120 code='cannot_delete_current_user',
121 component='user')
122 try:
123 mgr.ACCESS_CTRL_DB.delete_user(username)
124 except UserDoesNotExist:
125 raise cherrypy.HTTPError(404)
126 mgr.ACCESS_CTRL_DB.save()
127
9f95a23c
TL
128 def set(self, username, password=None, name=None, email=None, roles=None,
129 enabled=None, pwdExpirationDate=None, pwdUpdateRequired=False):
130 if JwtManager.get_username() == username and enabled is False:
131 raise DashboardException(msg='You are not allowed to disable your user',
132 code='cannot_disable_current_user',
133 component='user')
134
11fdf7f2
TL
135 try:
136 user = mgr.ACCESS_CTRL_DB.get_user(username)
137 except UserDoesNotExist:
138 raise cherrypy.HTTPError(404)
139 user_roles = []
140 if roles:
141 user_roles = User._get_user_roles(roles)
142 if password:
9f95a23c 143 validate_password_policy(password, username)
11fdf7f2 144 user.set_password(password)
9f95a23c
TL
145 if pwdExpirationDate and \
146 (pwdExpirationDate < int(time.mktime(datetime.utcnow().timetuple()))):
147 raise DashboardException(
148 msg='Password expiration date must not be in the past',
149 code='pwd_past_expiration_date', component='user')
11fdf7f2
TL
150 user.name = name
151 user.email = email
9f95a23c
TL
152 if enabled is not None:
153 user.enabled = enabled
154 user.pwd_expiration_date = pwdExpirationDate
11fdf7f2 155 user.set_roles(user_roles)
9f95a23c 156 user.pwd_update_required = pwdUpdateRequired
11fdf7f2
TL
157 mgr.ACCESS_CTRL_DB.save()
158 return User._user_to_dict(user)
9f95a23c
TL
159
160
a4b75251
TL
161@APIRouter('/user')
162@APIDoc("Get User Password Policy Details", "UserPasswordPolicy")
9f95a23c
TL
163class UserPasswordPolicy(RESTController):
164
165 @Endpoint('POST')
f91f0fd5 166 @allow_empty_body
9f95a23c
TL
167 def validate_password(self, password, username=None, old_password=None):
168 """
169 Check if the password meets the password policy.
170 :param password: The password to validate.
171 :param username: The name of the user (optional).
172 :param old_password: The old password (optional).
f67539c2
TL
173 :return: An object with properties valid, credits and valuation.
174 'credits' contains the password complexity credits and
175 'valuation' the textual summary of the validation.
9f95a23c
TL
176 """
177 result = {'valid': False, 'credits': 0, 'valuation': None}
178 try:
179 result['credits'] = validate_password_policy(password, username, old_password)
180 if result['credits'] < 15:
181 result['valuation'] = 'Weak'
182 elif result['credits'] < 20:
183 result['valuation'] = 'OK'
184 elif result['credits'] < 25:
185 result['valuation'] = 'Strong'
186 else:
187 result['valuation'] = 'Very strong'
188 result['valid'] = True
189 except DashboardException as ex:
190 result['valuation'] = str(ex)
191 return result
192
193
a4b75251
TL
194@APIRouter('/user/{username}')
195@APIDoc("Change User Password", "UserChangePassword")
9f95a23c
TL
196class UserChangePassword(BaseController):
197
198 @Endpoint('POST')
199 def change_password(self, username, old_password, new_password):
200 session_username = JwtManager.get_username()
201 if username != session_username:
202 raise DashboardException(msg='Invalid user context',
203 code='invalid_user_context',
204 component='user')
205 try:
206 user = mgr.ACCESS_CTRL_DB.get_user(session_username)
207 except UserDoesNotExist:
208 raise cherrypy.HTTPError(404)
209 if not user.compare_password(old_password):
210 raise DashboardException(msg='Invalid old password',
211 code='invalid_old_password',
212 component='user')
213 validate_password_policy(new_password, username, old_password)
214 user.set_password(new_password)
215 mgr.ACCESS_CTRL_DB.save()