Added seed code for access-management.
[ta/access-management.git] / src / access_management / rest-plugin / users_ownpasswords.py
1 # Copyright 2019 Nokia
2
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 import json
16 import crypt
17 import requests
18 import access_management.db.amdb as amdb
19 from am_api_base import *
20 from keystoneauth1 import exceptions
21 from cmframework.apis import cmclient
22
23
24 class UsersOwnpasswords(AMApiBase):
25
26     """
27     User set password operations
28
29     .. :quickref: User ownpasswords;User set password operations
30
31     .. http:post:: /am/v1/users/ownpasswords
32
33     **Start User set password**
34
35     **Example request**:
36
37     .. sourcecode:: http
38
39         POST am/v1/users/ownpasswords HTTP/1.1
40         Host: haproxyvip:61200
41         Accept: application/json
42         {
43             "npassword: "Passwd_1",
44             "opassword: "Passwd_2",
45             "username": "test_user"
46         }
47
48     :> json string npassword: The user's new password
49     :> json string opassword: The user's old password
50     :> json string username: The user's username
51     :> json string id: The user's ID
52     Only one of username or id needs to be present.
53
54     **Example response**:
55
56     .. sourcecode:: http
57
58         HTTP/1.1 200 OK
59         {
60             "code": 0,
61             "description": "User password changed successfully!"
62         }
63
64     :> json int code: the status code
65     :> json string description: the error description, present if code is non zero
66     """
67
68     endpoints = ['users/ownpasswords']
69     parser_arguments = ['npassword',
70                         'opassword',
71                         'username',
72                         'id']
73     FAILURE_RESPONSE = AMApiBase.embed_data({}, 1, "Password change failed!")
74
75     def post(self):
76         self.logger.info("Received a set password request!")
77         args = self.parse_args()
78
79         user = {}
80         state_open, message_open = self._open_db()
81         if state_open:
82             try:
83                 if args["username"]:
84                     user["name"] = args["username"]
85                     user["uuid"] = self.db.get_user_uuid(args["username"])
86                 else:
87                     user["uuid"] = args["id"]
88                     user["name"] = self.db.get_user_name(args["id"])
89
90                 try:
91                     keystone = self.auth_keystone_with_pass(args["opassword"], user["name"])
92                     passstate = self.passwd_validator(args["npassword"])
93                     if passstate is not None:
94                         self.logger.error(passstate)
95                         return AMApiBase.embed_data({}, 1, passstate)
96                     keystone.users.update_password(args["opassword"], args["npassword"])
97                     state = True
98                 except exceptions.http.Unauthorized as ex:
99                     if "password is expired" in ex.message:
100                         passstate = self.passwd_validator(args["npassword"])
101                         if passstate is not None:
102                             self.logger.error(passstate)
103                             return AMApiBase.embed_data({}, 1, passstate)
104                         state = self.change_password_with_request(args, user["uuid"])
105                     else:
106                         self.logger.error("{0}".format(ex))
107                         state = False
108                 except Exception as ex:
109                     self.logger.error("{0}".format(ex))
110                     return self.FAILURE_RESPONSE
111
112                 if state:
113                     state = self.set_ownpass_in_db(args, user)
114                     if state:
115                         self.logger.info("User password changed successfully!")
116                         return AMApiBase.embed_data({}, 0, "User password changed successfully!")
117                     else:
118                         return self.FAILURE_RESPONSE
119                 else:
120                     return self.FAILURE_RESPONSE
121
122             except amdb.NotExist as ex:
123                 self.logger.error("User does not exist")
124                 return self.FAILURE_RESPONSE
125             except Exception as ex:
126                 self.logger.error("Internal error: {0}".format(ex))
127                 return self.FAILURE_RESPONSE
128             finally:
129                 state_close, message_close = self._close_db()
130                 if not state_close:
131                     self._close_db()
132         else:
133             return self.FAILURE_RESPONSE
134
135     def change_password_with_request(self, args, uuid):
136         url = self.config["Keystone"]["auth_uri"] + "/users/" + uuid + "/password"
137         parameter = {"user": {"password": args["npassword"], "original_password": args["opassword"]}}
138         header = {"Content-Type": "application/json"}
139         s_user_out = requests.post(url, data=json.dumps(parameter), headers=header, timeout=30)
140
141         if s_user_out.status_code != 204:
142             s_user_out = s_user_out.json()
143             self.logger.error(s_user_out["error"]["message"])
144             return False
145         return True
146
147     def set_ownpass_in_db(self, args, user):
148         linux_user_role = False
149         chroot_user_role = False
150         state_open, message_open = self._open_db()
151         if state_open:
152             try:
153                 roles = self.db.get_user_roles(user["uuid"])
154
155                 for role in roles:
156                     if self.db.is_chroot_role(role):
157                         chroot_user_role = True
158                     if role == "linux_user":
159                         linux_user_role = True
160
161                 # if the user has a chroot or linux account, change the pwd of that also
162                 if chroot_user_role:
163                     self.linux_chroot_pass_handling("Chroot", "cloud.chroot", args["npassword"], user["name"])
164                 if linux_user_role:
165                     self.linux_chroot_pass_handling("Linux", "cloud.linuxuser", args["npassword"], user["name"])
166
167             except amdb.NotExist as ex:
168                 self.logger.error("User does not exist")
169                 return False
170             except Exception as ex:
171                 self.logger.error("Internal error: {0}".format(ex))
172                 return False
173             finally:
174                 state_close, message_close = self._close_db()
175                 if not state_close:
176                     self._close_db()
177         else:
178             self.logger.error("Could not open DB")
179             return False
180
181         return True
182
183     def linux_chroot_pass_handling(self, user_type, list_name, passwd, username):
184         cmc = cmclient.CMClient()
185         user_list = cmc.get_property(list_name)
186         user_list = json.loads(user_list)
187         self.logger.debug("{0} user list before the change: {1}".format(user_type, json.dumps(user_list)))
188         if user_list is not None:
189             self.logger.debug("The {0} user list exists!".format(user_type))
190             self.logger.debug("Username: {0}".format(username))
191             for val in user_list:
192                 if val["name"] == username:
193                     val["password"] = crypt.crypt(passwd, crypt.mksalt(crypt.METHOD_SHA512))
194                     break
195             self.logger.debug("{0} user list after the change: {1}".format(user_type, json.dumps(user_list)))
196             cmc.set_property(list_name, json.dumps(user_list))