async def _set_reset_password_to_user(self, user_id, membership_id, reset_password_model): try: await self.db.users.update_one( { '_id': maybe_object_id(user_id), 'membership_id': membership_id }, {'$set': reset_password_model}) except Exception as e: raise ErtisError( err_code="errors.errorOccurredWhileUpdatingUser", err_msg= "An error occurred while updating user with provided body", status_code=500, context={'provided_body': reset_password_model}, reason=str(e)) user = await self.db.users.find_one({ '_id': maybe_object_id(user_id), 'membership_id': membership_id }) if not user: raise ErtisError( err_msg="User not found in db by given _id: <{}>".format( user_id), err_code="errors.userNotFound", status_code=404) return user
def me(): auth_header = request.headers.get('Authorization') if not auth_header: raise ErtisError( err_code="errors.authorizationHeaderRequired", err_msg="Authorization header is required for using ME API", status_code=401) try: auth_header = auth_header.split(' ') except Exception as e: ErtisError(err_msg="Invalid authorization header provided.", err_code="errors.authorizationHeaderIsInvalid", status_code=401, context={'message': str(e)}) if len(auth_header) != 2: raise ErtisError(err_msg="Bearer token usage is invalid", err_code="errors.invalidBearerTokenUsage", status_code=401) token = auth_header[1] security_manager = ErtisSecurityManager(app.db) user = security_manager.load_user(token, settings['application_secret'], settings['verify_token']) user.pop('password', None) return Response(json.dumps(user, default=bson_to_json), mimetype='application/json', status=200)
async def validate_token(self, token, secret, verify): try: decoded = jwt.decode(token, key=secret, algorithms='HS256', verify=verify) except ExpiredSignatureError as e: raise ErtisError(status_code=401, err_msg="Provided token has expired", err_code="errors.tokenExpiredError", context={'message': str(e)}) except Exception as e: raise ErtisError(status_code=401, err_msg="Provided token is invalid", err_code="errors.tokenIsInvalid", context={'e': str(e)}) where = {'_id': maybe_object_id(decoded['prn'])} user = await self.db.users.find_one(where) if not user: raise ErtisError(err_msg="User could not be found with this token", err_code="errors.userNotFound", status_code=404) user['decoded_token'] = decoded return user
def refresh_token(): try: body = json.loads(request.data) except ValueError as e: raise ErtisError(err_code="errors.badRequest", err_msg="Invalid json provided", status_code=400, context={'message': str(e)}) token = body.get('token') if not token: raise ErtisError(err_msg="Token is required", err_code="errors.tokenRequired", status_code=400) security_manager = ErtisSecurityManager(app.db) user = security_manager.load_user(token, settings['application_secret'], settings['verify_token']) new_token = ErtisTokenService.refresh_token( user, settings['application_secret'], settings['token_ttl'], ) response = {'token': new_token} return Response(json.dumps(response), mimetype='application/json', status=201)
async def create_provider(self, resource, utilizer): resource['membership_id'] = utilizer['membership_id'] resource['_id'] = ObjectId() resource['sys'] = { 'created_at': datetime.datetime.utcnow(), 'created_by': utilizer.get('username', utilizer.get('name')) } resource['slug'] = slugify(resource['name']) default_role = resource.get('default_role') exists_role = await self.db.roles.find_one({ "slug": default_role, "membership_id": utilizer['membership_id'] }) if not exists_role: raise ErtisError( err_code="errors.roleNotFound", err_msg="Role not found by given slug: <{}>".format( default_role), status_code=400) exist_provider = await self.db.providers.find_one({ 'slug': resource['slug'], 'membership_id': utilizer['membership_id'] }) if exist_provider: raise ErtisError( err_msg= "Provider already exists in db with given generated slug: <{}>" .format(resource['slug']), err_code="errors.resourceAlreadyExists", status_code=409) await self.db.providers.insert_one(resource) await self.event_service.on_event((Event( **{ 'document': resource, 'prior': {}, 'utilizer': utilizer, 'type': 'ProviderCreatedEvent', 'membership_id': utilizer['membership_id'], 'sys': { 'created_at': datetime.datetime.utcnow(), 'created_by': utilizer.get('username', utilizer.get( 'name')) } }))) return resource
async def query(db, membership_id=None, where=None, select=None, limit=None, skip=None, sort=None, collection=None): try: where = _pre_process_where(where) if not membership_id: raise ErtisError(err_msg="membership_id not passed to query", err_code="errors.internalServerError", status_code=500) where.update({'membership_id': membership_id}) if not select: select = None if not limit or limit > 500: limit = 200 cursor = db[collection].find(where, select) total_count = await cursor.explain() if skip: cursor.skip(int(skip)) if limit: cursor.limit(int(limit)) if sort: cursor.sort(sort) items = await cursor.to_list(None) return items, total_count["executionStats"]["nReturned"] except OperationFailure as e: if e.code in [2, 4]: raise ErtisError(context=e.details, err_msg='Please provide valid query...', err_code='errors.badQuery', status_code=400) raise
def check_property_name(property_name): if not PROPERTY_NAME_REGEX.match(property_name): raise ErtisError(err_code="errors.invalidPropertyName", err_msg="Property name should match: " + PROPERTY_NAME_PATTERN, status_code=400, context={"property_name": property_name})
def check_keywords_by_type(schema): properties = schema.get('properties', {}) for property_name, keywords in properties.items(): check_property_name(property_name) check_property_type(property_name, keywords) _type = keywords['type'] valid_keywords = KEYWORDS_BASED_ON_TYPES[_type] for keyword, value in keywords.items(): if keyword == 'type': continue if keyword not in valid_keywords: raise ErtisError( err_code="errors.invalidKeywordForPropertyType", err_msg= "Given <{}> keyword is not allowed for this property type." .format(keyword), status_code=400, context={ "property_name": property_name, "property": keywords, "valid_keywords_for_{}_type".format(_type): valid_keywords })
def fill_min_max_fields_if_not_given(resource): """ Auto complete min & max fields to properties of user type :param resource: :return: resource """ schema = resource.get('schema', {}) if not schema: raise ErtisError(err_code="errors.resourceMustContainSchema", err_msg="Resource must contain schema.", status_code=400, context={"given_resource": resource}) for prop, keywords in schema.get('properties', {}).items(): property_type = keywords.get('type') if property_type == 'string': if 'minLength' not in keywords: keywords['minLength'] = 0 if 'maxLength' not in keywords: keywords['maxLength'] = 100 if property_type in ['integer', 'number']: if 'minimum' not in keywords: keywords['minimum'] = 0 if 'maximum' not in keywords: keywords['maximum'] = 100 return resource
def save(self, document, collection, force=False): if "_id" not in document: document["_id"] = create_id() else: document['_id'] = maybe_object_id(document['_id']) try: if force: self.db[collection].replace_one( {'_id': document['_id']}, document, upsert=True ) else: self.db[collection].save(document) except DuplicateKeyError as e: raise ErtisError( err_code="errors.duplicateKeyError", status_code=409, err_msg="There is already existing record in collection<{}>.".format(collection), context={ 'collection': collection, 'error_message': e.details['errmsg'] } ) return document
def get_where(request): try: _json = request.json.get("where", None) return _json except Exception as ex: logging.error(ex) raise ErtisError(err_code="errors.BadJsonGiven", err_msg="Body Json is Invalid", status_code=400)
def get_select(request): try: _json = json.loads(request.data.decode("utf-8")).get("select", None) return _json except Exception as ex: logging.error(ex) raise ErtisError(err_code="errors.BadJsonGiven", err_msg="Body Json is Invalid", status_code=400)
async def ensure_utilizer_is_permitted(role, required_permission): permissions = role.get('permissions', []) has_permission = implies_any(permissions, required_permission) if not has_permission: raise ErtisError( err_code="errors.permissionDenied", err_msg="Permission denied for this action <{}>".format( required_permission), status_code=403)
def _check_user_status(user): if not user.get('status', None) or user['status'] not in [ 'active', 'warning' ]: raise ErtisError( err_msg="User status: <{}> is not valid to generate token". format(user.get('status', None)), err_code="errors.userStatusIsNotValid", status_code=401)
def disallow_predefined_permission_group_operations(resource): if resource.get('is_default', None): raise ErtisError( err_msg="Predefined permission groups cant be deleted or changed", err_code="errors.predefinedPermissionGroupsCantBeChangedOrDeleted", status_code=403 ) return resource
async def update_user(self, resource_id, data, utilizer, user_type_service, event_service): membership_id = utilizer['membership_id'] resource = await self._find_user(resource_id, membership_id) provided_data = pop_non_updatable_fields(data) await validate_user_model_by_user_type(membership_id, resource, user_type_service, operation=OperationTypes.UPDATE) resource = prepare_user_fields(resource, utilizer['membership_id'], data.get('password'), opt='update') _resource = copy.deepcopy(resource) _resource.update(provided_data) if _resource == resource: raise ErtisError(err_code="errors.identicalDocument", err_msg="Identical document error", status_code=409) if _resource['username'] != resource['username']: await self.revoke_and_delete_old_active_tokens(_resource) resource['sys'].update({ 'modified_at': datetime.datetime.utcnow(), 'modified_by': utilizer.get('username', utilizer.get('name')) }) provided_data['sys'] = resource['sys'] resource = await self.update_user_with_body(resource_id, utilizer['membership_id'], provided_data) resource.pop('password') resource['_id'] = str(resource['_id']) _resource['_id'] = str(_resource['_id']) await event_service.on_event((Event( **{ 'document': resource, 'prior': _resource, 'utilizer': utilizer, 'type': 'UserUpdatedEvent', 'membership_id': utilizer['membership_id'], 'sys': { 'created_at': datetime.datetime.utcnow(), 'created_by': utilizer.get('username', utilizer.get( 'name')) } }))) return resource
def _check_identicality(resource, provided_body): _resource = copy.deepcopy(resource) resource.update(provided_body) if resource == _resource: raise ErtisError(err_msg="Identical document error", err_code="errors.identicalDocument", status_code=409) return _resource
def raise_resource_not_found_err(collection_name, key): raise ErtisError( err_code="errors.resourceNotFound", status_code=404, err_msg="Not found any resource in collection<{}> with given key <{}>".format(collection_name, key), context={ 'collection_name': collection_name, 'key': key } )
async def ensure_membership_is_exists(db, membership_id, user=None): membership = await db.memberships.find_one( {'_id': maybe_object_id(membership_id)}) if not membership: raise ErtisError( err_msg="Membership not found in db by given membership_id: <{}>". format(membership_id), err_code="errors.MembershipNotFound", status_code=404) if user and str(user['membership_id']) != membership_id: raise ErtisError( err_code="errors.userNotPermittedForMembership", err_msg="User is not permitted for membership: <{}>".format( membership_id), status_code=401) return membership
def site_map(): auth_header = request.headers.get('Authorization') if not auth_header: raise ErtisError( err_code="errors.authorizationHeaderRequired", err_msg="Authorization header is required for using ME API", status_code=401) try: auth_header = auth_header.split(' ') except Exception as e: ErtisError(err_msg="Invalid authorization header provided.", err_code="errors.authorizationHeaderIsInvalid", status_code=401, context={'message': str(e)}) if len(auth_header) != 2: raise ErtisError(err_msg="Bearer token usage is invalid", err_code="errors.invalidBearerTokenUsage", status_code=401) token = auth_header[1] security_manager = ErtisSecurityManager(app.db) security_manager.load_user(token, settings['application_secret'], settings['verify_token']) links = [] for rule in app.url_map.iter_rules(): options = {} for arg in rule.arguments: options[arg] = "[{0}]".format(arg) methods = ','.join(rule.methods) url = str(rule) if rule.endpoint == 'static': continue line = "{:50s} {:20s} {}".format(rule.endpoint, methods, url) links.append(line) return Response(json.dumps(links, default=bson_to_json), mimetype='application/json', status=200)
async def _find_user_by_query(self, where): users = await self.db.users.find(where).to_list(length=None) if not users: raise ErtisError( err_msg="User not found by given query <{}>".format( json.dumps(where)), err_code="errors.userNotFound", status_code=404) return users[0]
def disallow_update_fields(resource, _resource): for field in DISALLOW_UPDATE_FILEDS: if resource[field] != _resource[field]: raise ErtisError( err_msg= "{} is not updatable. Because its generated automatically by system" .format(field), err_code="errors.badRequest", status_code=400) return resource
async def get_role(self, role_id, membership_id): role = await self._find_role(role_id, membership_id) if not role: raise ErtisError( err_msg="Role not found by given _id: <{}> in membership".format(role_id), err_code="errors.roleNotFoundError", status_code=404 ) return role
def ensure_token_provided(auth_header): if not auth_header: raise ErtisError( err_code="errors.authorizationHeaderRequired", err_msg="Authorization header is required for using this api<{}>", status_code=401) try: auth_header = auth_header.split(' ') except Exception as e: ErtisError(err_msg="Invalid authorization header provided.", err_code="errors.authorizationHeaderIsInvalid", status_code=401, context={'message': str(e)}) if len(auth_header) != 2: raise ErtisError(err_msg="Bearer token usage is invalid", err_code="errors.invalidBearerTokenUsage", status_code=401) return auth_header
async def remove_provider(db, membership_id, provider_id): try: await db.providers.delete_one({ '_id': maybe_object_id(provider_id), 'membership_id': membership_id }) except Exception as e: raise ErtisError(err_msg="An error occurred while deleting provider", err_code="errors.errorOccurredWhileDeletingProvider", status_code=500, context={'_id': provider_id}, reason=str(e))
def ensure_keys_of_schema_properties_is_in_valid_keywords(properties): for key, val in properties.items(): if key not in VALID_KEYWORDS: raise ErtisError(err_code="errors.invalidKeyword", err_msg="Invalid keyword", status_code=400, context={ "given_keyword": key, "valid_keywords": VALID_KEYWORDS }) elif key == 'properties': if type(val) != dict: raise ErtisError(err_code="errors.invalidKeyword", err_msg="Properties must be a dictionary", status_code=400, context={"given_properties": val}) for _key, _val in val.items(): ensure_keys_of_schema_properties_is_in_valid_keywords(_val)
async def refresh_token(self, do_revoke, refreshable_token, settings, event_service): revoke_flag = True if do_revoke == 'true' else False user = await self._load_user(refreshable_token, settings['application_secret'], settings['verify_token']) if user['decoded_token']['rf'] is False: raise ErtisError(err_msg="Provided token is not refreshable", err_code="errors.refreshableTokenError", status_code=400) membership = await self.db.memberships.find_one( {'_id': maybe_object_id(user['membership_id'])}) await ensure_token_is_not_revoked(self.db, refreshable_token) refreshed_token = await self._refresh_token( refreshable_token, user, settings['application_secret'], membership['token_ttl'], membership['refresh_token_ttl']) tasks = [ self._update_user_with_token(refreshed_token, user), event_service.on_event((Event( **{ 'document': refreshed_token, 'prior': {}, 'utilizer': user, 'type': 'TokenRefreshedEvent', 'membership_id': user['membership_id'], 'sys': { 'created_at': datetime.datetime.utcnow() } }))) ] if revoke_flag: tasks.append( self.revoke_token(refreshable_token, settings, event_service, user=user)) tasks.append( self._remove_from_active_tokens(user, refreshable_token, user['decoded_token']['rf'])) tasks.append( self._insert_active_tokens(user, refreshed_token, membership)) await asyncio.gather(*tasks) return refreshed_token
async def _remove_user(self, user_id, membership_id): try: await self.db.users.delete_one({ '_id': maybe_object_id(user_id), 'membership_id': membership_id }) except Exception as e: raise ErtisError(err_msg="An error occurred while deleting user", err_code="errors.errorOccurredWhileDeletingUser", status_code=500, context={'user_id': user_id}, reason=str(e))
async def me_api(request, **kwargs): if request.ctx.utilizer_type == UtilizerTypes.USER: utilizer = await app.bearer_token_service.me(request.ctx.utilizer) elif request.ctx.utilizer_type == UtilizerTypes.APPLICATION: utilizer = await app.basic_token_service.me(request.ctx.utilizer) else: raise ErtisError(err_code="errors.authorizationError", err_msg="Unsupported authorization header", status_code=401) return response.json( json.loads(json.dumps(utilizer, default=bson_to_json)))
def ensure_db_is_available(generic_service): try: generic_service.find_one_by(collection='healtcheck') except Exception as e: raise ErtisError( err_msg="Ertis DB is not available", err_code="errors.dbConnectionError", status_code=500, context={ 'message': str(e) } )