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