1 # -*- coding: utf-8 -*-
5 from hashlib
import sha256
6 from http
import HTTPStatus
7 from validation
import user_new_schema
, user_edit_schema
, project_new_schema
, project_edit_schema
8 from validation
import vim_account_new_schema
, vim_account_edit_schema
, sdn_new_schema
, sdn_edit_schema
9 from base_topic
import BaseTopic
, EngineException
11 __author__
= "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
14 class UserTopic(BaseTopic
):
17 schema_new
= user_new_schema
18 schema_edit
= user_edit_schema
20 def __init__(self
, db
, fs
, msg
):
21 BaseTopic
.__init
__(self
, db
, fs
, msg
)
24 def _get_project_filter(session
, write
=False, show_all
=True):
26 Generates a filter dictionary for querying database users.
27 Current policy is admin can show all, non admin, only its own user.
28 :param session: contains "username", if user is "admin" and the working "project_id"
29 :param write: if operation is for reading (False) or writing (True)
30 :param show_all: if True it will show public or
33 if session
["admin"]: # allows all
36 return {"username": session
["username"]}
38 def check_conflict_on_new(self
, session
, indata
, force
=False):
39 # check username not exists
40 if self
.db
.get_one(self
.topic
, {"username": indata
.get("username")}, fail_on_empty
=False, fail_on_more
=False):
41 raise EngineException("username '{}' exists".format(indata
["username"]), HTTPStatus
.CONFLICT
)
44 for p
in indata
["projects"]:
47 if not self
.db
.get_one("projects", {"_id": p
}, fail_on_empty
=False, fail_on_more
=False):
48 raise EngineException("project '{}' does not exists".format(p
), HTTPStatus
.CONFLICT
)
50 def check_conflict_on_del(self
, session
, _id
, force
=False):
51 if _id
== session
["username"]:
52 raise EngineException("You cannot delete your own user", http_code
=HTTPStatus
.CONFLICT
)
55 def format_on_new(content
, project_id
=None, make_public
=False):
56 BaseTopic
.format_on_new(content
, make_public
=False)
57 content
["_id"] = content
["username"]
59 content
["_admin"]["salt"] = salt
60 if content
.get("password"):
61 content
["password"] = sha256(content
["password"].encode('utf-8') + salt
.encode('utf-8')).hexdigest()
64 def format_on_edit(final_content
, edit_content
):
65 BaseTopic
.format_on_edit(final_content
, edit_content
)
66 if edit_content
.get("password"):
68 final_content
["_admin"]["salt"] = salt
69 final_content
["password"] = sha256(edit_content
["password"].encode('utf-8') +
70 salt
.encode('utf-8')).hexdigest()
72 def edit(self
, session
, _id
, indata
=None, kwargs
=None, force
=False, content
=None):
73 if not session
["admin"]:
74 raise EngineException("needed admin privileges", http_code
=HTTPStatus
.UNAUTHORIZED
)
75 return BaseTopic
.edit(self
, session
, _id
, indata
=indata
, kwargs
=kwargs
, force
=force
, content
=content
)
77 def new(self
, rollback
, session
, indata
=None, kwargs
=None, headers
=None, force
=False, make_public
=False):
78 if not session
["admin"]:
79 raise EngineException("needed admin privileges", http_code
=HTTPStatus
.UNAUTHORIZED
)
80 return BaseTopic
.new(self
, rollback
, session
, indata
=indata
, kwargs
=kwargs
, headers
=headers
, force
=force
,
81 make_public
=make_public
)
84 class ProjectTopic(BaseTopic
):
86 topic_msg
= "projects"
87 schema_new
= project_new_schema
88 schema_edit
= project_edit_schema
90 def __init__(self
, db
, fs
, msg
):
91 BaseTopic
.__init
__(self
, db
, fs
, msg
)
93 def check_conflict_on_new(self
, session
, indata
, force
=False):
94 if not indata
.get("name"):
95 raise EngineException("missing 'name'")
96 # check name not exists
97 if self
.db
.get_one(self
.topic
, {"name": indata
.get("name")}, fail_on_empty
=False, fail_on_more
=False):
98 raise EngineException("name '{}' exists".format(indata
["name"]), HTTPStatus
.CONFLICT
)
101 def format_on_new(content
, project_id
=None, make_public
=False):
102 BaseTopic
.format_on_new(content
, None)
103 content
["_id"] = content
["name"]
105 def check_conflict_on_del(self
, session
, _id
, force
=False):
106 if _id
== session
["project_id"]:
107 raise EngineException("You cannot delete your own project", http_code
=HTTPStatus
.CONFLICT
)
110 _filter
= {"projects": _id
}
111 if self
.db
.get_list("users", _filter
):
112 raise EngineException("There is some USER that contains this project", http_code
=HTTPStatus
.CONFLICT
)
114 def edit(self
, session
, _id
, indata
=None, kwargs
=None, force
=False, content
=None):
115 if not session
["admin"]:
116 raise EngineException("needed admin privileges", http_code
=HTTPStatus
.UNAUTHORIZED
)
117 return BaseTopic
.edit(self
, session
, _id
, indata
=indata
, kwargs
=kwargs
, force
=force
, content
=content
)
119 def new(self
, rollback
, session
, indata
=None, kwargs
=None, headers
=None, force
=False, make_public
=False):
120 if not session
["admin"]:
121 raise EngineException("needed admin privileges", http_code
=HTTPStatus
.UNAUTHORIZED
)
122 return BaseTopic
.new(self
, rollback
, session
, indata
=indata
, kwargs
=kwargs
, headers
=headers
, force
=force
,
123 make_public
=make_public
)
126 class VimAccountTopic(BaseTopic
):
127 topic
= "vim_accounts"
128 topic_msg
= "vim_account"
129 schema_new
= vim_account_new_schema
130 schema_edit
= vim_account_edit_schema
131 vim_config_encrypted
= ("admin_password", "nsx_password", "vcenter_password")
133 def __init__(self
, db
, fs
, msg
):
134 BaseTopic
.__init
__(self
, db
, fs
, msg
)
136 def check_conflict_on_new(self
, session
, indata
, force
=False):
137 self
.check_unique_name(session
, indata
["name"], _id
=None)
139 def check_conflict_on_edit(self
, session
, final_content
, edit_content
, _id
, force
=False):
140 if not force
and edit_content
.get("name"):
141 self
.check_unique_name(session
, edit_content
["name"], _id
=_id
)
144 schema_version
= final_content
.get("schema_version")
146 if edit_content
.get("vim_password"):
147 final_content
["vim_password"] = self
.db
.encrypt(edit_content
["vim_password"],
148 schema_version
=schema_version
, salt
=_id
)
149 if edit_content
.get("config"):
150 for p
in self
.vim_config_encrypted
:
151 if edit_content
["config"].get(p
):
152 final_content
["config"][p
] = self
.db
.encrypt(edit_content
["config"][p
],
153 schema_version
=schema_version
, salt
=_id
)
155 def format_on_new(self
, content
, project_id
=None, make_public
=False):
156 BaseTopic
.format_on_new(content
, project_id
=project_id
, make_public
=make_public
)
157 content
["schema_version"] = schema_version
= "1.1"
160 if content
.get("vim_password"):
161 content
["vim_password"] = self
.db
.encrypt(content
["vim_password"], schema_version
=schema_version
,
163 if content
.get("config"):
164 for p
in self
.vim_config_encrypted
:
165 if content
["config"].get(p
):
166 content
["config"][p
] = self
.db
.encrypt(content
["config"][p
], schema_version
=schema_version
,
169 content
["_admin"]["operationalState"] = "PROCESSING"
171 def delete(self
, session
, _id
, force
=False, dry_run
=False):
173 Delete item by its internal _id
174 :param session: contains the used login username, working project, and admin rights
175 :param _id: server internal id
176 :param force: indicates if deletion must be forced in case of conflict
177 :param dry_run: make checking but do not delete
178 :return: dictionary with deleted item _id. It raises EngineException on error: not found, conflict, ...
180 # TODO add admin to filter, validate rights
181 if dry_run
or force
: # delete completely
182 return BaseTopic
.delete(self
, session
, _id
, force
, dry_run
)
183 else: # if not, sent to kafka
184 v
= BaseTopic
.delete(self
, session
, _id
, force
, dry_run
=True)
185 self
.db
.set_one("vim_accounts", {"_id": _id
}, {"_admin.to_delete": True}) # TODO change status
186 self
._send
_msg
("delete", {"_id": _id
})
187 return v
# TODO indicate an offline operation to return 202 ACCEPTED
190 class SdnTopic(BaseTopic
):
193 schema_new
= sdn_new_schema
194 schema_edit
= sdn_edit_schema
196 def __init__(self
, db
, fs
, msg
):
197 BaseTopic
.__init
__(self
, db
, fs
, msg
)
199 def check_conflict_on_new(self
, session
, indata
, force
=False):
200 self
.check_unique_name(session
, indata
["name"], _id
=None)
202 def check_conflict_on_edit(self
, session
, final_content
, edit_content
, _id
, force
=False):
203 if not force
and edit_content
.get("name"):
204 self
.check_unique_name(session
, edit_content
["name"], _id
=_id
)
207 schema_version
= final_content
.get("schema_version")
208 if schema_version
and edit_content
.get("password"):
209 final_content
["password"] = self
.db
.encrypt(edit_content
["password"], schema_version
=schema_version
,
212 def format_on_new(self
, content
, project_id
=None, make_public
=False):
213 BaseTopic
.format_on_new(content
, project_id
=project_id
, make_public
=make_public
)
214 content
["schema_version"] = schema_version
= "1.1"
216 if content
.get("password"):
217 content
["password"] = self
.db
.encrypt(content
["password"], schema_version
=schema_version
,
220 content
["_admin"]["operationalState"] = "PROCESSING"
222 def delete(self
, session
, _id
, force
=False, dry_run
=False):
224 Delete item by its internal _id
225 :param session: contains the used login username, working project, and admin rights
226 :param _id: server internal id
227 :param force: indicates if deletion must be forced in case of conflict
228 :param dry_run: make checking but do not delete
229 :return: dictionary with deleted item _id. It raises EngineException on error: not found, conflict, ...
231 if dry_run
or force
: # delete completely
232 return BaseTopic
.delete(self
, session
, _id
, force
, dry_run
)
233 else: # if not sent to kafka
234 v
= BaseTopic
.delete(self
, session
, _id
, force
, dry_run
=True)
235 self
.db
.set_one("sdns", {"_id": _id
}, {"_admin.to_delete": True}) # TODO change status
236 self
._send
_msg
("delete", {"_id": _id
})
237 return v
# TODO indicate an offline operation to return 202 ACCEPTED