1 # -*- coding: utf-8 -*-
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
17 from uuid
import uuid4
18 from hashlib
import sha256
19 from http
import HTTPStatus
20 from validation
import user_new_schema
, user_edit_schema
, project_new_schema
, project_edit_schema
21 from validation
import vim_account_new_schema
, vim_account_edit_schema
, sdn_new_schema
, sdn_edit_schema
22 from validation
import wim_account_new_schema
, wim_account_edit_schema
23 from base_topic
import BaseTopic
, EngineException
25 __author__
= "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
28 class UserTopic(BaseTopic
):
31 schema_new
= user_new_schema
32 schema_edit
= user_edit_schema
34 def __init__(self
, db
, fs
, msg
):
35 BaseTopic
.__init
__(self
, db
, fs
, msg
)
38 def _get_project_filter(session
, write
=False, show_all
=True):
40 Generates a filter dictionary for querying database users.
41 Current policy is admin can show all, non admin, only its own user.
42 :param session: contains "username", if user is "admin" and the working "project_id"
43 :param write: if operation is for reading (False) or writing (True)
44 :param show_all: if True it will show public or
47 if session
["admin"]: # allows all
50 return {"username": session
["username"]}
52 def check_conflict_on_new(self
, session
, indata
, force
=False):
53 # check username not exists
54 if self
.db
.get_one(self
.topic
, {"username": indata
.get("username")}, fail_on_empty
=False, fail_on_more
=False):
55 raise EngineException("username '{}' exists".format(indata
["username"]), HTTPStatus
.CONFLICT
)
58 for p
in indata
["projects"]:
61 if not self
.db
.get_one("projects", {"_id": p
}, fail_on_empty
=False, fail_on_more
=False):
62 raise EngineException("project '{}' does not exists".format(p
), HTTPStatus
.CONFLICT
)
64 def check_conflict_on_del(self
, session
, _id
, force
=False):
65 if _id
== session
["username"]:
66 raise EngineException("You cannot delete your own user", http_code
=HTTPStatus
.CONFLICT
)
69 def format_on_new(content
, project_id
=None, make_public
=False):
70 BaseTopic
.format_on_new(content
, make_public
=False)
71 content
["_id"] = content
["username"]
73 content
["_admin"]["salt"] = salt
74 if content
.get("password"):
75 content
["password"] = sha256(content
["password"].encode('utf-8') + salt
.encode('utf-8')).hexdigest()
78 def format_on_edit(final_content
, edit_content
):
79 BaseTopic
.format_on_edit(final_content
, edit_content
)
80 if edit_content
.get("password"):
82 final_content
["_admin"]["salt"] = salt
83 final_content
["password"] = sha256(edit_content
["password"].encode('utf-8') +
84 salt
.encode('utf-8')).hexdigest()
86 def edit(self
, session
, _id
, indata
=None, kwargs
=None, force
=False, content
=None):
87 if not session
["admin"]:
88 raise EngineException("needed admin privileges", http_code
=HTTPStatus
.UNAUTHORIZED
)
89 return BaseTopic
.edit(self
, session
, _id
, indata
=indata
, kwargs
=kwargs
, force
=force
, content
=content
)
91 def new(self
, rollback
, session
, indata
=None, kwargs
=None, headers
=None, force
=False, make_public
=False):
92 if not session
["admin"]:
93 raise EngineException("needed admin privileges", http_code
=HTTPStatus
.UNAUTHORIZED
)
94 return BaseTopic
.new(self
, rollback
, session
, indata
=indata
, kwargs
=kwargs
, headers
=headers
, force
=force
,
95 make_public
=make_public
)
98 class ProjectTopic(BaseTopic
):
100 topic_msg
= "projects"
101 schema_new
= project_new_schema
102 schema_edit
= project_edit_schema
104 def __init__(self
, db
, fs
, msg
):
105 BaseTopic
.__init
__(self
, db
, fs
, msg
)
107 def check_conflict_on_new(self
, session
, indata
, force
=False):
108 if not indata
.get("name"):
109 raise EngineException("missing 'name'")
110 # check name not exists
111 if self
.db
.get_one(self
.topic
, {"name": indata
.get("name")}, fail_on_empty
=False, fail_on_more
=False):
112 raise EngineException("name '{}' exists".format(indata
["name"]), HTTPStatus
.CONFLICT
)
115 def format_on_new(content
, project_id
=None, make_public
=False):
116 BaseTopic
.format_on_new(content
, None)
117 content
["_id"] = content
["name"]
119 def check_conflict_on_del(self
, session
, _id
, force
=False):
120 if _id
== session
["project_id"]:
121 raise EngineException("You cannot delete your own project", http_code
=HTTPStatus
.CONFLICT
)
124 _filter
= {"projects": _id
}
125 if self
.db
.get_list("users", _filter
):
126 raise EngineException("There is some USER that contains this project", http_code
=HTTPStatus
.CONFLICT
)
128 def edit(self
, session
, _id
, indata
=None, kwargs
=None, force
=False, content
=None):
129 if not session
["admin"]:
130 raise EngineException("needed admin privileges", http_code
=HTTPStatus
.UNAUTHORIZED
)
131 return BaseTopic
.edit(self
, session
, _id
, indata
=indata
, kwargs
=kwargs
, force
=force
, content
=content
)
133 def new(self
, rollback
, session
, indata
=None, kwargs
=None, headers
=None, force
=False, make_public
=False):
134 if not session
["admin"]:
135 raise EngineException("needed admin privileges", http_code
=HTTPStatus
.UNAUTHORIZED
)
136 return BaseTopic
.new(self
, rollback
, session
, indata
=indata
, kwargs
=kwargs
, headers
=headers
, force
=force
,
137 make_public
=make_public
)
140 class VimAccountTopic(BaseTopic
):
141 topic
= "vim_accounts"
142 topic_msg
= "vim_account"
143 schema_new
= vim_account_new_schema
144 schema_edit
= vim_account_edit_schema
145 vim_config_encrypted
= ("admin_password", "nsx_password", "vcenter_password")
147 def __init__(self
, db
, fs
, msg
):
148 BaseTopic
.__init
__(self
, db
, fs
, msg
)
150 def check_conflict_on_new(self
, session
, indata
, force
=False):
151 self
.check_unique_name(session
, indata
["name"], _id
=None)
153 def check_conflict_on_edit(self
, session
, final_content
, edit_content
, _id
, force
=False):
154 if not force
and edit_content
.get("name"):
155 self
.check_unique_name(session
, edit_content
["name"], _id
=_id
)
158 schema_version
= final_content
.get("schema_version")
160 if edit_content
.get("vim_password"):
161 final_content
["vim_password"] = self
.db
.encrypt(edit_content
["vim_password"],
162 schema_version
=schema_version
, salt
=_id
)
163 if edit_content
.get("config"):
164 for p
in self
.vim_config_encrypted
:
165 if edit_content
["config"].get(p
):
166 final_content
["config"][p
] = self
.db
.encrypt(edit_content
["config"][p
],
167 schema_version
=schema_version
, salt
=_id
)
169 def format_on_new(self
, content
, project_id
=None, make_public
=False):
170 BaseTopic
.format_on_new(content
, project_id
=project_id
, make_public
=make_public
)
171 content
["schema_version"] = schema_version
= "1.1"
174 if content
.get("vim_password"):
175 content
["vim_password"] = self
.db
.encrypt(content
["vim_password"], schema_version
=schema_version
,
177 if content
.get("config"):
178 for p
in self
.vim_config_encrypted
:
179 if content
["config"].get(p
):
180 content
["config"][p
] = self
.db
.encrypt(content
["config"][p
], schema_version
=schema_version
,
183 content
["_admin"]["operationalState"] = "PROCESSING"
185 def delete(self
, session
, _id
, force
=False, dry_run
=False):
187 Delete item by its internal _id
188 :param session: contains the used login username, working project, and admin rights
189 :param _id: server internal id
190 :param force: indicates if deletion must be forced in case of conflict
191 :param dry_run: make checking but do not delete
192 :return: dictionary with deleted item _id. It raises EngineException on error: not found, conflict, ...
194 # TODO add admin to filter, validate rights
195 if dry_run
or force
: # delete completely
196 return BaseTopic
.delete(self
, session
, _id
, force
, dry_run
)
197 else: # if not, sent to kafka
198 v
= BaseTopic
.delete(self
, session
, _id
, force
, dry_run
=True)
199 self
.db
.set_one("vim_accounts", {"_id": _id
}, {"_admin.to_delete": True}) # TODO change status
200 self
._send
_msg
("delete", {"_id": _id
})
201 return v
# TODO indicate an offline operation to return 202 ACCEPTED
204 class WimAccountTopic(BaseTopic
):
205 topic
= "wim_accounts"
206 topic_msg
= "wim_account"
207 schema_new
= wim_account_new_schema
208 schema_edit
= wim_account_edit_schema
209 wim_config_encrypted
= ()
211 def __init__(self
, db
, fs
, msg
):
212 BaseTopic
.__init
__(self
, db
, fs
, msg
)
214 def check_conflict_on_new(self
, session
, indata
, force
=False):
215 self
.check_unique_name(session
, indata
["name"], _id
=None)
217 def check_conflict_on_edit(self
, session
, final_content
, edit_content
, _id
, force
=False):
218 if not force
and edit_content
.get("name"):
219 self
.check_unique_name(session
, edit_content
["name"], _id
=_id
)
222 schema_version
= final_content
.get("schema_version")
224 if edit_content
.get("wim_password"):
225 final_content
["wim_password"] = self
.db
.encrypt(edit_content
["wim_password"],
226 schema_version
=schema_version
, salt
=_id
)
227 if edit_content
.get("config"):
228 for p
in self
.wim_config_encrypted
:
229 if edit_content
["config"].get(p
):
230 final_content
["config"][p
] = self
.db
.encrypt(edit_content
["config"][p
],
231 schema_version
=schema_version
, salt
=_id
)
233 def format_on_new(self
, content
, project_id
=None, make_public
=False):
234 BaseTopic
.format_on_new(content
, project_id
=project_id
, make_public
=make_public
)
235 content
["schema_version"] = schema_version
= "1.1"
238 if content
.get("wim_password"):
239 content
["wim_password"] = self
.db
.encrypt(content
["wim_password"], schema_version
=schema_version
,
241 if content
.get("config"):
242 for p
in self
.wim_config_encrypted
:
243 if content
["config"].get(p
):
244 content
["config"][p
] = self
.db
.encrypt(content
["config"][p
], schema_version
=schema_version
,
247 content
["_admin"]["operationalState"] = "PROCESSING"
249 def delete(self
, session
, _id
, force
=False, dry_run
=False):
251 Delete item by its internal _id
252 :param session: contains the used login username, working project, and admin rights
253 :param _id: server internal id
254 :param force: indicates if deletion must be forced in case of conflict
255 :param dry_run: make checking but do not delete
256 :return: dictionary with deleted item _id. It raises EngineException on error: not found, conflict, ...
258 # TODO add admin to filter, validate rights
259 if dry_run
or force
: # delete completely
260 return BaseTopic
.delete(self
, session
, _id
, force
, dry_run
)
261 else: # if not, sent to kafka
262 v
= BaseTopic
.delete(self
, session
, _id
, force
, dry_run
=True)
263 self
.db
.set_one("wim_accounts", {"_id": _id
}, {"_admin.to_delete": True}) # TODO change status
264 self
._send
_msg
("delete", {"_id": _id
})
265 return v
# TODO indicate an offline operation to return 202 ACCEPTED
268 class SdnTopic(BaseTopic
):
271 schema_new
= sdn_new_schema
272 schema_edit
= sdn_edit_schema
274 def __init__(self
, db
, fs
, msg
):
275 BaseTopic
.__init
__(self
, db
, fs
, msg
)
277 def check_conflict_on_new(self
, session
, indata
, force
=False):
278 self
.check_unique_name(session
, indata
["name"], _id
=None)
280 def check_conflict_on_edit(self
, session
, final_content
, edit_content
, _id
, force
=False):
281 if not force
and edit_content
.get("name"):
282 self
.check_unique_name(session
, edit_content
["name"], _id
=_id
)
285 schema_version
= final_content
.get("schema_version")
286 if schema_version
and edit_content
.get("password"):
287 final_content
["password"] = self
.db
.encrypt(edit_content
["password"], schema_version
=schema_version
,
290 def format_on_new(self
, content
, project_id
=None, make_public
=False):
291 BaseTopic
.format_on_new(content
, project_id
=project_id
, make_public
=make_public
)
292 content
["schema_version"] = schema_version
= "1.1"
294 if content
.get("password"):
295 content
["password"] = self
.db
.encrypt(content
["password"], schema_version
=schema_version
,
298 content
["_admin"]["operationalState"] = "PROCESSING"
300 def delete(self
, session
, _id
, force
=False, dry_run
=False):
302 Delete item by its internal _id
303 :param session: contains the used login username, working project, and admin rights
304 :param _id: server internal id
305 :param force: indicates if deletion must be forced in case of conflict
306 :param dry_run: make checking but do not delete
307 :return: dictionary with deleted item _id. It raises EngineException on error: not found, conflict, ...
309 if dry_run
or force
: # delete completely
310 return BaseTopic
.delete(self
, session
, _id
, force
, dry_run
)
311 else: # if not sent to kafka
312 v
= BaseTopic
.delete(self
, session
, _id
, force
, dry_run
=True)
313 self
.db
.set_one("sdns", {"_id": _id
}, {"_admin.to_delete": True}) # TODO change status
314 self
._send
_msg
("delete", {"_id": _id
})
315 return v
# TODO indicate an offline operation to return 202 ACCEPTED