blob: 0f4b52398476443fcbf22f34a805a6eab11e87ed [file] [log] [blame]
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001# -*- coding: utf-8 -*-
2
Eduardo Sousad795f872019-02-05 16:05:53 +00003# Copyright 2018 Whitestack, LLC
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16#
17# For those usages not covered by the Apache License, Version 2.0 please
18# contact: esousa@whitestack.com or glavado@whitestack.com
19##
20
Eduardo Sousa819d34c2018-07-31 01:20:02 +010021"""
22Authconn implements an Abstract class for the Auth backend connector
23plugins with the definition of the methods to be implemented.
24"""
25
garciadeblas4568a372021-03-24 09:19:48 +010026__author__ = (
27 "Eduardo Sousa <esousa@whitestack.com>, "
28 "Pedro de la Cruz Ramos <pdelacruzramos@altran.com>"
29)
Eduardo Sousa819d34c2018-07-31 01:20:02 +010030__date__ = "$27-jul-2018 23:59:59$"
31
32from http import HTTPStatus
tierno23acf402019-08-28 13:36:34 +000033from osm_nbi.base_topic import BaseTopic
Eduardo Sousa819d34c2018-07-31 01:20:02 +010034
35
36class AuthException(Exception):
37 """
tiernoc8445362019-06-14 12:07:15 +000038 Authentication error, because token, user password not recognized
Eduardo Sousa819d34c2018-07-31 01:20:02 +010039 """
garciadeblas4568a372021-03-24 09:19:48 +010040
Eduardo Sousa819d34c2018-07-31 01:20:02 +010041 def __init__(self, message, http_code=HTTPStatus.UNAUTHORIZED):
Eduardo Sousa5c01e192019-05-08 02:35:47 +010042 super(AuthException, self).__init__(message)
Eduardo Sousa819d34c2018-07-31 01:20:02 +010043 self.http_code = http_code
Eduardo Sousa819d34c2018-07-31 01:20:02 +010044
45
tiernoc8445362019-06-14 12:07:15 +000046class AuthExceptionUnauthorized(AuthException):
47 """
48 Authentication error, because not having rights to make this operation
49 """
garciadeblas4568a372021-03-24 09:19:48 +010050
tiernoc8445362019-06-14 12:07:15 +000051 pass
52
53
Eduardo Sousa819d34c2018-07-31 01:20:02 +010054class AuthconnException(Exception):
55 """
56 Common and base class Exception for all authconn exceptions.
57 """
garciadeblas4568a372021-03-24 09:19:48 +010058
Eduardo Sousa819d34c2018-07-31 01:20:02 +010059 def __init__(self, message, http_code=HTTPStatus.UNAUTHORIZED):
Eduardo Sousa5c01e192019-05-08 02:35:47 +010060 super(AuthconnException, self).__init__(message)
Eduardo Sousa819d34c2018-07-31 01:20:02 +010061 self.http_code = http_code
62
63
64class AuthconnConnectionException(AuthconnException):
65 """
66 Connectivity error with Auth backend.
67 """
garciadeblas4568a372021-03-24 09:19:48 +010068
Eduardo Sousa819d34c2018-07-31 01:20:02 +010069 def __init__(self, message, http_code=HTTPStatus.BAD_GATEWAY):
Eduardo Sousa5c01e192019-05-08 02:35:47 +010070 super(AuthconnConnectionException, self).__init__(message, http_code)
Eduardo Sousa819d34c2018-07-31 01:20:02 +010071
72
73class AuthconnNotSupportedException(AuthconnException):
74 """
75 The request is not supported by the Auth backend.
76 """
garciadeblas4568a372021-03-24 09:19:48 +010077
Eduardo Sousa819d34c2018-07-31 01:20:02 +010078 def __init__(self, message, http_code=HTTPStatus.NOT_IMPLEMENTED):
Eduardo Sousa5c01e192019-05-08 02:35:47 +010079 super(AuthconnNotSupportedException, self).__init__(message, http_code)
Eduardo Sousa819d34c2018-07-31 01:20:02 +010080
81
82class AuthconnNotImplementedException(AuthconnException):
83 """
84 The method is not implemented by the Auth backend.
85 """
garciadeblas4568a372021-03-24 09:19:48 +010086
Eduardo Sousa819d34c2018-07-31 01:20:02 +010087 def __init__(self, message, http_code=HTTPStatus.NOT_IMPLEMENTED):
Eduardo Sousa5c01e192019-05-08 02:35:47 +010088 super(AuthconnNotImplementedException, self).__init__(message, http_code)
Eduardo Sousa819d34c2018-07-31 01:20:02 +010089
90
91class AuthconnOperationException(AuthconnException):
92 """
93 The operation executed failed.
94 """
garciadeblas4568a372021-03-24 09:19:48 +010095
Eduardo Sousa819d34c2018-07-31 01:20:02 +010096 def __init__(self, message, http_code=HTTPStatus.INTERNAL_SERVER_ERROR):
Eduardo Sousa5c01e192019-05-08 02:35:47 +010097 super(AuthconnOperationException, self).__init__(message, http_code)
Eduardo Sousa819d34c2018-07-31 01:20:02 +010098
99
tiernocf042d32019-06-13 09:06:40 +0000100class AuthconnNotFoundException(AuthconnException):
101 """
102 The operation executed failed because element not found.
103 """
garciadeblas4568a372021-03-24 09:19:48 +0100104
tiernocf042d32019-06-13 09:06:40 +0000105 def __init__(self, message, http_code=HTTPStatus.NOT_FOUND):
106 super().__init__(message, http_code)
107
108
tierno1f029d82019-06-13 22:37:04 +0000109class AuthconnConflictException(AuthconnException):
110 """
111 The operation has conflicts.
112 """
garciadeblas4568a372021-03-24 09:19:48 +0100113
tierno1f029d82019-06-13 22:37:04 +0000114 def __init__(self, message, http_code=HTTPStatus.CONFLICT):
115 super().__init__(message, http_code)
116
117
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100118class Authconn:
119 """
120 Abstract base class for all the Auth backend connector plugins.
121 Each Auth backend connector plugin must be a subclass of
122 Authconn class.
123 """
garciadeblas4568a372021-03-24 09:19:48 +0100124
tierno9e87a7f2020-03-23 09:24:10 +0000125 def __init__(self, config, db, role_permissions):
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100126 """
127 Constructor of the Authconn class.
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100128 :param config: configuration dictionary containing all the
129 necessary configuration parameters.
tierno9e87a7f2020-03-23 09:24:10 +0000130 :param db: internal database classs
131 :param role_permissions: read only role permission list
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100132 """
133 self.config = config
tierno9e87a7f2020-03-23 09:24:10 +0000134 self.role_permissions = role_permissions
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100135
tierno6486f742020-02-13 16:30:14 +0000136 def authenticate(self, credentials, token_info=None):
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100137 """
tierno701018c2019-06-25 11:13:14 +0000138 Authenticate a user using username/password or token_info, plus project
tierno6486f742020-02-13 16:30:14 +0000139 :param credentials: dictionary that contains:
140 username: name, id or None
141 password: password or None
142 project_id: name, id, or None. If None first found project will be used to get an scope token
143 other items are allowed for specific auth backends
tierno701018c2019-06-25 11:13:14 +0000144 :param token_info: previous token_info to obtain authorization
tierno38dcfeb2019-06-10 16:44:00 +0000145 :return: the scoped token info or raises an exception. The token is a dictionary with:
146 _id: token string id,
147 username: username,
148 project_id: scoped_token project_id,
149 project_name: scoped_token project_name,
150 expires: epoch time when it expires,
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100151
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100152 """
153 raise AuthconnNotImplementedException("Should have implemented this")
154
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100155 def validate_token(self, token):
156 """
157 Check if the token is valid.
158
159 :param token: token to validate
160 :return: dictionary with information associated with the token. If the
161 token is not valid, returns None.
162 """
163 raise AuthconnNotImplementedException("Should have implemented this")
164
165 def revoke_token(self, token):
166 """
167 Invalidate a token.
168
169 :param token: token to be revoked
170 """
171 raise AuthconnNotImplementedException("Should have implemented this")
172
delacruzramo01b15d32019-07-02 14:37:47 +0200173 def create_user(self, user_info):
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100174 """
175 Create a user.
176
delacruzramo01b15d32019-07-02 14:37:47 +0200177 :param user_info: full user info.
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100178 :raises AuthconnOperationException: if user creation failed.
179 """
180 raise AuthconnNotImplementedException("Should have implemented this")
181
delacruzramo01b15d32019-07-02 14:37:47 +0200182 def update_user(self, user_info):
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100183 """
tiernocf042d32019-06-13 09:06:40 +0000184 Change the user name and/or password.
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100185
delacruzramo01b15d32019-07-02 14:37:47 +0200186 :param user_info: user info modifications
187 :raises AuthconnNotImplementedException: if function not implemented
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100188 """
189 raise AuthconnNotImplementedException("Should have implemented this")
190
Eduardo Sousa5c01e192019-05-08 02:35:47 +0100191 def delete_user(self, user_id):
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100192 """
193 Delete user.
194
Eduardo Sousa5c01e192019-05-08 02:35:47 +0100195 :param user_id: user identifier.
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100196 :raises AuthconnOperationException: if user deletion failed.
197 """
198 raise AuthconnNotImplementedException("Should have implemented this")
199
tiernocf042d32019-06-13 09:06:40 +0000200 def get_user_list(self, filter_q=None):
Eduardo Sousa5c01e192019-05-08 02:35:47 +0100201 """
202 Get user list.
203
tiernocf042d32019-06-13 09:06:40 +0000204 :param filter_q: dictionary to filter user list by name (username is also admited) and/or _id
Eduardo Sousa5c01e192019-05-08 02:35:47 +0100205 :return: returns a list of users.
206 """
207
tierno23acf402019-08-28 13:36:34 +0000208 def get_user(self, _id, fail=True):
209 """
210 Get one user
211 :param _id: id or name
212 :param fail: True to raise exception on not found. False to return None on not found
213 :return: dictionary with the user information
214 """
215 filt = {BaseTopic.id_field("users", _id): _id}
delacruzramo01b15d32019-07-02 14:37:47 +0200216 users = self.get_user_list(filt)
217 if not users:
218 if fail:
garciadeblas4568a372021-03-24 09:19:48 +0100219 raise AuthconnNotFoundException(
220 "User with {} not found".format(filt),
221 http_code=HTTPStatus.NOT_FOUND,
222 )
delacruzramo01b15d32019-07-02 14:37:47 +0200223 else:
224 return None
225 return users[0]
226
227 def create_role(self, role_info):
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100228 """
229 Create a role.
230
delacruzramo01b15d32019-07-02 14:37:47 +0200231 :param role_info: full role info.
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100232 :raises AuthconnOperationException: if role creation failed.
233 """
234 raise AuthconnNotImplementedException("Should have implemented this")
235
Eduardo Sousa5c01e192019-05-08 02:35:47 +0100236 def delete_role(self, role_id):
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100237 """
238 Delete a role.
239
Eduardo Sousa5c01e192019-05-08 02:35:47 +0100240 :param role_id: role identifier.
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100241 :raises AuthconnOperationException: if user deletion failed.
242 """
243 raise AuthconnNotImplementedException("Should have implemented this")
244
tierno1f029d82019-06-13 22:37:04 +0000245 def get_role_list(self, filter_q=None):
Eduardo Sousa5c01e192019-05-08 02:35:47 +0100246 """
247 Get all the roles.
248
tierno1f029d82019-06-13 22:37:04 +0000249 :param filter_q: dictionary to filter role list by _id and/or name.
Eduardo Sousa5c01e192019-05-08 02:35:47 +0100250 :return: list of roles
251 """
252 raise AuthconnNotImplementedException("Should have implemented this")
253
tierno23acf402019-08-28 13:36:34 +0000254 def get_role(self, _id, fail=True):
255 """
256 Get one role
257 :param _id: id or name
258 :param fail: True to raise exception on not found. False to return None on not found
259 :return: dictionary with the role information
260 """
261 filt = {BaseTopic.id_field("roles", _id): _id}
delacruzramo01b15d32019-07-02 14:37:47 +0200262 roles = self.get_role_list(filt)
263 if not roles:
264 if fail:
265 raise AuthconnNotFoundException("Role with {} not found".format(filt))
266 else:
267 return None
268 return roles[0]
269
270 def update_role(self, role_info):
tierno1f029d82019-06-13 22:37:04 +0000271 """
delacruzramo01b15d32019-07-02 14:37:47 +0200272 Change the information of a role
273 :param role_info: full role info
tierno1f029d82019-06-13 22:37:04 +0000274 :return: None
275 """
276 raise AuthconnNotImplementedException("Should have implemented this")
277
delacruzramo01b15d32019-07-02 14:37:47 +0200278 def create_project(self, project_info):
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100279 """
280 Create a project.
281
delacruzramo01b15d32019-07-02 14:37:47 +0200282 :param project_info: full project info.
tierno4015b472019-06-10 13:57:29 +0000283 :return: the internal id of the created project
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100284 :raises AuthconnOperationException: if project creation failed.
285 """
286 raise AuthconnNotImplementedException("Should have implemented this")
287
Eduardo Sousa5c01e192019-05-08 02:35:47 +0100288 def delete_project(self, project_id):
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100289 """
290 Delete a project.
291
Eduardo Sousa5c01e192019-05-08 02:35:47 +0100292 :param project_id: project identifier.
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100293 :raises AuthconnOperationException: if project deletion failed.
294 """
295 raise AuthconnNotImplementedException("Should have implemented this")
296
tierno38dcfeb2019-06-10 16:44:00 +0000297 def get_project_list(self, filter_q=None):
Eduardo Sousa5c01e192019-05-08 02:35:47 +0100298 """
299 Get all the projects.
300
tierno38dcfeb2019-06-10 16:44:00 +0000301 :param filter_q: dictionary to filter project list, by "name" and/or "_id"
Eduardo Sousa5c01e192019-05-08 02:35:47 +0100302 :return: list of projects
303 """
304 raise AuthconnNotImplementedException("Should have implemented this")
305
tierno23acf402019-08-28 13:36:34 +0000306 def get_project(self, _id, fail=True):
307 """
308 Get one project
309 :param _id: id or name
310 :param fail: True to raise exception on not found. False to return None on not found
311 :return: dictionary with the project information
312 """
313 filt = {BaseTopic.id_field("projects", _id): _id}
delacruzramo01b15d32019-07-02 14:37:47 +0200314 projs = self.get_project_list(filt)
315 if not projs:
316 if fail:
garciadeblas4568a372021-03-24 09:19:48 +0100317 raise AuthconnNotFoundException(
318 "project with {} not found".format(filt)
319 )
delacruzramo01b15d32019-07-02 14:37:47 +0200320 else:
321 return None
322 return projs[0]
323
324 def update_project(self, project_id, project_info):
tierno4015b472019-06-10 13:57:29 +0000325 """
delacruzramo01b15d32019-07-02 14:37:47 +0200326 Change the information of a project
tierno4015b472019-06-10 13:57:29 +0000327 :param project_id: project to be changed
delacruzramo01b15d32019-07-02 14:37:47 +0200328 :param project_info: full project info
tierno4015b472019-06-10 13:57:29 +0000329 :return: None
330 """
331 raise AuthconnNotImplementedException("Should have implemented this")